diff --git a/.travis.yml b/.travis.yml index 42d594fc..51fc4f0c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ sudo: false install: - pip install tox - - if [[ $TRAVIS_PYTHON_VERSION == 3.3 ]]; then pip install virtualenv==15.2.0; fi script: - tox language: @@ -20,18 +19,18 @@ matrix: env: TOXENV=flake8 - python: 2.7 env: TOXENV=pydocstyle - - python: 2.6 - env: TOXENV=py26 - python: 2.7 env: TOXENV=py27 - - python: 3.3 - env: TOXENV=py33 - python: 3.4 env: TOXENV=py34 - python: 3.5 env: TOXENV=py35 - python: 3.6 env: TOXENV=py36 + - python: 3.7 + env: TOXENV=py37 + sudo: required + dist: xenial - python: pypy env: TOXENV=pypy notifications: diff --git a/docs/examples/chained_factories.rst b/docs/examples/chained_factories.rst new file mode 100644 index 00000000..9233af7c --- /dev/null +++ b/docs/examples/chained_factories.rst @@ -0,0 +1,22 @@ +Chained Factories pattern +========================= + +This example demonstrate implementation of "Chained Factories" pattern. +Main idea of this pattern is about wrapping `Factory` into other `Factory` +that mix additional arguments or keyword arguments to a wrapped one. + +Listing of ``data.py``, demonstrates sample classes structure: + +.. literalinclude:: ../../examples/miniapps/factory_patterns/data.py + :language: python + :linenos: + +Listing of ``chained_factories.py``, demonstrates "Chained Factories" +pattern and provide some explanation: + +.. literalinclude:: ../../examples/miniapps/factory_patterns/chained_factories.py + :language: python + :linenos: + + +.. disqus:: diff --git a/docs/examples/factory_of_factories.rst b/docs/examples/factory_of_factories.rst new file mode 100644 index 00000000..adabd042 --- /dev/null +++ b/docs/examples/factory_of_factories.rst @@ -0,0 +1,22 @@ +Factory of Factories pattern +============================ + +This example demonstrate implementation of "Factory of Factories" pattern. +Main idea of this pattern is about creation of a :py:class:`Factory` that +creates another :py:class:`Factory` and mix additional arguments to it. + +Listing of ``data.py``, demonstrates sample classes structure: + +.. literalinclude:: ../../examples/miniapps/factory_patterns/data.py + :language: python + :linenos: + +Listing of ``factory_of_factories.py``, demonstrates "Chained Factories" +pattern and provide some explanation: + +.. literalinclude:: ../../examples/miniapps/factory_patterns/factory_of_factories.py + :language: python + :linenos: + + +.. disqus:: diff --git a/docs/examples/index.rst b/docs/examples/index.rst index 977a35a2..c61c4009 100644 --- a/docs/examples/index.rst +++ b/docs/examples/index.rst @@ -21,3 +21,5 @@ and powered by *Dependency Injector* framework. bundles_miniapp use_cases_miniapp password_hashing_miniapp + chained_factories + factory_of_factories diff --git a/docs/main/changelog.rst b/docs/main/changelog.rst index 16d3679b..f63ebaa2 100644 --- a/docs/main/changelog.rst +++ b/docs/main/changelog.rst @@ -7,6 +7,14 @@ that were made in every particular version. From version 0.7.6 *Dependency Injector* framework strictly follows `Semantic versioning`_ +3.13.0 +------ +- Add Python 3.7 support. +- Drop Python 3.3 support. +- Drop Python 2.6 support. +- Add example of "Chained Factories" pattern. +- Add example of "Factory of Factories" pattern. + 3.12.4 ------ - Fix bug `#200 `_. diff --git a/examples/miniapps/factory_patterns/chained_factories.py b/examples/miniapps/factory_patterns/chained_factories.py new file mode 100644 index 00000000..993d1fa2 --- /dev/null +++ b/examples/miniapps/factory_patterns/chained_factories.py @@ -0,0 +1,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} diff --git a/examples/miniapps/factory_patterns/data.py b/examples/miniapps/factory_patterns/data.py new file mode 100644 index 00000000..80fdd20f --- /dev/null +++ b/examples/miniapps/factory_patterns/data.py @@ -0,0 +1,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() diff --git a/examples/miniapps/factory_patterns/factory_of_factories.py b/examples/miniapps/factory_patterns/factory_of_factories.py new file mode 100644 index 00000000..7a6a5fc1 --- /dev/null +++ b/examples/miniapps/factory_patterns/factory_of_factories.py @@ -0,0 +1,69 @@ +"""`Factory of Factories` pattern.""" + +from dependency_injector import providers + +from data import ( + id_generator, + session, + SqlAlchemyDatabaseService, + TokensService, + Token, + UsersService, + User, +) + + +# "Factory of Factories" pattern + +database_factory = providers.Factory( + providers.Factory, + SqlAlchemyDatabaseService, + session=session, +) + +tokens = providers.Factory( + TokensService, + id_generator=id_generator, + database=database_factory(base_class=Token), +) + +users = providers.Factory( + UsersService, + id_generator=id_generator, + database=database_factory(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 +factory_of_dict_factories = providers.Factory( + providers.Factory, + dict, + arg1=1, +) +dict_factory = factory_of_dict_factories(arg2=2) +print(dict_factory()) # prints: {'arg1': 1, 'arg2': 2} + +# 2. Keyword arguments of upper level factory have priority +factory_of_dict_factories = providers.Factory( + providers.Factory, + dict, + arg1=1, +) +dict_factory = factory_of_dict_factories(arg1=2) +print(dict_factory()) # prints: {'arg1': 2} + +# 3. Keyword arguments provided from context have most priority +factory_of_dict_factories = providers.Factory( + providers.Factory, + dict, + arg1=1, +) +dict_factory = factory_of_dict_factories(arg1=2) +print(dict_factory(arg1=3)) # prints: {'arg1': 3} diff --git a/setup.py b/setup.py index 768eae5c..47eb0b78 100644 --- a/setup.py +++ b/setup.py @@ -78,13 +78,12 @@ setup(name='dependency-injector', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', 'Topic :: Software Development', diff --git a/src/dependency_injector/__init__.py b/src/dependency_injector/__init__.py index 80683df0..546bedd9 100644 --- a/src/dependency_injector/__init__.py +++ b/src/dependency_injector/__init__.py @@ -1,6 +1,6 @@ """Dependency injector top-level package.""" -__version__ = '3.12.4' +__version__ = '3.13.0' """Version number that follows semantic versioning. :type: str