engin 0.1.0b5__py3-none-any.whl → 0.1.0rc2__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/_cli/_graph.py CHANGED
@@ -1,4 +1,5 @@
1
1
  import contextlib
2
+ import json
2
3
  import socketserver
3
4
  import threading
4
5
  from http.server import BaseHTTPRequestHandler
@@ -9,9 +10,10 @@ from typing import Annotated, Any
9
10
  import typer
10
11
  from rich import print
11
12
 
12
- from engin import Entrypoint, Invoke, TypeId
13
+ from engin import Engin, Entrypoint, Invoke, TypeId
13
14
  from engin._cli._common import COMMON_HELP, get_engin_instance
14
15
  from engin._dependency import Dependency, Provide, Supply
16
+ from engin._graph import Node
15
17
  from engin.extensions.asgi import ASGIEngin
16
18
 
17
19
  try:
@@ -28,12 +30,16 @@ _APP_ORIGIN = ""
28
30
  @cli.command(name="graph")
29
31
  def serve_graph(
30
32
  app: Annotated[
31
- str,
33
+ str | None,
32
34
  typer.Argument(help=COMMON_HELP["app"]),
33
- ],
35
+ ] = None,
34
36
  ) -> None:
35
37
  """
36
38
  Creates a visualisation of your application's dependencies.
39
+
40
+ Examples:
41
+
42
+ 1. `engin graph`
37
43
  """
38
44
  module_name, _, instance = get_engin_instance(app)
39
45
 
