anydi 0.56.0__py3-none-any.whl → 0.58.0__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.
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import importlib.util
3
4
  import inspect
4
5
  import logging
5
6
  from collections.abc import Callable, Iterator
@@ -9,47 +10,199 @@ import pytest
9
10
  from anyio.pytest_plugin import extract_backend_and_options, get_runner
10
11
  from typing_extensions import get_annotations
11
12
 
12
- from anydi import Container
13
+ from anydi import Container, import_container
13
14
 
14
15
  logger = logging.getLogger(__name__)
15
16
 
16
-
17
- def pytest_configure(config: pytest.Config) -> None:
18
- config.addinivalue_line(
19
- "markers",
20
- "inject: mark test as needing dependency injection",
21
- )
17
+ # Storage for fixtures with inject markers
18
+ _INJECTED_FIXTURES: dict[str, dict[str, Any]] = {}
22
19
 
23
20
 
24
21
  def pytest_addoption(parser: pytest.Parser) -> None:
22
+ parser.addini(
23
+ "anydi_autoinject",
24
+ help="Automatically inject dependencies into all test functions",
25
+ type="bool",
26
+ default=False,
27
+ )
25
28
  parser.addini(
26
29
  "anydi_inject_all",
27
- help="Inject all dependencies",
30
+ help="Deprecated: use 'anydi_autoinject' instead",
31
+ type="bool",
32
+ default=False,
33
+ )
34
+ parser.addini(
35
+ "anydi_container",
36
+ help=(
37
+ "Path to container instance or factory "
38
+ "(e.g., 'myapp.container:container' or 'myapp.container.container')"
39
+ ),
40
+ type="string",
41
+ default=None,
42
+ )
43
+ parser.addini(
44
+ "anydi_fixture_inject_enabled",
45
+ help=(
46
+ "Enable dependency injection into fixtures marked with @pytest.mark.inject"
47
+ ),
28
48
  type="bool",
29
49
  default=False,
30
50
  )
31
51
 
32
52
 
33
- CONTAINER_FIXTURE_NAME = "container"
53
+ def pytest_configure(config: pytest.Config) -> None:
54
+ config.addinivalue_line(
55
+ "markers",
56
+ "inject: mark test as needing dependency injection",
57
+ )
34
58
 
59
+ # Enable fixture injection if configured
60
+ inject_fixtures_enabled = cast(bool, config.getini("anydi_fixture_inject_enabled"))
61
+ if inject_fixtures_enabled:
62
+ autoinject = cast(bool, config.getini("anydi_autoinject"))
63
+ inject_all = cast(bool, config.getini("anydi_inject_all"))
64
+ _patch_pytest_fixtures(autoinject=autoinject or inject_all)
65
+ logger.debug(
66
+ "Fixture injection enabled via anydi_fixture_inject_enabled config"
67
+ )
35
68
 
36
- @pytest.fixture
37
- def anydi_setup_container(request: pytest.FixtureRequest) -> Container:
69
+
70
+ @pytest.hookimpl(hookwrapper=True)
71
+ def pytest_fixture_setup( # noqa: C901
72
+ fixturedef: pytest.FixtureDef[Any],
73
+ request: pytest.FixtureRequest,
74
+ ) -> Iterator[None]:
75
+ """Inject dependencies into fixtures marked with @pytest.mark.inject."""
76
+ # Check if this fixture has injection metadata
77
+ fixture_name = fixturedef.argname
78
+ if fixture_name not in _INJECTED_FIXTURES:
79
+ yield
80
+ return
81
+
82
+ # Get the metadata
83
+ fixture_info = _INJECTED_FIXTURES[fixture_name]
84
+ original_func = fixture_info["func"]
85
+ parameters: list[tuple[str, Any]] = fixture_info["parameters"]
86
+
87
+ # Get the container
38
88
  try:
