diff --git a/docs/images/providers/factory.png b/docs/images/providers/factory.png index 86830b60..ff79ee5e 100644 Binary files a/docs/images/providers/factory.png and b/docs/images/providers/factory.png differ diff --git a/docs/images/providers/factory_attribute_injection.png b/docs/images/providers/factory_attribute_injection.png deleted file mode 100644 index 0edda7ab..00000000 Binary files a/docs/images/providers/factory_attribute_injection.png and /dev/null differ diff --git a/docs/images/providers/factory_attribute_injections.png b/docs/images/providers/factory_attribute_injections.png deleted file mode 100644 index 0edda7ab..00000000 Binary files a/docs/images/providers/factory_attribute_injections.png and /dev/null differ diff --git a/docs/images/providers/factory_delegation.png b/docs/images/providers/factory_delegation.png index 41192639..e9a741dc 100644 Binary files a/docs/images/providers/factory_delegation.png and b/docs/images/providers/factory_delegation.png differ diff --git a/docs/images/providers/factory_init_injections.png b/docs/images/providers/factory_init_injections.png index 15909700..f39b44d7 100644 Binary files a/docs/images/providers/factory_init_injections.png and b/docs/images/providers/factory_init_injections.png differ diff --git a/docs/images/providers/factory_init_injections_and_contexts.png b/docs/images/providers/factory_init_injections_and_contexts.png deleted file mode 100644 index 8958edfe..00000000 Binary files a/docs/images/providers/factory_init_injections_and_contexts.png and /dev/null differ diff --git a/docs/images/providers/factory_method_injections.png b/docs/images/providers/factory_method_injections.png deleted file mode 100644 index caab35a1..00000000 Binary files a/docs/images/providers/factory_method_injections.png and /dev/null differ diff --git a/docs/providers/factory.rst b/docs/providers/factory.rst index a85cc68f..24609098 100644 --- a/docs/providers/factory.rst +++ b/docs/providers/factory.rst @@ -24,137 +24,29 @@ that are used as ``__init__()`` injections. Every time, when :py:class:`Factory` creates new one instance, positional and keyword argument injections would be passed as an instance's arguments. -Such behaviour is very similar to the standard Python ``functools.partial`` -object with several more things: +Injections are done according to the next rules: + All providers (instances of :py:class:`Provider`) are called every time when injection needs to be done. + Providers could be injected "as is" (delegated), if it is defined obviously. Check out `Factory providers delegation`_. -+ All other injectable values are provided *"as is"* - ++ All other injectable values are provided *"as is"*. ++ Positional context arguments will be appended after :py:class:`Factory` + positional injections. ++ Keyword context arguments have priority on :py:class:`Factory` keyword + injections and will be merged over them. + For example, if injectable value of injection is a :py:class:`Factory`, it will provide new one instance (as a result of its call) every time, when injection needs to be done. Example below is a little bit more complicated. It shows how to create -:py:class:`Factory` of particular class with ``__init__()`` argument -injections which injectable values are also provided by another factories: - -.. note:: - - Current positional and keyword argument injections syntax (in the examples - below) is a **simplified one** version of full syntax. Examples of full - syntax and other types of injections could be found in sections below. - - While positional / keyword argument injections may be the best way of - passing injections, current simplified syntax might be the preferable one - and could be widely used. +:py:class:`Factory` of particular class with ``__init__()`` injections which +injectable values are also provided by another factories: .. image:: /images/providers/factory_init_injections.png - :width: 90% - :align: center -Example of usage positional argument injections: - -.. literalinclude:: ../../examples/providers/factory_init_args.py - :language: python - :linenos: - -Example of usage keyword argument injections: - -.. literalinclude:: ../../examples/providers/factory_init_kwargs.py - :language: python - :linenos: - -Factory providers and __init__ injections priority -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Next example shows how :py:class:`Factory` provider deals with positional and -keyword ``__init__()`` context arguments. In few words, :py:class:`Factory` -behaviour here is very like a standard Python ``functools.partial``: - -- Positional context arguments will be appended after :py:class:`Factory` - positional injections. -- Keyword context arguments have priority on :py:class:`Factory` keyword - injections and will be merged over them. - -So, please, follow the example below: - -.. image:: /images/providers/factory_init_injections_and_contexts.png - -.. literalinclude:: ../../examples/providers/factory_init_injections_and_contexts.py - :language: python - :linenos: - - -Factory providers and other types of injections -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Objects can take dependencies in different forms (some objects take init -arguments, other use attributes setting or method calls). It affects how -such objects are created and initialized. - -:py:class:`Factory` provider takes various number of positional and keyword -arguments, that define what kinds of dependency injections have to be used. - -All of those instructions are defined in -:py:mod:`dependency_injector.injections` module and are subclasses of -:py:class:`dependency_injector.injections.Injection`. There are several types -of injections that are used by :py:class:`Factory` provider: - -+ :py:class:`dependency_injector.injections.Arg` - injection is done by - passing injectable value in object's ``__init__()`` method in time of - object's creation as positional argument. Takes injectable value only. -+ :py:class:`dependency_injector.injections.KwArg` - injection is done by - passing injectable value in object's ``__init__()`` method in time of - object's creation as keyword argument. Takes keyword name of - ``__init__()`` argument and injectable value. -+ :py:class:`dependency_injector.injections.Attribute` - injection is done - by setting specified attribute with injectable value right after - object's creation. Takes attribute's name and injectable value. -+ :py:class:`dependency_injector.injections.Method` - injection is done by - calling of specified method with injectable value right after object's - creation and attribute injections are done. Takes method name and - injectable value. - -All :py:class:`dependency_injector.injections.Injection`'s injectable values -are provided *"as is"*, except of providers (subclasses of -:py:class:`Provider`). Providers will be called every time, when injection -needs to be done. - -Factory providers and attribute injections -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Example below shows how to create :py:class:`Factory` of particular class with -attribute injections. Those injections are done by setting specified attributes -with injectable values right after object's creation. - -Example: - -.. image:: /images/providers/factory_attribute_injections.png - -.. literalinclude:: ../../examples/providers/factory_attribute_injections.py - :language: python - :linenos: - -Factory providers and method injections -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Current example shows how to create :py:class:`Factory` of particular class -with method injections. Those injections are done by calling of specified -method with injectable value right after object's creation and attribute -injections are done. - -Method injections are not very popular in Python due Python best practices -(usage of public attributes instead of setter methods), but they may appear in -some cases. - -Example: - -.. image:: /images/providers/factory_method_injections.png - -.. literalinclude:: ../../examples/providers/factory_method_injections.py +.. literalinclude:: ../../examples/providers/factory_init_injections.py :language: python :linenos: @@ -162,16 +54,26 @@ Factory providers delegation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :py:class:`Factory` provider could be delegated to any other provider via any -kind of injection. As it was mentioned earlier, if :py:class:`Factory` is -injectable value, it will be called every time when injection is done. +kind of injection. + +As it was mentioned earlier, if :py:class:`Factory` is +injectable value, it will be called every time when injection needs to be +done. But sometimes there is a need to inject :py:class:`Factory` provider +itself (not a result of its call) as a dependency. Such injections are called +- *delegated provider injections*. + +Saying in other words, delegation of factories - is a way to inject factories +themselves, instead of results of their calls. + :py:class:`Factory` delegation is performed by wrapping delegated :py:class:`Factory` into special provider type - :py:class:`Delegate`, that -just returns wrapped :py:class:`Factory`. Saying in other words, delegation -of factories - is a way to inject factories themselves, instead of results -of their calls. +just returns wrapped :py:class:`Factory`. -Actually, there are two ways of creating factory delegates: +Actually, there are three ways for creating factory delegates: ++ ``DelegatedFactory(...)`` - use special type of factory - + :py:class:`DelegatedFactory`. Such factories are always injected as + delegates ("as is"). + ``Delegate(Factory(...))`` - obviously wrapping factory into :py:class:`Delegate` provider. + ``Factory(...).delegate()`` - calling factory :py:meth:`Factory.delegate` @@ -187,24 +89,14 @@ Example: :language: python :linenos: -Alternative way of doing :py:class:`Factory` delegation is an usage of -:py:class:`DelegatedFactory`. :py:class:`DelegatedFactory` is a -:py:class:`Factory` that is always injected "as is". - -Example: - -.. literalinclude:: ../../examples/providers/delegated_factory.py - :language: python - :linenos: - Factory providers specialization ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :py:class:`Factory` provider could be specialized for any kind of needs via -declaring its subclasses. +creating its subclasses. -One of such `builtin` features is a limitation to :py:class:`Factory` provided -type: +One of such specialization features is a limitation to :py:class:`Factory` +provided type: .. literalinclude:: ../../examples/providers/factory_provided_type.py :language: python diff --git a/docs/providers/index.rst b/docs/providers/index.rst index 0d57268a..87ecc6db 100644 --- a/docs/providers/index.rst +++ b/docs/providers/index.rst @@ -1,7 +1,7 @@ Providers ========= -Providers are strategies of accessing objects. They describe how particular +Providers are strategies of accessing objects. They define how particular objects are provided. Base providers class is - :py:class:`dependency_injector.providers.Provider` diff --git a/examples/providers/delegated_factory.py b/examples/providers/delegated_factory.py deleted file mode 100644 index e5ab61b0..00000000 --- a/examples/providers/delegated_factory.py +++ /dev/null @@ -1,46 +0,0 @@ -"""`DelegatedFactory` providers example.""" - -import dependency_injector.providers as providers - - -class User(object): - """Example class User.""" - - def __init__(self, photos_factory): - """Initializer. - - :param photos_factory: providers.Factory -> Photo - """ - self.photos_factory = photos_factory - self._main_photo = None - super(User, self).__init__() - - @property - def main_photo(self): - """Return user's main photo.""" - if not self._main_photo: - self._main_photo = self.photos_factory() - return self._main_photo - - -class Photo(object): - """Example class Photo.""" - -# User and Photo factories: -photos_factory = providers.DelegatedFactory(Photo) -users_factory = providers.DelegatedFactory(User, - photos_factory=photos_factory) - -# Creating several User objects: -user1 = users_factory() -user2 = users_factory() - -# Making some asserts: -assert isinstance(user1, User) -assert isinstance(user1.main_photo, Photo) - -assert isinstance(user2, User) -assert isinstance(user2.main_photo, Photo) - -assert user1 is not user2 -assert user1.main_photo is not user2.main_photo diff --git a/examples/providers/factory.py b/examples/providers/factory.py index dbb25160..b8889c20 100644 --- a/examples/providers/factory.py +++ b/examples/providers/factory.py @@ -1,18 +1,14 @@ """`Factory` providers example.""" +import collections import dependency_injector.providers as providers -class User(object): - """Example class User.""" +User = collections.namedtuple('User', []) # Factory provider creates new instance of specified class on every call. 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) +user1 = users_factory() # Same as: user1 = User() +user2 = users_factory() # Same as: user2 = User() diff --git a/examples/providers/factory_attribute_injections.py b/examples/providers/factory_attribute_injections.py deleted file mode 100644 index 9f70d4f0..00000000 --- a/examples/providers/factory_attribute_injections.py +++ /dev/null @@ -1,52 +0,0 @@ -"""`Factory` providers with attribute injections example.""" - -import dependency_injector.providers as providers -import dependency_injector.injections as injections - - -class User(object): - """Example class User.""" - - def __init__(self): - """Initializer.""" - self.main_photo = None - self.credit_card = None - - -class Photo(object): - """Example class Photo.""" - - -class CreditCard(object): - """Example class CreditCard.""" - -# User, Photo and CreditCard factories: -credit_cards_factory = providers.Factory(CreditCard) -photos_factory = providers.Factory(Photo) -users_factory = providers.Factory(User, - injections.Attribute('main_photo', - photos_factory), - injections.Attribute('credit_card', - credit_cards_factory)) - -# Creating several User objects: -user1 = users_factory() -# Same as: user1 = User() -# user1.main_photo = Photo() -# user1.credit_card = CreditCard() -user2 = users_factory() -# Same as: user2 = User() -# user2.main_photo = Photo() -# user2.credit_card = CreditCard() - -# Making some asserts: -assert user1 is not user2 - -assert isinstance(user1.main_photo, Photo) -assert isinstance(user1.credit_card, CreditCard) - -assert isinstance(user2.main_photo, Photo) -assert isinstance(user2.credit_card, CreditCard) - -assert user1.main_photo is not user2.main_photo -assert user1.credit_card is not user2.credit_card diff --git a/examples/providers/factory_delegation.py b/examples/providers/factory_delegation.py index b3c7773e..c5b3a6a1 100644 --- a/examples/providers/factory_delegation.py +++ b/examples/providers/factory_delegation.py @@ -1,19 +1,19 @@ """`Factory` providers delegation example.""" -from dependency_injector import providers +import collections +import dependency_injector.providers as providers + + +Photo = collections.namedtuple('Photo', []) class User(object): - """Example class User.""" + """Example user model.""" def __init__(self, photos_factory): - """Initializer. - - :param photos_factory: providers.Factory -> Photo - """ + """Initializer.""" self.photos_factory = photos_factory self._main_photo = None - super(User, self).__init__() @property def main_photo(self): @@ -23,24 +23,30 @@ class User(object): return self._main_photo -class Photo(object): - """Example class Photo.""" +# Defining User and Photo factories using DelegatedFactory provider: +photos_factory = providers.DelegatedFactory(Photo) +users_factory = providers.DelegatedFactory(User, + photos_factory=photos_factory) + +# or using Delegate(Factory(...)) + +photos_factory = providers.Factory(Photo) +users_factory = providers.Factory(User, + photos_factory=providers.Delegate( + photos_factory)) + + +# or using Factory(...).delegate() -# User and Photo factories: photos_factory = providers.Factory(Photo) users_factory = providers.Factory(User, photos_factory=photos_factory.delegate()) + # Creating several User objects: -user1 = users_factory() -user2 = users_factory() +user1 = users_factory() # Same as: user1 = User(photos_factory=photos_factory) +user2 = users_factory() # Same as: user2 = User(photos_factory=photos_factory) # Making some asserts: -assert isinstance(user1, User) assert isinstance(user1.main_photo, Photo) - -assert isinstance(user2, User) assert isinstance(user2.main_photo, Photo) - -assert user1 is not user2 -assert user1.main_photo is not user2.main_photo diff --git a/examples/providers/factory_init_args.py b/examples/providers/factory_init_args.py deleted file mode 100644 index 4ceb61d7..00000000 --- a/examples/providers/factory_init_args.py +++ /dev/null @@ -1,34 +0,0 @@ -"""`Factory` providers with init positional injections example.""" - -import dependency_injector.providers as providers - - -class User(object): - """Example class User.""" - - def __init__(self, main_photo): - """Initializer.""" - self.main_photo = main_photo - super(User, self).__init__() - - -class Photo(object): - """Example class Photo.""" - -# User and Photo factories: -photos_factory = providers.Factory(Photo) -users_factory = providers.Factory(User, photos_factory) - -# Creating several User objects: -user1 = users_factory() # Same as: user1 = User(Photo()) -user2 = users_factory() # Same as: user2 = User(Photo()) - -# Making some asserts: -assert isinstance(user1, User) -assert isinstance(user1.main_photo, Photo) - -assert isinstance(user2, User) -assert isinstance(user2.main_photo, Photo) - -assert user1 is not user2 -assert user1.main_photo is not user2.main_photo diff --git a/examples/providers/factory_init_injections.py b/examples/providers/factory_init_injections.py new file mode 100644 index 00000000..3d385fab --- /dev/null +++ b/examples/providers/factory_init_injections.py @@ -0,0 +1,38 @@ +"""`Factory` providers init injections example.""" + +import collections +import dependency_injector.providers as providers + + +CreditCard = collections.namedtuple('CreditCard', []) +Photo = collections.namedtuple('Photo', []) +User = collections.namedtuple('User', ['uid', 'main_photo', 'credit_card']) + +# User, Photo and CreditCard factories: +credit_cards_factory = providers.Factory(CreditCard) +photos_factory = providers.Factory(Photo) +users_factory = providers.Factory(User, + main_photo=photos_factory, + credit_card=credit_cards_factory) + +# Creating several User objects: +user1 = users_factory(1) +# Same as: user1 = User(1, +# main_photo=Photo(), +# credit_card=CreditCard()) +user2 = users_factory(2) +# Same as: user2 = User(2, +# main_photo=Photo(), +# credit_card=CreditCard()) + + +# Context keyword arguments have priority on keyword argument injections: +main_photo = Photo() +credit_card = CreditCard() + +user3 = users_factory(3, + main_photo=main_photo, + credit_card=credit_card) +# Same as: user3 = User(3, +# main_photo=main_photo, +# credit_card=credit_card) diff --git a/examples/providers/factory_init_injections_and_contexts.py b/examples/providers/factory_init_injections_and_contexts.py deleted file mode 100644 index 4fcbd4ca..00000000 --- a/examples/providers/factory_init_injections_and_contexts.py +++ /dev/null @@ -1,71 +0,0 @@ -"""`Factory` providers with init injections priority example.""" - -import dependency_injector.providers as providers - - -class User(object): - """Example class User. - - Class User has to be provided with user id. - - Also Class User has dependencies on class Photo and class CreditCard - objects. - - All of the dependencies have to be provided like __init__ arguments. - """ - - def __init__(self, id, main_photo, credit_card): - """Initializer.""" - self.id = id - self.main_photo = main_photo - self.credit_card = credit_card - super(User, self).__init__() - - -class Photo(object): - """Example class Photo.""" - - -class CreditCard(object): - """Example class CreditCard.""" - -# User, Photo and CreditCard factories: -credit_cards_factory = providers.Factory(CreditCard) -photos_factory = providers.Factory(Photo) -users_factory = providers.Factory(User, - main_photo=photos_factory, - credit_card=credit_cards_factory) - -# Creating several User objects: -user1 = users_factory(1) -# Same as: user1 = User(1, -# main_photo=Photo(), -# credit_card=CreditCard()) -user2 = users_factory(2) -# Same as: user2 = User(2, -# main_photo=Photo(), -# credit_card=CreditCard()) - -# Making some asserts: -assert user1.id == 1 -assert isinstance(user1.main_photo, Photo) -assert isinstance(user1.credit_card, CreditCard) - -assert user2.id == 2 -assert isinstance(user2.main_photo, Photo) -assert isinstance(user2.credit_card, CreditCard) - -assert user1.main_photo is not user2.main_photo -assert user1.credit_card is not user2.credit_card - -# Context keyword arguments have priority on keyword argument injections: -main_photo_mock = Photo() -credit_card_mock = CreditCard() - -user3 = users_factory(3, - main_photo=main_photo_mock, - credit_card=credit_card_mock) - -assert user3.id == 3 -assert user3.main_photo is main_photo_mock -assert user3.credit_card is credit_card_mock diff --git a/examples/providers/factory_init_kwargs.py b/examples/providers/factory_init_kwargs.py deleted file mode 100644 index 2e991a4e..00000000 --- a/examples/providers/factory_init_kwargs.py +++ /dev/null @@ -1,34 +0,0 @@ -"""`Factory` providers with init keyword injections example.""" - -import dependency_injector.providers as providers - - -class User(object): - """Example class User.""" - - def __init__(self, main_photo): - """Initializer.""" - self.main_photo = main_photo - super(User, self).__init__() - - -class Photo(object): - """Example class Photo.""" - -# User and Photo factories: -photos_factory = providers.Factory(Photo) -users_factory = providers.Factory(User, main_photo=photos_factory) - -# Creating several User objects: -user1 = users_factory() # Same as: user1 = User(main_photo=Photo()) -user2 = users_factory() # Same as: user2 = User(main_photo=Photo()) - -# Making some asserts: -assert isinstance(user1, User) -assert isinstance(user1.main_photo, Photo) - -assert isinstance(user2, User) -assert isinstance(user2.main_photo, Photo) - -assert user1 is not user2 -assert user1.main_photo is not user2.main_photo diff --git a/examples/providers/factory_provided_type.py b/examples/providers/factory_provided_type.py index 5cdbe385..9a282f64 100644 --- a/examples/providers/factory_provided_type.py +++ b/examples/providers/factory_provided_type.py @@ -1,4 +1,4 @@ -"""`Factory` specialization for limitation to provided type example.""" +"""`Factory` specialization with limitation to provided type example.""" import dependency_injector.providers as providers import dependency_injector.errors as errors @@ -8,12 +8,8 @@ class BaseService(object): """Base service class.""" -class UsersService(BaseService): - """Users service.""" - - -class PhotosService(BaseService): - """Photos service.""" +class SomeService(BaseService): + """Some service.""" class ServiceProvider(providers.Factory): @@ -22,11 +18,10 @@ class ServiceProvider(providers.Factory): provided_type = BaseService -# Creating several service providers with BaseService instances: -users_service_provider = ServiceProvider(UsersService) -photos_service_provider = ServiceProvider(PhotosService) +# Creating service provider with correct provided type: +some_service_provider = ServiceProvider(SomeService) -# Trying to create service provider with not a BaseService instance: +# Trying to create service provider incorrect provided type: try: some_service_provider = ServiceProvider(object) except errors.Error as exception: