haiway 0.5.2__tar.gz → 0.6.0__tar.gz

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.
Files changed (49) hide show
  1. {haiway-0.5.2/src/haiway.egg-info → haiway-0.6.0}/PKG-INFO +3 -2
  2. {haiway-0.5.2 → haiway-0.6.0}/pyproject.toml +3 -2
  3. {haiway-0.5.2 → haiway-0.6.0}/src/haiway/context/access.py +1 -5
  4. {haiway-0.5.2 → haiway-0.6.0}/src/haiway/context/metrics.py +3 -1
  5. {haiway-0.5.2 → haiway-0.6.0}/src/haiway/helpers/tracing.py +4 -4
  6. {haiway-0.5.2 → haiway-0.6.0}/src/haiway/state/attributes.py +6 -1
  7. {haiway-0.5.2 → haiway-0.6.0}/src/haiway/state/structure.py +4 -3
  8. haiway-0.6.0/src/haiway/state/validation.py +295 -0
  9. {haiway-0.5.2 → haiway-0.6.0/src/haiway.egg-info}/PKG-INFO +3 -2
  10. {haiway-0.5.2 → haiway-0.6.0}/src/haiway.egg-info/requires.txt +2 -1
  11. {haiway-0.5.2 → haiway-0.6.0}/tests/test_async_queue.py +2 -1
  12. {haiway-0.5.2 → haiway-0.6.0}/tests/test_auto_retry.py +2 -1
  13. {haiway-0.5.2 → haiway-0.6.0}/tests/test_cache.py +2 -1
  14. {haiway-0.5.2 → haiway-0.6.0}/tests/test_context.py +2 -1
  15. {haiway-0.5.2 → haiway-0.6.0}/tests/test_state.py +14 -9
  16. {haiway-0.5.2 → haiway-0.6.0}/tests/test_streaming.py +2 -1
  17. {haiway-0.5.2 → haiway-0.6.0}/tests/test_timeout.py +2 -1
  18. haiway-0.5.2/src/haiway/state/validation.py +0 -149
  19. {haiway-0.5.2 → haiway-0.6.0}/LICENSE +0 -0
  20. {haiway-0.5.2 → haiway-0.6.0}/README.md +0 -0
  21. {haiway-0.5.2 → haiway-0.6.0}/setup.cfg +0 -0
  22. {haiway-0.5.2 → haiway-0.6.0}/src/haiway/__init__.py +11 -11
  23. {haiway-0.5.2 → haiway-0.6.0}/src/haiway/context/__init__.py +1 -1
  24. {haiway-0.5.2 → haiway-0.6.0}/src/haiway/context/disposables.py +0 -0
  25. {haiway-0.5.2 → haiway-0.6.0}/src/haiway/context/state.py +0 -0
  26. {haiway-0.5.2 → haiway-0.6.0}/src/haiway/context/tasks.py +0 -0
  27. {haiway-0.5.2 → haiway-0.6.0}/src/haiway/context/types.py +0 -0
  28. {haiway-0.5.2 → haiway-0.6.0}/src/haiway/helpers/__init__.py +1 -1
  29. {haiway-0.5.2 → haiway-0.6.0}/src/haiway/helpers/asynchrony.py +0 -0
  30. {haiway-0.5.2 → haiway-0.6.0}/src/haiway/helpers/caching.py +0 -0
  31. {haiway-0.5.2 → haiway-0.6.0}/src/haiway/helpers/retries.py +0 -0
  32. {haiway-0.5.2 → haiway-0.6.0}/src/haiway/helpers/throttling.py +0 -0
  33. {haiway-0.5.2 → haiway-0.6.0}/src/haiway/helpers/timeouted.py +0 -0
  34. {haiway-0.5.2 → haiway-0.6.0}/src/haiway/py.typed +0 -0
  35. {haiway-0.5.2 → haiway-0.6.0}/src/haiway/state/__init__.py +1 -1
  36. {haiway-0.5.2 → haiway-0.6.0}/src/haiway/types/__init__.py +2 -2
  37. {haiway-0.5.2 → haiway-0.6.0}/src/haiway/types/frozen.py +0 -0
  38. {haiway-0.5.2 → haiway-0.6.0}/src/haiway/types/missing.py +0 -0
  39. {haiway-0.5.2 → haiway-0.6.0}/src/haiway/utils/__init__.py +1 -1
  40. {haiway-0.5.2 → haiway-0.6.0}/src/haiway/utils/always.py +0 -0
  41. {haiway-0.5.2 → haiway-0.6.0}/src/haiway/utils/env.py +1 -1
  42. {haiway-0.5.2 → haiway-0.6.0}/src/haiway/utils/immutable.py +0 -0
  43. {haiway-0.5.2 → haiway-0.6.0}/src/haiway/utils/logs.py +0 -0
  44. {haiway-0.5.2 → haiway-0.6.0}/src/haiway/utils/mimic.py +0 -0
  45. {haiway-0.5.2 → haiway-0.6.0}/src/haiway/utils/noop.py +0 -0
  46. {haiway-0.5.2 → haiway-0.6.0}/src/haiway/utils/queue.py +0 -0
  47. {haiway-0.5.2 → haiway-0.6.0}/src/haiway.egg-info/SOURCES.txt +0 -0
  48. {haiway-0.5.2 → haiway-0.6.0}/src/haiway.egg-info/dependency_links.txt +0 -0
  49. {haiway-0.5.2 → haiway-0.6.0}/src/haiway.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: haiway
