engin 0.0.5__tar.gz → 0.0.6__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 (72) hide show
  1. {engin-0.0.5 → engin-0.0.6}/CHANGELOG.md +10 -0
  2. {engin-0.0.5 → engin-0.0.6}/PKG-INFO +1 -1
  3. {engin-0.0.5 → engin-0.0.6}/pyproject.toml +1 -1
  4. {engin-0.0.5 → engin-0.0.6}/src/engin/_engin.py +23 -8
  5. {engin-0.0.5 → engin-0.0.6}/tests/test_engin.py +11 -2
  6. {engin-0.0.5 → engin-0.0.6}/.github/workflows/check.yaml +0 -0
  7. {engin-0.0.5 → engin-0.0.6}/.github/workflows/publish.yaml +0 -0
  8. {engin-0.0.5 → engin-0.0.6}/.gitignore +0 -0
  9. {engin-0.0.5 → engin-0.0.6}/.readthedocs.yaml +0 -0
  10. {engin-0.0.5 → engin-0.0.6}/LICENSE +0 -0
  11. {engin-0.0.5 → engin-0.0.6}/README.md +0 -0
  12. {engin-0.0.5 → engin-0.0.6}/docs/concepts/providers.md +0 -0
  13. {engin-0.0.5 → engin-0.0.6}/docs/engin.md +0 -0
  14. {engin-0.0.5 → engin-0.0.6}/docs/guides/dependency_injection.md +0 -0
  15. {engin-0.0.5 → engin-0.0.6}/docs/index.md +0 -0
  16. {engin-0.0.5 → engin-0.0.6}/docs/js/readthedocs.js +0 -0
  17. {engin-0.0.5 → engin-0.0.6}/docs/overrides/main.html +0 -0
  18. {engin-0.0.5 → engin-0.0.6}/examples/__init__.py +0 -0
  19. {engin-0.0.5 → engin-0.0.6}/examples/asgi/__init__.py +0 -0
  20. {engin-0.0.5 → engin-0.0.6}/examples/asgi/app.py +0 -0
  21. {engin-0.0.5 → engin-0.0.6}/examples/asgi/common/__init__.py +0 -0
  22. {engin-0.0.5 → engin-0.0.6}/examples/asgi/common/db/__init__.py +0 -0
  23. {engin-0.0.5 → engin-0.0.6}/examples/asgi/common/db/adapaters/__init__.py +0 -0
  24. {engin-0.0.5 → engin-0.0.6}/examples/asgi/common/db/adapaters/memory.py +0 -0
  25. {engin-0.0.5 → engin-0.0.6}/examples/asgi/common/db/block.py +0 -0
  26. {engin-0.0.5 → engin-0.0.6}/examples/asgi/common/db/ports.py +0 -0
  27. {engin-0.0.5 → engin-0.0.6}/examples/asgi/common/starlette/__init__.py +0 -0
  28. {engin-0.0.5 → engin-0.0.6}/examples/asgi/common/starlette/endpoint.py +0 -0
  29. {engin-0.0.5 → engin-0.0.6}/examples/asgi/features/__init__.py +0 -0
  30. {engin-0.0.5 → engin-0.0.6}/examples/asgi/features/cats/__init__.py +0 -0
  31. {engin-0.0.5 → engin-0.0.6}/examples/asgi/features/cats/api/__init__.py +0 -0
  32. {engin-0.0.5 → engin-0.0.6}/examples/asgi/features/cats/api/get.py +0 -0
  33. {engin-0.0.5 → engin-0.0.6}/examples/asgi/features/cats/api/post.py +0 -0
  34. {engin-0.0.5 → engin-0.0.6}/examples/asgi/features/cats/block.py +0 -0
  35. {engin-0.0.5 → engin-0.0.6}/examples/asgi/features/cats/domain.py +0 -0
  36. {engin-0.0.5 → engin-0.0.6}/examples/asgi/main.py +0 -0
  37. {engin-0.0.5 → engin-0.0.6}/examples/fastapi/__init__.py +0 -0
  38. {engin-0.0.5 → engin-0.0.6}/examples/fastapi/app.py +0 -0
  39. {engin-0.0.5 → engin-0.0.6}/examples/fastapi/main.py +0 -0
  40. {engin-0.0.5 → engin-0.0.6}/examples/fastapi/routes/__init__.py +0 -0
  41. {engin-0.0.5 → engin-0.0.6}/examples/fastapi/routes/cats/__init__.py +0 -0
  42. {engin-0.0.5 → engin-0.0.6}/examples/fastapi/routes/cats/adapters/__init__.py +0 -0
  43. {engin-0.0.5 → engin-0.0.6}/examples/fastapi/routes/cats/adapters/repository.py +0 -0
  44. {engin-0.0.5 → engin-0.0.6}/examples/fastapi/routes/cats/api.py +0 -0
  45. {engin-0.0.5 → engin-0.0.6}/examples/fastapi/routes/cats/block.py +0 -0
  46. {engin-0.0.5 → engin-0.0.6}/examples/fastapi/routes/cats/domain.py +0 -0
  47. {engin-0.0.5 → engin-0.0.6}/examples/fastapi/routes/cats/ports.py +0 -0
  48. {engin-0.0.5 → engin-0.0.6}/examples/simple/__init__.py +0 -0
  49. {engin-0.0.5 → engin-0.0.6}/examples/simple/main.py +0 -0
  50. {engin-0.0.5 → engin-0.0.6}/mkdocs.yaml +0 -0
  51. {engin-0.0.5 → engin-0.0.6}/src/engin/__init__.py +0 -0
  52. {engin-0.0.5 → engin-0.0.6}/src/engin/_assembler.py +0 -0
  53. {engin-0.0.5 → engin-0.0.6}/src/engin/_block.py +0 -0
  54. {engin-0.0.5 → engin-0.0.6}/src/engin/_dependency.py +0 -0
  55. {engin-0.0.5 → engin-0.0.6}/src/engin/_exceptions.py +0 -0
  56. {engin-0.0.5 → engin-0.0.6}/src/engin/_lifecycle.py +0 -0
  57. {engin-0.0.5 → engin-0.0.6}/src/engin/_type_utils.py +0 -0
  58. {engin-0.0.5 → engin-0.0.6}/src/engin/ext/__init__.py +0 -0
  59. {engin-0.0.5 → engin-0.0.6}/src/engin/ext/asgi.py +0 -0
  60. {engin-0.0.5 → engin-0.0.6}/src/engin/ext/fastapi.py +0 -0
  61. {engin-0.0.5 → engin-0.0.6}/src/engin/py.typed +0 -0
  62. {engin-0.0.5 → engin-0.0.6}/tests/__init__.py +0 -0
  63. {engin-0.0.5 → engin-0.0.6}/tests/acceptance/__init__.py +0 -0
  64. {engin-0.0.5 → engin-0.0.6}/tests/acceptance/test_error_in_shutdown.py +0 -0
  65. {engin-0.0.5 → engin-0.0.6}/tests/acceptance/test_error_in_start_up.py +0 -0
  66. {engin-0.0.5 → engin-0.0.6}/tests/conftest.py +0 -0
  67. {engin-0.0.5 → engin-0.0.6}/tests/deps.py +0 -0
  68. {engin-0.0.5 → engin-0.0.6}/tests/test_assembler.py +0 -0
  69. {engin-0.0.5 → engin-0.0.6}/tests/test_dependencies.py +0 -0
  70. {engin-0.0.5 → engin-0.0.6}/tests/test_modules.py +0 -0
  71. {engin-0.0.5 → engin-0.0.6}/tests/test_utils.py +0 -0
  72. {engin-0.0.5 → engin-0.0.6}/uv.lock +0 -0
