anydi 0.55.1__tar.gz → 0.57.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.
Files changed (29) hide show
  1. anydi-0.57.0/PKG-INFO +266 -0
  2. anydi-0.57.0/README.md +231 -0
  3. {anydi-0.55.1 → anydi-0.57.0}/anydi/__init__.py +4 -2
  4. {anydi-0.55.1 → anydi-0.57.0}/anydi/_container.py +67 -110
  5. anydi-0.57.0/anydi/_injector.py +132 -0
  6. {anydi-0.55.1 → anydi-0.57.0}/anydi/_resolver.py +225 -122
  7. anydi-0.57.0/anydi/_scanner.py +118 -0
  8. {anydi-0.55.1 → anydi-0.57.0}/anydi/_types.py +48 -7
  9. {anydi-0.55.1 → anydi-0.57.0}/anydi/ext/fastapi.py +31 -33
  10. {anydi-0.55.1 → anydi-0.57.0}/anydi/ext/faststream.py +25 -31
  11. {anydi-0.55.1 → anydi-0.57.0}/anydi/ext/pydantic_settings.py +2 -1
  12. anydi-0.57.0/anydi/ext/pytest_plugin.py +477 -0
  13. anydi-0.57.0/anydi/testing.py +46 -0
  14. {anydi-0.55.1 → anydi-0.57.0}/pyproject.toml +7 -2
  15. anydi-0.55.1/PKG-INFO +0 -193
  16. anydi-0.55.1/README.md +0 -158
  17. anydi-0.55.1/anydi/_scanner.py +0 -110
  18. anydi-0.55.1/anydi/ext/pytest_plugin.py +0 -147
  19. anydi-0.55.1/anydi/testing.py +0 -125
  20. {anydi-0.55.1 → anydi-0.57.0}/anydi/_async_lock.py +0 -0
  21. {anydi-0.55.1 → anydi-0.57.0}/anydi/_context.py +0 -0
  22. {anydi-0.55.1 → anydi-0.57.0}/anydi/_decorators.py +0 -0
  23. {anydi-0.55.1 → anydi-0.57.0}/anydi/_module.py +0 -0
  24. {anydi-0.55.1 → anydi-0.57.0}/anydi/_provider.py +0 -0
  25. {anydi-0.55.1 → anydi-0.57.0}/anydi/ext/__init__.py +0 -0
  26. {anydi-0.55.1 → anydi-0.57.0}/anydi/ext/django/__init__.py +0 -0
  27. {anydi-0.55.1 → anydi-0.57.0}/anydi/ext/starlette/__init__.py +0 -0
  28. {anydi-0.55.1 → anydi-0.57.0}/anydi/ext/starlette/middleware.py +0 -0
  29. {anydi-0.55.1 → anydi-0.57.0}/anydi/py.typed +0 -0
