engin 0.0.1__py3-none-any.whl → 0.0.3__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/__init__.py CHANGED
@@ -1,3 +1,4 @@
1
+ from engin import ext
1
2
  from engin._assembler import Assembler
2
3
  from engin._block import Block, invoke, provide
3
4
  from engin._dependency import Entrypoint, Invoke, Provide, Supply
@@ -16,6 +17,7 @@ __all__ = [
16
17
  "Option",
17
18
  "Provide",
18
19
  "Supply",
20
+ "ext",
19
21
  "invoke",
20
22
  "provide",
21
23
  ]
engin/_assembler.py CHANGED
@@ -4,7 +4,7 @@ from collections import defaultdict
4
4
  from collections.abc import Collection, Iterable
5
5
  from dataclasses import dataclass
6
6
  from inspect import BoundArguments, Signature
7
- from typing import Any, Generic, TypeVar
7
+ from typing import Any, Generic, TypeVar, cast
8
8
 
9
9
  from engin._dependency import Dependency, Provide, Supply
10
10
  from engin._exceptions import AssemblyError
@@ -113,7 +113,7 @@ class Assembler:
113
113
  async def get(self, type_: type[T]) -> T:
114
114
  type_id = type_id_of(type_)
115
115
  if type_id in self._dependencies:
116
- return self._dependencies[type_id]
116
+ return cast(T, self._dependencies[type_id])
117
117
  if type_id.multi:
118
118
  out = []
119
119
  for provider in self._multiproviders[type_id]:
engin/_block.py CHANGED
@@ -1,5 +1,6 @@
1
1
  import inspect
2
2
  from collections.abc import Iterable, Iterator
3
+ from typing import ClassVar
3
4
 
4
5
  from engin._dependency import Func, Invoke, Provide
5
6
 
@@ -15,11 +16,10 @@ def invoke(func: Func) -> Func:
15
16
 
16
17
 
17
18
  class Block(Iterable[Provide | Invoke]):
18
- _name: str
19
- _options: list[Provide | Invoke]
19
+ options: ClassVar[list[Provide | Invoke]] = []
20
20
 
21
21
  def __init__(self, /, block_name: str | None = None) -> None:
22
- self._options: list[Provide | Invoke] = []
22
+ self._options: list[Provide | Invoke] = self.options[:]
23
23
  self._name = block_name or f"{type(self).__name__}"
24
24
  for _, method in inspect.getmembers(self):
25
25
  if opt := getattr(method, "_opt", None):
