Chained Factories pattern

This example demonstrate implementation of “Chained Factories” pattern. Main idea of this pattern is about wrapping Factory into another Factory that mix additional arguments or keyword arguments to a wrapped one.

Listing of data.py, demonstrates sample classes structure:

 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
"""Sample data classes."""


class SqlAlchemyDatabaseService:
    """Database service of an entity."""

    def __init__(self, session, base_class):
        """Initialize object."""
        self.session = session
        self.base_class = base_class


class TokensService:
    """Tokens service."""

    def __init__(self, id_generator, database):
        """Initialize object."""
        self.id_generator = id_generator
        self.database = database


class Token:
    """Token entity."""


class UsersService:
    """Users service."""

    def __init__(self, id_generator, database):
        """Initialize object."""
        self.id_generator = id_generator
        self.database = database


class User:
    """User entity."""


# Sample objects
session = object()
id_generator = object()

Listing of chained_factories.py, demonstrates “Chained Factories” pattern and provide some explanation:

 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
"""`Chained Factories` pattern."""

from dependency_injector import providers

from data import (
    id_generator,
    session,
    SqlAlchemyDatabaseService,
    TokensService,
    Token,
    UsersService,
    User,
)


# "Chained Factories" pattern

database = providers.Factory(SqlAlchemyDatabaseService, session=session)

tokens = providers.Factory(
    TokensService,
    id_generator=id_generator,
    database=providers.Factory(database, base_class=Token),
)

users = providers.Factory(
    UsersService,
    id_generator=id_generator,
    database=providers.Factory(database, base_class=User),
)

tokens_service = tokens()
assert tokens_service.database.base_class is Token

users_service = users()
assert users_service.database.base_class is User

# Explanation & some more examples

# 1. Keyword arguments of upper level factory are added to lower level factory
chained_dict_factory = providers.Factory(
    providers.Factory(dict, arg1=1),
    arg2=2,
)
print(chained_dict_factory())  # prints: {'arg1': 1, 'arg2': 2}

# 2. Keyword arguments of upper level factory have priority
chained_dict_factory = providers.Factory(
    providers.Factory(dict, arg1=1),
    arg1=2,
)
print(chained_dict_factory())  # prints: {'arg1': 2}

# 3. Keyword arguments provided from context have most priority
chained_dict_factory = providers.Factory(
    providers.Factory(dict, arg1=1),
    arg1=2,
)
print(chained_dict_factory(arg1=3))  # prints: {'arg1': 3}