haiway 0.10.0__py3-none-any.whl → 0.10.10__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/context/access.py +8 -0
- haiway/context/identifier.py +18 -1
- haiway/helpers/metrics.py +24 -4
- haiway/state/attributes.py +4 -1
- haiway/state/validation.py +13 -4
- {haiway-0.10.0.dist-info → haiway-0.10.10.dist-info}/METADATA +14 -15
- {haiway-0.10.0.dist-info → haiway-0.10.10.dist-info}/RECORD +9 -10
- {haiway-0.10.0.dist-info → haiway-0.10.10.dist-info}/WHEEL +1 -2
- haiway-0.10.0.dist-info/top_level.txt +0 -1
- {haiway-0.10.0.dist-info → haiway-0.10.10.dist-info/licenses}/LICENSE +0 -0
haiway/context/access.py
CHANGED
@@ -195,6 +195,14 @@ class ScopeContext:
|
|
195
195
|
|
196
196
|
@final
|
197
197
|
class ctx:
|
198
|
+
@staticmethod
|
199
|
+
def trace_id() -> str:
|
200
|
+
"""
|
201
|
+
Get the current context trace identifier.
|
202
|
+
"""
|
203
|
+
|
204
|
+
return ScopeIdentifier.current_trace_id()
|
205
|
+
|
198
206
|
@staticmethod
|
199
207
|
def scope(
|
200
208
|
label: str,
|
haiway/context/identifier.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
from contextvars import ContextVar, Token
|
2
2
|
from types import TracebackType
|
3
|
-
from typing import Self, final
|
3
|
+
from typing import Any, Self, final
|
4
4
|
from uuid import uuid4
|
5
5
|
|
6
6
|
__all__ = [
|
@@ -12,6 +12,14 @@ __all__ = [
|
|
12
12
|
class ScopeIdentifier:
|
13
13
|
_context = ContextVar[Self]("ScopeIdentifier")
|
14
14
|
|
15
|
+
@classmethod
|
16
|
+
def current_trace_id(cls) -> str:
|
17
|
+
try:
|
18
|
+
return ScopeIdentifier._context.get().trace_id
|
19
|
+
|
20
|
+
except LookupError as exc:
|
21
|
+
raise RuntimeError("Attempting to access scope identifier outside of scope") from exc
|
22
|
+
|
15
23
|
@classmethod
|
16
24
|
def scope(
|
17
25
|
cls,
|
@@ -60,6 +68,15 @@ class ScopeIdentifier:
|
|
60
68
|
def __str__(self) -> str:
|
61
69
|
return self.unique_name
|
62
70
|
|
71
|
+
def __eq__(self, other: Any) -> bool:
|
72
|
+
if not isinstance(other, self.__class__):
|
73
|
+
return False
|
74
|
+
|
75
|
+
return self.scope_id == other.scope_id and self.trace_id == other.trace_id
|
76
|
+
|
77
|
+
def __hash__(self) -> int:
|
78
|
+
return hash(self.scope_id)
|
79
|
+
|
63
80
|
def __enter__(self) -> None:
|
64
81
|
assert not hasattr(self, "_token"), "Context reentrance is not allowed" # nosec: B101
|
65
82
|
self._token: Token[ScopeIdentifier] = ScopeIdentifier._context.set(self)
|
haiway/helpers/metrics.py
CHANGED
@@ -101,6 +101,7 @@ class MetricsHolder:
|
|
101
101
|
)
|
102
102
|
|
103
103
|
def __init__(self) -> None:
|
104
|
+
self.root_scope: ScopeIdentifier | None = None
|
104
105
|
self.scopes: dict[ScopeIdentifier, MetricsScopeStore] = {}
|
105
106
|
|
106
107
|
def record(
|
@@ -109,7 +110,9 @@ class MetricsHolder:
|
|
109
110
|
/,
|
110
111
|
metric: State,
|
111
112
|
) -> None:
|
113
|
+
assert self.root_scope is not None # nosec: B101
|
112
114
|
assert scope in self.scopes # nosec: B101
|
115
|
+
|
113
116
|
metric_type: type[State] = type(metric)
|
114
117
|
metrics: dict[type[State], State] = self.scopes[scope].metrics
|
115
118
|
if (current := metrics.get(metric_type)) and hasattr(current, "__add__"):
|
@@ -125,6 +128,9 @@ class MetricsHolder:
|
|
125
128
|
metric: type[Metric],
|
126
129
|
merged: bool,
|
127
130
|
) -> Metric | None:
|
131
|
+
assert self.root_scope is not None # nosec: B101
|
132
|
+
assert scope in self.scopes # nosec: B101
|
133
|
+
|
128
134
|
if merged:
|
129
135
|
return self.scopes[scope].merged(metric)
|
130
136
|
|
@@ -139,7 +145,11 @@ class MetricsHolder:
|
|
139
145
|
assert scope not in self.scopes # nosec: B101
|
140
146
|
scope_metrics = MetricsScopeStore(scope)
|
141
147
|
self.scopes[scope] = scope_metrics
|
142
|
-
|
148
|
+
|
149
|
+
if self.root_scope is None:
|
150
|
+
self.root_scope = scope
|
151
|
+
|
152
|
+
else:
|
143
153
|
for key in self.scopes.keys():
|
144
154
|
if key.scope_id == scope.parent_id:
|
145
155
|
self.scopes[key].nested.append(scope_metrics)
|
@@ -182,9 +192,10 @@ class MetricsLogger:
|
|
182
192
|
items_limit: int | None,
|
183
193
|
redact_content: bool,
|
184
194
|
) -> None:
|
195
|
+
self.root_scope: ScopeIdentifier | None = None
|
196
|
+
self.scopes: dict[ScopeIdentifier, MetricsScopeStore] = {}
|
185
197
|
self.items_limit: int | None = items_limit
|
186
198
|
self.redact_content: bool = redact_content
|
187
|
-
self.scopes: dict[ScopeIdentifier, MetricsScopeStore] = {}
|
188
199
|
|
189
200
|
def record(
|
190
201
|
self,
|
@@ -192,7 +203,9 @@ class MetricsLogger:
|
|
192
203
|
/,
|
193
204
|
metric: State,
|
194
205
|
) -> None:
|
206
|
+
assert self.root_scope is not None # nosec: B101
|
195
207
|
assert scope in self.scopes # nosec: B101
|
208
|
+
|
196
209
|
metric_type: type[State] = type(metric)
|
197
210
|
metrics: dict[type[State], State] = self.scopes[scope].metrics
|
198
211
|
if (current := metrics.get(metric_type)) and hasattr(current, "__add__"):
|
@@ -214,6 +227,9 @@ class MetricsLogger:
|
|
214
227
|
metric: type[Metric],
|
215
228
|
merged: bool,
|
216
229
|
) -> Metric | None:
|
230
|
+
assert self.root_scope is not None # nosec: B101
|
231
|
+
assert scope in self.scopes # nosec: B101
|
232
|
+
|
217
233
|
if merged:
|
218
234
|
return self.scopes[scope].merged(metric)
|
219
235
|
|
@@ -228,7 +244,11 @@ class MetricsLogger:
|
|
228
244
|
assert scope not in self.scopes # nosec: B101
|
229
245
|
scope_metrics = MetricsScopeStore(scope)
|
230
246
|
self.scopes[scope] = scope_metrics
|
231
|
-
|
247
|
+
|
248
|
+
if self.root_scope is None:
|
249
|
+
self.root_scope = scope
|
250
|
+
|
251
|
+
else:
|
232
252
|
for key in self.scopes.keys():
|
233
253
|
if key.scope_id == scope.parent_id:
|
234
254
|
self.scopes[key].nested.append(scope_metrics)
|
@@ -246,7 +266,7 @@ class MetricsLogger:
|
|
246
266
|
assert scope in self.scopes # nosec: B101
|
247
267
|
self.scopes[scope].exited = monotonic()
|
248
268
|
|
249
|
-
if scope.
|
269
|
+
if scope == self.root_scope and self.scopes[scope].finished:
|
250
270
|
if log := _tree_log(
|
251
271
|
self.scopes[scope],
|
252
272
|
list_items_limit=self.items_limit,
|
haiway/state/attributes.py
CHANGED
@@ -509,7 +509,10 @@ def _resolve_type_typeddict(
|
|
509
509
|
self_annotation=resolved_attribute,
|
510
510
|
recursion_guard=recursion_guard,
|
511
511
|
).update_required(key in annotation.__required_keys__)
|
512
|
-
resolved_attribute.extra =
|
512
|
+
resolved_attribute.extra = {
|
513
|
+
"attributes": attributes,
|
514
|
+
"required": annotation.__required_keys__,
|
515
|
+
}
|
513
516
|
return resolved_attribute
|
514
517
|
|
515
518
|
|
haiway/state/validation.py
CHANGED
@@ -86,6 +86,12 @@ class AttributeValidator[Type]:
|
|
86
86
|
assert self.validation is not MISSING # nosec: B101
|
87
87
|
return self.validation(value) # pyright: ignore[reportCallIssue, reportUnknownVariableType]
|
88
88
|
|
89
|
+
def __str__(self) -> str:
|
90
|
+
return f"Validator[{self.annotation}]"
|
91
|
+
|
92
|
+
def __repr__(self) -> str:
|
93
|
+
return f"Validator[{self.annotation}]"
|
94
|
+
|
89
95
|
|
90
96
|
def _prepare_validator_of_any(
|
91
97
|
annotation: AttributeAnnotation,
|
@@ -391,11 +397,12 @@ def _prepare_validator_of_typed_dict(
|
|
391
397
|
case _:
|
392
398
|
raise TypeError(f"'{value}' is not matching expected type of 'str'")
|
393
399
|
|
400
|
+
formatted_type: str = str(annotation)
|
394
401
|
values_validators: dict[str, AttributeValidation[Any]] = {
|
395
402
|
key: AttributeValidator.of(element, recursion_guard=recursion_guard)
|
396
|
-
for key, element in annotation.extra.items()
|
403
|
+
for key, element in annotation.extra["attributes"].items()
|
397
404
|
}
|
398
|
-
|
405
|
+
required_values: Set[str] = annotation.extra["required"]
|
399
406
|
|
400
407
|
def validator(
|
401
408
|
value: Any,
|
@@ -406,8 +413,10 @@ def _prepare_validator_of_typed_dict(
|
|
406
413
|
validated: MutableMapping[str, Any] = {}
|
407
414
|
for key, validator in values_validators.items():
|
408
415
|
element: Any = elements.get(key, MISSING)
|
409
|
-
if element is not
|
410
|
-
|
416
|
+
if element is MISSING and key not in required_values:
|
417
|
+
continue # skip missing and not required
|
418
|
+
|
419
|
+
validated[key_validator(key)] = validator(element)
|
411
420
|
|
412
421
|
# TODO: make sure dict is not mutable with MappingProxyType?
|
413
422
|
return validated
|
@@ -1,7 +1,9 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: haiway
|
3
|
-
Version: 0.10.
|
3
|
+
Version: 0.10.10
|
4
4
|
Summary: Framework for dependency injection and state management within structured concurrency model.
|
5
|
+
Project-URL: Homepage, https://miquido.com
|
6
|
+
Project-URL: Repository, https://github.com/miquido/haiway.git
|
5
7
|
Maintainer-email: Kacper Kaliński <kacper.kalinski@miquido.com>
|
6
8
|
License: MIT License
|
7
9
|
|
@@ -24,25 +26,22 @@ License: MIT License
|
|
24
26
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
25
27
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
26
28
|
SOFTWARE.
|
27
|
-
|
28
|
-
Project-URL: Repository, https://github.com/miquido/haiway.git
|
29
|
-
Classifier: License :: OSI Approved :: MIT License
|
29
|
+
License-File: LICENSE
|
30
30
|
Classifier: Intended Audience :: Developers
|
31
|
+
Classifier: License :: OSI Approved :: MIT License
|
31
32
|
Classifier: Programming Language :: Python
|
32
|
-
Classifier: Typing :: Typed
|
33
33
|
Classifier: Topic :: Software Development
|
34
34
|
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
35
|
+
Classifier: Typing :: Typed
|
35
36
|
Requires-Python: >=3.12
|
36
|
-
Description-Content-Type: text/markdown
|
37
|
-
License-File: LICENSE
|
38
37
|
Provides-Extra: dev
|
39
|
-
Requires-Dist:
|
40
|
-
Requires-Dist:
|
41
|
-
Requires-Dist:
|
42
|
-
Requires-Dist:
|
43
|
-
Requires-Dist: pytest~=7.4; extra ==
|
44
|
-
Requires-Dist:
|
45
|
-
|
38
|
+
Requires-Dist: bandit~=1.7; extra == 'dev'
|
39
|
+
Requires-Dist: pyright~=1.1; extra == 'dev'
|
40
|
+
Requires-Dist: pytest-asyncio~=0.23; extra == 'dev'
|
41
|
+
Requires-Dist: pytest-cov~=4.1; extra == 'dev'
|
42
|
+
Requires-Dist: pytest~=7.4; extra == 'dev'
|
43
|
+
Requires-Dist: ruff~=0.9; extra == 'dev'
|
44
|
+
Description-Content-Type: text/markdown
|
46
45
|
|
47
46
|
# 🚗 haiway 🚕 🚚 🚙
|
48
47
|
|
@@ -1,9 +1,9 @@
|
|
1
1
|
haiway/__init__.py,sha256=IEUCyFYKT5IPHnkiUvDVZHdJeHqCaBnG8FhPD20Zgo8,1929
|
2
2
|
haiway/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
3
|
haiway/context/__init__.py,sha256=eRvuhifx7xCd-_6desgk55idzNpD5S5sprmCfGb3_9M,662
|
4
|
-
haiway/context/access.py,sha256=
|
4
|
+
haiway/context/access.py,sha256=Fc2NfoIV2zWE5qPnNgSitK1dy-PME-rJJfUPhoNwJzY,17125
|
5
5
|
haiway/context/disposables.py,sha256=DZjnMp-wMfF-em2Wjhbm1MvXubNpuzFBT70BQNIxC7M,2019
|
6
|
-
haiway/context/identifier.py,sha256=
|
6
|
+
haiway/context/identifier.py,sha256=Fyb6OHx5FPaSLLRK249HUEr_KlBSG5F-eW01Oxg_Ke8,2570
|
7
7
|
haiway/context/logging.py,sha256=ptwgENuyw-WFgokVsYx9OXZGhJENuO_wgfVjcBryUKM,4251
|
8
8
|
haiway/context/metrics.py,sha256=Ve628X39u0rdLm0vYmVZt7aGeoEeRquR6f67vJIXClY,4213
|
9
9
|
haiway/context/state.py,sha256=LCcFxXqDBu6prvPyPicN-ecONSNHyR56PfQ5u5jNFCU,3000
|
@@ -12,17 +12,17 @@ haiway/context/types.py,sha256=VvJA7wAPZ3ISpgyThVguioYUXqhHf0XkPfRd0M1ERiQ,142
|
|
12
12
|
haiway/helpers/__init__.py,sha256=8XRJWNhidWuBKqRZ1Hyc2xqt7DeWLcoOs2V-oexl8VY,579
|
13
13
|
haiway/helpers/asynchrony.py,sha256=9lo9wT3G0TyPb4vfmTnWGBvB_eN6p6nIlj46_9Ag8fQ,6022
|
14
14
|
haiway/helpers/caching.py,sha256=Ok_WE5Whe7XqnIuLZo4rNNBFeWap-aUWX799s4b1JAQ,9536
|
15
|
-
haiway/helpers/metrics.py,sha256=
|
15
|
+
haiway/helpers/metrics.py,sha256=0oFBiO-hAzihyC5jvXevNrYOoTcUGc2yGhE1A_866Mc,13314
|
16
16
|
haiway/helpers/retries.py,sha256=gIkyUlqJLDYaxIZd3qzeqGFY9y5Gp8dgZLlZ6hs8hoc,7538
|
17
17
|
haiway/helpers/throttling.py,sha256=zo0OwFq64si5KUwhd58cFHLmGAmYwRbFRJMbv9suhPs,3844
|
18
18
|
haiway/helpers/timeouted.py,sha256=1xU09hQnFdj6p48BwZl5xUvtIr3zC0ZUXehkdrduCjs,3074
|
19
19
|
haiway/helpers/tracing.py,sha256=eQpkIoGSB51jRF8RcLaihvHX3VzJIRdyRxTx3I14Pzg,3346
|
20
20
|
haiway/state/__init__.py,sha256=emTuwGFn7HyjyTJ_ass69J5jQIA7_WHO4teZz_dR05Y,355
|
21
|
-
haiway/state/attributes.py,sha256=
|
21
|
+
haiway/state/attributes.py,sha256=0gJbgOKiH79RNM9IW66NNWSWM29037yGTTpoln6vPvU,22984
|
22
22
|
haiway/state/path.py,sha256=4vh-fYQv8_xRWjS0ErMQslKDWRI6-KVECAr8JhYk0UY,17503
|
23
23
|
haiway/state/requirement.py,sha256=3iQqzp5Q7w6y5uClamJGH7S5Hib9pciuTAV27PP5lS8,6161
|
24
24
|
haiway/state/structure.py,sha256=bSIj0S_HG-F1Z5GxSlY6VpGtrtiwG82-AIL_PL1lRLo,12465
|
25
|
-
haiway/state/validation.py,sha256=
|
25
|
+
haiway/state/validation.py,sha256=r0EMIs-nvoXsmSA74oGu6Lrbw8lkzmseaY82_-E8ous,13814
|
26
26
|
haiway/types/__init__.py,sha256=-j4uDN6ix3GBXLBqXC-k_QOJSDlO6zvNCxDej8vVzek,342
|
27
27
|
haiway/types/default.py,sha256=IVQsNzDnfukL3-XlScYv2PgTcJ1x_BNP9i5UlS5oEbg,2179
|
28
28
|
haiway/types/frozen.py,sha256=CZhFCXnWAKEhuWSfILxA8smfdpMd5Ku694ycfLh98R8,76
|
@@ -36,8 +36,7 @@ haiway/utils/logs.py,sha256=oDsc1ZdqKDjlTlctLbDcp9iX98Acr-1tdw-Pyg3DElo,1577
|
|
36
36
|
haiway/utils/mimic.py,sha256=BkVjTVP2TxxC8GChPGyDV6UXVwJmiRiSWeOYZNZFHxs,1828
|
37
37
|
haiway/utils/noop.py,sha256=qgbZlOKWY6_23Zs43OLukK2HagIQKRyR04zrFVm5rWI,344
|
38
38
|
haiway/utils/queue.py,sha256=oQ3GXCJ-PGNtMEr6EPdgqAvYZoj8lAa7Z2drBKBEoBM,2345
|
39
|
-
haiway-0.10.
|
40
|
-
haiway-0.10.
|
41
|
-
haiway-0.10.
|
42
|
-
haiway-0.10.
|
43
|
-
haiway-0.10.0.dist-info/RECORD,,
|
39
|
+
haiway-0.10.10.dist-info/METADATA,sha256=PhcebR048J2VPSVrFIQOnQSEIvQpwolFKMmRuOGTWAg,3858
|
40
|
+
haiway-0.10.10.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
41
|
+
haiway-0.10.10.dist-info/licenses/LICENSE,sha256=GehQEW_I1pkmxkkj3NEa7rCTQKYBn7vTPabpDYJlRuo,1063
|
42
|
+
haiway-0.10.10.dist-info/RECORD,,
|
@@ -1 +0,0 @@
|
|
1
|
-
haiway
|
File without changes
|