diff --git a/docs/images/providers/callable.png b/docs/images/providers/callable.png deleted file mode 100644 index f34b2a6a..00000000 Binary files a/docs/images/providers/callable.png and /dev/null differ diff --git a/docs/images/providers/dependency.png b/docs/images/providers/dependency.png deleted file mode 100644 index dfd2f149..00000000 Binary files a/docs/images/providers/dependency.png and /dev/null differ diff --git a/docs/main/changelog.rst b/docs/main/changelog.rst index ba3196e7..83459780 100644 --- a/docs/main/changelog.rst +++ b/docs/main/changelog.rst @@ -9,8 +9,9 @@ follows `Semantic versioning`_ Development version ------------------- -- Update ``Singleton`` provider documentation. -- Rework ``Singleton`` provider examples. +- Update documentation and rework examples for: ``Singleton``, ``Callable``, ``Coroutine``, + ``Object``, ``List``, ``Configuration``, ``Selector``, and ``Dependency`` providers. +- Fix mypy stub of the ``DeclarativeContainer`` to specify the ``__init__`` interface. 3.34.0 ------ diff --git a/docs/providers/callable.rst b/docs/providers/callable.rst index 9c82c87f..e0cb5089 100644 --- a/docs/providers/callable.rst +++ b/docs/providers/callable.rst @@ -1,69 +1,20 @@ -Callable providers ------------------- +Callable provider +----------------- + +.. meta:: + :keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Function,Method,Example + :description: Callable provider helps to make dependencies injection into functions. This page + demonstrates how to use a Callable provider. .. currentmodule:: dependency_injector.providers -:py:class:`Callable` provider calls wrapped callable on every call. +:py:class:`Callable` provider calls a function, a method or another callable. -Callable providers and injections -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -:py:class:`Callable` provider takes a various number of positional and keyword -arguments that are used as wrapped callable injections. Every time, when -:py:class:`Callable` provider is called, positional and keyword argument -injections would be passed as callable arguments. - -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 :ref:`callable_providers_delegation`. -+ All other injectable values are provided *"as is"*. -+ Positional context arguments will be appended after :py:class:`Callable` - positional injections. -+ Keyword context arguments have priority on :py:class:`Callable` keyword - injections and will be merged over them. - -Example that shows usage of :py:class:`Callable` with positional argument -injections: - -.. literalinclude:: ../../examples/providers/callable_args.py +.. literalinclude:: ../../examples/providers/callable.py :language: python + :lines: 3- -Next one example shows usage of :py:class:`Callable` with keyword argument -injections: - -.. image:: /images/providers/callable.png - :width: 100% - :align: center - -.. literalinclude:: ../../examples/providers/callable_kwargs.py - :language: python - -.. _callable_providers_delegation: - -Callable providers delegation -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -:py:class:`Callable` provider could be delegated to any other provider via -any kind of injection. - -Delegation of :py:class:`Callable` providers is the same as -:py:class:`Factory` providers delegation, please follow -:ref:`factory_providers_delegation` section for examples (with exception -of using :py:class:`DelegatedCallable` instead of -:py:class:`DelegatedFactory`). - -Abstract callable providers -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -:py:class:`AbstractCallable` provider is a :py:class:`Callable` provider that -must be explicitly overridden before calling. - -Behaviour of :py:class:`AbstractCallable` providers is the same as of -:py:class:`AbstractFactory`, please follow :ref:`abstract_factory_providers` -section for examples (with exception of using :py:class:`AbstractCallable` -provider instead of :py:class:`AbstractFactory`). +``Callable`` provider handles an injection of the dependencies the same way like a +:ref:`factory-provider`. .. disqus:: diff --git a/docs/providers/configuration.rst b/docs/providers/configuration.rst index dfa20539..43fd3ee3 100644 --- a/docs/providers/configuration.rst +++ b/docs/providers/configuration.rst @@ -1,5 +1,12 @@ -Configuration providers ------------------------ +Configuration provider +---------------------- + +.. meta:: + :keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Configuration,Injection, + Option,Ini,Json,Yaml,Dict,Environment Variable,Load,Read,Get + :description: Configuration provides configuration options to the other providers. This page + demonstrates how to use Configuration provider to inject the dependencies, load + a configuration from an ini or yaml file, dictionary or an environment variable. .. currentmodule:: dependency_injector.providers @@ -10,13 +17,13 @@ Configuration providers :emphasize-lines: 4,9-10 :lines: 4-14 -It implements "use first, define later" principle. +It implements the principle "use first, define later". -Loading from ``ini`` file -~~~~~~~~~~~~~~~~~~~~~~~~~ +Loading from an INI file +~~~~~~~~~~~~~~~~~~~~~~~~ -:py:class:`Configuration` provider can load configuration from ``ini`` file using -:py:meth:`Configuration.from_ini`: +``Configuration`` provider can load configuration from an ``ini`` file using the +:py:meth:`Configuration.from_ini` method: .. literalinclude:: ../../examples/providers/configuration/configuration_ini.py :language: python @@ -28,15 +35,15 @@ where ``examples/providers/configuration/config.ini`` is: .. literalinclude:: ../../examples/providers/configuration/config.ini :language: ini -:py:meth:`Configuration.from_ini` supports environment variables interpolation. Use -``${ENV_NAME}`` format in the configuration file to substitute value of environment +:py:meth:`Configuration.from_ini` method supports environment variables interpolation. Use +``${ENV_NAME}`` format in the configuration file to substitute value of the environment variable ``ENV_NAME``. -Loading from ``yaml`` file -~~~~~~~~~~~~~~~~~~~~~~~~~~ +Loading from a YAML file +~~~~~~~~~~~~~~~~~~~~~~~~ -:py:class:`Configuration` provider can load configuration from ``yaml`` file using -:py:meth:`Configuration.from_yaml`: +``Configuration`` provider can load configuration from a ``yaml`` file using the +:py:meth:`Configuration.from_yaml` method: .. literalinclude:: ../../examples/providers/configuration/configuration_yaml.py :language: python @@ -48,43 +55,51 @@ where ``examples/providers/configuration/config.yml`` is: .. literalinclude:: ../../examples/providers/configuration/config.yml :language: ini -:py:meth:`Configuration.from_yaml` supports environment variables interpolation. Use -``${ENV_NAME}`` format in the configuration file to substitute value of environment +:py:meth:`Configuration.from_yaml` method supports environment variables interpolation. Use +``${ENV_NAME}`` format in the configuration file to substitute value of the environment variable ``ENV_NAME``. .. note:: - Loading configuration from yaml requires ``PyYAML`` package. You can install - `Dependency Injector` with extras ``pip install dependency-injector[yaml]`` or install - ``PyYAML`` separately ``pip install pyyaml``. + Loading of a yaml configuration requires ``PyYAML`` package. -Loading from ``dict`` -~~~~~~~~~~~~~~~~~~~~~ + You can install the ``Dependency Injector`` with an extra dependency:: -:py:class:`Configuration` provider can load configuration from Python ``dict`` using -:py:meth:`Configuration.from_dict`: + pip install dependency-injector[yaml] + + or install ``PyYAML`` directly:: + + pip install pyyaml + + *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: .. literalinclude:: ../../examples/providers/configuration/configuration_dict.py :language: python :lines: 3-5,6- :emphasize-lines: 6-13 -Loading from environment variable -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Loading from an environment variable +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -:py:class:`Configuration` provider can load configuration from environment variable using -:py:meth:`Configuration.from_env`: +``Configuration`` provider can load configuration from an environment variable using the +:py:meth:`Configuration.from_env` method: .. literalinclude:: ../../examples/providers/configuration/configuration_env.py :language: python :lines: 5-7,13-21 :emphasize-lines: 6-8 -Loading from multiple sources -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Loading from the multiple sources +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -:py:class:`Configuration` provider can load configuration from multiple sources. Loaded -configuration is merged recursively over existing configuration. +``Configuration`` provider can load configuration from the multiple sources. Loaded +configuration is merged recursively over the existing configuration. .. literalinclude:: ../../examples/providers/configuration/configuration_multiple.py :language: python @@ -96,12 +111,12 @@ where ``examples/providers/configuration/config.local.yml`` is: .. literalinclude:: ../../examples/providers/configuration/config.local.yml :language: ini -Specifying value type -~~~~~~~~~~~~~~~~~~~~~ +Specifying the value type +~~~~~~~~~~~~~~~~~~~~~~~~~ You can specify the type of the injected configuration value explicitly. -This helps when you read the value from the ini file or the environment variable and need to +This helps when you read the value from an ini file or an environment variable and need to convert it into an ``int`` or a ``float``. .. literalinclude:: ../../examples/providers/configuration/configuration_type.py @@ -109,20 +124,20 @@ convert it into an ``int`` or a ``float``. :lines: 3- :emphasize-lines: 17 -:py:class:`Configuration` provider has next helper methods: +``Configuration`` provider has next helper methods: - ``.as_int()`` - ``.as_float()`` - ``.as_(callback, *args, **kwargs)`` -The last method ``.as_(callback, *args, **kwargs)`` helps to implement a other conversions. +The last method ``.as_(callback, *args, **kwargs)`` helps to implement other conversions. .. literalinclude:: ../../examples/providers/configuration/configuration_type_custom.py :language: python :lines: 3- :emphasize-lines: 16 -With the ``.as_(callback, *args, **kwargs)`` you can specify the function that will be called +With the ``.as_(callback, *args, **kwargs)`` you can specify a function that will be called before the injection. The value from the config will be passed as a first argument. The returned value will be injected. Parameters ``*args`` and ``**kwargs`` are handled as any other injections. diff --git a/docs/providers/coroutine.rst b/docs/providers/coroutine.rst index 303e420e..3db7846a 100644 --- a/docs/providers/coroutine.rst +++ b/docs/providers/coroutine.rst @@ -1,72 +1,27 @@ -Coroutine providers -------------------- +Coroutine provider +------------------ + +.. meta:: + :keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Coroutine,Asynchronous, + Asyncio,Example + :description: Coroutine provider creates a coroutine. This page demonstrates how to use a + Coroutine provider. .. currentmodule:: dependency_injector.providers -:py:class:`Coroutine` provider create wrapped coroutine on every call. +:py:class:`Coroutine` provider creates a coroutine. -:py:class:`Coroutine` provider is designed for making better integration with -``asyncio`` coroutines. In particular, :py:class:`Coroutine` provider returns -``True`` for ``asyncio.iscoroutinefunction()`` checks. - -.. note:: - - :py:class:`Coroutine` provider works only for Python 3.4+. - -Example of usage :py:class:`Coroutine` provider with ``async / await``-based -coroutine: - -.. literalinclude:: ../../examples/providers/coroutine_async_await.py +.. literalinclude:: ../../examples/providers/coroutine.py :language: python - -Coroutine providers and injections -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -:py:class:`Coroutine` provider takes a various number of positional and keyword -arguments that are used as wrapped coroutine injections. Every time, when -:py:class:`Coroutine` provider is called, positional and keyword argument -injections would be passed as coroutine arguments. - -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 :ref:`coroutine_providers_delegation`. -+ All other injectable values are provided *"as is"*. -+ Positional context arguments will be appended after :py:class:`Coroutine` - positional injections. -+ Keyword context arguments have priority on :py:class:`Coroutine` keyword - injections and will be merged over them. + :lines: 3- .. note:: + The example works on Python 3.7+. For earlier versions use ``loop.run_until_complete()``. - Examples of making injections could be found in API docs - - :py:class:`Coroutine`. +``Coroutine`` provider handles an injection of the dependencies the same way like a +:ref:`factory-provider`. -.. _coroutine_providers_delegation: - -Coroutine providers delegation -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -:py:class:`Coroutine` provider could be delegated to any other provider via -any kind of injection. - -Delegation of :py:class:`Coroutine` providers is the same as -:py:class:`Factory` providers delegation, please follow -:ref:`factory_providers_delegation` section for examples (with exception -of using :py:class:`DelegatedCoroutine` instead of -:py:class:`DelegatedFactory`). - -Abstract coroutine providers -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -:py:class:`AbstractCoroutine` provider is a :py:class:`Coroutine` provider that -must be explicitly overridden before calling. - -Behaviour of :py:class:`AbstractCoroutine` providers is the same as of -:py:class:`AbstractFactory`, please follow :ref:`abstract_factory_providers` -section for examples (with exception of using :py:class:`AbstractCoroutine` -provider instead of :py:class:`AbstractFactory`). +.. note:: + ``Coroutine`` provider returns ``True`` for ``asyncio.iscoroutinefunction()`` check. .. disqus:: diff --git a/docs/providers/dependency.rst b/docs/providers/dependency.rst index 4bd9bf2f..9ce3977b 100644 --- a/docs/providers/dependency.rst +++ b/docs/providers/dependency.rst @@ -1,43 +1,21 @@ -Dependency providers --------------------- +Dependency provider +------------------- .. currentmodule:: dependency_injector.providers -:py:class:`Dependency` provider can be useful for development of -self-sufficient libraries / modules / applications that have required external -dependencies. +:py:class:`Dependency` provider is a placeholder for the dependency of the specified type. -For example, you have created self-sufficient library / module / application, -that has dependency on *database connection*. +The first argument of the ``Dependency`` provider specifies a type of the dependency. It is +called ``instance_of``. ``Dependency`` provider controls the type of the returned object to be an +instance of the ``instance_of`` type. -Second step you want to do is to make this software component to be easy -reusable by wide amount of developers and to be easily integrated into many -applications. - -It may be good idea, to move all external dependencies (like -*database connection*) to the top level and make them to be injected on your -software component's initialization. It will make third party developers feel -themselves free about integration of your component in their applications, -because they would be able to find right place / right way for doing this -in their application's architectures. - -At the same time, you can be sure, that your external dependency will be -satisfied with appropriate instance. - -Example: - -.. note:: - - Class ``UsersService`` is a part of some library. ``UsersService`` has - dependency on database connection, which can be satisfied with any - DBAPI 2.0 database connection. Being a self-sufficient library, - ``UsersService`` doesn't hardcode any kind of database management logic. - Instead of this, ``UsersService`` has external dependency, that has to - be satisfied by client's code, out of library's scope. - -.. image:: /images/providers/dependency.png +The ``Dependency`` provider must be overridden before usage. It can be overridden by any type of +the provider. The only rule is that overriding provider must return an instance of ``instance_of`` +dependency type. .. literalinclude:: ../../examples/providers/dependency.py :language: python + :lines: 3- + :emphasize-lines: 26 .. disqus:: diff --git a/docs/providers/list.rst b/docs/providers/list.rst index 4b92eeca..9ad4ece4 100644 --- a/docs/providers/list.rst +++ b/docs/providers/list.rst @@ -1,5 +1,10 @@ -List providers --------------- +List provider +------------- + +.. meta:: + :keywords: Python,DI,Dependency injection,IoC,Inversion of Control,List,Injection + :description: List provider helps to inject a list of the dependencies. This page demonstrates + how to use a List provider. .. currentmodule:: dependency_injector.providers @@ -7,28 +12,12 @@ List providers .. literalinclude:: ../../examples/providers/list.py :language: python - :emphasize-lines: 6-9 - :lines: 6-8, 23-29 - -:py:class:`List` provider is needed for injecting a list of dependencies. It handles -positional argument injections the same way as :py:class:`Factory` provider: - -+ 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 explicitly. Check out - :ref:`factory_providers_delegation`. -+ All other values are injected *"as is"*. -+ Positional context arguments will be appended after :py:class:`List` positional injections. - -Full example: - -.. literalinclude:: ../../examples/providers/list.py - :language: python - :emphasize-lines: 23-26 :lines: 3- + :emphasize-lines: 19-22 + +``List`` provider handles positional arguments the same way as a :ref:`factory-provider`. .. note:: - - Keyword argument injections are not supported. + Keyword argument are not supported. .. disqus:: diff --git a/docs/providers/object.rst b/docs/providers/object.rst index 6599dd92..800b8116 100644 --- a/docs/providers/object.rst +++ b/docs/providers/object.rst @@ -1,14 +1,17 @@ -Object providers ----------------- +Object provider +--------------- + +.. meta:: + :keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Object + :description: Object provider provides an object "as is". This page demonstrates how to use an + Object provider. .. currentmodule:: dependency_injector.providers -:py:class:`Object` provider returns provided instance "as is". - -Example: +:py:class:`Object` provider returns an object "as is". .. literalinclude:: ../../examples/providers/object.py :language: python - + :lines: 3- .. disqus:: diff --git a/docs/providers/selector.rst b/docs/providers/selector.rst index 29867217..f7b0a9e7 100644 --- a/docs/providers/selector.rst +++ b/docs/providers/selector.rst @@ -3,29 +3,31 @@ Selector providers ------------------ +.. meta:: + :keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Configuration,Injection, + Selector,Polymorphism,Environment Variable,Flexibility + :description: Selector selects provider based on a configuration value or another callable. + This page demonstrates how to implement the polymorphism and increase the + flexibility of your application using the Selector provider. + .. currentmodule:: dependency_injector.providers -:py:class:`Selector` provider selects provider based on the configuration value or other callable. +:py:class:`Selector` provider selects provider based on a configuration value or another callable. .. literalinclude:: ../../examples/providers/selector.py :language: python - :emphasize-lines: 6-10 - :lines: 3-5,14-20 - -:py:class:`Selector` provider has a callable called ``selector`` and a dictionary of providers. - -The ``selector`` callable is provided as a first positional argument. It can be -:py:class:`Configuration` provider or any other callable. It has to return a string value. -This value is used as a key for selecting the provider from the dictionary of providers. - -The providers are provided as keyword arguments. Argument name is used as a key for -selecting the provider. - -Full example: - -.. literalinclude:: ../../examples/providers/selector.py - :language: python - :emphasize-lines: 14-18 :lines: 3- + :emphasize-lines: 14-18 + +The first argument of the ``Selector`` provider is called ``selector``. It can be an option of +a ``Configuration`` provider or any other callable. The ``selector`` callable has to return a +string value. This value is used as a key for selecting the provider. + +The providers are provided as keyword arguments. Argument name is used as a key for selecting the +provider. + +When a ``Selector`` provider is called, it gets a ``selector`` value and delegates the work to +the provider with a matching name. The ``selector`` callable works as a switch: when the returned +value is changed the ``Selector`` provider will delegate the work to another provider. .. disqus:: diff --git a/examples/providers/callable.py b/examples/providers/callable.py new file mode 100644 index 00000000..40e0d363 --- /dev/null +++ b/examples/providers/callable.py @@ -0,0 +1,18 @@ +"""`Callable` provider example.""" + +import passlib.hash + +from dependency_injector import providers + + +password_hasher = providers.Callable( + passlib.hash.sha256_crypt.hash, + salt_size=16, + rounds=10000, +) +password_verifier = providers.Callable(passlib.hash.sha256_crypt.verify) + + +if __name__ == '__main__': + hashed_password = password_hasher('super secret') + assert password_verifier('super secret', hashed_password) diff --git a/examples/providers/callable_args.py b/examples/providers/callable_args.py deleted file mode 100644 index a4292de4..00000000 --- a/examples/providers/callable_args.py +++ /dev/null @@ -1,16 +0,0 @@ -"""`Callable` providers with positional arguments example.""" - -import dependency_injector.providers as providers - - -# Creating even and odd filter providers: -even_filter = providers.Callable(filter, lambda x: x % 2 == 0) -odd_filter = providers.Callable(filter, lambda x: x % 2 != 0) - -# Creating even and odd ranges using range() and filter providers: -even_range = even_filter(range(1, 10)) -odd_range = odd_filter(range(1, 10)) - -# Making some asserts: -assert even_range == [2, 4, 6, 8] -assert odd_range == [1, 3, 5, 7, 9] diff --git a/examples/providers/callable_kwargs.py b/examples/providers/callable_kwargs.py deleted file mode 100644 index a6b89990..00000000 --- a/examples/providers/callable_kwargs.py +++ /dev/null @@ -1,16 +0,0 @@ -"""`Callable` providers with keyword arguments example.""" - -import passlib.hash - -import dependency_injector.providers as providers - - -# Password hasher and verifier providers: -password_hasher = providers.Callable(passlib.hash.sha256_crypt.encrypt, - salt_size=16, - rounds=10000) -password_verifier = providers.Callable(passlib.hash.sha256_crypt.verify) - -# Making some asserts: -hashed_password = password_hasher('super secret') -assert password_verifier('super secret', hashed_password) diff --git a/examples/providers/coroutine.py b/examples/providers/coroutine.py index 054173ca..98767295 100644 --- a/examples/providers/coroutine.py +++ b/examples/providers/coroutine.py @@ -1,26 +1,19 @@ -"""`Coroutine` providers example with @asyncio.coroutine decorator. - -Current example works only fot Python 3.4+. -""" +"""`Coroutine` providers example with async / await syntax.""" import asyncio -import dependency_injector.providers as providers +from dependency_injector import providers -@asyncio.coroutine -def coroutine_function(arg1, arg2): - """Sample coroutine function.""" - yield from asyncio.sleep(0.1) +async def coroutine(arg1, arg2): + await asyncio.sleep(0.1) return arg1, arg2 -coroutine_provider = providers.Coroutine(coroutine_function, arg1=1, arg2=2) +coroutine_provider = providers.Coroutine(coroutine, arg1=1, arg2=2) if __name__ == '__main__': - loop = asyncio.get_event_loop() - arg1, arg2 = loop.run_until_complete(coroutine_provider()) - + arg1, arg2 = asyncio.run(coroutine_provider()) assert (arg1, arg2) == (1, 2) assert asyncio.iscoroutinefunction(coroutine_provider) diff --git a/examples/providers/coroutine_async_await.py b/examples/providers/coroutine_async_await.py deleted file mode 100644 index cca13c26..00000000 --- a/examples/providers/coroutine_async_await.py +++ /dev/null @@ -1,25 +0,0 @@ -"""`Coroutine` providers example with async / await syntax. - -Current example works only fot Python 3.5+. -""" - -import asyncio - -import dependency_injector.providers as providers - - -async def coroutine_function(arg1, arg2): - """Sample coroutine function.""" - await asyncio.sleep(0.1) - return arg1, arg2 - - -coroutine_provider = providers.Coroutine(coroutine_function, arg1=1, arg2=2) - - -if __name__ == '__main__': - loop = asyncio.get_event_loop() - arg1, arg2 = loop.run_until_complete(coroutine_provider()) - - assert (arg1, arg2) == (1, 2) - assert asyncio.iscoroutinefunction(coroutine_provider) diff --git a/examples/providers/dependency.py b/examples/providers/dependency.py index a96aa58d..a1114cba 100644 --- a/examples/providers/dependency.py +++ b/examples/providers/dependency.py @@ -1,73 +1,50 @@ -"""`Dependency` providers example.""" +"""`Dependency` provider example.""" -import sqlite3 -import contextlib +import abc +import dataclasses -import dependency_injector.providers as providers +from dependency_injector import containers, providers, errors -class UsersService: - """Example class UsersService. - - UsersService has dependency on DBAPI 2.0 database connection. - """ - - def __init__(self, database): - """Initialize instance. - - :param database: Database connection. - :type database: sqlite3.dbapi2.Connection - """ - self.database = database - self.database.row_factory = sqlite3.dbapi2.Row - - def init_database(self): - """Initialize database, if it has not been initialized yet.""" - with contextlib.closing(self.database.cursor()) as cursor: - cursor.execute(""" - CREATE TABLE IF NOT EXISTS users( - id INTEGER PRIMARY KEY AUTOINCREMENT, - name VARCHAR(32) - ) - """) - - def create(self, name): - """Create user with provided name and return his id.""" - with contextlib.closing(self.database.cursor()) as cursor: - cursor.execute('INSERT INTO users(name) VALUES (?)', (name,)) - return cursor.lastrowid - - def get_by_id(self, id): - """Return user info by user id.""" - with contextlib.closing(self.database.cursor()) as cursor: - cursor.execute('SELECT id, name FROM users WHERE id=?', (id,)) - return cursor.fetchone() +class DbAdapter(metaclass=abc.ABCMeta): + ... -# Database and UsersService providers: -database = providers.Dependency(instance_of=sqlite3.dbapi2.Connection) -users_service_factory = providers.Factory(UsersService, - database=database) +class SqliteDbAdapter(DbAdapter): + ... -# Out of library's scope. -# -# Setting database provider: -database.provided_by(providers.Singleton(sqlite3.dbapi2.Connection, - database=':memory:', - timeout=30, - detect_types=True, - isolation_level='EXCLUSIVE')) -# Creating UsersService instance: -users_service = users_service_factory() +class PostgresDbAdapter(DbAdapter): + ... -# Initializing UsersService database: -users_service.init_database() -# Creating test user and retrieving full information about him: -test_user_id = users_service.create(name='test_user') -test_user = users_service.get_by_id(test_user_id) +@dataclasses.dataclass +class UserService: + database: DbAdapter -# Making some asserts: -assert test_user['id'] == 1 -assert test_user['name'] == 'test_user' + +class Container(containers.DeclarativeContainer): + + database = providers.Dependency(instance_of=DbAdapter) + + user_service = providers.Factory( + UserService, + database=database, + ) + + +if __name__ == '__main__': + container1 = Container(database=providers.Singleton(SqliteDbAdapter)) + container2 = Container(database=providers.Singleton(PostgresDbAdapter)) + + assert isinstance(container1.user_service().database, SqliteDbAdapter) + assert isinstance(container2.user_service().database, PostgresDbAdapter) + + container3 = Container(database=providers.Singleton(object)) + try: + container3.user_service() + except errors.Error as exception: + print(exception) + # The output is: + # is not an + # instance of diff --git a/examples/providers/list.py b/examples/providers/list.py index cbf33bd6..1d2eb474 100644 --- a/examples/providers/list.py +++ b/examples/providers/list.py @@ -8,15 +8,11 @@ from dependency_injector import providers @dataclasses.dataclass class Module: - """Example module.""" - name: str @dataclasses.dataclass class Dispatcher: - """Example dispatcher.""" - modules: List[Module] @@ -28,6 +24,7 @@ dispatcher_factory = providers.Factory( ), ) + if __name__ == '__main__': dispatcher = dispatcher_factory() @@ -35,11 +32,10 @@ if __name__ == '__main__': assert dispatcher.modules[0].name == 'm1' assert dispatcher.modules[1].name == 'm2' - # Call of dispatcher_factory() is equivalent to: - - dispatcher = Dispatcher( - modules=[ - Module(name='m1'), - Module(name='m2'), - ], - ) + # Call "dispatcher = dispatcher_factory()" is an equivalent for: + # dispatcher = Dispatcher( + # modules=[ + # Module(name='m1'), + # Module(name='m2'), + # ], + # ) diff --git a/examples/providers/object.py b/examples/providers/object.py index f3fa6693..7da9cb26 100644 --- a/examples/providers/object.py +++ b/examples/providers/object.py @@ -1,10 +1,10 @@ -"""Object providers example.""" +"""`Object` provider example.""" -import dependency_injector.providers as providers +from dependency_injector import providers -# Creating object provider: object_provider = providers.Object(1) -# Making some asserts: -assert object_provider() == 1 + +if __name__ == '__main__': + assert object_provider() == 1 diff --git a/examples/providers/selector.py b/examples/providers/selector.py index 9c3036bc..7c7c17cd 100644 --- a/examples/providers/selector.py +++ b/examples/providers/selector.py @@ -19,10 +19,11 @@ selector = providers.Selector( another=providers.Factory(SomeOtherClass), ) -config.override({'one_or_another': 'one'}) -instance_1 = selector() -assert isinstance(instance_1, SomeClass) +if __name__ == '__main__': + config.override({'one_or_another': 'one'}) + instance_1 = selector() + assert isinstance(instance_1, SomeClass) -config.override({'one_or_another': 'another'}) -instance_2 = selector() -assert isinstance(instance_2, SomeOtherClass) + config.override({'one_or_another': 'another'}) + instance_2 = selector() + assert isinstance(instance_2, SomeOtherClass) diff --git a/src/dependency_injector/containers.pyi b/src/dependency_injector/containers.pyi index 9c2798fb..62117b43 100644 --- a/src/dependency_injector/containers.pyi +++ b/src/dependency_injector/containers.pyi @@ -24,6 +24,7 @@ class DynamicContainer(Container): ... class DeclarativeContainer(Container): cls_providers: ClassVar[Dict[str, Provider]] inherited_providers: ClassVar[Dict[str, Provider]] + def __init__(self, **overriding_providers: Provider) -> None: ... def override(container: Container) -> _Callable[[Container], Container]: ...