anydi 0.40.0__py3-none-any.whl → 0.42.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.
anydi/_decorators.py ADDED
@@ -0,0 +1,80 @@
1
+ from collections.abc import Iterable
2
+ from typing import Callable, Concatenate, ParamSpec, TypedDict, TypeVar, overload
3
+
4
+ from ._module import Module
5
+ from ._scope import Scope
6
+
7
+ T = TypeVar("T")
8
+ P = ParamSpec("P")
9
+
10
+ ClassT = TypeVar("ClassT", bound=type)
11
+ ModuleT = TypeVar("ModuleT", bound=Module)
12
+
13
+
14
+ def provided(*, scope: Scope) -> Callable[[ClassT], ClassT]:
15
+ """Decorator for marking a class as provided by AnyDI with a specific scope."""
16
+
17
+ def decorator(cls: ClassT) -> ClassT:
18
+ cls.__provided__ = True
19
+ cls.__scope__ = scope
20
+ return cls
21
+
22
+ return decorator
23
+
24
+
25
+ # Scoped decorators for class-level providers
26
+ transient = provided(scope="transient")
27
+ request = provided(scope="request")
28
+ singleton = provided(scope="singleton")
29
+
30
+
31
+ class ProviderMetadata(TypedDict):
32
+ scope: Scope
33
+ override: bool
34
+
35
+
36
+ def provider(
37
+ *, scope: Scope, override: bool = False
38
+ ) -> Callable[
39
+ [Callable[Concatenate[ModuleT, P], T]], Callable[Concatenate[ModuleT, P], T]
40
+ ]:
41
+ """Decorator for marking a function or method as a provider in a AnyDI module."""
42
+
43
+ def decorator(
44
+ target: Callable[Concatenate[ModuleT, P], T],
45
+ ) -> Callable[Concatenate[ModuleT, P], T]:
46
+ target.__provider__ = ProviderMetadata(scope=scope, override=override) # type: ignore
47
+ return target
48
+
49
+ return decorator
50
+
51
+
52
+ class InjectableMetadata(TypedDict):
53
+ wrapped: bool
54
+ tags: Iterable[str] | None
55
+
56
+
57
+ @overload
58
+ def injectable(func: Callable[P, T]) -> Callable[P, T]: ...
59
+
60
+
61
+ @overload
62
+ def injectable(
63
+ *, tags: Iterable[str] | None = None
64
+ ) -> Callable[[Callable[P, T]], Callable[P, T]]: ...
65
+
66
+
67
+ def injectable(
68
+ func: Callable[P, T] | None = None,
69
+ tags: Iterable[str] | None = None,
70
+ ) -> Callable[[Callable[P, T]], Callable[P, T]] | Callable[P, T]:
71
+ """Decorator for marking a function or method as requiring dependency injection."""
72
+
73
+ def decorator(inner: Callable[P, T]) -> Callable[P, T]:
74
+ inner.__injectable__ = InjectableMetadata(wrapped=True, tags=tags) # type: ignore
75
+ return inner
76
+
77
+ if func is None:
78
+ return decorator
79
+
80
+ return decorator(func)
anydi/_module.py ADDED
@@ -0,0 +1,76 @@
1
+ from __future__ import annotations
2
+
3
+ import importlib
4
+ import inspect
5
+ from typing import TYPE_CHECKING, Any, Callable
6
+
7
+ if TYPE_CHECKING:
8
+ from ._container import Container
9
+ from ._decorators import ProviderMetadata
10
+
11
+
12
+ class ModuleMeta(type):
13
+ """A metaclass used for the Module base class."""
14
+
15
+ def __new__(cls, name: str, bases: tuple[type, ...], attrs: dict[str, Any]) -> Any:
16
+ attrs["providers"] = [
17
+ (name, getattr(value, "__provider__"))
18
+ for name, value in attrs.items()
19
+ if hasattr(value, "__provider__")
20
+ ]
21
+ return super().__new__(cls, name, bases, attrs)
22
+
23
+
24
+ class Module(metaclass=ModuleMeta):
25
+ """A base class for defining AnyDI modules."""
26
+
27
+ providers: list[tuple[str, ProviderMetadata]]
28
+
29
+ def configure(self, container: Container) -> None:
30
+ """Configure the AnyDI container with providers and their dependencies."""
31
+
32
+
33
+ ModuleDef = Module | type[Module] | Callable[["Container"], None] | str
34
+
35
+
36
+ class ModuleRegistrar:
37
+ def __init__(self, container: Container) -> None:
38
+ self._container = container
39
+
40
+ def register(self, module: ModuleDef) -> None:
41
+ """Register a module as a callable, module type, or module instance."""
42
+ # Callable Module
43
+ if inspect.isfunction(module):
44
+ module(self._container)
45
+ return
46
+
47
+ # Module path
48
+ if isinstance(module, str):
49
+ module = self.import_module_from_string(module)
50
+
51
+ # Class based Module or Module type
52
+ if inspect.isclass(module) and issubclass(module, Module):
53
+ module = module()
54
+
55
+ if isinstance(module, Module):
56
+ module.configure(self._container)
57
+ for provider_name, metadata in module.providers:
58
+ obj = getattr(module, provider_name)
59
+ self._container.provider(**metadata)(obj)
60
+ else:
61
+ raise TypeError(
62
+ "The module must be a callable, a module type, or a module instance."
63
+ )
64
+
65
+ @staticmethod
66
+ def import_module_from_string(dotted_path: str) -> Any:
67
+ """Import a module or attribute from a dotted path."""
68
+ try:
69
+ module_path, _, attribute_name = dotted_path.rpartition(".")
70
+ if module_path:
71
+ module = importlib.import_module(module_path)
72
+ return getattr(module, attribute_name)
73
+ else:
74
+ return importlib.import_module(attribute_name)
75
+ except (ImportError, AttributeError) as exc:
76
+ raise ImportError(f"Cannot import '{dotted_path}': {exc}") from exc
anydi/_provider.py ADDED
@@ -0,0 +1,81 @@
1
+ from __future__ import annotations
2
+
3
+ import enum
4
+ import inspect
5
+ from dataclasses import dataclass
6
+ from functools import cached_property
7
+ from typing import Any, Callable, NamedTuple
8
+
9
+ from ._scope import Scope
10
+ from ._typing import NOT_SET
11
+
12
+
13
+ class ProviderKind(enum.IntEnum):
14
+ CLASS = 1
15
+ FUNCTION = 2
16
+ COROUTINE = 3
17
+ GENERATOR = 4
18
+ ASYNC_GENERATOR = 5
19
+
20
+ @classmethod
21
+ def from_call(cls, call: Callable[..., Any]) -> ProviderKind:
22
+ if inspect.isclass(call):
23
+ return cls.CLASS
24
+ if inspect.iscoroutinefunction(call):
25
+ return cls.COROUTINE
26
+ if inspect.isasyncgenfunction(call):
27
+ return cls.ASYNC_GENERATOR
28
+ if inspect.isgeneratorfunction(call):
29
+ return cls.GENERATOR
30
+ if inspect.isfunction(call) or inspect.ismethod(call):
31
+ return cls.FUNCTION
32
+ raise TypeError(
33
+ f"The provider `{call}` is invalid because it is not a callable object."
34
+ )
35
+
36
+ @classmethod
37
+ def is_resource(cls, kind: ProviderKind) -> bool:
38
+ return kind in (cls.GENERATOR, cls.ASYNC_GENERATOR)
39
+
40
+
41
+ @dataclass(kw_only=True, frozen=True)
42
+ class Provider:
43
+ call: Callable[..., Any]
44
+ scope: Scope
45
+ interface: Any
46
+ name: str
47
+ parameters: list[inspect.Parameter]
48
+ kind: ProviderKind
49
+
50
+ def __str__(self) -> str:
51
+ return self.name
52
+
53
+ @cached_property
54
+ def is_class(self) -> bool:
55
+ return self.kind == ProviderKind.CLASS
56
+
57
+ @cached_property
58
+ def is_coroutine(self) -> bool:
59
+ return self.kind == ProviderKind.COROUTINE
60
+
61
+ @cached_property
62
+ def is_generator(self) -> bool:
63
+ return self.kind == ProviderKind.GENERATOR
64
+
65
+ @cached_property
66
+ def is_async_generator(self) -> bool:
67
+ return self.kind == ProviderKind.ASYNC_GENERATOR
68
+
69
+ @cached_property
70
+ def is_async(self) -> bool:
71
+ return self.is_coroutine or self.is_async_generator
72
+
73
+ @cached_property
74
+ def is_resource(self) -> bool:
75
+ return ProviderKind.is_resource(self.kind)
76
+
77
+
78
+ class ProviderDef(NamedTuple):
79
+ call: Callable[..., Any]
80
+ scope: Scope
81
+ interface: Any = NOT_SET
anydi/_scan.py ADDED
@@ -0,0 +1,110 @@
1
+ from __future__ import annotations
2
+
3
+ import importlib
4
+ import inspect
5
+ import pkgutil
6
+ from collections.abc import Iterable
7
+ from dataclasses import dataclass
8
+ from types import ModuleType
9
+ from typing import TYPE_CHECKING, Any, Union
10
+
11
+ from ._decorators import InjectableMetadata
12
+ from ._typing import get_typed_parameters, is_marker
13
+
14
+ if TYPE_CHECKING:
15
+ from ._container import Container
16
+
17
+ Package = Union[ModuleType, str]
18
+ PackageOrIterable = Union[Package, Iterable[Package]]
19
+
20
+
21
+ @dataclass(kw_only=True)
22
+ class ScannedDependency:
23
+ member: Any
24
+ module: ModuleType
25
+
26
+ def __post_init__(self) -> None:
27
+ # Unwrap decorated functions if necessary
28
+ if hasattr(self.member, "__wrapped__"):
29
+ self.member = self.member.__wrapped__
30
+
31
+
32
+ class Scanner:
33
+ def __init__(self, container: Container) -> None:
34
+ self._container = container
35
+
36
+ def scan(
37
+ self, /, packages: PackageOrIterable, *, tags: Iterable[str] | None = None
38
+ ) -> None:
39
+ """Scan packages or modules for decorated members and inject dependencies."""
40
+ if isinstance(packages, (ModuleType, str)):
41
+ scan_packages: Iterable[Package] = [packages]
42
+ else:
43
+ scan_packages = packages
44
+
45
+ dependencies = [
46
+ dependency
47
+ for package in scan_packages
48
+ for dependency in self._scan_package(package, tags=tags)
49
+ ]
50
+
51
+ for dependency in dependencies:
52
+ decorated = self._container.inject()(dependency.member)
53
+ setattr(dependency.module, dependency.member.__name__, decorated)
54
+
55
+ def _scan_package(
56
+ self, package: Package, *, tags: Iterable[str] | None = None
57
+ ) -> list[ScannedDependency]:
58
+ """Scan a package or module for decorated members."""
59
+ tags = list(tags) if tags else []
60
+
61
+ if isinstance(package, str):
62
+ package = importlib.import_module(package)
63
+
64
+ if not hasattr(package, "__path__"):
65
+ return self._scan_module(package, tags=tags)
66
+
67
+ dependencies: list[ScannedDependency] = []
68
+ for module_info in pkgutil.walk_packages(
69
+ package.__path__, prefix=package.__name__ + "."
70
+ ):
71
+ module = importlib.import_module(module_info.name)
72
+ dependencies.extend(self._scan_module(module, tags=tags))
73
+
74
+ return dependencies
75
+
76
+ @staticmethod
77
+ def _scan_module(
78
+ module: ModuleType, *, tags: Iterable[str]
79
+ ) -> list[ScannedDependency]:
80
+ """Scan a module for decorated members."""
81
+ dependencies: list[ScannedDependency] = []
82
+
83
+ for _, member in inspect.getmembers(module, predicate=callable):
84
+ if getattr(member, "__module__", None) != module.__name__:
85
+ continue
86
+
87
+ metadata: InjectableMetadata = getattr(
88
+ member,
89
+ "__injectable__",
90
+ InjectableMetadata(wrapped=False, tags=[]),
91
+ )
92
+
93
+ should_include = False
94
+ if metadata["wrapped"]:
95
+ should_include = True
96
+ elif tags and metadata["tags"]:
97
+ should_include = bool(set(metadata["tags"]) & set(tags))
98
+ elif tags and not metadata["tags"]:
99
+ continue # tags are provided but member has none
100
+
101
+ if not should_include:
102
+ for param in get_typed_parameters(member):
103
+ if is_marker(param.default):
104
+ should_include = True
105
+ break
106
+
107
+ if should_include:
108
+ dependencies.append(ScannedDependency(member=member, module=module))
109
+
110
+ return dependencies
anydi/_scope.py ADDED
@@ -0,0 +1,9 @@
1
+ from typing import Literal
2
+
3
+ Scope = Literal["transient", "singleton", "request"]
4
+
5
+ ALLOWED_SCOPES: dict[Scope, list[Scope]] = {
6
+ "singleton": ["singleton"],
7
+ "request": ["request", "singleton"],
8
+ "transient": ["transient", "request", "singleton"],
9
+ }
@@ -3,29 +3,22 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import builtins
6
- import functools
7
- import importlib
8
6
  import inspect
