Edit Dependency provider docs

This commit is contained in:
Roman Mogylatov 2020-09-01 21:16:54 -04:00
parent 9e6b52aad4
commit 6fac06fd91
4 changed files with 51 additions and 96 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

View File

@ -10,7 +10,7 @@ follows `Semantic versioning`_
Development version
-------------------
- Update documentation and rework examples for: ``Singleton``, ``Callable``, ``Coroutine``,
``Object``, ``List``, ``Configuration``, ``Selector`` providers.
``Object``, ``List``, ``Configuration``, ``Selector``, and ``Dependency`` providers.
- Fix mypy stub of the ``DeclarativeContainer`` to specify the ``__init__`` interface.
3.34.0

View File

@ -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::

View File

@ -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.
class DbAdapter(metaclass=abc.ABCMeta):
...
UsersService has dependency on DBAPI 2.0 database connection.
"""
def __init__(self, database):
"""Initialize instance.
class SqliteDbAdapter(DbAdapter):
...
: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)
class PostgresDbAdapter(DbAdapter):
...
@dataclasses.dataclass
class UserService:
database: DbAdapter
class Container(containers.DeclarativeContainer):
database = providers.Dependency(instance_of=DbAdapter)
user_service = providers.Factory(
UserService,
database=database,
)
""")
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()
# Database and UsersService providers:
database = providers.Dependency(instance_of=sqlite3.dbapi2.Connection)
users_service_factory = providers.Factory(UsersService,
database=database)
if __name__ == '__main__':
container1 = Container(database=providers.Singleton(SqliteDbAdapter))
container2 = Container(database=providers.Singleton(PostgresDbAdapter))
# 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'))
assert isinstance(container1.user_service().database, SqliteDbAdapter)
assert isinstance(container2.user_service().database, PostgresDbAdapter)
# Creating UsersService instance:
users_service = users_service_factory()
# 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)
# Making some asserts:
assert test_user['id'] == 1
assert test_user['name'] == 'test_user'
container3 = Container(database=providers.Singleton(object))
try:
container3.user_service()
except errors.Error as exception:
print(exception)
# The output is:
# <object object at 0x107ce5c40> is not an
# instance of <class '__main__.DbAdapter'>