diff --git a/docs/main/changelog.rst b/docs/main/changelog.rst index 20c321eb..c3890dda 100644 --- a/docs/main/changelog.rst +++ b/docs/main/changelog.rst @@ -11,15 +11,17 @@ follows `Semantic versioning`_ Development version ------------------- -- Add functionality for decorating classes with ``@di.inject``. - Add functionality for creating ``di.AbstractCatalog`` provider bundles. -- Add enhancement for ``di.AbstractCatalog`` inheritance. +- Enhance ``di.AbstractCatalog`` inheritance. - Add images for catalog "Writing catalogs" and "Operating with catalogs" examples. -- Add support of Python 3.5. -- Add support of six 1.10.0. +- Add functionality for using positional argument injections with + ``di.Factory`` and ``di.Singleton`` providers. - Add optimization for ``di.Injection.value`` property that will compute type of injection once, instead of doing this on every call. +- Add functionality for decorating classes with ``@di.inject``. +- Add support of Python 3.5. +- Add support of six 1.10.0. - Add minor refactorings and code style fixes. 0.9.5 diff --git a/docs/providers/factory.rst b/docs/providers/factory.rst index 5f9c6109..320e768b 100644 --- a/docs/providers/factory.rst +++ b/docs/providers/factory.rst @@ -15,46 +15,57 @@ Nothing could be better than brief example: Factory providers and __init__ injections ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -``di.Factory`` takes a various number of keyword arguments that are -transformed into keyword argument injections. Every time, when ``di.Factory`` -creates new one instance, keyword argument injections would be passed as an -instance's keyword arguments. +``di.Factory`` takes a various number of positional and keyword arguments that +are used as ``__init__()`` injections. Every time, when ``di.Factory`` +creates new one instance, positional and keyword argument injections would be +passed as an instance's arguments. -All injectable values are provided *"as is"*, except of providers (subclasses -of ``di.Provider``). Providers will be called every time, when injection needs -to be done. For example, if injectable value of keyword argument injection is a -``di.Factory``, it will provide new one instance (as a result of its call) as -an injectable value every time, when injection needs to be done. +Such behaviour is very similar to the standard Python ``functools.partial`` +object, except of one thing: all injectable values are provided +*"as is"*, except of providers (subclasses of ``di.Provider``). Providers +will be called every time, when injection needs to be done. For example, +if injectable value of injection is a ``di.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 -``di.Factory`` of particular class with ``__init__`` keyword argument -injections which injectable values are also provided by another factories: +``di.Factory`` of particular class with ``__init__()`` argument injections +which injectable values are also provided by another factories: .. note:: - Current keyword argument injections syntax (in an example below) is a - **simplified one**. Full syntax and other types of injections could be - found in sections below. + 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 keyword argument injections may be the best way of passing - injections, current simplified syntax might be the preferable one and - could be widely used. + 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. .. image:: /images/providers/factory_init_injections.png :width: 90% :align: center -.. literalinclude:: ../../examples/providers/factory_init_injections.py +Example of usage positional argument injections: + +.. literalinclude:: ../../examples/providers/factory_init_args.py + :language: python + +Example of usage keyword argument injections: + +.. literalinclude:: ../../examples/providers/factory_init_kwargs.py :language: python Factory providers and __init__ injections priority ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Next example shows how ``di.Factory`` provider deals with positional and -keyword ``__init__`` context arguments. In few words, ``di.Factory`` -provider fully passes positional context arguments to class's ``__init__`` -method, but keyword context arguments have priority on predefined keyword -argument injections. +keyword ``__init__()`` context arguments. In few words, ``di.Factory`` +behaviour here is very like a standard Python ``functools.partial``: + +- Positional context arguments will be appended after ``di.Factory`` + positional injections. +- Keyword context arguments have priority on ``di.Factory`` keyword injections + and will be merged over them. So, please, follow the example below: @@ -67,7 +78,7 @@ So, please, follow the example below: Factory providers and other types of injections ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Objects can take dependencies in different forms(some objects take init +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. @@ -78,8 +89,11 @@ All of those instructions are defined in ``di.injections`` module and are subclasses of ``di.injections.Injection`` (shortcut ``di.Injection``). There are several types of injections that are used by ``di.Factory`` provider: ++ ``di.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. + ``di.KwArg`` - injection is done by passing injectable value in object's - ``__init__()`` method in time of object's creation via keyword argument. + ``__init__()`` method in time of object's creation as keyword argument. Takes keyword name of ``__init__()`` argument and injectable value. + ``di.Attribute`` - injection is done by setting specified attribute with injectable value right after object's creation. Takes attribute's name diff --git a/examples/providers/factory_init_args.py b/examples/providers/factory_init_args.py new file mode 100644 index 00000000..d3b66eff --- /dev/null +++ b/examples/providers/factory_init_args.py @@ -0,0 +1,34 @@ +"""`di.Factory` providers with init positional injections example.""" + +import dependency_injector as di + + +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 = di.Factory(Photo) +users_factory = di.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_and_contexts.py b/examples/providers/factory_init_injections_and_contexts.py index 6bc196d3..7917ba0b 100644 --- a/examples/providers/factory_init_injections_and_contexts.py +++ b/examples/providers/factory_init_injections_and_contexts.py @@ -62,7 +62,8 @@ assert user1.credit_card is not user2.credit_card main_photo_mock = Photo() credit_card_mock = CreditCard() -user3 = users_factory(3, main_photo=main_photo_mock, +user3 = users_factory(3, + main_photo=main_photo_mock, credit_card=credit_card_mock) assert user3.id == 3 diff --git a/examples/providers/factory_init_injections.py b/examples/providers/factory_init_kwargs.py similarity index 83% rename from examples/providers/factory_init_injections.py rename to examples/providers/factory_init_kwargs.py index 1649c9ec..2159e335 100644 --- a/examples/providers/factory_init_injections.py +++ b/examples/providers/factory_init_kwargs.py @@ -1,4 +1,4 @@ -"""`di.Factory` providers with init injections example.""" +"""`di.Factory` providers with init keyword injections example.""" import dependency_injector as di @@ -17,8 +17,7 @@ class Photo(object): # User and Photo factories: photos_factory = di.Factory(Photo) -users_factory = di.Factory(User, - main_photo=photos_factory) +users_factory = di.Factory(User, main_photo=photos_factory) # Creating several User objects: user1 = users_factory() # Same as: user1 = User(main_photo=Photo())