External dependency providersΒΆ

ExternalDependency provider can be useful for development of self-sufficient libraries / modules / applications that have required external dependencies.

For example, you have created self-sufficient library / module / application, that has dependency on database connection.

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 yours 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 cleint’s code, out of library’s scope.

../_images/external_dependency.png
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
"""`ExternalDependency` providers example."""

import sqlite3
import contextlib

import dependency_injector.providers as providers


class UsersService(object):
    """Example class UsersService.

    UsersService has dependency on DBAPI 2.0 database connection.
    """

    def __init__(self, database):
        """Initializer.

        :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()


# Database and UsersService providers:
database = providers.ExternalDependency(instance_of=sqlite3.dbapi2.Connection)
users_service_factory = providers.Factory(UsersService,
                                          database=database)

# 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()

# 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'