engin 0.0.dev1__py3-none-any.whl → 0.0.1__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
@@ -8,11 +8,11 @@ from engin._lifecycle import Lifecycle
8
8
  __all__ = [
9
9
  "Assembler",
10
10
  "AssemblyError",
11
+ "Block",
11
12
  "Engin",
12
13
  "Entrypoint",
13
14
  "Invoke",
14
15
  "Lifecycle",
15
- "Block",
16
16
  "Option",
17
17
  "Provide",
18
18
  "Supply",
engin/_assembler.py CHANGED
@@ -49,7 +49,7 @@ class Assembler:
49
49
  if not providers:
50
50
  if type_id.multi:
51
51
  LOG.warning(f"no provider for '{type_id}' defaulting to empty list")
52
- providers = [(Supply([], type_hint=list[type_id.type]))]
52
+ providers = [(Supply([], type_hint=list[type_id.type]))] # type: ignore[name-defined]
53
53
  else:
54
54
  raise LookupError(f"No Provider registered for dependency '{type_id}'")
55
55
 
@@ -112,15 +112,20 @@ class Assembler:
112
112
 
113
113
  async def get(self, type_: type[T]) -> T:
114
114
  type_id = type_id_of(type_)
115
+ if type_id in self._dependencies:
116
+ return self._dependencies[type_id]
115
117
  if type_id.multi:
116
118
  out = []
117
119
  for provider in self._multiproviders[type_id]:
118
120
  assembled_dependency = await self.assemble(provider)
119
121
  out.extend(await assembled_dependency())
120
- return out
122
+ self._dependencies[type_id] = out
123
+ return out # type: ignore[return-value]
121
124
  else:
122
125
  assembled_dependency = await self.assemble(self._providers[type_id])
123
- return await assembled_dependency()
126
+ value = await assembled_dependency()
127
+ self._dependencies[type_id] = value
128
+ return value # type: ignore[return-value]
124
129
 
125
130
  def has(self, type_: type[T]) -> bool:
126
131
  return type_id_of(type_) in self._providers
engin/_engin.py CHANGED
@@ -21,7 +21,7 @@ class Engin:
21
21
  _LIB_OPTIONS: ClassVar[list[Option]] = [Provide(Lifecycle)]
22
22
 
23
23
  def __init__(self, *options: Option) -> None:
24
- self._providers: dict[TypeId, Provide] = {}
24
+ self._providers: dict[TypeId, Provide] = {TypeId.from_type(Engin): Provide(self._self)}
25
25
  self._invokables: list[Invoke] = []
26
26
  self._stop_event = Event()
27
27
 
@@ -87,3 +87,6 @@ class Engin:
87
87
  LOG.debug(f"ENTRYPOINT {type_id!s:<35}")
88
88
  elif isinstance(opt, Invoke):
89
89
  LOG.debug(f"INVOKE {opt.name:<35}")
90
+
91
+ def _self(self) -> "Engin":
92
+ return self
@@ -1,6 +1,6 @@
1
1
  import traceback
2
2
  import typing
3
- from typing import Protocol, TypeAlias
3
+ from typing import ClassVar, Protocol, TypeAlias
4
4
 
5
5
  from engin import Engin, Option
6
6
 
@@ -18,13 +18,16 @@ class ASGIType(Protocol):
18
18
 
19
19
 
20
20
  class ASGIEngin(Engin, ASGIType):
21
+ _asgi_type: ClassVar[type[ASGIType]] = ASGIType # type: ignore[type-abstract]
21
22
  _asgi_app: ASGIType
22
23
 
23
24
  def __init__(self, *options: Option) -> None:
24
25
  super().__init__(*options)
25
26
 
26
- if not self._assembler.has(ASGIType):
27
- raise LookupError("A provider for `ASGIType` was expected, none found")
27
+ if not self._assembler.has(self._asgi_type):
28
+ raise LookupError(
29
+ f"A provider for `{self._asgi_type.__name__}` was expected, none found"
30
+ )
28
31
 
29
32
  async def __call__(self, scope: _Scope, receive: _Receive, send: _Send) -> None:
30
33
  if scope["type"] == "lifespan":
@@ -44,7 +47,7 @@ class ASGIEngin(Engin, ASGIType):
44
47
 
45
48
  async def _startup(self) -> None:
46
49
  await self.start()
47
- self._asgi_app = await self._assembler.get(ASGIType)
50
+ self._asgi_app = await self._assembler.get(self._asgi_type)
48
51
 
49
52
 
50
53
  class _Rereceive:
engin/ext/fastapi.py ADDED
@@ -0,0 +1,42 @@
1
+ import typing
2
+ from typing import ClassVar, TypeVar
3
+
4
+ from engin import Engin, Invoke, Option
5
+ from engin.ext.asgi import ASGIEngin
6
+
7
+ try:
8
+ from fastapi import FastAPI
9
+ from fastapi.params import Depends
10
+ from starlette.requests import HTTPConnection
11
+ except ImportError:
12
+ raise ImportError("fastapi must be installed to use the corresponding extension")
13
+
14
+
15
+ if typing.TYPE_CHECKING:
16
+ from fastapi import FastAPI
17
+ from fastapi.params import Depends
18
+
19
+ __all__ = ["FastAPIEngin", "Inject"]
20
+
21
+
22
+ def _attach_engin(
23
+ app: FastAPI,
24
+ engin: Engin,
25
+ ) -> None:
26
+ app.state.engin = engin
27
+
28
+
29
+ class FastAPIEngin(ASGIEngin):
30
+ _LIB_OPTIONS: ClassVar[list[Option]] = [*ASGIEngin._LIB_OPTIONS, Invoke(_attach_engin)] # type: ignore[arg-type]
31
+ _asgi_type = FastAPI
32
+
33
+
34
+ T = TypeVar("T")
35
+
36
+
37
+ def Inject(interface: type[T]) -> Depends:
38
+ async def inner(conn: HTTPConnection) -> T:
39
+ engin: Engin = conn.app.state.engin
40
+ return await engin.assembler.get(interface)
41
+
42
+ return Depends(inner)
@@ -1,6 +1,5 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: engin
3
- Version: 0.0.dev1
3
+ Version: 0.0.1
4
4
  Summary: An async-first modular application framework
5
- License-File: LICENSE
6
5
  Requires-Python: >=3.10
@@ -0,0 +1,16 @@
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,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.25.0
2
+ Generator: hatchling 1.26.3
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,15 +0,0 @@
1
- engin/__init__.py,sha256=T2tv-pdULMTp-p0PhB_XYmr9CEWOmMzcu0jebZx7-2Q,475
2
- engin/_assembler.py,sha256=HPnrTGk6XbO-iG3BZfD65zzOxWC4C1yWOZf7Vv3MMKk,4944
3
- engin/_block.py,sha256=XrGMFEeps_rvOGsYTlsefUkJGAY6jqdp71G8jE9FbwU,1109
4
- engin/_dependency.py,sha256=cL1qQFKDt7Rgg7mx8IXVkCZlUigg5cvhPFF9g2LNrgg,4749
5
- engin/_engin.py,sha256=Sg7sxESuwzTXx-4xgFv869YkpBRxE9d-kjIOTOTtcxU,3071
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/extensions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
- engin/extensions/asgi.py,sha256=EHQhevGJt3UJZp_7Y7AIuYVm-bYUO-seuzE3kMuOJWM,1775
12
- engin-0.0.dev1.dist-info/METADATA,sha256=263kjboSCI7Th0GHl7bbpF5cCHAKsB51_56v5xcJThk,152
13
- engin-0.0.dev1.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
14
- engin-0.0.dev1.dist-info/licenses/LICENSE,sha256=XHh5LPUPKZWTBqBv2xxN2RU7D59nHoiJGb5RIt8f45w,1070
15
- engin-0.0.dev1.dist-info/RECORD,,
File without changes