9
7
  import re
10
8
  import sys
11
9
  from collections.abc import AsyncIterator, Iterator
12
- from types import TracebackType
13
- from typing import Any, Callable, ForwardRef, TypeVar
10
+ from typing import Any, Callable, ForwardRef
14
11
 
15
- import anyio.to_thread
16
- from typing_extensions import ParamSpec, Self, get_args, get_origin
12
+ from typing_extensions import Self, get_args, get_origin
17
13
 
18
14
  try:
19
15
  from types import NoneType
20
16
  except ImportError:
21
- NoneType = type(None) # type: ignore[misc]
17
+ NoneType = type(None)
22
18
 
23
- T = TypeVar("T")
24
- P = ParamSpec("P")
25
19
 
26
-
27
- def get_full_qualname(obj: Any) -> str:
28
- """Get the fully qualified name of an object."""
20
+ def type_repr(obj: Any) -> str:
21
+ """Get a string representation of a type or object."""
29
22
  if isinstance(obj, str):
30
23
  return obj
31
24
 
@@ -36,8 +29,8 @@ def get_full_qualname(obj: Any) -> str:
36
29
  origin = get_origin(obj)
37
30
  # If origin exists, handle generics recursively
38
31
  if origin:
39
- args = ", ".join(get_full_qualname(arg) for arg in get_args(obj))
40
- return f"{get_full_qualname(origin)}[{args}]"
32
+ args = ", ".join(type_repr(arg) for arg in get_args(obj))
33
+ return f"{type_repr(origin)}[{args}]"
41
34
 
