Skip to content

Commit 67cabea

Browse files
fixed some ruff lints
1 parent 0811ef0 commit 67cabea

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+832
-673
lines changed

pedantic/decorators/__init__.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,34 @@
1-
from .fn_deco_context_manager import safe_contextmanager, safe_async_contextmanager
1+
from .class_decorators import for_all_methods, pedantic_class, pedantic_class_require_docstring, trace_class
2+
from .cls_deco_frozen_dataclass import frozen_dataclass, frozen_type_safe_dataclass
3+
from .fn_deco_context_manager import safe_async_contextmanager, safe_contextmanager
24
from .fn_deco_deprecated import deprecated
3-
from .fn_deco_in_subprocess import in_subprocess, calculate_in_subprocess
5+
from .fn_deco_in_subprocess import calculate_in_subprocess, in_subprocess
46
from .fn_deco_overrides import overrides
57
from .fn_deco_pedantic import pedantic, pedantic_require_docstring
68
from .fn_deco_require_kwargs import require_kwargs
79
from .fn_deco_retry import retry, retry_func
810
from .fn_deco_trace import trace
911
from .fn_deco_trace_if_returns import trace_if_returns
10-
from .class_decorators import pedantic_class, pedantic_class_require_docstring, trace_class, for_all_methods
11-
from .cls_deco_frozen_dataclass import frozen_dataclass, frozen_type_safe_dataclass
12+
13+
__all__ = [
14+
'calculate_in_subprocess',
15+
'deprecated',
16+
'deprecated',
17+
'for_all_methods',
18+
'frozen_dataclass',
19+
'frozen_type_safe_dataclass',
20+
'in_subprocess',
21+
'overrides',
22+
'pedantic',
23+
'pedantic_class',
24+
'pedantic_class_require_docstring',
25+
'pedantic_require_docstring',
26+
'require_kwargs',
27+
'retry',
28+
'retry_func',
29+
'safe_async_contextmanager',
30+
'safe_contextmanager',
31+
'trace',
32+
'trace_class',
33+
'trace_if_returns',
34+
]

pedantic/decorators/class_decorators.py

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,28 @@
11
import enum
22
import types
3+
from collections.abc import Callable
34
from dataclasses import is_dataclass
4-
from typing import Callable, Optional, Dict, Type
55

6-
from pedantic.constants import TYPE_VAR_ATTR_NAME, TYPE_VAR_METHOD_NAME, F, C, TYPE_VAR_SELF
7-
from pedantic.decorators import trace
6+
from pedantic.constants import TYPE_VAR_ATTR_NAME, TYPE_VAR_METHOD_NAME, TYPE_VAR_SELF, C, F
7+
from pedantic.decorators.fn_deco_trace import trace
88
from pedantic.decorators.fn_deco_pedantic import pedantic, pedantic_require_docstring
99
from pedantic.env_var_logic import is_enabled
1010
from pedantic.exceptions import PedanticTypeCheckException
11-
from pedantic.type_checking_logic.check_generic_classes import check_instance_of_generic_class_and_get_type_vars, \
12-
is_instance_of_generic_class
11+
from pedantic.type_checking_logic.check_generic_classes import (
12+
check_instance_of_generic_class_and_get_type_vars,
13+
is_instance_of_generic_class,
14+
)
1315

1416

15-
def for_all_methods(decorator: F) -> Callable[[Type[C]], Type[C]]:
17+
def for_all_methods(decorator: F) -> Callable[[type[C]], type[C]]:
1618
"""
17-
Applies a decorator to all methods of a class.
19+
Applies a decorator to all methods of a class.
1820
19-
Example:
20-
21-
>>> @for_all_methods(pedantic)
22-
... class MyClass(object):
23-
... def m1(self): pass
24-
... def m2(self, x): pass
21+
Example:
22+
>>> @for_all_methods(pedantic)
23+
... class MyClass(object):
24+
... def m1(self): pass
25+
... def m2(self, x): pass
2526
"""
2627
def decorate(cls: C) -> C:
2728
if not is_enabled():
@@ -54,30 +55,30 @@ def decorate(cls: C) -> C:
5455

5556

5657
def pedantic_class(cls: C) -> C:
57-
""" Shortcut for @for_all_methods(pedantic) """
58+
"""Shortcut for @for_all_methods(pedantic)"""
5859
return for_all_methods(decorator=pedantic)(cls=cls)
5960

6061

