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 From version 0.7.6 *Dependency Injector* framework strictly
follows `Semantic versioning`_ 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 4.39.0
------ ------
- Optimize injections and wiring from x1.5 to x7 times depending on the use case. - Optimize injections and wiring from x1.5 to x7 times depending on the use case.

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -9,23 +9,27 @@ import types
from . import providers from . import providers
from .wiring import _Marker from .wiring import _Marker
from .providers cimport Provider
def _get_sync_patched(fn): def _get_sync_patched(fn):
@functools.wraps(fn) @functools.wraps(fn)
def _patched(*args, **kwargs): def _patched(*args, **kwargs):
cdef object result cdef object result
cdef dict to_inject cdef dict to_inject
cdef object arg_key
cdef Provider provider
to_inject = kwargs.copy() to_inject = kwargs.copy()
for injection, provider in _patched.__injections__.items(): for arg_key, provider in _patched.__injections__.items():
if injection not in kwargs or isinstance(kwargs[injection], _Marker): if arg_key not in kwargs or isinstance(kwargs[arg_key], _Marker):
to_inject[injection] = provider() to_inject[arg_key] = provider()
result = fn(*args, **to_inject) result = fn(*args, **to_inject)
if _patched.__closing__: if _patched.__closing__:
for injection, provider in _patched.__closing__.items(): for arg_key, provider in _patched.__closing__.items():
if injection in kwargs and not isinstance(kwargs[injection], _Marker): if arg_key in kwargs and not isinstance(kwargs[arg_key], _Marker):
continue continue
if not isinstance(provider, providers.Resource): if not isinstance(provider, providers.Resource):
continue continue
@ -35,49 +39,45 @@ def _get_sync_patched(fn):
return _patched return _patched
def _get_async_patched(fn): async def _async_inject(object fn, tuple args, dict kwargs, dict injections, dict closings):
@functools.wraps(fn) cdef object result
async def _patched(*args, **kwargs): cdef dict to_inject
cdef object result cdef list to_inject_await = []
cdef dict to_inject cdef list to_close_await = []
cdef list to_inject_await = [] cdef object arg_key
cdef list to_close_await = [] cdef Provider provider
to_inject = kwargs.copy() to_inject = kwargs.copy()
for injection, provider in _patched.__injections__.items(): for arg_key, provider in injections.items():
if injection not in kwargs or isinstance(kwargs[injection], _Marker): if arg_key not in kwargs or isinstance(kwargs[arg_key], _Marker):
provide = provider() provide = provider()
if _isawaitable(provide): if provider.is_async_mode_enabled():
to_inject_await.append((injection, provide)) to_inject_await.append((arg_key, provide))
else: elif _isawaitable(provide):
to_inject[injection] = provide to_inject_await.append((arg_key, provide))
else:
to_inject[arg_key] = provide
if to_inject_await: if to_inject_await:
async_to_inject = await asyncio.gather(*(provide for _, provide in 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): for provide, (injection, _) in zip(async_to_inject, to_inject_await):
to_inject[injection] = provide to_inject[injection] = provide
result = await fn(*args, **to_inject) result = await fn(*args, **to_inject)
if _patched.__closing__: if closings:
for injection, provider in _patched.__closing__.items(): for arg_key, provider in closings.items():
if injection in kwargs \ if arg_key in kwargs and isinstance(kwargs[arg_key], _Marker):
and isinstance(kwargs[injection], _Marker): continue
continue if not isinstance(provider, providers.Resource):
if not isinstance(provider, providers.Resource): continue
continue shutdown = provider.shutdown()
shutdown = provider.shutdown() if _isawaitable(shutdown):
if _isawaitable(shutdown): to_close_await.append(shutdown)
to_close_await.append(shutdown)
await asyncio.gather(*to_close_await) await asyncio.gather(*to_close_await)
return result 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
cdef bint _isawaitable(object instance): 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 tuple __overrides
cdef int __async_mode 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 object _provide(self, tuple args, dict kwargs)
cpdef void _copy_overridings(self, Provider copied, dict memo) 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 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.""" """Check if async mode is enabled."""
return self.__async_mode == ASYNC_MODE_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.""" """Check if async mode is disabled."""
return self.__async_mode == ASYNC_MODE_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.""" """Check if async mode is undefined."""
return self.__async_mode == ASYNC_MODE_UNDEFINED return self.__async_mode == ASYNC_MODE_UNDEFINED

View File

@ -1,5 +1,5 @@
"""Wiring module.""" """Wiring module."""
import functools
import inspect import inspect
import importlib import importlib
import importlib.machinery import importlib.machinery
@ -895,4 +895,19 @@ _loader = AutoLoader()
# Optimizations # Optimizations
from ._cwiring import _get_sync_patched # noqa 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)