diff --git a/README.rst b/README.rst index 05fe1412..bb9f09c4 100644 --- a/README.rst +++ b/README.rst @@ -55,20 +55,20 @@ Installation Example ------- +Brief example below demonstrates usage of *Dependency Injector* catalogs and +providers for definition of several IoC containers for some microservice +system that consists from several business and platform services: + .. code-block:: python - """Dependency Injector example.""" + """Example of several Dependency Injector IoC containers.""" - import sys import sqlite3 - - from boto.s3.connection import S3Connection + import boto.s3.connection + import example.services from dependency_injector import catalogs from dependency_injector import providers - from dependency_injector import injections - - from example import services class Platform(catalogs.DeclarativeCatalog): @@ -76,7 +76,7 @@ Example database = providers.Singleton(sqlite3.connect, ':memory:') - s3 = providers.Singleton(S3Connection, + s3 = providers.Singleton(boto.s3.connection.S3Connection, aws_access_key_id='KEY', aws_secret_access_key='SECRET') @@ -84,32 +84,105 @@ Example class Services(catalogs.DeclarativeCatalog): """Catalog of business service providers.""" - users = providers.Factory(services.Users, + users = providers.Factory(example.services.Users, db=Platform.database) - photos = providers.Factory(services.Photos, + photos = providers.Factory(example.services.Photos, db=Platform.database, s3=Platform.s3) - auth = providers.Factory(services.Auth, + auth = providers.Factory(example.services.Auth, db=Platform.database, token_ttl=3600) +Next example demonstrates usage of ``@inject`` decorator with IoC containers +defined above: - @injections.inject(users_service=Services.users) - @injections.inject(auth_service=Services.auth) - @injections.inject(photos_service=Services.photos) - def main(argv, users_service, auth_service, photos_service): +.. code-block:: python + + """Dependency Injector @inject decorator example.""" + + from dependency_injector.injections import inject + + from catalogs import Services + + + @inject(users_service=Services.users) + @inject(auth_service=Services.auth) + @inject(photos_service=Services.photos) + def main(users_service, auth_service, photos_service): """Main function.""" - login, password, photo_path = argv[1:] - - user = users_service.get_user(login) - auth_service.authenticate(user, password) - photos_service.upload_photo(user['id'], photo_path) + user = users_service.get_user('user') + auth_service.authenticate(user, 'secret') + photos_service.upload_photo(user['id'], 'photo.jpg') if __name__ == '__main__': - main(sys.argv) + main() + +Alternative definition styles +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +*Dependecy Injector* supports few other styles of dependency injections +definition. + +IoC containers from previous example could look like these: + +.. code-block:: python + + class Platform(catalogs.DeclarativeCatalog): + """Catalog of platform service providers.""" + + database = providers.Singleton(sqlite3.connect) \ + .args(':memory:') + + s3 = providers.Singleton(boto.s3.connection.S3Connection) \ + .kwargs(aws_access_key_id='KEY', + aws_secret_access_key='SECRET') + + + class Services(catalogs.DeclarativeCatalog): + """Catalog of business service providers.""" + + users = providers.Factory(example.services.Users) \ + .kwargs(db=Platform.database) + + photos = providers.Factory(example.services.Photos) \ + .kwargs(db=Platform.database, + s3=Platform.s3) + + auth = providers.Factory(example.services.Auth) \ + .kwargs(db=Platform.database, + token_ttl=3600) + +or like this these: + +.. code-block:: python + + class Platform(catalogs.DeclarativeCatalog): + """Catalog of platform service providers.""" + + database = providers.Singleton(sqlite3.connect) + database.args(':memory:') + + s3 = providers.Singleton(boto.s3.connection.S3Connection) + s3.kwargs(aws_access_key_id='KEY', + aws_secret_access_key='SECRET') + + + class Services(catalogs.DeclarativeCatalog): + """Catalog of business service providers.""" + + users = providers.Factory(example.services.Users) + users.kwargs(db=Platform.database) + + photos = providers.Factory(example.services.Photos) + photos.kwargs(db=Platform.database, + s3=Platform.s3) + + auth = providers.Factory(example.services.Auth) + auth.kwargs(db=Platform.database, + token_ttl=3600) You can get more *Dependency Injector* examples in ``/examples`` directory on GitHub: diff --git a/dependency_injector/injections.py b/dependency_injector/injections.py index 27f6cfc6..2387c63c 100644 --- a/dependency_injector/injections.py +++ b/dependency_injector/injections.py @@ -1,13 +1,15 @@ """Injections module.""" +import itertools + import six from dependency_injector.utils import ( is_provider, - is_delegated_provider, is_injection, is_arg_injection, is_kwarg_injection, + is_delegated_provider, fetch_cls_init, ) @@ -36,7 +38,7 @@ class Injection(object): """ __IS_INJECTION__ = True - __slots__ = ('injectable', 'call_injectable') + __slots__ = ('injectable', 'get_value') def __init__(self, injectable): """Initializer. @@ -47,24 +49,14 @@ class Injection(object): :py:class:`dependency_injector.providers.Provider` """ self.injectable = injectable - self.call_injectable = (is_provider(injectable) and - not is_delegated_provider(injectable)) + + if not is_provider(injectable) or is_delegated_provider(injectable): + def injectable(): + return self.injectable + self.get_value = injectable + super(Injection, self).__init__() - @property - def value(self): - """Read-only property that represents injectable value. - - Injectable values and delegated providers are provided "as is". - Other providers will be called every time, when injection needs to - be done. - - :rtype: object - """ - if self.call_injectable: - return self.injectable.provide() - return self.injectable - def __str__(self): """Return string representation of provider. @@ -227,11 +219,11 @@ def inject(*args, **kwargs): def decorated(*args, **kwargs): """Decorated with dependency injection callback.""" if decorated.args: - args = tuple(arg.value for arg in decorated.args) + args + args = tuple(arg.get_value() for arg in decorated.args) + args for kwarg in decorated.kwargs: if kwarg.name not in kwargs: - kwargs[kwarg.name] = kwarg.value + kwargs[kwarg.name] = kwarg.get_value() return callback(*args, **kwargs) @@ -246,18 +238,16 @@ def inject(*args, **kwargs): def _parse_args_injections(args): - """Parse positional argument injections according to current syntax.""" return tuple(Arg(arg) if not is_injection(arg) else arg for arg in args if not is_injection(arg) or is_arg_injection(arg)) def _parse_kwargs_injections(args, kwargs): - """Parse keyword argument injections according to current syntax.""" kwarg_injections = tuple(injection for injection in args if is_kwarg_injection(injection)) if kwargs: - kwarg_injections += tuple(KwArg(name, value) - for name, value in six.iteritems(kwargs)) + kwarg_injections += tuple(itertools.starmap(KwArg, + six.iteritems(kwargs))) return kwarg_injections diff --git a/dependency_injector/providers/callable.py b/dependency_injector/providers/callable.py index a69f70eb..f2714b3c 100644 --- a/dependency_injector/providers/callable.py +++ b/dependency_injector/providers/callable.py @@ -3,70 +3,46 @@ import six from dependency_injector.providers.base import Provider -from dependency_injector.injections import ( - _parse_args_injections, - _parse_kwargs_injections, -) +from dependency_injector.injections import Arg, KwArg from dependency_injector.utils import represent_provider from dependency_injector.errors import Error @six.python_2_unicode_compatible class Callable(Provider): - """:py:class:`Callable` provider calls wrapped callable on every call. + r""":py:class:`Callable` provider calls wrapped callable on every call. :py:class:`Callable` provider provides callable that is called on every provider call with some predefined dependency injections. - :py:class:`Callable` syntax of passing injections is the same like - :py:class:`Factory` one: + :py:class:`Callable` supports positional and keyword argument injections: .. code-block:: python - # simplified syntax for passing positional and keyword argument - # injections: - some_function = Callable(some_function, 'arg1', 'arg2', arg3=3, arg4=4) - - # extended (full) syntax for passing positional and keyword argument - # injections: some_function = Callable(some_function, - injections.Arg(1), - injections.Arg(2), - injections.KwArg('some_arg', 3), - injections.KwArg('other_arg', 4)) + 'positional_arg1', 'positional_arg2', + keyword_argument1=3, keyword_argument=4) - .. py:attribute:: provides + # or - Provided callable. + some_function = Callable(some_function) \ + .args('positional_arg1', 'positional_arg2') \ + .kwargs(keyword_argument1=3, keyword_argument=4) - :type: callable + # or - .. py:attribute:: args - - Tuple of positional argument injections. - - :type: tuple[:py:class:`dependency_injector.injections.Arg`] - - .. py:attribute:: kwargs - - Tuple of keyword argument injections. - - :type: tuple[:py:class:`dependency_injector.injections.KwArg`] + some_function = Callable(some_function) + some_function.args('positional_arg1', 'positional_arg2') + some_function.kwargs(keyword_argument1=3, keyword_argument=4) """ - __slots__ = ('provides', 'args', 'kwargs') + __slots__ = ('_provides', '_args', '_kwargs') def __init__(self, provides, *args, **kwargs): """Initializer. :param provides: Wrapped callable. :type provides: callable - - :param args: Tuple of injections. - :type args: tuple - - :param kwargs: Dictionary of injections. - :type kwargs: dict """ if not callable(provides): raise Error('Provider {0} expected to get callable, ' @@ -74,12 +50,12 @@ class Callable(Provider): self.__class__.__name__)), provides)) - self.provides = provides + self._provides = provides + self._args = tuple() + self._kwargs = tuple() - self.args = tuple() - self.kwargs = tuple() - - self.add_injections(*args, **kwargs) + self.args(*args) + self.kwargs(**kwargs) super(Callable, self).__init__() @@ -89,19 +65,30 @@ class Callable(Provider): :rtype: tuple[:py:class:`dependency_injector.injections.Injection`] """ - return self.args + self.kwargs + return self._args + self._kwargs - def add_injections(self, *args, **kwargs): - """Add provider injections. + def args(self, *args): + """Add postional argument injections. :param args: Tuple of injections. :type args: tuple + :return: Reference ``self`` + """ + self._args += tuple(Arg(value) for value in args) + return self + + def kwargs(self, **kwargs): + """Add keyword argument injections. + :param kwargs: Dictionary of injections. :type kwargs: dict + + :return: Reference ``self`` """ - self.args += _parse_args_injections(args) - self.kwargs += _parse_kwargs_injections(args, kwargs) + self._kwargs += tuple(KwArg(name, value) + for name, value in six.iteritems(kwargs)) + return self def _provide(self, *args, **kwargs): """Return provided instance. @@ -114,21 +101,21 @@ class Callable(Provider): :rtype: object """ - if self.args: - args = tuple(arg.value for arg in self.args) + args + if self._args: + args = tuple(arg.get_value() for arg in self._args) + args - for kwarg in self.kwargs: + for kwarg in self._kwargs: if kwarg.name not in kwargs: - kwargs[kwarg.name] = kwarg.value + kwargs[kwarg.name] = kwarg.get_value() - return self.provides(*args, **kwargs) + return self._provides(*args, **kwargs) def __str__(self): """Return string representation of provider. :rtype: str """ - return represent_provider(provider=self, provides=self.provides) + return represent_provider(provider=self, provides=self._provides) __repr__ = __str__ @@ -138,24 +125,6 @@ class DelegatedCallable(Callable): :py:class:`DelegatedCallable` is a :py:class:`Callable`, that is injected "as is". - - .. py:attribute:: provides - - Provided callable. - - :type: callable - - .. py:attribute:: args - - Tuple of positional argument injections. - - :type: tuple[:py:class:`dependency_injector.injections.Arg`] - - .. py:attribute:: kwargs - - Tuple of keyword argument injections. - - :type: tuple[:py:class:`dependency_injector.injections.KwArg`] """ __IS_DELEGATED__ = True diff --git a/dependency_injector/providers/creational.py b/dependency_injector/providers/creational.py index cfd2dea3..4a307ed1 100644 --- a/dependency_injector/providers/creational.py +++ b/dependency_injector/providers/creational.py @@ -1,40 +1,53 @@ """Dependency injector creational providers.""" +import six + from dependency_injector.providers.callable import Callable -from dependency_injector.utils import ( - is_attribute_injection, - GLOBAL_LOCK, -) +from dependency_injector.injections import Attribute +from dependency_injector.utils import GLOBAL_LOCK from dependency_injector.errors import Error class Factory(Callable): - """:py:class:`Factory` provider creates new instance on every call. + r""":py:class:`Factory` provider creates new instance on every call. - :py:class:`Factory` supports different syntaxes of passing injections: + :py:class:`Factory` supports positional & keyword argument injections, + as well as attribute injections. + + Positional and keyword argument injections could be defined like this: .. code-block:: python - # simplified syntax for passing positional and keyword argument - # injections only: - factory = Factory(SomeClass, 'arg1', 'arg2', arg3=3, arg4=4) - - # extended (full) syntax for passing any type of injections: factory = Factory(SomeClass, - injections.Arg(1), - injections.Arg(2), - injections.KwArg('some_arg', 3), - injections.KwArg('other_arg', 4), - injections.Attribute('some_attribute', 5)) + 'positional_arg1', 'positional_arg2', + keyword_argument1=3, keyword_argument=4) + + # or + + factory = Factory(SomeClass) \ + .args('positional_arg1', 'positional_arg2') \ + .kwargs(keyword_argument1=3, keyword_argument=4) + + # or + + factory = Factory(SomeClass) + factory.args('positional_arg1', 'positional_arg2') + factory.kwargs(keyword_argument1=3, keyword_argument=4) + + + Attribute injections are defined by using :py:meth:`Factory.attributes`: + + .. code-block:: python + + factory = Factory(SomeClass) \ + .attributes(attribute1=1, attribute2=2) Retrieving of provided instance can be performed via calling :py:class:`Factory` object: .. code-block:: python - factory = Factory(SomeClass, - some_arg1=1, - some_arg2=2) + factory = Factory(SomeClass) some_object = factory() .. py:attribute:: provided_type @@ -45,41 +58,17 @@ class Factory(Callable): :type: type | None - .. py:attribute:: provides - - Class or other callable that provides object. - - :type: type | callable - .. py:attribute:: cls Class that provides object. Alias for :py:attr:`provides`. :type: type - - .. py:attribute:: args - - Tuple of positional argument injections. - - :type: tuple[:py:class:`dependency_injector.injections.Arg`] - - .. py:attribute:: kwargs - - Tuple of keyword argument injections. - - :type: tuple[:py:class:`dependency_injector.injections.KwArg`] - - .. py:attribute:: attributes - - Tuple of attribute injections. - - :type: tuple[:py:class:`dependency_injector.injections.Attribute`] """ provided_type = None - __slots__ = ('cls', 'attributes') + __slots__ = ('cls', '_attributes') def __init__(self, provides, *args, **kwargs): """Initializer. @@ -87,23 +76,17 @@ class Factory(Callable): :param provides: Class or other callable that provides object for creation. :type provides: type | callable - - :param args: Tuple of injections. - :type args: tuple - - :param kwargs: Dictionary of injections. - :type kwargs: dict """ if (self.__class__.provided_type and not issubclass(provides, self.__class__.provided_type)): raise Error('{0} can provide only {1} instances'.format( self.__class__, self.__class__.provided_type)) - self.attributes = tuple() + self._attributes = tuple() super(Factory, self).__init__(provides, *args, **kwargs) - self.cls = self.provides + self.cls = self._provides @property def injections(self): @@ -111,22 +94,18 @@ class Factory(Callable): :rtype: tuple[:py:class:`dependency_injector.injections.Injection`] """ - return self.args + self.kwargs + self.attributes + return super(Factory, self).injections + self._attributes - def add_injections(self, *args, **kwargs): - """Add provider injections. - - :param args: Tuple of injections. - :type args: tuple + def attributes(self, **kwargs): + """Add attribute injections. :param kwargs: Dictionary of injections. :type kwargs: dict - """ - self.attributes += tuple(injection - for injection in args - if is_attribute_injection(injection)) - super(Factory, self).add_injections(*args, **kwargs) + :return: Reference ``self`` + """ + self._attributes += tuple(Attribute(name, value) + for name, value in six.iteritems(kwargs)) return self def _provide(self, *args, **kwargs): @@ -140,17 +119,17 @@ class Factory(Callable): :rtype: object """ - if self.args: - args = tuple(arg.value for arg in self.args) + args + if self._args: + args = tuple(arg.get_value() for arg in self._args) + args - for kwarg in self.kwargs: + for kwarg in self._kwargs: if kwarg.name not in kwargs: - kwargs[kwarg.name] = kwarg.value + kwargs[kwarg.name] = kwarg.get_value() - instance = self.provides(*args, **kwargs) + instance = self._provides(*args, **kwargs) - for attribute in self.attributes: - setattr(instance, attribute.name, attribute.value) + for attribute in self._attributes: + setattr(instance, attribute.name, attribute.get_value()) return instance @@ -169,36 +148,12 @@ class DelegatedFactory(Factory): :type: type | None - .. py:attribute:: provides - - Class or other callable that provides object. - - :type: type | callable - .. py:attribute:: cls Class that provides object. Alias for :py:attr:`provides`. :type: type - - .. py:attribute:: args - - Tuple of positional argument injections. - - :type: tuple[:py:class:`dependency_injector.injections.Arg`] - - .. py:attribute:: kwargs - - Tuple of keyword argument injections. - - :type: tuple[:py:class:`dependency_injector.injections.KwArg`] - - .. py:attribute:: attributes - - Tuple of attribute injections. - - :type: tuple[:py:class:`dependency_injector.injections.Attribute`] """ __IS_DELEGATED__ = True @@ -219,9 +174,7 @@ class Singleton(Factory): .. code-block:: python - singleton = Singleton(SomeClass, - some_arg1=1, - some_arg2=2) + singleton = Singleton(SomeClass) some_object = singleton() .. py:attribute:: provided_type @@ -232,45 +185,15 @@ class Singleton(Factory): :type: type | None - .. py:attribute:: instance - - Read-only reference to singleton's instance. - - :type: object - - .. py:attribute:: provides - - Class or other callable that provides object. - - :type: type | callable - .. py:attribute:: cls Class that provides object. Alias for :py:attr:`provides`. :type: type - - .. py:attribute:: args - - Tuple of positional argument injections. - - :type: tuple[:py:class:`dependency_injector.injections.Arg`] - - .. py:attribute:: kwargs - - Tuple of keyword argument injections. - - :type: tuple[:py:class:`dependency_injector.injections.KwArg`] - - .. py:attribute:: attributes - - Tuple of attribute injections. - - :type: tuple[:py:class:`dependency_injector.injections.Attribute`] """ - __slots__ = ('instance',) + __slots__ = ('_instance',) def __init__(self, provides, *args, **kwargs): """Initializer. @@ -278,14 +201,8 @@ class Singleton(Factory): :param provides: Class or other callable that provides object for creation. :type provides: type | callable - - :param args: Tuple of injections. - :type args: tuple - - :param kwargs: Dictionary of injections. - :type kwargs: dict """ - self.instance = None + self._instance = None super(Singleton, self).__init__(provides, *args, **kwargs) def reset(self): @@ -293,7 +210,7 @@ class Singleton(Factory): :rtype: None """ - self.instance = None + self._instance = None def _provide(self, *args, **kwargs): """Return provided instance. @@ -306,13 +223,13 @@ class Singleton(Factory): :rtype: object """ - if self.instance: - return self.instance + if self._instance: + return self._instance with GLOBAL_LOCK: - self.instance = super(Singleton, self)._provide(*args, **kwargs) + self._instance = super(Singleton, self)._provide(*args, **kwargs) - return self.instance + return self._instance class DelegatedSingleton(Singleton): @@ -329,42 +246,12 @@ class DelegatedSingleton(Singleton): :type: type | None - .. py:attribute:: instance - - Read-only reference to singleton's instance. - - :type: object - - .. py:attribute:: provides - - Class or other callable that provides object. - - :type: type | callable - .. py:attribute:: cls Class that provides object. Alias for :py:attr:`provides`. :type: type - - .. py:attribute:: args - - Tuple of positional argument injections. - - :type: tuple[:py:class:`dependency_injector.injections.Arg`] - - .. py:attribute:: kwargs - - Tuple of keyword argument injections. - - :type: tuple[:py:class:`dependency_injector.injections.KwArg`] - - .. py:attribute:: attributes - - Tuple of attribute injections. - - :type: tuple[:py:class:`dependency_injector.injections.Attribute`] """ __IS_DELEGATED__ = True diff --git a/docs/main/changelog.rst b/docs/main/changelog.rst index 1ea8f9fc..dc8f0d66 100644 --- a/docs/main/changelog.rst +++ b/docs/main/changelog.rst @@ -13,7 +13,9 @@ Development version 2.0.0 ------ -- Drop backward compatibilities of 1.x. +- Introduce new injections style for ``Callable``, ``Factory`` & + ``Singleton`` providers. +- Increase performance of retrieving injections in 2 times (+100%). - Drop providers: - ``Static`` - ``Value`` @@ -21,6 +23,7 @@ Development version - ``Class`` - ``Config`` - Drop ``Method`` injections. +- Drop backward compatibilities of 1.x. 1.17.0 ------ diff --git a/examples/miniapps/services/README.rst b/examples/miniapps/services/README.rst index ccf3947b..833bd44e 100644 --- a/examples/miniapps/services/README.rst +++ b/examples/miniapps/services/README.rst @@ -1,8 +1,8 @@ -Dependency Injector example -=========================== +Dependency Injector IoC containers example +========================================== -Instructions for running: +Instructions for running .. code-block:: bash - python main.py dependency-injector secret myself.jpg + python main.py diff --git a/examples/miniapps/services/catalogs.py b/examples/miniapps/services/catalogs.py new file mode 100644 index 00000000..37a5bdf7 --- /dev/null +++ b/examples/miniapps/services/catalogs.py @@ -0,0 +1,33 @@ +"""Example of several Dependency Injector IoC containers.""" + +import sqlite3 +import boto.s3.connection +import example.services + +from dependency_injector import catalogs +from dependency_injector import providers + + +class Platform(catalogs.DeclarativeCatalog): + """Catalog of platform service providers.""" + + database = providers.Singleton(sqlite3.connect, ':memory:') + + s3 = providers.Singleton(boto.s3.connection.S3Connection, + aws_access_key_id='KEY', + aws_secret_access_key='SECRET') + + +class Services(catalogs.DeclarativeCatalog): + """Catalog of business service providers.""" + + users = providers.Factory(example.services.Users, + db=Platform.database) + + photos = providers.Factory(example.services.Photos, + db=Platform.database, + s3=Platform.s3) + + auth = providers.Factory(example.services.Auth, + db=Platform.database, + token_ttl=3600) diff --git a/examples/miniapps/services/catalogs_alt_syntax_1.py b/examples/miniapps/services/catalogs_alt_syntax_1.py new file mode 100644 index 00000000..8cc97603 --- /dev/null +++ b/examples/miniapps/services/catalogs_alt_syntax_1.py @@ -0,0 +1,37 @@ +"""Example of several Dependency Injector IoC containers. + +Alternative injections definition style #1. +""" + +import sqlite3 +import boto.s3.connection +import example.services + +from dependency_injector import catalogs +from dependency_injector import providers + + +class Platform(catalogs.DeclarativeCatalog): + """Catalog of platform service providers.""" + + database = providers.Singleton(sqlite3.connect) \ + .args(':memory:') + + s3 = providers.Singleton(boto.s3.connection.S3Connection) \ + .kwargs(aws_access_key_id='KEY', + aws_secret_access_key='SECRET') + + +class Services(catalogs.DeclarativeCatalog): + """Catalog of business service providers.""" + + users = providers.Factory(example.services.Users) \ + .kwargs(db=Platform.database) + + photos = providers.Factory(example.services.Photos) \ + .kwargs(db=Platform.database, + s3=Platform.s3) + + auth = providers.Factory(example.services.Auth) \ + .kwargs(db=Platform.database, + token_ttl=3600) diff --git a/examples/miniapps/services/catalogs_alt_syntax_2.py b/examples/miniapps/services/catalogs_alt_syntax_2.py new file mode 100644 index 00000000..1d5746b9 --- /dev/null +++ b/examples/miniapps/services/catalogs_alt_syntax_2.py @@ -0,0 +1,37 @@ +"""Example of several Dependency Injector IoC containers. + +Alternative injections definition style #2. +""" + +import sqlite3 +import boto.s3.connection +import example.services + +from dependency_injector import catalogs +from dependency_injector import providers + + +class Platform(catalogs.DeclarativeCatalog): + """Catalog of platform service providers.""" + + database = providers.Singleton(sqlite3.connect) + database.args(':memory:') + + s3 = providers.Singleton(boto.s3.connection.S3Connection) + s3.kwargs(aws_access_key_id='KEY', + aws_secret_access_key='SECRET') + + +class Services(catalogs.DeclarativeCatalog): + """Catalog of business service providers.""" + + users = providers.Factory(example.services.Users) + users.kwargs(db=Platform.database) + + photos = providers.Factory(example.services.Photos) + photos.kwargs(db=Platform.database, + s3=Platform.s3) + + auth = providers.Factory(example.services.Auth) + auth.kwargs(db=Platform.database, + token_ttl=3600) diff --git a/examples/miniapps/services/main.py b/examples/miniapps/services/main.py index 30ebdb76..9398c8b3 100644 --- a/examples/miniapps/services/main.py +++ b/examples/miniapps/services/main.py @@ -1,53 +1,19 @@ -"""Dependency Injector example.""" +"""Dependency Injector @inject decorator example.""" -import sys -import sqlite3 +from dependency_injector.injections import inject -from boto.s3.connection import S3Connection - -from dependency_injector import catalogs -from dependency_injector import providers -from dependency_injector import injections - -from example import services +from catalogs import Services -class Platform(catalogs.DeclarativeCatalog): - """Catalog of platform service providers.""" - - database = providers.Singleton(sqlite3.connect, ':memory:') - - s3 = providers.Singleton(S3Connection, - aws_access_key_id='KEY', - aws_secret_access_key='SECRET') - - -class Services(catalogs.DeclarativeCatalog): - """Catalog of business service providers.""" - - users = providers.Factory(services.Users, - db=Platform.database) - - photos = providers.Factory(services.Photos, - db=Platform.database, - s3=Platform.s3) - - auth = providers.Factory(services.Auth, - db=Platform.database, - token_ttl=3600) - - -@injections.inject(users_service=Services.users) -@injections.inject(auth_service=Services.auth) -@injections.inject(photos_service=Services.photos) -def main(argv, users_service, auth_service, photos_service): +@inject(users_service=Services.users) +@inject(auth_service=Services.auth) +@inject(photos_service=Services.photos) +def main(users_service, auth_service, photos_service): """Main function.""" - login, password, photo_path = argv[1:] - - user = users_service.get_user(login) - auth_service.authenticate(user, password) - photos_service.upload_photo(user['id'], photo_path) + user = users_service.get_user('user') + auth_service.authenticate(user, 'secret') + photos_service.upload_photo(user['id'], 'photo.jpg') if __name__ == '__main__': - main(sys.argv) + main() diff --git a/tests/catalogs/test_declarative.py b/tests/catalogs/test_declarative.py index e3279c2c..0bf2a86d 100644 --- a/tests/catalogs/test_declarative.py +++ b/tests/catalogs/test_declarative.py @@ -382,7 +382,8 @@ class CopyingTests(unittest.TestCase): """Test catalog providers copying.""" class CatalogA(catalogs.DeclarativeCatalog): p11 = providers.Object(0) - p12 = providers.Factory(dict, p11=p11) + p12 = providers.Factory(dict) \ + .kwargs(p11=p11) @catalogs.copy(CatalogA) class CatalogA1(CatalogA): diff --git a/tests/providers/test_callable.py b/tests/providers/test_callable.py index cfaf83df..2f526470 100644 --- a/tests/providers/test_callable.py +++ b/tests/providers/test_callable.py @@ -2,12 +2,7 @@ import unittest2 as unittest -from dependency_injector import ( - providers, - injections, - utils, - errors, -) +from dependency_injector import providers, utils, errors class CallableTests(unittest.TestCase): @@ -35,60 +30,51 @@ class CallableTests(unittest.TestCase): New simplified syntax. """ - provider = providers.Callable(self.example, 1, 2, 3, 4) + provider = providers.Callable(self.example) \ + .args(1, 2, 3, 4) + self.assertTupleEqual(provider(), (1, 2, 3, 4)) def test_call_with_keyword_args(self): - """Test call with keyword args. + """Test call with keyword args.""" + provider = providers.Callable(self.example) \ + .kwargs(arg1=1, arg2=2, arg3=3, arg4=4) - New simplified syntax. - """ - provider = providers.Callable(self.example, - arg1=1, - arg2=2, - arg3=3, - arg4=4) self.assertTupleEqual(provider(), (1, 2, 3, 4)) def test_call_with_positional_and_keyword_args(self): - """Test call with positional and keyword args. + """Test call with positional and keyword args.""" + provider = providers.Callable(self.example) \ + .args(1, 2) \ + .kwargs(arg3=3, arg4=4) - Simplified syntax of positional and keyword arg injections. - """ - provider = providers.Callable(self.example, 1, 2, arg3=3, arg4=4) - self.assertTupleEqual(provider(), (1, 2, 3, 4)) - - def test_call_with_positional_and_keyword_args_extended_syntax(self): - """Test call with positional and keyword args. - - Extended syntax of positional and keyword arg injections. - """ - provider = providers.Callable(self.example, - injections.Arg(1), - injections.Arg(2), - injections.KwArg('arg3', 3), - injections.KwArg('arg4', 4)) self.assertTupleEqual(provider(), (1, 2, 3, 4)) def test_call_with_context_args(self): """Test call with context args.""" - provider = providers.Callable(self.example, 1, 2) + provider = providers.Callable(self.example) \ + .args(1, 2) + self.assertTupleEqual(provider(3, 4), (1, 2, 3, 4)) def test_call_with_context_kwargs(self): """Test call with context kwargs.""" - provider = providers.Callable(self.example, - injections.KwArg('arg1', 1)) + provider = providers.Callable(self.example) \ + .kwargs(arg1=1) + self.assertTupleEqual(provider(arg2=2, arg3=3, arg4=4), (1, 2, 3, 4)) def test_call_with_context_args_and_kwargs(self): """Test call with context args and kwargs.""" - provider = providers.Callable(self.example, 1) + provider = providers.Callable(self.example) \ + .args(1) + self.assertTupleEqual(provider(2, arg3=3, arg4=4), (1, 2, 3, 4)) def test_call_overridden(self): """Test creation of new instances on overridden provider.""" provider = providers.Callable(self.example) + provider.override(providers.Object((4, 3, 2, 1))) provider.override(providers.Object((1, 2, 3, 4))) @@ -96,24 +82,20 @@ class CallableTests(unittest.TestCase): def test_injections(self): """Test getting a full list of injections using injections property.""" - provider = providers.Callable(self.example, 1, 2, arg3=3, arg4=4) + provider = providers.Callable(self.example) \ + .args(1, 2) \ + .kwargs(arg3=3, arg4=4) + self.assertEquals(len(provider.injections), 4) def test_repr(self): """Test representation of provider.""" - provider = providers.Callable(self.example, - injections.KwArg( - 'arg1', - providers.Factory(dict)), - injections.KwArg( - 'arg2', - providers.Factory(list)), - injections.KwArg( - 'arg3', - providers.Factory(set)), - injections.KwArg( - 'arg4', - providers.Factory(tuple))) + provider = providers.Callable(self.example) \ + .kwargs(arg1=providers.Factory(dict), + arg2=providers.Factory(list), + arg3=providers.Factory(set), + arg4=providers.Factory(tuple)) + self.assertEqual(repr(provider), ''.format( diff --git a/tests/providers/test_creational.py b/tests/providers/test_creational.py index 5c5f9b19..215f557d 100644 --- a/tests/providers/test_creational.py +++ b/tests/providers/test_creational.py @@ -2,12 +2,7 @@ import unittest2 as unittest -from dependency_injector import ( - providers, - injections, - utils, - errors, -) +from dependency_injector import providers, injections, utils, errors class Example(object): @@ -47,7 +42,8 @@ class FactoryTests(unittest.TestCase): provided_type = Example - example_provider = ExampleProvider(Example, 1, 2) + example_provider = ExampleProvider(Example) \ + .args(1, 2) self.assertIsInstance(example_provider(), Example) @@ -61,7 +57,8 @@ class FactoryTests(unittest.TestCase): class NewExampe(Example): """Example class subclass.""" - example_provider = ExampleProvider(NewExampe, 1, 2) + example_provider = ExampleProvider(NewExampe) \ + .args(1, 2) self.assertIsInstance(example_provider(), NewExampe) @@ -76,8 +73,9 @@ class FactoryTests(unittest.TestCase): ExampleProvider(list) def test_call(self): - """Test creation of new instances.""" + """Test call.""" provider = providers.Factory(Example) + instance1 = provider() instance2 = provider() @@ -86,11 +84,9 @@ class FactoryTests(unittest.TestCase): self.assertIsInstance(instance2, Example) def test_call_with_init_positional_args(self): - """Test creation of new instances with init positional args. - - New simplified syntax. - """ - provider = providers.Factory(Example, 'i1', 'i2') + """Test call with init positional args.""" + provider = providers.Factory(Example) \ + .args('i1', 'i2') instance1 = provider() instance2 = provider() @@ -106,11 +102,9 @@ class FactoryTests(unittest.TestCase): self.assertIsInstance(instance2, Example) def test_call_with_init_keyword_args(self): - """Test creation of new instances with init keyword args. - - New simplified syntax. - """ - provider = providers.Factory(Example, init_arg1='i1', init_arg2='i2') + """Test call with init keyword args.""" + provider = providers.Factory(Example) \ + .kwargs(init_arg1='i1', init_arg2='i2') instance1 = provider() instance2 = provider() @@ -126,33 +120,10 @@ class FactoryTests(unittest.TestCase): self.assertIsInstance(instance2, Example) def test_call_with_init_positional_and_keyword_args(self): - """Test creation of new instances with init positional and keyword args. - - Simplified syntax of positional and keyword arg injections. - """ - provider = providers.Factory(Example, 'i1', init_arg2='i2') - - instance1 = provider() - instance2 = provider() - - self.assertEqual(instance1.init_arg1, 'i1') - self.assertEqual(instance1.init_arg2, 'i2') - - self.assertEqual(instance2.init_arg1, 'i1') - self.assertEqual(instance2.init_arg2, 'i2') - - self.assertIsNot(instance1, instance2) - self.assertIsInstance(instance1, Example) - self.assertIsInstance(instance2, Example) - - def test_call_with_init_positional_and_keyword_args_extended_syntax(self): - """Test creation of new instances with init positional and keyword args. - - Extended syntax of positional and keyword arg injections. - """ - provider = providers.Factory(Example, - injections.Arg('i1'), - injections.KwArg('init_arg2', 'i2')) + """Test call with init positional and keyword args.""" + provider = providers.Factory(Example) \ + .args('i1') \ + .kwargs(init_arg2='i2') instance1 = provider() instance2 = provider() @@ -168,10 +139,9 @@ class FactoryTests(unittest.TestCase): self.assertIsInstance(instance2, Example) def test_call_with_attributes(self): - """Test creation of new instances with attribute injections.""" - provider = providers.Factory(Example, - injections.Attribute('attribute1', 'a1'), - injections.Attribute('attribute2', 'a2')) + """Test call with attribute injections.""" + provider = providers.Factory(Example) \ + .attributes(attribute1='a1', attribute2='a2') instance1 = provider() instance2 = provider() @@ -187,8 +157,10 @@ class FactoryTests(unittest.TestCase): self.assertIsInstance(instance2, Example) def test_call_with_context_args(self): - """Test creation of new instances with context args.""" - provider = providers.Factory(Example, 11, 22) + """Test call with context args.""" + provider = providers.Factory(Example) \ + .args(11, 22) + instance = provider(33, 44) self.assertEqual(instance.init_arg1, 11) @@ -197,9 +169,9 @@ class FactoryTests(unittest.TestCase): self.assertEqual(instance.init_arg4, 44) def test_call_with_context_kwargs(self): - """Test creation of new instances with context kwargs.""" - provider = providers.Factory(Example, - injections.KwArg('init_arg1', 1)) + """Test call with context kwargs.""" + provider = providers.Factory(Example) \ + .kwargs(init_arg1=1) instance1 = provider(init_arg2=22) self.assertEqual(instance1.init_arg1, 1) @@ -210,8 +182,10 @@ class FactoryTests(unittest.TestCase): self.assertEqual(instance2.init_arg2, 22) def test_call_with_context_args_and_kwargs(self): - """Test creation of new instances with context args and kwargs.""" - provider = providers.Factory(Example, 11) + """Test call with context args and kwargs.""" + provider = providers.Factory(Example) \ + .args(11) + instance = provider(22, init_arg3=33, init_arg4=44) self.assertEqual(instance.init_arg1, 11) @@ -220,7 +194,7 @@ class FactoryTests(unittest.TestCase): self.assertEqual(instance.init_arg4, 44) def test_call_overridden(self): - """Test creation of new instances on overridden provider.""" + """Test call on overridden provider.""" provider = providers.Factory(Example) overriding_provider1 = providers.Factory(dict) overriding_provider2 = providers.Factory(list) @@ -237,20 +211,19 @@ class FactoryTests(unittest.TestCase): def test_injections(self): """Test getting a full list of injections using injections property.""" - provider = providers.Factory(Example, - injections.Arg(1), - injections.KwArg('init_arg2', 2), - injections.Attribute('attribute1', 3), - injections.Attribute('attribute2', 4)) + provider = providers.Factory(Example) \ + .args(1) \ + .kwargs(init_arg2=2) \ + .attributes(attribute1=3, attribute2=4) + self.assertEquals(len(provider.injections), 4) def test_repr(self): """Test representation of provider.""" - provider = providers.Factory(Example, - injections.KwArg('init_arg1', - providers.Factory(dict)), - injections.KwArg('init_arg2', - providers.Factory(list))) + provider = providers.Factory(Example) \ + .kwargs(init_arg1=providers.Factory(dict), + init_arg2=providers.Factory(list)) + self.assertEqual(repr(provider), ''.format( @@ -300,7 +273,8 @@ class SingletonTests(unittest.TestCase): provided_type = Example - example_provider = ExampleProvider(Example, 1, 2) + example_provider = ExampleProvider(Example) \ + .args(1, 2) self.assertIsInstance(example_provider(), Example) @@ -314,7 +288,8 @@ class SingletonTests(unittest.TestCase): class NewExampe(Example): """Example class subclass.""" - example_provider = ExampleProvider(NewExampe, 1, 2) + example_provider = ExampleProvider(NewExampe) \ + .args(1, 2) self.assertIsInstance(example_provider(), NewExampe) @@ -331,6 +306,7 @@ class SingletonTests(unittest.TestCase): def test_call(self): """Test getting of instances.""" provider = providers.Singleton(Example) + instance1 = provider() instance2 = provider() @@ -339,11 +315,9 @@ class SingletonTests(unittest.TestCase): self.assertIsInstance(instance2, Example) def test_call_with_init_positional_args(self): - """Test getting of instances with init positional args. - - New simplified syntax. - """ - provider = providers.Singleton(Example, 'i1', 'i2') + """Test getting of instances with init positional args.""" + provider = providers.Singleton(Example) \ + .args('i1', 'i2') instance1 = provider() instance2 = provider() @@ -359,11 +333,9 @@ class SingletonTests(unittest.TestCase): self.assertIsInstance(instance2, Example) def test_call_with_init_keyword_args(self): - """Test getting of instances with init keyword args. - - New simplified syntax. - """ - provider = providers.Singleton(Example, init_arg1='i1', init_arg2='i2') + """Test getting of instances with init keyword args.""" + provider = providers.Singleton(Example) \ + .kwargs(init_arg1='i1', init_arg2='i2') instance1 = provider() instance2 = provider() @@ -379,33 +351,10 @@ class SingletonTests(unittest.TestCase): self.assertIsInstance(instance2, Example) def test_call_with_init_positional_and_keyword_args(self): - """Test getting of instances with init positional and keyword args. - - Simplified syntax of positional and keyword arg injections. - """ - provider = providers.Singleton(Example, 'i1', init_arg2='i2') - - instance1 = provider() - instance2 = provider() - - self.assertEqual(instance1.init_arg1, 'i1') - self.assertEqual(instance1.init_arg2, 'i2') - - self.assertEqual(instance2.init_arg1, 'i1') - self.assertEqual(instance2.init_arg2, 'i2') - - self.assertIs(instance1, instance2) - self.assertIsInstance(instance1, Example) - self.assertIsInstance(instance2, Example) - - def test_call_with_init_positional_and_keyword_args_extended_syntax(self): - """Test getting of instances with init positional and keyword args. - - Extended syntax of positional and keyword arg injections. - """ - provider = providers.Singleton(Example, - injections.Arg('i1'), - injections.KwArg('init_arg2', 'i2')) + """Test getting of instances with init positional and keyword args.""" + provider = providers.Singleton(Example) \ + .args('i1') \ + .kwargs(init_arg2='i2') instance1 = provider() instance2 = provider() @@ -422,11 +371,8 @@ class SingletonTests(unittest.TestCase): def test_call_with_attributes(self): """Test getting of instances with attribute injections.""" - provider = providers.Singleton(Example, - injections.Attribute('attribute1', - 'a1'), - injections.Attribute('attribute2', - 'a2')) + provider = providers.Singleton(Example) \ + .attributes(attribute1='a1', attribute2='a2') instance1 = provider() instance2 = provider() @@ -444,6 +390,7 @@ class SingletonTests(unittest.TestCase): def test_call_with_context_args(self): """Test getting of instances with context args.""" provider = providers.Singleton(Example) + instance = provider(11, 22) self.assertEqual(instance.init_arg1, 11) @@ -451,8 +398,8 @@ class SingletonTests(unittest.TestCase): def test_call_with_context_kwargs(self): """Test getting of instances with context kwargs.""" - provider = providers.Singleton(Example, - injections.KwArg('init_arg1', 1)) + provider = providers.Singleton(Example) \ + .kwargs(init_arg1=1) instance1 = provider(init_arg2=22) self.assertEqual(instance1.init_arg1, 1) @@ -465,7 +412,8 @@ class SingletonTests(unittest.TestCase): def test_call_with_context_args_and_kwargs(self): """Test getting of instances with context args and kwargs.""" - provider = providers.Singleton(Example, 11) + provider = providers.Singleton(Example) \ + .args(11) instance = provider(22, init_arg3=33, init_arg4=44) self.assertEqual(instance.init_arg1, 11) @@ -489,35 +437,13 @@ class SingletonTests(unittest.TestCase): self.assertIsInstance(instance1, object) self.assertIsInstance(instance2, object) - def test_provides_attr(self): - """Test provides attribute.""" - provider = providers.Singleton(Example) - self.assertIs(provider.provides, Example) - - def test_args_attr(self): - """Test args attribute.""" - provider = providers.Singleton(Example, 1, 2) - self.assertEquals(len(provider.args), 2) - - def test_kwargs_attr(self): - """Test kwargs attribute.""" - provider = providers.Singleton(Example, init_arg1=1, init_arg2=2) - self.assertEquals(len(provider.kwargs), 2) - - def test_attributes_attr(self): - """Test attributes attribute.""" - provider = providers.Singleton(Example, - injections.Attribute('attribute1', 1), - injections.Attribute('attribute2', 2)) - self.assertEquals(len(provider.attributes), 2) - def test_injections(self): """Test getting a full list of injections using injections property.""" - provider = providers.Singleton(Example, - injections.Arg(1), - injections.KwArg('init_arg2', 2), - injections.Attribute('attribute1', 3), - injections.Attribute('attribute2', 4)) + provider = providers.Singleton(Example) \ + .args(1) \ + .kwargs(init_arg2=2) \ + .attributes(attribute1=3, attribute2=4) + self.assertEquals(len(provider.injections), 4) def test_reset(self): @@ -536,13 +462,10 @@ class SingletonTests(unittest.TestCase): def test_repr(self): """Test representation of provider.""" - provider = providers.Singleton(Example, - injections.KwArg( - 'init_arg1', - providers.Factory(dict)), - injections.KwArg( - 'init_arg2', - providers.Factory(list))) + provider = providers.Singleton(Example) \ + .kwargs(init_arg1=providers.Factory(dict), + init_arg2=providers.Factory(list)) + self.assertEqual(repr(provider), ''.format( diff --git a/tests/providers/test_static.py b/tests/providers/test_static.py index 02bc72ec..0c1ed66e 100644 --- a/tests/providers/test_static.py +++ b/tests/providers/test_static.py @@ -7,6 +7,8 @@ from dependency_injector import ( utils, ) +# TODO: move to test_base + class ObjectProviderTests(unittest.TestCase): """Object provider tests.""" diff --git a/tests/test_injections.py b/tests/test_injections.py index 9fca174f..c9385e18 100644 --- a/tests/test_injections.py +++ b/tests/test_injections.py @@ -19,12 +19,12 @@ class InjectionTests(unittest.TestCase): def test_value_with_scalar_injectable(self): """Test Injection value property with scalar value.""" injection = injections.Injection('some_value') - self.assertEqual(injection.value, 'some_value') + self.assertEqual(injection.get_value(), 'some_value') def test_value_with_provider_injectable(self): """Test Injection value property with provider.""" injection = injections.Injection(providers.Factory(object)) - self.assertIsInstance(injection.value, object) + self.assertIsInstance(injection.get_value(), object) def test_value_with_catalog_bundle_injectable(self): """Test Injection value property with catalog bundle.""" @@ -35,7 +35,7 @@ class InjectionTests(unittest.TestCase): injection = injections.Injection( TestCatalog.Bundle(TestCatalog.provider)) - self.assertIsInstance(injection.value, TestCatalog.Bundle) + self.assertIsInstance(injection.get_value(), TestCatalog.Bundle) def test_repr(self): """Test Injection representation."""