39
- return cast(Container, request.getfixturevalue(CONTAINER_FIXTURE_NAME))
40
- except pytest.FixtureLookupError as exc:
41
- exc.msg = (
42
- "`container` fixture is not found. Make sure to define it in your test "
43
- "module or override `anydi_setup_container` fixture."
89
+ container = cast(Container, request.getfixturevalue("container"))
90
+ except pytest.FixtureLookupError:
91
+ yield
92
+ return
93
+
94
+ resolvable_params = _select_resolvable_parameters(container, parameters)
95
+
96
+ if not resolvable_params:
97
+ yield
98
+ return
99
+
100
+ target_name = f"fixture '{fixture_name}'"
101
+
102
+ def _prepare_sync_call_kwargs(kwargs: dict[str, Any]) -> dict[str, Any]:
103
+ combined_kwargs = dict(kwargs)
104
+ combined_kwargs.update(
105
+ _resolve_dependencies_sync(container, resolvable_params, target=target_name)
44
106
  )
45
- raise exc
107
+ return combined_kwargs
108
+
109
+ async def _prepare_async_call_kwargs(kwargs: dict[str, Any]) -> dict[str, Any]:
110
+ combined_kwargs = dict(kwargs)
111
+ combined_kwargs.update(
112
+ await _resolve_dependencies_async(
113
+ container, resolvable_params, target=target_name
114
+ )
115
+ )
116
+ return combined_kwargs
117
+
118
+ def _ensure_anyio_backend() -> tuple[str, dict[str, Any]]:
119
+ try:
120
+ backend = request.getfixturevalue("anyio_backend")
121
+ except pytest.FixtureLookupError as exc: # pragma: no cover - defensive
122
+ msg = (
123
+ "To run async fixtures with AnyDI, please configure the `anyio` pytest "
124
+ "plugin (provide the `anyio_backend` fixture)."
125
+ )
126
+ pytest.fail(msg, pytrace=False)
127
+ raise RuntimeError from exc # Unreachable but satisfies type checkers
128
+
129
+ return extract_backend_and_options(backend)
130
+
131
+ # Replace the fixture function with one that mirrors the original's type and
132
+ # injects dependencies before delegating to the user-defined function.
133
+ original_fixture_func = fixturedef.func
134
+
135
+ if inspect.isasyncgenfunction(original_func):
136
+
137
+ def asyncgen_wrapper(*args: Any, **kwargs: Any) -> Iterator[Any]:
138
+ backend_name, backend_options = _ensure_anyio_backend()
139
+
140
+ async def _fixture() -> Any:
141
+ call_kwargs = await _prepare_async_call_kwargs(kwargs)
142
+ async for value in original_func(**call_kwargs):
143
+ yield value
144
+
145
+ with get_runner(backend_name, backend_options) as runner:
146
+ yield from runner.run_asyncgen_fixture(_fixture, {}) # type: ignore
147
+
148
+ fixturedef.func = asyncgen_wrapper # type: ignore[misc]
149
+ elif inspect.iscoroutinefunction(original_func):
150
+
151
+ def async_wrapper(*args: Any, **kwargs: Any) -> Any:
152
+ backend_name, backend_options = _ensure_anyio_backend()
153
+
154
+ async def _fixture() -> Any:
155
+ call_kwargs = await _prepare_async_call_kwargs(kwargs)
156
+ return await original_func(**call_kwargs)
157
+
158
+ with get_runner(backend_name, backend_options) as runner:
159
+ return runner.run_fixture(_fixture, {})
160
+
161
+ fixturedef.func = async_wrapper # type: ignore[misc]
162
+ elif inspect.isgeneratorfunction(original_func):
163
+
164
+ def generator_wrapper(*args: Any, **kwargs: Any) -> Iterator[Any]:
165
+ call_kwargs = _prepare_sync_call_kwargs(kwargs)
166
+ yield from original_func(**call_kwargs)
167
+
168
+ fixturedef.func = generator_wrapper # type: ignore[misc]
169
+ else:
170
+
171
+ def sync_wrapper(*args: Any, **kwargs: Any) -> Any:
172
+ call_kwargs = _prepare_sync_call_kwargs(kwargs)
173
+ return original_func(**call_kwargs)
174
+
175
+ fixturedef.func = sync_wrapper # type: ignore[misc]
176
+
177
+ # Let pytest execute the modified fixture
178
+ yield
179
+
180
+ # Restore the original function
181
+ fixturedef.func = original_fixture_func # type: ignore[misc]
182
+
183
+
184
+ @pytest.fixture(scope="session")
185
+ def container(request: pytest.FixtureRequest) -> Container:
186
+ """Container fixture."""
187
+ return _find_container(request)
46
188
 
47
189
 
48
190
  @pytest.fixture
49
191
  def _anydi_should_inject(request: pytest.FixtureRequest) -> bool:
50
192
  marker = request.node.get_closest_marker("inject")
193
+
194
+ # Check new config option first
195
+ autoinject = cast(bool, request.config.getini("anydi_autoinject"))
196
+
197
+ # Check deprecated option for backward compatibility
51
198
  inject_all = cast(bool, request.config.getini("anydi_inject_all"))
52
- return marker is not None or inject_all
199
+ if inject_all:
200
+ logger.warning(
201
+ "Configuration option 'anydi_inject_all' is deprecated. "
202
+ "Please use 'anydi_autoinject' instead."
203
+ )
204
+
205
+ return marker is not None or autoinject or inject_all
53
206
 
54
207
 
55
208
  @pytest.fixture
@@ -60,12 +213,8 @@ def _anydi_injected_parameter_iterator(
60
213
  request.node._fixtureinfo.name2fixturedefs.keys()
61
214
  )
62
215
 
63
- def _iterator() -> Iterator[tuple[str, inspect.Parameter]]:
64
- for name, annotation in get_annotations(
65
- request.function, eval_str=True
66
- ).items():
67
- if name == "return":
68
- continue
216
+ def _iterator() -> Iterator[tuple[str, Any]]:
217
+ for name, annotation in _iter_injectable_parameters(request.function):
69
218
  if name not in fixturenames:
70
219
  continue
71
220
  yield name, annotation
@@ -84,20 +233,20 @@ def _anydi_inject(
84
233
  if inspect.iscoroutinefunction(request.function) or not _anydi_should_inject:
85
234
  return
86
235
 
87
- # Setup the container
88
- container = cast(Container, request.getfixturevalue("anydi_setup_container"))
236
+ parameters = list(_anydi_injected_parameter_iterator())
237
+ if not parameters:
238
+ return
89
239
 
90
- for argname, interface in _anydi_injected_parameter_iterator():
91
- # Skip if the interface has no provider
92
- if not container.has_provider_for(interface):
93
- continue
240
+ container = cast(Container, request.getfixturevalue("container"))
241
+ resolvable = _select_resolvable_parameters(container, parameters)
242
+ if not resolvable:
243
+ return
94
244
 
95
- try:
96
- request.node.funcargs[argname] = container.resolve(interface)
97
- except Exception as exc:
98
- logger.warning(
99
- f"Failed to resolve dependency for argument '{argname}'.", exc_info=exc
100
- )
245
+ resolved = _resolve_dependencies_sync(
246
+ container, resolvable, target=request.node.nodeid
247
+ )
248
+ for argname, value in resolved.items():
249
+ request.node.funcargs[argname] = value
101
250
 
102
251
 
103
252
  @pytest.fixture(autouse=True)
@@ -123,25 +272,206 @@ def _anydi_ainject(
123
272
  )
124
273
  pytest.fail(msg, pytrace=False)
125
274
 
126
- async def _awrapper() -> None:
127
- # Setup the container
128
- container = cast(Container, request.getfixturevalue("anydi_setup_container"))
275
+ parameters = list(_anydi_injected_parameter_iterator())
276
+ if not parameters:
277
+ return
129
278
 
130
- for argname, interface in _anydi_injected_parameter_iterator():
131
- # Skip if the interface has no provider
132
- if not container.has_provider_for(interface):
133
- continue
279
+ container = cast(Container, request.getfixturevalue("container"))
280
+ resolvable = _select_resolvable_parameters(container, parameters)
281
+ if not resolvable:
282
+ return
134
283
 
135
- try:
136
- request.node.funcargs[argname] = await container.aresolve(interface)
137
- except Exception as exc:
138
- logger.warning(
139
- f"Failed to resolve dependency for argument '{argname}'.",
140
- exc_info=exc,
141
- )
284
+ async def _awrapper() -> None:
285
+ resolved = await _resolve_dependencies_async(
286
+ container, resolvable, target=request.node.nodeid
287
+ )
288
+ for argname, value in resolved.items():
289
+ request.node.funcargs[argname] = value
142
290
 
143
291
  anyio_backend = request.getfixturevalue("anyio_backend")
144
292
  backend_name, backend_options = extract_backend_and_options(anyio_backend)
145
293
 
146
294
  with get_runner(backend_name, backend_options) as runner:
147
295
  runner.run_fixture(_awrapper, {})
296
+
297
+
298
+ def _find_container(request: pytest.FixtureRequest) -> Container:
299
+ """Find container."""
300
+
301
+ # Look for 'anydi_container' defined in pytest.ini (highest priority)
302
+ container_path = cast(str | None, request.config.getini("anydi_container"))
303
+ if container_path:
304
+ try:
305
+ return import_container(container_path)
306
+ except ImportError as exc:
307
+ raise RuntimeError(
308
+ f"Failed to load container from config "
309
+ f"'anydi_container={container_path}': {exc}"
310
+ ) from exc
311
+
312
+ # Detect pytest-django + anydi_django availability
313
+ pluginmanager = request.config.pluginmanager
314
+ if pluginmanager.hasplugin("django") and importlib.util.find_spec("anydi_django"):
315
+ return import_container("anydi_django.container")
316
+
317
+ # Neither fixture nor config found
318
+ raise pytest.FixtureLookupError(
319
+ None,
320
+ request,
321
+ "`container` fixture is not found and 'anydi_container' config is not set. "
322
+ "Either define a `container` fixture in your test module "
323
+ "or set 'anydi_container' in pytest.ini.",
324
+ )
325
+
326
+
327
+ def _patch_pytest_fixtures(*, autoinject: bool) -> None: # noqa: C901
328
+ """Patch pytest.fixture decorator to intercept fixtures with inject markers."""
329
+ from _pytest.fixtures import fixture as original_fixture_decorator
330
+
331
+ def patched_fixture(*args: Any, **kwargs: Any) -> Any: # noqa: C901
332
+ """Patched fixture decorator that handles inject markers."""
333
+
334
+ def should_process(func: Callable[..., Any]) -> bool:
335
+ has_inject_marker = False
336
+ if hasattr(func, "pytestmark"):
337
+ markers = getattr(func, "pytestmark", [])
338
+ if not isinstance(markers, list):
339
+ markers = [markers]
340
+
341
+ has_inject_marker = any(
342
+ marker.name == "inject"
343
+ for marker in markers
344
+ if hasattr(marker, "name")
345
+ )
346
+
347
+ return autoinject or has_inject_marker
348
+
349
+ def register_fixture(func: Callable[..., Any]) -> Callable[..., Any] | None:
350
+ if not should_process(func):
351
+ return None
352
+
353
+ parameters = list(_iter_injectable_parameters(func))
354
+ if not parameters:
355
+ return None
356
+
357
+ sig = inspect.signature(func, eval_str=True)
358
+ has_request_param = "request" in sig.parameters
359
+
360
+ if has_request_param:
361
+
362
+ def wrapper_with_request(request: Any) -> Any:
363
+ return func
364
+
365
+ wrapper_func = wrapper_with_request
366
+ else:
367
+
368
+ def wrapper_no_request() -> Any:
369
+ return func
370
+
371
+ wrapper_func = wrapper_no_request
372
+
373
+ wrapper_func.__name__ = func.__name__
374
+ wrapper_func.__annotations__ = {}
375
+
376
+ fixture_name = func.__name__
377
+ _INJECTED_FIXTURES[fixture_name] = {
378
+ "func": func,
379
+ "parameters": parameters,
380
+ }
381
+ logger.debug(
382
+ "Registered injectable fixture '%s' with params: %s",
383
+ fixture_name,
384
+ [name for name, _ in parameters],
385
+ )
386
+
387
+ return wrapper_func
388
+
389
+ # Handle both @pytest.fixture and @pytest.fixture() usage
390
+ if len(args) == 1 and callable(args[0]) and not kwargs:
391
+ func = args[0]
392
+ wrapper_func = register_fixture(func)
393
+ if wrapper_func:
394
+ return original_fixture_decorator(wrapper_func)
395
+
396
+ return original_fixture_decorator(func)
397
+ else:
398
+
399
+ def decorator(func: Callable[..., Any]) -> Any:
400
+ wrapper_func = register_fixture(func)
401
+ if wrapper_func:
402
+ return original_fixture_decorator(*args, **kwargs)(wrapper_func)
403
+
404
+ return original_fixture_decorator(*args, **kwargs)(func)
405
+
406
+ return decorator
407
+
408
+ # Replace pytest.fixture
409
+ pytest.fixture = patched_fixture # type: ignore[assignment]
410
+ # Also patch _pytest.fixtures.fixture
411
+ import _pytest.fixtures
412
+
413
+ _pytest.fixtures.fixture = patched_fixture # type: ignore[assignment]
414
+
415
+
416
+ def _iter_injectable_parameters(
417
+ func: Callable[..., Any], *, skip: tuple[str, ...] = ("request",)
418
+ ) -> Iterator[tuple[str, Any]]:
419
+ annotations = get_annotations(func, eval_str=True)
420
+ skip_names = set(skip)
421
+ for name, annotation in annotations.items():
422
+ if name in skip_names or name == "return":
423
+ continue
424
+ yield name, annotation
425
+
426
+
427
+ def _select_resolvable_parameters(
428
+ container: Container,
429
+ parameters: Iterator[tuple[str, Any]] | list[tuple[str, Any]],
430
+ ) -> list[tuple[str, Any]]:
431
+ return [
432
+ (name, annotation)
433
+ for name, annotation in parameters
434
+ if container.has_provider_for(annotation)
435
+ ]
436
+
437
+
438
+ def _resolve_dependencies_sync(
439
+ container: Container,
440
+ parameters: list[tuple[str, Any]],
441
+ *,
442
+ target: str,
443
+ ) -> dict[str, Any]:
444
+ resolved: dict[str, Any] = {}
445
+ for param_name, annotation in parameters:
446
+ try:
447
+ resolved[param_name] = container.resolve(annotation)
448
+ logger.debug("Resolved %s=%s for %s", param_name, annotation, target)
449
+ except Exception as exc: # pragma: no cover - defensive logging
450
+ logger.warning(
451
+ "Failed to resolve dependency for '%s' on %s.",
452
+ param_name,
453
+ target,
454
+ exc_info=exc,
455
+ )
456
+ return resolved
457
+
458
+
459
+ async def _resolve_dependencies_async(
460
+ container: Container,
461
+ parameters: list[tuple[str, Any]],
462
+ *,
463
+ target: str,
464
+ ) -> dict[str, Any]:
465
+ resolved: dict[str, Any] = {}
466
+ for param_name, annotation in parameters:
467
+ try:
468
+ resolved[param_name] = await container.aresolve(annotation)
469
+ logger.debug("Resolved %s=%s for async %s", param_name, annotation, target)
470
+ except Exception as exc: # pragma: no cover - defensive logging
471
+ logger.warning(
472
+ "Failed to resolve async dependency for '%s' on %s.",
473
+ param_name,
474
+ target,
475
+ exc_info=exc,
476
+ )
477
+ return resolved
@@ -5,7 +5,7 @@ from starlette.requests import Request
5
5
  from starlette.responses import Response
6
6
  from starlette.types import ASGIApp
7
7
 
8
- from anydi._container import Container
8
+ from anydi import Container
9
9
 
10
10
 
11
11
  class RequestScopedMiddleware(BaseHTTPMiddleware):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: anydi
3
- Version: 0.56.0
3
+ Version: 0.58.0
4
4
  Summary: Dependency Injection library
5
5
  Keywords: dependency injection,dependencies,di,async,asyncio,application
6
6
  Author: Anton Ruhlov
@@ -58,9 +58,9 @@ The key features are:
58
58
 
59
59
  * **Type-safe**: Dependency resolution is driven by type hints.
60
60
  * **Async-ready**: Works the same for sync and async providers or injections.
61
- * **Scoped**: Built-in singleton, transient, and request lifetimes.
61
+ * **Scoped**: Built-in singleton, transient, and request scopes, plus custom scopes.
62
62
  * **Simple**: Small surface area keeps boilerplate low.
63
- * **Fast**: Resolver still adds only microseconds of overhead.
63
+ * **Fast**: Has minimal overhead and resolves dependencies quickly.
64
64
  * **Named**: `Annotated[...]` makes multiple bindings per type simple.
65
65
  * **Managed**: Providers can open/close resources via context managers.
66
66
  * **Modular**: Compose containers or modules for large apps.
@@ -74,7 +74,7 @@ The key features are:
74
74
  pip install anydi
75
75
  ```
76
76
 
77
- ## Comprehensive Example
77
+ ## Quick Example
78
78
 
79
79
  ### Define a Service (`app/services.py`)
80
80
 
@@ -116,19 +116,18 @@ if __name__ == "__main__":
116
116
  ### Inject Into Functions (`app/main.py`)
117
117
 
118
118
  ```python
119
- from anydi import Inject
119
+ from anydi import Provide
120
120
 
121
121
  from app.container import container
122
122
  from app.services import GreetingService
123
123
 
124
124
 
125
- @container.inject
126
- def greet(service: GreetingService = Inject()) -> str:
125
+ def greet(service: Provide[GreetingService]) -> str:
127
126
  return service.greet("World")
128
127
 
129
128
 
130
129
  if __name__ == "__main__":
131
- print(greet())
130
+ print(container.run(greet))
132
131
  ```
133
132
 
134
133
  ### Test with Overrides (`tests/test_app.py`)
@@ -146,7 +145,7 @@ def test_greet() -> None:
146
145
  service_mock.greet.return_value = "Mocked"
147
146
 
148
147
  with container.override(GreetingService, service_mock):
149
- result = greet()
148
+ result = container.run(greet)
150
149
 
151
150
  assert result == "Mocked"
152
151
  ```
@@ -158,8 +157,8 @@ from typing import Annotated
158
157
 
159
158
  import anydi.ext.fastapi
160
159
  from fastapi import FastAPI
161
- from anydi.ext.fastapi import Inject
162
160
 
161
+ from anydi import Provide
163
162
  from app.container import container
164
163
  from app.services import GreetingService
165
164
 
@@ -169,7 +168,7 @@ app = FastAPI()
169
168
 
170
169
  @app.get("/greeting")
171
170
  async def greet(
172
- service: Annotated[GreetingService, Inject()]
171
+ service: Provide[GreetingService]
173
172
  ) -> dict[str, str]:
174
173
  return {"greeting": service.greet("World")}
175
174
 
@@ -245,7 +244,7 @@ Wire Django Ninja (`urls.py`):
245
244
  ```python
246
245
  from typing import Annotated, Any
247
246
 
248
- from anydi import Inject
247
+ from anydi import Provide
249
248
  from django.http import HttpRequest
250
249
  from django.urls import path
251
250
  from ninja import NinjaAPI
@@ -257,7 +256,7 @@ api = NinjaAPI()
257
256
 
258
257
 
259
258
  @api.get("/greeting")
260
- def greet(request: HttpRequest, service: Annotated[GreetingService, Inject()]) -> Any:
259
+ def greet(request: HttpRequest, service: Provide[GreetingService]) -> Any:
261
260
  return {"greeting": service.greet("World")}
262
261
 
263
262
 
@@ -265,3 +264,23 @@ urlpatterns = [
265
264
  path("api/", api.urls),
266
265
  ]
267
266
  ```
267
+
268
+ ## What's Next?
269
+
270
+ Ready to learn more? Check out these resources:
271
+
272
+ **Core Documentation:**
273
+ - [Core Concepts](https://anydi.readthedocs.io/en/latest/concepts/) - Understand containers, providers, scopes, and dependency injection
274
+ - [Providers](https://anydi.readthedocs.io/en/latest/usage/providers/) - Learn about registration, named providers, and resource management
275
+ - [Scopes](https://anydi.readthedocs.io/en/latest/usage/scopes/) - Master lifecycle management with built-in and custom scopes
276
+ - [Dependency Injection](https://anydi.readthedocs.io/en/latest/usage/injection/) - Explore injection patterns and techniques
277
+ - [Testing](https://anydi.readthedocs.io/en/latest/usage/testing/) - Write testable code with provider overrides
278
+
279
+ **Framework Integrations:**
280
+ - [FastAPI](https://anydi.readthedocs.io/en/latest/extensions/fastapi/) - Build modern APIs with automatic dependency injection
281
+ - [Django](https://anydi.readthedocs.io/en/latest/extensions/django/) - Integrate with Django and Django Ninja
282
+ - [FastStream](https://anydi.readthedocs.io/en/latest/extensions/faststream/) - Message broker applications
283
+ - [Pydantic Settings](https://anydi.readthedocs.io/en/latest/extensions/pydantic_settings/) - Configuration management
284
+
285
+ **Full Documentation:**
286
+ - [Read the Docs](https://anydi.readthedocs.io/) - Complete documentation with examples and guides
@@ -0,0 +1,25 @@
1
+ anydi/__init__.py,sha256=bQKzn9qfNnIMi1m3J-DdSknSDwNg8j08fdQg_-Edkto,613
2
+ anydi/_async_lock.py,sha256=3dwZr0KthXFYha0XKMyXf8jMmGb1lYoNC0O5w29V9ic,1104
3
+ anydi/_container.py,sha256=j8XM5UYw3PVwPq92TMtEwLwqcMObs-9ZTGAEsTj4caE,26023
4
+ anydi/_context.py,sha256=-9QqeMWo9OpZVXZxZCQgIsswggl3Ch7lgx1KiFX_ezc,3752
5
+ anydi/_decorators.py,sha256=J3W261ZAG7q4XKm4tbAv1wsWr9ysx9_5MUbUvSJB_MQ,2809
6
+ anydi/_injector.py,sha256=IxKTh2rzMHrsW554tbiJl33Hb5sRGKYY_NU1rC4UvxE,4378
7
+ anydi/_module.py,sha256=2kN5uEXLd2Dsc58gz5IWK43wJewr_QgIVGSO3iWp798,2609
8
+ anydi/_provider.py,sha256=OV1WFHTYv7W2U0XDk_Kql1r551Vhq8o-pUV5ep1HQcU,1574
9
+ anydi/_resolver.py,sha256=dMtWU4OHWMYP7J2Rks0sdwyjLHF1uKo77lnvTGhhtBo,30222
10
+ anydi/_scanner.py,sha256=rbRkHzyd2zMu7AFLffN6_tZJcMaW9gy7E-lVdHLHYrs,4294
11
+ anydi/_types.py,sha256=tLJS27j0lWJFd4fIIGlGbbfKBVTPTnWgPYqZlenktis,2939
12
+ anydi/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
+ anydi/ext/django/__init__.py,sha256=Ve8lncLU9dPY_Vjt4zihPgsSxwAtFHACn0XvBM5JG8k,367
14
+ anydi/ext/fastapi.py,sha256=TQoS1Vh9chV0Tj0qyxjgqoGTYEMC3jp0il9CDZCZLcc,2326
15
+ anydi/ext/faststream.py,sha256=XT80r1FGL-xlU7r8urm9sNpUfl4OPMJseW4dade_fR4,1836
16
+ anydi/ext/pydantic_settings.py,sha256=jVJZ1wPaPpsxdNPlJj9yq282ebqLZ9tckWpZ0eIwWLg,1533
17
+ anydi/ext/pytest_plugin.py,sha256=M54DkA-KxD9GqLnXdoCyn-Qur2c44MB6d0AgJuYCZ5w,16171
18
+ anydi/ext/starlette/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
+ anydi/ext/starlette/middleware.py,sha256=9CQtGg5ZzUz2gFSzJr8U4BWzwNjK8XMctm3n52M77Z0,792
20
+ anydi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
+ anydi/testing.py,sha256=cHg3mMScZbEep9smRqSNQ81BZMQOkyugHe8TvKdPnEg,1347
22
+ anydi-0.58.0.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
23
+ anydi-0.58.0.dist-info/entry_points.txt,sha256=AgOcQYM5KyS4D37QcYb00tiid0QA-pD1VrjHHq4QAps,44
24
+ anydi-0.58.0.dist-info/METADATA,sha256=KE1cG2fc8ZyuoUBMhGP0pUqcUkr2z8ZOGjPVy3-ILV0,7901
25
+ anydi-0.58.0.dist-info/RECORD,,
@@ -1,24 +0,0 @@
1
- anydi/__init__.py,sha256=Cz-beqReX0d05SFDmYcrzIs3FqQkWAwpy1Aqzd5db34,547
2
- anydi/_async_lock.py,sha256=3dwZr0KthXFYha0XKMyXf8jMmGb1lYoNC0O5w29V9ic,1104
3
- anydi/_container.py,sha256=dKRT4FB0ONyEt2-MRz4T0MZziwZAMb0XyulV6lVn04g,24997
4
- anydi/_context.py,sha256=-9QqeMWo9OpZVXZxZCQgIsswggl3Ch7lgx1KiFX_ezc,3752
5
- anydi/_decorators.py,sha256=J3W261ZAG7q4XKm4tbAv1wsWr9ysx9_5MUbUvSJB_MQ,2809
6
- anydi/_module.py,sha256=2kN5uEXLd2Dsc58gz5IWK43wJewr_QgIVGSO3iWp798,2609
7
- anydi/_provider.py,sha256=OV1WFHTYv7W2U0XDk_Kql1r551Vhq8o-pUV5ep1HQcU,1574
8
- anydi/_resolver.py,sha256=-MF2KsERF5qzU6uqYPF1fI58isgsjxXPLERylzFFDHE,28787
9
- anydi/_scanner.py,sha256=oycIC9kw9fsIG9qgtRHeBkj3HjmcLK0FTqWLXTLLSWE,3636
10
- anydi/_types.py,sha256=l3xQ0Zn15gRAwvBoQ9PRfCBigi2rrtSqGV-C50xXrLw,1780
11
- anydi/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
- anydi/ext/django/__init__.py,sha256=Ve8lncLU9dPY_Vjt4zihPgsSxwAtFHACn0XvBM5JG8k,367
13
- anydi/ext/fastapi.py,sha256=FflFBdK-moyv9Vsfem4NyNt8jgPwFLweeCl5cfU0Iks,2348
14
- anydi/ext/faststream.py,sha256=dJPInvi0JUx-SS8H4aBRl3u-PAW6S_TqqfxVOl3D1L0,1929
15
- anydi/ext/pydantic_settings.py,sha256=0GQjw7QpQlT5p6GxFClXYdtc6J42PClmAnRWPEzMjvY,1488
16
- anydi/ext/pytest_plugin.py,sha256=Es1K1S6_2gIdTUYkbw2d1aZcHnjJutGFafVsLPGcVJc,4684
17
- anydi/ext/starlette/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
- anydi/ext/starlette/middleware.py,sha256=MxnzshAs-CMvjJp0r457k52MzBL8O4KAuClnF6exBdU,803
19
- anydi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
- anydi/testing.py,sha256=cHg3mMScZbEep9smRqSNQ81BZMQOkyugHe8TvKdPnEg,1347
21
- anydi-0.56.0.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
22
- anydi-0.56.0.dist-info/entry_points.txt,sha256=AgOcQYM5KyS4D37QcYb00tiid0QA-pD1VrjHHq4QAps,44
23
- anydi-0.56.0.dist-info/METADATA,sha256=JkjqRSFZ-Vu22IYLnWS2uRTZA7uUbX41o2b-51yp0dA,6561
24
- anydi-0.56.0.dist-info/RECORD,,
File without changes