haiway 0.2.0__tar.gz → 0.3.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 (46) hide show
  1. {haiway-0.2.0/src/haiway.egg-info → haiway-0.3.1}/PKG-INFO +1 -1
  2. {haiway-0.2.0 → haiway-0.3.1}/pyproject.toml +1 -1
  3. {haiway-0.2.0 → haiway-0.3.1}/src/haiway/__init__.py +3 -1
  4. {haiway-0.2.0 → haiway-0.3.1}/src/haiway/context/__init__.py +3 -0
  5. {haiway-0.2.0 → haiway-0.3.1}/src/haiway/context/access.py +79 -23
  6. {haiway-0.2.0/src/haiway/helpers → haiway-0.3.1/src/haiway/context}/disposables.py +3 -0
  7. {haiway-0.2.0 → haiway-0.3.1}/src/haiway/helpers/__init__.py +2 -5
  8. haiway-0.2.0/src/haiway/helpers/asynchronous.py → haiway-0.3.1/src/haiway/helpers/asynchrony.py +12 -28
  9. {haiway-0.2.0 → haiway-0.3.1}/src/haiway/types/missing.py +3 -3
  10. {haiway-0.2.0 → haiway-0.3.1/src/haiway.egg-info}/PKG-INFO +1 -1
  11. {haiway-0.2.0 → haiway-0.3.1}/src/haiway.egg-info/SOURCES.txt +3 -3
  12. {haiway-0.2.0 → haiway-0.3.1}/LICENSE +0 -0
  13. {haiway-0.2.0 → haiway-0.3.1}/README.md +0 -0
  14. {haiway-0.2.0 → haiway-0.3.1}/setup.cfg +0 -0
  15. {haiway-0.2.0 → haiway-0.3.1}/src/haiway/context/metrics.py +0 -0
  16. {haiway-0.2.0 → haiway-0.3.1}/src/haiway/context/state.py +0 -0
  17. {haiway-0.2.0 → haiway-0.3.1}/src/haiway/context/tasks.py +0 -0
  18. {haiway-0.2.0 → haiway-0.3.1}/src/haiway/context/types.py +0 -0
  19. /haiway-0.2.0/src/haiway/helpers/cached.py → /haiway-0.3.1/src/haiway/helpers/caching.py +0 -0
  20. {haiway-0.2.0 → haiway-0.3.1}/src/haiway/helpers/retries.py +0 -0
  21. {haiway-0.2.0 → haiway-0.3.1}/src/haiway/helpers/throttling.py +0 -0
  22. {haiway-0.2.0 → haiway-0.3.1}/src/haiway/helpers/timeouted.py +0 -0
  23. {haiway-0.2.0 → haiway-0.3.1}/src/haiway/py.typed +0 -0
  24. {haiway-0.2.0 → haiway-0.3.1}/src/haiway/state/__init__.py +0 -0
  25. {haiway-0.2.0 → haiway-0.3.1}/src/haiway/state/attributes.py +0 -0
  26. {haiway-0.2.0 → haiway-0.3.1}/src/haiway/state/structure.py +0 -0
  27. {haiway-0.2.0 → haiway-0.3.1}/src/haiway/state/validation.py +0 -0
  28. {haiway-0.2.0 → haiway-0.3.1}/src/haiway/types/__init__.py +0 -0
  29. {haiway-0.2.0 → haiway-0.3.1}/src/haiway/types/frozen.py +0 -0
  30. {haiway-0.2.0 → haiway-0.3.1}/src/haiway/utils/__init__.py +0 -0
  31. {haiway-0.2.0 → haiway-0.3.1}/src/haiway/utils/always.py +0 -0
  32. {haiway-0.2.0 → haiway-0.3.1}/src/haiway/utils/env.py +0 -0
  33. {haiway-0.2.0 → haiway-0.3.1}/src/haiway/utils/immutable.py +0 -0
  34. {haiway-0.2.0 → haiway-0.3.1}/src/haiway/utils/logs.py +0 -0
  35. {haiway-0.2.0 → haiway-0.3.1}/src/haiway/utils/mimic.py +0 -0
  36. {haiway-0.2.0 → haiway-0.3.1}/src/haiway/utils/noop.py +0 -0
  37. {haiway-0.2.0 → haiway-0.3.1}/src/haiway/utils/queue.py +0 -0
  38. {haiway-0.2.0 → haiway-0.3.1}/src/haiway.egg-info/dependency_links.txt +0 -0
  39. {haiway-0.2.0 → haiway-0.3.1}/src/haiway.egg-info/requires.txt +0 -0
  40. {haiway-0.2.0 → haiway-0.3.1}/src/haiway.egg-info/top_level.txt +0 -0
  41. {haiway-0.2.0 → haiway-0.3.1}/tests/test_async_queue.py +0 -0
  42. {haiway-0.2.0 → haiway-0.3.1}/tests/test_auto_retry.py +0 -0
  43. {haiway-0.2.0 → haiway-0.3.1}/tests/test_cache.py +0 -0
  44. {haiway-0.2.0 → haiway-0.3.1}/tests/test_context.py +0 -0
  45. {haiway-0.2.0 → haiway-0.3.1}/tests/test_state.py +0 -0
  46. {haiway-0.2.0 → haiway-0.3.1}/tests/test_timeout.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: haiway
