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.
- {haiway-0.7.0/src/haiway.egg-info → haiway-0.7.1}/PKG-INFO +1 -1
- {haiway-0.7.0 → haiway-0.7.1}/pyproject.toml +1 -1
- {haiway-0.7.0 → haiway-0.7.1}/src/haiway/state/structure.py +88 -1
- {haiway-0.7.0 → haiway-0.7.1/src/haiway.egg-info}/PKG-INFO +1 -1
- {haiway-0.7.0 → haiway-0.7.1}/tests/test_state.py +40 -1
- {haiway-0.7.0 → haiway-0.7.1}/LICENSE +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/README.md +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/setup.cfg +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/src/haiway/__init__.py +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/src/haiway/context/__init__.py +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/src/haiway/context/access.py +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/src/haiway/context/disposables.py +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/src/haiway/context/metrics.py +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/src/haiway/context/state.py +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/src/haiway/context/tasks.py +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/src/haiway/context/types.py +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/src/haiway/helpers/__init__.py +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/src/haiway/helpers/asynchrony.py +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/src/haiway/helpers/caching.py +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/src/haiway/helpers/retries.py +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/src/haiway/helpers/throttling.py +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/src/haiway/helpers/timeouted.py +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/src/haiway/helpers/tracing.py +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/src/haiway/py.typed +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/src/haiway/state/__init__.py +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/src/haiway/state/attributes.py +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/src/haiway/state/path.py +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/src/haiway/state/requirement.py +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/src/haiway/state/validation.py +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/src/haiway/types/__init__.py +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/src/haiway/types/frozen.py +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/src/haiway/types/missing.py +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/src/haiway/utils/__init__.py +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/src/haiway/utils/always.py +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/src/haiway/utils/env.py +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/src/haiway/utils/immutable.py +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/src/haiway/utils/logs.py +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/src/haiway/utils/mimic.py +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/src/haiway/utils/noop.py +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/src/haiway/utils/queue.py +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/src/haiway.egg-info/SOURCES.txt +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/src/haiway.egg-info/dependency_links.txt +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/src/haiway.egg-info/requires.txt +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/src/haiway.egg-info/top_level.txt +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/tests/test_async_queue.py +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/tests/test_attribute_path.py +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/tests/test_auto_retry.py +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/tests/test_cache.py +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/tests/test_context.py +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/tests/test_streaming.py +0 -0
- {haiway-0.7.0 → haiway-0.7.1}/tests/test_timeout.py +0 -0
@@ -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.
|
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,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
|
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
|
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
|