42
35
  # Substitute standard library prefixes for clarity
43
36
  full_qualname = f"{module}.{qualname}"
@@ -100,62 +93,49 @@ def get_typed_parameters(obj: Callable[..., Any]) -> list[inspect.Parameter]:
100
93
  ]
101
94
 
102
95
 
103
- async def run_async(
104
- func: Callable[P, T],
105
- /,
106
- *args: P.args,
107
- **kwargs: P.kwargs,
108
- ) -> T:
109
- """Runs the given function asynchronously using the `anyio` library."""
110
- return await anyio.to_thread.run_sync(functools.partial(func, *args, **kwargs))
111
-
112
-
113
- def import_string(dotted_path: str) -> Any:
114
- """
115
- Import a module or a specific attribute from a module using its dotted string path.
116
- """
117
- try:
118
- module_path, _, attribute_name = dotted_path.rpartition(".")
119
- if module_path:
120
- module = importlib.import_module(module_path)
121
- return getattr(module, attribute_name)
122
- else:
123
- return importlib.import_module(attribute_name)
124
- except (ImportError, AttributeError) as exc:
125
- raise ImportError(f"Cannot import '{dotted_path}': {exc}") from exc
126
-
96
+ class _Marker:
97
+ """A marker class for marking dependencies."""
127
98
 
