haiway 0.11.0__py3-none-any.whl → 0.12.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- haiway/__init__.py +2 -0
- haiway/context/access.py +95 -17
- haiway/context/disposables.py +27 -4
- haiway/context/identifier.py +76 -9
- haiway/context/logging.py +75 -9
- haiway/context/metrics.py +56 -7
- haiway/context/state.py +78 -10
- haiway/context/tasks.py +50 -7
- haiway/helpers/asynchrony.py +14 -0
- haiway/helpers/caching.py +30 -0
- haiway/helpers/metrics.py +20 -0
- haiway/helpers/throttling.py +16 -0
- haiway/helpers/timeouted.py +13 -0
- haiway/helpers/tracing.py +43 -18
- haiway/state/attributes.py +15 -1
- haiway/state/path.py +172 -12
- haiway/state/requirement.py +50 -7
- haiway/state/structure.py +50 -4
- haiway/state/validation.py +63 -8
- haiway/types/default.py +2 -0
- haiway/utils/__init__.py +9 -1
- haiway/utils/env.py +50 -0
- haiway/utils/queue.py +69 -10
- {haiway-0.11.0.dist-info → haiway-0.12.0.dist-info}/METADATA +1 -1
- haiway-0.12.0.dist-info/RECORD +42 -0
- haiway-0.11.0.dist-info/RECORD +0 -42
- {haiway-0.11.0.dist-info → haiway-0.12.0.dist-info}/WHEEL +0 -0
- {haiway-0.11.0.dist-info → haiway-0.12.0.dist-info}/licenses/LICENSE +0 -0
haiway/context/tasks.py
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
from asyncio import Task, TaskGroup, get_event_loop
|
2
2
|
from collections.abc import Callable, Coroutine
|
3
|
-
from contextvars import ContextVar, copy_context
|
3
|
+
from contextvars import ContextVar, Token, copy_context
|
4
4
|
from types import TracebackType
|
5
|
-
from typing import final
|
5
|
+
from typing import Any, final
|
6
6
|
|
7
7
|
__all__ = [
|
8
8
|
"TaskGroupContext",
|
@@ -33,15 +33,54 @@ class TaskGroupContext:
|
|
33
33
|
context=copy_context(),
|
34
34
|
)
|
35
35
|
|
36
|
+
__slots__ = (
|
37
|
+
"_group",
|
38
|
+
"_token",
|
39
|
+
)
|
40
|
+
|
36
41
|
def __init__(
|
37
42
|
self,
|
38
43
|
) -> None:
|
39
|
-
self._group: TaskGroup
|
44
|
+
self._group: TaskGroup
|
45
|
+
object.__setattr__(
|
46
|
+
self,
|
47
|
+
"_group",
|
48
|
+
TaskGroup(),
|
49
|
+
)
|
50
|
+
self._token: Token[TaskGroup] | None
|
51
|
+
object.__setattr__(
|
52
|
+
self,
|
53
|
+
"_token",
|
54
|
+
None,
|
55
|
+
)
|
56
|
+
|
57
|
+
def __setattr__(
|
58
|
+
self,
|
59
|
+
name: str,
|
60
|
+
value: Any,
|
61
|
+
) -> Any:
|
62
|
+
raise AttributeError(
|
63
|
+
f"Can't modify immutable {self.__class__.__qualname__},"
|
64
|
+
f" attribute - '{name}' cannot be modified"
|
65
|
+
)
|
66
|
+
|
67
|
+
def __delattr__(
|
68
|
+
self,
|
69
|
+
name: str,
|
70
|
+
) -> None:
|
71
|
+
raise AttributeError(
|
72
|
+
f"Can't modify immutable {self.__class__.__qualname__},"
|
73
|
+
f" attribute - '{name}' cannot be deleted"
|
74
|
+
)
|
40
75
|
|
41
76
|
async def __aenter__(self) -> None:
|
42
|
-
assert
|
77
|
+
assert self._token is None, "Context reentrance is not allowed" # nosec: B101
|
43
78
|
await self._group.__aenter__()
|
44
|
-
|
79
|
+
object.__setattr__(
|
80
|
+
self,
|
81
|
+
"_token",
|
82
|
+
TaskGroupContext._context.set(self._group),
|
83
|
+
)
|
45
84
|
|
46
85
|
async def __aexit__(
|
47
86
|
self,
|
@@ -49,9 +88,13 @@ class TaskGroupContext:
|
|
49
88
|
exc_val: BaseException | None,
|
50
89
|
exc_tb: TracebackType | None,
|
51
90
|
) -> None:
|
52
|
-
assert
|
91
|
+
assert self._token is not None, "Unbalanced context enter/exit" # nosec: B101
|
53
92
|
TaskGroupContext._context.reset(self._token)
|
54
|
-
|
93
|
+
object.__setattr__(
|
94
|
+
self,
|
95
|
+
"_token",
|
96
|
+
None,
|
97
|
+
)
|
55
98
|
|
56
99
|
try:
|
57
100
|
await self._group.__aexit__(
|
haiway/helpers/asynchrony.py
CHANGED
@@ -107,6 +107,20 @@ def asynchronous[**Args, Result](
|
|
107
107
|
|
108
108
|
|
109
109
|
class _ExecutorWrapper[**Args, Result]:
|
110
|
+
__slots__ = (
|
111
|
+
"__annotations__",
|
112
|
+
"__defaults__",
|
113
|
+
"__doc__",
|
114
|
+
"__globals__",
|
115
|
+
"__kwdefaults__",
|
116
|
+
"__name__",
|
117
|
+
"__qualname__",
|
118
|
+
"__wrapped__",
|
119
|
+
"_executor",
|
120
|
+
"_function",
|
121
|
+
"_loop",
|
122
|
+
)
|
123
|
+
|
110
124
|
def __init__(
|
111
125
|
self,
|
112
126
|
function: Callable[Args, Result],
|
haiway/helpers/caching.py
CHANGED
@@ -89,6 +89,21 @@ class _CacheEntry[Entry](NamedTuple):
|
|
89
89
|
|
90
90
|
|
91
91
|
class _SyncCache[**Args, Result]:
|
92
|
+
__slots__ = (
|
93
|
+
"__annotations__",
|
94
|
+
"__defaults__",
|
95
|
+
"__doc__",
|
96
|
+
"__globals__",
|
97
|
+
"__kwdefaults__",
|
98
|
+
"__name__",
|
99
|
+
"__qualname__",
|
100
|
+
"__wrapped__",
|
101
|
+
"_cached",
|
102
|
+
"_function",
|
103
|
+
"_limit",
|
104
|
+
"_next_expire_time",
|
105
|
+
)
|
106
|
+
|
92
107
|
def __init__(
|
93
108
|
self,
|
94
109
|
function: Callable[Args, Result],
|
@@ -206,6 +221,21 @@ class _SyncCache[**Args, Result]:
|
|
206
221
|
|
207
222
|
|
208
223
|
class _AsyncCache[**Args, Result]:
|
224
|
+
__slots__ = (
|
225
|
+
"__annotations__",
|
226
|
+
"__defaults__",
|
227
|
+
"__doc__",
|
228
|
+
"__globals__",
|
229
|
+
"__kwdefaults__",
|
230
|
+
"__name__",
|
231
|
+
"__qualname__",
|
232
|
+
"__wrapped__",
|
233
|
+
"_cached",
|
234
|
+
"_function",
|
235
|
+
"_limit",
|
236
|
+
"_next_expire_time",
|
237
|
+
)
|
238
|
+
|
209
239
|
def __init__(
|
210
240
|
self,
|
211
241
|
function: Callable[Args, Coroutine[None, None, Result]],
|
haiway/helpers/metrics.py
CHANGED
@@ -14,6 +14,14 @@ __all_ = [
|
|
14
14
|
|
15
15
|
|
16
16
|
class MetricsScopeStore:
|
17
|
+
__slots__ = (
|
18
|
+
"entered",
|
19
|
+
"exited",
|
20
|
+
"identifier",
|
21
|
+
"metrics",
|
22
|
+
"nested",
|
23
|
+
)
|
24
|
+
|
17
25
|
def __init__(
|
18
26
|
self,
|
19
27
|
identifier: ScopeIdentifier,
|
@@ -100,6 +108,11 @@ class MetricsHolder:
|
|
100
108
|
exit_scope=store_handler.exit_scope,
|
101
109
|
)
|
102
110
|
|
111
|
+
__slots__ = (
|
112
|
+
"root_scope",
|
113
|
+
"scopes",
|
114
|
+
)
|
115
|
+
|
103
116
|
def __init__(self) -> None:
|
104
117
|
self.root_scope: ScopeIdentifier | None = None
|
105
118
|
self.scopes: dict[ScopeIdentifier, MetricsScopeStore] = {}
|
@@ -187,6 +200,13 @@ class MetricsLogger:
|
|
187
200
|
exit_scope=logger_handler.exit_scope,
|
188
201
|
)
|
189
202
|
|
203
|
+
__slots__ = (
|
204
|
+
"items_limit",
|
205
|
+
"redact_content",
|
206
|
+
"root_scope",
|
207
|
+
"scopes",
|
208
|
+
)
|
209
|
+
|
190
210
|
def __init__(
|
191
211
|
self,
|
192
212
|
items_limit: int | None,
|
haiway/helpers/throttling.py
CHANGED
@@ -89,6 +89,22 @@ def throttle[**Args, Result](
|
|
89
89
|
|
90
90
|
|
91
91
|
class _AsyncThrottle[**Args, Result]:
|
92
|
+
__slots__ = (
|
93
|
+
"__annotations__",
|
94
|
+
"__defaults__",
|
95
|
+
"__doc__",
|
96
|
+
"__globals__",
|
97
|
+
"__kwdefaults__",
|
98
|
+
"__name__",
|
99
|
+
"__qualname__",
|
100
|
+
"__wrapped__",
|
101
|
+
"_entries",
|
102
|
+
"_function",
|
103
|
+
"_limit",
|
104
|
+
"_lock",
|
105
|
+
"_period",
|
106
|
+
)
|
107
|
+
|
92
108
|
def __init__(
|
93
109
|
self,
|
94
110
|
function: Callable[Args, Coroutine[None, None, Result]],
|
haiway/helpers/timeouted.py
CHANGED
@@ -45,6 +45,19 @@ def timeout[**Args, Result](
|
|
45
45
|
|
46
46
|
|
47
47
|
class _AsyncTimeout[**Args, Result]:
|
48
|
+
__slots__ = (
|
49
|
+
"__annotations__",
|
50
|
+
"__defaults__",
|
51
|
+
"__doc__",
|
52
|
+
"__globals__",
|
53
|
+
"__kwdefaults__",
|
54
|
+
"__name__",
|
55
|
+
"__qualname__",
|
56
|
+
"__wrapped__",
|
57
|
+
"_function",
|
58
|
+
"_timeout",
|
59
|
+
)
|
60
|
+
|
48
61
|
def __init__(
|
49
62
|
self,
|
50
63
|
function: Callable[Args, Coroutine[None, None, Result]],
|
haiway/helpers/tracing.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
from asyncio import iscoroutinefunction
|
2
2
|
from collections.abc import Callable, Coroutine, Mapping, Sequence
|
3
|
-
from typing import Any, Self, cast
|
3
|
+
from typing import Any, Self, cast, overload
|
4
4
|
|
5
5
|
from haiway.context import ctx
|
6
6
|
from haiway.state import State
|
@@ -61,28 +61,53 @@ class ResultTrace(State):
|
|
61
61
|
result: Any | Missing
|
62
62
|
|
63
63
|
|
64
|
+
@overload
|
64
65
|
def traced[**Args, Result](
|
65
66
|
function: Callable[Args, Result],
|
66
67
|
/,
|
67
|
-
) -> Callable[Args, Result]:
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
),
|
76
|
-
)
|
68
|
+
) -> Callable[Args, Result]: ...
|
69
|
+
|
70
|
+
|
71
|
+
@overload
|
72
|
+
def traced[**Args, Result](
|
73
|
+
*,
|
74
|
+
label: str,
|
75
|
+
) -> Callable[[Callable[Args, Result]], Callable[Args, Result]]: ...
|
77
76
|
|
78
|
-
else:
|
79
|
-
return _traced_sync(
|
80
|
-
function,
|
81
|
-
label=function.__name__,
|
82
|
-
)
|
83
77
|
|
84
|
-
|
85
|
-
|
78
|
+
def traced[**Args, Result](
|
79
|
+
function: Callable[Args, Result] | None = None,
|
80
|
+
/,
|
81
|
+
*,
|
82
|
+
label: str | None = None,
|
83
|
+
) -> Callable[[Callable[Args, Result]], Callable[Args, Result]] | Callable[Args, Result]:
|
84
|
+
def wrap(
|
85
|
+
wrapped: Callable[Args, Result],
|
86
|
+
) -> Callable[Args, Result]:
|
87
|
+
if __debug__:
|
88
|
+
if iscoroutinefunction(wrapped):
|
89
|
+
return cast(
|
90
|
+
Callable[Args, Result],
|
91
|
+
_traced_async(
|
92
|
+
wrapped,
|
93
|
+
label=label or wrapped.__name__,
|
94
|
+
),
|
95
|
+
)
|
96
|
+
|
97
|
+
else:
|
98
|
+
return _traced_sync(
|
99
|
+
wrapped,
|
100
|
+
label=label or wrapped.__name__,
|
101
|
+
)
|
102
|
+
|
103
|
+
else: # do not trace on non debug runs
|
104
|
+
return wrapped
|
105
|
+
|
106
|
+
if function := function:
|
107
|
+
return wrap(wrapped=function)
|
108
|
+
|
109
|
+
else:
|
110
|
+
return wrap
|
86
111
|
|
87
112
|
|
88
113
|
def _traced_sync[**Args, Result](
|
haiway/state/attributes.py
CHANGED
@@ -14,6 +14,7 @@ from typing import (
|
|
14
14
|
TypeVar,
|
15
15
|
TypeVarTuple,
|
16
16
|
_GenericAlias, # pyright: ignore
|
17
|
+
final,
|
17
18
|
get_args,
|
18
19
|
get_origin,
|
19
20
|
get_type_hints,
|
@@ -31,7 +32,15 @@ __all__ = [
|
|
31
32
|
]
|
32
33
|
|
33
34
|
|
35
|
+
@final
|
34
36
|
class AttributeAnnotation:
|
37
|
+
__slots__ = (
|
38
|
+
"arguments",
|
39
|
+
"extra",
|
40
|
+
"origin",
|
41
|
+
"required",
|
42
|
+
)
|
43
|
+
|
35
44
|
def __init__(
|
36
45
|
self,
|
37
46
|
*,
|
@@ -49,6 +58,7 @@ class AttributeAnnotation:
|
|
49
58
|
self.arguments = arguments
|
50
59
|
|
51
60
|
self.required: bool = required
|
61
|
+
|
52
62
|
self.extra: Mapping[str, Any]
|
53
63
|
if extra is None:
|
54
64
|
self.extra = {}
|
@@ -61,7 +71,11 @@ class AttributeAnnotation:
|
|
61
71
|
required: bool,
|
62
72
|
/,
|
63
73
|
) -> Self:
|
64
|
-
|
74
|
+
object.__setattr__(
|
75
|
+
self,
|
76
|
+
"required",
|
77
|
+
self.required and required,
|
78
|
+
)
|
65
79
|
|
66
80
|
return self
|
67
81
|
|
haiway/state/path.py
CHANGED
@@ -41,6 +41,12 @@ class AttributePathComponent(ABC):
|
|
41
41
|
|
42
42
|
@final
|
43
43
|
class PropertyAttributePathComponent(AttributePathComponent):
|
44
|
+
__slots__ = (
|
45
|
+
"_access",
|
46
|
+
"_assigning",
|
47
|
+
"_name",
|
48
|
+
)
|
49
|
+
|
44
50
|
def __init__[Root, Attribute](
|
45
51
|
self,
|
46
52
|
root: type[Root],
|
@@ -104,9 +110,43 @@ class PropertyAttributePathComponent(AttributePathComponent):
|
|
104
110
|
|
105
111
|
return updated # pyright: ignore[reportUnknownVariableType]
|
106
112
|
|
107
|
-
self._access: Callable[[Any], Any]
|
108
|
-
|
109
|
-
|
113
|
+
self._access: Callable[[Any], Any]
|
114
|
+
object.__setattr__(
|
115
|
+
self,
|
116
|
+
"_access",
|
117
|
+
access,
|
118
|
+
)
|
119
|
+
self._assigning: Callable[[Any, Any], Any]
|
120
|
+
object.__setattr__(
|
121
|
+
self,
|
122
|
+
"_assigning",
|
123
|
+
assigning,
|
124
|
+
)
|
125
|
+
self._name: str
|
126
|
+
object.__setattr__(
|
127
|
+
self,
|
128
|
+
"_name",
|
129
|
+
name,
|
130
|
+
)
|
131
|
+
|
132
|
+
def __setattr__(
|
133
|
+
self,
|
134
|
+
name: str,
|
135
|
+
value: Any,
|
136
|
+
) -> Any:
|
137
|
+
raise AttributeError(
|
138
|
+
f"Can't modify immutable {self.__class__.__qualname__},"
|
139
|
+
f" attribute - '{name}' cannot be modified"
|
140
|
+
)
|
141
|
+
|
142
|
+
def __delattr__(
|
143
|
+
self,
|
144
|
+
name: str,
|
145
|
+
) -> None:
|
146
|
+
raise AttributeError(
|
147
|
+
f"Can't modify immutable {self.__class__.__qualname__},"
|
148
|
+
f" attribute - '{name}' cannot be deleted"
|
149
|
+
)
|
110
150
|
|
111
151
|
def path_str(
|
112
152
|
self,
|
@@ -137,6 +177,12 @@ class PropertyAttributePathComponent(AttributePathComponent):
|
|
137
177
|
|
138
178
|
@final
|
139
179
|
class SequenceItemAttributePathComponent(AttributePathComponent):
|
180
|
+
__slots__ = (
|
181
|
+
"_access",
|
182
|
+
"_assigning",
|
183
|
+
"_index",
|
184
|
+
)
|
185
|
+
|
140
186
|
def __init__[Root: Sequence[Any], Attribute](
|
141
187
|
self,
|
142
188
|
root: type[Root],
|
@@ -182,9 +228,43 @@ class SequenceItemAttributePathComponent(AttributePathComponent):
|
|
182
228
|
temp_list[index] = value
|
183
229
|
return subject.__class__(temp_list) # pyright: ignore[reportCallIssue, reportUnknownVariableType, reportUnknownMemberType]
|
184
230
|
|
185
|
-
self._access: Callable[[Any], Any]
|
186
|
-
|
187
|
-
|
231
|
+
self._access: Callable[[Any], Any]
|
232
|
+
object.__setattr__(
|
233
|
+
self,
|
234
|
+
"_access",
|
235
|
+
access,
|
236
|
+
)
|
237
|
+
self._assigning: Callable[[Any, Any], Any]
|
238
|
+
object.__setattr__(
|
239
|
+
self,
|
240
|
+
"_assigning",
|
241
|
+
assigning,
|
242
|
+
)
|
243
|
+
self._index: Any
|
244
|
+
object.__setattr__(
|
245
|
+
self,
|
246
|
+
"_index",
|
247
|
+
index,
|
248
|
+
)
|
249
|
+
|
250
|
+
def __setattr__(
|
251
|
+
self,
|
252
|
+
name: str,
|
253
|
+
value: Any,
|
254
|
+
) -> Any:
|
255
|
+
raise AttributeError(
|
256
|
+
f"Can't modify immutable {self.__class__.__qualname__},"
|
257
|
+
f" attribute - '{name}' cannot be modified"
|
258
|
+
)
|
259
|
+
|
260
|
+
def __delattr__(
|
261
|
+
self,
|
262
|
+
name: str,
|
263
|
+
) -> None:
|
264
|
+
raise AttributeError(
|
265
|
+
f"Can't modify immutable {self.__class__.__qualname__},"
|
266
|
+
f" attribute - '{name}' cannot be deleted"
|
267
|
+
)
|
188
268
|
|
189
269
|
def path_str(
|
190
270
|
self,
|
@@ -211,6 +291,12 @@ class SequenceItemAttributePathComponent(AttributePathComponent):
|
|
211
291
|
|
212
292
|
@final
|
213
293
|
class MappingItemAttributePathComponent(AttributePathComponent):
|
294
|
+
__slots__ = (
|
295
|
+
"_access",
|
296
|
+
"_assigning",
|
297
|
+
"_key",
|
298
|
+
)
|
299
|
+
|
214
300
|
def __init__[Root: Mapping[Any, Any], Attribute](
|
215
301
|
self,
|
216
302
|
root: type[Root],
|
@@ -256,9 +342,43 @@ class MappingItemAttributePathComponent(AttributePathComponent):
|
|
256
342
|
temp_dict[key] = value
|
257
343
|
return subject.__class__(temp_dict) # pyright: ignore[reportCallIssue, reportUnknownVariableType, reportUnknownMemberType]
|
258
344
|
|
259
|
-
self._access: Callable[[Any], Any]
|
260
|
-
|
261
|
-
|
345
|
+
self._access: Callable[[Any], Any]
|
346
|
+
object.__setattr__(
|
347
|
+
self,
|
348
|
+
"_access",
|
349
|
+
access,
|
350
|
+
)
|
351
|
+
self._assigning: Callable[[Any, Any], Any]
|
352
|
+
object.__setattr__(
|
353
|
+
self,
|
354
|
+
"_assigning",
|
355
|
+
assigning,
|
356
|
+
)
|
357
|
+
self._key: Any
|
358
|
+
object.__setattr__(
|
359
|
+
self,
|
360
|
+
"_key",
|
361
|
+
key,
|
362
|
+
)
|
363
|
+
|
364
|
+
def __setattr__(
|
365
|
+
self,
|
366
|
+
name: str,
|
367
|
+
value: Any,
|
368
|
+
) -> Any:
|
369
|
+
raise AttributeError(
|
370
|
+
f"Can't modify immutable {self.__class__.__qualname__},"
|
371
|
+
f" attribute - '{name}' cannot be modified"
|
372
|
+
)
|
373
|
+
|
374
|
+
def __delattr__(
|
375
|
+
self,
|
376
|
+
name: str,
|
377
|
+
) -> None:
|
378
|
+
raise AttributeError(
|
379
|
+
f"Can't modify immutable {self.__class__.__qualname__},"
|
380
|
+
f" attribute - '{name}' cannot be deleted"
|
381
|
+
)
|
262
382
|
|
263
383
|
def path_str(
|
264
384
|
self,
|
@@ -285,6 +405,12 @@ class MappingItemAttributePathComponent(AttributePathComponent):
|
|
285
405
|
|
286
406
|
@final
|
287
407
|
class AttributePath[Root, Attribute]:
|
408
|
+
__slots__ = (
|
409
|
+
"__attribute__",
|
410
|
+
"__components__",
|
411
|
+
"__root__",
|
412
|
+
)
|
413
|
+
|
288
414
|
@overload
|
289
415
|
def __init__(
|
290
416
|
self,
|
@@ -311,9 +437,43 @@ class AttributePath[Root, Attribute]:
|
|
311
437
|
attribute: type[Attribute],
|
312
438
|
) -> None:
|
313
439
|
assert components or root == attribute # nosec: B101
|
314
|
-
self.__root__: type[Root]
|
315
|
-
|
316
|
-
|
440
|
+
self.__root__: type[Root]
|
441
|
+
object.__setattr__(
|
442
|
+
self,
|
443
|
+
"__root__",
|
444
|
+
root,
|
445
|
+
)
|
446
|
+
self.__attribute__: type[Attribute]
|
447
|
+
object.__setattr__(
|
448
|
+
self,
|
449
|
+
"__attribute__",
|
450
|
+
attribute,
|
451
|
+
)
|
452
|
+
self.__components__: tuple[AttributePathComponent, ...]
|
453
|
+
object.__setattr__(
|
454
|
+
self,
|
455
|
+
"__components__",
|
456
|
+
components,
|
457
|
+
)
|
458
|
+
|
459
|
+
def __setattr__(
|
460
|
+
self,
|
461
|
+
name: str,
|
462
|
+
value: Any,
|
463
|
+
) -> Any:
|
464
|
+
raise AttributeError(
|
465
|
+
f"Can't modify immutable {self.__class__.__qualname__},"
|
466
|
+
f" attribute - '{name}' cannot be modified"
|
467
|
+
)
|
468
|
+
|
469
|
+
def __delattr__(
|
470
|
+
self,
|
471
|
+
name: str,
|
472
|
+
) -> None:
|
473
|
+
raise AttributeError(
|
474
|
+
f"Can't modify immutable {self.__class__.__qualname__},"
|
475
|
+
f" attribute - '{name}' cannot be deleted"
|
476
|
+
)
|
317
477
|
|
318
478
|
@property
|
319
479
|
def components(self) -> Sequence[str]:
|
haiway/state/requirement.py
CHANGED
@@ -2,7 +2,6 @@ from collections.abc import Callable, Collection, Iterable
|
|
2
2
|
from typing import Any, Literal, Self, cast, final
|
3
3
|
|
4
4
|
from haiway.state.path import AttributePath
|
5
|
-
from haiway.utils import freeze
|
6
5
|
|
7
6
|
__all__ = [
|
8
7
|
"AttributeRequirement",
|
@@ -142,6 +141,13 @@ class AttributeRequirement[Root]:
|
|
142
141
|
check=check_contained_in,
|
143
142
|
)
|
144
143
|
|
144
|
+
__slots__ = (
|
145
|
+
"_check",
|
146
|
+
"lhs",
|
147
|
+
"operator",
|
148
|
+
"rhs",
|
149
|
+
)
|
150
|
+
|
145
151
|
def __init__(
|
146
152
|
self,
|
147
153
|
lhs: Any,
|
@@ -157,7 +163,12 @@ class AttributeRequirement[Root]:
|
|
157
163
|
rhs: Any,
|
158
164
|
check: Callable[[Root], None],
|
159
165
|
) -> None:
|
160
|
-
self.lhs: Any
|
166
|
+
self.lhs: Any
|
167
|
+
object.__setattr__(
|
168
|
+
self,
|
169
|
+
"lhs",
|
170
|
+
lhs,
|
171
|
+
)
|
161
172
|
self.operator: Literal[
|
162
173
|
"equal",
|
163
174
|
"not_equal",
|
@@ -166,11 +177,24 @@ class AttributeRequirement[Root]:
|
|
166
177
|
"contained_in",
|
167
178
|
"and",
|
168
179
|
"or",
|
169
|
-
]
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
180
|
+
]
|
181
|
+
object.__setattr__(
|
182
|
+
self,
|
183
|
+
"operator",
|
184
|
+
operator,
|
185
|
+
)
|
186
|
+
self.rhs: Any
|
187
|
+
object.__setattr__(
|
188
|
+
self,
|
189
|
+
"rhs",
|
190
|
+
rhs,
|
191
|
+
)
|
192
|
+
self._check: Callable[[Root], None]
|
193
|
+
object.__setattr__(
|
194
|
+
self,
|
195
|
+
"_check",
|
196
|
+
check,
|
197
|
+
)
|
174
198
|
|
175
199
|
def __and__(
|
176
200
|
self,
|
@@ -227,3 +251,22 @@ class AttributeRequirement[Root]:
|
|
227
251
|
values: Iterable[Root],
|
228
252
|
) -> list[Root]:
|
229
253
|
return [value for value in values if self.check(value, raise_exception=False)]
|
254
|
+
|
255
|
+
def __setattr__(
|
256
|
+
self,
|
257
|
+
name: str,
|
258
|
+
value: Any,
|
259
|
+
) -> Any:
|
260
|
+
raise AttributeError(
|
261
|
+
f"Can't modify immutable {self.__class__.__qualname__},"
|
262
|
+
f" attribute - '{name}' cannot be modified"
|
263
|
+
)
|
264
|
+
|
265
|
+
def __delattr__(
|
266
|
+
self,
|
267
|
+
name: str,
|
268
|
+
) -> None:
|
269
|
+
raise AttributeError(
|
270
|
+
f"Can't modify immutable {self.__class__.__qualname__},"
|
271
|
+
f" attribute - '{name}' cannot be deleted"
|
272
|
+
)
|