diff --git a/docs/main/changelog.rst b/docs/main/changelog.rst index ecc656bc..3df4dcef 100644 --- a/docs/main/changelog.rst +++ b/docs/main/changelog.rst @@ -7,6 +7,11 @@ that were made in every particular version. From version 0.7.6 *Dependency Injector* framework strictly follows `Semantic versioning`_ +4.26.0 +------ +- Add wiring by string id. +- Improve error message for ``Dependency`` provider missing attribute. + 4.25.1 ------ - Amend docs and add another example for ``@containers.copy()`` decorator. diff --git a/docs/wiring.rst b/docs/wiring.rst index 16126de6..195f469e 100644 --- a/docs/wiring.rst +++ b/docs/wiring.rst @@ -88,6 +88,82 @@ Also you can use ``Provide`` marker to inject a container. :emphasize-lines: 16-19 :lines: 3- +Strings identifiers +------------------- + +You can use wiring with string identifiers. String identifier should match provider name in the container: + +.. literalinclude:: ../examples/wiring/example_string_id.py + :language: python + :emphasize-lines: 17 + :lines: 3- + +With string identifiers you don't need to use a container to specify an injection. + +To specify an injection from a nested container use point ``.`` as a separator: + +.. code-block:: python + + @inject + def foo(service: UserService = Provide['services.user']) -> None: + ... + +You can also use injection modifiers: + +.. code-block:: python + + from dependency_injector.wiring import ( + inject, + Provide, + as_int, + as_float, + as_, + required, + invariant, + provided, + ) + + + @inject + def foo(value: int = Provide['config.option', as_int()]) -> None: + ... + + + @inject + def foo(value: float = Provide['config.option', as_float()]) -> None: + ... + + + @inject + def foo(value: Decimal = Provide['config.option', as_(Decimal)]) -> None: + ... + + @inject + def foo(value: str = Provide['config.option', required()]) -> None: + ... + + @inject + def foo(value: int = Provide['config.option', required().as_int()]) -> None: + ... + + + @inject + def foo(value: int = Provide['config.option', invariant('config.switch')]) -> None: + ... + + @inject + def foo(value: int = Provide['service', provided().foo['bar'].call()]) -> None: + ... + + +To inject a container use special identifier ````: + +.. code-block:: python + + @inject + def foo(container: Container = Provide['']) -> None: + ... + Wiring with modules and packages -------------------------------- diff --git a/examples/wiring/example_string_id.py b/examples/wiring/example_string_id.py new file mode 100644 index 00000000..c18fb4fb --- /dev/null +++ b/examples/wiring/example_string_id.py @@ -0,0 +1,27 @@ +"""Wiring string id example.""" + +import sys + +from dependency_injector import containers, providers +from dependency_injector.wiring import inject, Provide + + +class Service: + ... + + +class Container(containers.DeclarativeContainer): + + service = providers.Factory(Service) + + +@inject +def main(service: Service = Provide['service']) -> None: + ... + + +if __name__ == '__main__': + container = Container() + container.wire(modules=[sys.modules[__name__]]) + + main() diff --git a/src/dependency_injector/__init__.py b/src/dependency_injector/__init__.py index 5e4629b5..46c5e9d0 100644 --- a/src/dependency_injector/__init__.py +++ b/src/dependency_injector/__init__.py @@ -1,6 +1,6 @@ """Top-level package.""" -__version__ = '4.25.1' +__version__ = '4.26.0' """Version number. :type: str diff --git a/src/dependency_injector/providers.c b/src/dependency_injector/providers.c index 37efd4fd..3afb77b9 100644 --- a/src/dependency_injector/providers.c +++ b/src/dependency_injector/providers.c @@ -3561,18 +3561,18 @@ static PyObject *__pyx_builtin_IndexError; static PyObject *__pyx_builtin_DeprecationWarning; static PyObject *__pyx_builtin_NotImplementedError; static PyObject *__pyx_builtin_TypeError; -static PyObject *__pyx_builtin_hex; static PyObject *__pyx_builtin_AttributeError; +static PyObject *__pyx_builtin_hex; static PyObject *__pyx_builtin_filter; static PyObject *__pyx_builtin_IOError; static PyObject *__pyx_builtin_ValueError; static PyObject *__pyx_builtin_KeyError; static PyObject *__pyx_builtin_StopIteration; static const char __pyx_k__2[] = "_"; -static const char __pyx_k__5[] = "<"; -static const char __pyx_k__6[] = "."; -static const char __pyx_k__7[] = "("; -static const char __pyx_k__8[] = "\""; +static const char __pyx_k__5[] = "\""; +static const char __pyx_k__6[] = "<"; +static const char __pyx_k__7[] = "."; +static const char __pyx_k__8[] = "("; static const char __pyx_k__9[] = ">"; static const char __pyx_k_at[] = ") at "; static const char __pyx_k_id[] = "id"; @@ -3694,7 +3694,6 @@ static const char __pyx_k_default[] = "default"; static const char __pyx_k_environ[] = "environ"; static const char __pyx_k_factory[] = "factory"; static const char __pyx_k_genexpr[] = "genexpr"; -static const char __pyx_k_getattr[] = "__getattr__"; static const char __pyx_k_im_func[] = "im_func"; static const char __pyx_k_im_self[] = "im_self"; static const char __pyx_k_inspect[] = "inspect"; @@ -3708,7 +3707,7 @@ static const char __pyx_k_visited[] = "visited"; static const char __pyx_k_Callable[] = "Callable"; static const char __pyx_k_Delegate[] = "Delegate"; static const char __pyx_k_KeyError[] = "KeyError"; -static const char __pyx_k_Provider[] = "Provider"; +static const char __pyx_k_Provider[] = "Provider \""; static const char __pyx_k_Resource[] = "Resource"; static const char __pyx_k_Selector[] = "Selector"; static const char __pyx_k_StringIO[] = "StringIO"; @@ -3771,6 +3770,7 @@ static const char __pyx_k_IndexError[] = "IndexError"; static const char __pyx_k_ItemGetter[] = "ItemGetter"; static const char __pyx_k_List___get[] = "List.__get__"; static const char __pyx_k_MethodType[] = "MethodType"; +static const char __pyx_k_Provider_2[] = "Provider"; static const char __pyx_k_SafeLoader[] = "SafeLoader"; static const char __pyx_k_ValueError[] = "ValueError"; static const char __pyx_k_YamlLoader[] = "YamlLoader"; @@ -3878,6 +3878,7 @@ static const char __pyx_k_ProvidedInstance[] = "ProvidedInstance"; static const char __pyx_k_Providers_module[] = "Providers module."; static const char __pyx_k_clear_attributes[] = "clear_attributes"; static const char __pyx_k_copy_overridings[] = "_copy_overridings"; +static const char __pyx_k_has_no_attribute[] = "\" has no attribute \""; static const char __pyx_k_AbstractCoroutine[] = "AbstractCoroutine"; static const char __pyx_k_AbstractSingleton[] = "AbstractSingleton"; static const char __pyx_k_CoroutineDelegate[] = "CoroutineDelegate"; @@ -4192,11 +4193,12 @@ static PyObject *__pyx_n_s_PickleError; static PyObject *__pyx_n_s_PositionalInjection; static PyObject *__pyx_n_s_ProvidedInstance; static PyObject *__pyx_n_s_ProvidedInstance___get; -static PyObject *__pyx_n_s_Provider; +static PyObject *__pyx_kp_u_Provider; static PyObject *__pyx_kp_s_Provider_0_could_not_be_overridd; static PyObject *__pyx_kp_s_Provider_0_expected_to_get_calla; static PyObject *__pyx_kp_s_Provider_0_expected_to_get_corou; static PyObject *__pyx_kp_s_Provider_0_is_not_overridden; +static PyObject *__pyx_n_s_Provider_2; static PyObject *__pyx_n_s_Provider___get; static PyObject *__pyx_n_s_Pyx_CFunc_object____object; static PyObject *__pyx_n_s_Pyx_CFunc_void____object____ob; @@ -4237,8 +4239,8 @@ static PyObject *__pyx_kp_u__31; static PyObject *__pyx_kp_u__32; static PyObject *__pyx_kp_s__49; static PyObject *__pyx_kp_u__5; -static PyObject *__pyx_kp_s__6; static PyObject *__pyx_kp_u__6; +static PyObject *__pyx_kp_s__7; static PyObject *__pyx_kp_u__7; static PyObject *__pyx_kp_u__8; static PyObject *__pyx_kp_u__9; @@ -4344,8 +4346,8 @@ static PyObject *__pyx_n_s_get_2; static PyObject *__pyx_n_s_get_name; static PyObject *__pyx_n_s_get_self_name; static PyObject *__pyx_n_s_get_self_name_locals_genexpr; -static PyObject *__pyx_n_s_getattr; static PyObject *__pyx_n_s_getstate; +static PyObject *__pyx_kp_u_has_no_attribute; static PyObject *__pyx_n_s_hex; static PyObject *__pyx_n_s_id; static PyObject *__pyx_n_s_im_class; @@ -15698,7 +15700,9 @@ static PyObject *__pyx_pf_19dependency_injector_9providers_10Dependency_6__getat PyObject *__pyx_t_2 = NULL; PyObject *__pyx_t_3 = NULL; int __pyx_t_4; - PyObject *__pyx_t_5 = NULL; + Py_ssize_t __pyx_t_5; + Py_UCS4 __pyx_t_6; + PyObject *__pyx_t_7 = NULL; int __pyx_lineno = 0; const char *__pyx_filename = NULL; int __pyx_clineno = 0; @@ -15745,7 +15749,7 @@ static PyObject *__pyx_pf_19dependency_injector_9providers_10Dependency_6__getat * return getattr(self.__last_overriding, name) * elif self.__default is not UNDEFINED: # <<<<<<<<<<<<<< * return getattr(self.__default, name) - * return super().__getattr__(name) + * raise AttributeError(f'Provider "{self.__class__.__name__}" has no attribute "{name}"') */ __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_n_s_UNDEFINED); if (unlikely(!__pyx_t_3)) __PYX_ERR(1, 686, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_3); @@ -15758,7 +15762,7 @@ static PyObject *__pyx_pf_19dependency_injector_9providers_10Dependency_6__getat * return getattr(self.__last_overriding, name) * elif self.__default is not UNDEFINED: * return getattr(self.__default, name) # <<<<<<<<<<<<<< - * return super().__getattr__(name) + * raise AttributeError(f'Provider "{self.__class__.__name__}" has no attribute "{name}"') * */ __Pyx_XDECREF(__pyx_r); @@ -15776,50 +15780,62 @@ static PyObject *__pyx_pf_19dependency_injector_9providers_10Dependency_6__getat * return getattr(self.__last_overriding, name) * elif self.__default is not UNDEFINED: # <<<<<<<<<<<<<< * return getattr(self.__default, name) - * return super().__getattr__(name) + * raise AttributeError(f'Provider "{self.__class__.__name__}" has no attribute "{name}"') */ } /* "dependency_injector/providers.pyx":688 * elif self.__default is not UNDEFINED: * return getattr(self.__default, name) - * return super().__getattr__(name) # <<<<<<<<<<<<<< + * raise AttributeError(f'Provider "{self.__class__.__name__}" has no attribute "{name}"') # <<<<<<<<<<<<<< * * def __str__(self): */ - __Pyx_XDECREF(__pyx_r); - __pyx_t_3 = PyTuple_New(2); if (unlikely(!__pyx_t_3)) __PYX_ERR(1, 688, __pyx_L1_error) + __pyx_t_2 = PyTuple_New(5); if (unlikely(!__pyx_t_2)) __PYX_ERR(1, 688, __pyx_L1_error) + __Pyx_GOTREF(__pyx_t_2); + __pyx_t_5 = 0; + __pyx_t_6 = 127; + __Pyx_INCREF(__pyx_kp_u_Provider); + __pyx_t_5 += 10; + __Pyx_GIVEREF(__pyx_kp_u_Provider); + PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_kp_u_Provider); + __pyx_t_3 = __Pyx_PyObject_GetAttrStr(((PyObject *)__pyx_v_self), __pyx_n_s_class); if (unlikely(!__pyx_t_3)) __PYX_ERR(1, 688, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_3); - __Pyx_INCREF(((PyObject *)__pyx_ptype_19dependency_injector_9providers_Dependency)); - __Pyx_GIVEREF(((PyObject *)__pyx_ptype_19dependency_injector_9providers_Dependency)); - PyTuple_SET_ITEM(__pyx_t_3, 0, ((PyObject *)__pyx_ptype_19dependency_injector_9providers_Dependency)); - __Pyx_INCREF(((PyObject *)__pyx_v_self)); - __Pyx_GIVEREF(((PyObject *)__pyx_v_self)); - PyTuple_SET_ITEM(__pyx_t_3, 1, ((PyObject *)__pyx_v_self)); - __pyx_t_5 = __Pyx_PyObject_Call(__pyx_builtin_super, __pyx_t_3, NULL); if (unlikely(!__pyx_t_5)) __PYX_ERR(1, 688, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_5); + __pyx_t_7 = __Pyx_PyObject_GetAttrStr(__pyx_t_3, __pyx_n_s_name); if (unlikely(!__pyx_t_7)) __PYX_ERR(1, 688, __pyx_L1_error) + __Pyx_GOTREF(__pyx_t_7); __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; - __pyx_t_3 = __Pyx_PyObject_GetAttrStr(__pyx_t_5, __pyx_n_s_getattr); if (unlikely(!__pyx_t_3)) __PYX_ERR(1, 688, __pyx_L1_error) + __pyx_t_3 = __Pyx_PyObject_FormatSimple(__pyx_t_7, __pyx_empty_unicode); if (unlikely(!__pyx_t_3)) __PYX_ERR(1, 688, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_3); - __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; - __pyx_t_5 = NULL; - if (CYTHON_UNPACK_METHODS && likely(PyMethod_Check(__pyx_t_3))) { - __pyx_t_5 = PyMethod_GET_SELF(__pyx_t_3); - if (likely(__pyx_t_5)) { - PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_3); - __Pyx_INCREF(__pyx_t_5); - __Pyx_INCREF(function); - __Pyx_DECREF_SET(__pyx_t_3, function); - } - } - __pyx_t_2 = (__pyx_t_5) ? __Pyx_PyObject_Call2Args(__pyx_t_3, __pyx_t_5, __pyx_v_name) : __Pyx_PyObject_CallOneArg(__pyx_t_3, __pyx_v_name); - __Pyx_XDECREF(__pyx_t_5); __pyx_t_5 = 0; - if (unlikely(!__pyx_t_2)) __PYX_ERR(1, 688, __pyx_L1_error) + __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0; + __pyx_t_6 = (__Pyx_PyUnicode_MAX_CHAR_VALUE(__pyx_t_3) > __pyx_t_6) ? __Pyx_PyUnicode_MAX_CHAR_VALUE(__pyx_t_3) : __pyx_t_6; + __pyx_t_5 += __Pyx_PyUnicode_GET_LENGTH(__pyx_t_3); + __Pyx_GIVEREF(__pyx_t_3); + PyTuple_SET_ITEM(__pyx_t_2, 1, __pyx_t_3); + __pyx_t_3 = 0; + __Pyx_INCREF(__pyx_kp_u_has_no_attribute); + __pyx_t_5 += 20; + __Pyx_GIVEREF(__pyx_kp_u_has_no_attribute); + PyTuple_SET_ITEM(__pyx_t_2, 2, __pyx_kp_u_has_no_attribute); + __pyx_t_3 = __Pyx_PyObject_FormatSimple(__pyx_v_name, __pyx_empty_unicode); if (unlikely(!__pyx_t_3)) __PYX_ERR(1, 688, __pyx_L1_error) + __Pyx_GOTREF(__pyx_t_3); + __pyx_t_6 = (__Pyx_PyUnicode_MAX_CHAR_VALUE(__pyx_t_3) > __pyx_t_6) ? __Pyx_PyUnicode_MAX_CHAR_VALUE(__pyx_t_3) : __pyx_t_6; + __pyx_t_5 += __Pyx_PyUnicode_GET_LENGTH(__pyx_t_3); + __Pyx_GIVEREF(__pyx_t_3); + PyTuple_SET_ITEM(__pyx_t_2, 3, __pyx_t_3); + __pyx_t_3 = 0; + __Pyx_INCREF(__pyx_kp_u__5); + __pyx_t_5 += 1; + __Pyx_GIVEREF(__pyx_kp_u__5); + PyTuple_SET_ITEM(__pyx_t_2, 4, __pyx_kp_u__5); + __pyx_t_3 = __Pyx_PyUnicode_Join(__pyx_t_2, 5, __pyx_t_5, __pyx_t_6); if (unlikely(!__pyx_t_3)) __PYX_ERR(1, 688, __pyx_L1_error) + __Pyx_GOTREF(__pyx_t_3); + __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; + __pyx_t_2 = __Pyx_PyObject_CallOneArg(__pyx_builtin_AttributeError, __pyx_t_3); if (unlikely(!__pyx_t_2)) __PYX_ERR(1, 688, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_2); __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; - __pyx_r = __pyx_t_2; - __pyx_t_2 = 0; - goto __pyx_L0; + __Pyx_Raise(__pyx_t_2, 0, 0, 0); + __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; + __PYX_ERR(1, 688, __pyx_L1_error) /* "dependency_injector/providers.pyx":683 * return result @@ -15833,7 +15849,7 @@ static PyObject *__pyx_pf_19dependency_injector_9providers_10Dependency_6__getat __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_2); __Pyx_XDECREF(__pyx_t_3); - __Pyx_XDECREF(__pyx_t_5); + __Pyx_XDECREF(__pyx_t_7); __Pyx_AddTraceback("dependency_injector.providers.Dependency.__getattr__", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = NULL; __pyx_L0:; @@ -15843,7 +15859,7 @@ static PyObject *__pyx_pf_19dependency_injector_9providers_10Dependency_6__getat } /* "dependency_injector/providers.pyx":690 - * return super().__getattr__(name) + * raise AttributeError(f'Provider "{self.__class__.__name__}" has no attribute "{name}"') * * def __str__(self): # <<<<<<<<<<<<<< * """Return string representation of provider. @@ -15893,10 +15909,10 @@ static PyObject *__pyx_pf_19dependency_injector_9providers_10Dependency_8__str__ __Pyx_GOTREF(__pyx_t_1); __pyx_t_2 = 0; __pyx_t_3 = 127; - __Pyx_INCREF(__pyx_kp_u__5); + __Pyx_INCREF(__pyx_kp_u__6); __pyx_t_2 += 1; - __Pyx_GIVEREF(__pyx_kp_u__5); - PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_kp_u__5); + __Pyx_GIVEREF(__pyx_kp_u__6); + PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_kp_u__6); __pyx_t_4 = __Pyx_PyObject_GetAttrStr(((PyObject *)__pyx_v_self), __pyx_n_s_class); if (unlikely(!__pyx_t_4)) __PYX_ERR(1, 695, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_4); __pyx_t_5 = __Pyx_PyObject_GetAttrStr(__pyx_t_4, __pyx_n_s_module); if (unlikely(!__pyx_t_5)) __PYX_ERR(1, 695, __pyx_L1_error) @@ -15910,10 +15926,10 @@ static PyObject *__pyx_pf_19dependency_injector_9providers_10Dependency_8__str__ __Pyx_GIVEREF(__pyx_t_4); PyTuple_SET_ITEM(__pyx_t_1, 1, __pyx_t_4); __pyx_t_4 = 0; - __Pyx_INCREF(__pyx_kp_u__6); + __Pyx_INCREF(__pyx_kp_u__7); __pyx_t_2 += 1; - __Pyx_GIVEREF(__pyx_kp_u__6); - PyTuple_SET_ITEM(__pyx_t_1, 2, __pyx_kp_u__6); + __Pyx_GIVEREF(__pyx_kp_u__7); + PyTuple_SET_ITEM(__pyx_t_1, 2, __pyx_kp_u__7); __pyx_t_4 = __Pyx_PyObject_GetAttrStr(((PyObject *)__pyx_v_self), __pyx_n_s_class); if (unlikely(!__pyx_t_4)) __PYX_ERR(1, 695, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_4); __pyx_t_5 = __Pyx_PyObject_GetAttrStr(__pyx_t_4, __pyx_n_s_name); if (unlikely(!__pyx_t_5)) __PYX_ERR(1, 695, __pyx_L1_error) @@ -15944,10 +15960,10 @@ static PyObject *__pyx_pf_19dependency_injector_9providers_10Dependency_8__str__ __Pyx_GOTREF(__pyx_t_4); __pyx_t_2 = 0; __pyx_t_3 = 127; - __Pyx_INCREF(__pyx_kp_u__7); + __Pyx_INCREF(__pyx_kp_u__8); __pyx_t_2 += 1; - __Pyx_GIVEREF(__pyx_kp_u__7); - PyTuple_SET_ITEM(__pyx_t_4, 0, __pyx_kp_u__7); + __Pyx_GIVEREF(__pyx_kp_u__8); + PyTuple_SET_ITEM(__pyx_t_4, 0, __pyx_kp_u__8); __pyx_t_1 = __pyx_v_self->__pyx___instance_of; __Pyx_INCREF(__pyx_t_1); __pyx_t_5 = PyObject_Repr(__pyx_t_1); if (unlikely(!__pyx_t_5)) __PYX_ERR(1, 696, __pyx_L1_error) @@ -16025,10 +16041,10 @@ static PyObject *__pyx_pf_19dependency_injector_9providers_10Dependency_8__str__ __Pyx_GIVEREF(__pyx_t_5); PyTuple_SET_ITEM(__pyx_t_4, 1, __pyx_t_5); __pyx_t_5 = 0; - __Pyx_INCREF(__pyx_kp_u__8); + __Pyx_INCREF(__pyx_kp_u__5); __pyx_t_2 += 1; - __Pyx_GIVEREF(__pyx_kp_u__8); - PyTuple_SET_ITEM(__pyx_t_4, 2, __pyx_kp_u__8); + __Pyx_GIVEREF(__pyx_kp_u__5); + PyTuple_SET_ITEM(__pyx_t_4, 2, __pyx_kp_u__5); __pyx_t_5 = __Pyx_PyUnicode_Join(__pyx_t_4, 3, __pyx_t_2, __pyx_t_3); if (unlikely(!__pyx_t_5)) __PYX_ERR(1, 698, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_5); __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; @@ -16072,7 +16088,7 @@ static PyObject *__pyx_pf_19dependency_injector_9providers_10Dependency_8__str__ goto __pyx_L0; /* "dependency_injector/providers.pyx":690 - * return super().__getattr__(name) + * raise AttributeError(f'Provider "{self.__class__.__name__}" has no attribute "{name}"') * * def __str__(self): # <<<<<<<<<<<<<< * """Return string representation of provider. @@ -16793,7 +16809,7 @@ static PyObject *__pyx_pf_19dependency_injector_9providers_10Dependency_11parent __pyx_t_4 = __Pyx_PyObject_FormatSimple(__pyx_t_3, __pyx_empty_unicode); if (unlikely(!__pyx_t_4)) __PYX_ERR(1, 754, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_4); __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; - __pyx_t_3 = __Pyx_PyUnicode_Concat(__pyx_t_4, __pyx_kp_u__6); if (unlikely(!__pyx_t_3)) __PYX_ERR(1, 754, __pyx_L1_error) + __pyx_t_3 = __Pyx_PyUnicode_Concat(__pyx_t_4, __pyx_kp_u__7); if (unlikely(!__pyx_t_3)) __PYX_ERR(1, 754, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_3); __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; __pyx_t_4 = PyNumber_InPlaceAdd(__pyx_v_name, __pyx_t_3); if (unlikely(!__pyx_t_4)) __PYX_ERR(1, 754, __pyx_L1_error) @@ -20360,10 +20376,10 @@ static PyObject *__pyx_pf_19dependency_injector_9providers_21DependenciesContain __Pyx_GIVEREF(__pyx_t_7); PyTuple_SET_ITEM(__pyx_t_2, 1, __pyx_t_7); __pyx_t_7 = 0; - __Pyx_INCREF(__pyx_kp_u__8); + __Pyx_INCREF(__pyx_kp_u__5); __pyx_t_11 += 1; - __Pyx_GIVEREF(__pyx_kp_u__8); - PyTuple_SET_ITEM(__pyx_t_2, 2, __pyx_kp_u__8); + __Pyx_GIVEREF(__pyx_kp_u__5); + PyTuple_SET_ITEM(__pyx_t_2, 2, __pyx_kp_u__5); __pyx_t_7 = __Pyx_PyUnicode_Join(__pyx_t_2, 3, __pyx_t_11, __pyx_t_12); if (unlikely(!__pyx_t_7)) __PYX_ERR(1, 955, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_7); __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; @@ -20576,7 +20592,7 @@ static PyObject *__pyx_pf_19dependency_injector_9providers_21DependenciesContain __pyx_t_4 = __Pyx_PyObject_FormatSimple(__pyx_t_3, __pyx_empty_unicode); if (unlikely(!__pyx_t_4)) __PYX_ERR(1, 970, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_4); __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; - __pyx_t_3 = __Pyx_PyUnicode_Concat(__pyx_t_4, __pyx_kp_u__6); if (unlikely(!__pyx_t_3)) __PYX_ERR(1, 970, __pyx_L1_error) + __pyx_t_3 = __Pyx_PyUnicode_Concat(__pyx_t_4, __pyx_kp_u__7); if (unlikely(!__pyx_t_3)) __PYX_ERR(1, 970, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_3); __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; __pyx_t_4 = PyNumber_InPlaceAdd(__pyx_v_name, __pyx_t_3); if (unlikely(!__pyx_t_4)) __PYX_ERR(1, 970, __pyx_L1_error) @@ -22309,7 +22325,7 @@ static int __pyx_pf_19dependency_injector_9providers_8Callable___init__(struct _ PyTuple_SET_ITEM(__pyx_t_7, 1, __pyx_t_9); __pyx_t_8 = 0; __pyx_t_9 = 0; - __pyx_t_9 = __Pyx_PyString_Join(__pyx_kp_s__6, __pyx_t_7); if (unlikely(!__pyx_t_9)) __PYX_ERR(1, 1061, __pyx_L1_error) + __pyx_t_9 = __Pyx_PyString_Join(__pyx_kp_s__7, __pyx_t_7); if (unlikely(!__pyx_t_9)) __PYX_ERR(1, 1061, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_9); __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0; @@ -26839,7 +26855,7 @@ static int __pyx_pf_19dependency_injector_9providers_9Coroutine___init__(struct PyTuple_SET_ITEM(__pyx_t_7, 1, __pyx_t_9); __pyx_t_8 = 0; __pyx_t_9 = 0; - __pyx_t_9 = __Pyx_PyString_Join(__pyx_kp_s__6, __pyx_t_7); if (unlikely(!__pyx_t_9)) __PYX_ERR(1, 1312, __pyx_L1_error) + __pyx_t_9 = __Pyx_PyString_Join(__pyx_kp_s__7, __pyx_t_7); if (unlikely(!__pyx_t_9)) __PYX_ERR(1, 1312, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_9); __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0; @@ -30940,7 +30956,7 @@ static PyObject *__pyx_pf_19dependency_injector_9providers_19ConfigurationOption * segment() if is_provider(segment) else segment for segment in self.__name * ) */ - __pyx_t_2 = __Pyx_PyString_Join(__pyx_kp_s__6, __pyx_t_1); if (unlikely(!__pyx_t_2)) __PYX_ERR(1, 1462, __pyx_L1_error) + __pyx_t_2 = __Pyx_PyString_Join(__pyx_kp_s__7, __pyx_t_1); if (unlikely(!__pyx_t_2)) __PYX_ERR(1, 1462, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_2); __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; __pyx_r = __pyx_t_2; @@ -31104,7 +31120,7 @@ static PyObject *__pyx_pf_19dependency_injector_9providers_19ConfigurationOption PyTuple_SET_ITEM(__pyx_t_3, 1, __pyx_t_2); __pyx_t_1 = 0; __pyx_t_2 = 0; - __pyx_t_2 = __Pyx_PyString_Join(__pyx_kp_s__6, __pyx_t_3); if (unlikely(!__pyx_t_2)) __PYX_ERR(1, 1471, __pyx_L1_error) + __pyx_t_2 = __Pyx_PyString_Join(__pyx_kp_s__7, __pyx_t_3); if (unlikely(!__pyx_t_2)) __PYX_ERR(1, 1471, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_2); __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; __pyx_r = __pyx_t_2; @@ -37367,7 +37383,7 @@ static PyObject *__pyx_pf_19dependency_injector_9providers_13Configuration_16get __Pyx_DECREF_SET(__pyx_t_2, function); } } - __pyx_t_1 = (__pyx_t_3) ? __Pyx_PyObject_Call2Args(__pyx_t_2, __pyx_t_3, __pyx_kp_s__6) : __Pyx_PyObject_CallOneArg(__pyx_t_2, __pyx_kp_s__6); + __pyx_t_1 = (__pyx_t_3) ? __Pyx_PyObject_Call2Args(__pyx_t_2, __pyx_t_3, __pyx_kp_s__7) : __Pyx_PyObject_CallOneArg(__pyx_t_2, __pyx_kp_s__7); __Pyx_XDECREF(__pyx_t_3); __pyx_t_3 = 0; if (unlikely(!__pyx_t_1)) __PYX_ERR(1, 1803, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); @@ -37801,7 +37817,7 @@ static PyObject *__pyx_pf_19dependency_injector_9providers_13Configuration_18set __Pyx_DECREF_SET(__pyx_t_1, function); } } - __pyx_t_2 = (__pyx_t_3) ? __Pyx_PyObject_Call2Args(__pyx_t_1, __pyx_t_3, __pyx_kp_s__6) : __Pyx_PyObject_CallOneArg(__pyx_t_1, __pyx_kp_s__6); + __pyx_t_2 = (__pyx_t_3) ? __Pyx_PyObject_Call2Args(__pyx_t_1, __pyx_t_3, __pyx_kp_s__7) : __Pyx_PyObject_CallOneArg(__pyx_t_1, __pyx_kp_s__7); __Pyx_XDECREF(__pyx_t_3); __pyx_t_3 = 0; if (unlikely(!__pyx_t_2)) __PYX_ERR(1, 1829, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_2); @@ -69479,10 +69495,10 @@ static PyObject *__pyx_pf_19dependency_injector_9providers_9Container_10resolve_ __Pyx_GIVEREF(__pyx_t_7); PyTuple_SET_ITEM(__pyx_t_2, 1, __pyx_t_7); __pyx_t_7 = 0; - __Pyx_INCREF(__pyx_kp_u__8); + __Pyx_INCREF(__pyx_kp_u__5); __pyx_t_11 += 1; - __Pyx_GIVEREF(__pyx_kp_u__8); - PyTuple_SET_ITEM(__pyx_t_2, 2, __pyx_kp_u__8); + __Pyx_GIVEREF(__pyx_kp_u__5); + PyTuple_SET_ITEM(__pyx_t_2, 2, __pyx_kp_u__5); __pyx_t_7 = __Pyx_PyUnicode_Join(__pyx_t_2, 3, __pyx_t_11, __pyx_t_12); if (unlikely(!__pyx_t_7)) __PYX_ERR(1, 3628, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_7); __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; @@ -69695,7 +69711,7 @@ static PyObject *__pyx_pf_19dependency_injector_9providers_9Container_11parent_n __pyx_t_4 = __Pyx_PyObject_FormatSimple(__pyx_t_3, __pyx_empty_unicode); if (unlikely(!__pyx_t_4)) __PYX_ERR(1, 3643, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_4); __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; - __pyx_t_3 = __Pyx_PyUnicode_Concat(__pyx_t_4, __pyx_kp_u__6); if (unlikely(!__pyx_t_3)) __PYX_ERR(1, 3643, __pyx_L1_error) + __pyx_t_3 = __Pyx_PyUnicode_Concat(__pyx_t_4, __pyx_kp_u__7); if (unlikely(!__pyx_t_3)) __PYX_ERR(1, 3643, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_3); __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; __pyx_t_4 = PyNumber_InPlaceAdd(__pyx_v_name, __pyx_t_3); if (unlikely(!__pyx_t_4)) __PYX_ERR(1, 3643, __pyx_L1_error) @@ -71573,7 +71589,7 @@ static PyObject *__pyx_pf_19dependency_injector_9providers_8Selector_6__str__(st PyTuple_SET_ITEM(__pyx_t_3, 1, __pyx_t_5); __pyx_t_4 = 0; __pyx_t_5 = 0; - __pyx_t_5 = __Pyx_PyString_Join(__pyx_kp_s__6, __pyx_t_3); if (unlikely(!__pyx_t_5)) __PYX_ERR(1, 3730, __pyx_L1_error) + __pyx_t_5 = __Pyx_PyString_Join(__pyx_kp_s__7, __pyx_t_3); if (unlikely(!__pyx_t_5)) __PYX_ERR(1, 3730, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_5); __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; if (PyDict_SetItem(__pyx_t_2, __pyx_n_s_provider, __pyx_t_5) < 0) __PYX_ERR(1, 3730, __pyx_L1_error) @@ -78514,10 +78530,10 @@ static PyObject *__pyx_pf_19dependency_injector_9providers_12MethodCaller_2__rep __Pyx_GIVEREF(__pyx_t_4); PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_t_4); __pyx_t_4 = 0; - __Pyx_INCREF(__pyx_kp_u__7); + __Pyx_INCREF(__pyx_kp_u__8); __pyx_t_2 += 1; - __Pyx_GIVEREF(__pyx_kp_u__7); - PyTuple_SET_ITEM(__pyx_t_1, 1, __pyx_kp_u__7); + __Pyx_GIVEREF(__pyx_kp_u__8); + PyTuple_SET_ITEM(__pyx_t_1, 1, __pyx_kp_u__8); __pyx_t_4 = __Pyx_PyObject_FormatSimple(((PyObject *)__pyx_v_self->__pyx___provider), __pyx_empty_unicode); if (unlikely(!__pyx_t_4)) __PYX_ERR(1, 3991, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_4); __pyx_t_3 = (__Pyx_PyUnicode_MAX_CHAR_VALUE(__pyx_t_4) > __pyx_t_3) ? __Pyx_PyUnicode_MAX_CHAR_VALUE(__pyx_t_4) : __pyx_t_3; @@ -83721,7 +83737,7 @@ static PyObject *__pyx_f_19dependency_injector_9providers_represent_provider(PyO PyTuple_SET_ITEM(__pyx_t_3, 1, __pyx_t_5); __pyx_t_4 = 0; __pyx_t_5 = 0; - __pyx_t_5 = __Pyx_PyString_Join(__pyx_kp_s__6, __pyx_t_3); if (unlikely(!__pyx_t_5)) __PYX_ERR(1, 4248, __pyx_L1_error) + __pyx_t_5 = __Pyx_PyString_Join(__pyx_kp_s__7, __pyx_t_3); if (unlikely(!__pyx_t_5)) __PYX_ERR(1, 4248, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_5); __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; if (PyDict_SetItem(__pyx_t_2, __pyx_n_s_provider, __pyx_t_5) < 0) __PYX_ERR(1, 4248, __pyx_L1_error) @@ -121363,11 +121379,12 @@ static __Pyx_StringTabEntry __pyx_string_tab[] = { {&__pyx_n_s_PositionalInjection, __pyx_k_PositionalInjection, sizeof(__pyx_k_PositionalInjection), 0, 0, 1, 1}, {&__pyx_n_s_ProvidedInstance, __pyx_k_ProvidedInstance, sizeof(__pyx_k_ProvidedInstance), 0, 0, 1, 1}, {&__pyx_n_s_ProvidedInstance___get, __pyx_k_ProvidedInstance___get, sizeof(__pyx_k_ProvidedInstance___get), 0, 0, 1, 1}, - {&__pyx_n_s_Provider, __pyx_k_Provider, sizeof(__pyx_k_Provider), 0, 0, 1, 1}, + {&__pyx_kp_u_Provider, __pyx_k_Provider, sizeof(__pyx_k_Provider), 0, 1, 0, 0}, {&__pyx_kp_s_Provider_0_could_not_be_overridd, __pyx_k_Provider_0_could_not_be_overridd, sizeof(__pyx_k_Provider_0_could_not_be_overridd), 0, 0, 1, 0}, {&__pyx_kp_s_Provider_0_expected_to_get_calla, __pyx_k_Provider_0_expected_to_get_calla, sizeof(__pyx_k_Provider_0_expected_to_get_calla), 0, 0, 1, 0}, {&__pyx_kp_s_Provider_0_expected_to_get_corou, __pyx_k_Provider_0_expected_to_get_corou, sizeof(__pyx_k_Provider_0_expected_to_get_corou), 0, 0, 1, 0}, {&__pyx_kp_s_Provider_0_is_not_overridden, __pyx_k_Provider_0_is_not_overridden, sizeof(__pyx_k_Provider_0_is_not_overridden), 0, 0, 1, 0}, + {&__pyx_n_s_Provider_2, __pyx_k_Provider_2, sizeof(__pyx_k_Provider_2), 0, 0, 1, 1}, {&__pyx_n_s_Provider___get, __pyx_k_Provider___get, sizeof(__pyx_k_Provider___get), 0, 0, 1, 1}, {&__pyx_n_s_Pyx_CFunc_object____object, __pyx_k_Pyx_CFunc_object____object, sizeof(__pyx_k_Pyx_CFunc_object____object), 0, 0, 1, 1}, {&__pyx_n_s_Pyx_CFunc_void____object____ob, __pyx_k_Pyx_CFunc_void____object____ob, sizeof(__pyx_k_Pyx_CFunc_void____object____ob), 0, 0, 1, 1}, @@ -121408,8 +121425,8 @@ static __Pyx_StringTabEntry __pyx_string_tab[] = { {&__pyx_kp_u__32, __pyx_k__32, sizeof(__pyx_k__32), 0, 1, 0, 0}, {&__pyx_kp_s__49, __pyx_k__49, sizeof(__pyx_k__49), 0, 0, 1, 0}, {&__pyx_kp_u__5, __pyx_k__5, sizeof(__pyx_k__5), 0, 1, 0, 0}, - {&__pyx_kp_s__6, __pyx_k__6, sizeof(__pyx_k__6), 0, 0, 1, 0}, {&__pyx_kp_u__6, __pyx_k__6, sizeof(__pyx_k__6), 0, 1, 0, 0}, + {&__pyx_kp_s__7, __pyx_k__7, sizeof(__pyx_k__7), 0, 0, 1, 0}, {&__pyx_kp_u__7, __pyx_k__7, sizeof(__pyx_k__7), 0, 1, 0, 0}, {&__pyx_kp_u__8, __pyx_k__8, sizeof(__pyx_k__8), 0, 1, 0, 0}, {&__pyx_kp_u__9, __pyx_k__9, sizeof(__pyx_k__9), 0, 1, 0, 0}, @@ -121515,8 +121532,8 @@ static __Pyx_StringTabEntry __pyx_string_tab[] = { {&__pyx_n_s_get_name, __pyx_k_get_name, sizeof(__pyx_k_get_name), 0, 0, 1, 1}, {&__pyx_n_s_get_self_name, __pyx_k_get_self_name, sizeof(__pyx_k_get_self_name), 0, 0, 1, 1}, {&__pyx_n_s_get_self_name_locals_genexpr, __pyx_k_get_self_name_locals_genexpr, sizeof(__pyx_k_get_self_name_locals_genexpr), 0, 0, 1, 1}, - {&__pyx_n_s_getattr, __pyx_k_getattr, sizeof(__pyx_k_getattr), 0, 0, 1, 1}, {&__pyx_n_s_getstate, __pyx_k_getstate, sizeof(__pyx_k_getstate), 0, 0, 1, 1}, + {&__pyx_kp_u_has_no_attribute, __pyx_k_has_no_attribute, sizeof(__pyx_k_has_no_attribute), 0, 1, 0, 0}, {&__pyx_n_s_hex, __pyx_k_hex, sizeof(__pyx_k_hex), 0, 0, 1, 1}, {&__pyx_n_s_id, __pyx_k_id, sizeof(__pyx_k_id), 0, 0, 1, 1}, {&__pyx_n_s_im_class, __pyx_k_im_class, sizeof(__pyx_k_im_class), 0, 0, 1, 1}, @@ -121740,8 +121757,8 @@ static CYTHON_SMALL_CODE int __Pyx_InitCachedBuiltins(void) { __pyx_builtin_DeprecationWarning = __Pyx_GetBuiltinName(__pyx_n_s_DeprecationWarning); if (!__pyx_builtin_DeprecationWarning) __PYX_ERR(1, 325, __pyx_L1_error) __pyx_builtin_NotImplementedError = __Pyx_GetBuiltinName(__pyx_n_s_NotImplementedError); if (!__pyx_builtin_NotImplementedError) __PYX_ERR(1, 385, __pyx_L1_error) __pyx_builtin_TypeError = __Pyx_GetBuiltinName(__pyx_n_s_TypeError); if (!__pyx_builtin_TypeError) __PYX_ERR(1, 608, __pyx_L1_error) + __pyx_builtin_AttributeError = __Pyx_GetBuiltinName(__pyx_n_s_AttributeError); if (!__pyx_builtin_AttributeError) __PYX_ERR(1, 688, __pyx_L1_error) __pyx_builtin_hex = __Pyx_GetBuiltinName(__pyx_n_s_hex); if (!__pyx_builtin_hex) __PYX_ERR(1, 696, __pyx_L1_error) - __pyx_builtin_AttributeError = __Pyx_GetBuiltinName(__pyx_n_s_AttributeError); if (!__pyx_builtin_AttributeError) __PYX_ERR(1, 880, __pyx_L1_error) __pyx_builtin_filter = __Pyx_GetBuiltinName(__pyx_n_s_filter); if (!__pyx_builtin_filter) __PYX_ERR(1, 1193, __pyx_L1_error) __pyx_builtin_IOError = __Pyx_GetBuiltinName(__pyx_n_s_IOError); if (!__pyx_builtin_IOError) __PYX_ERR(1, 1536, __pyx_L1_error) __pyx_builtin_ValueError = __Pyx_GetBuiltinName(__pyx_n_s_ValueError); if (!__pyx_builtin_ValueError) __PYX_ERR(1, 1648, __pyx_L1_error) @@ -122356,7 +122373,7 @@ static int __Pyx_modinit_type_init_code(void) { } #endif if (__Pyx_SetVtable(__pyx_type_19dependency_injector_9providers_Provider.tp_dict, __pyx_vtabptr_19dependency_injector_9providers_Provider) < 0) __PYX_ERR(1, 119, __pyx_L1_error) - if (PyObject_SetAttr(__pyx_m, __pyx_n_s_Provider, (PyObject *)&__pyx_type_19dependency_injector_9providers_Provider) < 0) __PYX_ERR(1, 119, __pyx_L1_error) + if (PyObject_SetAttr(__pyx_m, __pyx_n_s_Provider_2, (PyObject *)&__pyx_type_19dependency_injector_9providers_Provider) < 0) __PYX_ERR(1, 119, __pyx_L1_error) if (__Pyx_setup_reduce((PyObject*)&__pyx_type_19dependency_injector_9providers_Provider) < 0) __PYX_ERR(1, 119, __pyx_L1_error) __pyx_ptype_19dependency_injector_9providers_Provider = &__pyx_type_19dependency_injector_9providers_Provider; __pyx_vtabptr_19dependency_injector_9providers_Object = &__pyx_vtable_19dependency_injector_9providers_Object; diff --git a/src/dependency_injector/providers.pyx b/src/dependency_injector/providers.pyx index 9b2dcee2..7e44ffd2 100644 --- a/src/dependency_injector/providers.pyx +++ b/src/dependency_injector/providers.pyx @@ -685,7 +685,7 @@ cdef class Dependency(Provider): return getattr(self.__last_overriding, name) elif self.__default is not UNDEFINED: return getattr(self.__default, name) - return super().__getattr__(name) + raise AttributeError(f'Provider "{self.__class__.__name__}" has no attribute "{name}"') def __str__(self): """Return string representation of provider. diff --git a/src/dependency_injector/wiring.py b/src/dependency_injector/wiring.py index 205eee04..c7debfdf 100644 --- a/src/dependency_injector/wiring.py +++ b/src/dependency_injector/wiring.py @@ -50,6 +50,12 @@ __all__ = ( 'wire', 'unwire', 'inject', + 'as_int', + 'as_float', + 'as_', + 'required', + 'invariant', + 'provided', 'Provide', 'Provider', 'Closing', @@ -85,16 +91,23 @@ _patched_registry = Registry() class ProvidersMap: + CONTAINER_STRING_ID = '' + def __init__(self, container): self._container = container self._map = self._create_providers_map( current_container=container, - original_container=container.declarative_parent, + original_container=( + container.declarative_parent + if container.declarative_parent + else container + ), ) def resolve_provider( self, - provider: providers.Provider, + provider: Union[providers.Provider, str], + modifier: Optional['Modifier'] = None, ) -> Optional[providers.Provider]: if isinstance(provider, providers.Delegate): return self._resolve_delegate(provider) @@ -109,14 +122,29 @@ class ProvidersMap: return self._resolve_config_option(provider) elif isinstance(provider, providers.TypedConfigurationOption): return self._resolve_config_option(provider.option, as_=provider.provides) + elif isinstance(provider, str): + return self._resolve_string_id(provider, modifier) else: return self._resolve_provider(provider) - def _resolve_delegate( + def _resolve_string_id( self, - original: providers.Delegate, + id: str, + modifier: Optional['Modifier'] = None, ) -> Optional[providers.Provider]: - return self._resolve_provider(original.provides) + if id == self.CONTAINER_STRING_ID: + return self._container.__self__ + + provider = self._container + for segment in id.split('.'): + try: + provider = getattr(provider, segment) + except AttributeError: + return None + + if modifier: + provider = modifier.modify(provider, providers_map=self) + return provider def _resolve_provided_instance( self, @@ -151,6 +179,12 @@ class ProvidersMap: return new + def _resolve_delegate( + self, + original: providers.Delegate, + ) -> Optional[providers.Provider]: + return self._resolve_provider(original.provides) + def _resolve_config_option( self, original: providers.ConfigurationOption, @@ -184,7 +218,7 @@ class ProvidersMap: try: return self._map[original] except KeyError: - pass + return None @classmethod def _create_providers_map( @@ -381,7 +415,7 @@ def _fetch_reference_injections( def _bind_injections(fn: Callable[..., Any], providers_map: ProvidersMap) -> None: for injection, marker in fn.__reference_injections__.items(): - provider = providers_map.resolve_provider(marker.provider) + provider = providers_map.resolve_provider(marker.provider, marker.modifier) if provider is None: continue @@ -516,20 +550,161 @@ def _is_declarative_container(instance: Any) -> bool: and getattr(instance, 'declarative_parent', None) is None) +class Modifier: + + def modify( + self, + provider: providers.ConfigurationOption, + providers_map: ProvidersMap, + ) -> providers.Provider: + ... + + +class TypeModifier(Modifier): + + def __init__(self, type_: Type): + self.type_ = type_ + + def modify( + self, + provider: providers.ConfigurationOption, + providers_map: ProvidersMap, + ) -> providers.Provider: + return provider.as_(self.type_) + + +def as_int() -> TypeModifier: + """Return int type modifier.""" + return TypeModifier(int) + + +def as_float() -> TypeModifier: + """Return float type modifier.""" + return TypeModifier(float) + + +def as_(type_: Type) -> TypeModifier: + """Return custom type modifier.""" + return TypeModifier(type_) + + +class RequiredModifier(Modifier): + + def __init__(self): + self.type_modifier = None + + def as_int(self) -> 'RequiredModifier': + self.type_modifier = TypeModifier(int) + return self + + def as_float(self) -> 'RequiredModifier': + self.type_modifier = TypeModifier(float) + return self + + def as_(self, type_: Type) -> 'RequiredModifier': + self.type_modifier = TypeModifier(type_) + return self + + def modify( + self, + provider: providers.ConfigurationOption, + providers_map: ProvidersMap, + ) -> providers.Provider: + provider = provider.required() + if self.type_modifier: + provider = provider.as_(self.type_modifier.type_) + return provider + + +def required() -> RequiredModifier: + """Return required modifier.""" + return RequiredModifier() + + +class InvariantModifier(Modifier): + + def __init__(self, id: str) -> None: + self.id = id + + def modify( + self, + provider: providers.ConfigurationOption, + providers_map: ProvidersMap, + ) -> providers.Provider: + invariant_segment = providers_map.resolve_provider(self.id) + return provider[invariant_segment] + + +def invariant(id: str) -> InvariantModifier: + """Return invariant modifier.""" + return InvariantModifier(id) + + +class ProvidedInstance(Modifier): + + TYPE_ATTRIBUTE = 'attr' + TYPE_ITEM = 'item' + TYPE_CALL = 'call' + + def __init__(self): + self.segments = [] + + def __getattr__(self, item): + self.segments.append((self.TYPE_ATTRIBUTE, item)) + return self + + def __getitem__(self, item): + self.segments.append((self.TYPE_ITEM, item)) + return self + + def call(self): + self.segments.append((self.TYPE_CALL, None)) + return self + + def modify( + self, + provider: providers.ConfigurationOption, + providers_map: ProvidersMap, + ) -> providers.Provider: + provider = provider.provided + for type_, value in self.segments: + if type_ == ProvidedInstance.TYPE_ATTRIBUTE: + provider = getattr(provider, value) + elif type_ == ProvidedInstance.TYPE_ITEM: + provider = provider[value] + elif type_ == ProvidedInstance.TYPE_CALL: + provider = provider.call() + return provider + + +def provided() -> ProvidedInstance: + """Return provided instance modifier.""" + return ProvidedInstance() + + class ClassGetItemMeta(GenericMeta): def __getitem__(cls, item): # Spike for Python 3.6 + if isinstance(item, tuple): + return cls(*item) return cls(item) class _Marker(Generic[T], metaclass=ClassGetItemMeta): - def __init__(self, provider: Union[providers.Provider, Container]) -> None: + def __init__( + self, + provider: Union[providers.Provider, Container, str], + modifier: Optional[Modifier] = None, + ) -> None: if _is_declarative_container(provider): provider = provider.__self__ - self.provider: providers.Provider = provider + self.provider = provider + self.modifier = modifier def __class_getitem__(cls, item) -> T: + if isinstance(item, tuple): + return cls(*item) return cls(item) def __call__(self) -> T: diff --git a/tests/unit/samples/wiringstringidssamples/__init__.py b/tests/unit/samples/wiringstringidssamples/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/unit/samples/wiringstringidssamples/asyncinjections.py b/tests/unit/samples/wiringstringidssamples/asyncinjections.py new file mode 100644 index 00000000..6ea88eb2 --- /dev/null +++ b/tests/unit/samples/wiringstringidssamples/asyncinjections.py @@ -0,0 +1,50 @@ +import asyncio + +from dependency_injector import containers, providers +from dependency_injector.wiring import inject, Provide, Closing + + +class TestResource: + def __init__(self): + self.init_counter = 0 + self.shutdown_counter = 0 + + def reset_counters(self): + self.init_counter = 0 + self.shutdown_counter = 0 + + +resource1 = TestResource() +resource2 = TestResource() + + +async def async_resource(resource): + await asyncio.sleep(0.001) + resource.init_counter += 1 + + yield resource + + await asyncio.sleep(0.001) + resource.shutdown_counter += 1 + + +class Container(containers.DeclarativeContainer): + + resource1 = providers.Resource(async_resource, providers.Object(resource1)) + resource2 = providers.Resource(async_resource, providers.Object(resource2)) + + +@inject +async def async_injection( + resource1: object = Provide['resource1'], + resource2: object = Provide['resource2'], +): + return resource1, resource2 + + +@inject +async def async_injection_with_closing( + resource1: object = Closing[Provide['resource1']], + resource2: object = Closing[Provide['resource2']], +): + return resource1, resource2 diff --git a/tests/unit/samples/wiringstringidssamples/container.py b/tests/unit/samples/wiringstringidssamples/container.py new file mode 100644 index 00000000..95681dbf --- /dev/null +++ b/tests/unit/samples/wiringstringidssamples/container.py @@ -0,0 +1,17 @@ +from dependency_injector import containers, providers + +from .service import Service + + +class SubContainer(containers.DeclarativeContainer): + + int_object = providers.Object(1) + + +class Container(containers.DeclarativeContainer): + + config = providers.Configuration() + + service = providers.Factory(Service) + + sub = providers.Container(SubContainer) diff --git a/tests/unit/samples/wiringstringidssamples/module.py b/tests/unit/samples/wiringstringidssamples/module.py new file mode 100644 index 00000000..f83a0a27 --- /dev/null +++ b/tests/unit/samples/wiringstringidssamples/module.py @@ -0,0 +1,116 @@ +"""Test module for wiring.""" + +from decimal import Decimal +from typing import Callable + +from dependency_injector.wiring import inject, Provide, Provider, as_int, as_, required, invariant, provided + +from .container import Container +from .service import Service + + +class TestClass: + + @inject + def __init__(self, service: Service = Provide['service']): + self.service = service + + @inject + def method(self, service: Service = Provide['service']): + return service + + @classmethod + @inject + def class_method(cls, service: Service = Provide['service']): + return service + + @staticmethod + @inject + def static_method(service: Service = Provide['service']): + return service + + +@inject +def test_function(service: Service = Provide['service']): + return service + + +@inject +def test_function_provider(service_provider: Callable[..., Service] = Provider['service']): + service = service_provider() + return service + + +@inject +def test_config_value( + value_int: int = Provide['config.a.b.c', as_int()], + value_str: str = Provide['config.a.b.c', as_(str)], + value_decimal: Decimal = Provide['config.a.b.c', as_(Decimal)], + value_required: str = Provide['config.a.b.c', required()], + value_required_int: int = Provide['config.a.b.c', required().as_int()], + value_required_str: str = Provide['config.a.b.c', required().as_(str)], + value_required_decimal: str = Provide['config.a.b.c', required().as_(Decimal)], +): + return ( + value_int, + value_str, + value_decimal, + value_required, + value_required_int, + value_required_str, + value_required_decimal, + ) + + +@inject +def test_config_value_required_undefined( + value_required: int = Provide['config.a.b.c', required()], +): + return value_required + + +@inject +def test_provide_provider(service_provider: Callable[..., Service] = Provide['service.provider']): + service = service_provider() + return service + + +@inject +def test_provided_instance(some_value: int = Provide['service', provided().foo['bar'].call()]): + return some_value + + +@inject +def test_subcontainer_provider(some_value: int = Provide['sub.int_object']): + return some_value + + +@inject +def test_config_invariant(some_value: int = Provide['config.option', invariant('config.switch')]): + return some_value + + +@inject +def test_provide_from_different_containers( + service: Service = Provide['service'], + some_value: int = Provide['int_object'], +): + return service, some_value + + +class ClassDecorator: + def __init__(self, fn): + self._fn = fn + + def __call__(self, *args, **kwargs): + return self._fn(*args, **kwargs) + + +@ClassDecorator +@inject +def test_class_decorator(service: Service = Provide['service']): + return service + + +def test_container(container: Container = Provide['']): + return container.service() diff --git a/tests/unit/samples/wiringstringidssamples/package/__init__.py b/tests/unit/samples/wiringstringidssamples/package/__init__.py new file mode 100644 index 00000000..bdc34ed2 --- /dev/null +++ b/tests/unit/samples/wiringstringidssamples/package/__init__.py @@ -0,0 +1,11 @@ +import sys + +if sys.version_info >= (3, 6): + from dependency_injector.wiring import Provide + + from ..container import Container + from ..service import Service + + + def test_package_function(service: Service = Provide[Container.service]): + return service diff --git a/tests/unit/samples/wiringstringidssamples/package/subpackage/__init__.py b/tests/unit/samples/wiringstringidssamples/package/subpackage/__init__.py new file mode 100644 index 00000000..b2d3278b --- /dev/null +++ b/tests/unit/samples/wiringstringidssamples/package/subpackage/__init__.py @@ -0,0 +1,11 @@ +import sys + +if sys.version_info >= (3, 6): + from dependency_injector.wiring import Provide + + from ...container import Container + from ...service import Service + + + def test_package_function(service: Service = Provide[Container.service]): + return service diff --git a/tests/unit/samples/wiringstringidssamples/package/subpackage/submodule.py b/tests/unit/samples/wiringstringidssamples/package/subpackage/submodule.py new file mode 100644 index 00000000..5723eb4a --- /dev/null +++ b/tests/unit/samples/wiringstringidssamples/package/subpackage/submodule.py @@ -0,0 +1,8 @@ +from dependency_injector.wiring import inject, Provide + +from ...service import Service + + +@inject +def test_function(service: Service = Provide['service']): + return service diff --git a/tests/unit/samples/wiringstringidssamples/resourceclosing.py b/tests/unit/samples/wiringstringidssamples/resourceclosing.py new file mode 100644 index 00000000..b8984665 --- /dev/null +++ b/tests/unit/samples/wiringstringidssamples/resourceclosing.py @@ -0,0 +1,37 @@ +from dependency_injector import containers, providers +from dependency_injector.wiring import inject, Provide, Closing + + +class Service: + init_counter: int = 0 + shutdown_counter: int = 0 + + @classmethod + def reset_counter(cls): + cls.init_counter = 0 + cls.shutdown_counter = 0 + + @classmethod + def init(cls): + cls.init_counter += 1 + + @classmethod + def shutdown(cls): + cls.shutdown_counter += 1 + + +def init_service(): + service = Service() + service.init() + yield service + service.shutdown() + + +class Container(containers.DeclarativeContainer): + + service = providers.Resource(init_service) + + +@inject +def test_function(service: Service = Closing[Provide['service']]): + return service diff --git a/tests/unit/samples/wiringstringidssamples/service.py b/tests/unit/samples/wiringstringidssamples/service.py new file mode 100644 index 00000000..4151b94f --- /dev/null +++ b/tests/unit/samples/wiringstringidssamples/service.py @@ -0,0 +1,2 @@ +class Service: + service_attr: int diff --git a/tests/unit/wiring/test_wiring_py36.py b/tests/unit/wiring/test_wiring_py36.py index 25f4b9f2..064aed9b 100644 --- a/tests/unit/wiring/test_wiring_py36.py +++ b/tests/unit/wiring/test_wiring_py36.py @@ -169,6 +169,9 @@ class WiringTest(unittest.TestCase): } self.container.config.from_dict(config) + value_default = module.test_config_invariant() + self.assertEqual(value_default, 1) + with self.container.config.switch.override('a'): value_a = module.test_config_invariant() self.assertEqual(value_a, 1) diff --git a/tests/unit/wiring/test_wiring_string_ids_py36.py b/tests/unit/wiring/test_wiring_string_ids_py36.py new file mode 100644 index 00000000..b4d41b3b --- /dev/null +++ b/tests/unit/wiring/test_wiring_string_ids_py36.py @@ -0,0 +1,391 @@ +import contextlib +from decimal import Decimal +import importlib +import unittest + +from dependency_injector.wiring import ( + wire, + Provide, + Closing, + register_loader_containers, + unregister_loader_containers, +) +from dependency_injector import errors + +# Runtime import to avoid syntax errors in samples on Python < 3.5 +import os +_TOP_DIR = os.path.abspath( + os.path.sep.join(( + os.path.dirname(__file__), + '../', + )), +) +_SAMPLES_DIR = os.path.abspath( + os.path.sep.join(( + os.path.dirname(__file__), + '../samples/', + )), +) +import sys +sys.path.append(_TOP_DIR) +sys.path.append(_SAMPLES_DIR) + +from asyncutils import AsyncTestCase + +from wiringstringidssamples import module, package +from wiringstringidssamples.service import Service +from wiringstringidssamples.container import Container, SubContainer + + +class WiringTest(unittest.TestCase): + + container: Container + + def setUp(self) -> None: + self.container = Container(config={'a': {'b': {'c': 10}}}) + self.container.wire( + modules=[module], + packages=[package], + ) + self.addCleanup(self.container.unwire) + + def test_package_lookup(self): + from wiringstringidssamples.package import test_package_function + service = test_package_function() + self.assertIsInstance(service, Service) + + def test_package_subpackage_lookup(self): + from wiringstringidssamples.package.subpackage import test_package_function + service = test_package_function() + self.assertIsInstance(service, Service) + + def test_package_submodule_lookup(self): + from wiringstringidssamples.package.subpackage.submodule import test_function + service = test_function() + self.assertIsInstance(service, Service) + + def test_class_wiring(self): + test_class_object = module.TestClass() + self.assertIsInstance(test_class_object.service, Service) + + def test_class_wiring_context_arg(self): + test_service = self.container.service() + + test_class_object = module.TestClass(service=test_service) + self.assertIs(test_class_object.service, test_service) + + def test_class_method_wiring(self): + test_class_object = module.TestClass() + service = test_class_object.method() + self.assertIsInstance(service, Service) + + def test_class_classmethod_wiring(self): + service = module.TestClass.class_method() + self.assertIsInstance(service, Service) + + def test_instance_classmethod_wiring(self): + instance = module.TestClass() + service = instance.class_method() + self.assertIsInstance(service, Service) + + def test_class_staticmethod_wiring(self): + service = module.TestClass.static_method() + self.assertIsInstance(service, Service) + + def test_instance_staticmethod_wiring(self): + instance = module.TestClass() + service = instance.static_method() + self.assertIsInstance(service, Service) + + def test_function_wiring(self): + service = module.test_function() + self.assertIsInstance(service, Service) + + def test_function_wiring_context_arg(self): + test_service = self.container.service() + + service = module.test_function(service=test_service) + self.assertIs(service, test_service) + + def test_function_wiring_provider(self): + service = module.test_function_provider() + self.assertIsInstance(service, Service) + + def test_function_wiring_provider_context_arg(self): + test_service = self.container.service() + + service = module.test_function_provider(service_provider=lambda: test_service) + self.assertIs(service, test_service) + + def test_configuration_option(self): + ( + value_int, + value_str, + value_decimal, + value_required, + value_required_int, + value_required_str, + value_required_decimal, + ) = module.test_config_value() + + self.assertEqual(value_int, 10) + self.assertEqual(value_str, '10') + self.assertEqual(value_decimal, Decimal(10)) + self.assertEqual(value_required, 10) + self.assertEqual(value_required_int, 10) + self.assertEqual(value_required_str, '10') + self.assertEqual(value_required_decimal, Decimal(10)) + + def test_configuration_option_required_undefined(self): + self.container.config.reset_override() + with self.assertRaisesRegex(errors.Error, 'Undefined configuration option "config.a.b.c"'): + module.test_config_value_required_undefined() + + def test_provide_provider(self): + service = module.test_provide_provider() + self.assertIsInstance(service, Service) + + def test_provided_instance(self): + class TestService: + foo = { + 'bar': lambda: 10, + } + + with self.container.service.override(TestService()): + some_value = module.test_provided_instance() + self.assertEqual(some_value, 10) + + def test_subcontainer(self): + some_value = module.test_subcontainer_provider() + self.assertEqual(some_value, 1) + + def test_config_invariant(self): + config = { + 'option': { + 'a': 1, + 'b': 2, + }, + 'switch': 'a', + } + self.container.config.from_dict(config) + + value_default = module.test_config_invariant() + self.assertEqual(value_default, 1) + + with self.container.config.switch.override('a'): + value_a = module.test_config_invariant() + self.assertEqual(value_a, 1) + + with self.container.config.switch.override('b'): + value_b = module.test_config_invariant() + self.assertEqual(value_b, 2) + + def test_wire_with_class_error(self): + with self.assertRaises(Exception): + wire( + container=Container, + modules=[module], + ) + + def test_unwire_function(self): + self.container.unwire() + self.assertIsInstance(module.test_function(), Provide) + + def test_unwire_class(self): + self.container.unwire() + test_class_object = module.TestClass() + self.assertIsInstance(test_class_object.service, Provide) + + def test_unwire_class_method(self): + self.container.unwire() + test_class_object = module.TestClass() + self.assertIsInstance(test_class_object.method(), Provide) + + def test_unwire_package_function(self): + self.container.unwire() + from wiringstringidssamples.package.subpackage.submodule import test_function + self.assertIsInstance(test_function(), Provide) + + def test_unwire_package_function_by_reference(self): + from wiringstringidssamples.package.subpackage import submodule + self.container.unwire() + self.assertIsInstance(submodule.test_function(), Provide) + + def test_wire_multiple_containers(self): + sub_container = SubContainer() + sub_container.wire( + modules=[module], + packages=[package], + ) + self.addCleanup(sub_container.unwire) + + service, some_value = module.test_provide_from_different_containers() + + self.assertIsInstance(service, Service) + self.assertEqual(some_value, 1) + + def test_closing_resource(self): + from wiringstringidssamples import resourceclosing + + resourceclosing.Service.reset_counter() + + container = resourceclosing.Container() + container.wire(modules=[resourceclosing]) + self.addCleanup(container.unwire) + + result_1 = resourceclosing.test_function() + self.assertIsInstance(result_1, resourceclosing.Service) + self.assertEqual(result_1.init_counter, 1) + self.assertEqual(result_1.shutdown_counter, 1) + + result_2 = resourceclosing.test_function() + self.assertIsInstance(result_2, resourceclosing.Service) + self.assertEqual(result_2.init_counter, 2) + self.assertEqual(result_2.shutdown_counter, 2) + + self.assertIsNot(result_1, result_2) + + def test_closing_resource_context(self): + from wiringstringidssamples import resourceclosing + + resourceclosing.Service.reset_counter() + service = resourceclosing.Service() + + container = resourceclosing.Container() + container.wire(modules=[resourceclosing]) + self.addCleanup(container.unwire) + + result_1 = resourceclosing.test_function(service=service) + self.assertIs(result_1, service) + self.assertEqual(result_1.init_counter, 0) + self.assertEqual(result_1.shutdown_counter, 0) + + result_2 = resourceclosing.test_function(service=service) + self.assertIs(result_2, service) + self.assertEqual(result_2.init_counter, 0) + self.assertEqual(result_2.shutdown_counter, 0) + + def test_class_decorator(self): + service = module.test_class_decorator() + self.assertIsInstance(service, Service) + + def test_container(self): + service = module.test_container() + self.assertIsInstance(service, Service) + + +class WiringAndFastAPITest(unittest.TestCase): + + container: Container + + def test_bypass_marker_injection(self): + container = Container() + container.wire(modules=[module]) + self.addCleanup(container.unwire) + + service = module.test_function(service=Provide[Container.service]) + self.assertIsInstance(service, Service) + + def test_closing_resource_bypass_marker_injection(self): + from wiringstringidssamples import resourceclosing + + resourceclosing.Service.reset_counter() + + container = resourceclosing.Container() + container.wire(modules=[resourceclosing]) + self.addCleanup(container.unwire) + + result_1 = resourceclosing.test_function( + service=Closing[Provide[resourceclosing.Container.service]], + ) + self.assertIsInstance(result_1, resourceclosing.Service) + self.assertEqual(result_1.init_counter, 1) + self.assertEqual(result_1.shutdown_counter, 1) + + result_2 = resourceclosing.test_function( + service=Closing[Provide[resourceclosing.Container.service]], + ) + self.assertIsInstance(result_2, resourceclosing.Service) + self.assertEqual(result_2.init_counter, 2) + self.assertEqual(result_2.shutdown_counter, 2) + + self.assertIsNot(result_1, result_2) + + +class WiringAsyncInjectionsTest(AsyncTestCase): + + def test_async_injections(self): + from wiringstringidssamples import asyncinjections + + container = asyncinjections.Container() + container.wire(modules=[asyncinjections]) + self.addCleanup(container.unwire) + + asyncinjections.resource1.reset_counters() + asyncinjections.resource2.reset_counters() + + resource1, resource2 = self._run(asyncinjections.async_injection()) + + self.assertIs(resource1, asyncinjections.resource1) + self.assertEqual(asyncinjections.resource1.init_counter, 1) + self.assertEqual(asyncinjections.resource1.shutdown_counter, 0) + + self.assertIs(resource2, asyncinjections.resource2) + self.assertEqual(asyncinjections.resource2.init_counter, 1) + self.assertEqual(asyncinjections.resource2.shutdown_counter, 0) + + def test_async_injections_with_closing(self): + from wiringstringidssamples import asyncinjections + + container = asyncinjections.Container() + container.wire(modules=[asyncinjections]) + self.addCleanup(container.unwire) + + asyncinjections.resource1.reset_counters() + asyncinjections.resource2.reset_counters() + + resource1, resource2 = self._run(asyncinjections.async_injection_with_closing()) + + self.assertIs(resource1, asyncinjections.resource1) + self.assertEqual(asyncinjections.resource1.init_counter, 1) + self.assertEqual(asyncinjections.resource1.shutdown_counter, 1) + + self.assertIs(resource2, asyncinjections.resource2) + self.assertEqual(asyncinjections.resource2.init_counter, 1) + self.assertEqual(asyncinjections.resource2.shutdown_counter, 1) + + resource1, resource2 = self._run(asyncinjections.async_injection_with_closing()) + + self.assertIs(resource1, asyncinjections.resource1) + self.assertEqual(asyncinjections.resource1.init_counter, 2) + self.assertEqual(asyncinjections.resource1.shutdown_counter, 2) + + self.assertIs(resource2, asyncinjections.resource2) + self.assertEqual(asyncinjections.resource2.init_counter, 2) + self.assertEqual(asyncinjections.resource2.shutdown_counter, 2) + + +# class AutoLoaderTest(unittest.TestCase): +# +# container: Container +# +# def setUp(self) -> None: +# self.container = Container(config={'a': {'b': {'c': 10}}}) +# importlib.reload(module) +# +# def tearDown(self) -> None: +# with contextlib.suppress(ValueError): +# unregister_loader_containers(self.container) +# +# self.container.unwire() +# +# @classmethod +# def tearDownClass(cls) -> None: +# importlib.reload(module) +# +# def test_register_container(self): +# register_loader_containers(self.container) +# importlib.reload(module) +# +# service = module.test_function() +# self.assertIsInstance(service, Service)