haiway 0.7.0__tar.gz → 0.7.1__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 (51) hide show
  1. {haiway-0.7.0/src/haiway.egg-info → haiway-0.7.1}/PKG-INFO +1 -1
  2. {haiway-0.7.0 → haiway-0.7.1}/pyproject.toml +1 -1
  3. {haiway-0.7.0 → haiway-0.7.1}/src/haiway/state/structure.py +88 -1
  4. {haiway-0.7.0 → haiway-0.7.1/src/haiway.egg-info}/PKG-INFO +1 -1
  5. {haiway-0.7.0 → haiway-0.7.1}/tests/test_state.py +40 -1
  6. {haiway-0.7.0 → haiway-0.7.1}/LICENSE +0 -0
  7. {haiway-0.7.0 → haiway-0.7.1}/README.md +0 -0
  8. {haiway-0.7.0 → haiway-0.7.1}/setup.cfg +0 -0
  9. {haiway-0.7.0 → haiway-0.7.1}/src/haiway/__init__.py +0 -0
  10. {haiway-0.7.0 → haiway-0.7.1}/src/haiway/context/__init__.py +0 -0
  11. {haiway-0.7.0 → haiway-0.7.1}/src/haiway/context/access.py +0 -0
  12. {haiway-0.7.0 → haiway-0.7.1}/src/haiway/context/disposables.py +0 -0
  13. {haiway-0.7.0 → haiway-0.7.1}/src/haiway/context/metrics.py +0 -0
  14. {haiway-0.7.0 → haiway-0.7.1}/src/haiway/context/state.py +0 -0
  15. {haiway-0.7.0 → haiway-0.7.1}/src/haiway/context/tasks.py +0 -0
  16. {haiway-0.7.0 → haiway-0.7.1}/src/haiway/context/types.py +0 -0
  17. {haiway-0.7.0 → haiway-0.7.1}/src/haiway/helpers/__init__.py +0 -0
  18. {haiway-0.7.0 → haiway-0.7.1}/src/haiway/helpers/asynchrony.py +0 -0
  19. {haiway-0.7.0 → haiway-0.7.1}/src/haiway/helpers/caching.py +0 -0
  20. {haiway-0.7.0 → haiway-0.7.1}/src/haiway/helpers/retries.py +0 -0
  21. {haiway-0.7.0 → haiway-0.7.1}/src/haiway/helpers/throttling.py +0 -0
  22. {haiway-0.7.0 → haiway-0.7.1}/src/haiway/helpers/timeouted.py +0 -0
  23. {haiway-0.7.0 → haiway-0.7.1}/src/haiway/helpers/tracing.py +0 -0
  24. {haiway-0.7.0 → haiway-0.7.1}/src/haiway/py.typed +0 -0
  25. {haiway-0.7.0 → haiway-0.7.1}/src/haiway/state/__init__.py +0 -0
  26. {haiway-0.7.0 → haiway-0.7.1}/src/haiway/state/attributes.py +0 -0
  27. {haiway-0.7.0 → haiway-0.7.1}/src/haiway/state/path.py +0 -0
  28. {haiway-0.7.0 → haiway-0.7.1}/src/haiway/state/requirement.py +0 -0
  29. {haiway-0.7.0 → haiway-0.7.1}/src/haiway/state/validation.py +0 -0
  30. {haiway-0.7.0 → haiway-0.7.1}/src/haiway/types/__init__.py +0 -0
  31. {haiway-0.7.0 → haiway-0.7.1}/src/haiway/types/frozen.py +0 -0
  32. {haiway-0.7.0 → haiway-0.7.1}/src/haiway/types/missing.py +0 -0
  33. {haiway-0.7.0 → haiway-0.7.1}/src/haiway/utils/__init__.py +0 -0
  34. {haiway-0.7.0 → haiway-0.7.1}/src/haiway/utils/always.py +0 -0
  35. {haiway-0.7.0 → haiway-0.7.1}/src/haiway/utils/env.py +0 -0
  36. {haiway-0.7.0 → haiway-0.7.1}/src/haiway/utils/immutable.py +0 -0
  37. {haiway-0.7.0 → haiway-0.7.1}/src/haiway/utils/logs.py +0 -0
  38. {haiway-0.7.0 → haiway-0.7.1}/src/haiway/utils/mimic.py +0 -0
  39. {haiway-0.7.0 → haiway-0.7.1}/src/haiway/utils/noop.py +0 -0
  40. {haiway-0.7.0 → haiway-0.7.1}/src/haiway/utils/queue.py +0 -0
  41. {haiway-0.7.0 → haiway-0.7.1}/src/haiway.egg-info/SOURCES.txt +0 -0
  42. {haiway-0.7.0 → haiway-0.7.1}/src/haiway.egg-info/dependency_links.txt +0 -0
  43. {haiway-0.7.0 → haiway-0.7.1}/src/haiway.egg-info/requires.txt +0 -0
  44. {haiway-0.7.0 → haiway-0.7.1}/src/haiway.egg-info/top_level.txt +0 -0
  45. {haiway-0.7.0 → haiway-0.7.1}/tests/test_async_queue.py +0 -0
  46. {haiway-0.7.0 → haiway-0.7.1}/tests/test_attribute_path.py +0 -0
  47. {haiway-0.7.0 → haiway-0.7.1}/tests/test_auto_retry.py +0 -0
  48. {haiway-0.7.0 → haiway-0.7.1}/tests/test_cache.py +0 -0
  49. {haiway-0.7.0 → haiway-0.7.1}/tests/test_context.py +0 -0
  50. {haiway-0.7.0 → haiway-0.7.1}/tests/test_streaming.py +0 -0
  51. {haiway-0.7.0 → haiway-0.7.1}/tests/test_timeout.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: haiway