anydi-0.57.0/PKG-INFO ADDED
@@ -0,0 +1,266 @@
1
+ Metadata-Version: 2.4
2
+ Name: anydi
3
+ Version: 0.57.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
+ [![CI](https://github.com/antonrh/anydi/actions/workflows/ci.yml/badge.svg)](https://github.com/antonrh/anydi/actions/workflows/ci.yml)
43
+ [![Coverage](https://codecov.io/gh/antonrh/anydi/branch/main/graph/badge.svg)](https://codecov.io/gh/antonrh/anydi)
44
+ [![Documentation](https://readthedocs.org/projects/anydi/badge/?version=latest)](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 Provide
120
+
121
+ from app.container import container
122
+ from app.services import GreetingService
123
+
124
+
125
+ def greet(service: Provide[GreetingService]) -> str:
126
+ return service.greet("World")
127
+
128
+
129
+ if __name__ == "__main__":
130
+ print(container.run(greet))
131
+ ```
132
+
133
+ ### Test with Overrides (`tests/test_app.py`)
134
+
135
+ ```python
136
+ from unittest import mock
137
+
138
+ from app.container import container
139
+ from app.services import GreetingService
140
+ from app.main import greet
141
+
142
+
143
+ def test_greet() -> None:
144
+ service_mock = mock.Mock(spec=GreetingService)
145
+ service_mock.greet.return_value = "Mocked"
146
+
147
+ with container.override(GreetingService, service_mock):
148
+ result = container.run(greet)
149
+
150
+ assert result == "Mocked"
151
+ ```
152
+
153
+ ### Integrate with FastAPI (`app/api.py`)
154
+
155
+ ```python
156
+ from typing import Annotated
157
+
158
+ import anydi.ext.fastapi
159
+ from fastapi import FastAPI
160
+
161
+ from anydi import Provide
162
+ from app.container import container
163
+ from app.services import GreetingService
164
+
165
+
166
+ app = FastAPI()
167
+
168
+
169
+ @app.get("/greeting")
170
+ async def greet(
171
+ service: Provide[GreetingService]
172
+ ) -> dict[str, str]:
173
+ return {"greeting": service.greet("World")}
174
+
175
+
176
+ anydi.ext.fastapi.install(app, container)
177
+ ```
178
+
179
+ ### Test the FastAPI Integration (`test_api.py`)
180
+
181
+ ```python
182
+ from unittest import mock
183
+
184
+ from fastapi.testclient import TestClient
185
+
186
+ from app.api import app
187
+ from app.container import container
188
+ from app.services import GreetingService
189
+
190
+
191
+ client = TestClient(app)
192
+
193
+
194
+ def test_api_greeting() -> None:
195
+ service_mock = mock.Mock(spec=GreetingService)
196
+ service_mock.greet.return_value = "Mocked"
197
+
198
+ with container.override(GreetingService, service_mock):
199
+ response = client.get("/greeting")
200
+
201
+ assert response.json() == {"greeting": "Mocked"}
202
+ ```
203
+
204
+ ### Integrate with Django Ninja
205
+
206
+ Install the Django integration extras:
207
+
208
+ ```sh
209
+ pip install 'anydi-django[ninja]'
210
+ ```
211
+
212
+ Expose the container factory (`app/container.py`):
213
+
214
+ ```python
215
+ from anydi import Container
216
+
217
+ from app.services import GreetingService
218
+
219
+
220
+ container = Container()
221
+
222
+
223
+ @container.provider(scope="singleton")
224
+ def service() -> GreetingService:
225
+ return GreetingService()
226
+ ```
227
+
228
+ Configure Django (`settings.py`):
229
+
230
+ ```python
231
+ INSTALLED_APPS = [
232
+ ...
233
+ "anydi_django",
234
+ ]
235
+
236
+ ANYDI = {
237
+ "CONTAINER_FACTORY": "app.container.container",
238
+ "PATCH_NINJA": True,
239
+ }
240
+ ```
241
+
242
+ Wire Django Ninja (`urls.py`):
243
+
244
+ ```python
245
+ from typing import Annotated, Any
246
+
247
+ from anydi import Provide
248
+ from django.http import HttpRequest
249
+ from django.urls import path
250
+ from ninja import NinjaAPI
251
+
252
+ from app.services import GreetingService
253
+
254
+
255
+ api = NinjaAPI()
256
+
257
+
258
+ @api.get("/greeting")
259
+ def greet(request: HttpRequest, service: Provide[GreetingService]) -> Any:
260
+ return {"greeting": service.greet("World")}
261
+
262
+
263
+ urlpatterns = [
264
+ path("api/", api.urls),
265
+ ]
266
+ ```
anydi-0.57.0/README.md ADDED
@@ -0,0 +1,231 @@
1
+ # AnyDI
2
+
3
+ <div style="text-align: center;">
4
+
5
+ Modern, lightweight Dependency Injection library using type annotations.
6
+
7
+ [![CI](https://github.com/antonrh/anydi/actions/workflows/ci.yml/badge.svg)](https://github.com/antonrh/anydi/actions/workflows/ci.yml)
8
+ [![Coverage](https://codecov.io/gh/antonrh/anydi/branch/main/graph/badge.svg)](https://codecov.io/gh/antonrh/anydi)
9
+ [![Documentation](https://readthedocs.org/projects/anydi/badge/?version=latest)](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 Provide
85
+
86
+ from app.container import container
87
+ from app.services import GreetingService
88
+
89
+
90
+ def greet(service: Provide[GreetingService]) -> str:
91
+ return service.greet("World")
92
+
93
+
94
+ if __name__ == "__main__":
95
+ print(container.run(greet))
96
+ ```
97
+
98
+ ### Test with Overrides (`tests/test_app.py`)
99
+
100
+ ```python
101
+ from unittest import mock
102
+
103
+ from app.container import container
104
+ from app.services import GreetingService
105
+ from app.main import greet
106
+
107
+
108
+ def test_greet() -> None:
109
+ service_mock = mock.Mock(spec=GreetingService)
110
+ service_mock.greet.return_value = "Mocked"
111
+
112
+ with container.override(GreetingService, service_mock):
113
+ result = container.run(greet)
114
+
115
+ assert result == "Mocked"
116
+ ```
117
+
118
+ ### Integrate with FastAPI (`app/api.py`)
119
+
120
+ ```python
121
+ from typing import Annotated
122
+
123
+ import anydi.ext.fastapi
124
+ from fastapi import FastAPI
125
+
126
+ from anydi import Provide
127
+ from app.container import container
128
+ from app.services import GreetingService
129
+
130
+
131
+ app = FastAPI()
132
+
133
+
134
+ @app.get("/greeting")
135
+ async def greet(
136
+ service: Provide[GreetingService]
137
+ ) -> dict[str, str]:
138
+ return {"greeting": service.greet("World")}
139
+
140
+
141
+ anydi.ext.fastapi.install(app, container)
142
+ ```
143
+
144
+ ### Test the FastAPI Integration (`test_api.py`)
145
+
146
+ ```python
147
+ from unittest import mock
148
+
149
+ from fastapi.testclient import TestClient
150
+
151
+ from app.api import app
152
+ from app.container import container
153
+ from app.services import GreetingService
154
+
155
+
156
+ client = TestClient(app)
157
+
158
+
159
+ def test_api_greeting() -> None:
160
+ service_mock = mock.Mock(spec=GreetingService)
161
+ service_mock.greet.return_value = "Mocked"
162
+
163
+ with container.override(GreetingService, service_mock):
164
+ response = client.get("/greeting")
165
+
166
+ assert response.json() == {"greeting": "Mocked"}
167
+ ```
168
+
169
+ ### Integrate with Django Ninja
170
+
171
+ Install the Django integration extras:
172
+
173
+ ```sh
174
+ pip install 'anydi-django[ninja]'
175
+ ```
176
+
177
+ Expose the container factory (`app/container.py`):
178
+
179
+ ```python
180
+ from anydi import Container
181
+
182
+ from app.services import GreetingService
183
+
184
+
185
+ container = Container()
186
+
187
+
188
+ @container.provider(scope="singleton")
189
+ def service() -> GreetingService:
190
+ return GreetingService()
191
+ ```
192
+
193
+ Configure Django (`settings.py`):
194
+
195
+ ```python
196
+ INSTALLED_APPS = [
197
+ ...
198
+ "anydi_django",
199
+ ]
200
+
201
+ ANYDI = {
202
+ "CONTAINER_FACTORY": "app.container.container",
203
+ "PATCH_NINJA": True,
204
+ }
205
+ ```
206
+
207
+ Wire Django Ninja (`urls.py`):
208
+
209
+ ```python
210
+ from typing import Annotated, Any
211
+
212
+ from anydi import Provide
213
+ from django.http import HttpRequest
214
+ from django.urls import path
215
+ from ninja import NinjaAPI
216
+
217
+ from app.services import GreetingService
218
+
219
+
220
+ api = NinjaAPI()
221
+
222
+
223
+ @api.get("/greeting")
224
+ def greet(request: HttpRequest, service: Provide[GreetingService]) -> Any:
225
+ return {"greeting": service.greet("World")}
226
+
227
+
228
+ urlpatterns = [
229
+ path("api/", api.urls),
230
+ ]
231
+ ```
@@ -1,10 +1,10 @@
1
1
  """AnyDI public objects and functions."""
2
2
 
3
- from ._container import Container
3
+ from ._container import Container, import_container
4
4
  from ._decorators import injectable, provided, provider, request, singleton, transient
5
5
  from ._module import Module
6
6
  from ._provider import ProviderDef as Provider
7
- from ._types import Inject, Scope
7
+ from ._types import Inject, Provide, Scope
8
8
 
9
9
  # Alias for dependency auto marker
10
10
  # TODO: deprecate it
@@ -15,9 +15,11 @@ __all__ = [
15
15
  "Container",
16
16
  "Inject",
17
17
  "Module",
18
+ "Provide",
18
19
  "Provider",
19
20
  "Scope",
20
21
  "auto",
22
+ "import_container",
21
23
  "injectable",
22
24
  "provided",
23
25
  "provider",