3
- Version: 0.5.2
3
+ Version: 0.6.0
4
4
  Summary: Framework for dependency injection and state management within structured concurrency model.
5
5
  Maintainer-email: Kacper Kaliński <kacper.kalinski@miquido.com>
6
6
  License: MIT License
@@ -36,7 +36,8 @@ Requires-Python: >=3.12
36
36
  Description-Content-Type: text/markdown
37
37
  License-File: LICENSE
38
38
  Provides-Extra: dev
39
- Requires-Dist: ruff~=0.5.0; extra == "dev"
39
+ Requires-Dist: haiway; extra == "dev"
40
+ Requires-Dist: ruff~=0.8.0; extra == "dev"
40
41
  Requires-Dist: pyright~=1.1; extra == "dev"
41
42
  Requires-Dist: bandit~=1.7; extra == "dev"
42
43
  Requires-Dist: pytest~=7.4; extra == "dev"
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
  [project]
6
6
  name = "haiway"
7
7
  description = "Framework for dependency injection and state management within structured concurrency model."
8
- version = "0.5.2"
8
+ version = "0.6.0"
9
9
  readme = "README.md"
10
10
  maintainers = [
11
11
  { name = "Kacper Kaliński", email = "kacper.kalinski@miquido.com" },
@@ -28,7 +28,8 @@ Repository = "https://github.com/miquido/haiway.git"
28
28
 
29
29
  [project.optional-dependencies]
30
30
  dev = [
31
- "ruff~=0.5.0",
31
+ "haiway",
32
+ "ruff~=0.8.0",
32
33
  "pyright~=1.1",
33
34
  "bandit~=1.7",
34
35
  "pytest~=7.4",
@@ -1,8 +1,4 @@
1
- from asyncio import (
2
- CancelledError,
3
- Task,
4
- current_task,
5
- )
1
+ from asyncio import CancelledError, Task, current_task
6
2
  from collections.abc import (
7
3
  AsyncGenerator,
8
4
  AsyncIterator,
@@ -21,8 +21,8 @@ from haiway.types import MISSING, Missing, not_missing
21
21
  from haiway.utils import freeze
22
22
 
23
23
  __all__ = [
24
- "ScopeMetrics",
25
24
  "MetricsContext",
25
+ "ScopeMetrics",
26
26
  ]
27
27
 
28
28
 
@@ -378,6 +378,7 @@ class MetricsContext:
378
378
  self._token is None and not self._metrics._finished # pyright: ignore[reportPrivateUsage]
379
379
  ), "MetricsContext reentrance is not allowed"
380
380
  self._token = MetricsContext._context.set(self._metrics)
381
+ self._metrics.log(INFO, "Started...")
381
382
 
382
383
  def __exit__(
383
384
  self,
@@ -391,3 +392,4 @@ class MetricsContext:
391
392
  MetricsContext._context.reset(self._token)
392
393
  self._metrics._finish() # pyright: ignore[reportPrivateUsage]
393
394
  self._token = None
395
+ self._metrics.log(INFO, f"...finished after {self._metrics.time:.2f}s")
@@ -1,5 +1,5 @@
1
1
  from asyncio import iscoroutinefunction
2
- from collections.abc import Callable, Coroutine
2
+ from collections.abc import Callable, Coroutine, Mapping, Sequence
3
3
  from typing import Any, Self, cast
4
4
 
5
5
  from haiway.context import ctx
@@ -8,9 +8,9 @@ from haiway.types import MISSING, Missing
8
8
  from haiway.utils import mimic_function
9
9
 
10
10
  __all__ = [
11
- "traced",
12
11
  "ArgumentsTrace",
13
12
  "ResultTrace",
13
+ "traced",
14
14
  ]
15
15
 
16
16
 
@@ -33,8 +33,8 @@ class ArgumentsTrace(State):
33
33
  kwargs=MISSING,
34
34
  )
35
35
 
36
- args: tuple[Any, ...] | Missing
37
- kwargs: dict[str, Any] | Missing
36
+ args: Sequence[Any] | Missing
37
+ kwargs: Mapping[str, Any] | Missing
38
38
 
39
39
 
40
40
  class ResultTrace(State):
@@ -17,8 +17,8 @@ from typing import (
17
17
  )
18
18
 
19
19
  __all__ = [
20
- "attribute_annotations",
21
20
  "AttributeAnnotation",
21
+ "attribute_annotations",
22
22
  ]
23
23
 
24
24
 
@@ -42,6 +42,11 @@ class AttributeAnnotation:
42
42
  and self.arguments == other.arguments
43
43
  )
44
44
 
45
+ def __str__(self) -> str:
46
+ return f"{getattr(self.origin, "__name__", str(self.origin))}" + (
47
+ ("[" + ", ".join(str(arg) for arg in self.arguments) + "]") if self.arguments else ""
48
+ )
49
+
45
50
 
46
51
  def attribute_annotations(
47
52
  cls: type[Any],
@@ -1,6 +1,6 @@
1
1
  from collections.abc import Callable
2
2
  from copy import deepcopy
3
- from types import GenericAlias
3
+ from types import EllipsisType, GenericAlias
4
4
  from typing import (
5
5
  Any,
6
6
  ClassVar,
@@ -15,7 +15,7 @@ from typing import (
15
15
  from weakref import WeakValueDictionary
16
16
 
17
17
  from haiway.state.attributes import AttributeAnnotation, attribute_annotations
18
- from haiway.state.validation import attribute_type_validator
18
+ from haiway.state.validation import attribute_validator
19
19
  from haiway.types import MISSING, Missing, not_missing
20
20
 
21
21
  __all__ = [
@@ -80,7 +80,7 @@ class StateMeta(type):
80
80
  attributes[key] = StateAttribute(
81
81
  annotation=annotation,
82
82
  default=getattr(state_type, key, MISSING),
83
- validator=attribute_type_validator(annotation),
83
+ validator=attribute_validator(annotation),
84
84
  )
85
85
 
86
86
  state_type.__ATTRIBUTES__ = attributes # pyright: ignore[reportAttributeAccessIssue]
@@ -104,6 +104,7 @@ class State(metaclass=StateMeta):
104
104
  Base class for immutable data structures.
105
105
  """
106
106
 
107
+ __IMMUTABLE__: ClassVar[EllipsisType] = ...
107
108
  __ATTRIBUTES__: ClassVar[dict[str, StateAttribute[Any]]]
108
109
 
109
110
  def __class_getitem__(
@@ -0,0 +1,295 @@
1
+ from collections.abc import Callable, Mapping, Sequence, Set
2
+ from enum import Enum
3
+ from types import MappingProxyType, NoneType, UnionType
4
+ from typing import Any, Literal, Protocol, Union
5
+
6
+ from haiway.state.attributes import AttributeAnnotation
7
+ from haiway.types import MISSING, Missing
8
+
9
+ __all__ = [
10
+ "attribute_validator",
11
+ ]
12
+
13
+
14
+ def attribute_validator(
15
+ annotation: AttributeAnnotation,
16
+ /,
17
+ ) -> Callable[[Any], Any]:
18
+ if validator := VALIDATORS.get(annotation.origin):
19
+ return validator(annotation)
20
+
21
+ elif hasattr(annotation.origin, "__IMMUTABLE__"):
22
+ return _prepare_validator_of_type(annotation)
23
+
24
+ elif issubclass(annotation.origin, Protocol):
25
+ return _prepare_validator_of_type(annotation)
26
+
27
+ elif issubclass(annotation.origin, Enum):
28
+ return _prepare_validator_of_type(annotation)
29
+
30
+ else:
31
+ raise TypeError(f"Unsupported type annotation: {annotation}")
32
+
33
+
34
+ def _prepare_validator_of_any(
35
+ annotation: AttributeAnnotation,
36
+ /,
37
+ ) -> Callable[[Any], Any]:
38
+ def validator(
39
+ value: Any,
40
+ ) -> Any:
41
+ return value # any is always valid
42
+
43
+ return validator
44
+
45
+
46
+ def _prepare_validator_of_none(
47
+ annotation: AttributeAnnotation,
48
+ /,
49
+ ) -> Callable[[Any], Any]:
50
+ def validator(
51
+ value: Any,
52
+ ) -> Any:
53
+ if value is None:
54
+ return value
55
+
56
+ else:
57
+ raise TypeError(f"'{value}' is not matching expected type of 'None'")
58
+
59
+ return validator
60
+
61
+
62
+ def _prepare_validator_of_missing(
63
+ annotation: AttributeAnnotation,
64
+ /,
65
+ ) -> Callable[[Any], Any]:
66
+ def validator(
67
+ value: Any,
68
+ ) -> Any:
69
+ if value is MISSING:
70
+ return value
71
+
72
+ else:
73
+ raise TypeError(f"'{value}' is not matching expected type of 'Missing'")
74
+
75
+ return validator
76
+
77
+
78
+ def _prepare_validator_of_literal(
79
+ annotation: AttributeAnnotation,
80
+ /,
81
+ ) -> Callable[[Any], Any]:
82
+ elements: list[Any] = annotation.arguments
83
+ formatted_type: str = str(annotation)
84
+
85
+ def validator(
86
+ value: Any,
87
+ ) -> Any:
88
+ if value in elements:
89
+ return value
90
+
91
+ else:
92
+ raise ValueError(f"'{value}' is not matching expected values of '{formatted_type}'")
93
+
94
+ return validator
95
+
96
+
97
+ def _prepare_validator_of_type(
98
+ annotation: AttributeAnnotation,
99
+ /,
100
+ ) -> Callable[[Any], Any]:
101
+ validated_type: type[Any] = annotation.origin
102
+ formatted_type: str = str(annotation)
103
+
104
+ def type_validator(
105
+ value: Any,
106
+ ) -> Any:
107
+ match value:
108
+ case value if isinstance(value, validated_type):
109
+ return value
110
+
111
+ case _:
112
+ raise TypeError(f"'{value}' is not matching expected type of '{formatted_type}'")
113
+
114
+ return type_validator
115
+
116
+
117
+ def _prepare_validator_of_set(
118
+ annotation: AttributeAnnotation,
119
+ /,
120
+ ) -> Callable[[Any], Any]:
121
+ element_validator: Callable[[Any], Any] = attribute_validator(annotation.arguments[0])
122
+ formatted_type: str = str(annotation)
123
+
124
+ def validator(
125
+ value: Any,
126
+ ) -> Any:
127
+ if isinstance(value, Set):
128
+ return frozenset(element_validator(element) for element in value) # pyright: ignore[reportUnknownVariableType]
129
+
130
+ else:
131
+ raise TypeError(f"'{value}' is not matching expected type of '{formatted_type}'")
132
+
133
+ return validator
134
+
135
+
136
+ def _prepare_validator_of_sequence(
137
+ annotation: AttributeAnnotation,
138
+ /,
139
+ ) -> Callable[[Any], Any]:
140
+ element_validator: Callable[[Any], Any] = attribute_validator(annotation.arguments[0])
141
+ formatted_type: str = str(annotation)
142
+
143
+ def validator(
144
+ value: Any,
145
+ ) -> Any:
146
+ match value:
147
+ case [*elements]:
148
+ return tuple(element_validator(element) for element in elements)
149
+
150
+ case _:
151
+ raise TypeError(f"'{value}' is not matching expected type of '{formatted_type}'")
152
+
153
+ return validator
154
+
155
+
156
+ def _prepare_validator_of_mapping(
157
+ annotation: AttributeAnnotation,
158
+ /,
159
+ ) -> Callable[[Any], Any]:
160
+ key_validator: Callable[[Any], Any] = attribute_validator(annotation.arguments[0])
161
+ value_validator: Callable[[Any], Any] = attribute_validator(annotation.arguments[1])
162
+ formatted_type: str = str(annotation)
163
+
164
+ def validator(
165
+ value: Any,
166
+ ) -> Any:
167
+ match value:
168
+ case {**elements}:
169
+ return MappingProxyType(
170
+ {key_validator(key): value_validator(value) for key, value in elements}
171
+ )
172
+
173
+ case _:
174
+ raise TypeError(f"'{value}' is not matching expected type of '{formatted_type}'")
175
+
176
+ return validator
177
+
178
+
179
+ def _prepare_validator_of_tuple(
180
+ annotation: AttributeAnnotation,
181
+ /,
182
+ ) -> Callable[[Any], Any]:
183
+ if annotation.arguments[-1].origin == Ellipsis:
184
+ element_validator: Callable[[Any], Any] = attribute_validator(annotation.arguments[0])
185
+ formatted_type: str = str(annotation)
186
+
187
+ def validator(
188
+ value: Any,
189
+ ) -> Any:
190
+ match value:
191
+ case [*elements]:
192
+ return tuple(element_validator(element) for element in elements)
193
+
194
+ case _:
195
+ raise TypeError(
196
+ f"'{value}' is not matching expected type of '{formatted_type}'"
197
+ )
198
+
199
+ return validator
200
+
201
+ else:
202
+ element_validators: list[Callable[[Any], Any]] = [
203
+ attribute_validator(alternative) for alternative in annotation.arguments
204
+ ]
205
+ elements_count: int = len(element_validators)
206
+ formatted_type: str = str(annotation)
207
+
208
+ def validator(
209
+ value: Any,
210
+ ) -> Any:
211
+ match value:
212
+ case [*elements]:
213
+ if len(elements) != elements_count:
214
+ raise ValueError(
215
+ f"'{value}' is not matching expected type of '{formatted_type}'"
216
+ )
217
+
218
+ return tuple(
219
+ element_validators[idx](value) for idx, value in enumerate(elements)
220
+ )
221
+
222
+ case _:
223
+ raise TypeError(
224
+ f"'{value}' is not matching expected type of '{formatted_type}'"
225
+ )
226
+
227
+ return validator
228
+
229
+
230
+ def _prepare_validator_of_union(
231
+ annotation: AttributeAnnotation,
232
+ /,
233
+ ) -> Callable[[Any], Any]:
234
+ validators: list[Callable[[Any], Any]] = [
235
+ attribute_validator(alternative) for alternative in annotation.arguments
236
+ ]
237
+ formatted_type: str = str(annotation)
238
+
239
+ def validator(
240
+ value: Any,
241
+ ) -> Any:
242
+ errors: list[Exception] = []
243
+ for validator in validators:
244
+ try:
245
+ return validator(value)
246
+
247
+ except Exception as exc:
248
+ errors.append(exc)
249
+
250
+ raise ExceptionGroup(
251
+ f"'{value}' is not matching expected type of '{formatted_type}'",
252
+ errors,
253
+ )
254
+
255
+ return validator
256
+
257
+
258
+ def _prepare_validator_of_callable(
259
+ annotation: AttributeAnnotation,
260
+ /,
261
+ ) -> Callable[[Any], Any]:
262
+ formatted_type: str = str(annotation)
263
+
264
+ def validator(
265
+ value: Any,
266
+ ) -> Any:
267
+ if callable(value):
268
+ return value
269
+
270
+ else:
271
+ raise TypeError(f"'{value}' is not matching expected type of '{formatted_type}'")
272
+
273
+ return validator
274
+
275
+
276
+ VALIDATORS: Mapping[Any, Callable[[AttributeAnnotation], Callable[[Any], Any]]] = {
277
+ Any: _prepare_validator_of_any,
278
+ NoneType: _prepare_validator_of_none,
279
+ Missing: _prepare_validator_of_missing,
280
+ type: _prepare_validator_of_type,
281
+ bool: _prepare_validator_of_type,
282
+ int: _prepare_validator_of_type,
283
+ float: _prepare_validator_of_type,
284
+ bytes: _prepare_validator_of_type,
285
+ str: _prepare_validator_of_type,
286
+ tuple: _prepare_validator_of_tuple,
287
+ frozenset: _prepare_validator_of_set,
288
+ Literal: _prepare_validator_of_literal,
289
+ Set: _prepare_validator_of_set,
290
+ Sequence: _prepare_validator_of_sequence,
291
+ Mapping: _prepare_validator_of_mapping,
292
+ Union: _prepare_validator_of_union,
293
+ UnionType: _prepare_validator_of_union,
294
+ Callable: _prepare_validator_of_callable,
295
+ }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: haiway
3
- Version: 0.5.2
3
+ Version: 0.6.0
4
4
  Summary: Framework for dependency injection and state management within structured concurrency model.
5
5
  Maintainer-email: Kacper Kaliński <kacper.kalinski@miquido.com>
6
6
  License: MIT License
@@ -36,7 +36,8 @@ Requires-Python: >=3.12
36
36
  Description-Content-Type: text/markdown
37
37
  License-File: LICENSE
38
38
  Provides-Extra: dev
39
- Requires-Dist: ruff~=0.5.0; extra == "dev"
39
+ Requires-Dist: haiway; extra == "dev"
40
+ Requires-Dist: ruff~=0.8.0; extra == "dev"
40
41
  Requires-Dist: pyright~=1.1; extra == "dev"
41
42
  Requires-Dist: bandit~=1.7; extra == "dev"
42
43
  Requires-Dist: pytest~=7.4; extra == "dev"
@@ -1,6 +1,7 @@
1
1
 
2
2
  [dev]
3
- ruff~=0.5.0
3
+ haiway
4
+ ruff~=0.8.0
4
5
  pyright~=1.1
5
6
  bandit~=1.7
6
7
  pytest~=7.4
@@ -1,8 +1,9 @@
1
1
  from asyncio import CancelledError
2
2
 
3
- from haiway import AsyncQueue, ctx
4
3
  from pytest import mark, raises
5
4
 
5
+ from haiway import AsyncQueue, ctx
6
+
6
7
 
7
8
  class FakeException(Exception):
8
9
  pass
@@ -2,9 +2,10 @@ from asyncio import CancelledError, Task, sleep
2
2
  from time import time
3
3
  from unittest import TestCase
4
4
 
5
- from haiway import retry
6
5
  from pytest import mark, raises
7
6
 
7
+ from haiway import retry
8
+
8
9
 
9
10
  class FakeException(Exception):
10
11
  pass
@@ -2,9 +2,10 @@ from asyncio import CancelledError, Task, sleep
2
2
  from collections.abc import Callable, Generator
3
3
  from time import sleep as sync_sleep
4
4
 
5
- from haiway import cache
6
5
  from pytest import fixture, mark, raises
7
6
 
7
+ from haiway import cache
8
+
8
9
 
9
10
  class FakeException(Exception):
10
11
  pass
@@ -1,8 +1,9 @@
1
1
  from asyncio import get_running_loop
2
2
 
3
- from haiway import MissingContext, ScopeMetrics, State, ctx
4
3
  from pytest import mark, raises
5
4
 
5
+ from haiway import MissingContext, ScopeMetrics, State, ctx
6
+
6
7
 
7
8
  class ExampleState(State):
8
9
  state: str = "default"
@@ -1,12 +1,14 @@
1
- from collections.abc import Callable
2
- from typing import Literal, Protocol, Self, TypedDict, runtime_checkable
1
+ from collections.abc import Callable, Sequence, Set
2
+ from enum import StrEnum
3
+ from typing import Literal, Protocol, Self, runtime_checkable
3
4
 
4
5
  from haiway import MISSING, Missing, State, frozenlist
5
6
 
6
7
 
7
8
  def test_basic_initializes_with_arguments() -> None:
8
- class DictTyped(TypedDict):
9
- value: str
9
+ class Selection(StrEnum):
10
+ A = "A"
11
+ B = "A"
10
12
 
11
13
  @runtime_checkable
12
14
  class Proto(Protocol):
@@ -15,7 +17,8 @@ def test_basic_initializes_with_arguments() -> None:
15
17
  class Basics(State):
16
18
  string: str
17
19
  literal: Literal["A", "B"]
18
- sequence: list[str]
20
+ sequence: Sequence[str]
21
+ string_set: Set[str]
19
22
  frozen: frozenlist[int]
20
23
  integer: int
21
24
  union: str | int
@@ -23,12 +26,13 @@ def test_basic_initializes_with_arguments() -> None:
23
26
  none: None
24
27
  function: Callable[[], None]
25
28
  proto: Proto
26
- dict_typed: DictTyped
29
+ selection: Selection
27
30
 
28
31
  basic = Basics(
29
32
  string="string",
30
33
  literal="A",
31
34
  sequence=["a", "b", "c"],
35
+ string_set={"a", "b"},
32
36
  frozen=(1, 2, 3),
33
37
  integer=0,
34
38
  union="union",
@@ -36,17 +40,18 @@ def test_basic_initializes_with_arguments() -> None:
36
40
  none=None,
37
41
  function=lambda: None,
38
42
  proto=lambda: None,
39
- dict_typed={"value": "42"},
43
+ selection=Selection.A,
40
44
  )
41
45
  assert basic.string == "string"
42
46
  assert basic.literal == "A"
43
- assert basic.sequence == ["a", "b", "c"]
47
+ assert basic.sequence == ("a", "b", "c")
48
+ assert basic.string_set == {"a", "b"}
44
49
  assert basic.frozen == (1, 2, 3)
45
50
  assert basic.integer == 0
46
51
  assert basic.union == "union"
47
52
  assert basic.optional == "optional"
48
53
  assert basic.none is None
49
- assert basic.dict_typed == {"value": "42"}
54
+ assert basic.selection == Selection.A
50
55
  assert callable(basic.function)
51
56
  assert isinstance(basic.proto, Proto)
52
57
 
@@ -1,10 +1,11 @@
1
1
  from asyncio import CancelledError, get_running_loop, sleep
2
2
  from collections.abc import AsyncGenerator, AsyncIterator
3
3
 
4
+ from pytest import mark, raises
5
+
4
6
  from haiway import ctx
5
7
  from haiway.context.metrics import ScopeMetrics
6
8
  from haiway.state.structure import State
7
- from pytest import mark, raises
8
9
 
9
10
 
10
11
  class FakeException(Exception):
@@ -1,8 +1,9 @@
1
1
  from asyncio import CancelledError, Task, sleep
2
2
 
3
- from haiway import timeout
4
3
  from pytest import mark, raises
5
4
 
5
+ from haiway import timeout
6
+
6
7
 
7
8
  class FakeException(Exception):
8
9
  pass
@@ -1,149 +0,0 @@
1
- import types
2
- import typing
3
- from collections.abc import Callable, Sequence
4
- from typing import Any
5
-
6
- from haiway import types as _types
7
- from haiway.state.attributes import AttributeAnnotation
8
-
9
- __all__ = [
10
- "attribute_type_validator",
11
- ]
12
-
13
-
14
- def attribute_type_validator( # noqa: PLR0911
15
- annotation: AttributeAnnotation,
16
- /,
17
- ) -> Callable[[Any], Any]:
18
- match annotation.origin:
19
- case types.NoneType:
20
- return _none_validator
21
-
22
- case _types.Missing:
23
- return _missing_validator
24
-
25
- case types.UnionType:
26
- return _prepare_union_validator(annotation.arguments)
27
-
28
- case typing.Literal:
29
- return _prepare_literal_validator(annotation.arguments)
30
-
31
- case typing.Any:
32
- return _any_validator
33
-
34
- # typed dicts fail on type checks
35
- case typed_dict if typing.is_typeddict(typed_dict):
36
- return _prepare_typed_dict_validator(typed_dict)
37
-
38
- case type() as other_type:
39
- return _prepare_type_validator(other_type)
40
-
41
- case other:
42
- raise TypeError(f"Unsupported type annotation: {other}")
43
-
44
-
45
- def _none_validator(
46
- value: Any,
47
- ) -> Any:
48
- match value:
49
- case None:
50
- return None
51
-
52
- case _:
53
- raise TypeError(f"Type '{type(value)}' is not matching expected type 'None'")
54
-
55
-
56
- def _missing_validator(
57
- value: Any,
58
- ) -> Any:
59
- match value:
60
- case _types.Missing():
61
- return _types.MISSING
62
-
63
- case _:
64
- raise TypeError(f"Type '{type(value)}' is not matching expected type 'Missing'")
65
-
66
-
67
- def _any_validator(
68
- value: Any,
69
- ) -> Any:
70
- return value # any is always valid
71
-
72
-
73
- def _prepare_union_validator(
74
- elements: Sequence[AttributeAnnotation],
75
- /,
76
- ) -> Callable[[Any], Any]:
77
- validators: list[Callable[[Any], Any]] = [
78
- attribute_type_validator(alternative) for alternative in elements
79
- ]
80
-
81
- def union_validator(
82
- value: Any,
83
- ) -> Any:
84
- errors: list[Exception] = []
85
- for validator in validators:
86
- try:
87
- return validator(value)
88
-
89
- except Exception as exc:
90
- errors.append(exc)
91
-
92
- raise ExceptionGroup("Multiple errors", errors)
93
-
94
- return union_validator
95
-
96
-
97
- def _prepare_literal_validator(
98
- elements: Sequence[Any],
99
- /,
100
- ) -> Callable[[Any], Any]:
101
- def literal_validator(
102
- value: Any,
103
- ) -> Any:
104
- if value in elements:
105
- return value
106
-
107
- else:
108
- raise ValueError(f"Value '{value}' is not matching expected '{elements}'")
109
-
110
- return literal_validator
111
-
112
-
113
- def _prepare_type_validator(
114
- validated_type: type[Any],
115
- /,
116
- ) -> Callable[[Any], Any]:
117
- def type_validator(
118
- value: Any,
119
- ) -> Any:
120
- match value:
121
- case value if isinstance(value, validated_type):
122
- return value
123
-
124
- case _:
125
- raise TypeError(
126
- f"Type '{type(value)}' is not matching expected type '{validated_type}'"
127
- )
128
-
129
- return type_validator
130
-
131
-
132
- def _prepare_typed_dict_validator(
133
- validated_type: type[Any],
134
- /,
135
- ) -> Callable[[Any], Any]:
136
- def typed_dict_validator(
137
- value: Any,
138
- ) -> Any:
139
- match value:
140
- case value if isinstance(value, dict):
141
- # for typed dicts check only if that is a dict
142
- return value # pyright: ignore[reportUnknownVariableType]
143
-
144
- case _:
145
- raise TypeError(
146
- f"Type '{type(value)}' is not matching expected type '{validated_type}'"
147
- )
148
-
149
- return typed_dict_validator
File without changes
File without changes
File without changes
@@ -43,16 +43,23 @@ from haiway.utils import (
43
43
  )
44
44
 
45
45
  __all__ = [
46
- "always",
46
+ "MISSING",
47
47
  "ArgumentsTrace",
48
+ "AsyncQueue",
49
+ "Disposable",
50
+ "Disposables",
51
+ "Missing",
52
+ "MissingContext",
53
+ "MissingState",
54
+ "ResultTrace",
55
+ "ScopeMetrics",
56
+ "State",
57
+ "always",
48
58
  "async_always",
49
59
  "async_noop",
50
60
  "asynchronous",
51
- "AsyncQueue",
52
61
  "cache",
53
62
  "ctx",
54
- "Disposable",
55
- "Disposables",
56
63
  "freeze",
57
64
  "frozenlist",
58
65
  "getenv_bool",
@@ -62,17 +69,10 @@ __all__ = [
62
69
  "is_missing",
63
70
  "load_env",
64
71
  "mimic_function",
65
- "Missing",
66
- "MISSING",
67
- "MissingContext",
68
- "MissingState",
69
72
  "noop",
70
73
  "not_missing",
71
- "ResultTrace",
72
74
  "retry",
73
- "ScopeMetrics",
74
75
  "setup_logging",
75
- "State",
76
76
  "throttle",
77
77
  "timeout",
78
78
  "traced",
@@ -4,10 +4,10 @@ from haiway.context.metrics import ScopeMetrics
4
4
  from haiway.context.types import MissingContext, MissingState
5
5
 
6
6
  __all__ = [
7
- "ctx",
8
7
  "Disposable",
9
8
  "Disposables",
10
9
  "MissingContext",
11
10
  "MissingState",
12
11
  "ScopeMetrics",
12
+ "ctx",
13
13
  ]
@@ -7,9 +7,9 @@ from haiway.helpers.tracing import ArgumentsTrace, ResultTrace, traced
7
7
 
8
8
  __all__ = [
9
9
  "ArgumentsTrace",
10
+ "ResultTrace",
10
11
  "asynchronous",
11
12
  "cache",
12
- "ResultTrace",
13
13
  "retry",
14
14
  "throttle",
15
15
  "timeout",
File without changes
@@ -2,7 +2,7 @@ from haiway.state.attributes import AttributeAnnotation, attribute_annotations
2
2
  from haiway.state.structure import State
3
3
 
4
4
  __all__ = [
5
- "attribute_annotations",
6
5
  "AttributeAnnotation",
7
6
  "State",
7
+ "attribute_annotations",
8
8
  ]
@@ -2,10 +2,10 @@ from haiway.types.frozen import frozenlist
2
2
  from haiway.types.missing import MISSING, Missing, is_missing, not_missing, when_missing
3
3
 
4
4
  __all__ = [
5
+ "MISSING",
6
+ "Missing",
5
7
  "frozenlist",
6
8
  "is_missing",
7
- "Missing",
8
- "MISSING",
9
9
  "not_missing",
10
10
  "when_missing",
11
11
  ]
@@ -7,10 +7,10 @@ from haiway.utils.noop import async_noop, noop
7
7
  from haiway.utils.queue import AsyncQueue
8
8
 
9
9
  __all__ = [
10
+ "AsyncQueue",
10
11
  "always",
11
12
  "async_always",
12
13
  "async_noop",
13
- "AsyncQueue",
14
14
  "freeze",
15
15
  "getenv_bool",
16
16
  "getenv_float",
@@ -3,8 +3,8 @@ from typing import overload
3
3
 
4
4
  __all__ = [
5
5
  "getenv_bool",
6
- "getenv_int",
7
6
  "getenv_float",
7
+ "getenv_int",
8
8
  "getenv_str",
9
9
  "load_env",
10
10
  ]
File without changes
File without changes
File without changes
File without changes