Skip to content

Commit 221963e

Browse files
authored
Rewrite type annotations (#119)
* Rewrite type annotations Move from comments to Python 3 type annotations. While at it cleanup also imports. * ci: bump python version we use to run lint to 3.10 So that the lastest tooling is installed and it's same we have on our machines :)
1 parent e3875fc commit 221963e

File tree

4 files changed

+46
-88
lines changed

4 files changed

+46
-88
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ jobs:
3333
- uses: actions/checkout@v4
3434
- uses: actions/setup-python@v5
3535
with:
36-
python-version: '3.7'
36+
python-version: '3.10'
3737
- run: .ci/scripts/lint.sh
3838

3939
test:

ecs_logging/_stdlib.py

Lines changed: 30 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -15,29 +15,27 @@
1515
# specific language governing permissions and limitations
1616
# under the License.
1717

18+
import collections.abc
1819
import logging
1920
import sys
2021
import time
22+
from functools import lru_cache
2123
from traceback import format_tb
2224

2325
from ._meta import ECS_VERSION
2426
from ._utils import (
25-
TYPE_CHECKING,
26-
collections_abc,
2727
de_dot,
2828
flatten_dict,
2929
json_dumps,
30-
lru_cache,
3130
merge_dicts,
3231
)
3332

34-
if TYPE_CHECKING:
35-
from typing import Any, Callable, Dict, Optional, Sequence
33+
from typing import Any, Callable, Dict, Optional, Sequence, Union
3634

37-
try:
38-
from typing import Literal, Union # type: ignore
39-
except ImportError:
40-
from typing_extensions import Literal, Union # type: ignore
35+
try:
36+
from typing import Literal # type: ignore
37+
except ImportError:
38+
from typing_extensions import Literal # type: ignore
4139

4240

4341
# Load the attributes of a LogRecord so if some are
@@ -78,16 +76,15 @@ class StdlibFormatter(logging.Formatter):
7876
converter = time.gmtime
7977

8078
def __init__(
81-
self, # type: Any
82-
fmt=None, # type: Optional[str]
83-
datefmt=None, # type: Optional[str]
84-
style="%", # type: Union[Literal["%"], Literal["{"], Literal["$"]]
85-
validate=None, # type: Optional[bool]
86-
stack_trace_limit=None, # type: Optional[int]
87-
extra=None, # type: Optional[Dict[str, Any]]
88-
exclude_fields=(), # type: Sequence[str]
89-
):
90-
# type: (...) -> None
79+
self,
80+
fmt: Optional[str] = None,
81+
datefmt: Optional[str] = None,
82+
style: Union[Literal["%"], Literal["{"], Literal["$"]] = "%",
83+
validate: Optional[bool] = None,
84+
stack_trace_limit: Optional[int] = None,
85+
extra: Optional[Dict[str, Any]] = None,
86+
exclude_fields: Sequence[str] = (),
87+
) -> None:
9188
"""Initialize the ECS formatter.
9289
9390
:param int stack_trace_limit:
@@ -127,7 +124,7 @@ def __init__(
127124
)
128125

129126
if (
130-
not isinstance(exclude_fields, collections_abc.Sequence)
127+
not isinstance(exclude_fields, collections.abc.Sequence)
131128
or isinstance(exclude_fields, str)
132129
or any(not isinstance(item, str) for item in exclude_fields)
133130
):
@@ -137,8 +134,7 @@ def __init__(
137134
self._exclude_fields = frozenset(exclude_fields)
138135
self._stack_trace_limit = stack_trace_limit
139136

140-
def _record_error_type(self, record):
141-
# type: (logging.LogRecord) -> Optional[str]
137+
def _record_error_type(self, record: logging.LogRecord) -> Optional[str]:
142138
exc_info = record.exc_info
143139
if not exc_info:
144140
# exc_info is either an iterable or bool. If it doesn't
@@ -151,8 +147,7 @@ def _record_error_type(self, record):
151147
return exc_info[0].__name__
152148
return None
153149

154-
def _record_error_message(self, record):
155-
# type: (logging.LogRecord) -> Optional[str]
150+
def _record_error_message(self, record: logging.LogRecord) -> Optional[str]:
156151
exc_info = record.exc_info
157152
if not exc_info:
158153
# exc_info is either an iterable or bool. If it doesn't
@@ -165,13 +160,11 @@ def _record_error_message(self, record):
165160
return str(exc_info[1])
166161
return None
167162

168-
def format(self, record):
169-
# type: (logging.LogRecord) -> str
163+
def format(self, record: logging.LogRecord) -> str:
170164
result = self.format_to_ecs(record)
171165
return json_dumps(result)
172166

173-
def format_to_ecs(self, record):
174-
# type: (logging.LogRecord) -> Dict[str, Any]
167+
def format_to_ecs(self, record: logging.LogRecord) -> Dict[str, Any]:
175168
"""Function that can be overridden to add additional fields to
176169
(or remove fields from) the JSON before being dumped into a string.
177170
@@ -185,7 +178,7 @@ def format_to_ecs(self, record):
185178
return result
186179
"""
187180

188-
extractors = {
181+
extractors: Dict[str, Callable[[logging.LogRecord], Any]] = {
189182
"@timestamp": self._record_timestamp,
190183
"ecs.version": lambda _: ECS_VERSION,
191184
"log.level": lambda r: (r.levelname.lower() if r.levelname else None),
@@ -201,9 +194,9 @@ def format_to_ecs(self, record):
201194
"error.type": self._record_error_type,
202195
"error.message": self._record_error_message,
203196
"error.stack_trace": self._record_error_stack_trace,
204-
} # type: Dict[str, Callable[[logging.LogRecord],Any]]
197+
}
205198

206-
result = {} # type: Dict[str, Any]
199+
result: Dict[str, Any] = {}
207200
for field in set(extractors.keys()).difference(self._exclude_fields):
208201
if self._is_field_excluded(field):
209202
continue
@@ -262,28 +255,26 @@ def format_to_ecs(self, record):
262255
return result
263256

264257
@lru_cache()
265-
def _is_field_excluded(self, field):
266-
# type: (str) -> bool
258+
def _is_field_excluded(self, field: str) -> bool:
267259
field_path = []
268260
for path in field.split("."):
269261
field_path.append(path)
270262
if ".".join(field_path) in self._exclude_fields:
271263
return True
272264
return False
273265

274-
def _record_timestamp(self, record):
275-
# type: (logging.LogRecord) -> str
266+
def _record_timestamp(self, record: logging.LogRecord) -> str:
276267
return "%s.%03dZ" % (
277268
self.formatTime(record, datefmt="%Y-%m-%dT%H:%M:%S"),
278269
record.msecs,
279270
)
280271

281-
def _record_attribute(self, attribute):
282-
# type: (str) -> Callable[[logging.LogRecord], Optional[Any]]
272+
def _record_attribute(
273+
self, attribute: str
274+
) -> Callable[[logging.LogRecord], Optional[Any]]:
283275
return lambda r: getattr(r, attribute, None)
284276

285-
def _record_error_stack_trace(self, record):
286-
# type: (logging.LogRecord) -> Optional[str]
277+
def _record_error_stack_trace(self, record: logging.LogRecord) -> Optional[str]:
287278
# Using stack_info=True will add 'error.stack_trace' even
288279
# if the type is not 'error', exc_info=True only gathers
289280
# when there's an active exception.

ecs_logging/_structlog.py

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,16 @@
1717

1818
import time
1919
import datetime
20-
from ._meta import ECS_VERSION
21-
from ._utils import json_dumps, normalize_dict, TYPE_CHECKING
20+
from typing import Any, Dict
2221

23-
if TYPE_CHECKING:
24-
from typing import Any, Dict
22+
from ._meta import ECS_VERSION
23+
from ._utils import json_dumps, normalize_dict
2524

2625

2726
class StructlogFormatter:
2827
"""ECS formatter for the ``structlog`` module"""
2928

30-
def __call__(self, _, name, event_dict):
31-
# type: (Any, str, Dict[str, Any]) -> str
29+
def __call__(self, _: Any, name: str, event_dict: Dict[str, Any]) -> str:
3230

3331
# Handle event -> message now so that stuff like `event.dataset` doesn't
3432
# cause problems down the line
@@ -38,8 +36,7 @@ def __call__(self, _, name, event_dict):
3836
event_dict = self.format_to_ecs(event_dict)
3937
return self._json_dumps(event_dict)
4038

41-
def format_to_ecs(self, event_dict):
42-
# type: (Dict[str, Any]) -> Dict[str, Any]
39+
def format_to_ecs(self, event_dict: Dict[str, Any]) -> Dict[str, Any]:
4340
if "@timestamp" not in event_dict:
4441
event_dict["@timestamp"] = (
4542
datetime.datetime.fromtimestamp(
@@ -58,6 +55,5 @@ def format_to_ecs(self, event_dict):
5855
event_dict.setdefault("ecs.version", ECS_VERSION)
5956
return event_dict
6057

61-
def _json_dumps(self, value):
62-
# type: (Dict[str, Any]) -> str
58+
def _json_dumps(self, value: Dict[str, Any]) -> str:
6359
return json_dumps(value=value)

ecs_logging/_utils.py

Lines changed: 9 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -15,52 +15,28 @@
1515
# specific language governing permissions and limitations
1616
# under the License.
1717

18+
import collections.abc
1819
import json
1920
import functools
20-
21-
try:
22-
import typing
23-
24-
TYPE_CHECKING = typing.TYPE_CHECKING
25-
except ImportError:
26-
typing = None # type: ignore
27-
TYPE_CHECKING = False
28-
29-
if TYPE_CHECKING:
30-
from typing import Any, Dict
31-
32-
try:
33-
import collections.abc as collections_abc
34-
except ImportError:
35-
import collections as collections_abc # type: ignore
36-
37-
try:
38-
from functools import lru_cache
39-
except ImportError:
40-
from backports.functools_lru_cache import lru_cache # type: ignore
21+
from typing import Any, Dict, Mapping
4122

4223

4324
__all__ = [
44-
"collections_abc",
4525
"normalize_dict",
4626
"de_dot",
4727
"merge_dicts",
4828
"json_dumps",
49-
"TYPE_CHECKING",
50-
"typing",
51-
"lru_cache",
5229
]
5330

5431

55-
def flatten_dict(value):
56-
# type: (typing.Mapping[str, Any]) -> Dict[str, Any]
32+
def flatten_dict(value: Mapping[str, Any]) -> Dict[str, Any]:
5733
"""Adds dots to all nested fields in dictionaries.
5834
Raises an error if there are entries which are represented
5935
with different forms of nesting. (ie {"a": {"b": 1}, "a.b": 2})
6036
"""
6137
top_level = {}
6238
for key, val in value.items():
63-
if not isinstance(val, collections_abc.Mapping):
39+
if not isinstance(val, collections.abc.Mapping):
6440
if key in top_level:
6541
raise ValueError(f"Duplicate entry for '{key}' with different nesting")
6642
top_level[key] = val
@@ -77,8 +53,7 @@ def flatten_dict(value):
7753
return top_level
7854

7955

80-
def normalize_dict(value):
81-
# type: (Dict[str, Any]) -> Dict[str, Any]
56+
def normalize_dict(value: Dict[str, Any]) -> Dict[str, Any]:
8257
"""Expands all dotted names to nested dictionaries"""
8358
if not isinstance(value, dict):
8459
return value
@@ -94,8 +69,7 @@ def normalize_dict(value):
9469
return value
9570

9671

97-
def de_dot(dot_string, msg):
98-
# type: (str, Any) -> Dict[str, Any]
72+
def de_dot(dot_string: str, msg: Any) -> Dict[str, Any]:
9973
"""Turn value and dotted string key into a nested dictionary"""
10074
arr = dot_string.split(".")
10175
ret = {arr[-1]: msg}
@@ -104,8 +78,7 @@ def de_dot(dot_string, msg):
10478
return ret
10579

10680

107-
def merge_dicts(from_, into):
108-
# type: (Dict[Any, Any], Dict[Any, Any]) -> Dict[Any, Any]
81+
def merge_dicts(from_: Dict[Any, Any], into: Dict[Any, Any]) -> Dict[Any, Any]:
10982
"""Merge deeply nested dictionary structures.
11083
When called has side-effects within 'destination'.
11184
"""
@@ -125,8 +98,7 @@ def merge_dicts(from_, into):
12598
return into
12699

127100

128-
def json_dumps(value):
129-
# type: (Dict[str, Any]) -> str
101+
def json_dumps(value: Dict[str, Any]) -> str:
130102

131103
# Ensure that the first three fields are '@timestamp',
132104
# 'log.level', and 'message' per ECS spec
@@ -175,8 +147,7 @@ def json_dumps(value):
175147
return json_dumps(value)
176148

177149

178-
def _json_dumps_fallback(value):
179-
# type: (Any) -> Any
150+
def _json_dumps_fallback(value: Any) -> Any:
180151
"""
181152
Fallback handler for json.dumps to handle objects json doesn't know how to
182153
serialize.

0 commit comments

Comments
 (0)