mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2025-06-23 06:53:12 +03:00
Merge branch 'release/4.48.1'
This commit is contained in:
commit
2c0aede4aa
4
.github/workflows/publishing.yml
vendored
4
.github/workflows/publishing.yml
vendored
|
@ -70,10 +70,10 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Build wheels
|
||||
uses: pypa/cibuildwheel@v2.23.3
|
||||
uses: pypa/cibuildwheel@v3.0.0
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: cibw-wheels-x86-${{ matrix.os }}-${{ strategy.job-index }}
|
||||
name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
|
||||
path: ./wheelhouse/*.whl
|
||||
|
||||
test-publish:
|
||||
|
|
|
@ -7,6 +7,14 @@ that were made in every particular version.
|
|||
From version 0.7.6 *Dependency Injector* framework strictly
|
||||
follows `Semantic versioning`_
|
||||
|
||||
4.48.1
|
||||
------
|
||||
|
||||
* Improve performance of ``dependency_injector._cwiring.DependencyResolver``
|
||||
* Add ``typing-extensions`` as a dependency for older Python versions (<3.11)
|
||||
* Produce warning on ``@inject``s without ``Provide[...]`` marks
|
||||
* Add support for `resource_type` in ``Lifespan``s
|
||||
|
||||
4.48.0
|
||||
------
|
||||
|
||||
|
|
|
@ -52,6 +52,11 @@ classifiers = [
|
|||
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||
]
|
||||
dynamic = ["version"]
|
||||
dependencies = [
|
||||
# typing.Annotated since v3.9
|
||||
# typing.Self since v3.11
|
||||
"typing-extensions; python_version<'3.11'",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
yaml = ["pyyaml"]
|
||||
|
@ -108,6 +113,7 @@ markers = [
|
|||
"pydantic: Tests with Pydantic as a dependency",
|
||||
]
|
||||
filterwarnings = [
|
||||
"ignore::dependency_injector.wiring.DIWiringWarning",
|
||||
"ignore:Module \"dependency_injector.ext.aiohttp\" is deprecated since version 4\\.0\\.0:DeprecationWarning",
|
||||
"ignore:Module \"dependency_injector.ext.flask\" is deprecated since version 4\\.0\\.0:DeprecationWarning",
|
||||
"ignore:Please use \\`.*?\\` from the \\`scipy.*?\\`(.*?)namespace is deprecated\\.:DeprecationWarning",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
"""Top-level package."""
|
||||
|
||||
__version__ = "4.48.0"
|
||||
__version__ = "4.48.1"
|
||||
"""Version number.
|
||||
|
||||
:type: str
|
||||
|
|
|
@ -5,24 +5,11 @@ from collections.abc import Awaitable
|
|||
from inspect import CO_ITERABLE_COROUTINE
|
||||
from types import CoroutineType, GeneratorType
|
||||
|
||||
from .providers cimport Provider, Resource, NULL_AWAITABLE
|
||||
from .providers cimport Provider, Resource
|
||||
from .wiring import _Marker
|
||||
|
||||
cimport cython
|
||||
|
||||
|
||||
@cython.internal
|
||||
@cython.no_gc
|
||||
cdef class KWPair:
|
||||
cdef str name
|
||||
cdef object value
|
||||
|
||||
def __cinit__(self, str name, object value, /):
|
||||
self.name = name
|
||||
self.value = value
|
||||
|
||||
|
||||
cdef inline bint _is_injectable(dict kwargs, str name):
|
||||
cdef inline bint _is_injectable(dict kwargs, object name):
|
||||
return name not in kwargs or isinstance(kwargs[name], _Marker)
|
||||
|
||||
|
||||
|
@ -38,11 +25,8 @@ cdef class DependencyResolver:
|
|||
self.injections = injections
|
||||
self.closings = closings
|
||||
|
||||
async def _await_injection(self, kw_pair: KWPair, /) -> None:
|
||||
self.to_inject[kw_pair.name] = await kw_pair.value
|
||||
|
||||
cdef object _await_injections(self, to_await: list):
|
||||
return gather(*map(self._await_injection, to_await))
|
||||
async def _await_injection(self, name: str, value: object, /) -> None:
|
||||
self.to_inject[name] = await value
|
||||
|
||||
cdef void _handle_injections_sync(self):
|
||||
cdef Provider provider
|
||||
|
@ -60,7 +44,7 @@ cdef class DependencyResolver:
|
|||
provide = provider()
|
||||
|
||||
if provider.is_async_mode_enabled() or _isawaitable(provide):
|
||||
to_await.append(KWPair(name, provide))
|
||||
to_await.append(self._await_injection(name, provide))
|
||||
else:
|
||||
self.to_inject[name] = provide
|
||||
|
||||
|
@ -93,13 +77,12 @@ cdef class DependencyResolver:
|
|||
|
||||
async def __aenter__(self):
|
||||
if to_await := self._handle_injections_async():
|
||||
await self._await_injections(to_await)
|
||||
await gather(*to_await)
|
||||
return self.to_inject
|
||||
|
||||
def __aexit__(self, *_):
|
||||
async def __aexit__(self, *_):
|
||||
if to_await := self._handle_closings_async():
|
||||
return gather(*to_await)
|
||||
return NULL_AWAITABLE
|
||||
await gather(*to_await)
|
||||
|
||||
|
||||
cdef bint _isawaitable(object instance):
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import sys
|
||||
from typing import Any
|
||||
from typing import Any, Type
|
||||
|
||||
if sys.version_info >= (3, 11): # pragma: no cover
|
||||
from typing import Self
|
||||
|
@ -7,6 +7,7 @@ else: # pragma: no cover
|
|||
from typing_extensions import Self
|
||||
|
||||
from dependency_injector.containers import Container
|
||||
from dependency_injector.providers import Resource
|
||||
|
||||
|
||||
class Lifespan:
|
||||
|
@ -29,24 +30,32 @@ class Lifespan:
|
|||
app = Factory(Starlette, lifespan=lifespan)
|
||||
|
||||
:param container: container instance
|
||||
:param resource_type: A :py:class:`~dependency_injector.resources.Resource`
|
||||
subclass. Limits the resources to be initialized and shutdown.
|
||||
"""
|
||||
|
||||
container: Container
|
||||
resource_type: Type[Resource[Any]]
|
||||
|
||||
def __init__(self, container: Container) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
container: Container,
|
||||
resource_type: Type[Resource[Any]] = Resource,
|
||||
) -> None:
|
||||
self.container = container
|
||||
self.resource_type = resource_type
|
||||
|
||||
def __call__(self, app: Any) -> Self:
|
||||
return self
|
||||
|
||||
async def __aenter__(self) -> None:
|
||||
result = self.container.init_resources()
|
||||
result = self.container.init_resources(self.resource_type)
|
||||
|
||||
if result is not None:
|
||||
await result
|
||||
|
||||
async def __aexit__(self, *exc_info: Any) -> None:
|
||||
result = self.container.shutdown_resources()
|
||||
result = self.container.shutdown_resources(self.resource_type)
|
||||
|
||||
if result is not None:
|
||||
await result
|
||||
|
|
|
@ -6,6 +6,8 @@ import importlib.machinery
|
|||
import inspect
|
||||
import pkgutil
|
||||
import sys
|
||||
from contextlib import suppress
|
||||
from inspect import isbuiltin, isclass
|
||||
from types import ModuleType
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
|
@ -15,6 +17,7 @@ from typing import (
|
|||
Dict,
|
||||
Iterable,
|
||||
Iterator,
|
||||
List,
|
||||
Optional,
|
||||
Protocol,
|
||||
Set,
|
||||
|
@ -24,6 +27,7 @@ from typing import (
|
|||
Union,
|
||||
cast,
|
||||
)
|
||||
from warnings import warn
|
||||
|
||||
try:
|
||||
from typing import Self
|
||||
|
@ -59,13 +63,11 @@ else:
|
|||
return None
|
||||
|
||||
|
||||
MARKER_EXTRACTORS = []
|
||||
MARKER_EXTRACTORS: List[Callable[[Any], Any]] = []
|
||||
INSPECT_EXCLUSION_FILTERS: List[Callable[[Any], bool]] = [isbuiltin]
|
||||
|
||||
try:
|
||||
with suppress(ImportError):
|
||||
from fastapi.params import Depends as FastAPIDepends
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
|
||||
def extract_marker_from_fastapi(param: Any) -> Any:
|
||||
if isinstance(param, FastAPIDepends):
|
||||
|
@ -74,11 +76,8 @@ else:
|
|||
|
||||
MARKER_EXTRACTORS.append(extract_marker_from_fastapi)
|
||||
|
||||
try:
|
||||
with suppress(ImportError):
|
||||
from fast_depends.dependencies import Depends as FastDepends
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
|
||||
def extract_marker_from_fast_depends(param: Any) -> Any:
|
||||
if isinstance(param, FastDepends):
|
||||
|
@ -88,16 +87,22 @@ else:
|
|||
MARKER_EXTRACTORS.append(extract_marker_from_fast_depends)
|
||||
|
||||
|
||||
try:
|
||||
import starlette.requests
|
||||
except ImportError:
|
||||
starlette = None
|
||||
with suppress(ImportError):
|
||||
from starlette.requests import Request as StarletteRequest
|
||||
|
||||
def is_starlette_request_cls(obj: Any) -> bool:
|
||||
return isclass(obj) and _safe_is_subclass(obj, StarletteRequest)
|
||||
|
||||
INSPECT_EXCLUSION_FILTERS.append(is_starlette_request_cls)
|
||||
|
||||
|
||||
try:
|
||||
import werkzeug.local
|
||||
except ImportError:
|
||||
werkzeug = None
|
||||
with suppress(ImportError):
|
||||
from werkzeug.local import LocalProxy as WerkzeugLocalProxy
|
||||
|
||||
def is_werkzeug_local_proxy(obj: Any) -> bool:
|
||||
return isinstance(obj, WerkzeugLocalProxy)
|
||||
|
||||
INSPECT_EXCLUSION_FILTERS.append(is_werkzeug_local_proxy)
|
||||
|
||||
from . import providers # noqa: E402
|
||||
|
||||
|
@ -130,6 +135,10 @@ else:
|
|||
Container = Any
|
||||
|
||||
|
||||
class DIWiringWarning(RuntimeWarning):
|
||||
"""Base class for all warnings raised by the wiring module."""
|
||||
|
||||
|
||||
class PatchedRegistry:
|
||||
|
||||
def __init__(self) -> None:
|
||||
|
@ -411,30 +420,11 @@ class ProvidersMap:
|
|||
return providers_map
|
||||
|
||||
|
||||
class InspectFilter:
|
||||
|
||||
def is_excluded(self, instance: object) -> bool:
|
||||
if self._is_werkzeug_local_proxy(instance):
|
||||
def is_excluded_from_inspect(obj: Any) -> bool:
|
||||
for is_excluded in INSPECT_EXCLUSION_FILTERS:
|
||||
if is_excluded(obj):
|
||||
return True
|
||||
elif self._is_starlette_request_cls(instance):
|
||||
return True
|
||||
elif self._is_builtin(instance):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def _is_werkzeug_local_proxy(self, instance: object) -> bool:
|
||||
return werkzeug and isinstance(instance, werkzeug.local.LocalProxy)
|
||||
|
||||
def _is_starlette_request_cls(self, instance: object) -> bool:
|
||||
return (
|
||||
starlette
|
||||
and isinstance(instance, type)
|
||||
and _safe_is_subclass(instance, starlette.requests.Request)
|
||||
)
|
||||
|
||||
def _is_builtin(self, instance: object) -> bool:
|
||||
return inspect.isbuiltin(instance)
|
||||
return False
|
||||
|
||||
|
||||
def wire( # noqa: C901
|
||||
|
@ -455,7 +445,7 @@ def wire( # noqa: C901
|
|||
|
||||
for module in modules:
|
||||
for member_name, member in _get_members_and_annotated(module):
|
||||
if _inspect_filter.is_excluded(member):
|
||||
if is_excluded_from_inspect(member):
|
||||
continue
|
||||
|
||||
if _is_marker(member):
|
||||
|
@ -520,6 +510,11 @@ def unwire( # noqa: C901
|
|||
def inject(fn: F) -> F:
|
||||
"""Decorate callable with injecting decorator."""
|
||||
reference_injections, reference_closing = _fetch_reference_injections(fn)
|
||||
|
||||
if not reference_injections:
|
||||
warn("@inject is not required here", DIWiringWarning, stacklevel=2)
|
||||
return fn
|
||||
|
||||
patched = _get_patched(fn, reference_injections, reference_closing)
|
||||
return cast(F, patched)
|
||||
|
||||
|
@ -1054,7 +1049,6 @@ def is_loader_installed() -> bool:
|
|||
|
||||
|
||||
_patched_registry = PatchedRegistry()
|
||||
_inspect_filter = InspectFilter()
|
||||
_loader = AutoLoader()
|
||||
|
||||
# Optimizations
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from typing import AsyncIterator, Iterator
|
||||
from typing import AsyncIterator, Iterator, TypeVar
|
||||
from unittest.mock import ANY
|
||||
|
||||
from pytest import mark
|
||||
|
@ -7,6 +7,12 @@ from dependency_injector.containers import DeclarativeContainer
|
|||
from dependency_injector.ext.starlette import Lifespan
|
||||
from dependency_injector.providers import Resource
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
class XResource(Resource[T]):
|
||||
"""A test provider"""
|
||||
|
||||
|
||||
class TestLifespan:
|
||||
@mark.parametrize("sync", [False, True])
|
||||
|
@ -28,11 +34,15 @@ class TestLifespan:
|
|||
yield
|
||||
shutdown = True
|
||||
|
||||
def nope():
|
||||
assert False, "should not be called"
|
||||
|
||||
class Container(DeclarativeContainer):
|
||||
x = Resource(sync_resource if sync else async_resource)
|
||||
x = XResource(sync_resource if sync else async_resource)
|
||||
y = Resource(nope)
|
||||
|
||||
container = Container()
|
||||
lifespan = Lifespan(container)
|
||||
lifespan = Lifespan(container, resource_type=XResource)
|
||||
|
||||
async with lifespan(ANY) as scope:
|
||||
assert scope is None
|
||||
|
|
Loading…
Reference in New Issue
Block a user