diff --git a/docs/conf.py b/docs/conf.py index f3638a1a..3d2cb7ad 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -52,7 +52,7 @@ master_doc = 'index' # General information about the project. project = u'Dependency Injector' -copyright = u'2017, ETS Labs' +copyright = u'2020, ETS Labs' author = u'ETS Labs' # The version info for the project you're documenting, acts as replacement for diff --git a/docs/containers/overriding.rst b/docs/containers/overriding.rst index cf5627af..3d1a7030 100644 --- a/docs/containers/overriding.rst +++ b/docs/containers/overriding.rst @@ -1,5 +1,5 @@ -Overriding of the container ---------------------------- +Container overriding +-------------------- .. currentmodule:: dependency_injector.containers diff --git a/docs/images/containers/bundles.png b/docs/images/containers/bundles.png deleted file mode 100644 index f015088a..00000000 Binary files a/docs/images/containers/bundles.png and /dev/null differ diff --git a/docs/images/providers/abstract_factory.png b/docs/images/providers/abstract_factory.png deleted file mode 100644 index ede202af..00000000 Binary files a/docs/images/providers/abstract_factory.png and /dev/null differ diff --git a/docs/images/providers/custom_provider.png b/docs/images/providers/custom_provider.png deleted file mode 100644 index 398e6bfa..00000000 Binary files a/docs/images/providers/custom_provider.png and /dev/null differ diff --git a/docs/images/providers/overriding_simple.png b/docs/images/providers/overriding_simple.png deleted file mode 100644 index cfa3c610..00000000 Binary files a/docs/images/providers/overriding_simple.png and /dev/null differ diff --git a/docs/images/providers/overriding_users_model.png b/docs/images/providers/overriding_users_model.png deleted file mode 100644 index 16f3ff51..00000000 Binary files a/docs/images/providers/overriding_users_model.png and /dev/null differ diff --git a/docs/images/providers/provider_override.png b/docs/images/providers/provider_override.png deleted file mode 100644 index 00549da8..00000000 Binary files a/docs/images/providers/provider_override.png and /dev/null differ diff --git a/docs/main/changelog.rst b/docs/main/changelog.rst index 2d5c5780..498ab0e3 100644 --- a/docs/main/changelog.rst +++ b/docs/main/changelog.rst @@ -7,6 +7,15 @@ that were made in every particular version. From version 0.7.6 *Dependency Injector* framework strictly follows `Semantic versioning`_ +3.36.0 +------ +- Update providers overriding documentation and rework examples. +- Update documentation on injecting provided object attributes, items or method calls. +- Update documentation and example on creating a custom provider. +- Update providers index documentation page to give better overview of providers functionality. +- Fix mypy stub of the ``Provider`` to specify the protected ``._copy_overridings()`` method. +- Update copyright year in the documentation. + 3.35.1 ------ - Fix minor issues in the providers documentation and examples. diff --git a/docs/providers/callable.rst b/docs/providers/callable.rst index e0cb5089..b9d72b8e 100644 --- a/docs/providers/callable.rst +++ b/docs/providers/callable.rst @@ -1,5 +1,5 @@ Callable provider ------------------ +================= .. meta:: :keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Function,Method,Example diff --git a/docs/providers/configuration.rst b/docs/providers/configuration.rst index 43fd3ee3..9d049e18 100644 --- a/docs/providers/configuration.rst +++ b/docs/providers/configuration.rst @@ -1,5 +1,5 @@ Configuration provider ----------------------- +====================== .. meta:: :keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Configuration,Injection, @@ -20,7 +20,7 @@ Configuration provider It implements the principle "use first, define later". Loading from an INI file -~~~~~~~~~~~~~~~~~~~~~~~~ +------------------------ ``Configuration`` provider can load configuration from an ``ini`` file using the :py:meth:`Configuration.from_ini` method: @@ -40,7 +40,7 @@ where ``examples/providers/configuration/config.ini`` is: variable ``ENV_NAME``. Loading from a YAML file -~~~~~~~~~~~~~~~~~~~~~~~~ +------------------------ ``Configuration`` provider can load configuration from a ``yaml`` file using the :py:meth:`Configuration.from_yaml` method: @@ -74,7 +74,7 @@ variable ``ENV_NAME``. *Don't forget to mirror the changes in the requirements file.* Loading from a dictionary -~~~~~~~~~~~~~~~~~~~~~~~~~ +------------------------- ``Configuration`` provider can load configuration from a Python ``dict`` using the :py:meth:`Configuration.from_dict` method: @@ -85,7 +85,7 @@ Loading from a dictionary :emphasize-lines: 6-13 Loading from an environment variable -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +------------------------------------ ``Configuration`` provider can load configuration from an environment variable using the :py:meth:`Configuration.from_env` method: @@ -96,7 +96,7 @@ Loading from an environment variable :emphasize-lines: 6-8 Loading from the multiple sources -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +--------------------------------- ``Configuration`` provider can load configuration from the multiple sources. Loaded configuration is merged recursively over the existing configuration. @@ -112,7 +112,7 @@ where ``examples/providers/configuration/config.local.yml`` is: :language: ini Specifying the value type -~~~~~~~~~~~~~~~~~~~~~~~~~ +------------------------- You can specify the type of the injected configuration value explicitly. diff --git a/docs/providers/coroutine.rst b/docs/providers/coroutine.rst index 3db7846a..6fac6c9e 100644 --- a/docs/providers/coroutine.rst +++ b/docs/providers/coroutine.rst @@ -1,5 +1,5 @@ Coroutine provider ------------------- +================== .. meta:: :keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Coroutine,Asynchronous, diff --git a/docs/providers/custom.rst b/docs/providers/custom.rst index 237504c2..45fbcd2c 100644 --- a/docs/providers/custom.rst +++ b/docs/providers/custom.rst @@ -1,35 +1,45 @@ -Writing of custom providers ---------------------------- +.. _create-provider: + +Creating a custom provider +========================== + +.. meta:: + :keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Custom provider, Create + :description: This page demonstrates how to create a custom provider. .. currentmodule:: dependency_injector.providers -List of *Dependency Injector* providers could be widened with custom providers. +You can create a custom provider. -Below are some tips and recommendations that have to be met: +To create a custom provider you need to follow these rules: - 1. Every custom provider has to extend base provider class - - :py:class:`Provider`. - 2. Custom provider's ``__init__()`` could be overridden, but parent's - initializer (:py:meth:`Provider.__init__`) has to be called. - 3. Providing strategy has to be implemented in custom provider's - :py:meth:`Provider.__call__` method. - 4. If custom provider is based on some standard providers, it is better to - use delegation of standard providers, then extending of them. - 5. If custom provider defines any attributes, it is good to list them in - ``__slots__`` attribute (as *Dependency Injector* does). It can save - some memory. - 6. If custom provider deals with injections, it is strongly recommended - to be consistent with :py:class:`Factory`, :py:class:`Singleton` and - :py:class:`Callable` providers style. - -Example: - -.. image:: /images/providers/custom_provider.png - :width: 100% - :align: center +1. New provider class should inherit :py:class:`Provider`. +2. You need to implement the ``Provider._provide()`` method. +3. You need to implement the ``Provider.__deepcopy__()`` method. It should return an + equivalent copy of a provider. All providers must be copied with a ``deepcopy()`` function + from the ``providers`` module. After the a new provider object is created use + ``Provider._copy_overriding()`` method to copy all overriding providers. See the example + below. +4. If the new provider has a ``__init__()`` method, it should call the parent + ``Provider.__init__()``. .. literalinclude:: ../../examples/providers/custom_factory.py :language: python + :lines: 3- +.. note:: + 1. Prefer delegation over inheritance. If you choose between inheriting a ``Factory`` or + inheriting a ``Provider`` and use ``Factory`` internally - the last is better. + 2. When create a new provider follow the ``Factory``-like injections style. Consistency matters. + 3. Use the ``__slots__`` attribute to make sure nothing could be attached to your provider. You + will also save some memory. + +.. note:: + If you don't find needed provider in the ``providers`` module and experience troubles creating + one by your own - open a + `Github Issue `_. + + I'll help you to resolve the issue if that's possible. If the new provider can be useful for + others I'll include it into the ``providers`` module. .. disqus:: diff --git a/docs/providers/dependency.rst b/docs/providers/dependency.rst index 9ce3977b..1246ceae 100644 --- a/docs/providers/dependency.rst +++ b/docs/providers/dependency.rst @@ -1,5 +1,5 @@ Dependency provider -------------------- +=================== .. currentmodule:: dependency_injector.providers diff --git a/docs/providers/factory.rst b/docs/providers/factory.rst index 23045591..626cda8c 100644 --- a/docs/providers/factory.rst +++ b/docs/providers/factory.rst @@ -41,7 +41,7 @@ injected following these rules: :lines: 3- Passing arguments to the underlying providers -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +--------------------------------------------- ``Factory`` provider can pass the arguments to the underlying providers. This helps when you need to assemble a nested objects graph and pass the arguments deep inside. @@ -88,7 +88,7 @@ If ```` is found the underlying provider will receive the .. _factory_providers_delegation: Passing providers to the objects -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +-------------------------------- When you need to inject the provider itself, but not the result of its call, use the ``.provider`` attribute of the provider that you're going to inject. @@ -105,7 +105,7 @@ attribute of the provider that you're going to inject. .. _factory-specialize-provided-type: Specializing the provided type -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +------------------------------ You can create a specialized ``Factory`` provider that provides only specific type. For doing this you need to create a subclass of the ``Factory`` provider and define the ``provided_type`` @@ -119,7 +119,7 @@ class attribute. .. _abstract-factory: Abstract factory -~~~~~~~~~~~~~~~~ +---------------- :py:class:`AbstractFactory` provider helps when you need to create a provider of some base class and the particular implementation is not yet know. ``AbstractFactory`` provider is a ``Factory`` @@ -138,7 +138,7 @@ provider with two peculiarities: :emphasize-lines: 32 Factory aggregate -~~~~~~~~~~~~~~~~~ +----------------- :py:class:`FactoryAggregate` provider aggregates multiple factories. When you call the ``FactoryAggregate`` it delegates the call to one of the factories. diff --git a/docs/providers/images/overriding.png b/docs/providers/images/overriding.png new file mode 100644 index 00000000..6400c709 Binary files /dev/null and b/docs/providers/images/overriding.png differ diff --git a/docs/providers/index.rst b/docs/providers/index.rst index bd5a4745..d2097593 100644 --- a/docs/providers/index.rst +++ b/docs/providers/index.rst @@ -7,7 +7,8 @@ Providers help to assemble the objects. They create objects and inject the depen Each provider is a callable. You call the provider like a function when you need to create an object. Provider retrieves the underlying dependencies and inject them into the created object. -It causes the cascade effect that helps to assemble object graphs. +It causes the cascade effect that helps to assemble object graphs. See ``Factory``, ``Singleton``, +``Callable`` and other provider docs below. .. code-block:: bash @@ -23,10 +24,13 @@ It causes the cascade effect that helps to assemble object graphs. │ └──> provider6() -Another providers feature is an overriding. Any of the providers can be overridden by another -provider. When provider is overridden it calls to the overriding provider instead of providing -the object by its own. This helps in testing. This also helps in overriding API clients with -stubs for the development or staging environment. +Another providers feature is an overriding. You can override any provider by another provider. +This helps in testing. This also helps in overriding API clients with stubs for the development +or staging environment. See the example at :ref:`provider-overriding`. + +If you need to inject not the whole object but the parts see :ref:`provided-instance`. + +To create a new provider see :ref:`create-provider`. Providers module API docs - :py:mod:`dependency_injector.providers` diff --git a/docs/providers/list.rst b/docs/providers/list.rst index 9ad4ece4..afd24a3d 100644 --- a/docs/providers/list.rst +++ b/docs/providers/list.rst @@ -1,5 +1,5 @@ List provider -------------- +============= .. meta:: :keywords: Python,DI,Dependency injection,IoC,Inversion of Control,List,Injection diff --git a/docs/providers/object.rst b/docs/providers/object.rst index 800b8116..61ad6ae1 100644 --- a/docs/providers/object.rst +++ b/docs/providers/object.rst @@ -1,5 +1,5 @@ Object provider ---------------- +=============== .. meta:: :keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Object diff --git a/docs/providers/overriding.rst b/docs/providers/overriding.rst index 7c0770bb..23effd80 100644 --- a/docs/providers/overriding.rst +++ b/docs/providers/overriding.rst @@ -1,47 +1,43 @@ -Overriding of providers ------------------------ +.. _provider-overriding: + +Provider overriding +=================== + +.. meta:: + :keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Override,Test,Unit + :description: This page demonstrates how to implement providers overriding. This helps in + testing and configuring the system for the multiple environments. .. currentmodule:: dependency_injector.providers -Every provider could be overridden by another provider. +You can override any provider by another provider. -This gives opportunity to make system behaviour more flexible at some point. -The main feature is that while your code is using providers, it depends on -providers, but not on the objects that providers provide. As a result of this, -you can change providing by provider object to a different one, but still -compatible one, without changing your previously written code. +When provider is overridden it calls to the overriding provider instead of providing +the object by its own. -Provider overriding functionality has such interface: +This helps in testing. This also helps in overriding API clients with stubs for the development +or staging environment. -.. image:: /images/providers/provider_override.png - :width: 55% - :align: center +To override a provider you need to call the ``Provider.override()`` method. This method receives +a single argument called ``overriding``. If the ``overriding`` value is a provider, this provider +is called instead of the original. If value is not a provider, this value is returned instead of +calling the original provider. -+ :py:meth:`Provider.override()` - takes another provider that will be used - instead of current provider. This method could be called several times. - In such case, last passed provider would be used as overriding one. -+ :py:meth:`Provider.reset_override()` - resets all overriding providers. - Provider starts to behave itself like usual. -+ :py:meth:`Provider.reset_last_overriding()` - remove last overriding - provider from stack of overriding providers. - -Example: - -.. image:: /images/providers/overriding_simple.png +.. image:: images/overriding.png :width: 80% :align: center -.. literalinclude:: ../../examples/providers/overriding_simple.py +.. literalinclude:: ../../examples/providers/overriding.py :language: python + :lines: 3- -Example: +You can override a provider multiple times. In that case the latest ``overriding`` value will be +used. The rest of the overriding values will form a stack. -.. image:: /images/providers/overriding_users_model.png - :width: 100% - :align: center - -.. literalinclude:: ../../examples/providers/overriding_users_model.py - :language: python +To reset an overriding you can use the ``Provider.reset_override()`` or +``Provider.reset_last_overriding()`` methods. +You can use a context manager for overriding a provider ``with Provider.override():``. The +overriding will be reset when context closed. .. disqus:: diff --git a/docs/providers/provided_instance.rst b/docs/providers/provided_instance.rst index 381557fe..a53035cf 100644 --- a/docs/providers/provided_instance.rst +++ b/docs/providers/provided_instance.rst @@ -1,13 +1,16 @@ -Injecting attributes, items, or call methods of the provided instance ---------------------------------------------------------------------- +.. _provided-instance: + +Injecting provided object attributes, items, or call its methods +================================================================ + +.. meta:: + :keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Attribute,Method,Call + :description: This page demonstrates how to inject attributes, items or call method of the + provided instance. .. currentmodule:: dependency_injector.providers -In this section you will know how to inject provided instance attribute or item into the other -provider. - -It also describes how to call a method of the provided instance and use the result of -this call as an injection value. +You can inject provided object attribute, item or result of its method call. .. literalinclude:: ../../examples/providers/provided_instance.py :language: python @@ -15,14 +18,14 @@ this call as an injection value. :lines: 3- To use the feature you should use the ``.provided`` attribute of the injected provider. This -attribute helps to specify what happens with the provided instance. You can retrieve an injection -value from: +attribute helps to specify what happens with the provided instance before the injection. You can +use any combination of the following: -- an attribute of the provided instance -- an item of the provided instance -- a call of the provided instance method +- an attribute of the provided object +- an item of the provided object +- a call of the provided object method -When you use the call of the provided instance method you can specify the injections into this +When you use a call of the provided instance method you can specify the injections for this method like you do with any other provider. You can do nested constructions: @@ -32,35 +35,24 @@ You can do nested constructions: :emphasize-lines: 24-30 :lines: 3- -Attribute ``.provided`` is available for the providers that return instances. Providers that -have ``.provided`` attribute: +The ``.provided`` attribute is available for the next providers: -- :py:class:`Callable` and its subclasses - :py:class:`Factory` and its subclasses - :py:class:`Singleton` and its subclasses +- :py:class:`Callable` and its subclasses - :py:class:`Object` - :py:class:`List` - :py:class:`Selector` - :py:class:`Dependency` -Special providers like :py:class:`Configuration` or :py:class:`Delegate` do not have the -``.provided`` attribute. - -Provider subclasses -------------------- - When you create a new provider subclass and want to implement the ``.provided`` attribute, you -should use the :py:class:`ProvidedInstance` provider. +should use the :py:class:`ProvidedInstance` provider. Add the ``.provided`` property +implementation to a new subclass: .. code-block:: python @property def provided(self): - """Return :py:class:`ProvidedInstance` provider.""" return ProvidedInstance(self) -In all other cases you should not use :py:class:`ProvidedInstance`, :py:class:`AttributeGetter`, -:py:class:`ItemGetter`, or :py:class:`MethodCaller` providers directly. Use the ``.provided`` -attribute of the injected provider instead. - .. disqus:: diff --git a/docs/providers/selector.rst b/docs/providers/selector.rst index b3f372e0..6326e051 100644 --- a/docs/providers/selector.rst +++ b/docs/providers/selector.rst @@ -1,7 +1,7 @@ .. _selector-provider: Selector provider ------------------ +================= .. meta:: :keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Configuration,Injection, diff --git a/docs/providers/singleton.rst b/docs/providers/singleton.rst index 10131e03..a3cc2d4d 100644 --- a/docs/providers/singleton.rst +++ b/docs/providers/singleton.rst @@ -1,5 +1,5 @@ Singleton provider ------------------- +================== .. meta:: :keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Singleton,Pattern,Example, @@ -33,7 +33,7 @@ factories: - :ref:`abstract-factory` Resetting memorized object -~~~~~~~~~~~~~~~~~~~~~~~~~~ +-------------------------- To reset a memorized object you need to call the ``.reset()`` method of the ``Singleton`` provider. @@ -48,7 +48,7 @@ provider. managed by the garbage collector. Using singleton with multiple threads -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +------------------------------------- ``Singleton`` provider is NOT thread-safe. You need to explicitly establish a synchronization for using the ``Singleton`` provider in the multi-threading application. Otherwise you could trap @@ -67,7 +67,7 @@ There are two thread-safe singleton implementations out of the box: :emphasize-lines: 11,12 Implementing scopes -~~~~~~~~~~~~~~~~~~~ +------------------- To implement a scoped singleton provider use a ``Singleton`` provider and reset its scope when needed. diff --git a/examples/providers/custom_factory.py b/examples/providers/custom_factory.py index 01c17425..706ed04c 100644 --- a/examples/providers/custom_factory.py +++ b/examples/providers/custom_factory.py @@ -1,40 +1,42 @@ -"""Custom `Factory` example.""" +"""Custom provider example.""" -import dependency_injector.providers as providers +from dependency_injector import providers -class User: - """Example class User.""" - - -class UsersFactory(providers.Provider): - """Example users factory.""" +class CustomFactory(providers.Provider): __slots__ = ('_factory',) - def __init__(self): - """Initialize instance.""" - self._factory = providers.Factory(User) + def __init__(self, provides, *args, **kwargs): + self._factory = providers.Factory(provides, *args, **kwargs) super().__init__() - def __call__(self, *args, **kwargs): - """Return provided object. + def __deepcopy__(self, memo): + copied = memo.get(id(self)) + if copied is not None: + return copied - Callable interface implementation. - """ - if self.last_overriding is not None: - return self.last_overriding._provide(args, kwargs) + copied = self.__class__( + self._factory.provides, + *providers.deepcopy(self._factory.args, memo), + **providers.deepcopy(self._factory.kwargs, memo), + ) + self._copy_overridings(copied, memo) + + return copied + + def _provide(self, args, kwargs): return self._factory(*args, **kwargs) -# Users factory: -users_factory = UsersFactory() +factory = CustomFactory(object) -# Creating several User objects: -user1 = users_factory() -user2 = users_factory() -# Making some asserts: -assert isinstance(user1, User) -assert isinstance(user2, User) -assert user1 is not user2 +if __name__ == '__main__': + object1 = factory() + assert isinstance(object1, object) + + object2 = factory() + assert isinstance(object1, object) + + assert object1 is not object2 diff --git a/examples/providers/overriding.py b/examples/providers/overriding.py new file mode 100644 index 00000000..42c01de5 --- /dev/null +++ b/examples/providers/overriding.py @@ -0,0 +1,42 @@ +"""Simple providers overriding example.""" + +import unittest.mock + +from dependency_injector import providers + + +class ApiClient: + ... + + +class ApiClientStub(ApiClient): + ... + + +class Service: + def __init__(self, api_client: ApiClient): + self._api_client = api_client + + +api_client_factory = providers.Factory(ApiClient) +service_factory = providers.Factory( + Service, + api_client=api_client_factory, +) + + +if __name__ == '__main__': + # 1. Use .override() to replace the API client with stub + api_client_factory.override(providers.Factory(ApiClientStub)) + service1 = service_factory() + assert isinstance(service1.api_client, ApiClientStub) + + # 2. Use .override() as a context manager to mock the API client in testing + with api_client_factory.override(unittest.mock.Mock(ApiClient)): + service3 = service_factory() + assert isinstance(service3.api_client, unittest.mock.Mock) + + # 3. Use .reset_override() to get back to normal + api_client_factory.reset_override() + service3 = service_factory() + assert isinstance(service3.api_client, ApiClient) diff --git a/examples/providers/overriding_simple.py b/examples/providers/overriding_simple.py deleted file mode 100644 index 96533d2c..00000000 --- a/examples/providers/overriding_simple.py +++ /dev/null @@ -1,36 +0,0 @@ -"""Simple providers overriding example.""" - -import dependency_injector.providers as providers - - -class User: - """Example class User.""" - - -# Users factory: -users_factory = providers.Factory(User) - -# Creating several User objects: -user1 = users_factory() -user2 = users_factory() - -# Making some asserts: -assert user1 is not user2 -assert isinstance(user1, User) and isinstance(user2, User) - - -# Extending User: -class SuperUser(User): - """Example class SuperUser.""" - - -# Overriding users factory: -users_factory.override(providers.Factory(SuperUser)) - -# Creating some more User objects using overridden users factory: -user3 = users_factory() -user4 = users_factory() - -# Making some asserts: -assert user4 is not user3 -assert isinstance(user3, SuperUser) and isinstance(user4, SuperUser) diff --git a/examples/providers/overriding_users_model.py b/examples/providers/overriding_users_model.py deleted file mode 100644 index 7af62069..00000000 --- a/examples/providers/overriding_users_model.py +++ /dev/null @@ -1,95 +0,0 @@ -"""Overriding user's model example.""" - -import dependency_injector.providers as providers - - -class User: - """Example class User.""" - - def __init__(self, id, password): - """Initialize instance.""" - self.id = id - self.password = password - super().__init__() - - -class UsersService: - """Example class UsersService.""" - - def __init__(self, user_cls): - """Initialize instance.""" - self.user_cls = user_cls - super().__init__() - - def get_by_id(self, id): - """Find user by his id and return user model.""" - return self.user_cls(id=id, password='secret' + str(id)) - - -# Users factory and UsersService provider: -users_service = providers.Factory(UsersService, user_cls=User) - -# Getting several users and making some asserts: -user1 = users_service().get_by_id(1) -user2 = users_service().get_by_id(2) - -assert isinstance(user1, User) -assert user1.id == 1 -assert user1.password == 'secret1' - -assert isinstance(user2, User) -assert user2.id == 2 -assert user2.password == 'secret2' - -assert user1 is not user2 - -# Extending user model and user service for adding custom attributes without -# making any changes to client's code. - - -class ExtendedUser(User): - """Example class ExtendedUser.""" - - def __init__(self, id, password, first_name=None, last_name=None, - gender=None): - """Initialize instance.""" - self.first_name = first_name - self.last_name = last_name - self.gender = gender - super().__init__(id, password) - - -class ExtendedUsersService(UsersService): - """Example class ExtendedUsersService.""" - - def get_by_id(self, id): - """Find user by his id and return user model.""" - user = super(ExtendedUsersService, self).get_by_id(id) - user.first_name = 'John' + str(id) - user.last_name = 'Smith' + str(id) - user.gender = 'male' - return user - - -# Overriding users_service provider: -extended_users_service = providers.Factory(ExtendedUsersService, - user_cls=ExtendedUser) -users_service.override(extended_users_service) - -# Getting few other users users and making some asserts: -user3 = users_service().get_by_id(3) -user4 = users_service().get_by_id(4) - -assert isinstance(user3, ExtendedUser) -assert user3.id == 3 -assert user3.password == 'secret3' -assert user3.first_name == 'John3' -assert user3.last_name == 'Smith3' - -assert isinstance(user4, ExtendedUser) -assert user4.id == 4 -assert user4.password == 'secret4' -assert user4.first_name == 'John4' -assert user4.last_name == 'Smith4' - -assert user3 is not user4 diff --git a/examples/providers/provided_instance.py b/examples/providers/provided_instance.py index 2e538120..e61f9bb7 100644 --- a/examples/providers/provided_instance.py +++ b/examples/providers/provided_instance.py @@ -33,6 +33,7 @@ client_factory = providers.Factory( value4=service.provided.get_value.call(), ) + if __name__ == '__main__': client = client_factory() assert client.value1 == client.value2 == client.value3 == 'foo' diff --git a/examples/providers/provided_instance_complex.py b/examples/providers/provided_instance_complex.py index 0e5ea107..20381f9b 100644 --- a/examples/providers/provided_instance_complex.py +++ b/examples/providers/provided_instance_complex.py @@ -31,6 +31,7 @@ demo_list = providers.List( dependency.provided['foo']['baz'].call(service)['arg'].get_value.call(), ) + if __name__ == '__main__': assert demo_list() == [ 10, diff --git a/src/dependency_injector/__init__.py b/src/dependency_injector/__init__.py index 724f9e1f..b1aa8bb1 100644 --- a/src/dependency_injector/__init__.py +++ b/src/dependency_injector/__init__.py @@ -1,6 +1,6 @@ """Dependency injector top-level package.""" -__version__ = '3.35.1' +__version__ = '3.36.0' """Version number that follows semantic versioning. :type: str diff --git a/src/dependency_injector/providers.pyi b/src/dependency_injector/providers.pyi index 0617e8f2..c8d97a70 100644 --- a/src/dependency_injector/providers.pyi +++ b/src/dependency_injector/providers.pyi @@ -43,6 +43,7 @@ class Provider(_Provider): def delegate(self) -> Provider: ... @property def provider(self) -> Provider: ... + def _copy_overridings(self, copied: Provider, memo: Optional[Dict[str, Any]]) -> None: ... class Object(Provider, Generic[T]):