haiway 0.9.1__py3-none-any.whl → 0.10.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.
haiway/__init__.py CHANGED
@@ -28,6 +28,8 @@ from haiway.helpers import (
28
28
  from haiway.state import AttributePath, AttributeRequirement, State
29
29
  from haiway.types import (
30
30
  MISSING,
31
+ Default,
32
+ DefaultValue,
31
33
  Missing,
32
34
  frozenlist,
33
35
  is_missing,
@@ -39,6 +41,7 @@ from haiway.utils import (
39
41
  always,
40
42
  as_dict,
41
43
  as_list,
44
+ as_set,
42
45
  as_tuple,
43
46
  async_always,
44
47
  async_noop,
@@ -59,6 +62,8 @@ __all__ = [
59
62
  "AsyncQueue",
60
63
  "AttributePath",
61
64
  "AttributeRequirement",
65
+ "Default",
66
+ "DefaultValue",
62
67
  "Disposable",
63
68
  "Disposables",
64
69
  "MetricsContext",
@@ -78,6 +83,7 @@ __all__ = [
78
83
  "always",
79
84
  "as_dict",
80
85
  "as_list",
86
+ "as_set",
81
87
  "as_tuple",
82
88
  "async_always",
83
89
  "async_noop",
@@ -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 = attributes
512
+ resolved_attribute.extra = {
513
+ "attributes": attributes,
514
+ "required": annotation.__required_keys__,
515
+ }
513
516
  return resolved_attribute
514
517
 
515
518
 
haiway/state/structure.py CHANGED
@@ -1,5 +1,5 @@
1
1
  import typing
2
- from collections.abc import Mapping
2
+ from collections.abc import Callable, Mapping
3
3
  from types import EllipsisType, GenericAlias
4
4
  from typing import (
5
5
  Any,
@@ -10,34 +10,55 @@ from typing import (
10
10
  cast,
11
11
  dataclass_transform,
12
12
  final,
13
+ overload,
13
14
  )
14
15
  from weakref import WeakValueDictionary
15
16
 
16
17
  from haiway.state.attributes import AttributeAnnotation, attribute_annotations
17
18
  from haiway.state.path import AttributePath
18
- from haiway.state.validation import (
19
- AttributeValidation,
20
- AttributeValidator,
21
- )
22
- from haiway.types import MISSING, Missing, not_missing
19
+ from haiway.state.validation import AttributeValidation, AttributeValidator
20
+ from haiway.types import MISSING, DefaultValue, Missing, not_missing
23
21
 
24
22
  __all__ = [
25
23
  "State",
26
24
  ]
27
25
 
28
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))
48
+
49
+
29
50
  @final
30
51
  class StateAttribute[Value]:
31
52
  def __init__(
32
53
  self,
33
54
  name: str,
34
55
  annotation: AttributeAnnotation,
35
- default: Value | Missing,
56
+ default: DefaultValue[Value],
36
57
  validator: AttributeValidation[Value],
37
58
  ) -> None:
38
59
  self.name: str = name
39
60
  self.annotation: AttributeAnnotation = annotation
40
- self.default: Value | Missing = default
61
+ self.default: DefaultValue[Value] = default
41
62
  self.validator: AttributeValidation[Value] = validator
42
63
 
43
64
  def validated(
@@ -45,13 +66,13 @@ class StateAttribute[Value]:
45
66
  value: Any | Missing,
46
67
  /,
47
68
  ) -> Value:
48
- return self.validator(self.default if value is MISSING else value)
69
+ return self.validator(self.default() if value is MISSING else value)
49
70
 
50
71
 
51
72
  @dataclass_transform(
52
73
  kw_only_default=True,
53
74
  frozen_default=True,
54
- field_specifiers=(),
75
+ field_specifiers=(DefaultValue,),
55
76
  )
56
77
  class StateMeta(type):
57
78
  def __new__(
@@ -81,7 +102,7 @@ class StateMeta(type):
81
102
  attributes[key] = StateAttribute(
82
103
  name=key,
83
104
  annotation=annotation.update_required(default is MISSING),
84
- default=default,
105
+ default=_resolve_default(default),
85
106
  validator=AttributeValidator.of(
86
107
  annotation,
87
108
  recursion_guard={
@@ -187,6 +208,18 @@ class StateMeta(type):
187
208
  return False # we have different base / comparing to not parametrized
188
209
 
189
210
 
211
+ def _resolve_default[Value](
212
+ value: DefaultValue[Value] | Value | Missing,
213
+ ) -> DefaultValue[Value]:
214
+ if isinstance(value, DefaultValue):
215
+ return cast(DefaultValue[Value], value)
216
+
217
+ return DefaultValue[Value](
218
+ value,
219
+ factory=MISSING,
220
+ )
221
+
222
+
190
223
  _types_cache: WeakValueDictionary[
191
224
  tuple[
192
225
  Any,
@@ -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
- formatted_type: str = str(annotation)
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 MISSING:
410
- validated[key_validator(key)] = validator(element)
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
haiway/types/__init__.py CHANGED
@@ -1,8 +1,11 @@
1
+ from haiway.types.default import Default, DefaultValue
1
2
  from haiway.types.frozen import frozenlist
2
3
  from haiway.types.missing import MISSING, Missing, is_missing, not_missing, when_missing
3
4
 
4
5
  __all__ = [
5
6
  "MISSING",
7
+ "Default",
8
+ "DefaultValue",
6
9
  "Missing",
7
10
  "frozenlist",
8
11
  "is_missing",
@@ -0,0 +1,108 @@
1
+ from collections.abc import Callable
2
+ from typing import Any, cast, final, overload
3
+
4
+ from haiway.types.missing import MISSING, Missing, not_missing
5
+ from haiway.utils.always import always
6
+
7
+ __all__ = [
8
+ "Default",
9
+ "DefaultValue",
10
+ ]
11
+
12
+
13
+ @final
14
+ class DefaultValue[Value]:
15
+ @overload
16
+ def __init__(
17
+ self,
18
+ value: Value,
19
+ /,
20
+ ) -> None: ...
21
+
22
+ @overload
23
+ def __init__(
24
+ self,
25
+ /,
26
+ *,
27
+ factory: Callable[[], Value],
28
+ ) -> None: ...
29
+
30
+ @overload
31
+ def __init__(
32
+ self,
33
+ value: Value | Missing,
34
+ /,
35
+ *,
36
+ factory: Callable[[], Value] | Missing,
37
+ ) -> None: ...
38
+
39
+ def __init__(
40
+ self,
41
+ value: Value | Missing = MISSING,
42
+ /,
43
+ *,
44
+ factory: Callable[[], Value] | Missing = MISSING,
45
+ ) -> None:
46
+ assert ( # nosec: B101
47
+ value is MISSING or factory is MISSING
48
+ ), "Can't specify both default value and factory"
49
+
50
+ self._value: Callable[[], Value | Missing]
51
+ if not_missing(factory):
52
+ object.__setattr__(
53
+ self,
54
+ "_value",
55
+ factory,
56
+ )
57
+
58
+ else:
59
+ object.__setattr__(
60
+ self,
61
+ "_value",
62
+ always(value),
63
+ )
64
+
65
+ def __call__(self) -> Value | Missing:
66
+ return self._value()
67
+
68
+ def __setattr__(
69
+ self,
70
+ __name: str,
71
+ __value: Any,
72
+ ) -> None:
73
+ raise AttributeError("Missing can't be modified")
74
+
75
+ def __delattr__(
76
+ self,
77
+ __name: str,
78
+ ) -> None:
79
+ raise AttributeError("Missing can't be modified")
80
+
81
+
82
+ @overload
83
+ def Default[Value](
84
+ value: Value,
85
+ /,
86
+ ) -> Value: ...
87
+
88
+
89
+ @overload
90
+ def Default[Value](
91
+ *,
92
+ factory: Callable[[], Value],
93
+ ) -> Value: ...
94
+
95
+
96
+ def Default[Value](
97
+ value: Value | Missing = MISSING,
98
+ /,
99
+ *,
100
+ factory: Callable[[], Value] | Missing = MISSING,
101
+ ) -> Value: # it is actually a DefaultValue, but type checker has to be fooled most some cases
102
+ return cast(
103
+ Value,
104
+ DefaultValue(
105
+ value,
106
+ factory=factory,
107
+ ),
108
+ )
haiway/types/missing.py CHANGED
@@ -85,6 +85,7 @@ def not_missing[Value](
85
85
  def when_missing[Value](
86
86
  check: Value | Missing,
87
87
  /,
88
+ *,
88
89
  value: Value,
89
90
  ) -> Value:
90
91
  if check is MISSING:
haiway/utils/__init__.py CHANGED
@@ -1,18 +1,18 @@
1
1
  from haiway.utils.always import always, async_always
2
+ from haiway.utils.collections import as_dict, as_list, as_set, as_tuple
2
3
  from haiway.utils.env import getenv_bool, getenv_float, getenv_int, getenv_str, load_env
3
- from haiway.utils.immutable import freeze
4
+ from haiway.utils.freezing import freeze
4
5
  from haiway.utils.logs import setup_logging
5
- from haiway.utils.mappings import as_dict
6
6
  from haiway.utils.mimic import mimic_function
7
7
  from haiway.utils.noop import async_noop, noop
8
8
  from haiway.utils.queue import AsyncQueue
9
- from haiway.utils.sequences import as_list, as_tuple
10
9
 
11
10
  __all__ = [
12
11
  "AsyncQueue",
13
12
  "always",
14
13
  "as_dict",
15
14
  "as_list",
15
+ "as_set",
16
16
  "as_tuple",
17
17
  "async_always",
18
18
  "async_noop",
@@ -0,0 +1,108 @@
1
+ from collections.abc import Mapping, Sequence, Set
2
+
3
+ __all__ = [
4
+ "as_dict",
5
+ "as_list",
6
+ "as_set",
7
+ "as_tuple",
8
+ ]
9
+
10
+
11
+ def as_list[T](
12
+ collection: Sequence[T],
13
+ /,
14
+ ) -> list[T]:
15
+ """
16
+ Converts any given Sequence into a list.
17
+
18
+ Parameters
19
+ ----------
20
+ collection : Sequence[T]
21
+ The input collection to be converted.
22
+
23
+ Returns
24
+ -------
25
+ list[T]
26
+ A new list containing all elements of the input collection,
27
+ or the original list if it was already one.
28
+ """
29
+ if isinstance(collection, list):
30
+ return collection
31
+
32
+ else:
33
+ return list(collection)
34
+
35
+
36
+ def as_tuple[T](
37
+ collection: Sequence[T],
38
+ /,
39
+ ) -> tuple[T, ...]:
40
+ """
41
+ Converts any given Sequence into a tuple.
42
+
43
+ Parameters
44
+ ----------
45
+ collection : Sequence[T]
46
+ The input collection to be converted.
47
+
48
+ Returns
49
+ -------
50
+ tuple[T]
51
+ A new tuple containing all elements of the input collection,
52
+ or the original tuple if it was already one.
53
+ """
54
+ if isinstance(collection, tuple):
55
+ return collection
56
+
57
+ else:
58
+ return tuple(collection)
59
+
60
+
61
+ def as_set[T](
62
+ collection: Set[T],
63
+ /,
64
+ ) -> set[T]:
65
+ """
66
+ Converts any given Set into a set.
67
+
68
+ Parameters
69
+ ----------
70
+ collection : Set[T]
71
+ The input collection to be converted.
72
+
73
+ Returns
74
+ -------
75
+ set[T]
76
+ A new set containing all elements of the input collection,
77
+ or the original set if it was already one.
78
+ """
79
+ if isinstance(collection, set):
80
+ return collection
81
+
82
+ else:
83
+ return set(collection)
84
+
85
+
86
+ def as_dict[K, V](
87
+ collection: Mapping[K, V],
88
+ /,
89
+ ) -> dict[K, V]:
90
+ """
91
+ Converts any given Mapping into a dict.
92
+
93
+ Parameters
94
+ ----------
95
+ collection : Mapping[K, V]
96
+ The input collection to be converted.
97
+
98
+ Returns
99
+ -------
100
+ dict[K, V]
101
+ A new dict containing all elements of the input collection,
102
+ or the original dict if it was already one.
103
+ """
104
+ if isinstance(collection, dict):
105
+ return collection
106
+
107
+ else:
108
+ return dict(collection)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: haiway
3
- Version: 0.9.1
3
+ Version: 0.10.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,4 +1,4 @@
1
- haiway/__init__.py,sha256=JoXmo8maenl0yVsE1CetmJjP80v0ifU2_5dJdzuzadg,1837
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
4
  haiway/context/access.py,sha256=RpTvs7107NoAxCgxxbp7BUThOM9gLXo_Vxv-JgDqW58,16954
@@ -18,26 +18,26 @@ haiway/helpers/throttling.py,sha256=zo0OwFq64si5KUwhd58cFHLmGAmYwRbFRJMbv9suhPs,
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=iQ7TJHnT3hlcYwKcxchXE56zU8WbOTJZhsVn_HocXBc,22903
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
- haiway/state/structure.py,sha256=N8lh_yKyROm5GG1NjIxmidYttWZkYH0S3fhEpNIx5FY,11694
25
- haiway/state/validation.py,sha256=hbvzPVBD36AD_B5u9ER4A11vjaXR9LJLD8G2k8IE-AI,13492
26
- haiway/types/__init__.py,sha256=00Ulp2BxcIWm9vWXKQPodpFEwE8hpqj6OYgrNxelp5s,252
24
+ haiway/state/structure.py,sha256=bSIj0S_HG-F1Z5GxSlY6VpGtrtiwG82-AIL_PL1lRLo,12465
25
+ haiway/state/validation.py,sha256=r0EMIs-nvoXsmSA74oGu6Lrbw8lkzmseaY82_-E8ous,13814
26
+ haiway/types/__init__.py,sha256=-j4uDN6ix3GBXLBqXC-k_QOJSDlO6zvNCxDej8vVzek,342
27
+ haiway/types/default.py,sha256=IVQsNzDnfukL3-XlScYv2PgTcJ1x_BNP9i5UlS5oEbg,2179
27
28
  haiway/types/frozen.py,sha256=CZhFCXnWAKEhuWSfILxA8smfdpMd5Ku694ycfLh98R8,76
28
- haiway/types/missing.py,sha256=JiXo5xdi7H-PbIJr0fuK5wpOuQZhjrDYUkMlfIFcsaE,1705
29
- haiway/utils/__init__.py,sha256=pBeKpaPNfEHeKe8tT81JV-zADS1Qj32WnYcFP9ThtKk,749
29
+ haiway/types/missing.py,sha256=rDnyA2wxPkTbJl0L-zbo0owp7IJ04xkCIp6xD6wh8NI,1712
30
+ haiway/utils/__init__.py,sha256=O7qmAmUktX-X_5D1L5FJMeCFEiOVrrnyYSyiycm4nyg,739
30
31
  haiway/utils/always.py,sha256=2abp8Lm9rQkrfS3rm1Iqhb-IcWyVfH1BULab3KMxgOw,1234
32
+ haiway/utils/collections.py,sha256=_MwB4o8sCZeAjrwry8Pu9JvGlt8m7O7MqWSFFmPjJ6k,2120
31
33
  haiway/utils/env.py,sha256=vlW21LEp8uOVNnUXpBfPtj3zKi9Kkjoemb_H5hQpYPQ,4433
32
- haiway/utils/immutable.py,sha256=K34ZIMzbkpgkHKH-KF73plEbXExsajNRkRTYp9nJEf4,620
34
+ haiway/utils/freezing.py,sha256=K34ZIMzbkpgkHKH-KF73plEbXExsajNRkRTYp9nJEf4,620
33
35
  haiway/utils/logs.py,sha256=oDsc1ZdqKDjlTlctLbDcp9iX98Acr-1tdw-Pyg3DElo,1577
34
- haiway/utils/mappings.py,sha256=XxELxCpGEPfRERO54ySIbcV7t4Sxcb7NuQmyayU5GoY,555
35
36
  haiway/utils/mimic.py,sha256=BkVjTVP2TxxC8GChPGyDV6UXVwJmiRiSWeOYZNZFHxs,1828
36
37
  haiway/utils/noop.py,sha256=qgbZlOKWY6_23Zs43OLukK2HagIQKRyR04zrFVm5rWI,344
37
38
  haiway/utils/queue.py,sha256=oQ3GXCJ-PGNtMEr6EPdgqAvYZoj8lAa7Z2drBKBEoBM,2345
38
- haiway/utils/sequences.py,sha256=mXLAzH94HZHi0P7fF593FHNNTn1eNMRHA4Uxlr3UOu0,1064
39
- haiway-0.9.1.dist-info/LICENSE,sha256=GehQEW_I1pkmxkkj3NEa7rCTQKYBn7vTPabpDYJlRuo,1063
40
- haiway-0.9.1.dist-info/METADATA,sha256=V8r02s5eNu41YHlynIq3wUTpXRCVSYBoOSNusk1ckU8,3894
41
- haiway-0.9.1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
42
- haiway-0.9.1.dist-info/top_level.txt,sha256=_LdXVLzUzgkvAGQnQJj5kQfoFhpPW6EF4Kj9NapniLg,7
43
- haiway-0.9.1.dist-info/RECORD,,
39
+ haiway-0.10.1.dist-info/LICENSE,sha256=GehQEW_I1pkmxkkj3NEa7rCTQKYBn7vTPabpDYJlRuo,1063
40
+ haiway-0.10.1.dist-info/METADATA,sha256=0m2_QdvBq_XiPN3GiIYFIE0cXf8cVw5esT8kA6_K_5Y,3895
41
+ haiway-0.10.1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
42
+ haiway-0.10.1.dist-info/top_level.txt,sha256=_LdXVLzUzgkvAGQnQJj5kQfoFhpPW6EF4Kj9NapniLg,7
43
+ haiway-0.10.1.dist-info/RECORD,,
haiway/utils/mappings.py DELETED
@@ -1,30 +0,0 @@
1
- from collections.abc import Mapping
2
-
3
- __all__ = [
4
- "as_dict",
5
- ]
6
-
7
-
8
- def as_dict[K, V](
9
- mapping: Mapping[K, V],
10
- /,
11
- ) -> dict[K, V]:
12
- """
13
- Converts any given mapping into a dict.
14
-
15
- Parameters
16
- ----------
17
- mapping : Mapping[K, V]
18
- The input mapping to be converted.
19
-
20
- Returns
21
- -------
22
- dict[K, V]
23
- A new dict containing all elements of the input mapping,
24
- or the original dict if it was already one.
25
- """
26
- if isinstance(mapping, dict):
27
- return mapping
28
-
29
- else:
30
- return dict(mapping)
haiway/utils/sequences.py DELETED
@@ -1,56 +0,0 @@
1
- from collections.abc import Sequence
2
-
3
- __all__ = [
4
- "as_list",
5
- "as_tuple",
6
- ]
7
-
8
-
9
- def as_list[T](
10
- sequence: Sequence[T],
11
- /,
12
- ) -> list[T]:
13
- """
14
- Converts any given sequence into a list.
15
-
16
- Parameters
17
- ----------
18
- sequence : Sequence[T]
19
- The input sequence to be converted.
20
-
21
- Returns
22
- -------
23
- list[T]
24
- A new list containing all elements of the input sequence,
25
- or the original list if it was already one.
26
- """
27
- if isinstance(sequence, list):
28
- return sequence
29
-
30
- else:
31
- return list(sequence)
32
-
33
-
34
- def as_tuple[T](
35
- sequence: Sequence[T],
36
- /,
37
- ) -> tuple[T, ...]:
38
- """
39
- Converts any given sequence into a tuple.
40
-
41
- Parameters
42
- ----------
43
- sequence : Sequence[T]
44
- The input sequence to be converted.
45
-
46
- Returns
47
- -------
48
- tuple[T]
49
- A new tuple containing all elements of the input sequence,
50
- or the original tuple if it was already one.
51
- """
52
- if isinstance(sequence, tuple):
53
- return sequence
54
-
55
- else:
56
- return tuple(sequence)
File without changes