6162
def pedantic_class_require_docstring(cls: C) -> C:
62-
""" Shortcut for @for_all_methods(pedantic_require_docstring) """
63+
"""Shortcut for @for_all_methods(pedantic_require_docstring)"""
6364
return for_all_methods(decorator=pedantic_require_docstring)(cls=cls)
6465

6566

6667
def trace_class(cls: C) -> C:
67-
""" Shortcut for @for_all_methods(trace) """
68+
"""Shortcut for @for_all_methods(trace)"""
6869
return for_all_methods(decorator=trace)(cls=cls)
6970

7071

71-
def _get_wrapped(prop: Optional[F], decorator: F) -> Optional[F]:
72+
def _get_wrapped(prop: F | None, decorator: F) -> F | None:
7273
return decorator(prop) if prop is not None else None
7374

7475

7576
def _add_type_var_attr_and_method_to_class(cls: C) -> None:
76-
def type_vars(self) -> Dict:
77+
def type_vars(self: C) -> dict:
7778
t_vars = {TYPE_VAR_SELF: cls}
7879

7980
if is_instance_of_generic_class(instance=self):
80-
type_vars_fifo = getattr(self, TYPE_VAR_ATTR_NAME, dict())
81+
type_vars_fifo = getattr(self, TYPE_VAR_ATTR_NAME, {})
8182
type_vars_generics = check_instance_of_generic_class_and_get_type_vars(instance=self)
8283
setattr(self, TYPE_VAR_ATTR_NAME, {**type_vars_fifo, **type_vars_generics, **t_vars})
8384
else:

pedantic/decorators/cls_deco_frozen_dataclass.py

Lines changed: 71 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,117 +1,117 @@
1+
from collections.abc import Callable
12
from copy import deepcopy
23
from dataclasses import dataclass, fields, replace
3-
from typing import Type, TypeVar, Any, Union, Callable, Dict
4+
from typing import Any, TypeVar
45

56
from pedantic.get_context import get_context
67
from pedantic.type_checking_logic.check_types import assert_value_matches_type
78

89
T = TypeVar('T')
910

1011

11-
def frozen_type_safe_dataclass(cls: Type[T]) -> Type[T]:
12-
""" Shortcut for @frozen_dataclass(type_safe=True) """
12+
def frozen_type_safe_dataclass(cls: type[T]) -> type[T]:
13+
"""Shortcut for @frozen_dataclass(type_safe=True)"""
1314

1415
return frozen_dataclass(type_safe=True)(cls)
1516

1617

