mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2025-10-09 21:36:41 +03:00
Merge branch 'release/4.48.2'
This commit is contained in:
commit
5a1aef9203
7
Makefile
7
Makefile
|
@ -1,6 +1,10 @@
|
||||||
VERSION := $(shell python setup.py --version)
|
VERSION := $(shell python setup.py --version)
|
||||||
|
|
||||||
export COVERAGE_RCFILE := pyproject.toml
|
export COVERAGE_RCFILE := pyproject.toml
|
||||||
|
export CIBW_ENVIRONMENT_PASS_LINUX := CFLAGS PIP_CONFIG_SETTINGS DEPENDENCY_INJECTOR_LIMITED_API
|
||||||
|
export PIP_CONFIG_SETTINGS ?= build_ext=-j4
|
||||||
|
export DEPENDENCY_INJECTOR_LIMITED_API ?= 1
|
||||||
|
export CFLAGS ?= -g0
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
# Clean sources
|
# Clean sources
|
||||||
|
@ -63,3 +67,6 @@ publish:
|
||||||
# Create and upload tag
|
# Create and upload tag
|
||||||
git tag -a $(VERSION) -m 'version $(VERSION)'
|
git tag -a $(VERSION) -m 'version $(VERSION)'
|
||||||
git push --tags
|
git push --tags
|
||||||
|
|
||||||
|
wheels:
|
||||||
|
cibuildwheel --output-dir wheelhouse
|
||||||
|
|
|
@ -7,6 +7,13 @@ that were made in every particular version.
|
||||||
From version 0.7.6 *Dependency Injector* framework strictly
|
From version 0.7.6 *Dependency Injector* framework strictly
|
||||||
follows `Semantic versioning`_
|
follows `Semantic versioning`_
|
||||||
|
|
||||||
|
4.48.2
|
||||||
|
------
|
||||||
|
|
||||||
|
- Add ``warn_unresolved=True`` to ``WiringConfiguration`` and ``container.wire()``
|
||||||
|
to produce warnings on unresolved string identifiers.
|
||||||
|
- ABI3 wheels are now built only for CPython version >=3.10 (see issue `#919 <https://github.com/ets-labs/python-dependency-injector/issues/919>`_).
|
||||||
|
|
||||||
4.48.1
|
4.48.1
|
||||||
------
|
------
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ factories:
|
||||||
- :ref:`factory-specialize-provided-type`
|
- :ref:`factory-specialize-provided-type`
|
||||||
- :ref:`abstract-factory`
|
- :ref:`abstract-factory`
|
||||||
|
|
||||||
``Singleton`` provider scope is tied to the container. Two different containers will provider
|
``Singleton`` provider scope is tied to the container. Two different containers will provide
|
||||||
two different singleton objects:
|
two different singleton objects:
|
||||||
|
|
||||||
.. literalinclude:: ../../examples/providers/singleton_multiple_containers.py
|
.. literalinclude:: ../../examples/providers/singleton_multiple_containers.py
|
||||||
|
|
|
@ -251,6 +251,32 @@ To inject a container use special identifier ``<container>``:
|
||||||
def foo(container: Container = Provide["<container>"]) -> None:
|
def foo(container: Container = Provide["<container>"]) -> None:
|
||||||
...
|
...
|
||||||
|
|
||||||
|
Caveats
|
||||||
|
~~~~~~~
|
||||||
|
|
||||||
|
While using string identifiers you may not notice a typo in the identifier until the code is executed.
|
||||||
|
In order to aid with catching such errors early, you may pass `warn_unresolved=True` to the ``wire`` method and/or :class:`WiringConfiguration`:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:emphasize-lines: 4
|
||||||
|
|
||||||
|
class Container(containers.DeclarativeContainer):
|
||||||
|
wiring_config = containers.WiringConfiguration(
|
||||||
|
modules=["yourapp.module"],
|
||||||
|
warn_unresolved=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
Or:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:emphasize-lines: 4
|
||||||
|
|
||||||
|
container = Container()
|
||||||
|
container.wire(
|
||||||
|
modules=["yourapp.module"],
|
||||||
|
warn_unresolved=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
Making injections into modules and class attributes
|
Making injections into modules and class attributes
|
||||||
---------------------------------------------------
|
---------------------------------------------------
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["setuptools", "Cython>=3.1.1"]
|
requires = ["setuptools", "Cython>=3.1.4"]
|
||||||
build-backend = "setuptools.build_meta"
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
|
@ -54,7 +54,7 @@ classifiers = [
|
||||||
dynamic = ["version"]
|
dynamic = ["version"]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
# typing.Annotated since v3.9
|
# typing.Annotated since v3.9
|
||||||
# typing.Self since v3.11
|
# typing.Self and typing.assert_never since v3.11
|
||||||
"typing-extensions; python_version<'3.11'",
|
"typing-extensions; python_version<'3.11'",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
cython==3.1.1
|
cython==3.1.4
|
||||||
setuptools
|
setuptools
|
||||||
pytest
|
pytest
|
||||||
pytest-asyncio
|
pytest-asyncio
|
||||||
|
|
7
setup.py
7
setup.py
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import sysconfig
|
||||||
|
|
||||||
from Cython.Build import cythonize
|
from Cython.Build import cythonize
|
||||||
from Cython.Compiler import Options
|
from Cython.Compiler import Options
|
||||||
|
@ -11,6 +12,8 @@ debug = os.environ.get("DEPENDENCY_INJECTOR_DEBUG_MODE") == "1"
|
||||||
limited_api = (
|
limited_api = (
|
||||||
os.environ.get("DEPENDENCY_INJECTOR_LIMITED_API") == "1"
|
os.environ.get("DEPENDENCY_INJECTOR_LIMITED_API") == "1"
|
||||||
and sys.implementation.name == "cpython"
|
and sys.implementation.name == "cpython"
|
||||||
|
and sys.version_info >= (3, 10)
|
||||||
|
and not sysconfig.get_config_var("Py_GIL_DISABLED")
|
||||||
)
|
)
|
||||||
defined_macros = []
|
defined_macros = []
|
||||||
options = {}
|
options = {}
|
||||||
|
@ -34,8 +37,8 @@ if debug:
|
||||||
|
|
||||||
if limited_api:
|
if limited_api:
|
||||||
options.setdefault("bdist_wheel", {})
|
options.setdefault("bdist_wheel", {})
|
||||||
options["bdist_wheel"]["py_limited_api"] = "cp38"
|
options["bdist_wheel"]["py_limited_api"] = "cp310"
|
||||||
defined_macros.append(("Py_LIMITED_API", "0x03080000"))
|
defined_macros.append(("Py_LIMITED_API", "0x030A0000"))
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
options=options,
|
options=options,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
"""Top-level package."""
|
"""Top-level package."""
|
||||||
|
|
||||||
__version__ = "4.48.1"
|
__version__ = "4.48.2"
|
||||||
"""Version number.
|
"""Version number.
|
||||||
|
|
||||||
:type: str
|
:type: str
|
||||||
|
|
|
@ -72,6 +72,7 @@ class Container:
|
||||||
modules: Optional[Iterable[Any]] = None,
|
modules: Optional[Iterable[Any]] = None,
|
||||||
packages: Optional[Iterable[Any]] = None,
|
packages: Optional[Iterable[Any]] = None,
|
||||||
from_package: Optional[str] = None,
|
from_package: Optional[str] = None,
|
||||||
|
warn_unresolved: bool = False,
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def unwire(self) -> None: ...
|
def unwire(self) -> None: ...
|
||||||
def init_resources(self, resource_type: Type[Resource[Any]] = Resource) -> Optional[Awaitable[None]]: ...
|
def init_resources(self, resource_type: Type[Resource[Any]] = Resource) -> Optional[Awaitable[None]]: ...
|
||||||
|
|
|
@ -20,15 +20,31 @@ from .wiring import wire, unwire
|
||||||
class WiringConfiguration:
|
class WiringConfiguration:
|
||||||
"""Container wiring configuration."""
|
"""Container wiring configuration."""
|
||||||
|
|
||||||
def __init__(self, modules=None, packages=None, from_package=None, auto_wire=True, keep_cache=False):
|
def __init__(
|
||||||
|
self,
|
||||||
|
modules=None,
|
||||||
|
packages=None,
|
||||||
|
from_package=None,
|
||||||
|
auto_wire=True,
|
||||||
|
keep_cache=False,
|
||||||
|
warn_unresolved=False,
|
||||||
|
):
|
||||||
self.modules = [*modules] if modules else []
|
self.modules = [*modules] if modules else []
|
||||||
self.packages = [*packages] if packages else []
|
self.packages = [*packages] if packages else []
|
||||||
self.from_package = from_package
|
self.from_package = from_package
|
||||||
self.auto_wire = auto_wire
|
self.auto_wire = auto_wire
|
||||||
self.keep_cache = keep_cache
|
self.keep_cache = keep_cache
|
||||||
|
self.warn_unresolved = warn_unresolved
|
||||||
|
|
||||||
def __deepcopy__(self, memo=None):
|
def __deepcopy__(self, memo=None):
|
||||||
return self.__class__(self.modules, self.packages, self.from_package, self.auto_wire, self.keep_cache)
|
return self.__class__(
|
||||||
|
self.modules,
|
||||||
|
self.packages,
|
||||||
|
self.from_package,
|
||||||
|
self.auto_wire,
|
||||||
|
self.keep_cache,
|
||||||
|
self.warn_unresolved,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Container:
|
class Container:
|
||||||
|
@ -259,7 +275,14 @@ class DynamicContainer(Container):
|
||||||
"""Check if auto wiring is needed."""
|
"""Check if auto wiring is needed."""
|
||||||
return self.wiring_config.auto_wire is True
|
return self.wiring_config.auto_wire is True
|
||||||
|
|
||||||
def wire(self, modules=None, packages=None, from_package=None, keep_cache=None):
|
def wire(
|
||||||
|
self,
|
||||||
|
modules=None,
|
||||||
|
packages=None,
|
||||||
|
from_package=None,
|
||||||
|
keep_cache=None,
|
||||||
|
warn_unresolved=False,
|
||||||
|
):
|
||||||
"""Wire container providers with provided packages and modules.
|
"""Wire container providers with provided packages and modules.
|
||||||
|
|
||||||
:rtype: None
|
:rtype: None
|
||||||
|
@ -298,6 +321,7 @@ class DynamicContainer(Container):
|
||||||
modules=modules,
|
modules=modules,
|
||||||
packages=packages,
|
packages=packages,
|
||||||
keep_cache=keep_cache,
|
keep_cache=keep_cache,
|
||||||
|
warn_unresolved=warn_unresolved,
|
||||||
)
|
)
|
||||||
|
|
||||||
if modules:
|
if modules:
|
||||||
|
|
|
@ -1599,7 +1599,7 @@ cdef class ConfigurationOption(Provider):
|
||||||
return self._root
|
return self._root
|
||||||
|
|
||||||
def get_name(self):
|
def get_name(self):
|
||||||
return ".".join((self._root.get_name(), self._get_self_name()))
|
return f"{self._root.get_name()}.{self._get_self_name()}"
|
||||||
|
|
||||||
def get_name_segments(self):
|
def get_name_segments(self):
|
||||||
return self._name
|
return self._name
|
||||||
|
|
|
@ -30,9 +30,9 @@ from typing import (
|
||||||
from warnings import warn
|
from warnings import warn
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from typing import Self
|
from typing import Self, assert_never
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from typing_extensions import Self
|
from typing_extensions import Self, assert_never
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from functools import cache
|
from functools import cache
|
||||||
|
@ -139,6 +139,10 @@ class DIWiringWarning(RuntimeWarning):
|
||||||
"""Base class for all warnings raised by the wiring module."""
|
"""Base class for all warnings raised by the wiring module."""
|
||||||
|
|
||||||
|
|
||||||
|
class UnresolvedMarkerWarning(DIWiringWarning):
|
||||||
|
"""Warning raised when a marker with string identifier cannot be resolved against container."""
|
||||||
|
|
||||||
|
|
||||||
class PatchedRegistry:
|
class PatchedRegistry:
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
|
@ -433,6 +437,7 @@ def wire( # noqa: C901
|
||||||
modules: Optional[Iterable[ModuleType]] = None,
|
modules: Optional[Iterable[ModuleType]] = None,
|
||||||
packages: Optional[Iterable[ModuleType]] = None,
|
packages: Optional[Iterable[ModuleType]] = None,
|
||||||
keep_cache: bool = False,
|
keep_cache: bool = False,
|
||||||
|
warn_unresolved: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Wire container providers with provided packages and modules."""
|
"""Wire container providers with provided packages and modules."""
|
||||||
modules = [*modules] if modules else []
|
modules = [*modules] if modules else []
|
||||||
|
@ -449,9 +454,23 @@ def wire( # noqa: C901
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if _is_marker(member):
|
if _is_marker(member):
|
||||||
_patch_attribute(module, member_name, member, providers_map)
|
_patch_attribute(
|
||||||
|
module,
|
||||||
|
member_name,
|
||||||
|
member,
|
||||||
|
providers_map,
|
||||||
|
warn_unresolved=warn_unresolved,
|
||||||
|
warn_unresolved_stacklevel=1,
|
||||||
|
)
|
||||||
elif inspect.isfunction(member):
|
elif inspect.isfunction(member):
|
||||||
_patch_fn(module, member_name, member, providers_map)
|
_patch_fn(
|
||||||
|
module,
|
||||||
|
member_name,
|
||||||
|
member,
|
||||||
|
providers_map,
|
||||||
|
warn_unresolved=warn_unresolved,
|
||||||
|
warn_unresolved_stacklevel=1,
|
||||||
|
)
|
||||||
elif inspect.isclass(member):
|
elif inspect.isclass(member):
|
||||||
cls = member
|
cls = member
|
||||||
try:
|
try:
|
||||||
|
@ -463,15 +482,30 @@ def wire( # noqa: C901
|
||||||
for cls_member_name, cls_member in cls_members:
|
for cls_member_name, cls_member in cls_members:
|
||||||
if _is_marker(cls_member):
|
if _is_marker(cls_member):
|
||||||
_patch_attribute(
|
_patch_attribute(
|
||||||
cls, cls_member_name, cls_member, providers_map
|
cls,
|
||||||
|
cls_member_name,
|
||||||
|
cls_member,
|
||||||
|
providers_map,
|
||||||
|
warn_unresolved=warn_unresolved,
|
||||||
|
warn_unresolved_stacklevel=1,
|
||||||
)
|
)
|
||||||
elif _is_method(cls_member):
|
elif _is_method(cls_member):
|
||||||
_patch_method(
|
_patch_method(
|
||||||
cls, cls_member_name, cls_member, providers_map
|
cls,
|
||||||
|
cls_member_name,
|
||||||
|
cls_member,
|
||||||
|
providers_map,
|
||||||
|
warn_unresolved=warn_unresolved,
|
||||||
|
warn_unresolved_stacklevel=1,
|
||||||
)
|
)
|
||||||
|
|
||||||
for patched in _patched_registry.get_callables_from_module(module):
|
for patched in _patched_registry.get_callables_from_module(module):
|
||||||
_bind_injections(patched, providers_map)
|
_bind_injections(
|
||||||
|
patched,
|
||||||
|
providers_map,
|
||||||
|
warn_unresolved=warn_unresolved,
|
||||||
|
warn_unresolved_stacklevel=1,
|
||||||
|
)
|
||||||
|
|
||||||
if not keep_cache:
|
if not keep_cache:
|
||||||
clear_cache()
|
clear_cache()
|
||||||
|
@ -524,6 +558,8 @@ def _patch_fn(
|
||||||
name: str,
|
name: str,
|
||||||
fn: Callable[..., Any],
|
fn: Callable[..., Any],
|
||||||
providers_map: ProvidersMap,
|
providers_map: ProvidersMap,
|
||||||
|
warn_unresolved: bool = False,
|
||||||
|
warn_unresolved_stacklevel: int = 0,
|
||||||
) -> None:
|
) -> None:
|
||||||
if not _is_patched(fn):
|
if not _is_patched(fn):
|
||||||
reference_injections, reference_closing = _fetch_reference_injections(fn)
|
reference_injections, reference_closing = _fetch_reference_injections(fn)
|
||||||
|
@ -531,7 +567,12 @@ def _patch_fn(
|
||||||
return
|
return
|
||||||
fn = _get_patched(fn, reference_injections, reference_closing)
|
fn = _get_patched(fn, reference_injections, reference_closing)
|
||||||
|
|
||||||
_bind_injections(fn, providers_map)
|
_bind_injections(
|
||||||
|
fn,
|
||||||
|
providers_map,
|
||||||
|
warn_unresolved=warn_unresolved,
|
||||||
|
warn_unresolved_stacklevel=warn_unresolved_stacklevel + 1,
|
||||||
|
)
|
||||||
|
|
||||||
setattr(module, name, fn)
|
setattr(module, name, fn)
|
||||||
|
|
||||||
|
@ -541,6 +582,8 @@ def _patch_method(
|
||||||
name: str,
|
name: str,
|
||||||
method: Callable[..., Any],
|
method: Callable[..., Any],
|
||||||
providers_map: ProvidersMap,
|
providers_map: ProvidersMap,
|
||||||
|
warn_unresolved: bool = False,
|
||||||
|
warn_unresolved_stacklevel: int = 0,
|
||||||
) -> None:
|
) -> None:
|
||||||
if (
|
if (
|
||||||
hasattr(cls, "__dict__")
|
hasattr(cls, "__dict__")
|
||||||
|
@ -558,7 +601,12 @@ def _patch_method(
|
||||||
return
|
return
|
||||||
fn = _get_patched(fn, reference_injections, reference_closing)
|
fn = _get_patched(fn, reference_injections, reference_closing)
|
||||||
|
|
||||||
_bind_injections(fn, providers_map)
|
_bind_injections(
|
||||||
|
fn,
|
||||||
|
providers_map,
|
||||||
|
warn_unresolved=warn_unresolved,
|
||||||
|
warn_unresolved_stacklevel=warn_unresolved_stacklevel + 1,
|
||||||
|
)
|
||||||
|
|
||||||
if fn is method:
|
if fn is method:
|
||||||
# Hotfix, see: https://github.com/ets-labs/python-dependency-injector/issues/884
|
# Hotfix, see: https://github.com/ets-labs/python-dependency-injector/issues/884
|
||||||
|
@ -594,9 +642,17 @@ def _patch_attribute(
|
||||||
name: str,
|
name: str,
|
||||||
marker: "_Marker",
|
marker: "_Marker",
|
||||||
providers_map: ProvidersMap,
|
providers_map: ProvidersMap,
|
||||||
|
warn_unresolved: bool = False,
|
||||||
|
warn_unresolved_stacklevel: int = 0,
|
||||||
) -> None:
|
) -> None:
|
||||||
provider = providers_map.resolve_provider(marker.provider, marker.modifier)
|
provider = providers_map.resolve_provider(marker.provider, marker.modifier)
|
||||||
if provider is None:
|
if provider is None:
|
||||||
|
if warn_unresolved:
|
||||||
|
warn(
|
||||||
|
f"Unresolved marker {name} in {member!r}",
|
||||||
|
UnresolvedMarkerWarning,
|
||||||
|
stacklevel=warn_unresolved_stacklevel + 2,
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
_patched_registry.register_attribute(PatchedAttribute(member, name, marker))
|
_patched_registry.register_attribute(PatchedAttribute(member, name, marker))
|
||||||
|
@ -673,7 +729,12 @@ def _fetch_reference_injections( # noqa: C901
|
||||||
return injections, closing
|
return injections, closing
|
||||||
|
|
||||||
|
|
||||||
def _bind_injections(fn: Callable[..., Any], providers_map: ProvidersMap) -> None:
|
def _bind_injections(
|
||||||
|
fn: Callable[..., Any],
|
||||||
|
providers_map: ProvidersMap,
|
||||||
|
warn_unresolved: bool = False,
|
||||||
|
warn_unresolved_stacklevel: int = 0,
|
||||||
|
) -> None:
|
||||||
patched_callable = _patched_registry.get_callable(fn)
|
patched_callable = _patched_registry.get_callable(fn)
|
||||||
if patched_callable is None:
|
if patched_callable is None:
|
||||||
return
|
return
|
||||||
|
@ -682,6 +743,12 @@ def _bind_injections(fn: Callable[..., Any], providers_map: ProvidersMap) -> Non
|
||||||
provider = providers_map.resolve_provider(marker.provider, marker.modifier)
|
provider = providers_map.resolve_provider(marker.provider, marker.modifier)
|
||||||
|
|
||||||
if provider is None:
|
if provider is None:
|
||||||
|
if warn_unresolved:
|
||||||
|
warn(
|
||||||
|
f"Unresolved marker {injection} in {fn.__qualname__}",
|
||||||
|
UnresolvedMarkerWarning,
|
||||||
|
stacklevel=warn_unresolved_stacklevel + 2,
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if isinstance(marker, Provide):
|
if isinstance(marker, Provide):
|
||||||
|
@ -791,6 +858,9 @@ class TypeModifier(Modifier):
|
||||||
) -> providers.Provider:
|
) -> providers.Provider:
|
||||||
return provider.as_(self.type_)
|
return provider.as_(self.type_)
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"{self.__class__.__name__}({self.type_!r})"
|
||||||
|
|
||||||
|
|
||||||
def as_int() -> TypeModifier:
|
def as_int() -> TypeModifier:
|
||||||
"""Return int type modifier."""
|
"""Return int type modifier."""
|
||||||
|
@ -809,8 +879,8 @@ def as_(type_: Type) -> TypeModifier:
|
||||||
|
|
||||||
class RequiredModifier(Modifier):
|
class RequiredModifier(Modifier):
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self, type_modifier: Optional[TypeModifier] = None) -> None:
|
||||||
self.type_modifier = None
|
self.type_modifier = type_modifier
|
||||||
|
|
||||||
def as_int(self) -> Self:
|
def as_int(self) -> Self:
|
||||||
self.type_modifier = TypeModifier(int)
|
self.type_modifier = TypeModifier(int)
|
||||||
|
@ -834,6 +904,11 @@ class RequiredModifier(Modifier):
|
||||||
provider = provider.as_(self.type_modifier.type_)
|
provider = provider.as_(self.type_modifier.type_)
|
||||||
return provider
|
return provider
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
if self.type_modifier:
|
||||||
|
return f"{self.__class__.__name__}({self.type_modifier!r})"
|
||||||
|
return f"{self.__class__.__name__}()"
|
||||||
|
|
||||||
|
|
||||||
def required() -> RequiredModifier:
|
def required() -> RequiredModifier:
|
||||||
"""Return required modifier."""
|
"""Return required modifier."""
|
||||||
|
@ -853,6 +928,9 @@ class InvariantModifier(Modifier):
|
||||||
invariant_segment = providers_map.resolve_provider(self.id)
|
invariant_segment = providers_map.resolve_provider(self.id)
|
||||||
return provider[invariant_segment]
|
return provider[invariant_segment]
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"{self.__class__.__name__}({self.id!r})"
|
||||||
|
|
||||||
|
|
||||||
def invariant(id: str) -> InvariantModifier:
|
def invariant(id: str) -> InvariantModifier:
|
||||||
"""Return invariant modifier."""
|
"""Return invariant modifier."""
|
||||||
|
@ -893,8 +971,28 @@ class ProvidedInstance(Modifier):
|
||||||
provider = provider[value]
|
provider = provider[value]
|
||||||
elif type_ == ProvidedInstance.TYPE_CALL:
|
elif type_ == ProvidedInstance.TYPE_CALL:
|
||||||
provider = provider.call()
|
provider = provider.call()
|
||||||
|
else:
|
||||||
|
assert_never(type_)
|
||||||
return provider
|
return provider
|
||||||
|
|
||||||
|
def _format_segments(self) -> str:
|
||||||
|
segments = []
|
||||||
|
for type_, value in self.segments:
|
||||||
|
if type_ == ProvidedInstance.TYPE_ATTRIBUTE:
|
||||||
|
segments.append(f".{value}")
|
||||||
|
elif type_ == ProvidedInstance.TYPE_ITEM:
|
||||||
|
segments.append(f"[{value!r}]")
|
||||||
|
elif type_ == ProvidedInstance.TYPE_CALL:
|
||||||
|
segments.append(".call()")
|
||||||
|
else:
|
||||||
|
assert_never(type_)
|
||||||
|
return "".join(segments)
|
||||||
|
|
||||||
|
__str__ = _format_segments
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"{self.__class__.__name__}(){self._format_segments()}"
|
||||||
|
|
||||||
|
|
||||||
def provided() -> ProvidedInstance:
|
def provided() -> ProvidedInstance:
|
||||||
"""Return provided instance modifier."""
|
"""Return provided instance modifier."""
|
||||||
|
@ -910,7 +1008,7 @@ MarkerItem = Union[
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING: # noqa
|
||||||
|
|
||||||
class _Marker(Protocol):
|
class _Marker(Protocol):
|
||||||
__IS_MARKER__: bool
|
__IS_MARKER__: bool
|
||||||
|
@ -918,6 +1016,7 @@ if TYPE_CHECKING:
|
||||||
def __call__(self) -> Self: ...
|
def __call__(self) -> Self: ...
|
||||||
def __getattr__(self, item: str) -> Self: ...
|
def __getattr__(self, item: str) -> Self: ...
|
||||||
def __getitem__(self, item: Any) -> Any: ...
|
def __getitem__(self, item: Any) -> Any: ...
|
||||||
|
def __repr__(self) -> str: ...
|
||||||
|
|
||||||
Provide: _Marker
|
Provide: _Marker
|
||||||
Provider: _Marker
|
Provider: _Marker
|
||||||
|
@ -946,6 +1045,12 @@ else:
|
||||||
def __call__(self) -> Self:
|
def __call__(self) -> Self:
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
cls_name = self.__class__.__name__
|
||||||
|
if self.modifier:
|
||||||
|
return f"{cls_name}[{self.provider!r}, {self.modifier!r}]"
|
||||||
|
return f"{cls_name}[{self.provider!r}]"
|
||||||
|
|
||||||
class Provide(_Marker): ...
|
class Provide(_Marker): ...
|
||||||
|
|
||||||
class Provider(_Marker): ...
|
class Provider(_Marker): ...
|
||||||
|
|
15
tests/unit/samples/wiringstringids/missing.py
Normal file
15
tests/unit/samples/wiringstringids/missing.py
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
from dependency_injector.wiring import Provide, inject
|
||||||
|
|
||||||
|
missing_obj: object = Provide["missing"]
|
||||||
|
|
||||||
|
|
||||||
|
class TestMissingClass:
|
||||||
|
obj: object = Provide["missing"]
|
||||||
|
|
||||||
|
def method(self, obj: object = Provide["missing"]) -> object:
|
||||||
|
return obj
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
|
def test_missing_function(obj: object = Provide["missing"]):
|
||||||
|
return obj
|
|
@ -1,5 +1,6 @@
|
||||||
"""Main wiring tests."""
|
"""Main wiring tests."""
|
||||||
|
|
||||||
|
import re
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
from dependency_injector import errors
|
from dependency_injector import errors
|
||||||
|
@ -67,7 +68,7 @@ def test_module_attributes_wiring():
|
||||||
|
|
||||||
def test_module_attribute_wiring_with_invalid_marker(container: Container):
|
def test_module_attribute_wiring_with_invalid_marker(container: Container):
|
||||||
from samples.wiring import module_invalid_attr_injection
|
from samples.wiring import module_invalid_attr_injection
|
||||||
with raises(Exception, match="Unknown type of marker {0}".format(module_invalid_attr_injection.service)):
|
with raises(Exception, match=re.escape("Unknown type of marker {0}".format(module_invalid_attr_injection.service))):
|
||||||
container.wire(modules=[module_invalid_attr_injection])
|
container.wire(modules=[module_invalid_attr_injection])
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,21 @@
|
||||||
"""Main wiring tests."""
|
"""Main wiring tests."""
|
||||||
|
|
||||||
|
import re
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
from pytest import fixture, mark, raises
|
from pytest import fixture, mark, raises, warns
|
||||||
from samples.wiringstringids import module, package, resourceclosing
|
from samples.wiringstringids import module, package, resourceclosing
|
||||||
from samples.wiringstringids.container import Container, SubContainer
|
from samples.wiringstringids.container import Container, SubContainer
|
||||||
from samples.wiringstringids.service import Service
|
from samples.wiringstringids.service import Service
|
||||||
|
|
||||||
from dependency_injector import errors
|
from dependency_injector import errors
|
||||||
from dependency_injector.wiring import Closing, Provide, Provider, wire
|
from dependency_injector.wiring import (
|
||||||
|
Closing,
|
||||||
|
Provide,
|
||||||
|
Provider,
|
||||||
|
UnresolvedMarkerWarning,
|
||||||
|
wire,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@fixture(autouse=True)
|
@fixture(autouse=True)
|
||||||
|
@ -68,10 +75,20 @@ def test_module_attributes_wiring():
|
||||||
|
|
||||||
def test_module_attribute_wiring_with_invalid_marker(container: Container):
|
def test_module_attribute_wiring_with_invalid_marker(container: Container):
|
||||||
from samples.wiringstringids import module_invalid_attr_injection
|
from samples.wiringstringids import module_invalid_attr_injection
|
||||||
with raises(Exception, match="Unknown type of marker {0}".format(module_invalid_attr_injection.service)):
|
with raises(Exception, match=re.escape("Unknown type of marker {0}".format(module_invalid_attr_injection.service))):
|
||||||
container.wire(modules=[module_invalid_attr_injection])
|
container.wire(modules=[module_invalid_attr_injection])
|
||||||
|
|
||||||
|
|
||||||
|
def test_warn_unresolved_marker(container: Container):
|
||||||
|
from samples.wiringstringids import missing
|
||||||
|
|
||||||
|
with warns(
|
||||||
|
UnresolvedMarkerWarning,
|
||||||
|
match=r"^Unresolved marker .+ in .+$",
|
||||||
|
):
|
||||||
|
container.wire(modules=[missing], warn_unresolved=True)
|
||||||
|
|
||||||
|
|
||||||
def test_class_wiring():
|
def test_class_wiring():
|
||||||
test_class_object = module.TestClass()
|
test_class_object = module.TestClass()
|
||||||
assert isinstance(test_class_object.service, Service)
|
assert isinstance(test_class_object.service, Service)
|
||||||
|
|
42
tests/unit/wiring/test_reprs.py
Normal file
42
tests/unit/wiring/test_reprs.py
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
from dependency_injector.wiring import (
|
||||||
|
Closing,
|
||||||
|
InvariantModifier,
|
||||||
|
Provide,
|
||||||
|
ProvidedInstance,
|
||||||
|
RequiredModifier,
|
||||||
|
TypeModifier,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_type_modifier_repr() -> None:
|
||||||
|
assert repr(TypeModifier(int)) == f"TypeModifier({int!r})"
|
||||||
|
|
||||||
|
|
||||||
|
def test_required_modifier_repr() -> None:
|
||||||
|
assert repr(RequiredModifier()) == "RequiredModifier()"
|
||||||
|
|
||||||
|
|
||||||
|
def test_required_modifier_with_type_repr() -> None:
|
||||||
|
type_modifier = TypeModifier(int)
|
||||||
|
required_modifier = RequiredModifier(type_modifier)
|
||||||
|
assert repr(required_modifier) == f"RequiredModifier({type_modifier!r})"
|
||||||
|
|
||||||
|
|
||||||
|
def test_invariant_modifier_repr() -> None:
|
||||||
|
assert repr(InvariantModifier("test")) == "InvariantModifier('test')"
|
||||||
|
|
||||||
|
|
||||||
|
def test_provided_instance_repr() -> None:
|
||||||
|
provided_instance = ProvidedInstance().test["attr"].call()
|
||||||
|
|
||||||
|
assert repr(provided_instance) == "ProvidedInstance().test['attr'].call()"
|
||||||
|
|
||||||
|
|
||||||
|
def test_marker_repr() -> None:
|
||||||
|
assert repr(Closing[Provide["test"]]) == "Closing[Provide['test']]"
|
||||||
|
|
||||||
|
|
||||||
|
def test_marker_with_modifier_repr() -> None:
|
||||||
|
marker = Provide["test", RequiredModifier()]
|
||||||
|
|
||||||
|
assert repr(marker) == "Provide['test', RequiredModifier()]"
|
Loading…
Reference in New Issue
Block a user