anydi 0.55.0__tar.gz → 0.56.0__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.
- anydi-0.56.0/PKG-INFO +267 -0
- anydi-0.56.0/README.md +232 -0
- {anydi-0.55.0 → anydi-0.56.0}/anydi/_container.py +14 -9
- {anydi-0.55.0 → anydi-0.56.0}/anydi/_provider.py +1 -0
- {anydi-0.55.0 → anydi-0.56.0}/anydi/_resolver.py +236 -125
- anydi-0.56.0/anydi/testing.py +46 -0
- {anydi-0.55.0 → anydi-0.56.0}/pyproject.toml +2 -2
- anydi-0.55.0/PKG-INFO +0 -193
- anydi-0.55.0/README.md +0 -158
- anydi-0.55.0/anydi/testing.py +0 -125
- {anydi-0.55.0 → anydi-0.56.0}/anydi/__init__.py +0 -0
- {anydi-0.55.0 → anydi-0.56.0}/anydi/_async_lock.py +0 -0
- {anydi-0.55.0 → anydi-0.56.0}/anydi/_context.py +0 -0
- {anydi-0.55.0 → anydi-0.56.0}/anydi/_decorators.py +0 -0
- {anydi-0.55.0 → anydi-0.56.0}/anydi/_module.py +0 -0
- {anydi-0.55.0 → anydi-0.56.0}/anydi/_scanner.py +0 -0
- {anydi-0.55.0 → anydi-0.56.0}/anydi/_types.py +0 -0
- {anydi-0.55.0 → anydi-0.56.0}/anydi/ext/__init__.py +0 -0
- {anydi-0.55.0 → anydi-0.56.0}/anydi/ext/django/__init__.py +0 -0
- {anydi-0.55.0 → anydi-0.56.0}/anydi/ext/fastapi.py +0 -0
- {anydi-0.55.0 → anydi-0.56.0}/anydi/ext/faststream.py +0 -0
- {anydi-0.55.0 → anydi-0.56.0}/anydi/ext/pydantic_settings.py +0 -0
- {anydi-0.55.0 → anydi-0.56.0}/anydi/ext/pytest_plugin.py +0 -0
- {anydi-0.55.0 → anydi-0.56.0}/anydi/ext/starlette/__init__.py +0 -0
- {anydi-0.55.0 → anydi-0.56.0}/anydi/ext/starlette/middleware.py +0 -0
- {anydi-0.55.0 → anydi-0.56.0}/anydi/py.typed +0 -0
anydi-0.56.0/PKG-INFO
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: anydi
|
|
3
|
+
Version: 0.56.0
|
|
4
|
+
Summary: Dependency Injection library
|
|
5
|
+
Keywords: dependency injection,dependencies,di,async,asyncio,application
|
|
6
|
+
Author: Anton Ruhlov
|
|
7
|
+
Author-email: Anton Ruhlov <antonruhlov@gmail.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
Classifier: Intended Audience :: Information Technology
|
|
10
|
+
Classifier: Intended Audience :: System Administrators
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Classifier: Topic :: Internet
|
|
14
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
17
|
+
Classifier: Topic :: Software Development
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Classifier: Environment :: Web Environment
|
|
20
|
+
Classifier: Intended Audience :: Developers
|
|
21
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
22
|
+
Classifier: Programming Language :: Python :: 3
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
26
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
27
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
28
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
29
|
+
Requires-Dist: typing-extensions>=4.15.0,<5
|
|
30
|
+
Requires-Dist: anyio>=3.7.1
|
|
31
|
+
Requires-Dist: wrapt>=1.17.0
|
|
32
|
+
Requires-Python: >=3.10.0, <3.15
|
|
33
|
+
Project-URL: Repository, https://github.com/antonrh/anydi
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
|
|
36
|
+
# AnyDI
|
|
37
|
+
|
|
38
|
+
<div style="text-align: center;">
|
|
39
|
+
|
|
40
|
+
Modern, lightweight Dependency Injection library using type annotations.
|
|
41
|
+
|
|
42
|
+
[](https://github.com/antonrh/anydi/actions/workflows/ci.yml)
|
|
43
|
+
[](https://codecov.io/gh/antonrh/anydi)
|
|
44
|
+
[](https://anydi.readthedocs.io/en/latest/)
|
|
45
|
+
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
Documentation
|
|
50
|
+
|
|
51
|
+
http://anydi.readthedocs.io/
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
`AnyDI` is a modern, lightweight Dependency Injection library suitable for any synchronous or asynchronous applications with Python 3.10+, based on type annotations ([PEP 484](https://peps.python.org/pep-0484/)).
|
|
56
|
+
|
|
57
|
+
The key features are:
|
|
58
|
+
|
|
59
|
+
* **Type-safe**: Dependency resolution is driven by type hints.
|
|
60
|
+
* **Async-ready**: Works the same for sync and async providers or injections.
|
|
61
|
+
* **Scoped**: Built-in singleton, transient, and request lifetimes.
|
|
62
|
+
* **Simple**: Small surface area keeps boilerplate low.
|
|
63
|
+
* **Fast**: Resolver still adds only microseconds of overhead.
|
|
64
|
+
* **Named**: `Annotated[...]` makes multiple bindings per type simple.
|
|
65
|
+
* **Managed**: Providers can open/close resources via context managers.
|
|
66
|
+
* **Modular**: Compose containers or modules for large apps.
|
|
67
|
+
* **Scanning**: Auto-discovers injectable callables.
|
|
68
|
+
* **Integrated**: Extensions for popular frameworks.
|
|
69
|
+
* **Testable**: Override providers directly in tests.
|
|
70
|
+
|
|
71
|
+
## Installation
|
|
72
|
+
|
|
73
|
+
```shell
|
|
74
|
+
pip install anydi
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Comprehensive Example
|
|
78
|
+
|
|
79
|
+
### Define a Service (`app/services.py`)
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
class GreetingService:
|
|
83
|
+
def greet(self, name: str) -> str:
|
|
84
|
+
return f"Hello, {name}!"
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Create the Container and Providers (`app/container.py`)
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from anydi import Container
|
|
91
|
+
|
|
92
|
+
from app.services import GreetingService
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
container = Container()
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@container.provider(scope="singleton")
|
|
99
|
+
def service() -> GreetingService:
|
|
100
|
+
return GreetingService()
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Resolve Dependencies Directly
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
from app.container import container
|
|
107
|
+
from app.services import GreetingService
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
service = container.resolve(GreetingService)
|
|
111
|
+
|
|
112
|
+
if __name__ == "__main__":
|
|
113
|
+
print(service.greet("World"))
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Inject Into Functions (`app/main.py`)
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
from anydi import Inject
|
|
120
|
+
|
|
121
|
+
from app.container import container
|
|
122
|
+
from app.services import GreetingService
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@container.inject
|
|
126
|
+
def greet(service: GreetingService = Inject()) -> str:
|
|
127
|
+
return service.greet("World")
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
if __name__ == "__main__":
|
|
131
|
+
print(greet())
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Test with Overrides (`tests/test_app.py`)
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
from unittest import mock
|
|
138
|
+
|
|
139
|
+
from app.container import container
|
|
140
|
+
from app.services import GreetingService
|
|
141
|
+
from app.main import greet
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def test_greet() -> None:
|
|
145
|
+
service_mock = mock.Mock(spec=GreetingService)
|
|
146
|
+
service_mock.greet.return_value = "Mocked"
|
|
147
|
+
|
|
148
|
+
with container.override(GreetingService, service_mock):
|
|
149
|
+
result = greet()
|
|
150
|
+
|
|
151
|
+
assert result == "Mocked"
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Integrate with FastAPI (`app/api.py`)
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
from typing import Annotated
|
|
158
|
+
|
|
159
|
+
import anydi.ext.fastapi
|
|
160
|
+
from fastapi import FastAPI
|
|
161
|
+
from anydi.ext.fastapi import Inject
|
|
162
|
+
|
|
163
|
+
from app.container import container
|
|
164
|
+
from app.services import GreetingService
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
app = FastAPI()
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
@app.get("/greeting")
|
|
171
|
+
async def greet(
|
|
172
|
+
service: Annotated[GreetingService, Inject()]
|
|
173
|
+
) -> dict[str, str]:
|
|
174
|
+
return {"greeting": service.greet("World")}
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
anydi.ext.fastapi.install(app, container)
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Test the FastAPI Integration (`test_api.py`)
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
from unittest import mock
|
|
184
|
+
|
|
185
|
+
from fastapi.testclient import TestClient
|
|
186
|
+
|
|
187
|
+
from app.api import app
|
|
188
|
+
from app.container import container
|
|
189
|
+
from app.services import GreetingService
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
client = TestClient(app)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def test_api_greeting() -> None:
|
|
196
|
+
service_mock = mock.Mock(spec=GreetingService)
|
|
197
|
+
service_mock.greet.return_value = "Mocked"
|
|
198
|
+
|
|
199
|
+
with container.override(GreetingService, service_mock):
|
|
200
|
+
response = client.get("/greeting")
|
|
201
|
+
|
|
202
|
+
assert response.json() == {"greeting": "Mocked"}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Integrate with Django Ninja
|
|
206
|
+
|
|
207
|
+
Install the Django integration extras:
|
|
208
|
+
|
|
209
|
+
```sh
|
|
210
|
+
pip install 'anydi-django[ninja]'
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Expose the container factory (`app/container.py`):
|
|
214
|
+
|
|
215
|
+
```python
|
|
216
|
+
from anydi import Container
|
|
217
|
+
|
|
218
|
+
from app.services import GreetingService
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
container = Container()
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
@container.provider(scope="singleton")
|
|
225
|
+
def service() -> GreetingService:
|
|
226
|
+
return GreetingService()
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Configure Django (`settings.py`):
|
|
230
|
+
|
|
231
|
+
```python
|
|
232
|
+
INSTALLED_APPS = [
|
|
233
|
+
...
|
|
234
|
+
"anydi_django",
|
|
235
|
+
]
|
|
236
|
+
|
|
237
|
+
ANYDI = {
|
|
238
|
+
"CONTAINER_FACTORY": "app.container.container",
|
|
239
|
+
"PATCH_NINJA": True,
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Wire Django Ninja (`urls.py`):
|
|
244
|
+
|
|
245
|
+
```python
|
|
246
|
+
from typing import Annotated, Any
|
|
247
|
+
|
|
248
|
+
from anydi import Inject
|
|
249
|
+
from django.http import HttpRequest
|
|
250
|
+
from django.urls import path
|
|
251
|
+
from ninja import NinjaAPI
|
|
252
|
+
|
|
253
|
+
from app.services import GreetingService
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
api = NinjaAPI()
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
@api.get("/greeting")
|
|
260
|
+
def greet(request: HttpRequest, service: Annotated[GreetingService, Inject()]) -> Any:
|
|
261
|
+
return {"greeting": service.greet("World")}
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
urlpatterns = [
|
|
265
|
+
path("api/", api.urls),
|
|
266
|
+
]
|
|
267
|
+
```
|
anydi-0.56.0/README.md
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
# AnyDI
|
|
2
|
+
|
|
3
|
+
<div style="text-align: center;">
|
|
4
|
+
|
|
5
|
+
Modern, lightweight Dependency Injection library using type annotations.
|
|
6
|
+
|
|
7
|
+
[](https://github.com/antonrh/anydi/actions/workflows/ci.yml)
|
|
8
|
+
[](https://codecov.io/gh/antonrh/anydi)
|
|
9
|
+
[](https://anydi.readthedocs.io/en/latest/)
|
|
10
|
+
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
Documentation
|
|
15
|
+
|
|
16
|
+
http://anydi.readthedocs.io/
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
`AnyDI` is a modern, lightweight Dependency Injection library suitable for any synchronous or asynchronous applications with Python 3.10+, based on type annotations ([PEP 484](https://peps.python.org/pep-0484/)).
|
|
21
|
+
|
|
22
|
+
The key features are:
|
|
23
|
+
|
|
24
|
+
* **Type-safe**: Dependency resolution is driven by type hints.
|
|
25
|
+
* **Async-ready**: Works the same for sync and async providers or injections.
|
|
26
|
+
* **Scoped**: Built-in singleton, transient, and request lifetimes.
|
|
27
|
+
* **Simple**: Small surface area keeps boilerplate low.
|
|
28
|
+
* **Fast**: Resolver still adds only microseconds of overhead.
|
|
29
|
+
* **Named**: `Annotated[...]` makes multiple bindings per type simple.
|
|
30
|
+
* **Managed**: Providers can open/close resources via context managers.
|
|
31
|
+
* **Modular**: Compose containers or modules for large apps.
|
|
32
|
+
* **Scanning**: Auto-discovers injectable callables.
|
|
33
|
+
* **Integrated**: Extensions for popular frameworks.
|
|
34
|
+
* **Testable**: Override providers directly in tests.
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
```shell
|
|
39
|
+
pip install anydi
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Comprehensive Example
|
|
43
|
+
|
|
44
|
+
### Define a Service (`app/services.py`)
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
class GreetingService:
|
|
48
|
+
def greet(self, name: str) -> str:
|
|
49
|
+
return f"Hello, {name}!"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Create the Container and Providers (`app/container.py`)
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
from anydi import Container
|
|
56
|
+
|
|
57
|
+
from app.services import GreetingService
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
container = Container()
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@container.provider(scope="singleton")
|
|
64
|
+
def service() -> GreetingService:
|
|
65
|
+
return GreetingService()
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Resolve Dependencies Directly
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
from app.container import container
|
|
72
|
+
from app.services import GreetingService
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
service = container.resolve(GreetingService)
|
|
76
|
+
|
|
77
|
+
if __name__ == "__main__":
|
|
78
|
+
print(service.greet("World"))
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Inject Into Functions (`app/main.py`)
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from anydi import Inject
|
|
85
|
+
|
|
86
|
+
from app.container import container
|
|
87
|
+
from app.services import GreetingService
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@container.inject
|
|
91
|
+
def greet(service: GreetingService = Inject()) -> str:
|
|
92
|
+
return service.greet("World")
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
if __name__ == "__main__":
|
|
96
|
+
print(greet())
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Test with Overrides (`tests/test_app.py`)
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
from unittest import mock
|
|
103
|
+
|
|
104
|
+
from app.container import container
|
|
105
|
+
from app.services import GreetingService
|
|
106
|
+
from app.main import greet
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def test_greet() -> None:
|
|
110
|
+
service_mock = mock.Mock(spec=GreetingService)
|
|
111
|
+
service_mock.greet.return_value = "Mocked"
|
|
112
|
+
|
|
113
|
+
with container.override(GreetingService, service_mock):
|
|
114
|
+
result = greet()
|
|
115
|
+
|
|
116
|
+
assert result == "Mocked"
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Integrate with FastAPI (`app/api.py`)
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
from typing import Annotated
|
|
123
|
+
|
|
124
|
+
import anydi.ext.fastapi
|
|
125
|
+
from fastapi import FastAPI
|
|
126
|
+
from anydi.ext.fastapi import Inject
|
|
127
|
+
|
|
128
|
+
from app.container import container
|
|
129
|
+
from app.services import GreetingService
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
app = FastAPI()
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
@app.get("/greeting")
|
|
136
|
+
async def greet(
|
|
137
|
+
service: Annotated[GreetingService, Inject()]
|
|
138
|
+
) -> dict[str, str]:
|
|
139
|
+
return {"greeting": service.greet("World")}
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
anydi.ext.fastapi.install(app, container)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Test the FastAPI Integration (`test_api.py`)
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
from unittest import mock
|
|
149
|
+
|
|
150
|
+
from fastapi.testclient import TestClient
|
|
151
|
+
|
|
152
|
+
from app.api import app
|
|
153
|
+
from app.container import container
|
|
154
|
+
from app.services import GreetingService
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
client = TestClient(app)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def test_api_greeting() -> None:
|
|
161
|
+
service_mock = mock.Mock(spec=GreetingService)
|
|
162
|
+
service_mock.greet.return_value = "Mocked"
|
|
163
|
+
|
|
164
|
+
with container.override(GreetingService, service_mock):
|
|
165
|
+
response = client.get("/greeting")
|
|
166
|
+
|
|
167
|
+
assert response.json() == {"greeting": "Mocked"}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Integrate with Django Ninja
|
|
171
|
+
|
|
172
|
+
Install the Django integration extras:
|
|
173
|
+
|
|
174
|
+
```sh
|
|
175
|
+
pip install 'anydi-django[ninja]'
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Expose the container factory (`app/container.py`):
|
|
179
|
+
|
|
180
|
+
```python
|
|
181
|
+
from anydi import Container
|
|
182
|
+
|
|
183
|
+
from app.services import GreetingService
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
container = Container()
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
@container.provider(scope="singleton")
|
|
190
|
+
def service() -> GreetingService:
|
|
191
|
+
return GreetingService()
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Configure Django (`settings.py`):
|
|
195
|
+
|
|
196
|
+
```python
|
|
197
|
+
INSTALLED_APPS = [
|
|
198
|
+
...
|
|
199
|
+
"anydi_django",
|
|
200
|
+
]
|
|
201
|
+
|
|
202
|
+
ANYDI = {
|
|
203
|
+
"CONTAINER_FACTORY": "app.container.container",
|
|
204
|
+
"PATCH_NINJA": True,
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Wire Django Ninja (`urls.py`):
|
|
209
|
+
|
|
210
|
+
```python
|
|
211
|
+
from typing import Annotated, Any
|
|
212
|
+
|
|
213
|
+
from anydi import Inject
|
|
214
|
+
from django.http import HttpRequest
|
|
215
|
+
from django.urls import path
|
|
216
|
+
from ninja import NinjaAPI
|
|
217
|
+
|
|
218
|
+
from app.services import GreetingService
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
api = NinjaAPI()
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
@api.get("/greeting")
|
|
225
|
+
def greet(request: HttpRequest, service: Annotated[GreetingService, Inject()]) -> Any:
|
|
226
|
+
return {"greeting": service.greet("World")}
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
urlpatterns = [
|
|
230
|
+
path("api/", api.urls),
|
|
231
|
+
]
|
|
232
|
+
```
|
|
@@ -339,6 +339,7 @@ class Container:
|
|
|
339
339
|
default=default,
|
|
340
340
|
has_default=has_default,
|
|
341
341
|
provider=sub_provider,
|
|
342
|
+
shared_scope=sub_provider.scope == scope and scope != "transient",
|
|
342
343
|
)
|
|
343
344
|
)
|
|
344
345
|
|
|
@@ -389,10 +390,10 @@ class Container:
|
|
|
389
390
|
@staticmethod
|
|
390
391
|
def _validate_provider_scope(scope: Scope, name: str, is_resource: bool) -> None:
|
|
391
392
|
"""Validate the provider scope."""
|
|
392
|
-
if scope not in
|
|
393
|
+
if scope not in ALLOWED_SCOPES:
|
|
393
394
|
raise ValueError(
|
|
394
395
|
f"The provider `{name}` scope is invalid. Only the following "
|
|
395
|
-
f"scopes are supported: {', '.join(
|
|
396
|
+
f"scopes are supported: {', '.join(ALLOWED_SCOPES)}. "
|
|
396
397
|
"Please use one of the supported scopes when registering a provider."
|
|
397
398
|
)
|
|
398
399
|
if scope == "transient" and is_resource:
|
|
@@ -670,13 +671,17 @@ class Container:
|
|
|
670
671
|
) -> None:
|
|
671
672
|
self._scanner.scan(packages=packages, tags=tags)
|
|
672
673
|
|
|
673
|
-
# == Testing ==
|
|
674
|
+
# == Testing / Override Support ==
|
|
674
675
|
|
|
675
676
|
@contextlib.contextmanager
|
|
676
677
|
def override(self, interface: Any, instance: Any) -> Iterator[None]:
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
)
|
|
678
|
+
"""Override a dependency with a specific instance for testing."""
|
|
679
|
+
if not self.has_provider_for(interface):
|
|
680
|
+
raise LookupError(
|
|
681
|
+
f"The provider interface `{type_repr(interface)}` not registered."
|
|
682
|
+
)
|
|
683
|
+
self._resolver.add_override(interface, instance)
|
|
684
|
+
try:
|
|
685
|
+
yield
|
|
686
|
+
finally:
|
|
687
|
+
self._resolver.remove_override(interface)
|