128
- class AsyncRLock:
129
- def __init__(self) -> None:
130
- self._lock = anyio.Lock()
131
- self._owner: anyio.TaskInfo | None = None
132
- self._count = 0
99
+ __slots__ = ()
133
100
 
134
- async def acquire(self) -> None:
135
- current_task = anyio.get_current_task()
136
- if self._owner == current_task:
137
- self._count += 1
138
- else:
139
- await self._lock.acquire()
140
- self._owner = current_task
141
- self._count = 1
142
-
143
- def release(self) -> None:
144
- if self._owner != anyio.get_current_task():
145
- raise RuntimeError("Lock can only be released by the owner")
146
- self._count -= 1
147
- if self._count == 0:
148
- self._owner = None
149
- self._lock.release()
150
-
151
- async def __aenter__(self) -> Self:
152
- await self.acquire()
101
+ def __call__(self) -> Self:
153
102
  return self
154
103
 
155
- async def __aexit__(
156
- self,
157
- exc_type: type[BaseException] | None,
158
- exc_val: BaseException | None,
159
- exc_tb: TracebackType | None,
160
- ) -> Any:
161
- self.release()
104
+
105
+ def Marker() -> Any:
106
+ return _Marker()
107
+
108
+
109
+ def is_marker(obj: Any) -> bool:
110
+ """Checks if an object is a marker."""
111
+ return isinstance(obj, _Marker)
112
+
113
+
114
+ class Event:
115
+ """Represents an event object."""
116
+
117
+ __slots__ = ()
118
+
119
+
120
+ def is_event_type(obj: Any) -> bool:
121
+ """Checks if an object is an event type."""
122
+ return inspect.isclass(obj) and issubclass(obj, Event)
123
+
124
+
125
+ class _Sentinel:
126
+ __slots__ = ("_name",)
127
+
128
+ def __init__(self, name: str) -> None:
129
+ self._name = name
130
+
131
+ def __repr__(self) -> str:
132
+ return f"<{self._name}>"
133
+
134
+ def __eq__(self, other: object) -> bool:
135
+ return self is other
136
+
137
+ def __hash__(self) -> int:
138
+ return id(self)
139
+
140
+
141
+ NOT_SET = _Sentinel("NOT_SET")
anydi/ext/_utils.py CHANGED
@@ -8,8 +8,8 @@ from typing import Annotated, Any, Callable
8
8
 
