anydi 0.39.0__tar.gz → 0.39.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.
- {anydi-0.39.0 → anydi-0.39.1}/PKG-INFO +2 -2
- {anydi-0.39.0 → anydi-0.39.1}/README.md +1 -1
- {anydi-0.39.0 → anydi-0.39.1}/anydi/_container.py +19 -44
- {anydi-0.39.0 → anydi-0.39.1}/anydi/_context.py +13 -2
- {anydi-0.39.0 → anydi-0.39.1}/anydi/_types.py +4 -5
- {anydi-0.39.0 → anydi-0.39.1}/anydi/_utils.py +16 -0
- {anydi-0.39.0 → anydi-0.39.1}/docs/usage.md +11 -10
- {anydi-0.39.0 → anydi-0.39.1}/pyproject.toml +2 -2
- {anydi-0.39.0 → anydi-0.39.1}/tests/fixtures.py +7 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/test_container.py +271 -232
- {anydi-0.39.0 → anydi-0.39.1}/uv.lock +1 -1
- {anydi-0.39.0 → anydi-0.39.1}/.editorconfig +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/.github/workflows/ci.yml +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/.gitignore +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/.readthedocs.yaml +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/LICENSE +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/Makefile +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/anydi/__init__.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/anydi/ext/__init__.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/anydi/ext/_utils.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/anydi/ext/django/__init__.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/anydi/ext/django/_container.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/anydi/ext/django/_settings.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/anydi/ext/django/_utils.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/anydi/ext/django/apps.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/anydi/ext/django/middleware.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/anydi/ext/django/ninja/__init__.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/anydi/ext/django/ninja/_operation.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/anydi/ext/django/ninja/_signature.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/anydi/ext/fastapi.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/anydi/ext/faststream.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/anydi/ext/pydantic_settings.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/anydi/ext/pytest_plugin.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/anydi/ext/starlette/__init__.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/anydi/ext/starlette/middleware.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/anydi/py.typed +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/docs/examples/basic.md +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/docs/extensions/django.md +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/docs/extensions/fastapi.md +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/docs/extensions/faststream.md +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/docs/extensions/pydantic_settings.md +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/docs/index.md +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/mkdocs.yml +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/__init__.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/conftest.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/ext/__init__.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/ext/django/__init__.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/ext/django/api/__init__.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/ext/django/api/router.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/ext/django/api/test_router.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/ext/django/api/urls.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/ext/django/conftest.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/ext/django/container.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/ext/django/scan/__init__.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/ext/django/services.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/ext/django/settings.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/ext/django/test_views.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/ext/django/urls.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/ext/django/views.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/ext/fastapi/__init__.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/ext/fastapi/app.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/ext/fastapi/conftest.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/ext/fastapi/test_auto_register.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/ext/fastapi/test_ext.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/ext/fastapi/test_routes.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/ext/faststream/__init__.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/ext/faststream/test_ext.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/ext/faststream/test_subscribers.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/ext/fixtures.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/ext/starlette/__init__.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/ext/starlette/app.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/ext/starlette/conftest.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/ext/starlette/test_routes.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/ext/test_pydantic.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/ext/test_pytest_plugin.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/scan_app/__init__.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/scan_app/a/__init__.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/scan_app/a/a1/__init__.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/scan_app/a/a1/handlers.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/scan_app/a/a2/__init__.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/scan_app/a/a2/a21/__init__.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/scan_app/a/a2/a21/handlers.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/scan_app/a/a3/__init__.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/scan_app/a/a3/handlers.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/scan_app/b/__init__.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/scan_app/b/handlers.py +0 -0
- {anydi-0.39.0 → anydi-0.39.1}/tests/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: anydi
|
|
3
|
-
Version: 0.39.
|
|
3
|
+
Version: 0.39.1
|
|
4
4
|
Summary: Dependency Injection library
|
|
5
5
|
Project-URL: Repository, https://github.com/antonrh/anydi
|
|
6
6
|
Author-email: Anton Ruhlov <antonruhlov@gmail.com>
|
|
@@ -91,7 +91,7 @@ container = Container()
|
|
|
91
91
|
|
|
92
92
|
@container.provider(scope="singleton")
|
|
93
93
|
def message() -> str:
|
|
94
|
-
return "Hello,
|
|
94
|
+
return "Hello, World!"
|
|
95
95
|
|
|
96
96
|
|
|
97
97
|
@container.inject
|
|
@@ -8,7 +8,6 @@ import importlib
|
|
|
8
8
|
import inspect
|
|
9
9
|
import logging
|
|
10
10
|
import pkgutil
|
|
11
|
-
import threading
|
|
12
11
|
import types
|
|
13
12
|
import uuid
|
|
14
13
|
from collections import defaultdict
|
|
@@ -36,7 +35,6 @@ from ._types import (
|
|
|
36
35
|
is_marker,
|
|
37
36
|
)
|
|
38
37
|
from ._utils import (
|
|
39
|
-
AsyncRLock,
|
|
40
38
|
get_full_qualname,
|
|
41
39
|
get_typed_annotation,
|
|
42
40
|
get_typed_parameters,
|
|
@@ -44,15 +42,11 @@ from ._utils import (
|
|
|
44
42
|
is_async_context_manager,
|
|
45
43
|
is_builtin_type,
|
|
46
44
|
is_context_manager,
|
|
45
|
+
is_iterator_type,
|
|
46
|
+
is_none_type,
|
|
47
47
|
run_async,
|
|
48
48
|
)
|
|
49
49
|
|
|
50
|
-
try:
|
|
51
|
-
from types import NoneType
|
|
52
|
-
except ImportError:
|
|
53
|
-
NoneType = type(None) # type: ignore[misc]
|
|
54
|
-
|
|
55
|
-
|
|
56
50
|
T = TypeVar("T", bound=Any)
|
|
57
51
|
M = TypeVar("M", bound="Module")
|
|
58
52
|
P = ParamSpec("P")
|
|
@@ -107,8 +101,6 @@ class Container:
|
|
|
107
101
|
self._logger = logger or logging.getLogger(__name__)
|
|
108
102
|
self._resources: dict[str, list[type[Any]]] = defaultdict(list)
|
|
109
103
|
self._singleton_context = InstanceContext()
|
|
110
|
-
self._singleton_lock = threading.RLock()
|
|
111
|
-
self._singleton_async_lock = AsyncRLock()
|
|
112
104
|
self._request_context_var: ContextVar[InstanceContext | None] = ContextVar(
|
|
113
105
|
"request_context", default=None
|
|
114
106
|
)
|
|
@@ -254,7 +246,7 @@ class Container:
|
|
|
254
246
|
)
|
|
255
247
|
return request_context
|
|
256
248
|
|
|
257
|
-
def
|
|
249
|
+
def _get_instance_context(self, scope: Scope) -> InstanceContext:
|
|
258
250
|
"""Get the instance context for the specified scope."""
|
|
259
251
|
if scope == "singleton":
|
|
260
252
|
return self._singleton_context
|
|
@@ -292,7 +284,7 @@ class Container:
|
|
|
292
284
|
# Cleanup instance context
|
|
293
285
|
if provider.scope != "transient":
|
|
294
286
|
try:
|
|
295
|
-
context = self.
|
|
287
|
+
context = self._get_instance_context(provider.scope)
|
|
296
288
|
except LookupError:
|
|
297
289
|
pass
|
|
298
290
|
else:
|
|
@@ -343,16 +335,16 @@ class Container:
|
|
|
343
335
|
interface = signature.return_annotation
|
|
344
336
|
if interface is inspect.Signature.empty:
|
|
345
337
|
interface = None
|
|
346
|
-
|
|
347
|
-
|
|
338
|
+
|
|
339
|
+
if isinstance(interface, str):
|
|
340
|
+
interface = get_typed_annotation(interface, globalns, module)
|
|
348
341
|
|
|
349
342
|
# If the callable is an iterator, return the actual type
|
|
350
|
-
|
|
351
|
-
if interface in iterator_types or get_origin(interface) in iterator_types:
|
|
343
|
+
if is_iterator_type(interface) or is_iterator_type(get_origin(interface)):
|
|
352
344
|
if args := get_args(interface):
|
|
353
345
|
interface = args[0]
|
|
354
346
|
# If the callable is a generator, return the resource type
|
|
355
|
-
if interface
|
|
347
|
+
if is_none_type(interface):
|
|
356
348
|
interface = type(f"Event_{uuid.uuid4().hex}", (Event,), {})
|
|
357
349
|
else:
|
|
358
350
|
raise TypeError(
|
|
@@ -361,7 +353,7 @@ class Container:
|
|
|
361
353
|
)
|
|
362
354
|
|
|
363
355
|
# None interface is not allowed
|
|
364
|
-
if interface
|
|
356
|
+
if is_none_type(interface):
|
|
365
357
|
raise TypeError(f"Missing `{name}` provider return annotation.")
|
|
366
358
|
|
|
367
359
|
# Check for existing provider
|
|
@@ -424,7 +416,7 @@ class Container:
|
|
|
424
416
|
|
|
425
417
|
# Check for unresolved parameters
|
|
426
418
|
if unresolved_parameter:
|
|
427
|
-
if detected_scope not in
|
|
419
|
+
if detected_scope not in ("singleton", "transient"):
|
|
428
420
|
self._unresolved_interfaces.add(interface)
|
|
429
421
|
else:
|
|
430
422
|
raise LookupError(
|
|
@@ -466,10 +458,7 @@ class Container:
|
|
|
466
458
|
f"scopes are supported: {', '.join(allowed_scopes)}. "
|
|
467
459
|
"Please use one of the supported scopes when registering a provider."
|
|
468
460
|
)
|
|
469
|
-
if (
|
|
470
|
-
kind in {ProviderKind.GENERATOR, ProviderKind.ASYNC_GENERATOR}
|
|
471
|
-
and scope == "transient"
|
|
472
|
-
):
|
|
461
|
+
if scope == "transient" and ProviderKind.is_resource(kind):
|
|
473
462
|
raise TypeError(
|
|
474
463
|
f"The resource provider `{name}` is attempting to register "
|
|
475
464
|
"with a transient scope, which is not allowed."
|
|
@@ -565,7 +554,7 @@ class Container:
|
|
|
565
554
|
return False
|
|
566
555
|
if provider.scope == "transient":
|
|
567
556
|
return False
|
|
568
|
-
context = self.
|
|
557
|
+
context = self._get_instance_context(provider.scope)
|
|
569
558
|
return interface in context
|
|
570
559
|
|
|
571
560
|
def release(self, interface: AnyInterface) -> None:
|
|
@@ -573,7 +562,7 @@ class Container:
|
|
|
573
562
|
provider = self._get_provider(interface)
|
|
574
563
|
if provider.scope == "transient":
|
|
575
564
|
return None
|
|
576
|
-
context = self.
|
|
565
|
+
context = self._get_instance_context(provider.scope)
|
|
577
566
|
del context[interface]
|
|
578
567
|
|
|
579
568
|
def reset(self) -> None:
|
|
@@ -582,7 +571,7 @@ class Container:
|
|
|
582
571
|
if provider.scope == "transient":
|
|
583
572
|
continue
|
|
584
573
|
try:
|
|
585
|
-
context = self.
|
|
574
|
+
context = self._get_instance_context(provider.scope)
|
|
586
575
|
except LookupError:
|
|
587
576
|
continue
|
|
588
577
|
del context[interface]
|
|
@@ -595,15 +584,8 @@ class Container:
|
|
|
595
584
|
if provider.scope == "transient":
|
|
596
585
|
instance = self._create_instance(provider, None, **defaults)
|
|
597
586
|
else:
|
|
598
|
-
context = self.
|
|
599
|
-
|
|
600
|
-
with self._singleton_lock:
|
|
601
|
-
instance = (
|
|
602
|
-
self._get_or_create_instance(provider, context)
|
|
603
|
-
if not create
|
|
604
|
-
else self._create_instance(provider, context, **defaults)
|
|
605
|
-
)
|
|
606
|
-
else:
|
|
587
|
+
context = self._get_instance_context(provider.scope)
|
|
588
|
+
with context.lock():
|
|
607
589
|
instance = (
|
|
608
590
|
self._get_or_create_instance(provider, context)
|
|
609
591
|
if not create
|
|
@@ -623,15 +605,8 @@ class Container:
|
|
|
623
605
|
if provider.scope == "transient":
|
|
624
606
|
instance = await self._acreate_instance(provider, None, **defaults)
|
|
625
607
|
else:
|
|
626
|
-
context = self.
|
|
627
|
-
|
|
628
|
-
async with self._singleton_async_lock:
|
|
629
|
-
instance = (
|
|
630
|
-
await self._aget_or_create_instance(provider, context)
|
|
631
|
-
if not create
|
|
632
|
-
else await self._acreate_instance(provider, context, **defaults)
|
|
633
|
-
)
|
|
634
|
-
else:
|
|
608
|
+
context = self._get_instance_context(provider.scope)
|
|
609
|
+
async with context.alock():
|
|
635
610
|
instance = (
|
|
636
611
|
await self._aget_or_create_instance(provider, context)
|
|
637
612
|
if not create
|
|
@@ -1,23 +1,26 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import contextlib
|
|
4
|
+
import threading
|
|
4
5
|
from types import TracebackType
|
|
5
6
|
from typing import Any
|
|
6
7
|
|
|
7
8
|
from typing_extensions import Self
|
|
8
9
|
|
|
9
|
-
from ._utils import run_async
|
|
10
|
+
from ._utils import AsyncRLock, run_async
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
class InstanceContext:
|
|
13
14
|
"""A context to store instances."""
|
|
14
15
|
|
|
15
|
-
__slots__ = ("_instances", "_stack", "_async_stack")
|
|
16
|
+
__slots__ = ("_instances", "_stack", "_async_stack", "_lock", "_async_lock")
|
|
16
17
|
|
|
17
18
|
def __init__(self) -> None:
|
|
18
19
|
self._instances: dict[type[Any], Any] = {}
|
|
19
20
|
self._stack = contextlib.ExitStack()
|
|
20
21
|
self._async_stack = contextlib.AsyncExitStack()
|
|
22
|
+
self._lock = threading.RLock()
|
|
23
|
+
self._async_lock = AsyncRLock()
|
|
21
24
|
|
|
22
25
|
def get(self, interface: type[Any]) -> Any | None:
|
|
23
26
|
"""Get an instance from the context."""
|
|
@@ -82,3 +85,11 @@ class InstanceContext:
|
|
|
82
85
|
async def aclose(self) -> None:
|
|
83
86
|
"""Close the scoped context asynchronously."""
|
|
84
87
|
await self.__aexit__(None, None, None)
|
|
88
|
+
|
|
89
|
+
def lock(self) -> threading.RLock:
|
|
90
|
+
"""Acquire the context lock."""
|
|
91
|
+
return self._lock
|
|
92
|
+
|
|
93
|
+
def alock(self) -> AsyncRLock:
|
|
94
|
+
"""Acquire the context lock asynchronously."""
|
|
95
|
+
return self._async_lock
|
|
@@ -82,10 +82,9 @@ class ProviderKind(enum.IntEnum):
|
|
|
82
82
|
"object. Only callable providers are allowed."
|
|
83
83
|
)
|
|
84
84
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
pass
|
|
85
|
+
@classmethod
|
|
86
|
+
def is_resource(cls, kind: ProviderKind) -> bool:
|
|
87
|
+
return kind in (cls.GENERATOR, cls.ASYNC_GENERATOR)
|
|
89
88
|
|
|
90
89
|
|
|
91
90
|
@dataclass(kw_only=True, frozen=True)
|
|
@@ -122,7 +121,7 @@ class Provider:
|
|
|
122
121
|
|
|
123
122
|
@cached_property
|
|
124
123
|
def is_resource(self) -> bool:
|
|
125
|
-
return
|
|
124
|
+
return ProviderKind.is_resource(self.kind)
|
|
126
125
|
|
|
127
126
|
|
|
128
127
|
class ProviderArgs(NamedTuple):
|
|
@@ -8,12 +8,18 @@ import importlib
|
|
|
8
8
|
import inspect
|
|
9
9
|
import re
|
|
10
10
|
import sys
|
|
11
|
+
from collections.abc import AsyncIterator, Iterator
|
|
11
12
|
from types import TracebackType
|
|
12
13
|
from typing import Any, Callable, ForwardRef, TypeVar
|
|
13
14
|
|
|
14
15
|
import anyio
|
|
15
16
|
from typing_extensions import ParamSpec, Self, get_args, get_origin
|
|
16
17
|
|
|
18
|
+
try:
|
|
19
|
+
from types import NoneType
|
|
20
|
+
except ImportError:
|
|
21
|
+
NoneType = type(None) # type: ignore[misc]
|
|
22
|
+
|
|
17
23
|
T = TypeVar("T")
|
|
18
24
|
P = ParamSpec("P")
|
|
19
25
|
|
|
@@ -57,6 +63,16 @@ def is_async_context_manager(obj: Any) -> bool:
|
|
|
57
63
|
return hasattr(obj, "__aenter__") and hasattr(obj, "__aexit__")
|
|
58
64
|
|
|
59
65
|
|
|
66
|
+
def is_none_type(tp: Any) -> bool:
|
|
67
|
+
"""Check if the given object is a None type."""
|
|
68
|
+
return tp in (None, NoneType)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def is_iterator_type(tp: Any) -> bool:
|
|
72
|
+
"""Check if the given object is an iterator type."""
|
|
73
|
+
return tp in (Iterator, AsyncIterator)
|
|
74
|
+
|
|
75
|
+
|
|
60
76
|
def get_typed_annotation(
|
|
61
77
|
annotation: Any, globalns: dict[str, Any], module: Any = None
|
|
62
78
|
) -> Any:
|
|
@@ -8,7 +8,7 @@ Once a provider is registered with `Container`, it can be used to resolve depend
|
|
|
8
8
|
### Registering Providers
|
|
9
9
|
|
|
10
10
|
To register a provider, you can use the `register` method of the `Container` instance. The method takes
|
|
11
|
-
three arguments: the type of the object to be provided, the provider function or class, and
|
|
11
|
+
three arguments: the type of the object to be provided, the provider function or class, and a scope.
|
|
12
12
|
|
|
13
13
|
```python
|
|
14
14
|
from anydi import Container
|
|
@@ -17,12 +17,12 @@ container = Container()
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
def message() -> str:
|
|
20
|
-
return "Hello,
|
|
20
|
+
return "Hello, World!"
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
container.register(str, message, scope="singleton")
|
|
24
24
|
|
|
25
|
-
assert container.resolve(str) == "Hello,
|
|
25
|
+
assert container.resolve(str) == "Hello, World!"
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
Alternatively, you can use the `@provider` decorator to register a provider function. The decorator takes care of registering the provider with `Container`.
|
|
@@ -35,10 +35,10 @@ container = Container()
|
|
|
35
35
|
|
|
36
36
|
@container.provider(scope="singleton")
|
|
37
37
|
def message() -> str:
|
|
38
|
-
return "Hello,
|
|
38
|
+
return "Hello, World!"
|
|
39
39
|
|
|
40
40
|
|
|
41
|
-
assert container.resolve(str) == "Hello,
|
|
41
|
+
assert container.resolve(str) == "Hello, World!"
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
### Annotated Providers
|
|
@@ -82,7 +82,7 @@ container = Container()
|
|
|
82
82
|
|
|
83
83
|
@container.provider(scope="singleton")
|
|
84
84
|
def message() -> str:
|
|
85
|
-
return "Hello,
|
|
85
|
+
return "Hello, World!"
|
|
86
86
|
|
|
87
87
|
|
|
88
88
|
assert container.is_registered(str)
|
|
@@ -105,13 +105,13 @@ container = Container()
|
|
|
105
105
|
|
|
106
106
|
@container.provider(scope="singleton")
|
|
107
107
|
def message() -> str:
|
|
108
|
-
return "Hello,
|
|
108
|
+
return "Hello, World!"
|
|
109
109
|
|
|
110
110
|
|
|
111
111
|
# Check if an instance is resolved
|
|
112
112
|
assert not container.is_resolved(str)
|
|
113
113
|
|
|
114
|
-
assert container.resolve(str) == "Hello,
|
|
114
|
+
assert container.resolve(str) == "Hello, World!"
|
|
115
115
|
|
|
116
116
|
assert container.is_resolved(str)
|
|
117
117
|
|
|
@@ -126,7 +126,7 @@ To release a provider instance, you can use the `release` method of the `Contain
|
|
|
126
126
|
from anydi import Container
|
|
127
127
|
|
|
128
128
|
container = Container()
|
|
129
|
-
container.register(str, lambda: "Hello,
|
|
129
|
+
container.register(str, lambda: "Hello, World!", scope="singleton")
|
|
130
130
|
container.register(int, lambda: 100, scope="singleton")
|
|
131
131
|
|
|
132
132
|
container.resolve(str)
|
|
@@ -165,6 +165,7 @@ class Database:
|
|
|
165
165
|
class Repository:
|
|
166
166
|
db: Database
|
|
167
167
|
|
|
168
|
+
|
|
168
169
|
@dataclass
|
|
169
170
|
class Service:
|
|
170
171
|
repo: Repository
|
|
@@ -445,7 +446,7 @@ async def resource_provider() -> AsyncIterator[Resource]:
|
|
|
445
446
|
async def main() -> None:
|
|
446
447
|
await container.astart() # start resources
|
|
447
448
|
|
|
448
|
-
assert (await container.
|
|
449
|
+
assert (await container.aresolve(Resource)).name == "demo"
|
|
449
450
|
|
|
450
451
|
await container.aclose() # close resources
|
|
451
452
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "anydi"
|
|
3
|
-
version = "0.39.
|
|
3
|
+
version = "0.39.1"
|
|
4
4
|
description = "Dependency Injection library"
|
|
5
5
|
authors = [{ name = "Anton Ruhlov", email = "antonruhlov@gmail.com" }]
|
|
6
6
|
requires-python = "~=3.9"
|
|
@@ -136,7 +136,7 @@ omit = [
|
|
|
136
136
|
]
|
|
137
137
|
|
|
138
138
|
[tool.bumpversion]
|
|
139
|
-
current_version = "0.39.
|
|
139
|
+
current_version = "0.39.1"
|
|
140
140
|
parse = """(?x)
|
|
141
141
|
(?P<major>0|[1-9]\\d*)\\.
|
|
142
142
|
(?P<minor>0|[1-9]\\d*)\\.
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import uuid
|
|
1
2
|
from collections.abc import AsyncIterator, Iterator
|
|
3
|
+
from dataclasses import dataclass, field
|
|
2
4
|
from typing import Annotated
|
|
3
5
|
|
|
4
6
|
from anydi import Container, Module, provider
|
|
@@ -36,6 +38,11 @@ def iterator() -> Iterator: # type: ignore[type-arg]
|
|
|
36
38
|
yield
|
|
37
39
|
|
|
38
40
|
|
|
41
|
+
@dataclass(frozen=True)
|
|
42
|
+
class UniqueId:
|
|
43
|
+
id: uuid.UUID = field(default_factory=uuid.uuid4)
|
|
44
|
+
|
|
45
|
+
|
|
39
46
|
class Service:
|
|
40
47
|
def __init__(self, ident: str) -> None:
|
|
41
48
|
self.ident = ident
|