engin 0.0.15__py3-none-any.whl → 0.0.17__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 +51 -4
- engin/_block.py +2 -2
- engin/_cli/_graph.html +78 -0
- engin/_cli/_graph.py +29 -54
- engin/_dependency.py +36 -18
- engin/_exceptions.py +29 -1
- engin/_graph.py +1 -1
- engin/ext/asgi.py +2 -1
- engin/ext/fastapi.py +2 -2
- {engin-0.0.15.dist-info → engin-0.0.17.dist-info}/METADATA +1 -1
- engin-0.0.17.dist-info/RECORD +24 -0
- engin-0.0.15.dist-info/RECORD +0 -23
- {engin-0.0.15.dist-info → engin-0.0.17.dist-info}/WHEEL +0 -0
- {engin-0.0.15.dist-info → engin-0.0.17.dist-info}/entry_points.txt +0 -0
- {engin-0.0.15.dist-info → engin-0.0.17.dist-info}/licenses/LICENSE +0 -0
engin/_assembler.py
CHANGED
@@ -2,17 +2,26 @@ import asyncio
|
|
2
2
|
import logging
|
3
3
|
from collections import defaultdict
|
4
4
|
from collections.abc import Iterable
|
5
|
+
from contextvars import ContextVar
|
5
6
|
from dataclasses import dataclass
|
6
7
|
from inspect import BoundArguments, Signature
|
8
|
+
from types import TracebackType
|
7
9
|
from typing import Any, Generic, TypeVar, cast
|
8
10
|
|
9
11
|
from engin._dependency import Dependency, Provide, Supply
|
10
|
-
from engin._exceptions import ProviderError
|
12
|
+
from engin._exceptions import NotInScopeError, ProviderError
|
11
13
|
from engin._type_utils import TypeId
|
12
14
|
|
13
15
|
LOG = logging.getLogger("engin")
|
14
16
|
|
15
17
|
T = TypeVar("T")
|
18
|
+
_SCOPE: ContextVar[list[str] | None] = ContextVar("_SCOPE", default=None)
|
19
|
+
|
20
|
+
|
21
|
+
def _get_scope() -> list[str]:
|
22
|
+
if _SCOPE.get() is None:
|
23
|
+
_SCOPE.set([])
|
24
|
+
return cast("list[str]", _SCOPE.get())
|
16
25
|
|
17
26
|
|
18
27
|
@dataclass(slots=True, kw_only=True, frozen=True)
|
@@ -112,6 +121,8 @@ class Assembler:
|
|
112
121
|
|
113
122
|
out = []
|
114
123
|
for provider in self._multiproviders[type_id]:
|
124
|
+
if provider.scope and provider.scope not in _get_scope():
|
125
|
+
raise NotInScopeError(provider=provider, scope_stack=_get_scope())
|
115
126
|
assembled_dependency = await self.assemble(provider)
|
116
127
|
try:
|
117
128
|
out.extend(await assembled_dependency())
|
@@ -127,12 +138,16 @@ class Assembler:
|
|
127
138
|
if type_id not in self._providers:
|
128
139
|
raise LookupError(f"no provider found for target type id '{type_id}'")
|
129
140
|
|
130
|
-
|
141
|
+
provider = self._providers[type_id]
|
142
|
+
if provider.scope and provider.scope not in _get_scope():
|
143
|
+
raise NotInScopeError(provider=provider, scope_stack=_get_scope())
|
144
|
+
|
145
|
+
assembled_dependency = await self.assemble(provider)
|
131
146
|
try:
|
132
147
|
value = await assembled_dependency()
|
133
148
|
except Exception as err:
|
134
149
|
raise ProviderError(
|
135
|
-
provider=
|
150
|
+
provider=provider,
|
136
151
|
error_type=type(err),
|
137
152
|
error_message=str(err),
|
138
153
|
) from err
|
@@ -178,6 +193,14 @@ class Assembler:
|
|
178
193
|
del self._assembled_outputs[type_id]
|
179
194
|
self._providers[type_id] = provider
|
180
195
|
|
196
|
+
def scope(self, scope: str) -> "_ScopeContextManager":
|
197
|
+
return _ScopeContextManager(scope=scope, assembler=self)
|
198
|
+
|
199
|
+
def _exit_scope(self, scope: str) -> None:
|
200
|
+
for type_id, provider in self._providers.items():
|
201
|
+
if provider.scope == scope:
|
202
|
+
self._assembled_outputs.pop(type_id, None)
|
203
|
+
|
181
204
|
def _resolve_providers(self, type_id: TypeId) -> Iterable[Provide]:
|
182
205
|
"""
|
183
206
|
Resolves the chain of providers required to satisfy the provider of a given type.
|
@@ -205,7 +228,7 @@ class Assembler:
|
|
205
228
|
yield from (
|
206
229
|
child_provider
|
207
230
|
for root_provider in root_providers
|
208
|
-
for root_provider_param in root_provider.
|
231
|
+
for root_provider_param in root_provider.parameter_type_ids
|
209
232
|
for child_provider in self._resolve_providers(root_provider_param)
|
210
233
|
)
|
211
234
|
yield from root_providers
|
@@ -251,3 +274,27 @@ class Assembler:
|
|
251
274
|
kwargs[param.name] = val
|
252
275
|
|
253
276
|
return signature.bind(*args, **kwargs)
|
277
|
+
|
278
|
+
|
279
|
+
class _ScopeContextManager:
|
280
|
+
def __init__(self, scope: str, assembler: Assembler) -> None:
|
281
|
+
self._scope = scope
|
282
|
+
self._assembler = assembler
|
283
|
+
|
284
|
+
def __enter__(self) -> Assembler:
|
285
|
+
_get_scope().append(self._scope)
|
286
|
+
return self._assembler
|
287
|
+
|
288
|
+
def __exit__(
|
289
|
+
self,
|
290
|
+
exc_type: type[BaseException] | None,
|
291
|
+
exc_value: BaseException | None,
|
292
|
+
traceback: TracebackType | None,
|
293
|
+
/,
|
294
|
+
) -> None:
|
295
|
+
popped = _get_scope().pop()
|
296
|
+
if popped != self._scope:
|
297
|
+
raise RuntimeError(
|
298
|
+
f"Exited scope '{popped}' is not the expected scope '{self._scope}'"
|
299
|
+
)
|
300
|
+
self._assembler._exit_scope(self._scope)
|
engin/_block.py
CHANGED
@@ -11,14 +11,14 @@ if TYPE_CHECKING:
|
|
11
11
|
|
12
12
|
|
13
13
|
def provide(
|
14
|
-
func_: Func | None = None, *, override: bool = False
|
14
|
+
func_: Func | None = None, *, scope: str | None = None, override: bool = False
|
15
15
|
) -> Func | Callable[[Func], Func]:
|
16
16
|
"""
|
17
17
|
A decorator for defining a Provider in a Block.
|
18
18
|
"""
|
19
19
|
|
20
20
|
def _inner(func: Func) -> Func:
|
21
|
-
func._opt = Provide(func, override=override) # type: ignore[attr-defined]
|
21
|
+
func._opt = Provide(func, override=override, scope=scope) # type: ignore[attr-defined]
|
22
22
|
return func
|
23
23
|
|
24
24
|
if func_ is None:
|
engin/_cli/_graph.html
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
<!doctype html>
|
2
|
+
<html lang="en">
|
3
|
+
<style>
|
4
|
+
#mermaid-container {
|
5
|
+
width: 100%;
|
6
|
+
height: 100%;
|
7
|
+
overflow: auto; /* Enables scrolling */
|
8
|
+
border: 1px solid #ddd;
|
9
|
+
cursor: grab;
|
10
|
+
position: relative;
|
11
|
+
white-space: nowrap; /* Prevents wrapping */
|
12
|
+
}
|
13
|
+
|
14
|
+
#mermaid-content {
|
15
|
+
width: max-content; /* Ensures content can expand */
|
16
|
+
height: max-content;
|
17
|
+
}
|
18
|
+
</style>
|
19
|
+
<script type="module">
|
20
|
+
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
|
21
|
+
let config = { flowchart: { useMaxWidth: false, htmlLabels: true, defaultRenderer: "elk" } };
|
22
|
+
mermaid.initialize(config);
|
23
|
+
|
24
|
+
// Drag-to-Move Functionality
|
25
|
+
const container = document.getElementById("mermaid-container");
|
26
|
+
|
27
|
+
let isDragging = false;
|
28
|
+
let startX, startY, scrollLeft, scrollTop;
|
29
|
+
|
30
|
+
container.addEventListener("pointerdown", (e) => {
|
31
|
+
isDragging = true;
|
32
|
+
startX = e.clientX;
|
33
|
+
startY = e.clientY;
|
34
|
+
scrollLeft = container.scrollLeft;
|
35
|
+
scrollTop = container.scrollTop;
|
36
|
+
container.style.cursor = "grabbing";
|
37
|
+
});
|
38
|
+
|
39
|
+
container.addEventListener("pointermove", (e) => {
|
40
|
+
if (!isDragging) return;
|
41
|
+
const x = e.clientX - startX;
|
42
|
+
const y = e.clientY - startY;
|
43
|
+
container.scrollLeft = scrollLeft - x;
|
44
|
+
container.scrollTop = scrollTop - y;
|
45
|
+
});
|
46
|
+
|
47
|
+
container.addEventListener("pointerup", () => {
|
48
|
+
isDragging = false;
|
49
|
+
container.style.cursor = "grab";
|
50
|
+
});
|
51
|
+
|
52
|
+
container.addEventListener("pointerleave", () => {
|
53
|
+
isDragging = false;
|
54
|
+
container.style.cursor = "grab";
|
55
|
+
});
|
56
|
+
</script>
|
57
|
+
<body>
|
58
|
+
<div style="border-style:outset">
|
59
|
+
<p>LEGEND</p>
|
60
|
+
<pre class="mermaid" id="legend">
|
61
|
+
graph LR
|
62
|
+
%%LEGEND%%
|
63
|
+
classDef b0 fill:#7fc97f;
|
64
|
+
classDef external stroke-dasharray: 5 5;
|
65
|
+
</pre>
|
66
|
+
</div>
|
67
|
+
<div id="mermaid-container" style="width: 100%; overflow-x: auto; border: 1px solid #ddd; cursor: grab; position: relative;">
|
68
|
+
<div id="mermaid-content" style="width: max-content; height: max-content;">
|
69
|
+
<pre class="mermaid" id="graph">
|
70
|
+
%%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
|
71
|
+
graph LR
|
72
|
+
%%DATA%%
|
73
|
+
classDef external stroke-dasharray: 5 5;
|
74
|
+
</pre>
|
75
|
+
</div>
|
76
|
+
</div>
|
77
|
+
</body>
|
78
|
+
</html>
|
engin/_cli/_graph.py
CHANGED
@@ -5,13 +5,14 @@ import socketserver
|
|
5
5
|
import sys
|
6
6
|
import threading
|
7
7
|
from http.server import BaseHTTPRequestHandler
|
8
|
+
from pathlib import Path
|
8
9
|
from time import sleep
|
9
10
|
from typing import Annotated, Any
|
10
11
|
|
11
12
|
import typer
|
12
13
|
from rich import print
|
13
14
|
|
14
|
-
from engin import Engin, Entrypoint, Invoke
|
15
|
+
from engin import Engin, Entrypoint, Invoke, TypeId
|
15
16
|
from engin._cli._utils import print_error
|
16
17
|
from engin._dependency import Dependency, Provide, Supply
|
17
18
|
from engin.ext.asgi import ASGIEngin
|
@@ -23,11 +24,9 @@ except ImportError:
|
|
23
24
|
|
24
25
|
cli = typer.Typer()
|
25
26
|
|
26
|
-
|
27
27
|
# mute logging from importing of files + engin's debug logging.
|
28
28
|
logging.disable()
|
29
29
|
|
30
|
-
|
31
30
|
_APP_ORIGIN = ""
|
32
31
|
|
33
32
|
_CLI_HELP = {
|
@@ -79,8 +78,25 @@ def serve_graph(
|
|
79
78
|
f"{_render_node(node.parent)} --> {_render_node(node.node)}"
|
80
79
|
for node in nodes
|
81
80
|
if node.parent is not None
|
81
|
+
and not (node.node.block_name and node.node.block_name == node.parent.block_name)
|
82
82
|
]
|
83
83
|
|
84
|
+
blocks = {node.node.block_name for node in nodes if node.node.block_name is not None}
|
85
|
+
|
86
|
+
# group blocks into subgraphs
|
87
|
+
for block in blocks:
|
88
|
+
dependencies.append(f"subgraph {block}")
|
89
|
+
dependencies.extend(
|
90
|
+
[
|
91
|
+
f"{_render_node(node.parent, False)} --> {_render_node(node.node, False)}"
|
92
|
+
for node in nodes
|
93
|
+
if node.parent is not None
|
94
|
+
and node.node.block_name == block
|
95
|
+
and node.parent.block_name == block
|
96
|
+
]
|
97
|
+
)
|
98
|
+
dependencies.append("end")
|
99
|
+
|
84
100
|
html = (
|
85
101
|
_GRAPH_HTML.replace("%%DATA%%", "\n".join(dependencies))
|
86
102
|
.replace(
|
@@ -123,18 +139,14 @@ _BLOCK_IDX: dict[str, int] = {}
|
|
123
139
|
_SEEN_BLOCKS: list[str] = []
|
124
140
|
|
125
141
|
|
126
|
-
def _render_node(node: Dependency) -> str:
|
142
|
+
def _render_node(node: Dependency, render_block: bool = True) -> str:
|
127
143
|
node_id = id(node)
|
128
144
|
md = ""
|
129
145
|
style = ""
|
130
146
|
|
131
147
|
# format block name
|
132
|
-
if n := node.block_name:
|
148
|
+
if render_block and (n := node.block_name):
|
133
149
|
md += f"_{n}_\n"
|
134
|
-
if n not in _BLOCK_IDX:
|
135
|
-
_BLOCK_IDX[n] = len(_SEEN_BLOCKS) % 8
|
136
|
-
_SEEN_BLOCKS.append(n)
|
137
|
-
style = f"b{_BLOCK_IDX[n]}"
|
138
150
|
|
139
151
|
node_root_package = node.source_package.split(".", maxsplit=1)[0]
|
140
152
|
if node_root_package != _APP_ORIGIN:
|
@@ -147,13 +159,13 @@ def _render_node(node: Dependency) -> str:
|
|
147
159
|
style = f":::{style}"
|
148
160
|
|
149
161
|
if isinstance(node, Supply):
|
150
|
-
md += f"{node.return_type_id}"
|
162
|
+
md += f"{_short_name(node.return_type_id)}"
|
151
163
|
return f'{node_id}("`{md}`"){style}'
|
152
164
|
if isinstance(node, Provide):
|
153
|
-
md += f"{node.return_type_id}"
|
165
|
+
md += f"{_short_name(node.return_type_id)}"
|
154
166
|
return f'{node_id}["`{md}`"]{style}'
|
155
167
|
if isinstance(node, Entrypoint):
|
156
|
-
entrypoint_type = node.
|
168
|
+
entrypoint_type = node.parameter_type_ids[0]
|
157
169
|
md += f"{entrypoint_type}"
|
158
170
|
return f'{node_id}[/"`{md}`"\\]{style}'
|
159
171
|
if isinstance(node, Invoke):
|
@@ -166,48 +178,11 @@ def _render_node(node: Dependency) -> str:
|
|
166
178
|
return f'{node_id}["`{node.name}`"]{style}'
|
167
179
|
|
168
180
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
<p>LEGEND</p>
|
175
|
-
<pre class="mermaid">
|
176
|
-
graph LR
|
177
|
-
%%LEGEND%%
|
178
|
-
classDef b0 fill:#7fc97f;
|
179
|
-
classDef external stroke-dasharray: 5 5;
|
180
|
-
</pre>
|
181
|
-
</div>
|
182
|
-
<pre class="mermaid">
|
183
|
-
graph TD
|
184
|
-
%%DATA%%
|
185
|
-
classDef b0 fill:#7fc97f;
|
186
|
-
classDef b1 fill:#beaed4;
|
187
|
-
classDef b2 fill:#fdc086;
|
188
|
-
classDef b3 fill:#ffff99;
|
189
|
-
classDef b4 fill:#386cb0;
|
190
|
-
classDef b5 fill:#f0027f;
|
191
|
-
classDef b6 fill:#bf5b17;
|
192
|
-
classDef b7 fill:#666666;
|
193
|
-
classDef b0E fill:#7fc97f,stroke-dasharray: 5 5;
|
194
|
-
classDef b1E fill:#beaed4,stroke-dasharray: 5 5;
|
195
|
-
classDef b2E fill:#fdc086,stroke-dasharray: 5 5;
|
196
|
-
classDef b3E fill:#ffff99,stroke-dasharray: 5 5;
|
197
|
-
classDef b4E fill:#386cb0,stroke-dasharray: 5 5;
|
198
|
-
classDef b5E fill:#f0027f,stroke-dasharray: 5 5;
|
199
|
-
classDef b6E fill:#bf5b17,stroke-dasharray: 5 5;
|
200
|
-
classDef b7E fill:#666666,stroke-dasharray: 5 5;
|
201
|
-
classDef external stroke-dasharray: 5 5;
|
202
|
-
</pre>
|
203
|
-
<script type="module">
|
204
|
-
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
|
205
|
-
let config = { flowchart: { useMaxWidth: false, htmlLabels: true } };
|
206
|
-
mermaid.initialize(config);
|
207
|
-
</script>
|
208
|
-
</body>
|
209
|
-
</html>
|
210
|
-
"""
|
181
|
+
def _short_name(name: TypeId) -> str:
|
182
|
+
return str(name).rsplit(".", maxsplit=1)[-1]
|
183
|
+
|
184
|
+
|
185
|
+
_GRAPH_HTML = (Path(__file__).parent / "_graph.html").read_text()
|
211
186
|
|
212
187
|
DEFAULT_LEGEND = (
|
213
188
|
"0[/Invoke/] ~~~ 1[/Entrypoint\\] ~~~ 2[Provide] ~~~ 3(Supply)"
|
engin/_dependency.py
CHANGED
@@ -76,7 +76,7 @@ class Dependency(ABC, Option, Generic[P, T]):
|
|
76
76
|
return f"{self._func.__module__}.{self._func.__name__}"
|
77
77
|
|
78
78
|
@property
|
79
|
-
def
|
79
|
+
def parameter_type_ids(self) -> list[TypeId]:
|
80
80
|
parameters = list(self._signature.parameters.values())
|
81
81
|
if not parameters:
|
82
82
|
return []
|
@@ -136,7 +136,7 @@ class Entrypoint(Invoke):
|
|
136
136
|
super().__init__(invocation=_noop)
|
137
137
|
|
138
138
|
@property
|
139
|
-
def
|
139
|
+
def parameter_type_ids(self) -> list[TypeId]:
|
140
140
|
return [TypeId.from_type(self._type)]
|
141
141
|
|
142
142
|
@property
|
@@ -152,24 +152,40 @@ class Entrypoint(Invoke):
|
|
152
152
|
|
153
153
|
|
154
154
|
class Provide(Dependency[Any, T]):
|
155
|
-
def __init__(
|
155
|
+
def __init__(
|
156
|
+
self,
|
157
|
+
builder: Func[P, T],
|
158
|
+
*,
|
159
|
+
scope: str | None = None,
|
160
|
+
as_type: type | None = None,
|
161
|
+
override: bool = False,
|
162
|
+
) -> None:
|
156
163
|
"""
|
157
164
|
Provide a type via a builder or factory function.
|
158
165
|
|
159
166
|
Args:
|
160
167
|
builder: the builder function that returns the type.
|
161
|
-
|
162
|
-
|
168
|
+
scope: (optional) associate this provider with a specific scope.
|
169
|
+
as_type: (optional) allows you to explicitly specify the provided type, e.g.
|
170
|
+
to type erase a concrete type, or to provide a mock implementation.
|
171
|
+
override: (optional) allow this provider to override other providers for the
|
172
|
+
same type from the same package.
|
163
173
|
"""
|
164
174
|
super().__init__(func=builder)
|
175
|
+
self._scope = scope
|
165
176
|
self._override = override
|
177
|
+
self._explicit_type = as_type
|
178
|
+
|
179
|
+
if self._explicit_type is not None:
|
180
|
+
self._signature = self._signature.replace(return_annotation=self._explicit_type)
|
181
|
+
|
166
182
|
self._is_multi = typing.get_origin(self.return_type) is list
|
167
183
|
|
168
184
|
# Validate that the provider does to depend on its own output value, as this will
|
169
185
|
# cause a recursion error and is undefined behaviour wise.
|
170
186
|
if any(
|
171
187
|
self.return_type == param.annotation
|
172
|
-
for param in self.
|
188
|
+
for param in self._signature.parameters.values()
|
173
189
|
):
|
174
190
|
raise ValueError("A provider cannot depend on its own return type")
|
175
191
|
|
@@ -183,6 +199,8 @@ class Provide(Dependency[Any, T]):
|
|
183
199
|
|
184
200
|
@property
|
185
201
|
def return_type(self) -> type[T]:
|
202
|
+
if self._explicit_type is not None:
|
203
|
+
return self._explicit_type
|
186
204
|
if isclass(self._func):
|
187
205
|
return_type = self._func # __init__ returns self
|
188
206
|
else:
|
@@ -203,6 +221,10 @@ class Provide(Dependency[Any, T]):
|
|
203
221
|
def is_multiprovider(self) -> bool:
|
204
222
|
return self._is_multi
|
205
223
|
|
224
|
+
@property
|
225
|
+
def scope(self) -> str | None:
|
226
|
+
return self._scope
|
227
|
+
|
206
228
|
def apply(self, engin: "Engin") -> None:
|
207
229
|
type_id = self.return_type_id
|
208
230
|
if self.is_multiprovider:
|
@@ -245,23 +267,19 @@ class Supply(Provide, Generic[T]):
|
|
245
267
|
function.
|
246
268
|
|
247
269
|
Args:
|
248
|
-
value: the value to Supply
|
249
|
-
as_type: allows you to specify the provided type,
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
package.
|
270
|
+
value: the value to Supply.
|
271
|
+
as_type: (optional) allows you to explicitly specify the provided type, e.g.
|
272
|
+
to type erase a concrete type, or to provide a mock implementation.
|
273
|
+
override: (optional) allow this provider to override other providers for the
|
274
|
+
same type from the same package.
|
254
275
|
"""
|
255
276
|
self._value = value
|
256
|
-
self.
|
257
|
-
if self._type_hint is not None:
|
258
|
-
self._get_val.__annotations__["return"] = as_type
|
259
|
-
super().__init__(builder=self._get_val, override=override)
|
277
|
+
super().__init__(builder=self._get_val, as_type=as_type, override=override)
|
260
278
|
|
261
279
|
@property
|
262
280
|
def return_type(self) -> type[T]:
|
263
|
-
if self.
|
264
|
-
return self.
|
281
|
+
if self._explicit_type is not None:
|
282
|
+
return self._explicit_type
|
265
283
|
if isinstance(self._value, list):
|
266
284
|
return list[type(self._value[0])] # type: ignore[misc,return-value]
|
267
285
|
return type(self._value)
|
engin/_exceptions.py
CHANGED
@@ -3,7 +3,19 @@ from typing import Any
|
|
3
3
|
from engin._dependency import Provide
|
4
4
|
|
5
5
|
|
6
|
-
class
|
6
|
+
class EnginError(Exception):
|
7
|
+
"""
|
8
|
+
Base class for all custom exceptions in the Engin library.
|
9
|
+
"""
|
10
|
+
|
11
|
+
|
12
|
+
class AssemblerError(EnginError):
|
13
|
+
"""
|
14
|
+
Base class for all custom exceptions raised by the Assembler.
|
15
|
+
"""
|
16
|
+
|
17
|
+
|
18
|
+
class ProviderError(AssemblerError):
|
7
19
|
"""
|
8
20
|
Raised when a Provider errors during Assembly.
|
9
21
|
"""
|
@@ -24,3 +36,19 @@ class ProviderError(Exception):
|
|
24
36
|
|
25
37
|
def __str__(self) -> str:
|
26
38
|
return self.message
|
39
|
+
|
40
|
+
|
41
|
+
class NotInScopeError(AssemblerError):
|
42
|
+
"""
|
43
|
+
Raised when a Provider is requested outside of its scope.
|
44
|
+
"""
|
45
|
+
|
46
|
+
def __init__(self, provider: Provide[Any], scope_stack: list[str]) -> None:
|
47
|
+
self.provider = provider
|
48
|
+
self.message = (
|
49
|
+
f"provider '{provider.name}' was requested outside of its specified scope "
|
50
|
+
f"'{provider.scope}', current scope stack is {scope_stack}"
|
51
|
+
)
|
52
|
+
|
53
|
+
def __str__(self) -> str:
|
54
|
+
return self.message
|
engin/_graph.py
CHANGED
engin/ext/asgi.py
CHANGED
@@ -47,7 +47,8 @@ class ASGIEngin(Engin, ASGIType):
|
|
47
47
|
elif message["type"] == "lifespan.shutdown":
|
48
48
|
await self.stop()
|
49
49
|
|
50
|
-
|
50
|
+
with self._assembler.scope("request"):
|
51
|
+
await self._asgi_app(scope, receive, send)
|
51
52
|
|
52
53
|
async def _startup(self) -> None:
|
53
54
|
self._asgi_app = await self._assembler.build(self._asgi_type)
|
engin/ext/fastapi.py
CHANGED
@@ -75,7 +75,7 @@ class _FastAPIDependencyGrapher(DependencyGrapher):
|
|
75
75
|
) -> list[Node]:
|
76
76
|
nodes: list[Node] = []
|
77
77
|
for root in roots:
|
78
|
-
for parameter in root.
|
78
|
+
for parameter in root.parameter_type_ids:
|
79
79
|
provider = self._providers[parameter]
|
80
80
|
|
81
81
|
# multiprovider
|
@@ -162,7 +162,7 @@ class APIRouteDependency(Dependency):
|
|
162
162
|
return self._route
|
163
163
|
|
164
164
|
@property
|
165
|
-
def
|
165
|
+
def parameter_type_ids(self) -> list[TypeId]:
|
166
166
|
parameters = list(self._signature.parameters.values())
|
167
167
|
if not parameters:
|
168
168
|
return []
|
@@ -0,0 +1,24 @@
|
|
1
|
+
engin/__init__.py,sha256=rBTteMLAVKg4TJSaMElJUwz72BA_X7nBTREg-I-bWhA,584
|
2
|
+
engin/_assembler.py,sha256=r2Vr9UO7pHYvj71087PIJYT9lFBNPni3x34MtjMHN7A,10952
|
3
|
+
engin/_block.py,sha256=8ysWrmHkWpTm6bmSc6jZVoO0Ax5Svu1HwxpZwAtIF_o,2617
|
4
|
+
engin/_dependency.py,sha256=5x4_0QvHtqv6R_brKHRc-INKE4oMh1JU8-9RCmulp4Q,8976
|
5
|
+
engin/_engin.py,sha256=yIpZdeqvm8hv0RxOV0veFuvyu9xQ054JSaeuUWwHdOQ,7380
|
6
|
+
engin/_exceptions.py,sha256=UzMppJWDk_Hx3qWAypcPVLw9OYCibqiZjLYeTl22zaE,1355
|
7
|
+
engin/_graph.py,sha256=y1g7Lm_Zy5GPEgRsggCKV5DDaDzcwUl8v3IZCK8jyGI,1631
|
8
|
+
engin/_introspect.py,sha256=VdREX6Lhhga5SnEP9G7mjHkgJR4mpqk_SMnmL2zTcqY,966
|
9
|
+
engin/_lifecycle.py,sha256=cSWe3euZkmpxmUPFvph2lsTtvuZbxttEfBL-RnOI7lo,5325
|
10
|
+
engin/_option.py,sha256=nZcdrehp1QwgxMUoIpsM0PJuu1q1pbXzhcVsetbsHpc,223
|
11
|
+
engin/_type_utils.py,sha256=Pmm4m1_WdevT5KTe8tzY_BseNxPyhu_nKsLGgyNcPpo,2247
|
12
|
+
engin/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
13
|
+
engin/_cli/__init__.py,sha256=lp1KiBpcgk_dZU5V9DjgLPwmp0ja444fwLH2CYCscNc,302
|
14
|
+
engin/_cli/_graph.html,sha256=rR5dnDKoz7KtSff0ERCi2UKuoH_Z03MRYiXI_W03G5k,2430
|
15
|
+
engin/_cli/_graph.py,sha256=YZ0awBLtWD5W6xrHO9ueMnk_EIsYM4_j_bmquiLsb2Y,5542
|
16
|
+
engin/_cli/_utils.py,sha256=AQFtLO8qjYRCTQc9A8Z1HVf7eZr8iGWogxbYzsgIkS4,360
|
17
|
+
engin/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
18
|
+
engin/ext/asgi.py,sha256=d5Z6gtMVWDZdAlvrTaMt987sKyiq__A0X4gJQ7IETmA,3247
|
19
|
+
engin/ext/fastapi.py,sha256=TGNf0LFLaTLMLlAycH7GgP_GcBld262v9xboGOwhvgE,6362
|
20
|
+
engin-0.0.17.dist-info/METADATA,sha256=1SUQZwsFr3neO0WJ_gLLv90kgJCcBNUbwWWi2zZZ77Q,2354
|
21
|
+
engin-0.0.17.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
22
|
+
engin-0.0.17.dist-info/entry_points.txt,sha256=sW247zZUMxm0b5UKYvPuqQQljYDtU-j2zK3cu7gHwM0,41
|
23
|
+
engin-0.0.17.dist-info/licenses/LICENSE,sha256=XHh5LPUPKZWTBqBv2xxN2RU7D59nHoiJGb5RIt8f45w,1070
|
24
|
+
engin-0.0.17.dist-info/RECORD,,
|
engin-0.0.15.dist-info/RECORD
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
engin/__init__.py,sha256=rBTteMLAVKg4TJSaMElJUwz72BA_X7nBTREg-I-bWhA,584
|
2
|
-
engin/_assembler.py,sha256=GpTLW9AmGChnwWWK3SUq5AsxJJ8ukH7yWpemBiH87pw,9294
|
3
|
-
engin/_block.py,sha256=Ypl6ffU52dgrHHgCcPokzfRD2-Lbu9b2wYMCgAZIx4g,2578
|
4
|
-
engin/_dependency.py,sha256=w-MxF6Ju1Rc2umc7pk3bXTlc65NVIs1VEBj8825WEcg,8328
|
5
|
-
engin/_engin.py,sha256=yIpZdeqvm8hv0RxOV0veFuvyu9xQ054JSaeuUWwHdOQ,7380
|
6
|
-
engin/_exceptions.py,sha256=fsc4pTOIGHUh0x7oZhEXPJUTE268sIhswLoiqXaudiw,635
|
7
|
-
engin/_graph.py,sha256=1pMB0cr--uS0XJycDb1rS_X45RBpoyA6NkKqbeSuz1Q,1628
|
8
|
-
engin/_introspect.py,sha256=VdREX6Lhhga5SnEP9G7mjHkgJR4mpqk_SMnmL2zTcqY,966
|
9
|
-
engin/_lifecycle.py,sha256=cSWe3euZkmpxmUPFvph2lsTtvuZbxttEfBL-RnOI7lo,5325
|
10
|
-
engin/_option.py,sha256=nZcdrehp1QwgxMUoIpsM0PJuu1q1pbXzhcVsetbsHpc,223
|
11
|
-
engin/_type_utils.py,sha256=Pmm4m1_WdevT5KTe8tzY_BseNxPyhu_nKsLGgyNcPpo,2247
|
12
|
-
engin/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
13
|
-
engin/_cli/__init__.py,sha256=lp1KiBpcgk_dZU5V9DjgLPwmp0ja444fwLH2CYCscNc,302
|
14
|
-
engin/_cli/_graph.py,sha256=1Kj09BnKh5BTmuM4tqaGICS4KVDGNWT4oGFIrUa9xdU,6230
|
15
|
-
engin/_cli/_utils.py,sha256=AQFtLO8qjYRCTQc9A8Z1HVf7eZr8iGWogxbYzsgIkS4,360
|
16
|
-
engin/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
17
|
-
engin/ext/asgi.py,sha256=6V5Aad37MyGzkCtU5TlDrm0o5C04Un_LLvcomxnAmHY,3196
|
18
|
-
engin/ext/fastapi.py,sha256=e8UV521Mq9Iqr55CT7_jtd51iaIZjWlAacoqFBXsh-k,6356
|
19
|
-
engin-0.0.15.dist-info/METADATA,sha256=qYhQHzJ_YrJEaZ_p4ddZL4OZDOtzWHkQFLimPH_XNDE,2354
|
20
|
-
engin-0.0.15.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
21
|
-
engin-0.0.15.dist-info/entry_points.txt,sha256=sW247zZUMxm0b5UKYvPuqQQljYDtU-j2zK3cu7gHwM0,41
|
22
|
-
engin-0.0.15.dist-info/licenses/LICENSE,sha256=XHh5LPUPKZWTBqBv2xxN2RU7D59nHoiJGb5RIt8f45w,1070
|
23
|
-
engin-0.0.15.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|