3
- Version: 0.7.0
3
+ Version: 0.7.1
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
@@ -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.7.0"
8
+ version = "0.7.1"
9
9
  readme = "README.md"
10
10
  maintainers = [
11
11
  { name = "Kacper Kaliński", email = "kacper.kalinski@miquido.com" },
@@ -1,3 +1,5 @@
1
+ import typing
2
+ from collections.abc import Mapping
1
3
  from copy import deepcopy
2
4
  from types import EllipsisType, GenericAlias
3
5
  from typing import (
@@ -89,10 +91,11 @@ class StateMeta(type):
89
91
  ),
90
92
  )
91
93
 
94
+ state_type.__PARAMETERS__ = type_parameters # pyright: ignore[reportAttributeAccessIssue]
92
95
  state_type.__ATTRIBUTES__ = attributes # pyright: ignore[reportAttributeAccessIssue]
93
96
  state_type.__slots__ = frozenset(attributes.keys()) # pyright: ignore[reportAttributeAccessIssue]
94
97
  state_type.__match_args__ = state_type.__slots__ # pyright: ignore[reportAttributeAccessIssue]
95
- state_type._ = AttributePath(state_type, attribute=state_type) # pyright: ignore[reportUnknownMemberType, reportAttributeAccessIssue]
98
+ state_type._ = AttributePath(state_type, attribute=state_type) # pyright: ignore[reportCallIssue, reportUnknownMemberType, reportAttributeAccessIssue]
96
99
 
97
100
  return state_type
98
101
 
@@ -102,6 +105,88 @@ class StateMeta(type):
102
105
  /,
103
106
  ) -> Any: ...
104
107
 
108
+ def __instancecheck__(
109
+ self,
110
+ instance: Any,
111
+ ) -> bool:
112
+ # check for type match
113
+ if self.__subclasscheck__(type(instance)): # pyright: ignore[reportUnknownArgumentType]
114
+ return True
115
+
116
+ # otherwise check if we are dealing with unparametrized base
117
+ # against the parametrized one, our generic subtypes have base of unparametrized type
118
+ if type(instance) not in self.__bases__:
119
+ return False
120
+
121
+ try:
122
+ # validate instance to check unparametrized fields
123
+ _ = self(**vars(instance))
124
+
125
+ except Exception:
126
+ return False
127
+
128
+ else:
129
+ return True
130
+
131
+ def __subclasscheck__( # noqa: C901, PLR0911, PLR0912
132
+ self,
133
+ subclass: type[Any],
134
+ ) -> bool:
135
+ # check if we are the same class for early exit
136
+ if self == subclass:
137
+ return True
138
+
139
+ # then check if we are parametrized
140
+ checked_parameters: Mapping[str, Any] | None = getattr(
141
+ self,
142
+ "__PARAMETERS__",
143
+ None,
144
+ )
145
+ if checked_parameters is None:
146
+ # if we are not parametrized allow any subclass
147
+ return self in subclass.__bases__
148
+
149
+ # verify if we have common base next - our generic subtypes have the same base
150
+ if self.__bases__ == subclass.__bases__:
151
+ # if we have the same bases we have different generic subtypes
152
+ # we can verify all of the attributes to check if we have common base
153
+ available_parameters: Mapping[str, Any] | None = getattr(
154
+ subclass,
155
+ "__PARAMETERS__",
156
+ None,
157
+ )
158
+
159
+ if available_parameters is None:
160
+ # if we have no parameters at this stage this is a serious bug
161
+ raise RuntimeError("Invalid type parametrization for %s", subclass)
162
+
163
+ for key, param in checked_parameters.items():
164
+ match available_parameters.get(key):
165
+ case None: # if any parameter is missing we should not be there already
166
+ return False
167
+
168
+ case typing.Any:
169
+ continue # Any ignores type checks
170
+
171
+ case checked:
172
+ if param is Any:
173
+ continue # Any ignores type checks
174
+
175
+ elif issubclass(checked, param):
176
+ continue # if we have matching type we are fine
177
+
178
+ else:
179
+ return False # types are not matching
180
+
181
+ return True # when all parameters were matching we have matching subclass
182
+
183
+ elif subclass in self.__bases__: # our generic subtypes have base of unparametrized type
184
+ # if subclass parameters were not provided then we can be valid ony if all were Any
185
+ return all(param is Any for param in checked_parameters.values())
186
+
187
+ else:
188
+ return False # we have different base / comparing to not parametrized
189
+
105
190
 