@@ -6,6 +6,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
8
 
9
+ ## [0.0.6] - 2025-02-19
10
+
11
+ ### Fixed
12
+
13
+ - Engin now respects intended multiproviders behaviour instead of treating them as normal
14
+ providers and overwriting existing multiproviders for that type.
15
+ - `Engin.shutdown()` does not block if shutdown is called before startup, or after aborted
16
+ startup.
17
+
18
+
9
19
  ## [0.0.5] - 2025-01-29
10
20
 
11
21
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: engin
3
- Version: 0.0.5
3
+ Version: 0.0.6
4
4
  Summary: An async-first modular application framework
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.10
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "engin"
3
- version = "0.0.5"
3
+ version = "0.0.6"
4
4
  description = "An async-first modular application framework"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -81,8 +81,6 @@ class Engin:
81
81
  Args:
82
82
  *options: an instance of Provide, Supply, Invoke, Entrypoint or a Block.
83
83
  """
84
- self._providers: dict[TypeId, Provide] = {TypeId.from_type(Engin): Provide(self._self)}
85
- self._invokables: list[Invoke] = []
86
84
 
87
85
  self._stop_requested_event = Event()
88
86
  self._stop_complete_event = Event()
@@ -90,8 +88,16 @@ class Engin:
90
88
  self._shutdown_task: Task | None = None
91
89
  self._run_task: Task | None = None
92
90
 
91
+ # TODO: refactor _destruct_options and related attributes into a dedicated class?
92
+ self._providers: dict[TypeId, Provide] = {TypeId.from_type(Engin): Provide(self._self)}
93
+ self._multiproviders: dict[TypeId, list[Provide]] = {}
94
+ self._invocations: list[Invoke] = []
95
+ # populates the above
93
96
  self._destruct_options(chain(self._LIB_OPTIONS, options))
94
- self._assembler = Assembler(self._providers.values())
97
+ multi_providers = [p for multi in self._multiproviders.values() for p in multi]
98
+ self._assembler = Assembler(chain(self._providers.values(), multi_providers))
99
+ self._providers.clear()
100
+ self._multiproviders.clear()
95
101
 
96
102
  @property
97
103
  def assembler(self) -> Assembler:
@@ -119,7 +125,7 @@ class Engin:
119
125
  """