engin/_dependency.py CHANGED
@@ -25,7 +25,7 @@ Func: TypeAlias = (
25
25
  _SELF = object()
26
26
 
27
27
 
28
- def _noop(*args, **kwargs) -> None: ...
28
+ def _noop(*args: Any, **kwargs: Any) -> None: ...
29
29
 
30
30
 
31
31
  class Dependency(ABC, Generic[P, T]):
engin/_engin.py CHANGED
@@ -32,15 +32,13 @@ class Engin:
32
32
  def assembler(self) -> Assembler:
33
33
  return self._assembler
34
34
 
35
- async def run(self):
35
+ async def run(self) -> None:
36
36
  await self.start()
37
37
 
38
- # lifecycle startup
39
-
40
38
  # wait till stop signal recieved
41
39
  await self._stop_event.wait()
42
40
 
43
- # lifecycle shutdown
41
+ await self.stop()
44
42
 
45
43
  async def start(self) -> None:
46
44
  LOG.info("starting engin")
@@ -57,8 +55,10 @@ class Engin:
57
55
 
58
56
  async def stop(self) -> None:
59
57
  self._stop_event.set()
58
+ lifecycle = await self._assembler.get(Lifecycle)
59
+ await lifecycle.shutdown()
60
60
 
61
- def _destruct_options(self, options: Iterable[Option]):
61
+ def _destruct_options(self, options: Iterable[Option]) -> None:
62
62
  for opt in options:
63
63
  if isinstance(opt, Block):
64
64
  self._destruct_options(opt)
engin/_lifecycle.py CHANGED
@@ -4,12 +4,12 @@ from contextlib import AbstractAsyncContextManager, AsyncExitStack
4
4
 
5
5
  class Lifecycle:
6
6
  def __init__(self) -> None:
7
- self._on_startup: list[Callable] = []
8
- self._on_shutdown: list[Callable] = []
7
+ self._on_startup: list[Callable[..., None]] = []
8
+ self._on_shutdown: list[Callable[..., None]] = []
9
9
  self._context_managers: list[AbstractAsyncContextManager] = []
10
10
  self._stack: AsyncExitStack = AsyncExitStack()
11
11
 
12
- def register_context(self, cm: AbstractAsyncContextManager):
12
+ def register_context(self, cm: AbstractAsyncContextManager) -> None:
13
13
  self._context_managers.append(cm)
14
14
 
15
15
  async def startup(self) -> None:
engin/ext/asgi.py CHANGED
@@ -1,16 +1,16 @@
1
1
  import traceback
2
- import typing
3
- from typing import ClassVar, Protocol, TypeAlias
2
+ from collections.abc import Awaitable, Callable, MutableMapping
3
+ from typing import Any, ClassVar, Protocol, TypeAlias
4
4
 
5
5
  from engin import Engin, Option
6
6
 
7
7
  __all__ = ["ASGIEngin", "ASGIType"]
8
8
 
9
9
 
10
- _Scope: TypeAlias = typing.MutableMapping[str, typing.Any]
11
- _Message: TypeAlias = typing.MutableMapping[str, typing.Any]
12
- _Receive: TypeAlias = typing.Callable[[], typing.Awaitable[_Message]]
13
- _Send: TypeAlias = typing.Callable[[_Message], typing.Awaitable[None]]
10
+ _Scope: TypeAlias = MutableMapping[str, Any]
11
+ _Message: TypeAlias = MutableMapping[str, Any]
12
+ _Receive: TypeAlias = Callable[[], Awaitable[_Message]]
13
+ _Send: TypeAlias = Callable[[_Message], Awaitable[None]]
14
14
 
15
15
 
16
16
  class ASGIType(Protocol):
@@ -54,5 +54,5 @@ class _Rereceive:
54
54
  def __init__(self, message: _Message) -> None:
55
55
  self._message = message
56
56
 
57
- async def __call__(self, *args, **kwargs) -> _Message:
57
+ async def __call__(self, *args: Any, **kwargs: Any) -> _Message:
58
58
  return self._message
@@ -0,0 +1,56 @@
1
+ Metadata-Version: 2.4
2
+ Name: engin
3
+ Version: 0.0.3
4
+ Summary: An async-first modular application framework
5
+ License-File: LICENSE
6
+ Requires-Python: >=3.10
7
+ Description-Content-Type: text/markdown
8
+
9
+ # Engin 🏎️
10
+
11
+ Engin is a zero-dependency application framework for modern Python.
12
+
13
+ ## Features ✨
14
+
15
+ - **Lightweight**: Engin has no dependencies.
16
+ - **Async First**: Engin provides first-class support for applications.
17
+ - **Dependency Injection**: Engin promotes a modular decoupled architecture in your application.
18
+ - **Lifecycle Management**: Engin provides an simple, portable approach for implememting
19
+ startup and shutdown tasks.
20
+ - **Ecosystem Compatability**: seamlessly integrate with frameworks such as FastAPI without
21
+ having to migrate your dependencies.
22
+ - **Code Reuse**: Engin's modular components work great as packages and distributions. Allowing
23
+ low boiler-plate code reuse within your Organisation.
24
+
25
+ ## Installation
26
+
27
+ Engin is available on PyPI, install using your favourite dependency manager:
28
+
29
+ - **pip**:`pip install engin`
30
+ - **poetry**: `poetry add engin`
31
+ - **uv**: `uv add engin`
32
+
33
+ ## Getting Started
34
+
35
+ A minimal example:
36
+
37
+ ```python
38
+ import asyncio
39
+
40
+ from httpx import AsyncClient
41
+
42
+ from engin import Engin, Invoke, Provide
43
+
44
+
45
+ def httpx_client() -> AsyncClient:
46
+ return AsyncClient()
47
+
48
+
49
+ async def main(http_client: AsyncClient) -> None:
50
+ print(await http_client.get("https://httpbin.org/get"))
51
+
52
+ engin = Engin(Provide(httpx_client), Invoke(main))
53
+
54
+ asyncio.run(engin.run())
55
+ ```
56
+
@@ -0,0 +1,16 @@
1
+ engin/__init__.py,sha256=AIE9wnwvfXwS1mwp6Sa9xtatBYyYzhWa36GKfAkEx_M,508
2
+ engin/_assembler.py,sha256=UY97tXdHgvRi_iph_kolXZ8YTrxRjHKoIc4tAhqYOcw,5258
3
+ engin/_block.py,sha256=N_rSakTKM5mEW9qUfNio6WRaW6hByu8-HHBSy9UuN8Y,1149
4
+ engin/_dependency.py,sha256=d1G3P6vYTFLJTFOh4DLu_EK5XW3rDX0ejBHGkE7JJbs,4759
5
+ engin/_engin.py,sha256=feqRxk7fXx5VeTcRVJLPn49E5a7TqZ1ZnxUDFRxGznQ,3244
6
+ engin/_exceptions.py,sha256=nkzTqxrW5nkcNgFDGoZ2TBtnHtO2RLk0qghM5LNAEmU,542
7
+ engin/_lifecycle.py,sha256=zW9W7wU78JaGpOI1RqAyH6MiK0mwRZtFLBZDLB-NhX8,759
8
+ engin/_type_utils.py,sha256=naEk-lknC3Fdsd4jiP4YZAxjX3KXZN0MhFde9EV-Fmo,1835
9
+ engin/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ engin/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ engin/ext/asgi.py,sha256=6vuC4zIhsvAdmwRn2I6uuUWPYfqobox1dv7skg2OWwE,1940
12
+ engin/ext/fastapi.py,sha256=vf7eB5no6eVuZyYdJZdvDYZmjJAmoKbrKhskgiSWg5g,1005
13
+ engin-0.0.3.dist-info/METADATA,sha256=ANtqVNefuIRfbjAJNXNWAMwMHzc4GbLRvOT_2zpYI3Y,1491
14
+ engin-0.0.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
15
+ engin-0.0.3.dist-info/licenses/LICENSE,sha256=XHh5LPUPKZWTBqBv2xxN2RU7D59nHoiJGb5RIt8f45w,1070
16
+ engin-0.0.3.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.26.3
2
+ Generator: hatchling 1.27.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,5 +0,0 @@
1
- Metadata-Version: 2.3
2
- Name: engin
3
- Version: 0.0.1
4
- Summary: An async-first modular application framework
5
- Requires-Python: >=3.10
@@ -1,16 +0,0 @@
1
- engin/__init__.py,sha256=tlqi9y7QWjIV95TxViPQBiQmVV4EIor8xLFkfTjg8IQ,475
2
- engin/_assembler.py,sha256=Py159y4HvI54QuDcmbjL2kfXtbucYDE2KFBOgoC4qCM,5243
3
- engin/_block.py,sha256=XrGMFEeps_rvOGsYTlsefUkJGAY6jqdp71G8jE9FbwU,1109
4
- engin/_dependency.py,sha256=cL1qQFKDt7Rgg7mx8IXVkCZlUigg5cvhPFF9g2LNrgg,4749
5
- engin/_engin.py,sha256=YWw1c1z8_Q2mVJizPHB74MWk40QX1OE9qclt9WHU39Y,3168
6
- engin/_exceptions.py,sha256=nkzTqxrW5nkcNgFDGoZ2TBtnHtO2RLk0qghM5LNAEmU,542
7
- engin/_lifecycle.py,sha256=0hk24fiwaBos5kaZrnG_Qm0VmUhWKnGtwYCjc007XDk,729
8
- engin/_type_utils.py,sha256=naEk-lknC3Fdsd4jiP4YZAxjX3KXZN0MhFde9EV-Fmo,1835
9
- engin/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- engin/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
- engin/ext/asgi.py,sha256=7viV6YgEwa0KatypDUrEYZT3YIQ3URQbzfVTGG7OuZE,1931
12
- engin/ext/fastapi.py,sha256=vf7eB5no6eVuZyYdJZdvDYZmjJAmoKbrKhskgiSWg5g,1005
13
- engin-0.0.1.dist-info/METADATA,sha256=-SaVsTSi5xCLs0HDU3sXAT7USe6JD6WBWPoidm5-zqs,127
14
- engin-0.0.1.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
15
- engin-0.0.1.dist-info/licenses/LICENSE,sha256=XHh5LPUPKZWTBqBv2xxN2RU7D59nHoiJGb5RIt8f45w,1070
16
- engin-0.0.1.dist-info/RECORD,,