engin 0.0.13__tar.gz → 0.0.15__tar.gz
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-0.0.13 → engin-0.0.15}/.readthedocs.yaml +1 -1
- {engin-0.0.13 → engin-0.0.15}/CHANGELOG.md +38 -1
- {engin-0.0.13 → engin-0.0.15}/PKG-INFO +3 -1
- {engin-0.0.13 → engin-0.0.15}/docs/concepts/engin.md +1 -1
- {engin-0.0.13 → engin-0.0.15}/docs/concepts/invocations.md +5 -8
- engin-0.0.15/docs/concepts/lifecycle.md +100 -0
- {engin-0.0.13 → engin-0.0.15}/docs/concepts/providers.md +36 -23
- {engin-0.0.13 → engin-0.0.15}/docs/getting-started.md +1 -1
- {engin-0.0.13 → engin-0.0.15}/docs/guides/fastapi.md +1 -1
- engin-0.0.15/docs/js/readthedocs.js +7 -0
- {engin-0.0.13 → engin-0.0.15}/mkdocs.yaml +0 -1
- {engin-0.0.13 → engin-0.0.15}/pyproject.toml +16 -12
- {engin-0.0.13 → engin-0.0.15}/src/engin/__init__.py +4 -1
- {engin-0.0.13 → engin-0.0.15}/src/engin/_assembler.py +63 -55
- engin-0.0.15/src/engin/_block.py +89 -0
- engin-0.0.15/src/engin/_cli/__init__.py +13 -0
- engin-0.0.13/src/engin/scripts/graph.py → engin-0.0.15/src/engin/_cli/_graph.py +48 -31
- engin-0.0.15/src/engin/_cli/_utils.py +18 -0
- {engin-0.0.13 → engin-0.0.15}/src/engin/_dependency.py +81 -36
- {engin-0.0.13 → engin-0.0.15}/src/engin/_engin.py +21 -59
- engin-0.0.15/src/engin/_introspect.py +34 -0
- {engin-0.0.13 → engin-0.0.15}/src/engin/_lifecycle.py +68 -2
- engin-0.0.15/src/engin/_option.py +10 -0
- {engin-0.0.13 → engin-0.0.15}/src/engin/_type_utils.py +8 -10
- {engin-0.0.13 → engin-0.0.15}/src/engin/ext/asgi.py +2 -1
- {engin-0.0.13 → engin-0.0.15}/src/engin/ext/fastapi.py +12 -5
- engin-0.0.15/tests/cli/test_graph.py +39 -0
- {engin-0.0.13 → engin-0.0.15}/tests/test_assembler.py +49 -11
- engin-0.0.15/tests/test_block.py +25 -0
- {engin-0.0.13 → engin-0.0.15}/tests/test_dependencies.py +38 -0
- engin-0.0.15/tests/test_graph.py +33 -0
- engin-0.0.15/tests/test_lifecycle.py +82 -0
- engin-0.0.15/tests/test_utils.py +81 -0
- {engin-0.0.13 → engin-0.0.15}/uv.lock +202 -121
- engin-0.0.13/docs/concepts/lifecycle.md +0 -0
- engin-0.0.13/docs/guides/dependency_injection.md +0 -4
- engin-0.0.13/docs/js/readthedocs.js +0 -32
- engin-0.0.13/src/engin/_block.py +0 -70
- engin-0.0.13/tests/test_modules.py +0 -19
- engin-0.0.13/tests/test_utils.py +0 -69
- {engin-0.0.13 → engin-0.0.15}/.github/workflows/check.yaml +0 -0
- {engin-0.0.13 → engin-0.0.15}/.github/workflows/publish.yaml +0 -0
- {engin-0.0.13 → engin-0.0.15}/.gitignore +0 -0
- {engin-0.0.13 → engin-0.0.15}/LICENSE +0 -0
- {engin-0.0.13 → engin-0.0.15}/README.md +0 -0
- {engin-0.0.13 → engin-0.0.15}/docs/guides/fastapi-graph.png +0 -0
- {engin-0.0.13 → engin-0.0.15}/docs/index.md +0 -0
- {engin-0.0.13 → engin-0.0.15}/docs/overrides/main.html +0 -0
- {engin-0.0.13 → engin-0.0.15}/docs/reference.md +0 -0
- {engin-0.0.13 → engin-0.0.15}/examples/__init__.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/examples/asgi/__init__.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/examples/asgi/app.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/examples/asgi/common/__init__.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/examples/asgi/common/db/__init__.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/examples/asgi/common/db/adapaters/__init__.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/examples/asgi/common/db/adapaters/memory.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/examples/asgi/common/db/block.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/examples/asgi/common/db/ports.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/examples/asgi/common/starlette/__init__.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/examples/asgi/common/starlette/endpoint.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/examples/asgi/features/__init__.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/examples/asgi/features/cats/__init__.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/examples/asgi/features/cats/api/__init__.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/examples/asgi/features/cats/api/get.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/examples/asgi/features/cats/api/post.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/examples/asgi/features/cats/block.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/examples/asgi/features/cats/domain.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/examples/asgi/main.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/examples/fastapi/__init__.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/examples/fastapi/app.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/examples/fastapi/main.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/examples/fastapi/routes/__init__.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/examples/fastapi/routes/cats/__init__.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/examples/fastapi/routes/cats/adapters/__init__.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/examples/fastapi/routes/cats/adapters/repository.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/examples/fastapi/routes/cats/api.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/examples/fastapi/routes/cats/block.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/examples/fastapi/routes/cats/domain.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/examples/fastapi/routes/cats/ports.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/examples/simple/__init__.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/examples/simple/main.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/src/engin/_exceptions.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/src/engin/_graph.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/src/engin/ext/__init__.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/src/engin/py.typed +0 -0
- {engin-0.0.13/src/engin/scripts → engin-0.0.15/tests}/__init__.py +0 -0
- {engin-0.0.13/tests → engin-0.0.15/tests/acceptance}/__init__.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/tests/acceptance/test_error_in_shutdown.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/tests/acceptance/test_error_in_start_up.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/tests/acceptance/test_fastapi.py +0 -0
- {engin-0.0.13/tests/acceptance → engin-0.0.15/tests/cli}/__init__.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/tests/conftest.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/tests/deps.py +0 -0
- {engin-0.0.13 → engin-0.0.15}/tests/test_engin.py +0 -0
@@ -6,7 +6,44 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
7
|
|
8
8
|
|
9
|
-
## [0.0.
|
9
|
+
## [0.0.15] - 2025-03-25
|
10
|
+
|
11
|
+
### Changed
|
12
|
+
|
13
|
+
- `Provide` & `Supply` will now raise an error if overriding an existing provider from the
|
14
|
+
same package. This is to prevent accidental overrides. Users can explicitly allow
|
15
|
+
overrides by specifying the `override` parameter when defining the provider
|
16
|
+
`Provide(..., override=True)` or `@provide(override=True)`.
|
17
|
+
- Lifecycle startup tasks will now timeout after 15 seconds and raise an error.
|
18
|
+
- Assembler's `get` method has been renamed to `build`.
|
19
|
+
- Supply's `type_hint` parameter has been renamed to `as_type`.
|
20
|
+
|
21
|
+
### Fixed
|
22
|
+
|
23
|
+
- `Assembler` would occasionally fail to call all multiproviders due to inconsistent
|
24
|
+
ordering.
|
25
|
+
|
26
|
+
|
27
|
+
## [0.0.14] - 2025-03-23
|
28
|
+
|
29
|
+
### Added
|
30
|
+
|
31
|
+
- `LifecycleHook` class to help build simple lifecycles with a start and stop call.
|
32
|
+
|
33
|
+
### Changed
|
34
|
+
|
35
|
+
- `engin-graph` has been replaced by `engin graph`.
|
36
|
+
- Engin now uses `typer` for an improved cli experience. Note the package now has an extra `cli` which must be installed to use the cli.
|
37
|
+
- `Assembler.add(...)` does not error when adding already registered providers.
|
38
|
+
- Use a more performant algorithm for inspecting frame stack.
|
39
|
+
|
40
|
+
### Fixed
|
41
|
+
|
42
|
+
- `ASGIEngin` now properly surfaces startup errors.
|
43
|
+
- `Engin.run()` doing a double shutdown.
|
44
|
+
|
45
|
+
|
46
|
+
## [0.0.13] - 2025-03-22
|
10
47
|
|
11
48
|
### Changed
|
12
49
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: engin
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.15
|
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/
|
@@ -10,6 +10,8 @@ License-Expression: MIT
|
|
10
10
|
License-File: LICENSE
|
11
11
|
Keywords: Application Framework,Dependency Injection
|
12
12
|
Requires-Python: >=3.10
|
13
|
+
Provides-Extra: cli
|
14
|
+
Requires-Dist: typer>=0.15; extra == 'cli'
|
13
15
|
Description-Content-Type: text/markdown
|
14
16
|
|
15
17
|
[](https://codecov.io/gh/invokermain/engin)
|
@@ -9,4 +9,4 @@ When ran the Engin takes care of the complete application lifecycle:
|
|
9
9
|
2. All Invocations are run sequentially in the order they were passed in to the Engin.
|
10
10
|
3. Any Lifecycle Startup defined by Provider that were assembled is ran.
|
11
11
|
4. The Engin waits for a stop signal, i.e. SIGINT or SIGTERM.
|
12
|
-
5. Any Lifecyce Shutdown
|
12
|
+
5. Any Lifecyce Shutdown tasks are run, in reverse order to the Startup order.
|
@@ -23,15 +23,12 @@ imports for brevity):
|
|
23
23
|
|
24
24
|
```python
|
25
25
|
def worker_factory(lifecycle: Lifecycle) -> Worker:
|
26
|
-
|
26
|
+
worker = Worker()
|
27
27
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
worker.shutdown()
|
33
|
-
|
34
|
-
lifecycle.append(worker_lifecycle)
|
28
|
+
lifecycle.hook(
|
29
|
+
on_start=worker.start,
|
30
|
+
on_stop=worker.shutdown
|
31
|
+
)
|
35
32
|
|
36
33
|
return worker
|
37
34
|
```
|
@@ -0,0 +1,100 @@
|
|
1
|
+
from contextlib import asynccontextmanager
|
2
|
+
|
3
|
+
# Lifecycle
|
4
|
+
|
5
|
+
Certain types of object naturally have some form of startup and shutdown behaviour
|
6
|
+
associated with them, these steps need to be tied the lifecycle of the application itself
|
7
|
+
in order to be useful. For example, a database connection manager might want to fill its
|
8
|
+
connection pool on startup, and gracefully release the connections on shutdown.
|
9
|
+
|
10
|
+
Doing this yourself can be tricky and is application dependent: most will not have any
|
11
|
+
special support for this and will expect you to manage your lifecycle concerns in your
|
12
|
+
entrypoint function, leading to unwieldy code in larger applications, whilst other
|
13
|
+
types application might expected you to translate the lifecyle tasks into something they
|
14
|
+
offer, e.g. an ASGI server would expect you to manage this via its lifespan. In both cases
|
15
|
+
you end up managing lifecycle in a completely different place to where you declare your
|
16
|
+
objects, which make the codebase more complicated to understand.
|
17
|
+
|
18
|
+
Luckily, engin makes declaring lifecycle tasks a breeze, and it can be done in the same
|
19
|
+
provider that build your object keeping your code nicely collocated.
|
20
|
+
|
21
|
+
## The Lifecycle type
|
22
|
+
|
23
|
+
Engin automatically provides a special type called `Lifecycle` that can be used like any
|
24
|
+
other provided type. This type allows you to register lifecycle tasks with the Engin which
|
25
|
+
will automatically be run as part of your application lifecycle.
|
26
|
+
|
27
|
+
## Registering lifecycle tasks
|
28
|
+
|
29
|
+
There are a few different ways to declare and register your lifecycle tasks, they all do
|
30
|
+
the same thing, so which one to use depends on whichever is easiest for your specific
|
31
|
+
lifecycle tasks.
|
32
|
+
|
33
|
+
### 1. Existing context manager
|
34
|
+
|
35
|
+
If your type exposes a context manager interface to handle its lifecycle, registering it
|
36
|
+
is as easy as calling `lifecycle.append(...)`, this works for sync and async context
|
37
|
+
managers.
|
38
|
+
|
39
|
+
Let's look at an example using `httpx.AsyncClient`:
|
40
|
+
|
41
|
+
```python
|
42
|
+
from engin import Lifecycle
|
43
|
+
from httpx import AsyncClient
|
44
|
+
|
45
|
+
|
46
|
+
def httpx_client(lifecycle: Lifecycle) -> AsyncClient:
|
47
|
+
client = AsyncClient()
|
48
|
+
lifecycle.append(client) # register the lifecycle tasks
|
49
|
+
return client
|
50
|
+
```
|
51
|
+
|
52
|
+
### 2. Explict startup & shutdown methods
|
53
|
+
|
54
|
+
If your type exposes meathods that must be called as part of the lifecycle, e.g. `start()`
|
55
|
+
& `stop()`, then `lifecycle.hook(on_start=..., on_stop=...)` is the way.
|
56
|
+
|
57
|
+
Let's look at an example using `piccolo.engine.PostgresEngin`:
|
58
|
+
|
59
|
+
```python
|
60
|
+
from engin import Lifecycle
|
61
|
+
from piccolo.engine import PostgresEngine
|
62
|
+
|
63
|
+
def postgres_engine(lifecyle: Lifecycle) -> PostgresEngine:
|
64
|
+
db_engine = PostgresEngine(...) # fill in actual connection details
|
65
|
+
|
66
|
+
lifecyle.hook(
|
67
|
+
on_start=db_engine.start_connection_pool(),
|
68
|
+
on_stop=db_engine.close_connection_pool(),
|
69
|
+
)
|
70
|
+
|
71
|
+
return db_engine
|
72
|
+
```
|
73
|
+
|
74
|
+
### 3. Custom context managers
|
75
|
+
|
76
|
+
For more advanced use cases you can always define your own context manager.
|
77
|
+
|
78
|
+
In this example assume that `worker.run()` will not return to us when we await it, and
|
79
|
+
therefore we want to manage it as a task.
|
80
|
+
|
81
|
+
|
82
|
+
```python
|
83
|
+
import asyncio
|
84
|
+
from engin import Lifecycle
|
85
|
+
from some_package import BlockingAsyncWorker
|
86
|
+
|
87
|
+
def blocking_worker(lifecycle: Lifecycle) -> BlockingWorker:
|
88
|
+
worker = BlockingAsyncWorker()
|
89
|
+
|
90
|
+
@asynccontextmanager
|
91
|
+
async def worker_lifecycle() -> AsyncIterator[None]:
|
92
|
+
task = asyncio.create_task(worker.run())
|
93
|
+
yield None
|
94
|
+
worker.stop()
|
95
|
+
del task
|
96
|
+
|
97
|
+
lifecycle.append(worker_lifecycle())
|
98
|
+
|
99
|
+
return worker
|
100
|
+
```
|
@@ -17,17 +17,19 @@ class: `Provide`.
|
|
17
17
|
```python
|
18
18
|
from engin import Engin, Provide
|
19
19
|
|
20
|
+
|
20
21
|
# define our constructor
|
21
22
|
def string_factory() -> str:
|
22
|
-
|
23
|
+
return "hello"
|
24
|
+
|
23
25
|
|
24
26
|
# register it as a provider with the Engin
|
25
27
|
engin = Engin(Provide(string_factory))
|
26
28
|
|
27
29
|
# construct the string
|
28
|
-
a_string = await engin.assembler.
|
30
|
+
a_string = await engin.assembler.build(str)
|
29
31
|
|
30
|
-
print(a_string)
|
32
|
+
print(a_string) # hello
|
31
33
|
```
|
32
34
|
|
33
35
|
Providers can be asynchronous as well, this factory function would work exactly the same
|
@@ -45,27 +47,31 @@ Providers that construct more interesting objects generally require their own pa
|
|
45
47
|
```python
|
46
48
|
from engin import Engin, Provide
|
47
49
|
|
50
|
+
|
48
51
|
class Greeter:
|
49
52
|
def __init__(self, greeting: str) -> None:
|
50
53
|
self._greeting = greeting
|
51
|
-
|
54
|
+
|
52
55
|
def greet(self, name: str) -> None:
|
53
56
|
print(f"{self._greeting}, {name}!")
|
54
|
-
|
57
|
+
|
58
|
+
|
55
59
|
# define our constructors
|
56
60
|
def string_factory() -> str:
|
57
|
-
|
61
|
+
return "hello"
|
62
|
+
|
58
63
|
|
59
64
|
def greeter_factory(greeting: str) -> Greeter:
|
60
65
|
return Greeter(greeting=greeting)
|
61
66
|
|
67
|
+
|
62
68
|
# register them as providers with the Engin
|
63
69
|
engin = Engin(Provide(string_factory), Provide(greeter_factory))
|
64
70
|
|
65
71
|
# construct the Greeter
|
66
|
-
greeter = await engin.assembler.
|
72
|
+
greeter = await engin.assembler.build(Greeter)
|
67
73
|
|
68
|
-
greeter.greet("Bob")
|
74
|
+
greeter.greet("Bob") # hello, Bob!
|
69
75
|
```
|
70
76
|
|
71
77
|
|
@@ -81,19 +87,21 @@ from engin import Engin, Provide
|
|
81
87
|
|
82
88
|
# define our constructors
|
83
89
|
def string_factory() -> str:
|
84
|
-
|
90
|
+
return "hello"
|
91
|
+
|
85
92
|
|
86
93
|
def evil_factory() -> int:
|
87
94
|
raise RuntimeError("I have ruined your plans")
|
88
95
|
|
96
|
+
|
89
97
|
# register them as providers with the Engin
|
90
98
|
engin = Engin(Provide(string_factory), Provide(evil_factory))
|
91
99
|
|
92
100
|
# this will not raise an error
|
93
|
-
await engin.assembler.
|
101
|
+
await engin.assembler.build(str)
|
94
102
|
|
95
103
|
# this will raise an error
|
96
|
-
await engin.assembler.
|
104
|
+
await engin.assembler.build(int)
|
97
105
|
```
|
98
106
|
|
99
107
|
|
@@ -109,20 +117,23 @@ To turn a factory into a multiprovider, simply return a list:
|
|
109
117
|
```python
|
110
118
|
from engin import Engin, Provide
|
111
119
|
|
120
|
+
|
112
121
|
# define our constructors
|
113
122
|
def animal_names_factory() -> list[str]:
|
114
|
-
|
123
|
+
return ["cat", "dog"]
|
124
|
+
|
115
125
|
|
116
126
|
def other_animal_names_factory() -> list[str]:
|
117
|
-
|
127
|
+
return ["horse", "cow"]
|
128
|
+
|
118
129
|
|
119
130
|
# register them as providers with the Engin
|
120
131
|
engin = Engin(Provide(animal_names_factory), Provide(other_animal_names_factory))
|
121
132
|
|
122
133
|
# construct the list of strings
|
123
|
-
animal_names = await engin.assembler.
|
134
|
+
animal_names = await engin.assembler.build(list[str])
|
124
135
|
|
125
|
-
print(animal_names)
|
136
|
+
print(animal_names) # ["cat", "dog", "horse", "cow"]
|
126
137
|
```
|
127
138
|
|
128
139
|
|
@@ -133,25 +144,28 @@ Providers of the same type can be discriminated using annotations.
|
|
133
144
|
```python
|
134
145
|
from engin import Engin, Provide
|
135
146
|
from typing import Annotated
|
136
|
-
|
147
|
+
|
148
|
+
|
137
149
|
# define our constructors
|
138
150
|
def greeting_factory() -> Annotated[str, "greeting"]:
|
139
|
-
|
151
|
+
return "hello"
|
152
|
+
|
140
153
|
|
141
154
|
def name_factory() -> Annotated[str, "name"]:
|
142
155
|
return "Jelena"
|
143
156
|
|
157
|
+
|
144
158
|
# register them as providers with the Engin
|
145
159
|
engin = Engin(Provide(greeting_factory), Provide(name_factory))
|
146
160
|
|
147
161
|
# this will return "hello"
|
148
|
-
await engin.assembler.
|
162
|
+
await engin.assembler.build(Annotated[str, "greeting"])
|
149
163
|
|
150
164
|
# this will return "Jelena"
|
151
|
-
await engin.assembler.
|
165
|
+
await engin.assembler.build(Annotated[str, "name"])
|
152
166
|
|
153
167
|
# N.B. this will raise an error!
|
154
|
-
await engin.assembler.
|
168
|
+
await engin.assembler.build(str)
|
155
169
|
```
|
156
170
|
|
157
171
|
|
@@ -162,7 +176,6 @@ provided type is automatically inferred.
|
|
162
176
|
|
163
177
|
For example the first example on this page could be rewritten as:
|
164
178
|
|
165
|
-
|
166
179
|
```python
|
167
180
|
from engin import Engin, Supply
|
168
181
|
|
@@ -170,7 +183,7 @@ from engin import Engin, Supply
|
|
170
183
|
engin = Engin(Supply("hello"))
|
171
184
|
|
172
185
|
# construct the string
|
173
|
-
a_string = await engin.assembler.
|
186
|
+
a_string = await engin.assembler.build(str)
|
174
187
|
|
175
|
-
print(a_string)
|
188
|
+
print(a_string) # hello
|
176
189
|
```
|
@@ -159,7 +159,7 @@ app = FastAPIEngin(AppBlock())
|
|
159
159
|
|
160
160
|
## Graphing Dependencies
|
161
161
|
|
162
|
-
Engin provides dependency visualisation functionality via the `engin
|
162
|
+
Engin provides dependency visualisation functionality via the `engin graph` script. When
|
163
163
|
working with a FastAPI application this can be used to visualise API Routes along with
|
164
164
|
their respective dependencies.
|
165
165
|
|
@@ -0,0 +1,7 @@
|
|
1
|
+
document.addEventListener("DOMContentLoaded", function(event) {
|
2
|
+
// Trigger Read the Docs' search addon instead of Material MkDocs default
|
3
|
+
document.querySelector(".md-search__input").addEventListener("focus", (e) => {
|
4
|
+
const event = new CustomEvent("readthedocs-search-show");
|
5
|
+
document.dispatchEvent(event);
|
6
|
+
});
|
7
|
+
});
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[project]
|
2
2
|
name = "engin"
|
3
|
-
version = "0.0.
|
3
|
+
version = "0.0.15"
|
4
4
|
description = "An async-first modular application framework"
|
5
5
|
readme = "README.md"
|
6
6
|
requires-python = ">=3.10"
|
@@ -8,19 +8,21 @@ license = "MIT"
|
|
8
8
|
keywords = ["Dependency Injection", "Application Framework"]
|
9
9
|
dependencies = []
|
10
10
|
|
11
|
+
[project.optional-dependencies]
|
12
|
+
cli = ["typer>=0.15"]
|
13
|
+
|
14
|
+
[project.scripts]
|
15
|
+
engin = "engin._cli:app"
|
16
|
+
|
11
17
|
[project.urls]
|
12
18
|
Homepage = "https://github.com/invokermain/engin"
|
13
19
|
Documentation = "https://engin.readthedocs.io/en/latest/"
|
14
20
|
Repository = "https://github.com/invokermain/engin.git"
|
15
21
|
Changelog = "https://github.com/invokermain/engin/blob/main/CHANGELOG.md"
|
16
22
|
|
17
|
-
[build-system]
|
18
|
-
requires = ["hatchling"]
|
19
|
-
build-backend = "hatchling.build"
|
20
|
-
|
21
23
|
|
22
|
-
[
|
23
|
-
dev
|
24
|
+
[dependency-groups]
|
25
|
+
dev = [
|
24
26
|
"fastapi>=0.115.6",
|
25
27
|
"httpx>=0.27.2",
|
26
28
|
"mypy>=1",
|
@@ -33,18 +35,20 @@ dev-dependencies = [
|
|
33
35
|
"starlette>=0.39.2",
|
34
36
|
"uvicorn>=0.31.1",
|
35
37
|
"pytest-cov>=6.0.0",
|
38
|
+
"typer>=0.15.2",
|
39
|
+
"pytest-mock>=3.14.0",
|
36
40
|
]
|
37
|
-
|
38
|
-
|
39
|
-
[dependency-groups]
|
40
41
|
docs = [
|
41
42
|
"mkdocs-material>=9.5.50",
|
42
43
|
"mkdocstrings[python]>=0.27.0",
|
43
44
|
]
|
44
45
|
|
46
|
+
[tool.uv]
|
47
|
+
default-groups = ["dev", "docs"]
|
45
48
|
|
46
|
-
[
|
47
|
-
|
49
|
+
[build-system]
|
50
|
+
requires = ["hatchling"]
|
51
|
+
build-backend = "hatchling.build"
|
48
52
|
|
49
53
|
|
50
54
|
[tool.ruff]
|
@@ -2,9 +2,11 @@ from engin import ext
|
|
2
2
|
from engin._assembler import Assembler
|
3
3
|
from engin._block import Block, invoke, provide
|
4
4
|
from engin._dependency import Entrypoint, Invoke, Provide, Supply
|
5
|
-
from engin._engin import Engin
|
5
|
+
from engin._engin import Engin
|
6
6
|
from engin._exceptions import ProviderError
|
7
7
|
from engin._lifecycle import Lifecycle
|
8
|
+
from engin._option import Option
|
9
|
+
from engin._type_utils import TypeId
|
8
10
|
|
9
11
|
__all__ = [
|
10
12
|
"Assembler",
|
@@ -17,6 +19,7 @@ __all__ = [
|
|
17
19
|
"Provide",
|
18
20
|
"ProviderError",
|
19
21
|
"Supply",
|
22
|
+
"TypeId",
|
20
23
|
"ext",
|
21
24
|
"invoke",
|
22
25
|
"provide",
|