engin 0.1.0rc2__py3-none-any.whl → 0.2.0a1__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.
engin/_cli/_check.py CHANGED
@@ -50,13 +50,6 @@ def check_dependencies(
50
50
  for missing_type in sorted_missing:
51
51
  console.print(f" • {missing_type}", style="red")
52
52
 
53
- available_providers = sorted(
54
- str(provider.return_type_id) for provider in assembler.providers
55
- )
56
- console.print("\nAvailable providers:", style="yellow")
57
- for available_type in available_providers:
58
- console.print(f" • {available_type}", style="yellow")
59
-
60
53
  raise typer.Exit(code=1)
61
54
  else:
62
55
  console.print("✅ All dependencies are satisfied!", style="green bold")
engin/_cli/_graph.html CHANGED
@@ -400,7 +400,7 @@
400
400
  }
401
401
 
402
402
  if (node.style_classes && node.style_classes.length > 0) {
403
- styleClasses = `:::${node.style_classes.join(' ')}`;
403
+ styleClasses = `:::${node.style_classes.join(',')}`;
404
404
  }
405
405
 
406
406
  return shape + styleClasses;
engin/_dependency.py CHANGED
@@ -171,6 +171,9 @@ class Provide(Dependency[Any, T]):
171
171
  override: (optional) allow this provider to override other providers for the
172
172
  same type from the same package.
173
173
  """
174
+ if not callable(factory):
175
+ msg = "Provided value is not callable, did you mean to use Supply instead?"
176
+ raise ValueError(msg)
174
177
  super().__init__(func=factory)
175
178
  self._scope = scope
176
179
  self._override = override
engin/_engin.py CHANGED
@@ -179,7 +179,9 @@ class Engin:
179
179
  try:
180
180
  async with supervisor:
181
181
  await self._stop_requested_event.wait()
182
- await self._shutdown()
182
+
183
+ # shutdown after stopping supervised tasks
184
+ await self._shutdown()
183
185
  except BaseException:
184
186
  await self._shutdown()
185
187
 
@@ -262,6 +264,7 @@ async def _stop_engin_on_signal(stop_requested_event: Event) -> None:
262
264
 
263
265
  # windows does not support signal_handlers, so this is the workaround
264
266
  def ctrlc_handler(sig: int, frame: FrameType | None) -> None:
267
+ LOG.debug(f"received {signal.SIGINT.name} signal")
265
268
  nonlocal should_stop
266
269
  if should_stop:
267
270
  raise KeyboardInterrupt("Forced keyboard interrupt")
engin/_supervisor.py CHANGED
@@ -15,7 +15,7 @@ if typing.TYPE_CHECKING:
15
15
 
16
16
  LOG = logging.getLogger("engin")
17
17
 
18
- TaskFactory: TypeAlias = Callable[[], Awaitable[None]]
18
+ AsyncFunction: TypeAlias = Callable[[], Awaitable[None]]
19
19
 
20
20
 
21
21
  class OnException(Enum):
@@ -41,15 +41,16 @@ class _SupervisorTask:
41
41
  """
42
42
  Attributes:
43
43
  - factory: a coroutine function that can create the task.
44
- - on_exception: determines the behaviour when task raises an exception.
44
+ - on_exception: determines the behaviour when the task raises an exception.
45
45
  - complete: will be set to true if task stops for any reason except cancellation.
46
46
  - last_exception: the last exception raised by the task.
