wireup 0.16.0__tar.gz → 1.0.1__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.
- wireup-1.0.1/PKG-INFO +272 -0
- {wireup-0.16.0 → wireup-1.0.1}/pyproject.toml +6 -7
- wireup-1.0.1/readme.md +234 -0
- wireup-1.0.1/wireup/__init__.py +25 -0
- wireup-1.0.1/wireup/_annotations.py +133 -0
- wireup-1.0.1/wireup/_async_to_sync.py +34 -0
- wireup-1.0.1/wireup/_decorators.py +95 -0
- wireup-1.0.1/wireup/_discovery.py +68 -0
- {wireup-0.16.0 → wireup-1.0.1}/wireup/errors.py +12 -19
- wireup-1.0.1/wireup/integration/django/__init__.py +3 -0
- wireup-1.0.1/wireup/integration/django/apps.py +164 -0
- wireup-1.0.1/wireup/integration/fastapi.py +141 -0
- wireup-1.0.1/wireup/integration/flask.py +49 -0
- wireup-1.0.1/wireup/integration/util.py +7 -0
- {wireup-0.16.0 → wireup-1.0.1}/wireup/ioc/_exit_stack.py +13 -11
- wireup-1.0.1/wireup/ioc/container/__init__.py +110 -0
- wireup-1.0.1/wireup/ioc/container/async_container.py +55 -0
- wireup-1.0.1/wireup/ioc/container/base_container.py +281 -0
- wireup-1.0.1/wireup/ioc/container/sync_container.py +33 -0
- {wireup-0.16.0 → wireup-1.0.1}/wireup/ioc/override_manager.py +2 -2
- wireup-1.0.1/wireup/ioc/parameter.py +60 -0
- wireup-1.0.1/wireup/ioc/service_registry.py +168 -0
- {wireup-0.16.0 → wireup-1.0.1}/wireup/ioc/types.py +25 -19
- {wireup-0.16.0 → wireup-1.0.1}/wireup/ioc/util.py +11 -41
- wireup-1.0.1/wireup/ioc/validation.py +126 -0
- wireup-0.16.0/PKG-INFO +0 -242
- wireup-0.16.0/readme.md +0 -204
- wireup-0.16.0/wireup/__init__.py +0 -53
- wireup-0.16.0/wireup/annotation/__init__.py +0 -189
- wireup-0.16.0/wireup/integration/django/__init__.py +0 -46
- wireup-0.16.0/wireup/integration/django/apps.py +0 -98
- wireup-0.16.0/wireup/integration/fastapi.py +0 -58
- wireup-0.16.0/wireup/integration/fastapi_integration.py +0 -45
- wireup-0.16.0/wireup/integration/flask.py +0 -28
- wireup-0.16.0/wireup/integration/flask_integration.py +0 -49
- wireup-0.16.0/wireup/integration/util.py +0 -17
- wireup-0.16.0/wireup/ioc/base_container.py +0 -96
- wireup-0.16.0/wireup/ioc/dependency_container.py +0 -429
- wireup-0.16.0/wireup/ioc/initialization_context.py +0 -75
- wireup-0.16.0/wireup/ioc/parameter.py +0 -105
- wireup-0.16.0/wireup/ioc/service_registry.py +0 -235
- wireup-0.16.0/wireup/util.py +0 -181
- {wireup-0.16.0 → wireup-1.0.1}/wireup/integration/__init__.py +0 -0
- {wireup-0.16.0 → wireup-1.0.1}/wireup/ioc/__init__.py +0 -0
- {wireup-0.16.0 → wireup-1.0.1}/wireup/py.typed +0 -0
wireup-1.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: wireup
|
|
3
|
+
Version: 1.0.1
|
|
4
|
+
Summary: Python Dependency Injection Library
|
|
5
|
+
Home-page: https://github.com/maldoinc/wireup
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: flask,django,injector,dependency injection,dependency injection container,dependency injector
|
|
8
|
+
Author: Aldo Mateli
|
|
9
|
+
Author-email: aldo.mateli@gmail.com
|
|
10
|
+
Requires-Python: >=3.8,<4.0
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Environment :: Web Environment
|
|
14
|
+
Classifier: Framework :: Django
|
|
15
|
+
Classifier: Framework :: FastAPI
|
|
16
|
+
Classifier: Framework :: Flask
|
|
17
|
+
Classifier: Framework :: aiohttp
|
|
18
|
+
Classifier: Intended Audience :: Developers
|
|
19
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
20
|
+
Classifier: Operating System :: OS Independent
|
|
21
|
+
Classifier: Programming Language :: Python
|
|
22
|
+
Classifier: Programming Language :: Python :: 3
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
26
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
27
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
28
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
29
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
30
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
31
|
+
Classifier: Typing :: Typed
|
|
32
|
+
Requires-Dist: typing_extensions (>=4.7,<5.0)
|
|
33
|
+
Project-URL: Changelog, https://github.com/maldoinc/wireup/releases
|
|
34
|
+
Project-URL: Documentation, https://maldoinc.github.io/wireup/
|
|
35
|
+
Project-URL: Repository, https://github.com/maldoinc/wireup
|
|
36
|
+
Description-Content-Type: text/markdown
|
|
37
|
+
|
|
38
|
+
<div align="center">
|
|
39
|
+
<h1>Wireup</h1>
|
|
40
|
+
<p>Performant, concise and type-safe Dependency Injection for Python 3.8+</p>
|
|
41
|
+
|
|
42
|
+
[](https://github.com/maldoinc/wireup)
|
|
43
|
+
[](https://github.com/maldoinc/wireup)
|
|
44
|
+
[](https://codeclimate.com/github/maldoinc/wireup)
|
|
45
|
+
[](https://pypi.org/project/wireup/)
|
|
46
|
+
[](https://pypi.org/project/wireup/)
|
|
47
|
+
|
|
48
|
+
<p><a target="_blank" href="https://maldoinc.github.io/wireup">📚 Documentation</a> | <a target="_blank" href="https://github.com/maldoinc/wireup-demo">🎮 Demo Application</a></p>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
> [!NOTE]
|
|
53
|
+
> Wireup 1.0 has been released, featuring support for scoped lifetimes, a simplified API, enhanced type safety, and improved documentation.
|
|
54
|
+
> Refer to the [Upgrading Guide](https://maldoinc.github.io/wireup/latest/upgrading/) for instructions on upgrading from version 0.x to 1.0.
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
Dependency Injection (DI) is a design pattern where dependencies are provided externally rather than created within objects. Wireup automates dependency management using Python's type system, with support for async, generators and modern Python features.
|
|
58
|
+
|
|
59
|
+
## Features
|
|
60
|
+
|
|
61
|
+
### ✨ Simple & Type-Safe DI
|
|
62
|
+
|
|
63
|
+
Inject services and configuration using a clean and intuitive syntax.
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
@service
|
|
67
|
+
class Database:
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
@service
|
|
71
|
+
class UserService:
|
|
72
|
+
def __init__(self, db: Database) -> None:
|
|
73
|
+
self.db = db
|
|
74
|
+
|
|
75
|
+
container = wireup.create_sync_container(services=[Database, UserService])
|
|
76
|
+
user_service = container.get(UserService) # ✅ Dependencies resolved.
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
<details>
|
|
80
|
+
<summary>Example With Configuration</summary>
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
@service
|
|
84
|
+
class Database:
|
|
85
|
+
def __init__(self, db_url: Annotated[str, Inject(param="db_url")]) -> None:
|
|
86
|
+
self.db_url = db_url
|
|
87
|
+
|
|
88
|
+
container = wireup.create_sync_container(
|
|
89
|
+
services=[Database],
|
|
90
|
+
parameters={"db_url": os.environ["APP_DB_URL"]}
|
|
91
|
+
)
|
|
92
|
+
database = container.get(Database) # ✅ Dependencies resolved.
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
</details>
|
|
96
|
+
|
|
97
|
+
### 🎯 Function Injection
|
|
98
|
+
|
|
99
|
+
Inject dependencies directly into functions with a simple decorator.
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
@inject_from_container(container)
|
|
103
|
+
def process_users(service: Injected[UserService]):
|
|
104
|
+
# ✅ UserService injected.
|
|
105
|
+
pass
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### 📝 Interfaces & Abstract Classes
|
|
109
|
+
|
|
110
|
+
Define abstract types and have the container automatically inject the implementation.
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
@abstract
|
|
114
|
+
class Notifier(abc.ABC):
|
|
115
|
+
pass
|
|
116
|
+
|
|
117
|
+
@service
|
|
118
|
+
class SlackNotifier(Notifier):
|
|
119
|
+
pass
|
|
120
|
+
|
|
121
|
+
notifier = container.get(Notifier)
|
|
122
|
+
# ✅ SlackNotifier instance.
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
### 🔄 Managed Service Lifetimes
|
|
127
|
+
|
|
128
|
+
Declare dependencies as singletons, scoped, or transient to control whether to inject a fresh copy or reuse existing instances.
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
# Singleton: One instance per application. `@service(lifetime="singleton")` is the default.
|
|
133
|
+
@service
|
|
134
|
+
class Database:
|
|
135
|
+
pass
|
|
136
|
+
|
|
137
|
+
# Scoped: One instance per scope/request, shared within that scope/request.
|
|
138
|
+
@service(lifetime="scoped")
|
|
139
|
+
class RequestContext:
|
|
140
|
+
def __init__(self) -> None:
|
|
141
|
+
self.request_id = uuid4()
|
|
142
|
+
|
|
143
|
+
# Transient: When full isolation and clean state is required.
|
|
144
|
+
# Every request to create transient services results in a new instance.
|
|
145
|
+
@service(lifetime="transient")
|
|
146
|
+
class OrderProcessor:
|
|
147
|
+
pass
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
### 🏭 Flexible Creation Patterns
|
|
152
|
+
|
|
153
|
+
Defer instantiation to specialized factories when complex initialization or cleanup is required.
|
|
154
|
+
Full support for async and generators. Wireup handles cleanup at the correct time depending on the service lifetime.
|
|
155
|
+
|
|
156
|
+
**Synchronous**
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
class WeatherClient:
|
|
160
|
+
def __init__(self, client: requests.Session) -> None:
|
|
161
|
+
self.client = client
|
|
162
|
+
|
|
163
|
+
@service
|
|
164
|
+
def weather_client_factory() -> Iterator[WeatherClient]:
|
|
165
|
+
with requests.Session() as session:
|
|
166
|
+
yield WeatherClient(client=session)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**Async**
|
|
170
|
+
|
|
171
|
+
```python
|
|
172
|
+
class WeatherClient:
|
|
173
|
+
def __init__(self, client: aiohttp.ClientSession) -> None:
|
|
174
|
+
self.client = client
|
|
175
|
+
|
|
176
|
+
@service
|
|
177
|
+
async def weather_client_factory() -> AsyncIterator[WeatherClient]:
|
|
178
|
+
async with aiohttp.ClientSession() as session:
|
|
179
|
+
yield WeatherClient(client=session)
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
### 🛡️ Improved Safety
|
|
184
|
+
|
|
185
|
+
Wireup is mypy strict compliant and will not introduce type errors in your code. It will also warn you at the earliest possible stage about configuration errors to avoid surprises.
|
|
186
|
+
|
|
187
|
+
**Container Creation**
|
|
188
|
+
|
|
189
|
+
The container will raise errors at creation time about missing dependencies or other issues.
|
|
190
|
+
|
|
191
|
+
```python
|
|
192
|
+
@service
|
|
193
|
+
class Foo:
|
|
194
|
+
def __init__(self, unknown: NotManagedByWireup) -> None:
|
|
195
|
+
pass
|
|
196
|
+
|
|
197
|
+
container = wireup.create_sync_container(services=[Foo])
|
|
198
|
+
# ❌ Parameter 'unknown' of 'Foo' depends on an unknown service 'NotManagedByWireup'.
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**Function Injection**
|
|
202
|
+
|
|
203
|
+
Injected functions will raise errors at module import time rather than when called.
|
|
204
|
+
|
|
205
|
+
```python
|
|
206
|
+
@inject_from_container(container)
|
|
207
|
+
def my_function(oops: Injected[NotManagedByWireup]):
|
|
208
|
+
pass
|
|
209
|
+
|
|
210
|
+
# ❌ Parameter 'oops' of 'my_function' depends on an unknown service 'NotManagedByWireup'.
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
**Integrations**
|
|
214
|
+
|
|
215
|
+
Wireup integrations assert that requested injections in the framework are valid.
|
|
216
|
+
|
|
217
|
+
```python
|
|
218
|
+
@app.get("/")
|
|
219
|
+
def home(foo: Injected[NotManagedByWireup]):
|
|
220
|
+
pass
|
|
221
|
+
|
|
222
|
+
wireup.integration.flask.setup(container, app)
|
|
223
|
+
# ❌ Parameter 'foo' of 'home' depends on an unknown service 'NotManagedByWireup'.
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### 📍 Framework-Agnostic
|
|
227
|
+
|
|
228
|
+
Wireup provides its own Dependency Injection mechanism and is not tied to specific frameworks. Use it anywhere you like.
|
|
229
|
+
|
|
230
|
+
### 🔗 Share Services Between Application and CLI
|
|
231
|
+
|
|
232
|
+
Share the service layer between your web application and its accompanying CLI using Wireup.
|
|
233
|
+
|
|
234
|
+
### 🔌 Native Integration with Django, FastAPI, or Flask
|
|
235
|
+
|
|
236
|
+
Integrate with popular frameworks for a smoother developer experience.
|
|
237
|
+
Integrations manage request scopes, injection in endpoints, and lifecycle of services.
|
|
238
|
+
|
|
239
|
+
```python
|
|
240
|
+
app = FastAPI()
|
|
241
|
+
container = wireup.create_async_container(services=[UserService, Database])
|
|
242
|
+
|
|
243
|
+
@app.get("/")
|
|
244
|
+
def users_list(user_service: Injected[UserService]):
|
|
245
|
+
pass
|
|
246
|
+
|
|
247
|
+
wireup.integration.fastapi.setup(container, app)
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### 🧪 Simplified Testing
|
|
251
|
+
|
|
252
|
+
Wireup does not patch your services and lets you test them in isolation.
|
|
253
|
+
|
|
254
|
+
If you need to use the container in your tests, you can have it create parts of your services
|
|
255
|
+
or perform dependency substitution.
|
|
256
|
+
|
|
257
|
+
```python
|
|
258
|
+
with container.override.service(target=Database, new=in_memory_database):
|
|
259
|
+
# The /users endpoint depends on Database.
|
|
260
|
+
# During the lifetime of this context manager, requests to inject `Database`
|
|
261
|
+
# will result in `in_memory_database` being injected instead.
|
|
262
|
+
response = client.get("/users")
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
## 📚 Documentation
|
|
266
|
+
|
|
267
|
+
For more information [check out the documentation](https://maldoinc.github.io/wireup)
|
|
268
|
+
|
|
269
|
+
## 🎮 Demo application
|
|
270
|
+
|
|
271
|
+
A demo flask application is available at [maldoinc/wireup-demo](https://github.com/maldoinc/wireup-demo)
|
|
272
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "wireup"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "1.0.1"
|
|
4
4
|
description = "Python Dependency Injection Library"
|
|
5
5
|
authors = ["Aldo Mateli <aldo.mateli@gmail.com>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -45,10 +45,10 @@ Changelog = "https://github.com/maldoinc/wireup/releases"
|
|
|
45
45
|
|
|
46
46
|
[tool.poetry.dependencies]
|
|
47
47
|
python = "^3.8"
|
|
48
|
-
|
|
48
|
+
typing_extensions = "^4.7"
|
|
49
49
|
|
|
50
50
|
[tool.poetry.group.dev.dependencies]
|
|
51
|
-
ruff = "0.
|
|
51
|
+
ruff = "0.11.0"
|
|
52
52
|
setuptools = "^68.0.0"
|
|
53
53
|
mkdocs = "^1.5.2"
|
|
54
54
|
mkdocs-material = "^9.4.14"
|
|
@@ -74,14 +74,12 @@ lint.ignore = [
|
|
|
74
74
|
"PT009", # Prefer using assertEqual instead of plain asserts
|
|
75
75
|
"TD003", # Exclude "missing issue link for todos",
|
|
76
76
|
"FIX002", # Disable "Line contains to do, consider resolving the issue". It will be done, in due time.
|
|
77
|
-
"
|
|
78
|
-
"D101",
|
|
79
|
-
"D102",
|
|
80
|
-
"D104", # Same as above, disable missing docstring in public package,
|
|
77
|
+
"D",
|
|
81
78
|
"D203", # Ignore "one blank line before class". Using "no blank lines before class rule".
|
|
82
79
|
"D213", # Disable "Summary must go into next line"
|
|
83
80
|
"D107", # Disable required docs for __init. Can be redundant if class also has them.
|
|
84
81
|
"A003", # Disable "shadows builtin". OverrideManager.set was flagged by this
|
|
82
|
+
"SLF001", # Allow usage of _fields.
|
|
85
83
|
"FA100", # Don't recommend __future__ annotations.
|
|
86
84
|
# Disable as they may cause conflicts with ruff formatter
|
|
87
85
|
"COM812",
|
|
@@ -103,6 +101,7 @@ lint.ignore = [
|
|
|
103
101
|
"SIM117",
|
|
104
102
|
"S101",
|
|
105
103
|
"PLR2004",
|
|
104
|
+
"PLR0913",
|
|
106
105
|
]
|
|
107
106
|
|
|
108
107
|
|
wireup-1.0.1/readme.md
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<h1>Wireup</h1>
|
|
3
|
+
<p>Performant, concise and type-safe Dependency Injection for Python 3.8+</p>
|
|
4
|
+
|
|
5
|
+
[](https://github.com/maldoinc/wireup)
|
|
6
|
+
[](https://github.com/maldoinc/wireup)
|
|
7
|
+
[](https://codeclimate.com/github/maldoinc/wireup)
|
|
8
|
+
[](https://pypi.org/project/wireup/)
|
|
9
|
+
[](https://pypi.org/project/wireup/)
|
|
10
|
+
|
|
11
|
+
<p><a target="_blank" href="https://maldoinc.github.io/wireup">📚 Documentation</a> | <a target="_blank" href="https://github.com/maldoinc/wireup-demo">🎮 Demo Application</a></p>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
> [!NOTE]
|
|
16
|
+
> Wireup 1.0 has been released, featuring support for scoped lifetimes, a simplified API, enhanced type safety, and improved documentation.
|
|
17
|
+
> Refer to the [Upgrading Guide](https://maldoinc.github.io/wireup/latest/upgrading/) for instructions on upgrading from version 0.x to 1.0.
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
Dependency Injection (DI) is a design pattern where dependencies are provided externally rather than created within objects. Wireup automates dependency management using Python's type system, with support for async, generators and modern Python features.
|
|
21
|
+
|
|
22
|
+
## Features
|
|
23
|
+
|
|
24
|
+
### ✨ Simple & Type-Safe DI
|
|
25
|
+
|
|
26
|
+
Inject services and configuration using a clean and intuitive syntax.
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
@service
|
|
30
|
+
class Database:
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
@service
|
|
34
|
+
class UserService:
|
|
35
|
+
def __init__(self, db: Database) -> None:
|
|
36
|
+
self.db = db
|
|
37
|
+
|
|
38
|
+
container = wireup.create_sync_container(services=[Database, UserService])
|
|
39
|
+
user_service = container.get(UserService) # ✅ Dependencies resolved.
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
<details>
|
|
43
|
+
<summary>Example With Configuration</summary>
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
@service
|
|
47
|
+
class Database:
|
|
48
|
+
def __init__(self, db_url: Annotated[str, Inject(param="db_url")]) -> None:
|
|
49
|
+
self.db_url = db_url
|
|
50
|
+
|
|
51
|
+
container = wireup.create_sync_container(
|
|
52
|
+
services=[Database],
|
|
53
|
+
parameters={"db_url": os.environ["APP_DB_URL"]}
|
|
54
|
+
)
|
|
55
|
+
database = container.get(Database) # ✅ Dependencies resolved.
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
</details>
|
|
59
|
+
|
|
60
|
+
### 🎯 Function Injection
|
|
61
|
+
|
|
62
|
+
Inject dependencies directly into functions with a simple decorator.
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
@inject_from_container(container)
|
|
66
|
+
def process_users(service: Injected[UserService]):
|
|
67
|
+
# ✅ UserService injected.
|
|
68
|
+
pass
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### 📝 Interfaces & Abstract Classes
|
|
72
|
+
|
|
73
|
+
Define abstract types and have the container automatically inject the implementation.
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
@abstract
|
|
77
|
+
class Notifier(abc.ABC):
|
|
78
|
+
pass
|
|
79
|
+
|
|
80
|
+
@service
|
|
81
|
+
class SlackNotifier(Notifier):
|
|
82
|
+
pass
|
|
83
|
+
|
|
84
|
+
notifier = container.get(Notifier)
|
|
85
|
+
# ✅ SlackNotifier instance.
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
### 🔄 Managed Service Lifetimes
|
|
90
|
+
|
|
91
|
+
Declare dependencies as singletons, scoped, or transient to control whether to inject a fresh copy or reuse existing instances.
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
# Singleton: One instance per application. `@service(lifetime="singleton")` is the default.
|
|
96
|
+
@service
|
|
97
|
+
class Database:
|
|
98
|
+
pass
|
|
99
|
+
|
|
100
|
+
# Scoped: One instance per scope/request, shared within that scope/request.
|
|
101
|
+
@service(lifetime="scoped")
|
|
102
|
+
class RequestContext:
|
|
103
|
+
def __init__(self) -> None:
|
|
104
|
+
self.request_id = uuid4()
|
|
105
|
+
|
|
106
|
+
# Transient: When full isolation and clean state is required.
|
|
107
|
+
# Every request to create transient services results in a new instance.
|
|
108
|
+
@service(lifetime="transient")
|
|
109
|
+
class OrderProcessor:
|
|
110
|
+
pass
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
### 🏭 Flexible Creation Patterns
|
|
115
|
+
|
|
116
|
+
Defer instantiation to specialized factories when complex initialization or cleanup is required.
|
|
117
|
+
Full support for async and generators. Wireup handles cleanup at the correct time depending on the service lifetime.
|
|
118
|
+
|
|
119
|
+
**Synchronous**
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
class WeatherClient:
|
|
123
|
+
def __init__(self, client: requests.Session) -> None:
|
|
124
|
+
self.client = client
|
|
125
|
+
|
|
126
|
+
@service
|
|
127
|
+
def weather_client_factory() -> Iterator[WeatherClient]:
|
|
128
|
+
with requests.Session() as session:
|
|
129
|
+
yield WeatherClient(client=session)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Async**
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
class WeatherClient:
|
|
136
|
+
def __init__(self, client: aiohttp.ClientSession) -> None:
|
|
137
|
+
self.client = client
|
|
138
|
+
|
|
139
|
+
@service
|
|
140
|
+
async def weather_client_factory() -> AsyncIterator[WeatherClient]:
|
|
141
|
+
async with aiohttp.ClientSession() as session:
|
|
142
|
+
yield WeatherClient(client=session)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
### 🛡️ Improved Safety
|
|
147
|
+
|
|
148
|
+
Wireup is mypy strict compliant and will not introduce type errors in your code. It will also warn you at the earliest possible stage about configuration errors to avoid surprises.
|
|
149
|
+
|
|
150
|
+
**Container Creation**
|
|
151
|
+
|
|
152
|
+
The container will raise errors at creation time about missing dependencies or other issues.
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
@service
|
|
156
|
+
class Foo:
|
|
157
|
+
def __init__(self, unknown: NotManagedByWireup) -> None:
|
|
158
|
+
pass
|
|
159
|
+
|
|
160
|
+
container = wireup.create_sync_container(services=[Foo])
|
|
161
|
+
# ❌ Parameter 'unknown' of 'Foo' depends on an unknown service 'NotManagedByWireup'.
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**Function Injection**
|
|
165
|
+
|
|
166
|
+
Injected functions will raise errors at module import time rather than when called.
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
@inject_from_container(container)
|
|
170
|
+
def my_function(oops: Injected[NotManagedByWireup]):
|
|
171
|
+
pass
|
|
172
|
+
|
|
173
|
+
# ❌ Parameter 'oops' of 'my_function' depends on an unknown service 'NotManagedByWireup'.
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**Integrations**
|
|
177
|
+
|
|
178
|
+
Wireup integrations assert that requested injections in the framework are valid.
|
|
179
|
+
|
|
180
|
+
```python
|
|
181
|
+
@app.get("/")
|
|
182
|
+
def home(foo: Injected[NotManagedByWireup]):
|
|
183
|
+
pass
|
|
184
|
+
|
|
185
|
+
wireup.integration.flask.setup(container, app)
|
|
186
|
+
# ❌ Parameter 'foo' of 'home' depends on an unknown service 'NotManagedByWireup'.
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### 📍 Framework-Agnostic
|
|
190
|
+
|
|
191
|
+
Wireup provides its own Dependency Injection mechanism and is not tied to specific frameworks. Use it anywhere you like.
|
|
192
|
+
|
|
193
|
+
### 🔗 Share Services Between Application and CLI
|
|
194
|
+
|
|
195
|
+
Share the service layer between your web application and its accompanying CLI using Wireup.
|
|
196
|
+
|
|
197
|
+
### 🔌 Native Integration with Django, FastAPI, or Flask
|
|
198
|
+
|
|
199
|
+
Integrate with popular frameworks for a smoother developer experience.
|
|
200
|
+
Integrations manage request scopes, injection in endpoints, and lifecycle of services.
|
|
201
|
+
|
|
202
|
+
```python
|
|
203
|
+
app = FastAPI()
|
|
204
|
+
container = wireup.create_async_container(services=[UserService, Database])
|
|
205
|
+
|
|
206
|
+
@app.get("/")
|
|
207
|
+
def users_list(user_service: Injected[UserService]):
|
|
208
|
+
pass
|
|
209
|
+
|
|
210
|
+
wireup.integration.fastapi.setup(container, app)
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### 🧪 Simplified Testing
|
|
214
|
+
|
|
215
|
+
Wireup does not patch your services and lets you test them in isolation.
|
|
216
|
+
|
|
217
|
+
If you need to use the container in your tests, you can have it create parts of your services
|
|
218
|
+
or perform dependency substitution.
|
|
219
|
+
|
|
220
|
+
```python
|
|
221
|
+
with container.override.service(target=Database, new=in_memory_database):
|
|
222
|
+
# The /users endpoint depends on Database.
|
|
223
|
+
# During the lifetime of this context manager, requests to inject `Database`
|
|
224
|
+
# will result in `in_memory_database` being injected instead.
|
|
225
|
+
response = client.get("/users")
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## 📚 Documentation
|
|
229
|
+
|
|
230
|
+
For more information [check out the documentation](https://maldoinc.github.io/wireup)
|
|
231
|
+
|
|
232
|
+
## 🎮 Demo application
|
|
233
|
+
|
|
234
|
+
A demo flask application is available at [maldoinc/wireup-demo](https://github.com/maldoinc/wireup-demo)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from wireup._annotations import Inject, Injected, abstract, service
|
|
2
|
+
from wireup._decorators import inject_from_container
|
|
3
|
+
from wireup.ioc.container import (
|
|
4
|
+
create_async_container,
|
|
5
|
+
create_sync_container,
|
|
6
|
+
)
|
|
7
|
+
from wireup.ioc.container.async_container import AsyncContainer
|
|
8
|
+
from wireup.ioc.container.sync_container import SyncContainer
|
|
9
|
+
from wireup.ioc.parameter import ParameterBag
|
|
10
|
+
from wireup.ioc.types import ParameterReference, ServiceOverride
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"AsyncContainer",
|
|
14
|
+
"Inject",
|
|
15
|
+
"Injected",
|
|
16
|
+
"ParameterBag",
|
|
17
|
+
"ParameterReference",
|
|
18
|
+
"ServiceOverride",
|
|
19
|
+
"SyncContainer",
|
|
20
|
+
"abstract",
|
|
21
|
+
"create_async_container",
|
|
22
|
+
"create_sync_container",
|
|
23
|
+
"inject_from_container",
|
|
24
|
+
"service",
|
|
25
|
+
]
|