Hotfix issue #574, bump version to 4.39.1

This commit is contained in:
Roman Mogylatov 2022-03-29 22:51:40 -04:00
parent 13cae77d57
commit cfed30cf07
10 changed files with 11983 additions and 2980 deletions

View File

@ -7,6 +7,12 @@ that were made in every particular version.
From version 0.7.6 *Dependency Injector* framework strictly
follows `Semantic versioning`_
4.39.1
------
- Fix bug `#574 <https://github.com/ets-labs/python-dependency-injector/issues/574>`_:
"``@inject`` breaks ``inspect.iscoroutinefunction``". Thanks to
`@burritoatspoton (Rafał Burczyński) <https://github.com/burritoatspoton>`_ for reporting the issue.
4.39.0
------
- Optimize injections and wiring from x1.5 to x7 times depending on the use case.

View File

@ -1,6 +1,6 @@
"""Top-level package."""
__version__ = "4.39.0"
__version__ = "4.39.1"
"""Version number.
:type: str

File diff suppressed because it is too large Load Diff

View File

@ -9,23 +9,27 @@ import types
from . import providers
from .wiring import _Marker
from .providers cimport Provider
def _get_sync_patched(fn):
@functools.wraps(fn)
def _patched(*args, **kwargs):
cdef object result
cdef dict to_inject
cdef object arg_key
cdef Provider provider
to_inject = kwargs.copy()
for injection, provider in _patched.__injections__.items():
if injection not in kwargs or isinstance(kwargs[injection], _Marker):
to_inject[injection] = provider()
for arg_key, provider in _patched.__injections__.items():
if arg_key not in kwargs or isinstance(kwargs[arg_key], _Marker):
to_inject[arg_key] = provider()
result = fn(*args, **to_inject)
if _patched.__closing__:
for injection, provider in _patched.__closing__.items():
if injection in kwargs and not isinstance(kwargs[injection], _Marker):
for arg_key, provider in _patched.__closing__.items():
if arg_key in kwargs and not isinstance(kwargs[arg_key], _Marker):
continue
if not isinstance(provider, providers.Resource):
continue
@ -35,49 +39,45 @@ def _get_sync_patched(fn):
return _patched
def _get_async_patched(fn):
@functools.wraps(fn)
async def _patched(*args, **kwargs):
cdef object result
cdef dict to_inject
cdef list to_inject_await = []
cdef list to_close_await = []
async def _async_inject(object fn, tuple args, dict kwargs, dict injections, dict closings):
cdef object result
cdef dict to_inject
cdef list to_inject_await = []
cdef list to_close_await = []
cdef object arg_key
cdef Provider provider
to_inject = kwargs.copy()
for injection, provider in _patched.__injections__.items():
if injection not in kwargs or isinstance(kwargs[injection], _Marker):
provide = provider()
if _isawaitable(provide):
to_inject_await.append((injection, provide))
else:
to_inject[injection] = provide
to_inject = kwargs.copy()
for arg_key, provider in injections.items():
if arg_key not in kwargs or isinstance(kwargs[arg_key], _Marker):
provide = provider()
if provider.is_async_mode_enabled():
to_inject_await.append((arg_key, provide))
elif _isawaitable(provide):
to_inject_await.append((arg_key, provide))
else:
to_inject[arg_key] = provide
if to_inject_await:
async_to_inject = await asyncio.gather(*(provide for _, provide in to_inject_await))
for provide, (injection, _) in zip(async_to_inject, to_inject_await):
to_inject[injection] = provide
if to_inject_await:
async_to_inject = await asyncio.gather(*(provide for _, provide in to_inject_await))
for provide, (injection, _) in zip(async_to_inject, to_inject_await):
to_inject[injection] = provide
result = await fn(*args, **to_inject)
result = await fn(*args, **to_inject)
if _patched.__closing__:
for injection, provider in _patched.__closing__.items():
if injection in kwargs \
and isinstance(kwargs[injection], _Marker):
continue
if not isinstance(provider, providers.Resource):
continue
shutdown = provider.shutdown()
if _isawaitable(shutdown):
to_close_await.append(shutdown)
if closings:
for arg_key, provider in closings.items():
if arg_key in kwargs and isinstance(kwargs[arg_key], _Marker):
continue
if not isinstance(provider, providers.Resource):
continue
shutdown = provider.shutdown()
if _isawaitable(shutdown):
to_close_await.append(shutdown)
await asyncio.gather(*to_close_await)
await asyncio.gather(*to_close_await)
return result
# Hotfix for iscoroutinefunction() for Cython < 3.0.0; can be removed after migration to Cython 3.0.0+
_patched._is_coroutine = asyncio.coroutines._is_coroutine
return _patched
return result
cdef bint _isawaitable(object instance):

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -25,6 +25,10 @@ cdef class Provider(object):
cdef tuple __overrides
cdef int __async_mode
cpdef bint is_async_mode_enabled(self)
cpdef bint is_async_mode_disabled(self)
cpdef bint is_async_mode_undefined(self)
cpdef object _provide(self, tuple args, dict kwargs)
cpdef void _copy_overridings(self, Provider copied, dict memo)

View File

@ -406,15 +406,15 @@ cdef class Provider(object):
"""
self.__async_mode = ASYNC_MODE_UNDEFINED
def is_async_mode_enabled(self):
cpdef bint is_async_mode_enabled(self):
"""Check if async mode is enabled."""
return self.__async_mode == ASYNC_MODE_ENABLED
def is_async_mode_disabled(self):
cpdef bint is_async_mode_disabled(self):
"""Check if async mode is disabled."""
return self.__async_mode == ASYNC_MODE_DISABLED
def is_async_mode_undefined(self):
cpdef bint is_async_mode_undefined(self):
"""Check if async mode is undefined."""
return self.__async_mode == ASYNC_MODE_UNDEFINED

View File

@ -1,5 +1,5 @@
"""Wiring module."""
import functools
import inspect
import importlib
import importlib.machinery
@ -895,4 +895,19 @@ _loader = AutoLoader()
# Optimizations
from ._cwiring import _get_sync_patched # noqa
from ._cwiring import _get_async_patched # noqa
from ._cwiring import _async_inject # noqa
# Wiring uses the following Python wrapper because there is
# no possibility to compile a first-type citizen coroutine in Cython.
def _get_async_patched(fn):
@functools.wraps(fn)
async def _patched(*args, **kwargs):
return await _async_inject(
fn,
args,
kwargs,
_patched.__injections__,
_patched.__closing__,
)
return _patched

View File

@ -0,0 +1,22 @@
"""Tests for compatibility of @inject-patched functions with asyncio and inspect module checks."""
import asyncio
import inspect
from dependency_injector.wiring import inject
def test_asyncio_iscoroutinefunction():
@inject
async def foo():
...
assert asyncio.iscoroutinefunction(foo)
def test_inspect_iscoroutinefunction():
@inject
async def foo():
...
assert inspect.iscoroutinefunction(foo)