120
126
  LOG.info("starting engin")
121
127
  assembled_invocations: list[AssembledDependency] = [
122
- await self._assembler.assemble(invocation) for invocation in self._invokables
128
+ await self._assembler.assemble(invocation) for invocation in self._invocations
123
129
  ]
124
130
 
125
131
  for invocation in assembled_invocations:
@@ -153,6 +159,8 @@ class Engin:
153
159
  started.
154
160
  """
155
161
  self._stop_requested_event.set()
162
+ if self._shutdown_task is None:
163
+ return
156
164
  await self._stop_complete_event.wait()
157
165
 
158
166
  async def _shutdown(self) -> None:
@@ -170,12 +178,19 @@ class Engin:
170
178
  if isinstance(opt, Block):
171
179
  self._destruct_options(opt)
172
180
  if isinstance(opt, Provide | Supply):
173
- existing = self._providers.get(opt.return_type_id)
174
- self._log_option(opt, overwrites=existing)
175
- self._providers[opt.return_type_id] = opt
181
+ if not opt.is_multiprovider:
182
+ existing = self._providers.get(opt.return_type_id)
183
+ self._log_option(opt, overwrites=existing)
184
+ self._providers[opt.return_type_id] = opt
185
+ else:
186
+ self._log_option(opt)
187
+ if opt.return_type_id in self._multiproviders:
188
+ self._multiproviders[opt.return_type_id].append(opt)
189
+ else:
190
+ self._multiproviders[opt.return_type_id] = [opt]
176
191
  elif isinstance(opt, Invoke):
177
192
  self._log_option(opt)
178
- self._invokables.append(opt)
193
+ self._invocations.append(opt)
179
194
 
180
195
  @staticmethod
181
196
  def _log_option(opt: Dependency, overwrites: Dependency | None = None) -> None:
@@ -31,10 +31,19 @@ async def test_engin():
31
31
  def c(_: B) -> C:
32
32
  return C()
33
33
 
34
- def main(c: C) -> None:
34
+ def multi_a() -> list[A]:
35
+ return [A()]
36
+
37
+ def multi_a_2() -> list[A]:
38
+ return [A(), A()]
39
+
40
+ def main(c: C, multi_a: list[A]) -> None:
35
41
  assert isinstance(c, C)
42
+ assert len(multi_a) == 3
36
43
 
37
- engin = Engin(Provide(a), Provide(b), Provide(c), Invoke(main))
44
+ engin = Engin(
45
+ Provide(a), Provide(b), Provide(c), Provide(multi_a), Provide(multi_a_2), Invoke(main)
46
+ )
38
47
 
39
48
  await engin.start()
40
49
  await engin.stop()
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