9
9
  from typing_extensions import get_args, get_origin
10
10
 
11
- from anydi import Container
12
- from anydi._utils import get_full_qualname
11
+ from anydi._container import Container
12
+ from anydi._typing import type_repr
13
13
 
14
14
  logger = logging.getLogger(__name__)
15
15
 
@@ -71,9 +71,9 @@ def patch_call_parameter(
71
71
 
72
72
  if not container.strict and not container.is_registered(parameter.annotation):
73
73
  logger.debug(
74
- f"Callable `{get_full_qualname(call)}` injected parameter "
74
+ f"Callable `{type_repr(call)}` injected parameter "
75
75
  f"`{parameter.name}` with an annotation of "
76
- f"`{get_full_qualname(parameter.annotation)}` "
76
+ f"`{type_repr(parameter.annotation)}` "
77
77
  "is not registered. It will be registered at runtime with the "
78
78
  "first call because it is running in non-strict mode."
79
79
  )
@@ -75,7 +75,7 @@ def inject_urlpatterns(container: Container, *, urlconf: str) -> None:
75
75
  if pattern.lookup_str.startswith("ninja."):
76
76
  continue # pragma: no cover
77
77
  pattern.callback = container.inject(pattern.callback)
78
- pattern.callback._injected = True # type: ignore[attr-defined]
78
+ pattern.callback._injected = True # type: ignore
79
79
 
80
80
 
81
81
  def iter_urlpatterns(
@@ -120,9 +120,9 @@ def _patch_any_typed_annotated(container: Container, *, prefix: str) -> None:
120
120
 
121
121
  return wrapper
122
122
 
123
- container.resolve = _patch_resolve( # type: ignore[method-assign]
123
+ container.resolve = _patch_resolve( # type: ignore
124
124
  container.resolve
125
125
  )
126
- container.aresolve = _patch_aresolve( # type: ignore[method-assign]
126
+ container.aresolve = _patch_aresolve( # type: ignore
127
127
  container.aresolve
128
128
  )
anydi/ext/django/apps.py CHANGED
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- import logging
4
3
  import types
5
4
  from typing import Callable, cast
6
5
 
@@ -14,8 +13,6 @@ import anydi
14
13
  from ._settings import get_settings
15
14
  from ._utils import inject_urlpatterns, register_components, register_settings
16
15
 
17
- logger = logging.getLogger(__name__)
18
-
19
16
 
20
17
  class ContainerConfig(AppConfig):
21
18
  name = "anydi.ext.django"
@@ -11,6 +11,6 @@ from ._signature import ViewSignature
11
11
 
12
12
 
13
13
  def patch_ninja() -> None:
14
- operation.ViewSignature = ViewSignature # type: ignore[attr-defined]
15
- operation.Operation = Operation # type: ignore[misc]
16
- operation.AsyncOperation = AsyncOperation # type: ignore[misc]
14
+ operation.ViewSignature = ViewSignature # type: ignore
15
+ operation.Operation = Operation # type: ignore
16
+ operation.AsyncOperation = AsyncOperation # type: ignore
@@ -54,7 +54,7 @@ class AsyncOperation(BaseAsyncOperation):
54
54
  super().__init__(*args, **kwargs)
55
55
  self.dependencies = self.signature.dependencies
56
56
 
57
- async def run(self, request: HttpRequest, **kw: Any) -> HttpResponseBase: # type: ignore
57
+ async def run(self, request: HttpRequest, **kw: Any) -> HttpResponseBase:
58
58
  error = await self._run_checks(request)
59
59
  if error:
60
60
  return error
@@ -11,7 +11,7 @@ from ninja.signature.details import (
11
11
  )
12
12
  from ninja.signature.utils import get_path_param_names, get_typed_signature
13
13
 
14
- from anydi._types import is_marker # noqa
14
+ from anydi._typing import is_marker # noqa
15
15
 
16
16
 
17
17
  class ViewSignature(BaseViewSignature):
anydi/ext/fastapi.py CHANGED
@@ -10,8 +10,8 @@ from fastapi.dependencies.models import Dependant
10
10
  from fastapi.routing import APIRoute
11
11
  from starlette.requests import Request
12
12
 
13
- from anydi import Container
14
- from anydi._utils import get_typed_parameters
13
+ from anydi._container import Container
14
+ from anydi._typing import get_typed_parameters
15
15
 
16
16
  from ._utils import HasInterface, patch_call_parameter
17
17
  from .starlette.middleware import RequestScopedMiddleware
anydi/ext/faststream.py CHANGED
@@ -9,7 +9,7 @@ from faststream import ContextRepo
9
9
  from faststream.broker.core.usecase import BrokerUsecase
10
10
 
11
11
  from anydi import Container
12
- from anydi._utils import get_typed_parameters
12
+ from anydi._typing import get_typed_parameters
13
13
 
14
14
  from ._utils import HasInterface, patch_call_parameter
15
15
 
@@ -21,7 +21,7 @@ def install(broker: BrokerUsecase[Any, Any], container: Container) -> None:
21
21
  it to the broker. It also patches the broker handlers to inject the required
22
22
  dependencies using AnyDI.
23
23
  """
24
- broker._container = container # type: ignore[attr-defined]
24
+ broker._container = container # type: ignore
25
25
 
26
26
  for handler in _get_broken_handlers(broker):
27
27
  call = handler._original_call # noqa
@@ -6,11 +6,10 @@ from collections.abc import Iterator
6
6
  from typing import Any, Callable, cast
7
7
 
8
8
  import pytest
9
- from _pytest.python import async_warn_and_skip
10
9
  from anyio.pytest_plugin import extract_backend_and_options, get_runner
11
10
 
12
11
  from anydi import Container
13
- from anydi._utils import get_typed_parameters
12
+ from anydi._typing import get_typed_parameters
14
13
 
15
14
  logger = logging.getLogger(__name__)
16
15
 
@@ -117,7 +116,12 @@ def _anydi_ainject(
117
116
 
118
117
  # Skip if the anyio backend is not available
119
118
  if "anyio_backend" not in request.fixturenames:
120
- async_warn_and_skip(request.node.nodeid)
119
+ msg = (
120
+ "To run async test functions with `anyio`, "
121
+ "please configure the `anyio` pytest plugin.\n"
122
+ "See: https://anyio.readthedocs.io/en/stable/testing.html"
123
+ )
124
+ pytest.fail(msg, pytrace=False)
121
125
 
122
126
  async def _awrapper() -> None:
123
127
  # Setup the container
@@ -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 import Container
8
+ from anydi._container import Container
9
9
 
10
10
 
11
11
  class RequestScopedMiddleware(BaseHTTPMiddleware):