17-
def frozen_dataclass(
18-
cls: Type[T] = None,
19-
type_safe: bool = False,
20-
order: bool = False,
21-
kw_only: bool = True,
22-
slots: bool = False,
23-
) -> Union[Type[T], Callable[[Type[T]], Type[T]]]:
18+
def frozen_dataclass( # noqa: C901
19+
cls: type[T] | None = None,
20+
type_safe: bool = False,
21+
order: bool = False,
22+
kw_only: bool = True,
23+
slots: bool = False,
24+
) -> type[T] | Callable[[type[T]], type[T]]:
2425
"""
25-
Makes the decorated class immutable and a dataclass by adding the [@dataclass(frozen=True)]
26-
decorator. Also adds useful copy_with() and validate_types() instance methods to this class (see below).
27-
28-
If [type_safe] is True, a type check is performed for each field after the __post_init__ method was called
29-
which itself s directly called after the __init__ constructor.
30-
Note this have a negative impact on the performance. It's recommend to use this for debugging and testing only.
31-
32-
In a nutshell, the followings methods will be added to the decorated class automatically:
33-
- __init__() gives you a simple constructor like "Foo(a=6, b='hi', c=True)"
34-
- __eq__() lets you compare objects easily with "a == b"
35-
- __hash__() is also needed for instance comparison
36-
- __repr__() gives you a nice output when you call "print(foo)"
37-
- copy_with() allows you to quickly create new similar frozen instances. Use this instead of setters.
38-
- deep_copy_with() allows you to create deep copies and modify them.
39-
- validate_types() allows you to validate the types of the dataclass.
40-
This is called automatically when [type_safe] is True.
41-
42-
If the [order] parameter is True (default is False), the following comparison methods
43-
will be added additionally:
44-
- __lt__() lets you compare instance like "a < b"
45-
- __le__() lets you compare instance like "a <= b"
46-
- __gt__() lets you compare instance like "a > b"
47-
- __ge__() lets you compare instance like "a >= b"
48-
49-
These compare the class as if it were a tuple of its fields, in order.
50-
Both instances in the comparison must be of the identical type.
51-
52-
The parameters slots and kw_only are only applied if the Python version is greater or equal to 3.10.
53-
54-
Example:
55-
56-
>>> @frozen_dataclass
57-
... class Foo:
58-
... a: int
59-
... b: str
60-
... c: bool
61-
>>> foo = Foo(a=6, b='hi', c=True)
62-
>>> print(foo)
63-
Foo(a=6, b='hi', c=True)
64-
>>> print(foo.copy_with())
65-
Foo(a=6, b='hi', c=True)
66-
>>> print(foo.copy_with(a=42))
67-
Foo(a=42, b='hi', c=True)
68-
>>> print(foo.copy_with(b='Hello'))
69-
Foo(a=6, b='Hello', c=True)
70-
>>> print(foo.copy_with(c=False))
71-
Foo(a=6, b='hi', c=False)
72-
>>> print(foo.copy_with(a=676676, b='new', c=False))
73-
Foo(a=676676, b='new', c=False)
26+
Makes the decorated class immutable and a dataclass by adding the [@dataclass(frozen=True)]
27+
decorator. Also adds useful copy_with() and validate_types() instance methods to this class (see below).
28+
29+
If [type_safe] is True, a type check is performed for each field after the __post_init__ method was called
30+
which itself s directly called after the __init__ constructor.
31+
Note this have a negative impact on the performance. It's recommend to use this for debugging and testing only.
32+
33+
In a nutshell, the followings methods will be added to the decorated class automatically:
34+
- __init__() gives you a simple constructor like "Foo(a=6, b='hi', c=True)"
35+
- __eq__() lets you compare objects easily with "a == b"
36+
- __hash__() is also needed for instance comparison
37+
- __repr__() gives you a nice output when you call "print(foo)"
38+
- copy_with() allows you to quickly create new similar frozen instances. Use this instead of setters.
39+
- deep_copy_with() allows you to create deep copies and modify them.
40+
- validate_types() allows you to validate the types of the dataclass.
41+
This is called automatically when [type_safe] is True.
42+
43+
If the [order] parameter is True (default is False), the following comparison methods
44+
will be added additionally:
45+
- __lt__() lets you compare instance like "a < b"
46+
- __le__() lets you compare instance like "a <= b"
47+
- __gt__() lets you compare instance like "a > b"
48+
- __ge__() lets you compare instance like "a >= b"
49+
50+
These compare the class as if it were a tuple of its fields, in order.
51+
Both instances in the comparison must be of the identical type.
52+
53+
The parameters slots and kw_only are only applied if the Python version is greater or equal to 3.10.
54+
55+
Example:
56+
>>> @frozen_dataclass
57+
... class Foo:
58+
... a: int
59+
... b: str
60+
... c: bool
61+
>>> foo = Foo(a=6, b='hi', c=True)
62+
>>> print(foo)
63+
Foo(a=6, b='hi', c=True)
64+
>>> print(foo.copy_with())
65+
Foo(a=6, b='hi', c=True)
66+
>>> print(foo.copy_with(a=42))
67+
Foo(a=42, b='hi', c=True)
68+
>>> print(foo.copy_with(b='Hello'))
69+
Foo(a=6, b='Hello', c=True)
70+
>>> print(foo.copy_with(c=False))
71+
Foo(a=6, b='hi', c=False)
72+
>>> print(foo.copy_with(a=676676, b='new', c=False))
73+
Foo(a=676676, b='new', c=False)
7474
"""
7575

76-
def decorator(cls_: Type[T]) -> Type[T]:
76+
def decorator(cls_: type[T]) -> type[T]:
7777
args = {'frozen': True, 'order': order, 'kw_only': kw_only, 'slots': slots}
7878

7979
if type_safe:
8080
old_post_init = getattr(cls_, '__post_init__', lambda _: None)
8181

82-
def new_post_init(self) -> None:
82+
def new_post_init(self: T) -> None:
8383
old_post_init(self)
8484
context = get_context(depth=3, increase_depth_if_name_matches=[
8585
copy_with.__name__,
8686
deep_copy_with.__name__,
8787
])
8888
self.validate_types(_context=context)
8989

90-
setattr(cls_, '__post_init__', new_post_init) # must be done before applying dataclass()
90+
cls_.__post_init__ = new_post_init # must be done before applying dataclass()
9191

9292
new_class = dataclass(**args)(cls_) # slots = True will create a new class!
9393