@@ -42,37 +48,11 @@ def serve_graph(
42
48
 
43
49
  nodes = instance.graph()
44
50
 
45
- # transform dependencies into mermaid syntax
46
- dependencies = [
47
- f"{_render_node(node.parent)} --> {_render_node(node.node)}"
48
- for node in nodes
49
- if node.parent is not None
50
- and not (node.node.block_name and node.node.block_name == node.parent.block_name)
51
- ]
51
+ # Generate JSON data for interactive graph
52
+ graph_data = _generate_graph_data(nodes, instance)
52
53
 
53
- blocks = {node.node.block_name for node in nodes if node.node.block_name is not None}
54
-
55
- # group blocks into subgraphs
56
- for block in blocks:
57
- dependencies.append(f"subgraph {block}")
58
- dependencies.extend(
59
- [
60
- f"{_render_node(node.parent, False)} --> {_render_node(node.node, False)}"
61
- for node in nodes
62
- if node.parent is not None
63
- and node.node.block_name == block
64
- and node.parent.block_name == block
65
- ]
66
- )
67
- dependencies.append("end")
68
-
69
- html = (
70
- _GRAPH_HTML.replace("%%DATA%%", "\n".join(dependencies))
71
- .replace(
72
- "%%LEGEND%%",
73
- ASGI_ENGIN_LEGEND if isinstance(instance, ASGIEngin) else DEFAULT_LEGEND,
74
- )
75
- .encode("utf8")
54
+ html = _GRAPH_HTML.replace("%%GRAPH_DATA%%", json.dumps(graph_data, indent=2)).encode(
55
+ "utf8"
76
56
  )
77
57
 
78
58
  class Handler(BaseHTTPRequestHandler):
@@ -104,47 +84,130 @@ def wait_for_interrupt() -> None:
104
84
  sleep(10000)
105
85
 
106
86
 
107
- _BLOCK_IDX: dict[str, int] = {}
108
- _SEEN_BLOCKS: list[str] = []
87
+ def _generate_graph_data(nodes: list[Node], instance: Engin) -> dict[str, Any]:
88
+ """Generate JSON data structure for interactive graph rendering."""
89
+ all_deps = set()
90
+ for node in nodes:
91
+ all_deps.add(node.node)
92
+ if node.parent:
93
+ all_deps.add(node.parent)
94
+
95
+ # Generate node data
96
+ node_data = []
97
+ for dep in all_deps:
98
+ node_info = _get_node_info(dep)
99
+ node_data.append(node_info)
100
+
101
+ # Generate edge data
102
+ edge_data = [
103
+ {
104
+ "from": f"n{id(node.parent)}",
105
+ "to": f"n{id(node.node)}",
106
+ "from_block": node.parent.block_name,
107
+ "to_block": node.node.block_name,
108
+ }
109
+ for node in nodes
110
+ if node.parent is not None
111
+ ]
109
112
 
113
+ # Get block information
114
+ blocks = list({node.node.block_name for node in nodes if node.node.block_name is not None})
110
115
 
111
- def _render_node(node: Dependency, render_block: bool = True) -> str:
112
- node_id = id(node)
113
- md = ""
114
- style = ""
116
+ # Generate legend
117
+ legend = ASGI_ENGIN_LEGEND if isinstance(instance, ASGIEngin) else DEFAULT_LEGEND
115
118
 
116
- # format block name
117
- if render_block and (n := node.block_name):
118
- md += f"_{n}_\n"
119
+ return {
120
+ "nodes": node_data,
121
+ "edges": edge_data,
122
+ "blocks": blocks,
123
+ "legend": legend,
124
+ "app_origin": _APP_ORIGIN,
125
+ }
119
126
 
120
- node_root_package = node.source_package.split(".", maxsplit=1)[0]
121
- if node_root_package != _APP_ORIGIN:
122
- if style:
123
- style += "E"
124
- else:
125
- style = "external"
126
127
 
127
- if style:
128
- style = f":::{style}"
128
+ def _get_node_info(node: Dependency) -> dict[str, Any]:
129
+ """Extract node information for JSON representation."""
130
+ node_id = f"n{id(node)}" # Add 'n' prefix to match mermaid node IDs
131
+ label = ""
132
+ style_classes = []
129
133
 
134
+ # Determine if external
135
+ node_root_package = node.source_package.split(".", maxsplit=1)[0]
136
+ is_external = node_root_package != _APP_ORIGIN
137
+ if is_external:
138
+ style_classes.append("external")
139
+
140
+ # Collect detailed information for tooltips
141
+ details: dict[str, Any] = {
142
+ "full_name": node.name,
143
+ "source_module": node.source_module,
144
+ "source_package": node.source_package,
145
+ "parameters": [],
146
+ "return_type": None,
147
+ "scope": None,
148
+ }
149
+
150
+ # Get parameter information
151
+ if hasattr(node, "parameter_type_ids"):
152
+ details["parameters"] = [str(param_id) for param_id in node.parameter_type_ids]
153
+
154
+ # Determine node type and extract specific details
130
155
  if isinstance(node, Supply):
131
- md += f"{_short_name(node.return_type_id)}"
132
- return f'{node_id}("`{md}`"){style}'
133
- if isinstance(node, Provide):
134
- md += f"{_short_name(node.return_type_id)}"
135
- return f'{node_id}["`{md}`"]{style}'
136
- if isinstance(node, Entrypoint):
156
+ node_type = "Supply"
157
+ label += f"{_short_name(node.return_type_id)}"
158
+ shape = "round"
159
+ details["return_type"] = str(node.return_type_id)
160
+ if hasattr(node, "_value"):
161
+ details["value_type"] = type(node._value).__name__
162
+ elif isinstance(node, Provide):
163
+ node_type = "Provide"
164
+ label += f"{_short_name(node.return_type_id)}"
165
+ shape = "rect"
166
+ details["return_type"] = str(node.return_type_id)
167
+ details["factory_function"] = node.func_name
168
+ if node.scope:
169
+ details["scope"] = node.scope
170
+ style_classes.append(f"scope-{node.scope}")
171
+ if node.is_multiprovider:
172
+ details["multiprovider"] = True
173
+ style_classes.append("multi")
174
+ elif isinstance(node, Entrypoint):
175
+ node_type = "Entrypoint"
137
176
  entrypoint_type = node.parameter_type_ids[0]
138
- md += f"{entrypoint_type}"
139
- return f'{node_id}[/"`{md}`"\\]{style}'
140
- if isinstance(node, Invoke):
141
- md += f"{node.func_name}"
142
- return f'{node_id}[/"`{md}`"/]{style}'
143
- if isinstance(node, APIRouteDependency):
144
- md += f"{node.name}"
145
- return f'{node_id}[["`{md}`"]]{style}'
177
+ label += f"{entrypoint_type}"
178
+ shape = "trapezoid"
179
+ details["entrypoint_type"] = str(entrypoint_type)
180
+ elif isinstance(node, Invoke):
181
+ node_type = "Invoke"
182
+ label += f"{node.func_name}"
183
+ shape = "trapezoid"
184
+ details["function"] = node.func_name
185
+ elif APIRouteDependency is not None and isinstance(node, APIRouteDependency):
186
+ node_type = "APIRoute"
187
+ label += f"{node.name}"
188
+ shape = "subroutine"
189
+ if hasattr(node, "route"):
190
+ details["methods"] = (
191
+ list(node.route.methods) if hasattr(node.route, "methods") else []
192
+ )
193
+ details["path"] = getattr(node.route, "path", "")
146
194
  else:
147
- return f'{node_id}["`{node.name}`"]{style}'
195
+ node_type = "Other"
196
+ label += f"{node.name}"
197
+ shape = "rect"
198
+
199
+ return {
200
+ "id": node_id,
201
+ "label": label,
202
+ "type": node_type,
203
+ "external": is_external,
204
+ "block": node.block_name,
205
+ "shape": shape,
206
+ "style_classes": style_classes,
207
+ "source_module": node.source_module,
208
+ "source_package": node.source_package,
209
+ "details": details,
210
+ }
148
211
 
149
212
 
150
213
  def _short_name(name: TypeId) -> str:
engin/_cli/_inspect.py CHANGED
@@ -11,7 +11,7 @@ from engin._cli._common import COMMON_HELP, get_engin_instance, print_error
11
11
  cli = typer.Typer()
12
12
  _CLI_HELP = {
13
13
  "type": "Filter providers by the provided type, e.g. `AsyncClient` or `float[]`",
14
- "module": "Filter providers by the provided types' module, e.g. `engin` or `httpx`",
14
+ "module": "Filter providers by the provided type's module, e.g. `engin` or `httpx`",
15
15
  "verbose": "Enables verbose output",
16
16
  }
17
17
 
@@ -19,9 +19,9 @@ _CLI_HELP = {
19
19
  @cli.command(name="inspect")
20
20
  def serve_graph(
21
21
  app: Annotated[
22
- str,
22
+ str | None,
23
23
  typer.Argument(help=COMMON_HELP["app"]),
24
- ],
24
+ ] = None,
25
25
  type_: Annotated[
26
26
  str | None,
27
27
  typer.Option("--type", help=_CLI_HELP["type"]),
@@ -39,9 +39,8 @@ def serve_graph(
39
39
 
40
40
  Examples:
41
41
 
42
- 1. `engin inspect examples.simple.main:engin --module httpx`
43
-
44
- 2. `engin inspect examples.simple.main:engin --type AsyncClient`
42
+ 1. `engin inspect --module httpx`
43
+ 2. `engin inspect --type AsyncClient`
45
44
  """
46
45
  module_name, _, instance = get_engin_instance(app)
47
46
 
@@ -75,8 +74,11 @@ def serve_graph(
75
74
  available = sorted(map(str, instance.assembler.providers))
76
75
  print_error(f"No matching providers, available: {available}")
77
76
 
78
- if matching_provider_count > 1:
79
- console.print(f"Found {matching_provider_count} matching providers", style="dim")
77
+ console.print(
78
+ f"Found {matching_provider_count} matching provider"
79
+ + ("s" if matching_provider_count > 1 else ""),
80
+ style="dim",
81
+ )
80
82
 
81
83
  table = Table(show_header=False, show_lines=False, box=box.ASCII)
82
84
 
engin/_engin.py CHANGED
@@ -61,8 +61,8 @@ class Engin:
61
61
  the Invoke options parameters are assembled.
62
62
  2. All Invocations are run sequentially in the order they were passed in to the Engin.
63
63
  3. Lifecycle Startup tasks registered by assembled dependencies are run sequentially.
64
- 4. The Engin waits for a stop signal, i.e. SIGINT or SIGTERM, or for something to
65
- set the ShutdownSwitch event.
64
+ 4. The Engin waits for a stop signal, i.e. SIGINT or SIGTERM, or a supervised task that
65
+ causes a shutdown.
66
66
  5. Lifecyce Shutdown tasks are run in the reverse order to the Startup order.
67
67
 
68
68
  Examples:
@@ -90,6 +90,7 @@ class Engin:
90
90
  """
91
91
 
92
92
  _LIB_OPTIONS: ClassVar[list[Option]] = [Provide(Lifecycle), Provide(Supervisor)]
93
+ _STOP_ON_SINGAL: ClassVar[bool] = True
93
94
 
94
95
  def __init__(self, *options: Option) -> None:
95
96
  """
@@ -172,7 +173,8 @@ class Engin:
172
173
  self._start_complete_event.set()
173
174
 
174
175
  async with create_task_group() as tg:
175
- tg.start_soon(_stop_engin_on_signal, self._stop_requested_event)
176
+ if self._STOP_ON_SINGAL:
177
+ tg.start_soon(_stop_engin_on_signal, self._stop_requested_event)
176
178
 
177
179
  try:
178
180
  async with supervisor:
engin/_supervisor.py CHANGED
@@ -58,19 +58,33 @@ class _SupervisorTask:
58
58
  await self.factory()
59
59
  self.complete = True
60
60
  return
61
- except get_cancelled_exc_class():
62
- LOG.info(f"{self.name} cancelled")
61
+ except get_cancelled_exc_class() as err:
62
+ LOG.debug(f"supervised task '{self.name}' was cancelled", exc_info=err)
63
63
  raise
64
64
  except Exception as err:
65
- LOG.error(f"Supervisor: {self.name} raised {type(err).__name__}", exc_info=err)
66
65
  self.last_exception = err
67
66
 
68
67
  if self.on_exception == OnException.IGNORE:
68
+ LOG.warning(
69
+ f"supervisor task '{self.name}' raised {type(err).__name__} "
70
+ "which will be ignored",
71
+ exc_info=err,
72
+ )
69
73
  self.complete = True
70
74
  return
71
75
  if self.on_exception == OnException.RETRY:
76
+ LOG.warning(
77
+ f"supervisor task '{self.name}' raised {type(err).__name__} "
78
+ "which will be retried",
79
+ exc_info=err,
80
+ )
72
81
  continue
73
82
  if self.on_exception == OnException.SHUTDOWN:
83
+ LOG.error(
84
+ f"supervisor task '{self.name}' raised {type(err).__name__}, "
85
+ "starting shutdown",
86
+ exc_info=err,
87
+ )
74
88
  self.complete = True
75
89
  raise get_cancelled_exc_class() from None
76
90
  assert_never(self.on_exception)
engin/exceptions.py CHANGED
@@ -1,6 +1,7 @@
1
1
  from typing import TYPE_CHECKING, Any
2
2
 
3
3
  from engin._dependency import Provide
4
+ from engin._type_utils import TypeId
4
5
 
5
6
  if TYPE_CHECKING:
6
7
  from engin._block import Block
@@ -12,12 +13,6 @@ class EnginError(Exception):
12
13
  """
13
14
 
14
15
 
15
- class AssemblerError(EnginError):
16
- """
17
- Base class for all custom exceptions raised by the Assembler.
18
- """
19
-
20
-
21
16
  class InvalidBlockError(EnginError):
22
17
  """
23
18
  Raised when an invalid block is instantiated.
@@ -32,6 +27,25 @@ class InvalidBlockError(EnginError):
32
27
  return self.message
33
28
 
34
29
 
30
+ class AssemblerError(EnginError):
31
+ """
32
+ Base class for all custom exceptions raised by the Assembler.
33
+ """
34
+
35
+
36
+ class TypeNotProvidedError(AssemblerError):
37
+ """
38
+ Raised when the Assembler cannot assemble a type due to a missing Provider.
39
+ """
40
+
41
+ def __init__(self, type_id: TypeId) -> None:
42
+ self.type_id = type_id
43
+ self.message = f"no provider found for '{type_id}'"
44
+
45
+ def __str__(self) -> str:
46
+ return self.message
47
+
48
+
35
49
  class ProviderError(AssemblerError):
36
50
  """
37
51
  Raised when a Provider errors during Assembly.
@@ -77,4 +91,5 @@ __all__ = [
77
91
  "InvalidBlockError",
78
92
  "NotInScopeError",
79
93
  "ProviderError",
94
+ "TypeNotProvidedError",
80
95
  ]
engin/extensions/asgi.py CHANGED
@@ -21,6 +21,8 @@ class ASGIType(Protocol):
21
21
 
22
22
 
23
23
  class ASGIEngin(Engin, ASGIType):
24
+ _STOP_ON_SINGAL = False # web server implementation is responsible for this
25
+
24
26
  _asgi_type: ClassVar[type[ASGIType]] = ASGIType # type: ignore[type-abstract]
25
27
  _asgi_app: ASGIType
26
28
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: engin
3
- Version: 0.1.0b5
3
+ Version: 0.1.0rc2
4
4
  Summary: An async-first modular application framework
5
5
  Project-URL: Homepage, https://github.com/invokermain/engin
6
6
  Project-URL: Documentation, https://engin.readthedocs.io/en/latest/
@@ -13,6 +13,7 @@ Requires-Python: >=3.10
13
13
  Requires-Dist: anyio>=4
14
14
  Requires-Dist: exceptiongroup>=1
15
15
  Provides-Extra: cli
16
+ Requires-Dist: tomli>=2.0; (python_version < '3.11') and extra == 'cli'
16
17
  Requires-Dist: typer>=0.15; extra == 'cli'
17
18
  Description-Content-Type: text/markdown
18
19
 
@@ -32,13 +33,13 @@ Engin is a lightweight application framework powered by dependency injection, it
32
33
  you build and maintain large monoliths and many microservices.
33
34
 
34
35
 
35
- ## Features
36
+ ## Feature
36
37
 
37
- The Engin framework includes:
38
+ The Engin framework gives you:
38
39
 
39
40
  - A fully-featured dependency injection system.
40
41
  - A robust application runtime with lifecycle hooks and supervised background tasks.
41
- - Zero boiler-plate code reuse across multiple applications.
42
+ - Zero boiler-plate code reuse across applications.
42
43
  - Integrations for other frameworks such as FastAPI.
43
44
  - Full async support.
44
45
  - CLI commands to aid local development.
@@ -46,7 +47,7 @@ The Engin framework includes:
46
47
 
47
48
  ## Installation
48
49
 
49
- Engin is available on PyPI, install using your favourite dependency manager:
50
+ Engin is available on PyPI, install it using your favourite dependency manager:
50
51
 
51
52
  - `pip install engin`
52
53
  - `poetry add engin`
@@ -54,13 +55,13 @@ Engin is available on PyPI, install using your favourite dependency manager:
54
55
 
55
56
  ## Example
56
57
 
57
- A small example which shows some of the runtime features of Engin. This application
58
- makes a http request and then performs a shutdown.
58
+ A small example which shows some of the features of Engin. This application
59
+ makes 3 http requests and shuts itself down.
59
60
 
60
61
  ```python
61
62
  import asyncio
62
63
  from httpx import AsyncClient
63
- from engin import Engin, Invoke, Lifecycle, Provide, Supervisor
64
+ from engin import Engin, Invoke, Lifecycle, OnException, Provide, Supervisor
64
65
 
65
66
 
66
67
  def httpx_client_factory(lifecycle: Lifecycle) -> AsyncClient:
@@ -75,13 +76,17 @@ async def main(
75
76
  httpx_client: AsyncClient,
76
77
  supervisor: Supervisor,
77
78
  ) -> None:
78
- async def http_request():
79
- await httpx_client.get("https://httpbin.org/get")
80
- # one we've made the http request shutdown the application
81
- raise asyncio.CancelledError("Forcing shutdown")
79
+ async def http_requests_task():
80
+ # simulate a background task
81
+ for x in range(3):
82
+ await httpx_client.get("https://httpbin.org/get")
83
+ await asyncio.sleep(1.0)
84
+ # raise an error to shutdown the application, normally you wouldn't do this!
85
+ raise RuntimeError("Forcing shutdown")
86
+
87
+ # supervise the http requests as part of the application's lifecycle
88
+ supervisor.supervise(http_requests_task, on_exception=OnException.SHUTDOWN)
82
89
 
83
- # supervise the http request as part of the application's lifecycle
84
- supervisor.supervise(http_request)
85
90
 
86
91
  # define our modular application
87
92
  engin = Engin(Provide(httpx_client_factory), Invoke(main))
@@ -96,6 +101,15 @@ With logs enabled this will output:
96
101
  INFO:engin:starting engin
97
102
  INFO:engin:startup complete
98
103
  INFO:httpx:HTTP Request: GET https://httpbin.org/get "HTTP/1.1 200 OK"
104
+ INFO:httpx:HTTP Request: GET https://httpbin.org/get "HTTP/1.1 200 OK"
105
+ INFO:httpx:HTTP Request: GET https://httpbin.org/get "HTTP/1.1 200 OK"
106
+ ERROR:engin:supervisor task 'http_requests_task' raised RuntimeError, starting shutdown
107
+ Traceback (most recent call last):
108
+ File "C:\dev\python\engin\src\engin\_supervisor.py", line 58, in __call__
109
+ await self.factory()
110
+ File "C:\dev\python\engin\readme_example.py", line 29, in http_requests_task
111
+ raise RuntimeError("Forcing shutdown")
112
+ RuntimeError: Forcing shutdown
99
113
  INFO:engin:stopping engin
100
114
  INFO:engin:shutdown complete
101
115
  ```
@@ -105,4 +119,4 @@ INFO:engin:shutdown complete
105
119
  Engin is heavily inspired by [Uber's Fx framework for Go](https://github.com/uber-go/fx)
106
120
  and the [Injector framework for Python](https://github.com/python-injector/injector).
107
121
 
108
- They are both great projects, check them out.
122
+ They are both great projects, go check them out.
@@ -0,0 +1,27 @@
1
+ engin/__init__.py,sha256=O0vS570kZFBq7Kwy4FgeJFIhfo4aIg5mv_Z_9vAQRio,577
2
+ engin/_assembler.py,sha256=0uXgtcO5M3EHg0I-TQK9y7LOzfxkLFmKia-zLyVHaxA,11178
3
+ engin/_block.py,sha256=IacP4PoJKRhSQCbQSdoyCtmu362a4vj6qoUQKyaJwzI,3062
4
+ engin/_dependency.py,sha256=xINk3sudxzsTmkUkNAKQwzBc0G0DfhpnrZli4z3ALBY,9459
5
+ engin/_engin.py,sha256=oGaf_iedMNKxl3rbADpPzIvNtTx1Pfs-6o0e8yRrmHk,9532
6
+ engin/_graph.py,sha256=y1g7Lm_Zy5GPEgRsggCKV5DDaDzcwUl8v3IZCK8jyGI,1631
7
+ engin/_introspect.py,sha256=VdREX6Lhhga5SnEP9G7mjHkgJR4mpqk_SMnmL2zTcqY,966
8
+ engin/_lifecycle.py,sha256=cSWe3euZkmpxmUPFvph2lsTtvuZbxttEfBL-RnOI7lo,5325
9
+ engin/_option.py,sha256=nZcdrehp1QwgxMUoIpsM0PJuu1q1pbXzhcVsetbsHpc,223
10
+ engin/_supervisor.py,sha256=HI0D4StqSJZE2l6x7RtouRLKWx1HOhUmLHqu8pUcWbQ,4343
11
+ engin/_type_utils.py,sha256=H3Tl-kJr2wY2RhaTXP9GrMqa2RsXMijHbjHKe1AxGmc,2276
12
+ engin/exceptions.py,sha256=lSMOJI4Yl-VIM0yDzFWbPhC0mQm4f0WvGULr9SldIaY,2353
13
+ engin/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ engin/_cli/__init__.py,sha256=Ixk3NoZeIN8Bj53I625uqJdLyyT9Gpbe_4GtNy-KQwM,636
15
+ engin/_cli/_check.py,sha256=YA37Gi4rimKIH-XMs7SEAFkSRNBIMG8OCKfF3W1V3-g,1976
16
+ engin/_cli/_common.py,sha256=6tyjxAkROCViw0LOFdx-X1U-iSXKyeW5CoE9UxWRybI,3282
17
+ engin/_cli/_graph.html,sha256=YIv34LR00aWsWgjNrqO4XBNu4frPo_y-i1CijaZyySo,29073
18
+ engin/_cli/_graph.py,sha256=MsxsNpL1v0v1AUT57ZS97l1diwacqRaPdVBObHHIGJE,6753
19
+ engin/_cli/_inspect.py,sha256=_uzldpHA51IX4srpUGzL4lZNiepqucsO9M3Zo83XBBM,3159
20
+ engin/extensions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
+ engin/extensions/asgi.py,sha256=7vQFaVs1jxq1KbhHGN8k7x2UFab6SPUq2_hXfX6HiXU,3329
22
+ engin/extensions/fastapi.py,sha256=7N6i-eZUEZRPo7kcvjS7kbRSY5QAPyKJXSeongSQ-OA,6371
23
+ engin-0.1.0rc2.dist-info/METADATA,sha256=I7BtKglKAs30NQ6n73p3gzLhZhxcvsIZXuuM356Ipa4,3951
24
+ engin-0.1.0rc2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
25
+ engin-0.1.0rc2.dist-info/entry_points.txt,sha256=sW247zZUMxm0b5UKYvPuqQQljYDtU-j2zK3cu7gHwM0,41
26
+ engin-0.1.0rc2.dist-info/licenses/LICENSE,sha256=XHh5LPUPKZWTBqBv2xxN2RU7D59nHoiJGb5RIt8f45w,1070
27
+ engin-0.1.0rc2.dist-info/RECORD,,
@@ -1,26 +0,0 @@
1
- engin/__init__.py,sha256=O0vS570kZFBq7Kwy4FgeJFIhfo4aIg5mv_Z_9vAQRio,577
2
- engin/_assembler.py,sha256=MC14BRsgabGlq9weyv2VXylH4RE282uNTyNH5rN8Lqc,11359
3
- engin/_block.py,sha256=IacP4PoJKRhSQCbQSdoyCtmu362a4vj6qoUQKyaJwzI,3062
4
- engin/_dependency.py,sha256=xINk3sudxzsTmkUkNAKQwzBc0G0DfhpnrZli4z3ALBY,9459
5
- engin/_engin.py,sha256=36trLrhXfKXf1t237qTZb_B3cnE3YvlzHA1c2JQfzN8,9453
6
- engin/_graph.py,sha256=y1g7Lm_Zy5GPEgRsggCKV5DDaDzcwUl8v3IZCK8jyGI,1631
7
- engin/_introspect.py,sha256=VdREX6Lhhga5SnEP9G7mjHkgJR4mpqk_SMnmL2zTcqY,966
8
- engin/_lifecycle.py,sha256=cSWe3euZkmpxmUPFvph2lsTtvuZbxttEfBL-RnOI7lo,5325
9
- engin/_option.py,sha256=nZcdrehp1QwgxMUoIpsM0PJuu1q1pbXzhcVsetbsHpc,223
10
- engin/_supervisor.py,sha256=37h036bPe7ew88WjpIOmhZwCvOdLVcvalyJgbWZr1vU,3716
11
- engin/_type_utils.py,sha256=H3Tl-kJr2wY2RhaTXP9GrMqa2RsXMijHbjHKe1AxGmc,2276
12
- engin/exceptions.py,sha256=-VPwPReZb9YEIkrWMR9TW2K5HEwmHHgEO7QWH6wfV8c,1946
13
- engin/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
- engin/_cli/__init__.py,sha256=koD5WTkZXb8QQIiVU5bJiSR1wwPGb5rv2iwd-v-BA7A,564
15
- engin/_cli/_common.py,sha256=zMYb1Bs1yUuR3qf3r6WuVozYzDwHJvTVthVbTQfTF9w,1261
16
- engin/_cli/_graph.html,sha256=rR5dnDKoz7KtSff0ERCi2UKuoH_Z03MRYiXI_W03G5k,2430
17
- engin/_cli/_graph.py,sha256=HMC91nWvTOr6_czPBNx1RU55Ib3qesJRCmbnL2DsdDk,4659
18
- engin/_cli/_inspect.py,sha256=0jm25d4wcbXVNJkyaeECSKY-irsxd-EIYBH1GDW_Yjc,3163
19
- engin/extensions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
- engin/extensions/asgi.py,sha256=d5Z6gtMVWDZdAlvrTaMt987sKyiq__A0X4gJQ7IETmA,3247
21
- engin/extensions/fastapi.py,sha256=7N6i-eZUEZRPo7kcvjS7kbRSY5QAPyKJXSeongSQ-OA,6371
22
- engin-0.1.0b5.dist-info/METADATA,sha256=k-K33xQpJTOGemxOVp0K8MlwKHFArl7LpyBdNKrWeS4,3201
23
- engin-0.1.0b5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
24
- engin-0.1.0b5.dist-info/entry_points.txt,sha256=sW247zZUMxm0b5UKYvPuqQQljYDtU-j2zK3cu7gHwM0,41
25
- engin-0.1.0b5.dist-info/licenses/LICENSE,sha256=XHh5LPUPKZWTBqBv2xxN2RU7D59nHoiJGb5RIt8f45w,1070
26
- engin-0.1.0b5.dist-info/RECORD,,