3
- Version: 0.2.0
3
+ Version: 0.3.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.2.0"
8
+ version = "0.3.1"
9
9
  readme = "README.md"
10
10
  maintainers = [
11
11
  { name = "Kacper Kaliński", email = "kacper.kalinski@miquido.com" },
@@ -1,10 +1,12 @@
1
1
  from haiway.context import (
2
+ Disposable,
3
+ Disposables,
2
4
  MissingContext,
3
5
  MissingState,
4
6
  ScopeMetrics,
5
7
  ctx,
6
8
  )
7
- from haiway.helpers import Disposable, Disposables, asynchronous, cache, retry, throttle, timeout
9
+ from haiway.helpers import asynchronous, cache, retry, throttle, timeout
8
10
  from haiway.state import State
9
11
  from haiway.types import (
10
12
  MISSING,
@@ -1,9 +1,12 @@
1
1
  from haiway.context.access import ctx
2
+ from haiway.context.disposables import Disposable, Disposables
2
3
  from haiway.context.metrics import ScopeMetrics
3
4
  from haiway.context.types import MissingContext, MissingState
4
5
 
5
6
  __all__ = [
6
7
  "ctx",
8
+ "Disposable",
9
+ "Disposables",
7
10
  "MissingContext",
8
11
  "MissingState",
9
12
  "ScopeMetrics",
@@ -5,11 +5,13 @@ from asyncio import (
5
5
  from collections.abc import (
6
6
  Callable,
7
7
  Coroutine,
8
+ Iterable,
8
9
  )
9
10
  from logging import Logger
10
11
  from types import TracebackType
11
12
  from typing import Any, final
12
13
 
14
+ from haiway.context.disposables import Disposable, Disposables
13
15
  from haiway.context.metrics import MetricsContext, ScopeMetrics
14
16
  from haiway.context.state import StateContext
15
17
  from haiway.context.tasks import TaskGroupContext
@@ -23,25 +25,41 @@ __all__ = [
23
25
 
24
26
  @final
25
27
  class ScopeContext:
26
- def __init__(
28
+ def __init__( # noqa: PLR0913
27
29
  self,
30
+ trace_id: str | None,
31
+ name: str,
32
+ logger: Logger | None,
33
+ state: tuple[State, ...],
34
+ disposables: Disposables | None,
28
35
  task_group: TaskGroupContext,
29
- state: StateContext,
30
- metrics: MetricsContext,
31
36
  completion: Callable[[ScopeMetrics], Coroutine[None, None, None]] | None,
32
37
  ) -> None:
33
38
  self._task_group: TaskGroupContext = task_group
34
- self._state: StateContext = state
35
- self._metrics: MetricsContext = metrics
39
+ self._logger: Logger | None = logger
40
+ self._trace_id: str | None = trace_id
41
+ self._name: str = name
42
+ self._state_context: StateContext
43
+ self._state: tuple[State, ...] = state
44
+ self._disposables: Disposables | None = disposables
45
+ self._metrics_context: MetricsContext
36
46
  self._completion: Callable[[ScopeMetrics], Coroutine[None, None, None]] | None = completion
37
47
 
38
48
  freeze(self)
39
49
 
40
50
  def __enter__(self) -> None:
41
51
  assert self._completion is None, "Can't enter synchronous context with completion" # nosec: B101
52
+ assert self._disposables is None, "Can't enter synchronous context with disposables" # nosec: B101
53
+
54
+ self._state_context = StateContext.updated(self._state)
55
+ self._metrics_context = MetricsContext.scope(
56
+ self._name,
57
+ logger=self._logger,
58
+ trace_id=self._trace_id,
59
+ )
42
60
 
43
- self._state.__enter__()
44
- self._metrics.__enter__()
61
+ self._state_context.__enter__()
62
+ self._metrics_context.__enter__()
45
63
 
46
64
  def __exit__(
47
65
  self,
@@ -49,49 +67,71 @@ class ScopeContext:
49
67
  exc_val: BaseException | None,
50
68
  exc_tb: TracebackType | None,
51
69
  ) -> None:
52
- self._metrics.__exit__(
70
+ self._metrics_context.__exit__(
53
71
  exc_type=exc_type,
54
72
  exc_val=exc_val,
55
73
  exc_tb=exc_tb,
56
74
  )
57
75
 
58
- self._state.__exit__(
76
+ self._state_context.__exit__(
59
77
  exc_type=exc_type,
60
78
  exc_val=exc_val,
61
79
  exc_tb=exc_tb,
62
80
  )
63
81
 
64
82
  async def __aenter__(self) -> None:
65
- self._state.__enter__()
66
- self._metrics.__enter__()
67
83
  await self._task_group.__aenter__()
68
84
 
85
+ if self._disposables:
86
+ self._state_context = StateContext.updated(
87
+ (*self._state, *await self._disposables.__aenter__())
88
+ )
89
+
90
+ else:
91
+ self._state_context = StateContext.updated(self._state)
92
+
93
+ self._metrics_context = MetricsContext.scope(
94
+ self._name,
95
+ logger=self._logger,
96
+ trace_id=self._trace_id,
97
+ )
98
+
99
+ self._state_context.__enter__()
100
+ self._metrics_context.__enter__()
101
+
69
102
  async def __aexit__(
70
103
  self,
71
104
  exc_type: type[BaseException] | None,
72
105
  exc_val: BaseException | None,
73
106
  exc_tb: TracebackType | None,
74
107
  ) -> None:
108
+ if self._disposables:
109
+ await self._disposables.__aexit__(
110
+ exc_type=exc_type,
111
+ exc_val=exc_val,
112
+ exc_tb=exc_tb,
113
+ )
114
+
75
115
  await self._task_group.__aexit__(
76
116
  exc_type=exc_type,
77
117
  exc_val=exc_val,
78
118
  exc_tb=exc_tb,
79
119
  )
80
120
 
81
- self._metrics.__exit__(
121
+ self._metrics_context.__exit__(
82
122
  exc_type=exc_type,
83
123
  exc_val=exc_val,
84
124
  exc_tb=exc_tb,
85
125
  )
86
126
 
87
- self._state.__exit__(
127
+ self._state_context.__exit__(
88
128
  exc_type=exc_type,
89
129
  exc_val=exc_val,
90
130
  exc_tb=exc_tb,
91
131
  )
92
132
 
93
133
  if completion := self._completion:
94
- await completion(self._metrics._metrics) # pyright: ignore[reportPrivateUsage]
134
+ await completion(self._metrics_context._metrics) # pyright: ignore[reportPrivateUsage]
95
135
 
96
136
 
97
137
  @final
@@ -101,6 +141,7 @@ class ctx:
101
141
  name: str,
102
142
  /,
103
143
  *state: State,
144
+ disposables: Disposables | Iterable[Disposable] | None = None,
104
145
  logger: Logger | None = None,
105
146
  trace_id: str | None = None,
106
147
  completion: Callable[[ScopeMetrics], Coroutine[None, None, None]] | None = None,
@@ -114,9 +155,14 @@ class ctx:
114
155
  name: Value
115
156
  name of the scope context
116
157
 
117
- *state: State
118
- state propagated within the scope context, will be merged with current if any\
119
- by replacing current with provided on conflict
158
+ *state: State | Disposable
159
+ state propagated within the scope context, will be merged with current state by\
160
+ replacing current with provided on conflict.
161
+
162
+ disposables: Disposables | list[Disposable] | None
163
+ disposables consumed within the context when entered. Produced state will automatically\
164
+ be added to the scope state. Using asynchronous context is required if any disposables\
165
+ were provided.
120
166
 
121
167
  logger: Logger | None
122
168
  logger used within the scope context, when not provided current logger will be used\
@@ -138,14 +184,24 @@ class ctx:
138
184
  context object intended to enter context manager with it
139
185
  """
140
186
 
187
+ resolved_disposables: Disposables | None
188
+ match disposables:
189
+ case None:
190
+ resolved_disposables = None
191
+
192
+ case Disposables() as disposables:
193
+ resolved_disposables = disposables
194
+
195
+ case iterable:
196
+ resolved_disposables = Disposables(*iterable)
197
+
141
198
  return ScopeContext(
199
+ trace_id=trace_id,
200
+ name=name,
201
+ logger=logger,
202
+ state=state,
203
+ disposables=resolved_disposables,
142
204
  task_group=TaskGroupContext(),
143
- metrics=MetricsContext.scope(
144
- name,
145
- logger=logger,
146
- trace_id=trace_id,
147
- ),
148
- state=StateContext.updated(state),
149
205
  completion=completion,
150
206
  )
151
207
 
@@ -53,6 +53,9 @@ class Disposables:
53
53
  elif exceptions:
54
54
  raise exceptions[0]
55
55
 
56
+ def __bool__(self) -> bool:
57
+ return len(self._disposables) > 0
58
+
56
59
  async def __aenter__(self) -> Iterable[State]:
57
60
  return await self.initialize()
58
61
 
@@ -1,6 +1,5 @@
1
- from haiway.helpers.asynchronous import asynchronous
2
- from haiway.helpers.cached import cache
3
- from haiway.helpers.disposables import Disposable, Disposables
1
+ from haiway.helpers.asynchrony import asynchronous
2
+ from haiway.helpers.caching import cache
4
3
  from haiway.helpers.retries import retry
5
4
  from haiway.helpers.throttling import throttle
6
5
  from haiway.helpers.timeouted import timeout
@@ -8,8 +7,6 @@ from haiway.helpers.timeouted import timeout
8
7
  __all__ = [
9
8
  "asynchronous",
10
9
  "cache",
11
- "Disposable",
12
- "Disposables",
13
10
  "retry",
14
11
  "throttle",
15
12
  "timeout",
@@ -3,9 +3,9 @@ from collections.abc import Callable, Coroutine
3
3
  from concurrent.futures import Executor
4
4
  from contextvars import Context, copy_context
5
5
  from functools import partial
6
- from typing import Any, Literal, cast, overload
6
+ from typing import Any, cast, overload
7
7
 
8
- from haiway.types.missing import MISSING, Missing, not_missing
8
+ from haiway.types.missing import MISSING, Missing
9
9
 
10
10
  __all__ = [
11
11
  "asynchronous",
@@ -25,7 +25,7 @@ def asynchronous[**Args, Result]() -> (
25
25
  def asynchronous[**Args, Result](
26
26
  *,
27
27
  loop: AbstractEventLoop | None = None,
28
- executor: Executor | Literal["default"],
28
+ executor: Executor,
29
29
  ) -> Callable[
30
30
  [Callable[Args, Result]],
31
31
  Callable[Args, Coroutine[None, None, Result]],
@@ -43,7 +43,7 @@ def asynchronous[**Args, Result](
43
43
  function: Callable[Args, Result] | None = None,
44
44
  /,
45
45
  loop: AbstractEventLoop | None = None,
46
- executor: Executor | Literal["default"] | Missing = MISSING,
46
+ executor: Executor | Missing = MISSING,
47
47
  ) -> (
48
48
  Callable[
49
49
  [Callable[Args, Result]],
@@ -63,10 +63,9 @@ def asynchronous[**Args, Result](
63
63
  loop: AbstractEventLoop | None
64
64
  loop used to call the function. When None was provided the loop currently running while \
65
65
  executing the function will be used. Default is None.
66
- executor: Executor | Literal["default"] | Missing
67
- executor used to run the function. Specifying "default" uses a default loop executor.
68
- When not provided (Missing) no executor will be used \
69
- (function will by just wrapped as an async function without any executor)
66
+ executor: Executor | Missing
67
+ executor used to run the function. When not provided (Missing) default loop executor\
68
+ will be used.
70
69
 
71
70
  Returns
72
71
  -------
@@ -79,26 +78,11 @@ def asynchronous[**Args, Result](
79
78
  ) -> Callable[Args, Coroutine[None, None, Result]]:
80
79
  assert not iscoroutinefunction(wrapped), "Cannot wrap async function in executor" # nosec: B101
81
80
 
82
- if not_missing(executor):
83
- return _ExecutorWrapper(
84
- wrapped,
85
- loop=loop,
86
- executor=cast(Executor | None, None if executor == "default" else executor),
87
- )
88
-
89
- else:
90
-
91
- async def wrapper(
92
- *args: Args.args,
93
- **kwargs: Args.kwargs,
94
- ) -> Result:
95
- return wrapped(
96
- *args,
97
- **kwargs,
98
- )
99
-
100
- _mimic_async(wrapped, within=wrapper)
101
- return wrapper
81
+ return _ExecutorWrapper(
82
+ wrapped,
83
+ loop=loop,
84
+ executor=cast(Executor | None, None if executor is MISSING else executor),
85
+ )
102
86
 
103
87
  if function := function:
104
88
  return wrap(wrapped=function)
@@ -46,20 +46,20 @@ class Missing(metaclass=MissingType):
46
46
  self,
47
47
  name: str,
48
48
  ) -> Any:
49
- raise RuntimeError("Missing has no attributes")
49
+ raise AttributeError("Missing has no attributes")
50
50
 
51
51
  def __setattr__(
52
52
  self,
53
53
  __name: str,
54
54
  __value: Any,
55
55
  ) -> None:
56
- raise RuntimeError("Missing can't be modified")
56
+ raise AttributeError("Missing can't be modified")
57
57
 
58
58
  def __delattr__(
59
59
  self,
60
60
  __name: str,
61
61
  ) -> None:
62
- raise RuntimeError("Missing can't be modified")
62
+ raise AttributeError("Missing can't be modified")
63
63
 
64
64
 
65
65
  MISSING: Final[Missing] = Missing()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: haiway
3
- Version: 0.2.0
3
+ Version: 0.3.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
@@ -10,14 +10,14 @@ src/haiway.egg-info/requires.txt
10
10
  src/haiway.egg-info/top_level.txt
11
11
  src/haiway/context/__init__.py
12
12
  src/haiway/context/access.py
13
+ src/haiway/context/disposables.py
13
14
  src/haiway/context/metrics.py
14
15
  src/haiway/context/state.py
15
16
  src/haiway/context/tasks.py
16
17
  src/haiway/context/types.py
17
18
  src/haiway/helpers/__init__.py
18
- src/haiway/helpers/asynchronous.py
19
- src/haiway/helpers/cached.py
20
- src/haiway/helpers/disposables.py
19
+ src/haiway/helpers/asynchrony.py
20
+ src/haiway/helpers/caching.py
21
21
  src/haiway/helpers/retries.py
22
22
  src/haiway/helpers/throttling.py
23
23
  src/haiway/helpers/timeouted.py
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