106
191
  _types_cache: WeakValueDictionary[
107
192
  tuple[
@@ -119,6 +204,7 @@ class State(metaclass=StateMeta):
119
204
 
120
205
  _: ClassVar[Self]
121
206
  __IMMUTABLE__: ClassVar[EllipsisType] = ...
207
+ __PARAMETERS__: ClassVar[Mapping[str, Any] | None] = None
122
208
  __ATTRIBUTES__: ClassVar[dict[str, StateAttribute[Any]]]
123
209
 
124
210
  @classmethod
@@ -127,6 +213,7 @@ class State(metaclass=StateMeta):
127
213
  type_argument: tuple[type[Any], ...] | type[Any],
128
214
  ) -> type[Self]:
129
215
  assert Generic in cls.__bases__, "Can't specialize non generic type!" # nosec: B101
216
+ assert cls.__PARAMETERS__ is None, "Can't specialize already specialized type!" # nosec: B101
130
217
 
131
218
  type_arguments: tuple[type[Any], ...]
132
219
  match type_argument:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: haiway
3
- Version: 0.7.0
3
+ Version: 0.7.1
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
@@ -1,9 +1,11 @@
1
1
  from collections.abc import Callable, Sequence, Set
2
2
  from datetime import date, datetime
3
3
  from enum import StrEnum
4
- from typing import Literal, NotRequired, Protocol, Required, Self, TypedDict, runtime_checkable
4
+ from typing import Any, Literal, NotRequired, Protocol, Required, Self, TypedDict, runtime_checkable
5
5
  from uuid import UUID, uuid4
6
6
 
7
+ from pytest import raises
8
+
7
9
  from haiway import MISSING, Missing, State, frozenlist
8
10
 
9
11
 
@@ -183,3 +185,40 @@ def test_initialization_allows_missing_properties() -> None:
183
185
  assert Basics(**{"string": "a", "integer": 1}) == Basics(string="a", integer=1)
184
186
  assert Basics(**{"string": "a", "integer": None}) == Basics(string="a", integer=None)
185
187
  assert Basics(**{"string": "a"}) == Basics(string="a", integer=MISSING)
188
+
189
+
190
+ def test_generic_subtypes_validation() -> None:
191
+ class NestedGeneric[T](State):
192
+ value: T
193
+
194
+ class Generic[T](State):
195
+ nested: NestedGeneric[T]
196
+
197
+ class Container(State):
198
+ generic: Generic[str]
199
+
200
+ assert isinstance(Generic[str](nested=NestedGeneric[str](value="ok")), Generic)
201
+ assert isinstance(Generic(nested=NestedGeneric[str](value="ok")), Generic)
202
+ assert isinstance(Generic(nested=NestedGeneric(value="ok")), Generic)
203
+
204
+ assert isinstance(Generic[str](nested=NestedGeneric[str](value="ok")), Generic[Any])
205
+ assert isinstance(Generic(nested=NestedGeneric[str](value="ok")), Generic[Any])
206
+ assert isinstance(Generic[str](nested=NestedGeneric(value="ok")), Generic[Any])
207
+ assert isinstance(Generic(nested=NestedGeneric(value="ok")), Generic[Any])
208
+
209
+ assert isinstance(Generic[str](nested=NestedGeneric[str](value="ok")), Generic[str])
210
+ assert isinstance(Generic(nested=NestedGeneric[str](value="ok")), Generic[str])
211
+ assert isinstance(Generic[str](nested=NestedGeneric(value="ok")), Generic[str])
212
+ assert isinstance(Generic(nested=NestedGeneric(value="ok")), Generic[str])
213
+
214
+ with raises(TypeError):
215
+ _ = Generic[int](nested=NestedGeneric[str](value="ok"))
216
+
217
+ with raises(TypeError):
218
+ _ = Container(generic=Generic(nested=NestedGeneric(value=42)))
219
+
220
+ with raises(TypeError):
221
+ _ = Container(generic=Generic[int](nested=NestedGeneric[str](value="ok")))
222
+
223
+ # not raises
224
+ _ = Container(generic=Generic(nested=NestedGeneric(value="ok")))
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes