haiway 0.17.0__py3-none-any.whl → 0.18.1__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.
Files changed (47) hide show
  1. haiway/__init__.py +24 -18
  2. haiway/context/__init__.py +23 -13
  3. haiway/context/access.py +127 -91
  4. haiway/context/disposables.py +2 -2
  5. haiway/context/identifier.py +4 -5
  6. haiway/context/observability.py +526 -0
  7. haiway/context/state.py +2 -2
  8. haiway/context/tasks.py +1 -3
  9. haiway/context/types.py +2 -2
  10. haiway/helpers/__init__.py +5 -7
  11. haiway/helpers/asynchrony.py +2 -2
  12. haiway/helpers/caching.py +2 -2
  13. haiway/helpers/observability.py +244 -0
  14. haiway/helpers/retries.py +1 -3
  15. haiway/helpers/throttling.py +1 -3
  16. haiway/helpers/timeouted.py +1 -3
  17. haiway/helpers/tracing.py +21 -35
  18. haiway/opentelemetry/__init__.py +3 -0
  19. haiway/opentelemetry/observability.py +452 -0
  20. haiway/state/__init__.py +2 -2
  21. haiway/state/attributes.py +2 -2
  22. haiway/state/path.py +1 -3
  23. haiway/state/requirement.py +1 -3
  24. haiway/state/structure.py +161 -30
  25. haiway/state/validation.py +2 -2
  26. haiway/types/__init__.py +2 -2
  27. haiway/types/default.py +2 -2
  28. haiway/types/frozen.py +1 -3
  29. haiway/types/missing.py +2 -2
  30. haiway/utils/__init__.py +2 -2
  31. haiway/utils/always.py +2 -2
  32. haiway/utils/collections.py +2 -2
  33. haiway/utils/env.py +2 -2
  34. haiway/utils/freezing.py +1 -3
  35. haiway/utils/logs.py +1 -3
  36. haiway/utils/mimic.py +1 -3
  37. haiway/utils/noop.py +2 -2
  38. haiway/utils/queue.py +1 -3
  39. haiway/utils/stream.py +1 -3
  40. {haiway-0.17.0.dist-info → haiway-0.18.1.dist-info}/METADATA +9 -5
  41. haiway-0.18.1.dist-info/RECORD +44 -0
  42. haiway/context/logging.py +0 -242
  43. haiway/context/metrics.py +0 -176
  44. haiway/helpers/metrics.py +0 -465
  45. haiway-0.17.0.dist-info/RECORD +0 -43
  46. {haiway-0.17.0.dist-info → haiway-0.18.1.dist-info}/WHEEL +0 -0
  47. {haiway-0.17.0.dist-info → haiway-0.18.1.dist-info}/licenses/LICENSE +0 -0
haiway/state/structure.py CHANGED
@@ -1,5 +1,5 @@
1
1
  import typing
2
- from collections.abc import Callable, Mapping
2
+ from collections.abc import ItemsView, Mapping, Sequence
3
3
  from types import EllipsisType, GenericAlias
4
4
  from typing import (
5
5
  Any,
@@ -10,7 +10,6 @@ from typing import (
10
10
  cast,
11
11
  dataclass_transform,
12
12
  final,
13
- overload,
14
13
  )
15
14
  from weakref import WeakValueDictionary
16
15
 
@@ -19,32 +18,7 @@ from haiway.state.path import AttributePath
19
18
  from haiway.state.validation import AttributeValidation, AttributeValidator
20
19
  from haiway.types import MISSING, DefaultValue, Missing, not_missing
21
20
 
22
- __all__ = [
23
- "State",
24
- ]
25
-
26
-
27
- @overload
28
- def Default[Value](
29
- value: Value,
30
- /,
31
- ) -> Value: ...
32
-
33
-
34
- @overload
35
- def Default[Value](
36
- *,
37
- factory: Callable[[], Value],
38
- ) -> Value: ...
39
-
40
-
41
- def Default[Value](
42
- value: Value | Missing = MISSING,
43
- /,
44
- *,
45
- factory: Callable[[], Value] | Missing = MISSING,
46
- ) -> Value: # it is actually a DefaultValue, but type checker has to be fooled
47
- return cast(Value, DefaultValue(value, factory=factory))
21
+ __all__ = ("State",)
48
22
 
49
23
 
50
24
  @final
@@ -392,11 +366,27 @@ class State(metaclass=StateMeta):
392
366
  ) -> Self:
393
367
  return self.__replace__(**kwargs)
394
368
 
395
- def as_dict(self) -> dict[str, Any]:
369
+ def to_str(
370
+ self,
371
+ pretty: bool = False,
372
+ ) -> str:
373
+ if pretty:
374
+ return _state_str(self)
375
+
376
+ else:
377
+ return self.__str__()
378
+
379
+ def to_mapping(
380
+ self,
381
+ recursive: bool = False,
382
+ ) -> Mapping[str, Any]:
396
383
  dict_result: dict[str, Any] = {}
397
384
  for key in self.__ATTRIBUTES__.keys():
398
385
  value: Any | Missing = getattr(self, key, MISSING)
399
- if not_missing(value):
386
+ if recursive and isinstance(value, State):
387
+ dict_result[key] = value.to_mapping(recursive=recursive)
388
+
389
+ elif not_missing(value):
400
390
  dict_result[key] = value
401
391
 
402
392
  return dict_result
@@ -458,3 +448,144 @@ class State(metaclass=StateMeta):
458
448
  **kwargs,
459
449
  }
460
450
  )
451
+
452
+
453
+ def _attribute_str(
454
+ *,
455
+ key: str,
456
+ value: str,
457
+ ) -> str:
458
+ return f"┝ {key}: {value}"
459
+
460
+
461
+ def _element_str(
462
+ *,
463
+ key: Any,
464
+ value: Any,
465
+ ) -> str:
466
+ return f"[{key}]: {value}"
467
+
468
+
469
+ def _state_str(
470
+ state: State,
471
+ /,
472
+ ) -> str:
473
+ variables: ItemsView[str, Any] = vars(state).items()
474
+
475
+ parts: list[str] = [f"┍━ {type(state).__name__}:"]
476
+ for key, value in variables:
477
+ value_string: str | None = _value_str(value)
478
+
479
+ if value_string:
480
+ parts.append(
481
+ _attribute_str(
482
+ key=key,
483
+ value=value_string,
484
+ )
485
+ )
486
+
487
+ else:
488
+ continue # skip empty elements
489
+
490
+ if parts:
491
+ return "\n".join(parts) + "\n┕━"
492
+
493
+ else:
494
+ return "╍"
495
+
496
+
497
+ def _mapping_str(
498
+ dictionary: Mapping[Any, Any],
499
+ /,
500
+ ) -> str | None:
501
+ elements: ItemsView[Any, Any] = dictionary.items()
502
+
503
+ parts: list[str] = []
504
+ for key, value in elements:
505
+ value_string: str | None = _value_str(value)
506
+
507
+ if value_string:
508
+ parts.append(
509
+ _element_str(
510
+ key=key,
511
+ value=value_string,
512
+ )
513
+ )
514
+
515
+ else:
516
+ continue # skip empty elements
517
+
518
+ if parts:
519
+ return "\n| " + "\n".join(parts).replace("\n", "\n| ")
520
+
521
+ else:
522
+ return None
523
+
524
+
525
+ def _sequence_str(
526
+ sequence: Sequence[Any],
527
+ /,
528
+ ) -> str | None:
529
+ parts: list[str] = []
530
+ for idx, element in enumerate(sequence):
531
+ element_string: str | None = _value_str(element)
532
+
533
+ if element_string:
534
+ parts.append(
535
+ _element_str(
536
+ key=idx,
537
+ value=element_string,
538
+ )
539
+ )
540
+
541
+ else:
542
+ continue # skip empty elements
543
+
544
+ if parts:
545
+ return "\n| " + "\n".join(parts).replace("\n", "\n| ")
546
+
547
+ else:
548
+ return None
549
+
550
+
551
+ def _raw_value_str(
552
+ value: Any,
553
+ /,
554
+ ) -> str | None:
555
+ if value is MISSING:
556
+ return None # skip missing
557
+
558
+ else:
559
+ return str(value).strip().replace("\n", "\n| ")
560
+
561
+
562
+ def _value_str( # noqa: PLR0911
563
+ value: Any,
564
+ /,
565
+ ) -> str | None:
566
+ # check for string
567
+ if isinstance(value, str):
568
+ if "\n" in value:
569
+ return f'"""\n{value}\n"""'.replace("\n", "\n| ")
570
+
571
+ else:
572
+ return f'"{value}"'
573
+
574
+ # check for bytes
575
+ elif isinstance(value, bytes):
576
+ return f'b"{value}"'
577
+
578
+ # try unpack state
579
+ elif isinstance(value, State):
580
+ return _state_str(value)
581
+
582
+ # try unpack mapping
583
+ elif isinstance(value, Mapping):
584
+ return _mapping_str(value)
585
+
586
+ # try unpack sequence
587
+ elif isinstance(value, Sequence):
588
+ return _sequence_str(value)
589
+
590
+ else: # fallback to other
591
+ return _raw_value_str(value)
@@ -10,11 +10,11 @@ from uuid import UUID
10
10
  from haiway.state.attributes import AttributeAnnotation
11
11
  from haiway.types import MISSING, Missing
12
12
 
13
- __all__ = [
13
+ __all__ = (
14
14
  "AttributeValidation",
15
15
  "AttributeValidationError",
16
16
  "AttributeValidator",
17
- ]
17
+ )
18
18
 
19
19
 
20
20
  class AttributeValidation[Type](Protocol):
haiway/types/__init__.py CHANGED
@@ -2,7 +2,7 @@ from haiway.types.default import Default, DefaultValue
2
2
  from haiway.types.frozen import frozenlist
3
3
  from haiway.types.missing import MISSING, Missing, is_missing, not_missing, when_missing
4
4
 
5
- __all__ = [
5
+ __all__ = (
6
6
  "MISSING",
7
7
  "Default",
8
8
  "DefaultValue",
@@ -11,4 +11,4 @@ __all__ = [
11
11
  "is_missing",
12
12
  "not_missing",
13
13
  "when_missing",
14
- ]
14
+ )
haiway/types/default.py CHANGED
@@ -4,10 +4,10 @@ from typing import Any, cast, final, overload
4
4
  from haiway.types.missing import MISSING, Missing, not_missing
5
5
  from haiway.utils.always import always
6
6
 
7
- __all__ = [
7
+ __all__ = (
8
8
  "Default",
9
9
  "DefaultValue",
10
- ]
10
+ )
11
11
 
12
12
 
13
13
  @final
haiway/types/frozen.py CHANGED
@@ -1,5 +1,3 @@
1
- __all__ = [
2
- "frozenlist",
3
- ]
1
+ __all__ = ("frozenlist",)
4
2
 
5
3
  type frozenlist[Value] = tuple[Value, ...]
haiway/types/missing.py CHANGED
@@ -1,12 +1,12 @@
1
1
  from typing import Any, Final, TypeGuard, cast, final
2
2
 
3
- __all__ = [
3
+ __all__ = (
4
4
  "MISSING",
5
5
  "Missing",
6
6
  "is_missing",
7
7
  "not_missing",
8
8
  "when_missing",
9
- ]
9
+ )
10
10
 
11
11
 
12
12
  class MissingType(type):
haiway/utils/__init__.py CHANGED
@@ -15,7 +15,7 @@ from haiway.utils.noop import async_noop, noop
15
15
  from haiway.utils.queue import AsyncQueue
16
16
  from haiway.utils.stream import AsyncStream
17
17
 
18
- __all__ = [
18
+ __all__ = (
19
19
  "AsyncQueue",
20
20
  "AsyncStream",
21
21
  "always",
@@ -36,4 +36,4 @@ __all__ = [
36
36
  "noop",
37
37
  "setup_logging",
38
38
  "without_missing",
39
- ]
39
+ )
haiway/utils/always.py CHANGED
@@ -1,10 +1,10 @@
1
1
  from collections.abc import Callable, Coroutine
2
2
  from typing import Any
3
3
 
4
- __all__ = [
4
+ __all__ = (
5
5
  "always",
6
6
  "async_always",
7
- ]
7
+ )
8
8
 
9
9
 
10
10
  def always[Value](
@@ -3,13 +3,13 @@ from typing import Any, cast, overload
3
3
 
4
4
  from haiway.types.missing import MISSING
5
5
 
6
- __all__ = [
6
+ __all__ = (
7
7
  "as_dict",
8
8
  "as_list",
9
9
  "as_set",
10
10
  "as_tuple",
11
11
  "without_missing",
12
- ]
12
+ )
13
13
 
14
14
 
15
15
  @overload
haiway/utils/env.py CHANGED
@@ -3,14 +3,14 @@ from collections.abc import Callable
3
3
  from os import environ, getenv
4
4
  from typing import Literal, overload
5
5
 
6
- __all__ = [
6
+ __all__ = (
7
7
  "getenv_base64",
8
8
  "getenv_bool",
9
9
  "getenv_float",
10
10
  "getenv_int",
11
11
  "getenv_str",
12
12
  "load_env",
13
- ]
13
+ )
14
14
 
15
15
 
16
16
  @overload
haiway/utils/freezing.py CHANGED
@@ -1,8 +1,6 @@
1
1
  from typing import Any
2
2
 
3
- __all__ = [
4
- "freeze",
5
- ]
3
+ __all__ = ("freeze",)
6
4
 
7
5
 
8
6
  def freeze(
haiway/utils/logs.py CHANGED
@@ -2,9 +2,7 @@ from logging.config import dictConfig
2
2
 
3
3
  from haiway.utils.env import getenv_bool
4
4
 
5
- __all__ = [
6
- "setup_logging",
7
- ]
5
+ __all__ = ("setup_logging",)
8
6
 
9
7
 
10
8
  def setup_logging(
haiway/utils/mimic.py CHANGED
@@ -1,9 +1,7 @@
1
1
  from collections.abc import Callable
2
2
  from typing import Any, cast, overload
3
3
 
4
- __all__ = [
5
- "mimic_function",
6
- ]
4
+ __all__ = ("mimic_function",)
7
5
 
8
6
 
9
7
  @overload
haiway/utils/noop.py CHANGED
@@ -1,9 +1,9 @@
1
1
  from typing import Any
2
2
 
3
- __all__ = [
3
+ __all__ = (
4
4
  "async_noop",
5
5
  "noop",
6
- ]
6
+ )
7
7
 
8
8
 
9
9
  def noop(
haiway/utils/queue.py CHANGED
@@ -3,9 +3,7 @@ from collections import deque
3
3
  from collections.abc import AsyncIterator
4
4
  from typing import Any
5
5
 
6
- __all__ = [
7
- "AsyncQueue",
8
- ]
6
+ __all__ = ("AsyncQueue",)
9
7
 
10
8
 
11
9
  class AsyncQueue[Element](AsyncIterator[Element]):
haiway/utils/stream.py CHANGED
@@ -6,9 +6,7 @@ from asyncio import (
6
6
  )
7
7
  from collections.abc import AsyncIterator
8
8
 
9
- __all__ = [
10
- "AsyncStream",
11
- ]
9
+ __all__ = ("AsyncStream",)
12
10
 
13
11
 
14
12
  class AsyncStream[Element](AsyncIterator[Element]):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: haiway
3
- Version: 0.17.0
3
+ Version: 0.18.1
4
4
  Summary: Framework for dependency injection and state management within structured concurrency model.
5
5
  Project-URL: Homepage, https://miquido.com
6
6
  Project-URL: Repository, https://github.com/miquido/haiway.git
@@ -35,12 +35,16 @@ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
35
35
  Classifier: Typing :: Typed
36
36
  Requires-Python: >=3.12
37
37
  Provides-Extra: dev
38
- Requires-Dist: bandit~=1.7; extra == 'dev'
38
+ Requires-Dist: bandit~=1.8; extra == 'dev'
39
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'
40
+ Requires-Dist: pytest-asyncio~=0.26; extra == 'dev'
41
+ Requires-Dist: pytest-cov~=6.1; extra == 'dev'
42
+ Requires-Dist: pytest~=8.3; extra == 'dev'
43
43
  Requires-Dist: ruff~=0.11; extra == 'dev'
44
+ Provides-Extra: opentelemetry
45
+ Requires-Dist: opentelemetry-api; extra == 'opentelemetry'
46
+ Requires-Dist: opentelemetry-exporter-otlp-proto-grpc; extra == 'opentelemetry'
47
+ Requires-Dist: opentelemetry-sdk; extra == 'opentelemetry'
44
48
  Description-Content-Type: text/markdown
45
49
 
46
50
  # 🚗 haiway 🚕 🚚 🚙
@@ -0,0 +1,44 @@
1
+ haiway/__init__.py,sha256=GAukz1qQxO0mhvxSXdabC0JYiWw4nGKIGnmuImtDWdQ,2389
2
+ haiway/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ haiway/context/__init__.py,sha256=eoxqxUmFtkWLhc_gH6tqax9m90tSDie-jiP1BHruTdk,1102
4
+ haiway/context/access.py,sha256=49yzFt9yJU6p0pDE3jV_ZIvmgSk6_czzOOT2gijSJk8,18648
5
+ haiway/context/disposables.py,sha256=xE8RZYsYgXiOZY_TjWR7UiPG6dirna6y2LBZvMwTkIs,2588
6
+ haiway/context/identifier.py,sha256=i6nO-tozps7iDnpS5Se7CRch7hh6z2akjZthutxHte8,3943
7
+ haiway/context/observability.py,sha256=Z89y2MYTMBwfsEprm9fGP-s_ZqrA9NLtLZo4Fj2JDGM,13738
8
+ haiway/context/state.py,sha256=0oq7ctNO0urJd7rVzwwNtgpguoXuI6Tp1exfCsxrS2M,5981
9
+ haiway/context/tasks.py,sha256=QOxFdjmMp4IYff0ihHElKLCQrcVksSJmxqTlOKfoH4o,2907
10
+ haiway/context/types.py,sha256=WulPvpqUbI1vYyny-s2NItldDnk3zh1O-n_hGibFZRY,142
11
+ haiway/helpers/__init__.py,sha256=awAEVFv9_talefS6ymMbofDXigB-Klsxkj2uth8la-g,615
12
+ haiway/helpers/asynchrony.py,sha256=k_A0yCWUKSFfzYZ8WvqK4wqTMljv6ykMivmERrDLHIU,6266
13
+ haiway/helpers/caching.py,sha256=4WX2Md5AOncduYB_RLLENI2s9C2zD5kNJgKZjMzPIGY,13257
14
+ haiway/helpers/observability.py,sha256=-UXr7dHWTq-rmGe5HAR-tx2gzCbzAIoO42mwTISnMDE,6990
15
+ haiway/helpers/retries.py,sha256=unssUKBDOENvquh6R4Ud65TuSKl4mTHgZ5N_b7mAYa4,7533
16
+ haiway/helpers/throttling.py,sha256=U6HJvSzffw47730VeiXxXSW4VVxpDx48k0oIAOpL-O4,4115
17
+ haiway/helpers/timeouted.py,sha256=_M8diuD_GN49pl5KQA5fMKn4iUHsUuhkDSatAwWXiK8,3331
18
+ haiway/helpers/tracing.py,sha256=0KIpr-phVHikfoFrbgzeQi581tjp65fZnMqX0h0MRDo,3653
19
+ haiway/opentelemetry/__init__.py,sha256=TV-1C14mDAtcHhFZ29ActFQdrGH6x5KuGV9w-JlKYJg,91
20
+ haiway/opentelemetry/observability.py,sha256=oOjIVZldk14SfffWQh5FO1lGMW3Dce1PMLbiDZ9lV5s,14264
21
+ haiway/state/__init__.py,sha256=AaMqlMhO4zKS_XNevy3A7BHh5PxmguA-Sk_FnaNDY1Q,355
22
+ haiway/state/attributes.py,sha256=p6jUBzg62bOl0zAYTCa7NIllsaNY2Kt68IooQ9tb-y8,23311
23
+ haiway/state/path.py,sha256=-IpbUpF2QHWg3hEITkWYHJ6ZPoRVixu-SOSuWk-bbBY,21318
24
+ haiway/state/requirement.py,sha256=oKh9eqgTwxcJF4JNhU-DAbHbHsaACMNSlX-mkVjeJeY,7034
25
+ haiway/state/structure.py,sha256=rlA7qTr7rmJ_cU7_lJYq_y9Y-GO1wsaeHtjyDiIB8y8,16195
26
+ haiway/state/validation.py,sha256=LiCkItybUHT3oKG6IyLu2x6IKKvnWnabuEcVkTbEP9Y,14996
27
+ haiway/types/__init__.py,sha256=73DMgf60Ftf1gLRCSQG66Nyu3_QFjdRJggBtS4-RQkY,342
28
+ haiway/types/default.py,sha256=IIU6QA73aDUKXLNu78KQ2dLQFbyBrU74w7jlFswHl-8,2208
29
+ haiway/types/frozen.py,sha256=zLVkj85_lj6LrXjXAdv06Yy0MCj4spC8FQ-AhZMDPKg,70
30
+ haiway/types/missing.py,sha256=769MX5qpJ3zjNu6xLUH75On8FgheY06f2JYFR21gs9o,1712
31
+ haiway/utils/__init__.py,sha256=RqeTPXTVhvYp8rd5YLMSyH0hYF9y4j6aSxdbjG4VZtA,907
32
+ haiway/utils/always.py,sha256=dd6jDQ1j4DpJjTKO1J2Tv5xS8X1LnMC4kQ0D7DtKUvw,1230
33
+ haiway/utils/collections.py,sha256=pSBXhtLdhrLqmYo9YZEx716JI9S_sIztLJ5z5wi2d7Y,4162
34
+ haiway/utils/env.py,sha256=gdZcQS9l82hKm4Jojy1vnE42s89JqPFbiYODAE8I2sA,5339
35
+ haiway/utils/freezing.py,sha256=QsThd6FJ8TgErio7pCsHSnUKmVQbHZu6iEDYiqvJteo,614
36
+ haiway/utils/logs.py,sha256=NuwoqKQnMNi1FMIA91cVFnAPefUFeg3UIT50IOl3sJk,1571
37
+ haiway/utils/mimic.py,sha256=L5AS4WEL2aPMZAQZlvLvRzHl0cipI7ivky60_eL4iwY,1822
38
+ haiway/utils/noop.py,sha256=f54PSLHGEwCQNYXQHkPAW5NDE-tk5yjzkNL1pZj0TJQ,344
39
+ haiway/utils/queue.py,sha256=YTvCn3wgSwLJiLqolMx44sa3304Xkv3tJG77gvfWnZs,4114
40
+ haiway/utils/stream.py,sha256=Mjhy2S-ZDR1g_NsgS_nuBA8AgVbhrGXKvG3wjJ5mCJQ,2826
41
+ haiway-0.18.1.dist-info/METADATA,sha256=dIvIkA3j0iGbQuF5VGH2vhdgPMTWYQS0LXG7GwDKw7E,4527
42
+ haiway-0.18.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
43
+ haiway-0.18.1.dist-info/licenses/LICENSE,sha256=3phcpHVNBP8jsi77gOO0E7rgKeDeu99Pi7DSnK9YHoQ,1069
44
+ haiway-0.18.1.dist-info/RECORD,,