engin 0.0.11__py3-none-any.whl → 0.0.13__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/_assembler.py +38 -1
- engin/_dependency.py +9 -0
- engin/_type_utils.py +3 -2
- engin/ext/asgi.py +9 -2
- engin/ext/fastapi.py +14 -10
- {engin-0.0.11.dist-info → engin-0.0.13.dist-info}/METADATA +1 -1
- {engin-0.0.11.dist-info → engin-0.0.13.dist-info}/RECORD +10 -10
- {engin-0.0.11.dist-info → engin-0.0.13.dist-info}/WHEEL +0 -0
- {engin-0.0.11.dist-info → engin-0.0.13.dist-info}/entry_points.txt +0 -0
- {engin-0.0.11.dist-info → engin-0.0.13.dist-info}/licenses/LICENSE +0 -0
engin/_assembler.py
CHANGED
@@ -141,7 +141,44 @@ class Assembler:
|
|
141
141
|
return value # type: ignore[return-value]
|
142
142
|
|
143
143
|
def has(self, type_: type[T]) -> bool:
|
144
|
-
|
144
|
+
"""
|
145
|
+
Returns True if this Assembler has a provider for the given type.
|
146
|
+
|
147
|
+
Args:
|
148
|
+
type_: the type to check.
|
149
|
+
|
150
|
+
Returns:
|
151
|
+
True if the Assembler has a provider for type else False.
|
152
|
+
"""
|
153
|
+
type_id = type_id_of(type_)
|
154
|
+
if type_id.multi:
|
155
|
+
return type_id in self._multiproviders
|
156
|
+
else:
|
157
|
+
return type_id in self._providers
|
158
|
+
|
159
|
+
def add(self, provider: Provide) -> None:
|
160
|
+
"""
|
161
|
+
Add a provider to the Assembler post-initialisation.
|
162
|
+
|
163
|
+
Args:
|
164
|
+
provider: the Provide instance to add.
|
165
|
+
|
166
|
+
Returns:
|
167
|
+
None
|
168
|
+
|
169
|
+
Raises:
|
170
|
+
ValueError: if a provider for this type already exists.
|
171
|
+
"""
|
172
|
+
type_id = provider.return_type_id
|
173
|
+
if provider.is_multiprovider:
|
174
|
+
if type_id in self._multiproviders:
|
175
|
+
self._multiproviders[type_id].append(provider)
|
176
|
+
else:
|
177
|
+
self._multiproviders[type_id] = [provider]
|
178
|
+
else:
|
179
|
+
if type_id in self._providers:
|
180
|
+
raise ValueError(f"A provider for '{type_id}' already exists")
|
181
|
+
self._providers[type_id] = provider
|
145
182
|
|
146
183
|
def _resolve_providers(self, type_id: TypeId) -> Collection[Provide]:
|
147
184
|
if type_id.multi:
|
engin/_dependency.py
CHANGED
@@ -158,6 +158,15 @@ class Provide(Dependency[Any, T]):
|
|
158
158
|
super().__init__(func=builder, block_name=block_name)
|
159
159
|
self._is_multi = typing.get_origin(self.return_type) is list
|
160
160
|
|
161
|
+
# Validate that the provider does to depend on its own output value, as this will
|
162
|
+
# cause a recursion error and is undefined behaviour wise.
|
163
|
+
if any(
|
164
|
+
self.return_type == param.annotation
|
165
|
+
for param in self.signature.parameters.values()
|
166
|
+
):
|
167
|
+
raise ValueError("A provider cannot depend on its own return type")
|
168
|
+
|
169
|
+
# Validate that multiproviders only return a list of one type.
|
161
170
|
if self._is_multi:
|
162
171
|
args = typing.get_args(self.return_type)
|
163
172
|
if len(args) != 1:
|
engin/_type_utils.py
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
import typing
|
2
2
|
from dataclasses import dataclass
|
3
|
+
from types import UnionType
|
3
4
|
from typing import Any
|
4
5
|
|
5
|
-
_implict_modules = ["builtins", "typing", "collections.abc"]
|
6
|
+
_implict_modules = ["builtins", "typing", "collections.abc", "types"]
|
6
7
|
|
7
8
|
|
8
9
|
@dataclass(frozen=True, eq=True, slots=True)
|
@@ -43,7 +44,7 @@ class TypeId:
|
|
43
44
|
def _args_to_str(type_: Any) -> str:
|
44
45
|
args = typing.get_args(type_)
|
45
46
|
if args:
|
46
|
-
arg_str = f"{type_.__name__}["
|
47
|
+
arg_str = "Union[" if isinstance(type_, UnionType) else f"{type_.__name__}["
|
47
48
|
for idx, arg in enumerate(args):
|
48
49
|
if isinstance(arg, list):
|
49
50
|
arg_str += "["
|
engin/ext/asgi.py
CHANGED
@@ -1,9 +1,10 @@
|
|
1
|
+
import contextlib
|
1
2
|
import traceback
|
2
3
|
from collections.abc import AsyncIterator, Awaitable, Callable, MutableMapping
|
3
4
|
from contextlib import AbstractAsyncContextManager, asynccontextmanager
|
4
5
|
from typing import Any, ClassVar, Protocol, TypeAlias
|
5
6
|
|
6
|
-
from engin import Engin, Entrypoint, Option
|
7
|
+
from engin import Engin, Entrypoint, Option, Supply
|
7
8
|
|
8
9
|
__all__ = ["ASGIEngin", "ASGIType", "engin_to_lifespan"]
|
9
10
|
|
@@ -79,7 +80,13 @@ def engin_to_lifespan(engin: Engin) -> Callable[[ASGIType], AbstractAsyncContext
|
|
79
80
|
"""
|
80
81
|
|
81
82
|
@asynccontextmanager
|
82
|
-
async def engin_lifespan(
|
83
|
+
async def engin_lifespan(app: ASGIType) -> AsyncIterator[None]:
|
84
|
+
# ensure the Engin
|
85
|
+
with contextlib.suppress(ValueError):
|
86
|
+
engin.assembler.add(Supply(app))
|
87
|
+
|
88
|
+
app.state.assembler = engin.assembler # type: ignore[attr-defined]
|
89
|
+
|
83
90
|
await engin.start()
|
84
91
|
yield
|
85
92
|
await engin.stop()
|
engin/ext/fastapi.py
CHANGED
@@ -6,7 +6,7 @@ from typing import ClassVar, TypeVar
|
|
6
6
|
|
7
7
|
from fastapi.routing import APIRoute
|
8
8
|
|
9
|
-
from engin import Engin, Entrypoint, Invoke, Option
|
9
|
+
from engin import Assembler, Engin, Entrypoint, Invoke, Option
|
10
10
|
from engin._dependency import Dependency, Supply, _noop
|
11
11
|
from engin._graph import DependencyGrapher, Node
|
12
12
|
from engin._type_utils import TypeId, type_id_of
|
@@ -24,15 +24,16 @@ except ImportError as err:
|
|
24
24
|
__all__ = ["APIRouteDependency", "FastAPIEngin", "Inject"]
|
25
25
|
|
26
26
|
|
27
|
-
def
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
27
|
+
def _attach_assembler(app: FastAPI, engin: Engin) -> None:
|
28
|
+
"""
|
29
|
+
An invocation that attaches the Engin's Assembler to the FastAPI application, enabling
|
30
|
+
the Inject marker.
|
31
|
+
"""
|
32
|
+
app.state.assembler = engin.assembler
|
32
33
|
|
33
34
|
|
34
35
|
class FastAPIEngin(ASGIEngin):
|
35
|
-
_LIB_OPTIONS: ClassVar[list[Option]] = [*ASGIEngin._LIB_OPTIONS, Invoke(
|
36
|
+
_LIB_OPTIONS: ClassVar[list[Option]] = [*ASGIEngin._LIB_OPTIONS, Invoke(_attach_assembler)]
|
36
37
|
_asgi_type = FastAPI
|
37
38
|
|
38
39
|
def graph(self) -> list[Node]:
|
@@ -40,7 +41,7 @@ class FastAPIEngin(ASGIEngin):
|
|
40
41
|
return grapher.resolve(
|
41
42
|
[
|
42
43
|
Entrypoint(self._asgi_type),
|
43
|
-
*[i for i in self._invocations if i.func_name != "
|
44
|
+
*[i for i in self._invocations if i.func_name != "_attach_assembler"],
|
44
45
|
]
|
45
46
|
)
|
46
47
|
|
@@ -50,8 +51,11 @@ T = TypeVar("T")
|
|
50
51
|
|
51
52
|
def Inject(interface: type[T]) -> Depends:
|
52
53
|
async def inner(conn: HTTPConnection) -> T:
|
53
|
-
|
54
|
-
|
54
|
+
try:
|
55
|
+
assembler: Assembler = conn.app.state.assembler
|
56
|
+
except AttributeError:
|
57
|
+
raise RuntimeError("Assembler is not attached to Application state") from None
|
58
|
+
return await assembler.get(interface)
|
55
59
|
|
56
60
|
dep = Depends(inner)
|
57
61
|
dep.__engin__ = True # type: ignore[attr-defined]
|
@@ -1,20 +1,20 @@
|
|
1
1
|
engin/__init__.py,sha256=yTc8k0HDGMIrxDdEEA90qGD_dExQjVIbXCyaOFRrnMg,508
|
2
|
-
engin/_assembler.py,sha256=
|
2
|
+
engin/_assembler.py,sha256=1ODW3HenDlIQLetg0LNEPPbsI6HkFnPU_AHzkR9Zxmc,8844
|
3
3
|
engin/_block.py,sha256=0QJtqyP5uTFjXsdVGr4ZONLI2LhfzUKmQGnNQWouB3o,2121
|
4
|
-
engin/_dependency.py,sha256=
|
4
|
+
engin/_dependency.py,sha256=RWOyGpMFp_5aLq5TP2wLKz0HT5f6CRoLvFyWvVif9jY,6825
|
5
5
|
engin/_engin.py,sha256=MTE4MkLrK45h0Nv7p5H92Kv5URa1nX246B9Pp1JkM3A,9134
|
6
6
|
engin/_exceptions.py,sha256=fsc4pTOIGHUh0x7oZhEXPJUTE268sIhswLoiqXaudiw,635
|
7
7
|
engin/_graph.py,sha256=1pMB0cr--uS0XJycDb1rS_X45RBpoyA6NkKqbeSuz1Q,1628
|
8
8
|
engin/_lifecycle.py,sha256=_jQnGFj4RYXsxMpcXPJQagFOwnoTVh7oSN8oUYoYuW0,3246
|
9
|
-
engin/_type_utils.py,sha256=
|
9
|
+
engin/_type_utils.py,sha256=EGyKZWuE2ZwuMlSgDhM1znF8giaEET1vcVoQcdGxFGQ,2210
|
10
10
|
engin/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
11
11
|
engin/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
12
|
-
engin/ext/asgi.py,sha256=
|
13
|
-
engin/ext/fastapi.py,sha256=
|
12
|
+
engin/ext/asgi.py,sha256=RUxkG03VTlvI6EG19c1nEJY8FnQw6MQwolfJSFnhUFE,3168
|
13
|
+
engin/ext/fastapi.py,sha256=GO3AIZNQ69MtzbWuACffx_6Pp34wC5a5Fi_fIAaQvTg,6186
|
14
14
|
engin/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
15
15
|
engin/scripts/graph.py,sha256=por62FkzcWx72V2Ha9sIoki-o99fe2Ifm1w-mdoHZIQ,5922
|
16
|
-
engin-0.0.
|
17
|
-
engin-0.0.
|
18
|
-
engin-0.0.
|
19
|
-
engin-0.0.
|
20
|
-
engin-0.0.
|
16
|
+
engin-0.0.13.dist-info/METADATA,sha256=dLFQgnZcD2c_xfKK-Z-Mx_f6_Q0kCta-XqNgqrEqRmo,2291
|
17
|
+
engin-0.0.13.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
18
|
+
engin-0.0.13.dist-info/entry_points.txt,sha256=Dehk4j5nK6zyuQtgOSRAoLE609V6eLzEp32bjqhO62Q,64
|
19
|
+
engin-0.0.13.dist-info/licenses/LICENSE,sha256=XHh5LPUPKZWTBqBv2xxN2RU7D59nHoiJGb5RIt8f45w,1070
|
20
|
+
engin-0.0.13.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|