engin 0.0.8__py3-none-any.whl → 0.0.9__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/_block.py +2 -0
- engin/_dependency.py +19 -1
- engin/_graph.py +25 -14
- engin/ext/asgi.py +6 -1
- engin/ext/fastapi.py +134 -4
- engin/scripts/graph.py +68 -16
- {engin-0.0.8.dist-info → engin-0.0.9.dist-info}/METADATA +1 -1
- engin-0.0.9.dist-info/RECORD +20 -0
- engin-0.0.8.dist-info/RECORD +0 -20
- {engin-0.0.8.dist-info → engin-0.0.9.dist-info}/WHEEL +0 -0
- {engin-0.0.8.dist-info → engin-0.0.9.dist-info}/entry_points.txt +0 -0
- {engin-0.0.8.dist-info → engin-0.0.9.dist-info}/licenses/LICENSE +0 -0
engin/_block.py
CHANGED
@@ -59,6 +59,8 @@ class Block(Iterable[Provide | Invoke]):
|
|
59
59
|
raise RuntimeError("Block option is not an instance of Provide or Invoke")
|
60
60
|
opt.set_block_name(self._name)
|
61
61
|
self._options.append(opt)
|
62
|
+
for opt in self.options:
|
63
|
+
opt.set_block_name(self._name)
|
62
64
|
|
63
65
|
@property
|
64
66
|
def name(self) -> str:
|
engin/_dependency.py
CHANGED
@@ -31,13 +31,23 @@ class Dependency(ABC, Generic[P, T]):
|
|
31
31
|
self._block_name = block_name
|
32
32
|
|
33
33
|
@property
|
34
|
-
def
|
34
|
+
def origin(self) -> str:
|
35
|
+
"""
|
36
|
+
The module that this Dependency originated from.
|
37
|
+
|
38
|
+
Returns:
|
39
|
+
A string, e.g. "examples.fastapi.app"
|
40
|
+
"""
|
35
41
|
return self._func.__module__
|
36
42
|
|
37
43
|
@property
|
38
44
|
def block_name(self) -> str | None:
|
39
45
|
return self._block_name
|
40
46
|
|
47
|
+
@property
|
48
|
+
def func_name(self) -> str:
|
49
|
+
return self._func.__name__
|
50
|
+
|
41
51
|
@property
|
42
52
|
def name(self) -> str:
|
43
53
|
if self._block_name:
|
@@ -105,6 +115,10 @@ class Entrypoint(Invoke):
|
|
105
115
|
self._type = type_
|
106
116
|
super().__init__(invocation=_noop, block_name=block_name)
|
107
117
|
|
118
|
+
@property
|
119
|
+
def origin(self) -> str:
|
120
|
+
return self._type.__module__
|
121
|
+
|
108
122
|
@property
|
109
123
|
def parameter_types(self) -> list[TypeId]:
|
110
124
|
return [type_id_of(self._type)]
|
@@ -172,6 +186,10 @@ class Supply(Provide, Generic[T]):
|
|
172
186
|
self._get_val.__annotations__["return"] = type_hint
|
173
187
|
super().__init__(builder=self._get_val, block_name=block_name)
|
174
188
|
|
189
|
+
@property
|
190
|
+
def origin(self) -> str:
|
191
|
+
return self._value.__module__
|
192
|
+
|
175
193
|
@property
|
176
194
|
def return_type(self) -> type[T]:
|
177
195
|
if self._type_hint is not None:
|
engin/_graph.py
CHANGED
@@ -1,39 +1,50 @@
|
|
1
1
|
from collections.abc import Iterable
|
2
|
-
from
|
2
|
+
from dataclasses import dataclass
|
3
3
|
|
4
|
-
from engin
|
4
|
+
from engin import Provide
|
5
|
+
from engin._dependency import Dependency
|
5
6
|
from engin._type_utils import TypeId
|
6
7
|
|
7
8
|
|
8
|
-
|
9
|
+
@dataclass(slots=True, frozen=True, kw_only=True)
|
10
|
+
class Node:
|
11
|
+
"""
|
12
|
+
A Node in the Dependency Graph.
|
13
|
+
"""
|
14
|
+
|
9
15
|
node: Dependency
|
10
16
|
parent: Dependency | None
|
11
17
|
|
18
|
+
def __repr__(self) -> str:
|
19
|
+
return f"Node(node={self.node!s},parent={self.parent!s})"
|
20
|
+
|
12
21
|
|
13
22
|
class DependencyGrapher:
|
14
23
|
def __init__(self, providers: dict[TypeId, Provide | list[Provide]]) -> None:
|
15
24
|
self._providers: dict[TypeId, Provide | list[Provide]] = providers
|
16
25
|
|
17
26
|
def resolve(self, roots: Iterable[Dependency]) -> list[Node]:
|
18
|
-
|
19
|
-
nodes: list[Node] = []
|
27
|
+
return self._resolve_recursive(roots, seen=set())
|
20
28
|
|
29
|
+
def _resolve_recursive(
|
30
|
+
self, roots: Iterable[Dependency], *, seen: set[TypeId]
|
31
|
+
) -> list[Node]:
|
32
|
+
nodes: list[Node] = []
|
21
33
|
for root in roots:
|
22
34
|
for parameter in root.parameter_types:
|
23
|
-
if parameter in seen:
|
24
|
-
continue
|
25
|
-
|
26
|
-
seen.add(parameter)
|
27
35
|
provider = self._providers[parameter]
|
28
36
|
|
29
37
|
# multiprovider
|
30
38
|
if isinstance(provider, list):
|
31
|
-
for p in provider
|
32
|
-
|
33
|
-
nodes.extend(self.
|
39
|
+
nodes.extend(Node(node=p, parent=root) for p in provider)
|
40
|
+
if parameter not in seen:
|
41
|
+
nodes.extend(self._resolve_recursive(provider, seen=seen))
|
34
42
|
# single provider
|
35
43
|
else:
|
36
|
-
nodes.append(
|
37
|
-
|
44
|
+
nodes.append(Node(node=provider, parent=root))
|
45
|
+
if parameter not in seen:
|
46
|
+
nodes.extend(self._resolve_recursive([provider], seen=seen))
|
47
|
+
|
48
|
+
seen.add(parameter)
|
38
49
|
|
39
50
|
return nodes
|
engin/ext/asgi.py
CHANGED
@@ -2,10 +2,11 @@ import traceback
|
|
2
2
|
from collections.abc import Awaitable, Callable, MutableMapping
|
3
3
|
from typing import Any, ClassVar, Protocol, TypeAlias
|
4
4
|
|
5
|
-
from engin import Engin, Option
|
5
|
+
from engin import Engin, Entrypoint, Option
|
6
6
|
|
7
7
|
__all__ = ["ASGIEngin", "ASGIType"]
|
8
8
|
|
9
|
+
from engin._graph import DependencyGrapher, Node
|
9
10
|
|
10
11
|
_Scope: TypeAlias = MutableMapping[str, Any]
|
11
12
|
_Message: TypeAlias = MutableMapping[str, Any]
|
@@ -49,6 +50,10 @@ class ASGIEngin(Engin, ASGIType):
|
|
49
50
|
await self.start()
|
50
51
|
self._asgi_app = await self._assembler.get(self._asgi_type)
|
51
52
|
|
53
|
+
def graph(self) -> list[Node]:
|
54
|
+
grapher = DependencyGrapher({**self._providers, **self._multiproviders})
|
55
|
+
return grapher.resolve([Entrypoint(self._asgi_type), *self._invocations])
|
56
|
+
|
52
57
|
|
53
58
|
class _Rereceive:
|
54
59
|
def __init__(self, message: _Message) -> None:
|
engin/ext/fastapi.py
CHANGED
@@ -1,10 +1,19 @@
|
|
1
|
+
import inspect
|
2
|
+
import typing
|
3
|
+
from collections.abc import Iterable
|
4
|
+
from inspect import Parameter
|
1
5
|
from typing import ClassVar, TypeVar
|
2
6
|
|
3
|
-
from
|
7
|
+
from fastapi.routing import APIRoute
|
8
|
+
|
9
|
+
from engin import Engin, Entrypoint, Invoke, Option
|
10
|
+
from engin._dependency import Dependency, Supply
|
11
|
+
from engin._graph import DependencyGrapher, Node
|
12
|
+
from engin._type_utils import TypeId, type_id_of
|
4
13
|
from engin.ext.asgi import ASGIEngin
|
5
14
|
|
6
15
|
try:
|
7
|
-
from fastapi import FastAPI
|
16
|
+
from fastapi import APIRouter, FastAPI
|
8
17
|
from fastapi.params import Depends
|
9
18
|
from starlette.requests import HTTPConnection
|
10
19
|
except ImportError as err:
|
@@ -12,7 +21,7 @@ except ImportError as err:
|
|
12
21
|
"fastapi package must be installed to use the fastapi extension"
|
13
22
|
) from err
|
14
23
|
|
15
|
-
__all__ = ["FastAPIEngin", "Inject"]
|
24
|
+
__all__ = ["APIRouteDependency", "FastAPIEngin", "Inject"]
|
16
25
|
|
17
26
|
|
18
27
|
def _attach_engin(
|
@@ -26,6 +35,15 @@ class FastAPIEngin(ASGIEngin):
|
|
26
35
|
_LIB_OPTIONS: ClassVar[list[Option]] = [*ASGIEngin._LIB_OPTIONS, Invoke(_attach_engin)]
|
27
36
|
_asgi_type = FastAPI
|
28
37
|
|
38
|
+
def graph(self) -> list[Node]:
|
39
|
+
grapher = _FastAPIDependencyGrapher({**self._providers, **self._multiproviders})
|
40
|
+
return grapher.resolve(
|
41
|
+
[
|
42
|
+
Entrypoint(self._asgi_type),
|
43
|
+
*[i for i in self._invocations if i.func_name != "_attach_engin"],
|
44
|
+
]
|
45
|
+
)
|
46
|
+
|
29
47
|
|
30
48
|
T = TypeVar("T")
|
31
49
|
|
@@ -35,4 +53,116 @@ def Inject(interface: type[T]) -> Depends:
|
|
35
53
|
engin: Engin = conn.app.state.engin
|
36
54
|
return await engin.assembler.get(interface)
|
37
55
|
|
38
|
-
|
56
|
+
dep = Depends(inner)
|
57
|
+
dep.__engin__ = True # type: ignore[attr-defined]
|
58
|
+
return dep
|
59
|
+
|
60
|
+
|
61
|
+
class _FastAPIDependencyGrapher(DependencyGrapher):
|
62
|
+
"""
|
63
|
+
This exists in order to bridge the gap between
|
64
|
+
"""
|
65
|
+
|
66
|
+
def _resolve_recursive(
|
67
|
+
self, roots: Iterable[Dependency], *, seen: set[TypeId]
|
68
|
+
) -> list[Node]:
|
69
|
+
nodes: list[Node] = []
|
70
|
+
for root in roots:
|
71
|
+
for parameter in root.parameter_types:
|
72
|
+
provider = self._providers[parameter]
|
73
|
+
|
74
|
+
# multiprovider
|
75
|
+
if isinstance(provider, list):
|
76
|
+
for p in provider:
|
77
|
+
nodes.append(Node(node=p, parent=root))
|
78
|
+
|
79
|
+
if isinstance(p, Supply):
|
80
|
+
route_dependencies = _extract_routes_from_supply(p)
|
81
|
+
nodes.extend(
|
82
|
+
Node(node=route_dependency, parent=p)
|
83
|
+
for route_dependency in route_dependencies
|
84
|
+
)
|
85
|
+
nodes.extend(
|
86
|
+
self._resolve_recursive(route_dependencies, seen=seen)
|
87
|
+
)
|
88
|
+
|
89
|
+
if parameter not in seen:
|
90
|
+
nodes.extend(self._resolve_recursive(provider, seen=seen))
|
91
|
+
# single provider
|
92
|
+
else:
|
93
|
+
nodes.append(Node(node=provider, parent=root))
|
94
|
+
# not sure why anyone would ever supply a single APIRouter in an
|
95
|
+
# application, but just in case
|
96
|
+
if isinstance(provider, Supply):
|
97
|
+
route_dependencies = _extract_routes_from_supply(provider)
|
98
|
+
nodes.extend(
|
99
|
+
Node(node=route_dependency, parent=provider)
|
100
|
+
for route_dependency in route_dependencies
|
101
|
+
)
|
102
|
+
nodes.extend(self._resolve_recursive(route_dependencies, seen=seen))
|
103
|
+
if parameter not in seen:
|
104
|
+
nodes.extend(self._resolve_recursive([provider], seen=seen))
|
105
|
+
|
106
|
+
seen.add(parameter)
|
107
|
+
|
108
|
+
return nodes
|
109
|
+
|
110
|
+
|
111
|
+
def _extract_routes_from_supply(supply: Supply) -> list[Dependency]:
|
112
|
+
if supply.is_multiprovider:
|
113
|
+
inner = supply._value[0]
|
114
|
+
if isinstance(inner, APIRouter):
|
115
|
+
return [
|
116
|
+
APIRouteDependency(route, block_name=supply.block_name)
|
117
|
+
for route in inner.routes
|
118
|
+
if isinstance(route, APIRoute)
|
119
|
+
]
|
120
|
+
return []
|
121
|
+
|
122
|
+
|
123
|
+
class APIRouteDependency(Dependency):
|
124
|
+
"""
|
125
|
+
This is a pseudo-dependency that is only used when calling FastAPIEngin.graph() in
|
126
|
+
order to provide richer metadata to the Node.
|
127
|
+
|
128
|
+
This class should never be constructed in application code.
|
129
|
+
"""
|
130
|
+
|
131
|
+
def __init__(self, route: APIRoute, block_name: str | None = None) -> None:
|
132
|
+
"""
|
133
|
+
Warning: this should never be constructed in application code.
|
134
|
+
"""
|
135
|
+
self._route = route
|
136
|
+
self._signature = inspect.signature(route.endpoint)
|
137
|
+
self._block_name = block_name
|
138
|
+
|
139
|
+
@property
|
140
|
+
def route(self) -> APIRoute:
|
141
|
+
return self._route
|
142
|
+
|
143
|
+
@property
|
144
|
+
def parameter_types(self) -> list[TypeId]:
|
145
|
+
parameters = list(self._signature.parameters.values())
|
146
|
+
if not parameters:
|
147
|
+
return []
|
148
|
+
if parameters[0].name == "self":
|
149
|
+
parameters.pop(0)
|
150
|
+
return [
|
151
|
+
type_id_of(typing.get_args(param.annotation)[0])
|
152
|
+
for param in parameters
|
153
|
+
if self._is_injected_param(param)
|
154
|
+
]
|
155
|
+
|
156
|
+
@staticmethod
|
157
|
+
def _is_injected_param(param: Parameter) -> bool:
|
158
|
+
if typing.get_origin(param.annotation) != typing.Annotated:
|
159
|
+
return False
|
160
|
+
args = typing.get_args(param.annotation)
|
161
|
+
if len(args) != 2:
|
162
|
+
return False
|
163
|
+
return isinstance(args[1], Depends) and hasattr(args[1], "__engin__")
|
164
|
+
|
165
|
+
@property
|
166
|
+
def name(self) -> str:
|
167
|
+
methods = ",".join(self._route.methods)
|
168
|
+
return f"{methods} {self._route.path}"
|
engin/scripts/graph.py
CHANGED
@@ -8,8 +8,10 @@ from http.server import BaseHTTPRequestHandler
|
|
8
8
|
from time import sleep
|
9
9
|
from typing import Any
|
10
10
|
|
11
|
-
from engin import Engin
|
12
|
-
from engin._dependency import Dependency, Provide
|
11
|
+
from engin import Engin, Entrypoint, Invoke
|
12
|
+
from engin._dependency import Dependency, Provide, Supply
|
13
|
+
from engin.ext.asgi import ASGIEngin
|
14
|
+
from engin.ext.fastapi import APIRouteDependency
|
13
15
|
|
14
16
|
# mute logging from importing of files + engin's debug logging.
|
15
17
|
logging.disable()
|
@@ -18,9 +20,6 @@ args = ArgumentParser(
|
|
18
20
|
prog="engin-graph",
|
19
21
|
description="Creates a visualisation of your application's dependencies",
|
20
22
|
)
|
21
|
-
args.add_argument(
|
22
|
-
"-e", "--exclude", help="a list of packages or module to exclude", default=["engin"]
|
23
|
-
)
|
24
23
|
args.add_argument(
|
25
24
|
"app",
|
26
25
|
help=(
|
@@ -37,7 +36,6 @@ def serve_graph() -> None:
|
|
37
36
|
parsed = args.parse_args()
|
38
37
|
|
39
38
|
app = parsed.app
|
40
|
-
excluded_modules = parsed.exclude
|
41
39
|
|
42
40
|
try:
|
43
41
|
module_name, engin_name = app.split(":", maxsplit=1)
|
@@ -60,13 +58,19 @@ def serve_graph() -> None:
|
|
60
58
|
|
61
59
|
# transform dependencies into mermaid syntax
|
62
60
|
dependencies = [
|
63
|
-
f"{_render_node(node
|
61
|
+
f"{_render_node(node.parent)} --> {_render_node(node.node)}"
|
64
62
|
for node in nodes
|
65
|
-
if node
|
66
|
-
and not _should_exclude(node["node"].module, excluded_modules)
|
63
|
+
if node.parent is not None
|
67
64
|
]
|
68
65
|
|
69
|
-
html =
|
66
|
+
html = (
|
67
|
+
_GRAPH_HTML.replace("%%DATA%%", "\n".join(dependencies))
|
68
|
+
.replace(
|
69
|
+
"%%LEGEND%%",
|
70
|
+
ASGI_ENGIN_LEGEND if isinstance(instance, ASGIEngin) else DEFAULT_LEGEND,
|
71
|
+
)
|
72
|
+
.encode("utf8")
|
73
|
+
)
|
70
74
|
|
71
75
|
class Handler(BaseHTTPRequestHandler):
|
72
76
|
def do_GET(self) -> None:
|
@@ -93,24 +97,66 @@ def serve_graph() -> None:
|
|
93
97
|
print("Exiting the server...")
|
94
98
|
|
95
99
|
|
100
|
+
_BLOCK_IDX: dict[str, int] = {}
|
101
|
+
_SEEN_BLOCKS: list[str] = []
|
102
|
+
|
103
|
+
|
96
104
|
def _render_node(node: Dependency) -> str:
|
105
|
+
node_id = id(node)
|
106
|
+
md = ""
|
107
|
+
style = ""
|
108
|
+
|
109
|
+
# format block name
|
110
|
+
if n := node.block_name:
|
111
|
+
md += f"_{n}_\n"
|
112
|
+
if n not in _BLOCK_IDX:
|
113
|
+
_BLOCK_IDX[n] = len(_SEEN_BLOCKS) % 8
|
114
|
+
_SEEN_BLOCKS.append(n)
|
115
|
+
style = f":::b{_BLOCK_IDX[n]}"
|
116
|
+
|
117
|
+
if isinstance(node, Supply):
|
118
|
+
md += f"{node.return_type_id}"
|
119
|
+
return f'{node_id}("`{md}`"){style}'
|
97
120
|
if isinstance(node, Provide):
|
98
|
-
|
121
|
+
md += f"{node.return_type_id}"
|
122
|
+
return f'{node_id}["`{md}`"]{style}'
|
123
|
+
if isinstance(node, Entrypoint):
|
124
|
+
entrypoint_type = node.parameter_types[0]
|
125
|
+
md += f"{entrypoint_type}"
|
126
|
+
return f'{node_id}[/"`{md}`"\\]{style}'
|
127
|
+
if isinstance(node, Invoke):
|
128
|
+
md += f"{node.func_name}"
|
129
|
+
return f'{node_id}[/"`{md}`"/]{style}'
|
130
|
+
if isinstance(node, APIRouteDependency):
|
131
|
+
md += f"{node.name}"
|
132
|
+
return f'{node_id}[["`{md}`"]]{style}'
|
99
133
|
else:
|
100
|
-
return node.name
|
101
|
-
|
102
|
-
|
103
|
-
def _should_exclude(module: str, excluded: list[str]) -> bool:
|
104
|
-
return any(module.startswith(e) for e in excluded)
|
134
|
+
return f'{node_id}["`{node.name}`"]{style}'
|
105
135
|
|
106
136
|
|
107
137
|
_GRAPH_HTML = """
|
108
138
|
<!doctype html>
|
109
139
|
<html lang="en">
|
110
140
|
<body>
|
141
|
+
<div style="border-style:outset">
|
142
|
+
<p>LEGEND</p>
|
143
|
+
<pre class="mermaid">
|
144
|
+
graph LR
|
145
|
+
%%LEGEND%%
|
146
|
+
classDef b0 fill:#7fc97f;
|
147
|
+
</pre>
|
148
|
+
</div>
|
111
149
|
<pre class="mermaid">
|
112
150
|
graph TD
|
113
151
|
%%DATA%%
|
152
|
+
classDef b0 fill:#7fc97f;
|
153
|
+
classDef b1 fill:#beaed4;
|
154
|
+
classDef b2 fill:#fdc086;
|
155
|
+
classDef b3 fill:#ffff99;
|
156
|
+
classDef b4 fill:#386cb0;
|
157
|
+
classDef b5 fill:#f0027f;
|
158
|
+
classDef b6 fill:#bf5b17;
|
159
|
+
classDef b7 fill:#666666;
|
114
160
|
</pre>
|
115
161
|
<script type="module">
|
116
162
|
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
|
@@ -120,3 +166,9 @@ _GRAPH_HTML = """
|
|
120
166
|
</body>
|
121
167
|
</html>
|
122
168
|
"""
|
169
|
+
|
170
|
+
DEFAULT_LEGEND = (
|
171
|
+
"0[/Invoke/] ~~~ 1[/Entrypoint\\] ~~~ 2[Provide] ~~~ 3(Supply)"
|
172
|
+
' ~~~ 4["`Block Grouping`"]:::b0'
|
173
|
+
)
|
174
|
+
ASGI_ENGIN_LEGEND = DEFAULT_LEGEND + " ~~~ 5[[API Route]]"
|
@@ -0,0 +1,20 @@
|
|
1
|
+
engin/__init__.py,sha256=yTc8k0HDGMIrxDdEEA90qGD_dExQjVIbXCyaOFRrnMg,508
|
2
|
+
engin/_assembler.py,sha256=VCZA_Gq4hnH5LueB_vEVqsKbGXx-nI6KQ65YhzXw-VY,7575
|
3
|
+
engin/_block.py,sha256=0QJtqyP5uTFjXsdVGr4ZONLI2LhfzUKmQGnNQWouB3o,2121
|
4
|
+
engin/_dependency.py,sha256=Nmyk4cGcK6ZZA8YWNZWgckLpDdKsRwOeRQhqRlxZmI0,5883
|
5
|
+
engin/_engin.py,sha256=i2IxMIz-3yKjedyg5L6gQEpdFDUZnCbXi9Pwhw8Hsxk,9133
|
6
|
+
engin/_exceptions.py,sha256=fsc4pTOIGHUh0x7oZhEXPJUTE268sIhswLoiqXaudiw,635
|
7
|
+
engin/_graph.py,sha256=1pMB0cr--uS0XJycDb1rS_X45RBpoyA6NkKqbeSuz1Q,1628
|
8
|
+
engin/_lifecycle.py,sha256=_jQnGFj4RYXsxMpcXPJQagFOwnoTVh7oSN8oUYoYuW0,3246
|
9
|
+
engin/_type_utils.py,sha256=C71kX2Dr-gluGSL018K4uihX3zkTe7QNWaHhFU10ZmA,2127
|
10
|
+
engin/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
11
|
+
engin/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
12
|
+
engin/ext/asgi.py,sha256=ViAWw-PNaP6y5fHK2rSGRfaxj7xxo217eqU2z2rhnq8,2200
|
13
|
+
engin/ext/fastapi.py,sha256=p9NekfRHbdNzK_1ttVNr500AO-IwLpBlG9Fhk6R0mmg,5649
|
14
|
+
engin/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
15
|
+
engin/scripts/graph.py,sha256=kmC_sSypGvIH89GddHz3KoRBEvooGNsGtrwLgw_uxNQ,4968
|
16
|
+
engin-0.0.9.dist-info/METADATA,sha256=fzCqyXhGBzC1hMcTtbRM9-oXVaHEP000abzPi9QZbFQ,2161
|
17
|
+
engin-0.0.9.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
18
|
+
engin-0.0.9.dist-info/entry_points.txt,sha256=Dehk4j5nK6zyuQtgOSRAoLE609V6eLzEp32bjqhO62Q,64
|
19
|
+
engin-0.0.9.dist-info/licenses/LICENSE,sha256=XHh5LPUPKZWTBqBv2xxN2RU7D59nHoiJGb5RIt8f45w,1070
|
20
|
+
engin-0.0.9.dist-info/RECORD,,
|
engin-0.0.8.dist-info/RECORD
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
engin/__init__.py,sha256=yTc8k0HDGMIrxDdEEA90qGD_dExQjVIbXCyaOFRrnMg,508
|
2
|
-
engin/_assembler.py,sha256=VCZA_Gq4hnH5LueB_vEVqsKbGXx-nI6KQ65YhzXw-VY,7575
|
3
|
-
engin/_block.py,sha256=-5qTp1Hdm3H54nScDGitFpcXRHLIyVHlDYATg_3dnPw,2045
|
4
|
-
engin/_dependency.py,sha256=WjJKJY2KhseYpqM1Gg-VhYls4dcLg1CgdlpniJEUHjI,5489
|
5
|
-
engin/_engin.py,sha256=i2IxMIz-3yKjedyg5L6gQEpdFDUZnCbXi9Pwhw8Hsxk,9133
|
6
|
-
engin/_exceptions.py,sha256=fsc4pTOIGHUh0x7oZhEXPJUTE268sIhswLoiqXaudiw,635
|
7
|
-
engin/_graph.py,sha256=piqcocrWt0mX_UAr0DVdkCtVYjkjVpkikLCdRVCoUnU,1230
|
8
|
-
engin/_lifecycle.py,sha256=_jQnGFj4RYXsxMpcXPJQagFOwnoTVh7oSN8oUYoYuW0,3246
|
9
|
-
engin/_type_utils.py,sha256=C71kX2Dr-gluGSL018K4uihX3zkTe7QNWaHhFU10ZmA,2127
|
10
|
-
engin/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
11
|
-
engin/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
12
|
-
engin/ext/asgi.py,sha256=6vuC4zIhsvAdmwRn2I6uuUWPYfqobox1dv7skg2OWwE,1940
|
13
|
-
engin/ext/fastapi.py,sha256=CH2Zi7Oh_Va0TJGx05e7_LqAiCsoI1qcu0Z59_rgfRk,899
|
14
|
-
engin/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
15
|
-
engin/scripts/graph.py,sha256=f_Yk4TRrw9N-gltdriiOviNGcowQckeM2R-7CPVXkHY,3424
|
16
|
-
engin-0.0.8.dist-info/METADATA,sha256=xX0-36ECkPFBcDsYjiY04-dhn-066fQhKKzEceUPkhQ,2161
|
17
|
-
engin-0.0.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
18
|
-
engin-0.0.8.dist-info/entry_points.txt,sha256=Dehk4j5nK6zyuQtgOSRAoLE609V6eLzEp32bjqhO62Q,64
|
19
|
-
engin-0.0.8.dist-info/licenses/LICENSE,sha256=XHh5LPUPKZWTBqBv2xxN2RU7D59nHoiJGb5RIt8f45w,1070
|
20
|
-
engin-0.0.8.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|