94-
def copy_with(self, **kwargs: Any) -> T:
94+
def copy_with(self: T, **kwargs: Any) -> T:
9595
"""
96-
Creates a new immutable instance that by copying all fields of this instance replaced by the new values.
97-
Keep in mind that this is a shallow copy!
96+
Creates a new immutable instance that by copying all fields of this instance replaced by the new values.
97+
Keep in mind that this is a shallow copy!
9898
"""
9999

100100
return replace(self, **kwargs)
101101

102-
def deep_copy_with(self, **kwargs: Any) -> T:
102+
def deep_copy_with(self: T, **kwargs: Any) -> T:
103103
"""
104-
Creates a new immutable instance that by deep copying all fields of
105-
this instance replaced by the new values.
104+
Creates a new immutable instance that by deep copying all fields of
105+
this instance replaced by the new values.
106106
"""
107107

108108
current_values = {field.name: deepcopy(getattr(self, field.name)) for field in fields(self)}
109109
return new_class(**{**current_values, **kwargs})
110110

111-
def validate_types(self, *, _context: Dict[str, Type] = None) -> None:
111+
def validate_types(self: T, *, _context: dict[str, type] | None = None) -> None:
112112
"""
113-
Checks that all instance variable have the correct type.
114-
Raises a [PedanticTypeCheckException] if at least one type is incorrect.
113+
Checks that all instance variable have the correct type.
114+
Raises a [PedanticTypeCheckException] if at least one type is incorrect.
115115
"""
116116

117117
props = fields(new_class)

pedantic/decorators/fn_deco_context_manager.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
from contextlib import contextmanager, asynccontextmanager
1+
from collections.abc import AsyncIterator, Callable, Iterator
2+
from contextlib import AbstractAsyncContextManager, AbstractContextManager, asynccontextmanager, contextmanager
23
from functools import wraps
34
from inspect import isasyncgenfunction, isgeneratorfunction
4-
from typing import Callable, TypeVar, Iterator, ContextManager, AsyncContextManager, AsyncIterator
5+
from typing import Any, TypeVar
56

67
T = TypeVar('T')
78

89

9-
def safe_contextmanager(f: Callable[..., Iterator[T]]) -> Callable[..., ContextManager[T]]:
10+
def safe_contextmanager(f: Callable[..., Iterator[T]]) -> Callable[..., AbstractContextManager[T]]:
1011
"""
1112
@safe_contextmanager decorator.
1213
@@ -49,21 +50,21 @@ def some_generator(<arguments>):
4950
raise AssertionError(f'{f.__name__} is not a generator.')
5051

5152
@wraps(f)
52-
def wrapper(*args, **kwargs) -> Iterator[T]:
53+
def wrapper(*args: Any, **kwargs: Any) -> Iterator[T]:
5354
iterator = f(*args, **kwargs)
5455

5556
try:
5657
yield next(iterator)
5758
finally:
58-
try:
59+
try: # noqa: SIM105
5960
next(iterator)
6061
except StopIteration:
6162
pass # this is intended
6263

63-
return contextmanager(wrapper) # type: ignore
64+
return contextmanager(wrapper)
6465

6566

66-
def safe_async_contextmanager(f: Callable[..., AsyncIterator[T]]) -> Callable[..., AsyncContextManager[T]]:
67+
def safe_async_contextmanager(f: Callable[..., AsyncIterator[T]]) -> Callable[..., AbstractAsyncContextManager[T]]:
6768
"""
6869
@safe_async_contextmanager decorator.
6970
@@ -100,7 +101,7 @@ async def some_async_generator(<arguments>):
100101
<body>
101102
finally:
102103
<cleanup>
103-
"""
104+
"""
104105

105106
if not isasyncgenfunction(f):
106107
if not isgeneratorfunction(f):
@@ -110,15 +111,15 @@ async def some_async_generator(<arguments>):
110111
f'So you need to use "safe_contextmanager" instead.')
111112

112113
@wraps(f)
113-
async def wrapper(*args, **kwargs) -> Iterator[T]:
114+
async def wrapper(*args: Any, **kwargs: Any) -> Iterator[T]:
114115
iterator = f(*args, **kwargs)
115116

116117
try:
117118
yield await anext(iterator)
118119
finally:
119-
try:
120+
try: # noqa: SIM105
120121
await anext(iterator)
121122
except StopAsyncIteration:
122-
pass # this is intended
123+
pass
123124

124-
return asynccontextmanager(wrapper) # type: ignore
125+
return asynccontextmanager(wrapper)

0 commit comments

Comments
 (0)