47
47
  """
48
48
 
49
- factory: TaskFactory
49
+ factory: AsyncFunction
50
50
  on_exception: OnException
51
51
  complete: bool = False
52
52
  last_exception: Exception | None = None
53
+ shutdown_hook: AsyncFunction | None = None
53
54
 
54
55
  async def __call__(self) -> None:
55
56
  # loop to allow for restarting erroring tasks
@@ -63,7 +64,6 @@ class _SupervisorTask:
63
64
  raise
64
65
  except Exception as err:
65
66
  self.last_exception = err
66
-
67
67
  if self.on_exception == OnException.IGNORE:
68
68
  LOG.warning(
69
69
  f"supervisor task '{self.name}' raised {type(err).__name__} "
@@ -107,9 +107,17 @@ class Supervisor:
107
107
  self._task_group: TaskGroup | None = None
108
108
 
109
109
  def supervise(
110
- self, func: TaskFactory, *, on_exception: OnException = OnException.SHUTDOWN
110
+ self,
111
+ func: AsyncFunction,
112
+ *,
113
+ on_exception: OnException = OnException.SHUTDOWN,
114
+ shutdown_hook: AsyncFunction | None = None,
111
115
  ) -> None:
112
- self._tasks.append(_SupervisorTask(factory=func, on_exception=on_exception))
116
+ self._tasks.append(
117
+ _SupervisorTask(
118
+ factory=func, on_exception=on_exception, shutdown_hook=shutdown_hook
119
+ )
120
+ )
113
121
 
114
122
  @property
115
123
  def empty(self) -> bool:
@@ -122,6 +130,7 @@ class Supervisor:
122
130
  self._task_group = await anyio.create_task_group().__aenter__()
123
131
 
124
132
  for task in self._tasks:
133
+ LOG.info(f"supervising task: {task.name}")
125
134
  self._task_group.start_soon(task, name=task.name)
126
135
 
127
136
  async def __aexit__(
@@ -132,6 +141,10 @@ class Supervisor:
132
141
  /,
133
142
  ) -> None:
134
143
  if self._task_group:
144
+ for task in self._tasks:
145
+ if task.shutdown_hook is not None:
146
+ LOG.debug(f"supervised task shutdown hook: {task.name}")
147
+ await task.shutdown_hook()
135
148
  if not self._task_group.cancel_scope.cancel_called:
136
149
  self._task_group.cancel_scope.cancel()
137
150
  await self._task_group.__aexit__(exc_type, exc_value, traceback)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: engin
3
- Version: 0.1.0rc2
3
+ Version: 0.2.0a1
4
4
  Summary: An async-first modular application framework
5
5
  Project-URL: Homepage, https://github.com/invokermain/engin
6
6
  Project-URL: Documentation, https://engin.readthedocs.io/en/latest/
@@ -39,7 +39,7 @@ The Engin framework gives you:
39
39
 
40
40
  - A fully-featured dependency injection system.
41
41
  - A robust application runtime with lifecycle hooks and supervised background tasks.
42
- - Zero boiler-plate code reuse across applications.
42
+ - Zero boilerplate code reuse across applications.
43
43
  - Integrations for other frameworks such as FastAPI.
44
44
  - Full async support.
45
45
  - CLI commands to aid local development.
@@ -1,27 +1,27 @@
1
1
  engin/__init__.py,sha256=O0vS570kZFBq7Kwy4FgeJFIhfo4aIg5mv_Z_9vAQRio,577
2
2
  engin/_assembler.py,sha256=0uXgtcO5M3EHg0I-TQK9y7LOzfxkLFmKia-zLyVHaxA,11178
3
3
  engin/_block.py,sha256=IacP4PoJKRhSQCbQSdoyCtmu362a4vj6qoUQKyaJwzI,3062
4
- engin/_dependency.py,sha256=xINk3sudxzsTmkUkNAKQwzBc0G0DfhpnrZli4z3ALBY,9459
5
- engin/_engin.py,sha256=oGaf_iedMNKxl3rbADpPzIvNtTx1Pfs-6o0e8yRrmHk,9532
4
+ engin/_dependency.py,sha256=Ie-z0obe7Ut6TF1O7BjP3Z7i216ufKqSeT_D82krdAc,9615
5
+ engin/_engin.py,sha256=1bgZmdmRlwHIZ9pz5rysYgvFu5s8e0gzlc9iq-Of97c,9651
6
6
  engin/_graph.py,sha256=y1g7Lm_Zy5GPEgRsggCKV5DDaDzcwUl8v3IZCK8jyGI,1631
7
7
  engin/_introspect.py,sha256=VdREX6Lhhga5SnEP9G7mjHkgJR4mpqk_SMnmL2zTcqY,966
8
8
  engin/_lifecycle.py,sha256=cSWe3euZkmpxmUPFvph2lsTtvuZbxttEfBL-RnOI7lo,5325
9
9
  engin/_option.py,sha256=nZcdrehp1QwgxMUoIpsM0PJuu1q1pbXzhcVsetbsHpc,223
10
- engin/_supervisor.py,sha256=HI0D4StqSJZE2l6x7RtouRLKWx1HOhUmLHqu8pUcWbQ,4343
10
+ engin/_supervisor.py,sha256=WGio-mfPHo2Wdfqo5ZB66WAoAU2ey0urwM0Dl4vRUTA,4824
11
11
  engin/_type_utils.py,sha256=H3Tl-kJr2wY2RhaTXP9GrMqa2RsXMijHbjHKe1AxGmc,2276
12
12
  engin/exceptions.py,sha256=lSMOJI4Yl-VIM0yDzFWbPhC0mQm4f0WvGULr9SldIaY,2353
13
13
  engin/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
14
  engin/_cli/__init__.py,sha256=Ixk3NoZeIN8Bj53I625uqJdLyyT9Gpbe_4GtNy-KQwM,636
15
- engin/_cli/_check.py,sha256=YA37Gi4rimKIH-XMs7SEAFkSRNBIMG8OCKfF3W1V3-g,1976
15
+ engin/_cli/_check.py,sha256=w9GA9RmCgSflvSU7EQqXKiOCqgrZB-pLS_UoJOqV56E,1666
16
16
  engin/_cli/_common.py,sha256=6tyjxAkROCViw0LOFdx-X1U-iSXKyeW5CoE9UxWRybI,3282
17
- engin/_cli/_graph.html,sha256=YIv34LR00aWsWgjNrqO4XBNu4frPo_y-i1CijaZyySo,29073
17
+ engin/_cli/_graph.html,sha256=5Dw5eyhsrU8KrpdhGn1mxo5kTUTJLMSzT-MBKvSv13g,29073
18
18
  engin/_cli/_graph.py,sha256=MsxsNpL1v0v1AUT57ZS97l1diwacqRaPdVBObHHIGJE,6753
19
19
  engin/_cli/_inspect.py,sha256=_uzldpHA51IX4srpUGzL4lZNiepqucsO9M3Zo83XBBM,3159
20
20
  engin/extensions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
21
  engin/extensions/asgi.py,sha256=7vQFaVs1jxq1KbhHGN8k7x2UFab6SPUq2_hXfX6HiXU,3329
22
22
  engin/extensions/fastapi.py,sha256=7N6i-eZUEZRPo7kcvjS7kbRSY5QAPyKJXSeongSQ-OA,6371
23
- engin-0.1.0rc2.dist-info/METADATA,sha256=I7BtKglKAs30NQ6n73p3gzLhZhxcvsIZXuuM356Ipa4,3951
24
- engin-0.1.0rc2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
25
- engin-0.1.0rc2.dist-info/entry_points.txt,sha256=sW247zZUMxm0b5UKYvPuqQQljYDtU-j2zK3cu7gHwM0,41
26
- engin-0.1.0rc2.dist-info/licenses/LICENSE,sha256=XHh5LPUPKZWTBqBv2xxN2RU7D59nHoiJGb5RIt8f45w,1070
27
- engin-0.1.0rc2.dist-info/RECORD,,
23
+ engin-0.2.0a1.dist-info/METADATA,sha256=OntxY2pdrpJuvpqb-zvj2SJwpohHmEjbjLmqc7w1zqE,3949
24
+ engin-0.2.0a1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
25
+ engin-0.2.0a1.dist-info/entry_points.txt,sha256=sW247zZUMxm0b5UKYvPuqQQljYDtU-j2zK3cu7gHwM0,41
26
+ engin-0.2.0a1.dist-info/licenses/LICENSE,sha256=XHh5LPUPKZWTBqBv2xxN2RU7D59nHoiJGb5RIt8f45w,1070
27
+ engin-0.2.0a1.dist-info/RECORD,,