Compare commits

..

No commits in common. "master" and "3.16.0" have entirely different histories.

741 changed files with 91129 additions and 41820 deletions

7
.coveragerc Normal file
View File

@ -0,0 +1,7 @@
[run]
source = src/dependency_injector
omit = tests/unit
plugins = Cython.Coverage
[html]
directory=reports/unittests/

View File

@ -1,29 +0,0 @@
---
description: Code in Python and Cython
globs:
alwaysApply: false
---
- Follow PEP 8 rules
- When you write imports, split system, 3rd-party, and local imports with a new line
- Have two empty lines between the import block and the rest of the code
- Have an empty line (\n) at the end of every file
- If a file is supposed to be run, always add ``if __name__ == 'main'``
- Always follow a consistent pattern of using double or single quotes
- When there is a class without a docblock, leave one blank line before its members, e.g.:
```python
class Container(containers.DeclarativeContainer):
service = providers.Factory(Service)
```
- Avoid shortcuts in names unless absolutely necessary, exceptions:
```
arg
args
kwarg
kwargs
obj
cls
```
- Avoid inline comments unless absolutely necessary

View File

@ -1,7 +0,0 @@
---
description: Build and run tests
globs:
alwaysApply: false
---
- Use Makefile commands to build, test, lint and other similar operations when they are available.
- Activate virtualenv before running any commands by ``. venv/bin/actvate``

View File

@ -1,8 +0,0 @@
---
description: Run examples
globs:
alwaysApply: false
---
- When you run an example from the ``examples/`` folder, switch to the example folder and run it from there.
- If there are instructions on running the examples or its tests in readme, follow them
- Activate virtualenv before running any commands by ``. venv/bin/actvate``

View File

@ -1,12 +0,0 @@
version = 1
test_patterns = ["tests/**/test_*.py"]
exclude_patterns = ["docs/**"]
[[analyzers]]
name = "python"
enabled = true
[analyzers.meta]
runtime_version = "3.x.x"

View File

@ -1,9 +0,0 @@
root = true
[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.{py,pyi,pxd,pyx}]
ij_visual_guides = 80,88

1
.github/FUNDING.yml vendored
View File

@ -1 +0,0 @@
github: rmk135

View File

@ -1,131 +0,0 @@
name: Publishing
on:
workflow_dispatch:
push:
tags:
- '*'
jobs:
tests:
name: Run tests
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: 3.13
- run: pip install tox
- run: tox
env:
TOXENV: 3.13
linters:
name: Run linters
runs-on: ubuntu-24.04
strategy:
matrix:
toxenv: [flake8, pydocstyle, mypy, pylint]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: 3.13
- run: pip install tox
- run: tox
env:
TOXENV: ${{ matrix.toxenv }}
build-sdist:
name: Build source tarball
needs: [tests, linters]
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: 3.13
- run: |
python -m pip install --upgrade build
python -m build --sdist
- uses: actions/upload-artifact@v4
with:
name: cibw-sdist
path: ./dist/*
build-wheels:
name: Build wheels
needs: [tests, linters]
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-24.04, ubuntu-24.04-arm, windows-2022, macos-14]
env:
CIBW_ENABLE: pypy
CIBW_ENVIRONMENT: >-
PIP_CONFIG_SETTINGS="build_ext=-j4"
DEPENDENCY_INJECTOR_LIMITED_API="1"
CFLAGS="-g0"
steps:
- uses: actions/checkout@v3
- name: Build wheels
uses: pypa/cibuildwheel@v3.0.0
- uses: actions/upload-artifact@v4
with:
name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
path: ./wheelhouse/*.whl
test-publish:
name: Upload release to TestPyPI
needs: [build-sdist, build-wheels]
runs-on: ubuntu-latest
environment: test-pypi
permissions:
id-token: write
steps:
- uses: actions/download-artifact@v4
with:
pattern: cibw-*
path: dist
merge-multiple: true
- uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
publish:
name: Upload release to PyPI
needs: [build-sdist, build-wheels, test-publish]
runs-on: ubuntu-latest
environment: pypi
permissions:
id-token: write
steps:
- uses: actions/download-artifact@v4
with:
pattern: cibw-*
path: dist
merge-multiple: true
- uses: pypa/gh-action-pypi-publish@release/v1
publish-docs:
name: Publish docs
needs: [publish]
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: 3.13
- run: pip install awscli
- run: pip install -r requirements-doc.txt
- run: pip install -e .
- run: (cd docs && make clean html)
- run: |
aws s3 sync docs/_build/html s3://python-dependency-injector-docs --delete
aws cloudfront create-invalidation --distribution-id ${{ secrets.AWS_CLOUDFRONT_DISTRIBUTION_ID }} --path "/*" > /dev/null
echo "Cache invalidation triggered"
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }}

View File

@ -1,67 +0,0 @@
name: Tests and linters
on: [push, pull_request, workflow_dispatch]
jobs:
test-on-different-versions:
name: Run tests
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- run: pip install tox
- run: tox
env:
DEPENDENCY_INJECTOR_LIMITED_API: 1
TOXENV: ${{ matrix.python-version }}
test-different-pydantic-versions:
name: Run tests with different pydantic versions
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: "3.12"
- run: pip install tox
- run: tox -e pydantic-v1,pydantic-v2
test-coverage:
name: Run tests with coverage
runs-on: ubuntu-latest
env:
DEPENDENCY_INJECTOR_DEBUG_MODE: 1
PIP_VERBOSE: 1
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: 3.12
- run: pip install tox
- run: tox -vv
env:
TOXENV: coveralls
linters:
name: Run linters
runs-on: ubuntu-latest
strategy:
matrix:
toxenv: [flake8, pydocstyle, mypy, pylint]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: 3.13
- run: pip install tox
- run: tox
env:
TOXENV: ${{ matrix.toxenv }}

21
.gitignore vendored
View File

@ -15,7 +15,6 @@ lib64/
parts/
sdist/
var/
wheelhouse/
*.egg-info/
.installed.cfg
*.egg
@ -37,7 +36,6 @@ reports/
.cache
nosetests.xml
coverage.xml
.hypothesis/
# Translations
*.mo
@ -56,7 +54,7 @@ target/
.idea/
# Virtualenv
venv*/
venv/
# SQLite
*.db
@ -64,13 +62,10 @@ venv*/
# Vim Rope
.ropeproject/
# Cython artifacts
src/**/*.c
src/**/*.h
src/**/*.so
src/**/*.html
# Workspace for samples
.workspace/
.vscode/
# C extensions
src/dependency_injector/*.h
src/dependency_injector/*.so
src/dependency_injector/containers/*.h
src/dependency_injector/containers/*.so
src/dependency_injector/providers/*.h
src/dependency_injector/providers/*.so

49
.pylintrc Normal file
View File

@ -0,0 +1,49 @@
[MASTER]
# Add <file or directory> to the black list. It should be a base name, not a
# path. You may set this option multiple times.
ignore=utils,tests
[MESSAGES CONTROL]
# Disable the message(s) with the given id(s).
# disable-msg=
[SIMILARITIES]
# Minimum lines number of a similarity.
min-similarity-lines=5
[TYPECHECK]
ignore-mixin-members=yes
# ignored-classes=
zope=no
# generated-members=providedBy,implementedBy,rawDataReceived
[DESIGN]
# Maximum number of arguments for function / method
max-args=10
# Maximum number of locals for function / method body
max-locals=20
# Maximum number of return / yield for function / method body
max-returns=10
# Maximum number of branch for function / method body
max-branchs=10
# Maximum number of statements in function / method body
max-statements=60
# Maximum number of parents for a class (see R0901).
max-parents=10
# Maximum number of attributes for a class (see R0902).
max-attributes=30
# Minimum number of public methods for a class (see R0903).
min-public-methods=0
# Maximum number of public methods for a class (see R0904).
max-public-methods=30

43
.travis.yml Normal file
View File

@ -0,0 +1,43 @@
sudo: false
install:
- pip install tox
script:
- tox
language:
- python
matrix:
include:
- python: 3.6
env: TOXENV=coveralls DEPENDENCY_INJECTOR_DEBUG_MODE=1
install:
- pip install tox
- pip install cython
- make cythonize
- python: 3.6
env: TOXENV=pylint
- python: 3.6
env: TOXENV=flake8
- python: 3.6
env: TOXENV=pydocstyle
- python: 2.7
env: TOXENV=py27
- 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: 3.8
env: TOXENV=py38
sudo: required
dist: xenial
- python: pypy
env: TOXENV=pypy
- python: pypy3
env: TOXENV=pypy3
notifications:
slack: ets-labs:g9OU0r5PXjA5ueeoQw01dVvV

View File

@ -11,14 +11,3 @@ Dependency Injector Contributors
+ Dmitry Kuzmin (xotonic)
+ supakeen (supakeen)
+ Bruno P. Kinoshita (kinow)
+ RobinsonMa (RobinsonMa)
+ Rüdiger Busche (JarnoRFB)
+ Dmitry Rassoshenko (rda-dev)
+ Fotis Koutoupas (kootoopas)
+ Shubhendra Singh Chauhan (withshubh)
+ sonthonaxrk (sonthonaxrk)
+ Ngo Thanh Loi (Leonn) (loingo95)
+ Thiago Hiromi (thiromi)
+ Felipe Rubio (krouw)
+ Anton Petrov (anton-petrov)
+ ZipFile (ZipFile)

View File

@ -1,4 +1,4 @@
Copyright (c) 2024, Roman Mogylatov
Copyright (c) 2017, ETS Labs
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,7 +1,8 @@
recursive-include src/dependency_injector *.py* *.c py.typed
recursive-include src/dependency_injector *.py *.pyx *.pxd *.c
recursive-include tests *.py
include README.rst
include CONTRIBUTORS.rst
include LICENSE.rst
include requirements.txt
include setup.py
include tox.ini

View File

@ -1,6 +1,14 @@
VERSION := $(shell python setup.py --version)
export COVERAGE_RCFILE := pyproject.toml
CYTHON_SRC := $(shell find src/dependency_injector -name '*.pyx')
CYTHON_DIRECTIVES = -Xlanguage_level=2
ifdef DEPENDENCY_INJECTOR_DEBUG_MODE
CYTHON_DIRECTIVES += -Xprofile=True
CYTHON_DIRECTIVES += -Xlinetrace=True
endif
clean:
# Clean sources
@ -17,45 +25,49 @@ clean:
find examples -name '*.py[co]' -delete
find examples -name '__pycache__' -delete
build: clean
# Compile C extensions
python setup.py build_ext --inplace
cythonize:
# Compile Cython to C
cython -a $(CYTHON_DIRECTIVES) $(CYTHON_SRC)
# Move all Cython html reports
mkdir -p reports/cython/
find src -name '*.html' -exec mv {} reports/cython/ \;
docs-live:
build: clean cythonize
# Compile C extensions
python setup.py build_ext --inplace
docs-live: clean
sphinx-autobuild docs docs/_build/html
install: uninstall clean build
install: uninstall clean cythonize
pip install -ve .
uninstall:
- pip uninstall -y -q dependency-injector 2> /dev/null
test:
test-py2: build
# Unit tests with coverage report
coverage erase
coverage run -m pytest
coverage report
coverage html
coverage run --rcfile=./.coveragerc -m unittest2 discover -s tests/unit/ -p test_*_py2_py3.py
coverage report --rcfile=./.coveragerc
coverage html --rcfile=./.coveragerc
test-py3: build
# Unit tests with coverage report
coverage erase
coverage run --rcfile=./.coveragerc -m unittest2 discover -s tests/unit/ -p test_*py3.py
coverage report --rcfile=./.coveragerc
coverage html --rcfile=./.coveragerc
check:
flake8 src/dependency_injector/
flake8 examples/
# Static analysis
flake8 --max-complexity=10 src/dependency_injector/
flake8 --max-complexity=10 examples/
# Code style analysis
pydocstyle src/dependency_injector/
pydocstyle examples/
mypy tests/typing
test-publish: build
# Create distributions
python -m build --sdist
# Upload distributions to PyPI
twine upload --repository testpypi dist/dependency-injector-$(VERSION)*
publish:
publish: cythonize
# Merge release to master branch
git checkout master
git merge --no-ff release/$(VERSION) -m "Merge branch 'release/$(VERSION)' into master"
@ -63,3 +75,5 @@ publish:
# Create and upload tag
git tag -a $(VERSION) -m 'version $(VERSION)'
git push --tags
# Create and upload build
python setup.py sdist upload

View File

@ -1,230 +1,412 @@
.. figure:: https://raw.githubusercontent.com/wiki/ets-labs/python-dependency-injector/img/logo.svg
:target: https://github.com/ets-labs/python-dependency-injector
====================================================================
Dependency Injector - Dependency injection microframework for Python
====================================================================
|
*Dependency Injector* is a dependency injection microframework for Python.
It was designed to be a unified and developer-friendly tool that helps
implement a dependency injection design pattern in a formal, pretty, and
Pythonic way.
.. image:: https://img.shields.io/pypi/v/dependency_injector.svg
:target: https://pypi.org/project/dependency-injector/
:alt: Latest Version
.. image:: https://img.shields.io/pypi/l/dependency_injector.svg
:target: https://pypi.org/project/dependency-injector/
:alt: License
The key features of the *Dependency Injector* framework are:
.. image:: https://img.shields.io/pypi/pyversions/dependency_injector.svg
:target: https://pypi.org/project/dependency-injector/
:alt: Supported Python versions
.. image:: https://img.shields.io/pypi/implementation/dependency_injector.svg
:target: https://pypi.org/project/dependency-injector/
:alt: Supported Python implementations
+ Easy, smart, and pythonic style.
+ Obvious and clear structure.
+ Extensibility and flexibility.
+ High performance.
+ Memory efficiency.
+ Thread safety.
+ Documented.
+ Semantically versioned.
.. image:: https://pepy.tech/badge/dependency-injector
:target: https://pepy.tech/project/dependency-injector
:alt: Downloads
*Dependency Injector* containers and providers are implemented as C extension
types using Cython.
.. image:: https://pepy.tech/badge/dependency-injector/month
:target: https://pepy.tech/project/dependency-injector
:alt: Downloads
Status
------
.. image:: https://pepy.tech/badge/dependency-injector/week
:target: https://pepy.tech/project/dependency-injector
:alt: Downloads
.. image:: https://img.shields.io/pypi/wheel/dependency-injector.svg
:target: https://pypi.org/project/dependency-injector/
:alt: Wheel
.. image:: https://img.shields.io/github/actions/workflow/status/ets-labs/python-dependency-injector/tests-and-linters.yml?branch=master
:target: https://github.com/ets-labs/python-dependency-injector/actions
:alt: Build Status
.. image:: https://coveralls.io/repos/github/ets-labs/python-dependency-injector/badge.svg?branch=master
:target: https://coveralls.io/github/ets-labs/python-dependency-injector?branch=master
:alt: Coverage Status
What is ``Dependency Injector``?
================================
``Dependency Injector`` is a dependency injection framework for Python.
It helps implement the dependency injection principle.
Key features of the ``Dependency Injector``:
- **Providers**. Provides ``Factory``, ``Singleton``, ``Callable``, ``Coroutine``, ``Object``,
``List``, ``Dict``, ``Configuration``, ``Resource``, ``Dependency``, and ``Selector`` providers
that help assemble your objects.
See `Providers <https://python-dependency-injector.ets-labs.org/providers/index.html>`_.
- **Overriding**. Can override any provider by another provider on the fly. This helps in testing
and configuring dev/stage environment to replace API clients with stubs etc. See
`Provider overriding <https://python-dependency-injector.ets-labs.org/providers/overriding.html>`_.
- **Configuration**. Reads configuration from ``yaml``, ``ini``, and ``json`` files, ``pydantic`` settings,
environment variables, and dictionaries.
See `Configuration provider <https://python-dependency-injector.ets-labs.org/providers/configuration.html>`_.
- **Resources**. Helps with initialization and configuring of logging, event loop, thread
or process pool, etc. Can be used for per-function execution scope in tandem with wiring.
See `Resource provider <https://python-dependency-injector.ets-labs.org/providers/resource.html>`_.
- **Containers**. Provides declarative and dynamic containers.
See `Containers <https://python-dependency-injector.ets-labs.org/containers/index.html>`_.
- **Wiring**. Injects dependencies into functions and methods. Helps integrate with
other frameworks: Django, Flask, Aiohttp, Sanic, FastAPI, etc.
See `Wiring <https://python-dependency-injector.ets-labs.org/wiring.html>`_.
- **Asynchronous**. Supports asynchronous injections.
See `Asynchronous injections <https://python-dependency-injector.ets-labs.org/providers/async.html>`_.
- **Typing**. Provides typing stubs, ``mypy``-friendly.
See `Typing and mypy <https://python-dependency-injector.ets-labs.org/providers/typing_mypy.html>`_.
- **Performance**. Fast. Written in ``Cython``.
- **Maturity**. Mature and production-ready. Well-tested, documented, and supported.
.. code-block:: python
from dependency_injector import containers, providers
from dependency_injector.wiring import Provide, inject
class Container(containers.DeclarativeContainer):
config = providers.Configuration()
api_client = providers.Singleton(
ApiClient,
api_key=config.api_key,
timeout=config.timeout,
)
service = providers.Factory(
Service,
api_client=api_client,
)
@inject
def main(service: Service = Provide[Container.service]) -> None:
...
if __name__ == "__main__":
container = Container()
container.config.api_key.from_env("API_KEY", required=True)
container.config.timeout.from_env("TIMEOUT", as_=int, default=5)
container.wire(modules=[__name__])
main() # <-- dependency is injected automatically
with container.api_client.override(mock.Mock()):
main() # <-- overridden dependency is injected automatically
When you call the ``main()`` function the ``Service`` dependency is assembled and injected automatically.
When you do testing, you call the ``container.api_client.override()`` method to replace the real API
client with a mock. When you call ``main()``, the mock is injected.
You can override any provider with another provider.
It also helps you in a re-configuring project for different environments: replace an API client
with a stub on the dev or stage.
With the ``Dependency Injector``, object assembling is consolidated in a container. Dependency injections are defined explicitly.
This makes it easier to understand and change how an application works.
.. figure:: https://raw.githubusercontent.com/wiki/ets-labs/python-dependency-injector/img/di-readme.svg
:target: https://github.com/ets-labs/python-dependency-injector
Visit the docs to know more about the
`Dependency injection and inversion of control in Python <https://python-dependency-injector.ets-labs.org/introduction/di_in_python.html>`_.
+---------------------------------------+--------------------------------------------------------------------------------------------------------------------+
| *PyPi* | .. image:: https://img.shields.io/pypi/v/dependency_injector.svg |
| | :target: https://pypi.org/project/dependency-injector/ |
| | :alt: Latest Version |
| | .. image:: https://img.shields.io/pypi/l/dependency_injector.svg |
| | :target: https://pypi.org/project/dependency-injector/ |
| | :alt: License |
| | .. image:: https://pepy.tech/badge/dependency-injector |
| | :target: https://pepy.tech/project/dependency-injector |
| | :alt: Downloads |
+---------------------------------------+--------------------------------------------------------------------------------------------------------------------+
| *Python versions and implementations* | .. image:: https://img.shields.io/pypi/pyversions/dependency_injector.svg |
| | :target: https://pypi.org/project/dependency-injector/ |
| | :alt: Supported Python versions |
| | .. image:: https://img.shields.io/pypi/implementation/dependency_injector.svg |
| | :target: https://pypi.org/project/dependency-injector/ |
| | :alt: Supported Python implementations |
+---------------------------------------+--------------------------------------------------------------------------------------------------------------------+
| *Builds and tests coverage* | .. image:: https://travis-ci.org/ets-labs/python-dependency-injector.svg?branch=master |
| | :target: https://travis-ci.org/ets-labs/python-dependency-injector |
| | :alt: Build Status |
| | .. image:: http://readthedocs.org/projects/python-dependency-injector/badge/?version=latest |
| | :target: http://python-dependency-injector.ets-labs.org/ |
| | :alt: Docs Status |
| | .. image:: https://coveralls.io/repos/github/ets-labs/python-dependency-injector/badge.svg?branch=master |
| | :target: https://coveralls.io/github/ets-labs/python-dependency-injector?branch=master |
| | :alt: Coverage Status |
+---------------------------------------+--------------------------------------------------------------------------------------------------------------------+
| *Github* | .. image:: https://img.shields.io/github/watchers/ets-labs/python-dependency-injector.svg?style=social&label=Watch |
| | :target: https://github.com/ets-labs/python-dependency-injector |
| | :alt: Github watchers |
| | .. image:: https://img.shields.io/github/stars/ets-labs/python-dependency-injector.svg?style=social&label=Star |
| | :target: https://github.com/ets-labs/python-dependency-injector |
| | :alt: Github stargazers |
| | .. image:: https://img.shields.io/github/forks/ets-labs/python-dependency-injector.svg?style=social&label=Fork |
| | :target: https://github.com/ets-labs/python-dependency-injector |
| | :alt: Github forks |
+---------------------------------------+--------------------------------------------------------------------------------------------------------------------+
Installation
------------
The package is available on the `PyPi`_::
The *Dependency Injector* library is available on `PyPi`_::
pip install dependency-injector
Documentation
-------------
The documentation is available `here <https://python-dependency-injector.ets-labs.org/>`_.
The *Dependency Injector* documentation is hosted on ReadTheDocs:
Examples
--------
- `User's guide`_
- `API docs`_
Choose one of the following:
Dependency injection
--------------------
- `Application example (single container) <https://python-dependency-injector.ets-labs.org/examples/application-single-container.html>`_
- `Application example (multiple containers) <https://python-dependency-injector.ets-labs.org/examples/application-multiple-containers.html>`_
- `Decoupled packages example (multiple containers) <https://python-dependency-injector.ets-labs.org/examples/decoupled-packages.html>`_
- `Boto3 example <https://python-dependency-injector.ets-labs.org/examples/boto3.html>`_
- `Django example <https://python-dependency-injector.ets-labs.org/examples/django.html>`_
- `Flask example <https://python-dependency-injector.ets-labs.org/examples/flask.html>`_
- `Aiohttp example <https://python-dependency-injector.ets-labs.org/examples/aiohttp.html>`_
- `Sanic example <https://python-dependency-injector.ets-labs.org/examples/sanic.html>`_
- `FastAPI example <https://python-dependency-injector.ets-labs.org/examples/fastapi.html>`_
- `FastAPI + Redis example <https://python-dependency-injector.ets-labs.org/examples/fastapi-redis.html>`_
- `FastAPI + SQLAlchemy example <https://python-dependency-injector.ets-labs.org/examples/fastapi-sqlalchemy.html>`_
`Dependency injection`_ is a software design pattern that implements
`Inversion of control`_ to resolve dependencies. Formally, if object **A**
depends on object **B**, object **A** must not create or import object **B**
directly. Instead of this object **A** must provide a way to *inject*
object **B**. The responsibilities of objects creation and dependency
injection are delegated to external code - the *dependency injector*.
Tutorials
---------
Popular terminology of the dependency injection pattern:
Choose one of the following:
+ Object **A**, which depends on object **B**, is often called -
the *client*.
+ Object **B**, which is depended on, is often called - the *service*.
+ External code that is responsible for creation of objects and injection
of dependencies is often called - the *dependency injector*.
- `Flask web application tutorial <https://python-dependency-injector.ets-labs.org/tutorials/flask.html>`_
- `Aiohttp REST API tutorial <https://python-dependency-injector.ets-labs.org/tutorials/aiohttp.html>`_
- `Asyncio monitoring daemon tutorial <https://python-dependency-injector.ets-labs.org/tutorials/asyncio-daemon.html>`_
- `CLI application tutorial <https://python-dependency-injector.ets-labs.org/tutorials/cli.html>`_
There are several ways to inject a *service* into a *client*:
Concept
-------
+ by passing it as an ``__init__`` argument (constructor / initializer
injection)
+ by setting it as an attribute's value (attribute injection)
+ by passing it as a method's argument (method injection)
The framework stands on the `PEP20 (The Zen of Python) <https://www.python.org/dev/peps/pep-0020/>`_ principle:
The dependency injection pattern has few strict rules that should be followed:
.. code-block:: bash
+ The *client* delegates to the *dependency injector* the responsibility
of injecting its dependencies - the *service(s)*.
+ The *client* doesn't know how to create the *service*, it knows only
the interface of the *service*. The *service* doesn't know that it is used by
the *client*.
+ The *dependency injector* knows how to create the *client* and
the *service*. It also knows that the *client* depends on the *service*,
and knows how to inject the *service* into the *client*.
+ The *client* and the *service* know nothing about the *dependency injector*.
Explicit is better than implicit
The dependency injection pattern provides the following advantages:
You need to specify how to assemble and where to inject the dependencies explicitly.
+ Control of application structure.
+ Decreased coupling of application components.
+ Increased code reusability.
+ Increased testability.
+ Increased maintainability.
+ Reconfiguration of a system without rebuilding.
The power of the framework is in its simplicity.
``Dependency Injector`` is a simple tool for the powerful concept.
Example of dependency injection
-------------------------------
Frequently asked questions
--------------------------
Let's go through next example:
What is dependency injection?
- dependency injection is a principle that decreases coupling and increases cohesion
.. image:: https://raw.githubusercontent.com/wiki/ets-labs/python-dependency-injector/img/engines_cars/diagram.png
:width: 100%
:align: center
Why should I do the dependency injection?
- your code becomes more flexible, testable, and clear 😎
Listing of ``example.engines`` module:
How do I start applying the dependency injection?
- you start writing the code following the dependency injection principle
- you register all of your application components and their dependencies in the container
- when you need a component, you specify where to inject it or get it from the container
.. code-block:: python
What price do I pay and what do I get?
- you need to explicitly specify the dependencies
- it will be extra work in the beginning
- it will payoff as project grows
"""Dependency injection example, engines module."""
Have a question?
- Open a `Github Issue <https://github.com/ets-labs/python-dependency-injector/issues>`_
Found a bug?
- Open a `Github Issue <https://github.com/ets-labs/python-dependency-injector/issues>`_
class Engine:
"""Example engine base class.
Want to help?
- |star| Star the ``Dependency Injector`` on the `Github <https://github.com/ets-labs/python-dependency-injector/>`_
- |new| Start a new project with the ``Dependency Injector``
- |tell| Tell your friend about the ``Dependency Injector``
Engine is a heart of every car. Engine is a very common term and could be
implemented in very different ways.
"""
Want to contribute?
- |fork| Fork the project
- |pull| Open a pull request to the ``develop`` branch
class GasolineEngine(Engine):
"""Gasoline engine."""
class DieselEngine(Engine):
"""Diesel engine."""
class ElectricEngine(Engine):
"""Electric engine."""
Listing of ``example.cars`` module:
.. code-block:: python
"""Dependency injection example, cars module."""
class Car:
"""Example car."""
def __init__(self, engine):
"""Initializer."""
self._engine = engine # Engine is injected
The next example demonstrates the creation of several cars with different engines:
.. code-block:: python
"""Dependency injection example, Cars & Engines."""
import example.cars
import example.engines
if __name__ == '__main__':
gasoline_car = example.cars.Car(example.engines.GasolineEngine())
diesel_car = example.cars.Car(example.engines.DieselEngine())
electric_car = example.cars.Car(example.engines.ElectricEngine())
While the previous example demonstrates the advantages of dependency injection,
there is a disadvantage demonstrated as well - the creation of a car requires
additional code to specify its dependencies. However, this disadvantage
could be avoided by using a dependency injection framework for the creation of
an inversion of control container (IoC container).
Here's an example of the creation of several inversion of control containers
(IoC containers) using *Dependency Injector*:
.. code-block:: python
"""Dependency injection example, Cars & Engines IoC containers."""
import example.cars
import example.engines
import dependency_injector.containers as containers
import dependency_injector.providers as providers
class Engines(containers.DeclarativeContainer):
"""IoC container of engine providers."""
gasoline = providers.Factory(example.engines.GasolineEngine)
diesel = providers.Factory(example.engines.DieselEngine)
electric = providers.Factory(example.engines.ElectricEngine)
class Cars(containers.DeclarativeContainer):
"""IoC container of car providers."""
gasoline = providers.Factory(example.cars.Car,
engine=Engines.gasoline)
diesel = providers.Factory(example.cars.Car,
engine=Engines.diesel)
electric = providers.Factory(example.cars.Car,
engine=Engines.electric)
if __name__ == '__main__':
gasoline_car = Cars.gasoline()
diesel_car = Cars.diesel()
electric_car = Cars.electric()
Dependency Injector structure
-----------------------------
*Dependency Injector* is a microframework and has a simple structure.
There are two main entities: providers and containers.
.. image:: https://raw.githubusercontent.com/wiki/ets-labs/python-dependency-injector/img/internals.png
:width: 100%
:align: center
Providers
~~~~~~~~~
Providers describe strategies of accessing objects. They define how particular
objects are provided.
- **Provider** - base provider class.
- **Callable** - provider that calls a wrapped callable on every call. Supports
positional and keyword argument injections.
- **Factory** - provider that creates new instance of specified class on every
call. Supports positional and keyword argument injections, as well as
attribute injections.
- **Singleton** - provider that creates new instance of specified class on its
first call and returns the same instance on every next call. Supports
position and keyword argument injections, as well as attribute injections.
- **Object** - provider that returns provided instance "as is".
- **ExternalDependency** - provider that can be useful for development of
self-sufficient libraries, modules, and applications that require external
dependencies.
- **Configuration** - provider that helps with implementing late static binding
of configuration options - use first, define later.
Containers
~~~~~~~~~~
Containers are collections of providers. The main purpose of containers is to
group providers.
- **DeclarativeContainer** - is an inversion of control container that can be
defined in a declarative manner. It covers most of the cases where a list of
providers that is be included in a container is deterministic
(that means the container will not change its structure in runtime).
- **DynamicContainer** - is an inversion of control container with a dynamic
structure. It covers most of the cases where a list of providers that
would be included in container is non-deterministic and depends on the
application's flow or its configuration (container's structure could be
determined just after the application starts and might perform some initial
work, like parsing a list of container providers from a configuration).
Dependency Injector in action
-----------------------------
The brief example below is a simplified version of inversion of control
containers from a real-life application. The example demonstrates the usage
of *Dependency Injector* inversion of control container and providers for
specifying application components and their dependencies on each other in one
module. Besides other previously mentioned advantages, it shows a great
opportunity to control and manage application's structure in one place.
.. code-block:: python
"""Example of dependency injection in Python."""
import logging
import sqlite3
import boto3
from dependency_injector import containers, providers
from example import services, main
class IocContainer(containers.DeclarativeContainer):
"""Application IoC container."""
config = providers.Configuration('config')
logger = providers.Singleton(logging.Logger, name='example')
# Gateways
database_client = providers.Singleton(sqlite3.connect, config.database.dsn)
s3_client = providers.Singleton(
boto3.client, 's3',
aws_access_key_id=config.aws.access_key_id,
aws_secret_access_key=config.aws.secret_access_key,
)
# Services
users_service = providers.Factory(
services.UsersService,
db=database_client,
logger=logger,
)
auth_service = providers.Factory(
services.AuthService,
token_ttl=config.auth.token_ttl,
db=database_client,
logger=logger,
)
photos_service = providers.Factory(
services.PhotosService,
db=database_client,
s3=s3_client,
logger=logger,
)
# Misc
main = providers.Callable(
main.main,
users_service=users_service,
auth_service=auth_service,
photos_service=photos_service,
)
The next example demonstrates a run of the example application defined above:
.. code-block:: python
"""Run example of dependency injection in Python."""
import sys
import logging
from container import IocContainer
if __name__ == '__main__':
# Configure container:
container = IocContainer(
config={
'database': {
'dsn': ':memory:',
},
'aws': {
'access_key_id': 'KEY',
'secret_access_key': 'SECRET',
},
'auth': {
'token_ttl': 3600,
},
}
)
container.logger().addHandler(logging.StreamHandler(sys.stdout))
# Run application:
container.main(*sys.argv[1:])
You can find more *Dependency Injector* examples in the ``/examples`` directory
on our GitHub:
https://github.com/ets-labs/python-dependency-injector
Feedback & Support
------------------
Feel free to post questions, bugs, feature requests, proposals, etc. on
the *Dependency Injector* GitHub issues page:
https://github.com/ets-labs/python-dependency-injector/issues
Your feedback is quite important!
.. _Dependency injection: http://en.wikipedia.org/wiki/Dependency_injection
.. _Inversion of control: https://en.wikipedia.org/wiki/Inversion_of_control
.. _PyPi: https://pypi.org/project/dependency-injector/
.. |star| unicode:: U+2B50 U+FE0F .. star sign1
.. |new| unicode:: U+1F195 .. new sign
.. |tell| unicode:: U+1F4AC .. tell sign
.. |fork| unicode:: U+1F500 .. fork sign
.. |pull| unicode:: U+2B05 U+FE0F .. pull sign
.. _User's guide: http://python-dependency-injector.ets-labs.org/
.. _API docs: http://python-dependency-injector.ets-labs.org/api/

View File

@ -1,9 +0,0 @@
.no-border {
border: 0 !important;
box-shadow: none !important;
-webkit-box-shadow: none !important;
}
.no-border td {
border: 0px !important;
padding: 0px 10px 0px 0px !important;
}

10
docs/_static/disqus.js vendored Normal file
View File

@ -0,0 +1,10 @@
var disqus_shortname;
var disqus_identifier;
(function() {{
var disqus_thread = $("#disqus_thread");
disqus_shortname = disqus_thread.data('disqus-shortname');
disqus_identifier = disqus_thread.data('disqus-identifier');
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
}})();

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.8 KiB

View File

@ -0,0 +1,3 @@
.rst-content .highlight>pre, .rst-content .linenodiv>pre {
line-height: normal;
}

View File

@ -1 +0,0 @@
<iframe src="https://github.com/sponsors/rmk135/button" title="Sponsor Dependency Injector" height="32" width="114" style="border: 0; border-radius: 6px;"></iframe>

View File

@ -1,9 +0,0 @@
dependency_injector.ext.starlette
=================================
.. automodule:: dependency_injector.ext.starlette
:members:
:inherited-members:
:show-inheritance:
.. disqus::

View File

@ -1,9 +1,9 @@
dependency_injector.containers
==============================
``dependency_injector.containers``
----------------------------------
.. automodule:: dependency_injector.containers
:members:
:inherited-members:
:show-inheritance:
:special-members:
.. disqus::

View File

@ -1,5 +1,5 @@
dependency_injector.errors
==========================
``dependency_injector.errors``
------------------------------
.. automodule:: dependency_injector.errors
:members:

View File

@ -2,11 +2,9 @@ API Documentation
=================
.. toctree::
:maxdepth: 2
:maxdepth: 2
top-level
providers
containers
wiring
errors
asgi-lifespan
top_level
providers
containers
errors

View File

@ -1,5 +1,5 @@
dependency_injector.providers
=============================
``dependency_injector.providers``
---------------------------------
.. automodule:: dependency_injector.providers
:members:

View File

@ -1,5 +1,5 @@
dependency_injector
===================
``dependency_injector``
-----------------------
.. automodule:: dependency_injector
:members: __version__

View File

@ -1,7 +0,0 @@
dependency_injector.wiring
=============================
.. automodule:: dependency_injector.wiring
:members:
.. disqus::

View File

@ -15,54 +15,50 @@
import os
import re
import sys
import alabaster
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath(".."))
sys.path.insert(0, os.path.abspath('..'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = "1.0"
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named "sphinx.ext.*") or your custom
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
"alabaster",
"sphinx.ext.autodoc",
"sphinx_disqus.disqus",
]
extensions = ['sphinx.ext.autodoc',
'sphinxcontrib.disqus']
# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]
templates_path = ['_templates']
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
# source_suffix = [".rst", ".md"]
source_suffix = ".rst"
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = "utf-8-sig"
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = "index"
master_doc = 'index'
# General information about the project.
project = "Dependency Injector"
copyright = "2024, Roman Mogylatov"
author = "Roman Mogylatov"
project = u'Dependency Injector'
copyright = u'2017, ETS Labs'
author = u'ETS Labs'
# The version info for the project you"re documenting, acts as replacement for
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
# Getting version:
with open("../src/dependency_injector/__init__.py") as init_file:
version = re.search("__version__ = \"(.*?)\"", init_file.read()).group(1)
with open('../src/dependency_injector/__init__.py') as init_file:
version = re.search('__version__ = \'(.*?)\'', init_file.read()).group(1)
# The full version, including alpha/beta/rc tags.
release = version
@ -72,23 +68,23 @@ release = version
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = "en"
language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ""
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = "%B %d, %Y"
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ["_build"]
exclude_patterns = ['_build']
# The reST default role (used for this markup: `text`) to use for all
# documents.
#default_role = None
# If true, "()" will be appended to :func: etc. cross-reference text.
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
@ -100,7 +96,7 @@ exclude_patterns = ["_build"]
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = "sphinx"
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
@ -116,8 +112,7 @@ todo_include_todos = False
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
# html_theme = "sphinx_rtd_theme"
html_theme = "alabaster"
html_theme = 'sphinx_rtd_theme'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
@ -125,7 +120,7 @@ html_theme = "alabaster"
# html_context = {}
# Add any paths that contain custom themes here, relative to this directory.
html_theme_path = [alabaster.get_path()]
#html_theme_path = ['_themes']
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
@ -141,24 +136,21 @@ html_theme_path = [alabaster.get_path()]
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
html_favicon = "favicon.ico"
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ["_static"]
html_css_files = [
"custom.css",
]
html_static_path = ['_static']
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
#html_extra_path = []
# If not "", a "Last updated on:" timestamp is inserted at every page bottom,
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = "%b %d, %Y"
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
@ -192,50 +184,50 @@ html_css_files = [
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ""
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Language to be used for generating the HTML full-text search index.
# Sphinx supports the following languages:
# "da", "de", "en", "es", "fi", "fr", "hu", "it", "ja"
# "nl", "no", "pt", "ro", "ru", "sv", "tr"
#html_search_language = "en"
# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr'
#html_search_language = 'en'
# A dictionary with options for the search language support, empty by default.
# Now only "ja" uses this config value
#html_search_options = {"type": "default"}
# Now only 'ja' uses this config value
#html_search_options = {'type': 'default'}
# The name of a javascript file (relative to the configuration directory) that
# implements a search results scorer. If empty, the default will be used.
#html_search_scorer = "scorer.js"
#html_search_scorer = 'scorer.js'
# Output file base name for HTML help builder.
htmlhelp_basename = "dependency_injectordoc"
htmlhelp_basename = 'dependency_injectordoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ("letterpaper" or "a4paper").
#"papersize": "letterpaper",
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ("10pt", "11pt" or "12pt").
#"pointsize": "10pt",
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#"preamble": "",
#'preamble': '',
# Latex figure (float) alignment
#"figure_align": "htbp",
#'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, "dependency_injector.tex", u"Dependency Injector Documentation",
u"Roman Mogylatov", "manual"),
(master_doc, 'dependency_injector.tex', u'Dependency Injector Documentation',
u'ETS Labs', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
@ -264,7 +256,7 @@ latex_documents = [
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, "Dependency Injector", u"Dependency Injector Documentation",
(master_doc, 'Dependency Injector', u'Dependency Injector Documentation',
[author], 1)
]
@ -278,9 +270,9 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, "Dependency Injector", u"Dependency Injector Documentation",
author, "Dependency Injector", "Dependency injection microframework for Python",
"Miscellaneous"),
(master_doc, 'Dependency Injector', u'Dependency Injector Documentation',
author, 'Dependency Injector', 'Dependency injection microframework for Python',
'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
@ -289,25 +281,16 @@ texinfo_documents = [
# If false, no module index is generated.
#texinfo_domain_indices = True
# How to display URL addresses: "footnote", "no", or "inline".
#texinfo_show_urls = "footnote"
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
# If true, do not generate a @detailmenu in the "Top" node"s menu.
# If true, do not generate a @detailmenu in the "Top" node's menu.
#texinfo_no_detailmenu = False
autodoc_member_order = "bysource"
autodoc_member_order = 'bysource'
disqus_shortname = "python-dependency-injector"
disqus_shortname = 'python-dependency-injector'
html_theme_options = {
"github_user": "ets-labs",
"github_repo": "python-dependency-injector",
"github_type": "star",
"github_button": True,
"github_banner": True,
"logo": "logo.svg",
"description": "Dependency injection framework for Python by Roman Mogylatov",
"code_font_size": "10pt",
"analytics_id": "UA-67012059-1",
"donate_url": "https://github.com/sponsors/rmk135",
}
def setup(app):
app.add_stylesheet('sphinx_rtd_theme-hotfix.css')

View File

@ -1,18 +0,0 @@
.. _check-container-dependencies:
Check container dependencies
----------------------------
To check container dependencies use method ``.check_dependencies()``.
.. literalinclude:: ../../examples/containers/check_dependencies.py
:language: python
:lines: 3-
:emphasize-lines: 12
Method ``.check_dependencies()`` raises an error if container has any undefined dependencies.
If all dependencies are provided or have defaults, no error is raised.
See also: :ref:`dependency-provider`.
.. disqus::

View File

@ -1,23 +0,0 @@
Container copying
-----------------
You can create declarative container copies using ``@containers.copy()`` decorator.
.. literalinclude:: ../../examples/containers/declarative_copy_decorator1.py
:language: python
:lines: 3-
:emphasize-lines: 18-22
Decorator ``@containers.copy()`` copies providers from source container to destination container.
Destination container provider will replace source provider, if names match.
Decorator ``@containers.copy()`` helps you when you create derived declarative containers
from the base one. Base container often keeps default dependencies while derived containers define
overriding providers. Without ``@containers.copy()`` decorator, overridden providers are available
in the derived container, but base class dependencies continue to be bound to the base class providers.
.. literalinclude:: ../../examples/containers/declarative_copy_decorator2.py
:language: python
:lines: 11-
.. disqus::

View File

@ -1,72 +1,58 @@
Declarative container
---------------------
Declarative containers
----------------------
.. currentmodule:: dependency_injector.containers
:py:class:`DeclarativeContainer` is a class-based style of the providers definition.
:py:class:`DeclarativeContainer` is inversion of control container that
could be defined in declarative manner. It should cover most of the cases
when list of providers that would be included in container is deterministic
(container will not change its structure in runtime).
You create the declarative container subclass, put the providers as attributes and create the
container instance.
Declarative containers have to extend base declarative container class -
:py:class:`dependency_injector.containers.DeclarativeContainer`.
Declarative container's providers have to be defined like container's class
attributes. Every provider in container has name. This name should follow
``some_provider`` convention, that is standard naming convention for
attribute names in Python.
.. note::
Declarative containers have several features that could be useful
for some kind of operations on container's providers, please visit API
documentation for getting full list of features -
:py:class:`dependency_injector.containers.DeclarativeContainer`.
Here is an simple example of defining declarative container with several
factories:
.. image:: /images/containers/declarative.png
:width: 85%
:align: center
.. literalinclude:: ../../examples/containers/declarative.py
:language: python
:lines: 3-
:linenos:
The declarative container providers should only be used when you have the container instance.
Working with the providers of the container on the class level will influence all further
instances.
Example of declarative containers inheritance:
The declarative container can not have any methods or any other attributes then providers.
The container class provides next attributes:
- ``providers`` - the dictionary of all the container providers
- ``cls_providers`` - the dictionary of the container providers of the current container
- ``inherited_providers`` - the dictionary of all the inherited container providers
.. image:: /images/containers/declarative_inheritance.png
:width: 100%
:align: center
.. literalinclude:: ../../examples/containers/declarative_inheritance.py
:language: python
:lines: 3-
:linenos:
Injections in the declarative container are done the usual way:
Example of declarative containers's provider injections:
.. image:: /images/containers/declarative_injections.png
:width: 100%
:align: center
.. literalinclude:: ../../examples/containers/declarative_injections.py
:language: python
:lines: 3-
:linenos:
You can override container providers while creating a container instance:
.. literalinclude:: ../../examples/containers/declarative_override_providers.py
:language: python
:lines: 3-
:emphasize-lines: 13
Alternatively, you can call ``container.override_providers()`` method when the container instance
already exists:
.. code-block:: python
:emphasize-lines: 3
container = Container()
container.override_providers(foo=mock.Mock(Foo), bar=mock.Mock(Bar))
assert isinstance(container.foo(), mock.Mock)
assert isinstance(container.bar(), mock.Mock)
You can also use ``container.override_providers()`` with a context manager to reset
provided overriding after the context is closed:
.. code-block:: python
:emphasize-lines: 3
container = Container()
with container.override_providers(foo=mock.Mock(Foo), bar=mock.Mock(Bar)):
assert isinstance(container.foo(), mock.Mock)
assert isinstance(container.bar(), mock.Mock)
assert isinstance(container.foo(), Foo)
assert isinstance(container.bar(), Bar)
.. disqus::

View File

@ -1,25 +1,30 @@
Dynamic container
-----------------
Dynamic containers
------------------
.. currentmodule:: dependency_injector.containers
:py:class:`DynamicContainer` is a collection of the providers defined in the runtime.
:py:class:`DynamicContainer` is an inversion of control container with dynamic
structure. It should cover most of the cases when list of providers that
would be included in container is non-deterministic and depends on
application's flow or its configuration (container's structure could be
determined just after application will be started and will do some initial
work, like parsing list of container's providers from the configuration).
You create the dynamic container instance and put the providers as attributes.
While :py:class:`DeclarativeContainer` acts on class-level,
:py:class:`DynamicContainer` does the same on instance-level.
Here is an simple example of defining dynamic container with several factories:
.. literalinclude:: ../../examples/containers/dynamic.py
:language: python
:lines: 3-
:linenos:
The dynamic container is good for the case when your application structure depends on the
configuration file or some other source that you can reach only after application is already
running (database, api, etc).
In this example we use the configuration to fill in the dynamic container with the providers:
Next example demonstrates creation of dynamic container based on some
configuration:
.. literalinclude:: ../../examples/containers/dynamic_runtime_creation.py
:language: python
:lines: 3-
:linenos:
.. disqus::

View File

@ -1,20 +1,23 @@
.. _containers:
IoC Containers
==============
Containers
==========
Containers are collections of providers. Main purpose of containers is to group
providers.
Containers are collections of the providers.
There are, actually, several popular cases of containers usage:
There are several use cases how you can use containers:
+ Keeping all the providers in a single container (most common).
+ Grouping of the providers from the same architectural layer (for example,
+ Keeping all providers in a single container.
+ Grouping of providers from the same architectural layer (for example,
``Services``, ``Models`` and ``Forms`` containers).
+ Grouping of providers from the same functional groups (for example,
container ``Users``, that contains all functional parts of the ``users``
package).
container ``Users``, that contains all functional parts of ``Users``
component).
Containers module API docs - :py:mod:`dependency_injector.containers`.
Also, for both of these and some other cases, it might be useful to attach
some init / shutdown functionality or something else, that deals with group
of providers.
Containers package API docs - :py:mod:`dependency_injector.containers`.
.. toctree::
:maxdepth: 2
@ -23,7 +26,3 @@ Containers module API docs - :py:mod:`dependency_injector.containers`.
dynamic
specialization
overriding
copying
reset_singletons
check_dependencies
traversal

View File

@ -1,40 +1,43 @@
Container overriding
--------------------
Overriding of containers
------------------------
.. currentmodule:: dependency_injector.containers
The container can be overridden by the other container. All of the providers from the overriding
container will override the providers with the same names in the overridden container.
Containers can be overridden by other containers. This, actually, means that
all of the providers from overriding container will override providers with
the same names in overridden container.
.. literalinclude:: ../../examples/containers/override.py
There are two ways to override :py:class:`DeclarativeContainer` with another
container:
- Use :py:meth:`DeclarativeContainer.override` method.
- Use :py:func:`override` class decorator.
Example of overriding container using :py:meth:`DeclarativeContainer.override`
method:
.. literalinclude:: ../../examples/containers/override_declarative.py
:language: python
:lines: 3-
:linenos:
It helps in a testing. Also you can use it for configuring project for the different
environments: replace an API client with a stub on the dev or stage.
Example of overriding container using :py:func:`override` decorator:
The container also has:
- ``container.overridden`` - tuple of all overriding containers.
- ``container.reset_last_overriding()`` - reset last overriding for each provider in the container.
- ``container.reset_override()`` - reset all overriding in the container.
:py:class:`DynamicContainer` has the same functionality.
Another possible way to override container providers on declarative level is
``@containers.override()`` decorator:
.. literalinclude:: ../../examples/containers/declarative_override_decorator.py
.. literalinclude:: ../../examples/containers/override_declarative_decorator.py
:language: python
:lines: 3-
:emphasize-lines: 12-16
:linenos:
Decorator ``@containers.override()`` takes a container for overriding as an argument.
This container providers will be overridden by the providers with the same names from
the decorated container.
Also there are several useful :py:class:`DeclarativeContainer` methods and
properties that help to work with container overridings:
- :py:attr:`DeclarativeContainer.overridden` - tuple of all overriding
containers.
- :py:meth:`DeclarativeContainer.reset_last_overriding()` - reset last
overriding provider for each container providers.
- :py:meth:`DeclarativeContainer.reset_override()` - reset all overridings
for each container providers.
:py:class:`DynamicContainer` has exactly the same functionality, except of
:py:func:`override` decorator.
It helps to change the behaviour of application by importing extension modules but not a code change.
Imported module can override providers in main container. While the code uses main container as
before, the overridden providers provide components defined in the extension module.
.. disqus::

View File

@ -1,31 +0,0 @@
.. _reset-container-singletons:
Reset container singletons
--------------------------
To reset all container singletons use method ``.reset_singletons()``.
.. literalinclude:: ../../examples/containers/reset_singletons.py
:language: python
:lines: 3-
:emphasize-lines: 16
Method ``.reset_singletons()`` also resets singletons in sub-containers: ``providers.Container`` and
``providers.DependenciesContainer.``
.. literalinclude:: ../../examples/containers/reset_singletons_subcontainers.py
:language: python
:lines: 3-
:emphasize-lines: 21
You can use ``.reset_singletons()`` method with a context manager. Singletons will be reset on
both entering and exiting a context.
.. literalinclude:: ../../examples/containers/reset_singletons_with.py
:language: python
:lines: 3-
:emphasize-lines: 14-15
See also: :ref:`singleton-provider`.
.. disqus::

View File

@ -1,25 +1,27 @@
Specialization of the container provider type
---------------------------------------------
Specialization of containers
----------------------------
.. currentmodule:: dependency_injector.containers
You can make a restriction of the :py:class:`DeclarativeContainer` provider type:
:py:class:`DeclarativeContainer` could be specialized for any kind of needs
via declaring its subclasses.
One of such `builtin` features is a limitation for providers type.
Next example shows usage of this feature with :py:class:`DeclarativeContainer`
in couple with feature of :py:class:`dependency_injector.providers.Factory`
for limitation of its provided type:
.. literalinclude:: ../../examples/containers/declarative_provider_type.py
:language: python
:lines: 3-
:emphasize-lines: 29-31
:linenos:
The emphasized lines will cause an error because ``other_provider`` is not a subtype of the
``ServiceProvider``. This helps to control the content of the container.
The same works for the :py:class:`DynamicContainer`:
Limitation for providers type could be used with :py:class:`DynamicContainer`
as well:
.. literalinclude:: ../../examples/containers/dynamic_provider_type.py
:language: python
:lines: 3-
:emphasize-lines: 23
:linenos:
The emphasized line will also cause an error.
.. disqus::

View File

@ -1,33 +0,0 @@
Container providers traversal
-----------------------------
To traverse container providers use method ``.traverse()``.
.. literalinclude:: ../../examples/containers/traverse.py
:language: python
:lines: 3-
:emphasize-lines: 38
Method ``.traverse()`` returns a generator. Traversal generator visits all container providers.
This includes nested providers even if they are not present on the root level of the container.
Traversal generator guarantees that each container provider will be visited only once.
It can traverse cyclic provider graphs.
Traversal generator does not guarantee traversal order.
You can use ``types=[...]`` argument to filter providers. Traversal generator will only return
providers matching specified types.
.. code-block:: python
:emphasize-lines: 3
container = Container()
for provider in container.traverse(types=[providers.Resource]):
print(provider)
# <dependency_injector.providers.Resource(<function init_database at 0x10bd2cb80>) at 0x10d346b40>
# <dependency_injector.providers.Resource(<function init_cache at 0x10be373a0>) at 0x10d346bc0>
.. disqus::

View File

@ -1,68 +0,0 @@
Chained Factories pattern
=========================
This example demonstrates "Chained Factories" pattern.
The idea of the pattern is in wrapping ``Factory`` into another ``Factory`` that adds
additional arguments.
.. code-block:: python
base_factory = providers.Factory(
SomeClass,
base_argument=1,
)
concrete_factory = providers.Factory(
base_factory,
extra_argument=2,
)
if __name__ == "__main__":
instance = concrete_factory()
# Same as: # instance = SomeClass(base_argument=1, extra_argument=2)
Sample code
-----------
Listing of the pattern example:
.. literalinclude:: ../../examples/miniapps/factory-patterns/chained_factories.py
:language: python
Arguments priority
------------------
Passing of the arguments works the same way like for any other :ref:`factory-provider`.
.. code-block:: python
# 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 the most priority
chained_dict_factory = providers.Factory(
providers.Factory(dict, arg1=1),
arg1=2,
)
print(chained_dict_factory(arg1=3)) # prints: {"arg1": 3}
Credits
-------
The "Chained Factories" pattern was suggested by the ``Dependency Injector`` users.
.. disqus::

View File

@ -1,74 +0,0 @@
Factory of Factories pattern
============================
This example demonstrates "Factory of Factories" pattern.
The idea of the pattern is in creating a ``Factory`` that creates another ``Factory`` and adds
additional arguments.
.. code-block:: python
base_factory = providers.Factory(
providers.Factory
SomeClass,
base_argument=1,
)
concrete_factory = providers.Factory(
OtherClass,
instance=base_factory(extra_argument=1),
)
if __name__ == "__main__":
instance = concrete_factory()
# Same as: # instance = SomeClass(base_argument=1, extra_argument=2)
Sample code
-----------
Listing of the pattern example:
.. literalinclude:: ../../examples/miniapps/factory-patterns/factory_of_factories.py
:language: python
Arguments priority
------------------
Passing of the arguments works the same way like for any other :ref:`factory-provider`.
.. code-block:: python
# 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 the 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}
Credits
-------
The "Factory of Factories" pattern was suggested by the ``Dependency Injector`` users.
.. disqus::

View File

@ -1,17 +0,0 @@
Other examples
==============
.. meta::
:keywords: Python,Dependency Injection,Inversion of Control,Container,Example,Application,
Framework
:description: This sections contains assorted Dependency Injector examples.
This sections contains assorted ``Dependency Injector`` examples.
.. toctree::
:maxdepth: 2
use-cases
password-hashing
chained-factories
factory-of-factories

View File

@ -1,30 +0,0 @@
Password hashing example
========================
.. meta::
:keywords: Python,Dependency Injection,Inversion of Control,Container,Example,Application,
Framework,Callable
:description: This example demonstrates a usage of the Callable provider.
This example demonstrates an injection of the ``Callable`` provider.
The source code is available on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/password-hashing>`_.
Sample code
-----------
Listing of the pattern example:
.. literalinclude:: ../../examples/miniapps/password-hashing/example.py
:language: python
Run the example
---------------
Instructions for running:
.. code-block:: bash
python example.py
.. disqus::

View File

@ -1,74 +0,0 @@
Use cases example
=================
.. meta::
:keywords: Python,Dependency Injection,Inversion of Control,Container,Example,Application,
Framework,DependenciesContainer
:description: This example demonstrates a usage of the DependenciesContainer provider.
This example demonstrates a usage of the ``DependenciesContainer`` provider.
The source code is available on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/decoupled-packages>`_.
Application structure
---------------------
Example application has next structure:
.. code-block:: bash
./
└── example/
├── __init__.py
├── __main__.py
├── adapters.py
├── containers.py
└── usecases.py
Containers
----------
Listing of the ``example/containers.py``:
.. literalinclude:: ../../examples/miniapps/use-cases/example/containers.py
:language: python
Main module
-----------
Listing of the ``example/__main__.py``:
.. literalinclude:: ../../examples/miniapps/use-cases/example/__main__.py
:language: python
Run the application
-------------------
Instructions for running in the "test" mode:
.. code-block:: bash
python run.py test example@example.com
Instructions for running in the "prod" mode:
.. code-block:: bash
python run.py prod example@example.com
Adapters and use cases
----------------------
Listing of the ``example/adapters.py``:
.. literalinclude:: ../../examples/miniapps/use-cases/example/adapters.py
:language: python
Listing of the ``example/usecases.py``:
.. literalinclude:: ../../examples/miniapps/use-cases/example/usecases.py
:language: python
.. disqus::

View File

@ -1,83 +0,0 @@
.. _aiohttp-example:
Aiohttp example
===============
.. meta::
:keywords: Python,Dependency Injection,Aiohttp,Example
:description: This example demonstrates a usage of the Aiohttp and Dependency Injector.
This example shows how to use ``Dependency Injector`` with `Aiohttp <https://docs.aiohttp.org/>`_.
The example application is a REST API that searches for funny GIFs on the `Giphy <https://giphy.com/>`_.
The source code is available on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/aiohttp>`_.
:ref:`aiohttp-tutorial` demonstrates how to build this application step-by-step.
Application structure
---------------------
Application has next structure:
.. code-block:: bash
./
├── giphynavigator/
│ ├── __init__.py
│ ├── application.py
│ ├── containers.py
│ ├── giphy.py
│ ├── handlers.py
│ ├── services.py
│ └── tests.py
├── config.yml
└── requirements.txt
Container
---------
Declarative container is defined in ``giphynavigator/containers.py``:
.. literalinclude:: ../../examples/miniapps/aiohttp/giphynavigator/containers.py
:language: python
Handlers
--------
Handler has dependencies on search service and some config options. The dependencies are injected
using :ref:`wiring` feature.
Listing of ``giphynavigator/handlers.py``:
.. literalinclude:: ../../examples/miniapps/aiohttp/giphynavigator/handlers.py
:language: python
Application factory
-------------------
Application factory creates container, wires it with the ``handlers`` module, creates
``Aiohttp`` app and setup routes.
Listing of ``giphynavigator/application.py``:
.. literalinclude:: ../../examples/miniapps/aiohttp/giphynavigator/application.py
:language: python
Tests
-----
Tests use :ref:`provider-overriding` feature to replace giphy client with a mock ``giphynavigator/tests.py``:
.. literalinclude:: ../../examples/miniapps/aiohttp/giphynavigator/tests.py
:language: python
:emphasize-lines: 32,59,73
Sources
-------
Explore the sources on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/aiohttp>`_.
.. include:: ../sponsor.rst
.. disqus::

View File

@ -1,89 +0,0 @@
.. _application-multiple-containers:
Application example (multiple containers)
=========================================
.. meta::
:keywords: Python,Dependency Injection,Inversion of Control,Container,Example,Application,
Framework,AWS,boto3,client
:description: This example shows how you can create an application using multiple declarative
containers. We build an example Python micro application following the dependency
injection principle. It consists from several services with a domain logic that
have dependencies on database & AWS S3.
This example shows how you can create an application using multiple declarative containers. Using
multiple declarative containers is a good choice for a large application. For
building a moderate or a small size application refer to :ref:`application-single-container`.
We build an example micro application following the dependency injection principle. It consists
of several services with a domain logic. The services have dependencies on database & AWS S3.
.. image:: images/application.png
:width: 100%
:align: center
Start from the scratch or jump to the section:
.. contents::
:local:
:backlinks: none
You can find the source code and instructions for running on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/application-multiple-containers>`_.
Application structure
---------------------
Application consists of an ``example`` package, a configuration file and a ``requirements.txt``
file.
.. code-block:: bash
./
├── example/
│ ├── __init__.py
│ ├── __main__.py
│ ├── containers.py
│ └── services.py
├── config.yml
└── requirements.txt
Containers
----------
Listing of the ``example/containers.py``:
.. literalinclude:: ../../examples/miniapps/application-multiple-containers/example/containers.py
:language: python
Main module
-----------
Listing of the ``example/__main__.py``:
.. literalinclude:: ../../examples/miniapps/application-multiple-containers/example/__main__.py
:language: python
Services
--------
Listing of the ``example/services.py``:
.. literalinclude:: ../../examples/miniapps/application-multiple-containers/example/services.py
:language: python
Configuration
-------------
Listing of the ``config.yml``:
.. literalinclude:: ../../examples/miniapps/application-multiple-containers/config.yml
:language: yaml
Run the application
-------------------
You can find the source code and instructions for running on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/application-multiple-containers>`_.
.. include:: ../sponsor.rst
.. disqus::

View File

@ -1,95 +0,0 @@
.. _application-single-container:
Application example (single container)
======================================
.. meta::
:keywords: Python,Dependency Injection,Inversion of Control,Container,Example,Application,
Framework,AWS,boto3,client
:description: This example shows how you can create an application using a single declarative
container. We build an example Python micro application following the dependency
injection principle. It consists from several services with a domain logic that
have dependencies on database & AWS S3.
This example shows how you can create an application using a single declarative container. Using
a single declarative container is a good choice for small or moderate size application. For
building a large application refer to :ref:`application-multiple-containers`.
We build an example micro application following the dependency injection principle. It consists
of several services with a domain logic. The services have dependencies on database & AWS S3.
.. image:: images/application.png
:width: 100%
:align: center
Start from the scratch or jump to the section:
.. contents::
:local:
:backlinks: none
You can find the source code and instructions for running on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/application-single-container>`_.
Application structure
---------------------
Application consists of an ``example`` package, several configuration files and a
``requirements.txt`` file.
.. code-block:: bash
./
├── example/
│ ├── __init__.py
│ ├── __main__.py
│ ├── containers.py
│ └── services.py
├── config.ini
├── logging.ini
└── requirements.txt
Container
---------
Listing of the ``example/containers.py``:
.. literalinclude:: ../../examples/miniapps/application-single-container/example/containers.py
:language: python
Main module
-----------
Listing of the ``example/__main__.py``:
.. literalinclude:: ../../examples/miniapps/application-single-container/example/__main__.py
:language: python
Services
--------
Listing of the ``example/services.py``:
.. literalinclude:: ../../examples/miniapps/application-single-container/example/services.py
:language: python
Configuration
-------------
Listing of the ``config.ini``:
.. literalinclude:: ../../examples/miniapps/application-single-container/config.ini
:language: ini
Listing of the ``logging.ini``:
.. literalinclude:: ../../examples/miniapps/application-single-container/logging.ini
:language: ini
Run the application
-------------------
You can find the source code and instructions for running on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/application-single-container>`_.
.. include:: ../sponsor.rst
.. disqus::

View File

@ -1,22 +0,0 @@
.. _boto3-example:
Boto3 example
=============
.. meta::
:keywords: Python,Dependency Injection,Boto3,AWS,Amazon Web Services,S3,SQS,Rout53,EC2,Lambda,Example
:description: This example demonstrates a usage of Boto3 AWS client and Dependency Injector.
This example shows how to use ``Dependency Injector`` with `Boto3 <https://github.com/boto/boto3>`_.
The source code is available on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/boto3-session>`_.
Listing of ``boto3_session_example.py``:
.. literalinclude:: ../../examples/miniapps/boto3-session/boto3_session_example.py
:language: python
.. include:: ../sponsor.rst
.. disqus::

View File

@ -0,0 +1,77 @@
Bundles mini application example
--------------------------------
.. currentmodule:: dependency_injector.containers
"Bundles" is an example mini application that is intended to demonstrate the
power of dependency injection for creation of re-usable application components
("bundles") with 100% transparency of their dependencies.
Example application
~~~~~~~~~~~~~~~~~~~
"Bundles" mini application has next structure:
.. code-block:: bash
bundles/
bundles/ <-- Bundles package
photos/ <-- Photos bundle
__init__.py <-- Photos bundle dependency injection container
entities.py
repositories.py
users/ <-- Users bundle
__init__.py <-- Users bundle dependency injection container
entities.py
repositories.py
run.py <-- Entrypoint
IoC containers
~~~~~~~~~~~~~~
Next two listings show :py:class:`DeclarativeContainer`'s for "users" and
"photos" bundles.
Listing of ``bundles/users/__init__.py``:
.. literalinclude:: ../../examples/miniapps/bundles/bundles/users/__init__.py
:language: python
:linenos:
.. note::
- ``Users`` container has dependency on database.
Listing of ``bundles/photos/__init__.py``:
.. literalinclude:: ../../examples/miniapps/bundles/bundles/photos/__init__.py
:language: python
:linenos:
.. note::
- ``Photos`` container has dependencies on database and file storage.
Run application
~~~~~~~~~~~~~~~
Finally, both "bundles" are initialized by providing needed dependencies.
Initialization of dependencies happens right in the runtime, not earlier.
Generally, it means, that any part of any bundle could be overridden on the
fly.
Listing of ``run.py``:
.. literalinclude:: ../../examples/miniapps/bundles/run.py
:language: python
:linenos:
Links
~~~~~
+ `Dependency Injector <https://github.com/ets-labs/python-dependency-injector/>`_
+ `Full example sources <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/bundles>`_
.. disqus::

View File

@ -0,0 +1,23 @@
Chained Factories pattern
=========================
This example demonstrate implementation of "Chained Factories" pattern.
Main idea of this pattern is about wrapping :py:class:`Factory` into
another :py:class:`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::

View File

@ -1,134 +0,0 @@
.. _decoupled-packages:
Decoupled packages example (multiple containers)
================================================
.. meta::
:keywords: Python,Dependency Injection,Inversion of Control,Container,Example,Application,
Framework,AWS,boto3,client
:description: This example shows how to use Dependency Injector to create Python decoupled packages.
To achieve a decoupling each package has a container with the components. When
a component needs a dependency from the outside of the package scope we use the
Dependency provider. The package container has no knowledge on where the
dependencies come from. It states a need that the dependencies must be provided.
This helps to decouple a package from the 3rd party dependencies and other
packages.
This example shows how to use ``Dependency Injector`` to create decoupled packages.
To achieve a decoupling each package has a container with the components. When a component needs a
dependency from the outside of the package scope we use the ``Dependency`` provider. The package
container has no knowledge on where the dependencies come from. It states a need that the
dependencies must be provided. This helps to decouple a package from the 3rd party dependencies
and other packages.
To wire the packages we use an application container. Application container has all 3rd party
dependencies and package containers. It wires the packages and dependencies to create a
complete application.
We build an example micro application that consists of 3 packages:
- ``user`` - a package with user domain logic, depends on a database
- ``photo`` - a package with photo domain logic, depends on a database and AWS S3
- ``analytics`` - a package with analytics domain logic, depends on the ``user`` and ``photo``
package components
.. image:: images/decoupled-packages.png
:width: 100%
:align: center
Start from the scratch or jump to the section:
.. contents::
:local:
:backlinks: none
You can find the source code and instructions for running on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/decoupled-packages>`_.
Application structure
---------------------
Application consists of an ``example`` package, a configuration file and a ``requirements.txt``
file.
.. code-block:: bash
./
├── example/
│ ├── analytics/
│ │ ├── __init__.py
│ │ ├── containers.py
│ │ └── services.py
│ ├── photo/
│ │ ├── __init__.py
│ │ ├── containers.py
│ │ ├── entities.py
│ │ └── repositories.py
│ ├── user/
│ │ ├── __init__.py
│ │ ├── containers.py
│ │ ├── entities.py
│ │ └── repositories.py
│ ├── __init__.py
│ ├── __main__.py
│ └── containers.py
├── config.ini
└── requirements.txt
Package containers
------------------
Listing of the ``example/user/containers.py``:
.. literalinclude:: ../../examples/miniapps/decoupled-packages/example/user/containers.py
:language: python
Listing of the ``example/photo/containers.py``:
.. literalinclude:: ../../examples/miniapps/decoupled-packages/example/photo/containers.py
:language: python
Listing of the ``example/analytics/containers.py``:
.. literalinclude:: ../../examples/miniapps/decoupled-packages/example/analytics/containers.py
:language: python
Application container
---------------------
Application container consists of all packages and 3rd party dependencies. Its role is to wire
everything together in a complete application.
Listing of the ``example/containers.py``:
.. literalinclude:: ../../examples/miniapps/decoupled-packages/example/containers.py
:language: python
.. note::
Package ``analytics`` has dependencies on the repositories from the ``user`` and
``photo`` packages. This is an example of how you can pass the dependencies from one package
to another.
Main module
-----------
Listing of the ``example/__main__.py``:
.. literalinclude:: ../../examples/miniapps/decoupled-packages/example/__main__.py
:language: python
Configuration
-------------
Listing of the ``config.ini``:
.. literalinclude:: ../../examples/miniapps/decoupled-packages/config.ini
:language: ini
Run the application
-------------------
You can find the source code and instructions for running on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/decoupled-packages>`_.
.. include:: ../sponsor.rst
.. disqus::

View File

@ -1,99 +0,0 @@
.. _django-example:
Django example
==============
.. meta::
:keywords: Python,Dependency Injection,Django,Example
:description: This example demonstrates a usage of the Django and Dependency Injector.
This example shows how to use ``Dependency Injector`` with `Django <https://www.djangoproject.com/>`_.
The example application helps to search for repositories on the Github.
.. image:: images/django.png
:width: 100%
:align: center
The source code is available on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/django>`_.
Application structure
---------------------
Application has standard Django project structure. It consists of ``githubnavigator`` project package and
``web`` application package:
.. code-block:: bash
./
├── githubnavigator/
│ ├── __init__.py
│ ├── asgi.py
│ ├── containers.py
│ ├── services.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── web/
│ ├── templates/
│ │ ├── base.html
│ │ └── index.html
│ ├── __init__.py
│ ├── apps.py
│ ├── tests.py
│ ├── urls.py
│ └── views.py
├── manage.py
└── requirements.txt
Container
---------
Declarative container is defined in ``githubnavigator/containers.py``:
.. literalinclude:: ../../examples/miniapps/django/githubnavigator/containers.py
:language: python
Container instance is created in ``githubnavigator/__init__.py``:
.. literalinclude:: ../../examples/miniapps/django/githubnavigator/__init__.py
:language: python
Views
-----
View has dependencies on search service and some config options. The dependencies are injected
using :ref:`wiring` feature.
Listing of ``web/views.py``:
.. literalinclude:: ../../examples/miniapps/django/web/views.py
:language: python
App config
----------
Container is wired to the ``views`` module in the app config ``web/apps.py``:
.. literalinclude:: ../../examples/miniapps/django/web/apps.py
:language: python
:emphasize-lines: 12
Tests
-----
Tests use :ref:`provider-overriding` feature to replace github client with a mock ``web/tests.py``:
.. literalinclude:: ../../examples/miniapps/django/web/tests.py
:language: python
:emphasize-lines: 39,60
Sources
-------
Explore the sources on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/django>`_.
.. include:: ../sponsor.rst
.. disqus::

View File

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

View File

@ -1,100 +0,0 @@
.. _fastapi-redis-example:
FastAPI + Redis example
=======================
.. meta::
:keywords: Python,Dependency Injection,FastAPI,Redis,Example
:description: This example demonstrates a usage of the FastAPI, Redis, and Dependency Injector.
This example shows how to use ``Dependency Injector`` with `FastAPI <https://fastapi.tiangolo.com/>`_ and
`Redis <https://redis.io/>`_.
The source code is available on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/fastapi-redis>`_.
See also:
- Provider :ref:`async-injections`
- Resource provider :ref:`resource-async-initializers`
- Wiring :ref:`async-injections-wiring`
Application structure
---------------------
Application has next structure:
.. code-block:: bash
./
├── fastapiredis/
│ ├── __init__.py
│ ├── application.py
│ ├── containers.py
│ ├── redis.py
│ ├── services.py
│ └── tests.py
├── docker-compose.yml
├── Dockerfile
└── requirements.txt
Redis
-----
Module ``redis`` defines Redis connection pool initialization and shutdown. See ``fastapiredis/redis.py``:
.. literalinclude:: ../../examples/miniapps/fastapi-redis/fastapiredis/redis.py
:language: python
Service
-------
Module ``services`` contains example service. Service has a dependency on Redis connection pool.
It uses it for getting and setting a key asynchronously. Real life service will do something more meaningful.
See ``fastapiredis/services.py``:
.. literalinclude:: ../../examples/miniapps/fastapi-redis/fastapiredis/services.py
:language: python
Container
---------
Declarative container wires example service with Redis connection pool. See ``fastapiredis/containers.py``:
.. literalinclude:: ../../examples/miniapps/fastapi-redis/fastapiredis/containers.py
:language: python
Application
-----------
Module ``application`` creates ``FastAPI`` app, setup endpoint, and init container.
Endpoint ``index`` has a dependency on example service. The dependency is injected using :ref:`wiring` feature.
Listing of ``fastapiredis/application.py``:
.. literalinclude:: ../../examples/miniapps/fastapi-redis/fastapiredis/application.py
:language: python
Tests
-----
Tests use :ref:`provider-overriding` feature to replace example service with a mock. See ``fastapiredis/tests.py``:
.. literalinclude:: ../../examples/miniapps/fastapi-redis/fastapiredis/tests.py
:language: python
:emphasize-lines: 24
Sources
-------
The source code is available on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/fastapi-redis>`_.
See also:
- Provider :ref:`async-injections`
- Resource provider :ref:`resource-async-initializers`
- Wiring :ref:`async-injections-wiring`
.. include:: ../sponsor.rst
.. disqus::

View File

@ -1,121 +0,0 @@
.. _fastapi-sqlalchemy-example:
FastAPI + SQLAlchemy example
============================
.. meta::
:keywords: Python,Dependency Injection,FastAPI,SQLAlchemy,Example
:description: This example demonstrates a usage of the FastAPI, SQLAlchemy, and Dependency Injector.
This example shows how to use ``Dependency Injector`` with `FastAPI <https://fastapi.tiangolo.com/>`_ and
`SQLAlchemy <https://www.sqlalchemy.org/>`_.
The source code is available on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/fastapi-sqlalchemy>`_.
Thanks to `@ShvetsovYura <https://github.com/ShvetsovYura>`_ for providing initial example:
`FastAPI_DI_SqlAlchemy <https://github.com/ShvetsovYura/FastAPI_DI_SqlAlchemy>`_.
Application structure
---------------------
Application has next structure:
.. code-block:: bash
./
├── webapp/
│ ├── __init__.py
│ ├── application.py
│ ├── containers.py
│ ├── database.py
│ ├── endpoints.py
│ ├── models.py
│ ├── repositories.py
│ ├── services.py
│ └── tests.py
├── config.yml
├── docker-compose.yml
├── Dockerfile
└── requirements.txt
Application factory
-------------------
Application factory creates container, wires it with the ``endpoints`` module, creates
``FastAPI`` app, and setup routes.
Application factory also creates database if it does not exist.
Listing of ``webapp/application.py``:
.. literalinclude:: ../../examples/miniapps/fastapi-sqlalchemy/webapp/application.py
:language: python
Endpoints
---------
Module ``endpoints`` contains example endpoints. Endpoints have a dependency on user service.
User service is injected using :ref:`wiring` feature. See ``webapp/endpoints.py``:
.. literalinclude:: ../../examples/miniapps/fastapi-sqlalchemy/webapp/endpoints.py
:language: python
Container
---------
Declarative container wires example user service, user repository, and utility database class.
See ``webapp/containers.py``:
.. literalinclude:: ../../examples/miniapps/fastapi-sqlalchemy/webapp/containers.py
:language: python
Services
--------
Module ``services`` contains example user service. See ``webapp/services.py``:
.. literalinclude:: ../../examples/miniapps/fastapi-sqlalchemy/webapp/services.py
:language: python
Repositories
------------
Module ``repositories`` contains example user repository. See ``webapp/repositories.py``:
.. literalinclude:: ../../examples/miniapps/fastapi-sqlalchemy/webapp/repositories.py
:language: python
Models
------
Module ``models`` contains example SQLAlchemy user model. See ``webapp/models.py``:
.. literalinclude:: ../../examples/miniapps/fastapi-sqlalchemy/webapp/models.py
:language: python
Database
--------
Module ``database`` defines declarative base and utility class with engine and session factory.
See ``webapp/database.py``:
.. literalinclude:: ../../examples/miniapps/fastapi-sqlalchemy/webapp/database.py
:language: python
Tests
-----
Tests use :ref:`provider-overriding` feature to replace repository with a mock. See ``webapp/tests.py``:
.. literalinclude:: ../../examples/miniapps/fastapi-sqlalchemy/webapp/tests.py
:language: python
:emphasize-lines: 25, 45, 58, 74, 86, 97
Sources
-------
The source code is available on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/fastapi-sqlalchemy>`_.
.. include:: ../sponsor.rst
.. disqus::

View File

@ -1,81 +0,0 @@
.. _fastapi-example:
FastAPI example
===============
.. meta::
:keywords: Python,Dependency Injection,FastAPI,Example
:description: This example demonstrates a usage of the FastAPI and Dependency Injector.
This example shows how to use ``Dependency Injector`` with `FastAPI <https://fastapi.tiangolo.com/>`_.
The example application is a REST API that searches for funny GIFs on the `Giphy <https://giphy.com/>`_.
The source code is available on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/fastapi>`_.
Application structure
---------------------
Application has next structure:
.. code-block:: bash
./
├── giphynavigator/
│ ├── __init__.py
│ ├── application.py
│ ├── containers.py
│ ├── endpoints.py
│ ├── giphy.py
│ ├── services.py
│ └── tests.py
├── config.yml
└── requirements.txt
Container
---------
Declarative container is defined in ``giphynavigator/containers.py``:
.. literalinclude:: ../../examples/miniapps/fastapi/giphynavigator/containers.py
:language: python
Endpoints
---------
Endpoint has a dependency on search service. There are also some config options that are used as default values.
The dependencies are injected using :ref:`wiring` feature.
Listing of ``giphynavigator/endpoints.py``:
.. literalinclude:: ../../examples/miniapps/fastapi/giphynavigator/endpoints.py
:language: python
Application factory
-------------------
Application factory creates container, wires it with the ``endpoints`` module, creates
``FastAPI`` app, and setup routes.
Listing of ``giphynavigator/application.py``:
.. literalinclude:: ../../examples/miniapps/fastapi/giphynavigator/application.py
:language: python
Tests
-----
Tests use :ref:`provider-overriding` feature to replace giphy client with a mock ``giphynavigator/tests.py``:
.. literalinclude:: ../../examples/miniapps/fastapi/giphynavigator/tests.py
:language: python
:emphasize-lines: 29,57,72
Sources
-------
Explore the sources on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/fastapi>`_.
.. include:: ../sponsor.rst
.. disqus::

View File

@ -1,48 +0,0 @@
.. _fastdepends-example:
FastDepends example
===================
.. meta::
:keywords: Python,Dependency Injection,FastDepends,Example
:description: This example demonstrates a usage of the FastDepends and Dependency Injector.
This example demonstrates how to use ``Dependency Injector`` with `FastDepends <https://github.com/Lancetnik/FastDepends>`_, a lightweight dependency injection framework inspired by FastAPI's dependency system, but without the web framework components.
Basic Usage
-----------
The integration between FastDepends and Dependency Injector is straightforward. Simply use Dependency Injector's ``Provide`` marker within FastDepends' ``Depends`` function:
.. code-block:: python
import sys
from dependency_injector import containers, providers
from dependency_injector.wiring import inject, Provide
from fast_depends import Depends
class CoefficientService:
@staticmethod
def get_coefficient() -> float:
return 1.2
class Container(containers.DeclarativeContainer):
service = providers.Factory(CoefficientService)
@inject
def apply_coefficient(
a: int,
coefficient_provider: CoefficientService = Depends(Provide[Container.service]),
) -> float:
return a * coefficient_provider.get_coefficient()
container = Container()
container.wire(modules=[sys.modules[__name__]])
apply_coefficient(100) == 120.0

View File

@ -1,91 +0,0 @@
.. _flask-blueprints-example:
Flask blueprints example
========================
.. meta::
:keywords: Python,Dependency Injection,Flask,Blueprints,Example
:description: This example demonstrates a usage of the Flask Blueprints and Dependency Injector.
This example shows how to use ``Dependency Injector`` with `Flask <https://flask.palletsprojects.com/en/1.1.x/>`_
blueprints.
The example application helps to search for repositories on the Github.
.. image:: images/flask.png
:width: 100%
:align: center
The source code is available on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/flask-blueprints>`_.
Application structure
---------------------
Application has next structure:
.. code-block:: bash
./
├── githubnavigator/
│ ├── blueprints
│ │ ├── __init__.py
│ │ └── example.py
│ ├── templates
│ │ ├── base.html
│ │ └── index.py
│ ├── __init__.py
│ ├── application.py
│ ├── containers.py
│ ├── services.py
│ └── tests.py
├── config.yml
└── requirements.txt
Container
---------
Declarative container is defined in ``githubnavigator/containers.py``:
.. literalinclude:: ../../examples/miniapps/flask-blueprints/githubnavigator/containers.py
:language: python
Blueprints
----------
Blueprint's view has dependencies on search service and some config options. The dependencies are injected
using :ref:`wiring` feature.
Listing of ``githubnavigator/blueprints/example.py``:
.. literalinclude:: ../../examples/miniapps/flask-blueprints/githubnavigator/blueprints/example.py
:language: python
Application factory
-------------------
Application factory creates container, wires it with the blueprints, creates
``Flask`` app, and setup routes.
Listing of ``githubnavigator/application.py``:
.. literalinclude:: ../../examples/miniapps/flask-blueprints/githubnavigator/application.py
:language: python
Tests
-----
Tests use :ref:`provider-overriding` feature to replace github client with a mock ``githubnavigator/tests.py``:
.. literalinclude:: ../../examples/miniapps/flask-blueprints/githubnavigator/tests.py
:language: python
:emphasize-lines: 44,67
Sources
-------
Explore the sources on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/flask-blueprints>`_.
.. include:: ../sponsor.rst
.. disqus::

View File

@ -1,89 +0,0 @@
.. _flask-example:
Flask example
=============
.. meta::
:keywords: Python,Dependency Injection,Flask,Example
:description: This example demonstrates a usage of the Flask and Dependency Injector.
This example shows how to use ``Dependency Injector`` with `Flask <https://flask.palletsprojects.com/en/1.1.x/>`_.
The example application helps to search for repositories on the Github.
.. image:: images/flask.png
:width: 100%
:align: center
The source code is available on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/flask>`_.
:ref:`flask-tutorial` demonstrates how to build this application step-by-step.
Application structure
---------------------
Application has next structure:
.. code-block:: bash
./
├── githubnavigator/
│ ├── templates
│ │ ├── base.html
│ │ └── index.py
│ ├── __init__.py
│ ├── application.py
│ ├── containers.py
│ ├── services.py
│ ├── tests.py
│ └── views.py
├── config.yml
└── requirements.txt
Container
---------
Declarative container is defined in ``githubnavigator/containers.py``:
.. literalinclude:: ../../examples/miniapps/flask/githubnavigator/containers.py
:language: python
Views
-----
View has dependencies on search service and some config options. The dependencies are injected
using :ref:`wiring` feature.
Listing of ``githubnavigator/views.py``:
.. literalinclude:: ../../examples/miniapps/flask/githubnavigator/views.py
:language: python
Application factory
-------------------
Application factory creates container, wires it with the ``views`` module, creates
``Flask`` app and setup routes.
Listing of ``githubnavigator/application.py``:
.. literalinclude:: ../../examples/miniapps/flask/githubnavigator/application.py
:language: python
Tests
-----
Tests use :ref:`provider-overriding` feature to replace github client with a mock ``githubnavigator/tests.py``:
.. literalinclude:: ../../examples/miniapps/flask/githubnavigator/tests.py
:language: python
:emphasize-lines: 44,67
Sources
-------
Explore the sources on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/flask>`_.
.. include:: ../sponsor.rst
.. disqus::

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 382 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 647 KiB

View File

@ -2,26 +2,24 @@ Examples
========
.. meta::
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Example
:description: Python dependency injection examples.
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control
:description: Current section of documentation is designed to provide
several example mini applications that are built on the top
of inversion of control principle and powered by
"Dependency Injector" framework.
Explore the examples to see the ``Dependency Injector`` in action.
Current section of documentation is designed to provide several example mini
applications that are built according to the inversion of control principle
and powered by *Dependency Injector* framework.
.. toctree::
:maxdepth: 2
application-single-container
application-multiple-containers
decoupled-packages
boto3
django
flask
flask-blueprints
aiohttp
sanic
fastapi
fastapi-redis
fastapi-sqlalchemy
fastdepends
.. disqus::
movie_lister
services_miniapp_v1
services_miniapp_v2
bundles_miniapp
use_cases_miniapp
password_hashing_miniapp
chained_factories
factory_of_factories

View File

@ -0,0 +1,130 @@
Movie lister naive example
--------------------------
.. meta::
:description: Movie lister - is a naive example of dependency injection and
inversion of control containers on Python. Original example
was taken from Martin Fowler's article about dependency
injection and inversion of control.
This naive example was taken from Martin Fowler's article about dependency
injection and inversion of control: http://www.martinfowler.com/articles/injection.html
Like Martin says:
.. pull-quote::
*Like all of my examples it's one of those super-simple examples;
small enough to be unreal, but hopefully enough for you to visualize
what's going on without falling into the bog of a real example.*
While original Martin's MovieLister example was a bit modified here, it
makes sense to provide some description. So, the idea of this example is to
create ``movies`` library that can be configured to work with different
movie databases (csv, sqlite, etc...) and provide 2 main features:
1. List all movies that were directed by certain person.
2. List all movies that were released in certain year.
Also this example contains 3 mini applications that are based on ``movies``
library:
1. ``app_csv.py`` - list movies by certain criteria from csv file database.
2. ``app_db.py`` - list movies by certain criteria from sqlite database.
3. ``app_db_csv.py`` - list movies by certain criteria from csv file and
sqlite databases.
Instructions for running:
.. code-block:: bash
python app_csv.py
python app_db.py
python app_db_csv.py
Full code of example could be found on GitHub_.
Movies library
~~~~~~~~~~~~~~
Classes diagram:
.. image:: /images/miniapps/movie_lister/classes.png
:width: 100%
:align: center
Movies library structure:
.. code-block:: bash
/movies
/__init__.py
/finders.py
/listers.py
/models.py
Listing of ``movies/__init__.py``:
.. literalinclude:: ../../examples/miniapps/movie_lister/movies/__init__.py
:language: python
:linenos:
Example application
~~~~~~~~~~~~~~~~~~~
Example application structure:
.. code-block:: bash
/example
/__init__.py
/db.py
/main.py
Listing of ``examples/main.py``:
.. literalinclude:: ../../examples/miniapps/movie_lister/example/main.py
:language: python
:linenos:
Listing of ``examples/db.py``:
.. literalinclude:: ../../examples/miniapps/movie_lister/example/db.py
:language: python
:linenos:
Csv application
~~~~~~~~~~~~~~~
Listing of ``app_csv.py``:
.. literalinclude:: ../../examples/miniapps/movie_lister/app_csv.py
:language: python
:linenos:
Database application
~~~~~~~~~~~~~~~~~~~~
Listing of ``app_db.py``:
.. literalinclude:: ../../examples/miniapps/movie_lister/app_db.py
:language: python
:linenos:
Csv and database application
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Listing of ``app_db_csv.py``:
.. literalinclude:: ../../examples/miniapps/movie_lister/app_db_csv.py
:language: python
:linenos:
.. disqus::
.. _GitHub: https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/movie_lister

View File

@ -0,0 +1,19 @@
Dependency injection and password hashing in Python
===================================================
Small example that demonstrates using of dependency injection for user
password hashing.
Instructions for running:
.. code-block:: bash
python example.py
Listing of ``example.py``:
.. literalinclude:: ../../examples/miniapps/password_hashing/example.py
:language: python
:linenos:
.. disqus::

View File

@ -1,82 +0,0 @@
.. _sanic-example:
Sanic example
==============
.. meta::
:keywords: Python,Dependency Injection,Sanic,Example
:description: This example demonstrates a usage of the Sanic and Dependency Injector.
This example shows how to use ``Dependency Injector`` with `Sanic <https://sanic.readthedocs.io/en/latest/>`_.
The example application is a REST API that searches for funny GIFs on the `Giphy <https://giphy.com/>`_.
The source code is available on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/sanic>`_.
Application structure
---------------------
Application has next structure:
.. code-block:: bash
./
├── giphynavigator/
│ ├── __init__.py
│ ├── __main__.py
│ ├── application.py
│ ├── containers.py
│ ├── giphy.py
│ ├── handlers.py
│ ├── services.py
│ └── tests.py
├── config.yml
└── requirements.txt
Container
---------
Declarative container is defined in ``giphynavigator/containers.py``:
.. literalinclude:: ../../examples/miniapps/sanic/giphynavigator/containers.py
:language: python
Handlers
--------
Handler has dependencies on search service and some config options. The dependencies are injected
using :ref:`wiring` feature.
Listing of ``giphynavigator/handlers.py``:
.. literalinclude:: ../../examples/miniapps/sanic/giphynavigator/handlers.py
:language: python
Application factory
-------------------
Application factory creates container, wires it with the ``handlers`` module, creates
``Sanic`` app and setup routes.
Listing of ``giphynavigator/application.py``:
.. literalinclude:: ../../examples/miniapps/sanic/giphynavigator/application.py
:language: python
Tests
-----
Tests use :ref:`provider-overriding` feature to replace giphy client with a mock ``giphynavigator/tests.py``:
.. literalinclude:: ../../examples/miniapps/sanic/giphynavigator/tests.py
:language: python
:emphasize-lines: 34,61,75
Sources
-------
Explore the sources on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/sanic>`_.
.. include:: ../sponsor.rst
.. disqus::

View File

@ -0,0 +1,77 @@
Services mini application example (v1 - multiple containers)
------------------------------------------------------------
.. meta::
:description: "Services miniapp" is an example mini application that
consists from several services that have dependencies on
some standard and 3rd-party libraries for logging,
interaction with database and remote service via API.
"Services miniapp" example demonstrates usage of
Dependency Injector for creating several inversion of control /
dependency injection containers.
"Services miniapp" is an example mini application that consists from several
services that have dependencies on some standard and 3rd-party libraries for
logging, interaction with database and remote service calls via API.
"Services miniapp" example demonstrates usage of
:doc:`Dependency Injector <../index>` for creating several IoC containers.
Instructions for running:
.. code-block:: bash
python run.py 1 secret photo.jpg
Example application
~~~~~~~~~~~~~~~~~~~
Classes diagram:
.. image:: /images/miniapps/services/classes.png
:width: 100%
:align: center
Example application structure:
.. code-block:: bash
/example
/__init__.py
/main.py
/services.py
Listing of ``example/services.py``:
.. literalinclude:: ../../examples/miniapps/services_v1/example/services.py
:language: python
:linenos:
Listing of ``example/main.py``:
.. literalinclude:: ../../examples/miniapps/services_v1/example/main.py
:language: python
:linenos:
IoC containers
~~~~~~~~~~~~~~
Listing of ``containers.py``:
.. literalinclude:: ../../examples/miniapps/services_v1/containers.py
:language: python
:linenos:
Run application
~~~~~~~~~~~~~~~
Listing of ``run.py``:
.. literalinclude:: ../../examples/miniapps/services_v1/run.py
:language: python
:linenos:
.. disqus::

View File

@ -0,0 +1,77 @@
Services mini application example (v2 - single container)
---------------------------------------------------------
.. meta::
:description: "Services miniapp" is an example mini application that
consists from several services that have dependencies on
some standard and 3rd-party libraries for logging,
interaction with database and remote service via API.
"Services miniapp" example demonstrates usage of
Dependency Injector for creating inversion of control /
dependency injection container.
"Services miniapp" is an example mini application that consists from several
services that have dependencies on some standard and 3rd-party libraries for
logging, interaction with database and remote service calls via API.
"Services miniapp" example demonstrates usage of
:doc:`Dependency Injector <../index>` for creating IoC container.
Instructions for running:
.. code-block:: bash
python run.py 1 secret photo.jpg
Example application
~~~~~~~~~~~~~~~~~~~
Classes diagram:
.. image:: /images/miniapps/services/classes.png
:width: 100%
:align: center
Example application structure:
.. code-block:: bash
/example
/__init__.py
/main.py
/services.py
Listing of ``example/services.py``:
.. literalinclude:: ../../examples/miniapps/services_v2/example/services.py
:language: python
:linenos:
Listing of ``example/main.py``:
.. literalinclude:: ../../examples/miniapps/services_v2/example/main.py
:language: python
:linenos:
IoC container
~~~~~~~~~~~~~
Listing of ``container.py``:
.. literalinclude:: ../../examples/miniapps/services_v2/container.py
:language: python
:linenos:
Run application
~~~~~~~~~~~~~~~
Listing of ``run.py``:
.. literalinclude:: ../../examples/miniapps/services_v2/run.py
:language: python
:linenos:
.. disqus::

View File

@ -0,0 +1,57 @@
Use cases mini application example
----------------------------------
.. currentmodule:: dependency_injector.providers
"Use cases" miniapp demonstrate usage of :py:class:`DependenciesContainer`
provider.
Example application
~~~~~~~~~~~~~~~~~~~
"Use cases" mini application has next structure:
.. code-block:: bash
use_cases/
example/ <-- Example package
__init__.py
adapters.py
use_cases.py
containers.py <-- Dependency injection containers
run.py <-- Entrypoint
IoC containers
~~~~~~~~~~~~~~
Listing of ``use_cases/containers.py``:
.. literalinclude:: ../../examples/miniapps/use_cases/containers.py
:language: python
:linenos:
Run application
~~~~~~~~~~~~~~~
Listing of ``run.py``:
.. literalinclude:: ../../examples/miniapps/use_cases/run.py
:language: python
:linenos:
Instructions for running:
.. code-block:: bash
python run.py prod example@example.com # Running in "production" environment
python run.py test example@example.com # Running in "testing" environment
Links
~~~~~
+ `Dependency Injector <https://github.com/ets-labs/python-dependency-injector/>`_
+ `Full example sources <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/use_cases>`_
.. disqus::

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

BIN
docs/images/internals.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -1,150 +1,94 @@
=================================================================
Dependency Injector --- Dependency injection framework for Python
=================================================================
======================================================================
Dependency Injector --- Dependency injection microframework for Python
======================================================================
.. meta::
:google-site-verification: V1hlKfpgL3AARAElwFcqP4qW1Smsx5bKSRU8O86i20Y
:google-site-verification: 6it89zX0_wccKEhAqbAiYQooS95f0BA8YfesHk6bsNA
:keywords: Python,Dependency injection,DI,Inversion of Control,IoC,
IoC Container,Factory, Singleton, Design Patterns
:description: Dependency Injector is a dependency injection framework
for Python. It helps to maintain you application structure.
It was designed to be unified, developer-friendly
tool that helps to implement dependency injection design
pattern in formal, pretty, Pythonic way. Dependency Injector
provides implementations of such popular design patterns
like IoC container, Factory and Singleton. Dependency
Injector providers are implemented as C extension types
:description: Dependency Injector is a dependency injection microframework
for Python. It was designed to be unified, developer-friendly
tool that helps to implement dependency injection design
pattern in formal, pretty, Pythonic way. Dependency Injector
provides implementations of such popular design patterns
like IoC container, Factory and Singleton. Dependency
Injector providers are implemented as C extension types
using Cython.
.. _index:
.. image:: https://img.shields.io/pypi/v/dependency_injector.svg
:target: https://pypi.org/project/dependency-injector/
:alt: Latest Version
*Dependency Injector* is a dependency injection microframework for Python.
It was designed to be a unified and developer-friendly tool that helps
implement a dependency injection design pattern in a formal, pretty, and
Pythonic way.
.. image:: https://img.shields.io/pypi/l/dependency_injector.svg
:target: https://pypi.org/project/dependency-injector/
:alt: License
The key features of the *Dependency Injector* framework are:
.. image:: https://img.shields.io/pypi/pyversions/dependency_injector.svg
:target: https://pypi.org/project/dependency-injector/
:alt: Supported Python versions
+ Easy, smart, and pythonic style.
+ Obvious and clear structure.
+ Extensibility and flexibility.
+ High performance.
+ Memory efficiency.
+ Thread safety.
+ Documented.
+ Semantically versioned.
.. image:: https://img.shields.io/pypi/implementation/dependency_injector.svg
:target: https://pypi.org/project/dependency-injector/
:alt: Supported Python implementations
*Dependency Injector* containers and providers are implemented as C extension
types using Cython.
.. image:: https://static.pepy.tech/badge/dependency-injector
:target: https://pepy.tech/project/dependency-injector
:alt: Downloads
Status
------
.. image:: https://static.pepy.tech/badge/dependency-injector/month
:target: https://pepy.tech/project/dependency-injector
:alt: Downloads
.. image:: https://static.pepy.tech/badge/dependency-injector/week
:target: https://pepy.tech/project/dependency-injector
:alt: Downloads
.. image:: https://img.shields.io/pypi/wheel/dependency-injector.svg
:target: https://pypi.org/project/dependency-injector/
:alt: Wheel
.. image:: https://img.shields.io/github/actions/workflow/status/ets-labs/python-dependency-injector/tests-and-linters.yml?branch=master
:target: https://github.com/ets-labs/python-dependency-injector/actions
:alt: Build Status
.. image:: https://coveralls.io/repos/github/ets-labs/python-dependency-injector/badge.svg?branch=master
:target: https://coveralls.io/github/ets-labs/python-dependency-injector?branch=master
:alt: Coverage Status
``Dependency Injector`` is a dependency injection framework for Python.
It helps implementing the dependency injection principle.
Key features of the ``Dependency Injector``:
- **Providers**. Provides ``Factory``, ``Singleton``, ``Callable``, ``Coroutine``, ``Object``,
``List``, ``Dict``, ``Configuration``, ``Resource``, ``Dependency``, and ``Selector`` providers
that help assemble your objects. See :ref:`providers`.
- **Overriding**. Can override any provider by another provider on the fly. This helps in testing
and configuring dev/stage environment to replace API clients with stubs etc. See
:ref:`provider-overriding`.
- **Configuration**. Reads configuration from ``yaml``, ``ini``, and ``json`` files, ``pydantic`` settings,
environment variables, and dictionaries. See :ref:`configuration-provider`.
- **Resources**. Helps with initialization and configuring of logging, event loop, thread
or process pool, etc. Can be used for per-function execution scope in tandem with wiring.
See :ref:`resource-provider`.
- **Containers**. Provides declarative and dynamic containers. See :ref:`containers`.
- **Wiring**. Injects dependencies into functions and methods. Helps integrate with
other frameworks: Django, Flask, Aiohttp, Sanic, FastAPI, etc. See :ref:`wiring`.
- **Asynchronous**. Supports asynchronous injections. See :ref:`async-injections`.
- **Typing**. Provides typing stubs, ``mypy``-friendly. See :ref:`provider-typing`.
- **Performance**. Fast. Written in ``Cython``.
- **Maturity**. Mature and production-ready. Well-tested, documented, and supported.
.. code-block:: python
from dependency_injector import containers, providers
from dependency_injector.wiring import Provide, inject
class Container(containers.DeclarativeContainer):
config = providers.Configuration()
api_client = providers.Singleton(
ApiClient,
api_key=config.api_key,
timeout=config.timeout,
)
service = providers.Factory(
Service,
api_client=api_client,
)
@inject
def main(service: Service = Provide[Container.service]) -> None:
...
if __name__ == "__main__":
container = Container()
container.config.api_key.from_env("API_KEY", required=True)
container.config.timeout.from_env("TIMEOUT", as_=int, default=5)
container.wire(modules=[__name__])
main() # <-- dependency is injected automatically
with container.api_client.override(mock.Mock()):
main() # <-- overridden dependency is injected automatically
With the ``Dependency Injector``, object assembling is consolidated in the container.
Dependency injections are defined explicitly.
This makes it easier to understand and change how the application works.
.. figure:: https://raw.githubusercontent.com/wiki/ets-labs/python-dependency-injector/img/di-readme.svg
:target: https://github.com/ets-labs/python-dependency-injector
Explore the documentation to know more about the ``Dependency Injector``.
.. _contents:
+---------------------------------------+--------------------------------------------------------------------------------------------------------------------+
| *PyPi* | .. image:: https://img.shields.io/pypi/v/dependency_injector.svg |
| | :target: https://pypi.org/project/dependency-injector/ |
| | :alt: Latest Version |
| | .. image:: https://img.shields.io/pypi/l/dependency_injector.svg |
| | :target: https://pypi.org/project/dependency-injector/ |
| | :alt: License |
| | .. image:: https://pepy.tech/badge/dependency-injector |
| | :target: https://pepy.tech/project/dependency-injector |
| | :alt: Downloads |
+---------------------------------------+--------------------------------------------------------------------------------------------------------------------+
| *Python versions and implementations* | .. image:: https://img.shields.io/pypi/pyversions/dependency_injector.svg |
| | :target: https://pypi.org/project/dependency-injector/ |
| | :alt: Supported Python versions |
| | .. image:: https://img.shields.io/pypi/implementation/dependency_injector.svg |
| | :target: https://pypi.org/project/dependency-injector/ |
| | :alt: Supported Python implementations |
+---------------------------------------+--------------------------------------------------------------------------------------------------------------------+
| *Builds and tests coverage* | .. image:: https://travis-ci.org/ets-labs/python-dependency-injector.svg?branch=master |
| | :target: https://travis-ci.org/ets-labs/python-dependency-injector |
| | :alt: Build Status |
| | .. image:: http://readthedocs.org/projects/python-dependency-injector/badge/?version=latest |
| | :target: http://python-dependency-injector.ets-labs.org/ |
| | :alt: Docs Status |
| | .. image:: https://coveralls.io/repos/github/ets-labs/python-dependency-injector/badge.svg?branch=master |
| | :target: https://coveralls.io/github/ets-labs/python-dependency-injector?branch=master |
| | :alt: Coverage Status |
+---------------------------------------+--------------------------------------------------------------------------------------------------------------------+
| *Github* | .. image:: https://img.shields.io/github/watchers/ets-labs/python-dependency-injector.svg?style=social&label=Watch |
| | :target: https://github.com/ets-labs/python-dependency-injector |
| | :alt: Github watchers |
| | .. image:: https://img.shields.io/github/stars/ets-labs/python-dependency-injector.svg?style=social&label=Star |
| | :target: https://github.com/ets-labs/python-dependency-injector |
| | :alt: Github stargazers |
| | .. image:: https://img.shields.io/github/forks/ets-labs/python-dependency-injector.svg?style=social&label=Fork |
| | :target: https://github.com/ets-labs/python-dependency-injector |
| | :alt: Github forks |
+---------------------------------------+--------------------------------------------------------------------------------------------------------------------+
Contents
--------
.. toctree::
:maxdepth: 2
.. toctree::
:maxdepth: 2
introduction/index
examples/index
tutorials/index
providers/index
containers/index
wiring
examples-other/index
api/index
main/feedback
main/changelog
introduction/index
main/installation
providers/index
containers/index
examples/index
api/index
main/feedback
main/changelog

View File

@ -1,315 +1,144 @@
Dependency injection and inversion of control in Python
=======================================================
-------------------------------------------------------
.. meta::
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Example
:description: This page describes a usage of the dependency injection and inversion of control
in Python. It contains Python examples that show how to implement dependency
injection. It demonstrates a usage of the dependency injection framework
Dependency Injector, its container, Factory, Singleton and Configuration
providers. The example show how to use Dependency Injector providers overriding
feature for testing or configuring project in different environments and explains
why it's better than monkey-patching.
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control
:description: This article describes benefits of dependency injection and
inversion of control for Python applications. Also it
contains some Python examples that show how dependency
injection and inversion could be implemented. In addition, it
demonstrates usage of dependency injection framework,
IoC container and such popular design pattern as Factory.
History
~~~~~~~
Originally, dependency injection pattern got popular in languages with static
typing, like Java. Dependency injection framework can
significantly improve flexibility of the language with static typing. Also,
implementation of dependency injection framework for language with static
typing is not something that one can do shortly, it could be quite complex
thing to be done well.
While Python is very flexible interpreted language with dynamic typing, there
is a meaning that dependency injection doesn't work for it as well, as it does
for Java. Also there is a meaning that dependency injection framework is
something that Python developer would not ever need, cause dependency injection
could be implemented easily using language fundamentals.
Discussion
~~~~~~~~~~
It is true.
Partly.
Dependency injection, as a software design pattern, has number of
advantages that are common for each language (including Python):
+ Dependency Injection decreases coupling between a class and its dependency.
+ Because dependency injection doesn't require any change in code behavior it
can be applied to legacy code as a refactoring. The result is clients that
are more independent and that are easier to unit test in isolation using
stubs or mock objects that simulate other objects not under test. This ease
of testing is often the first benefit noticed when using dependency
injection.
+ Dependency injection can be used to externalize a system's configuration
details into configuration files allowing the system to be reconfigured
without recompilation (rebuilding). Separate configurations can be written
for different situations that require different implementations of
components. This includes, but is not limited to, testing.
+ Reduction of boilerplate code in the application objects since all work to
initialize or set up dependencies is handled by a provider component.
+ Dependency injection allows a client to remove all knowledge of a concrete
implementation that it needs to use. This helps isolate the client from the
impact of design changes and defects. It promotes reusability, testability
and maintainability.
+ Dependency injection allows a client the flexibility of being configurable.
Only the client's behavior is fixed. The client may act on anything that
supports the intrinsic interface the client expects.
.. note::
While improved testability is one the first benefits of using dependency
injection, it could be easily overwhelmed by monkey-patching technique,
that works absolutely great in Python (you can monkey-patch anything,
anytime). At the same time, monkey-patching has nothing similar with
other advantages defined above. Also monkey-patching technique is
something that could be considered like too dirty to be used in production.
The complexity of dependency injection pattern implementation in Python is
definitely quite lower than in other languages (even with dynamic typing).
.. note::
Low complexity of dependency injection pattern implementation in Python
still means that some code should be written, reviewed, tested and
supported.
Talking about inversion of control, it is a software design principle that
also works for each programming language, not depending on its typing type.
Inversion of control is used to increase modularity of the program and make
it extensible.
Main design purposes of using inversion of control are:
+ To decouple the execution of a task from implementation.
+ To focus a module on the task it is designed for.
+ To free modules from assumptions about how other systems do what they do and
instead rely on contracts.
+ To prevent side effects when replacing a module.
Example
~~~~~~~
Let's go through next example:
.. image:: /images/miniapps/engines_cars/diagram.png
:width: 100%
:align: center
Listing of ``example.engines`` module:
.. literalinclude:: ../../examples/miniapps/engines_cars/example/engines.py
:language: python
:linenos:
Listing of ``example.cars`` module:
.. literalinclude:: ../../examples/miniapps/engines_cars/example/cars.py
:language: python
:linenos:
Next example demonstrates creation of several cars with different engines:
.. literalinclude:: ../../examples/miniapps/engines_cars/example_di.py
:language: python
:linenos:
While previous example demonstrates advantages of dependency injection, there
is a disadvantage demonstration as well - creation of car requires additional
code for specification of dependencies. Nevertheless, this disadvantage could
be easily avoided by using a dependency injection framework for creation of
inversion of control container (IoC container).
Originally dependency injection pattern got popular in languages with static typing like Java.
Dependency injection is a principle that helps to achieve an inversion of control. A
dependency injection framework can significantly improve the flexibility of a language
with static typing. Implementation of a dependency injection framework for a language
with static typing is not something that one can do quickly. It will be a quite complex thing
to be done well. And will take time.
Example of creation of several inversion of control containers (IoC containers)
using :doc:`Dependency Injector <../index>`:
Python is an interpreted language with dynamic typing. There is an opinion that dependency
injection doesn't work for it as well as it does for Java. A lot of the flexibility is already
built-in. Also, there is an opinion that a dependency injection framework is something that
Python developer rarely needs. Python developers say that dependency injection can be implemented
easily using language fundamentals.
This page describes the advantages of applying dependency injection in Python. It
contains Python examples that show how to implement dependency injection. It demonstrates the usage
of the ``Dependency Injector`` framework, its container, ``Factory``, ``Singleton``,
and ``Configuration`` providers. The example shows how to use providers' overriding feature
of ``Dependency Injector`` for testing or re-configuring a project in different environments and
explains why it's better than monkey-patching.
What is dependency injection?
-----------------------------
Let's see what the dependency injection is.
Dependency injection is a principle that helps to decrease coupling and increase cohesion.
.. image:: images/coupling-cohesion.png
What is coupling and cohesion?
Coupling and cohesion are about how tough the components are tied.
- **High coupling**. If the coupling is high it's like using superglue or welding. No easy way
to disassemble.
- **High cohesion**. High cohesion is like using screws. Quite easy to disassemble and
re-assemble in a different way. It is an opposite to high coupling.
Cohesion often correlates with coupling. Higher cohesion usually leads to lower coupling and vice versa.
Low coupling brings flexibility. Your code becomes easier to change and test.
How to implement the dependency injection?
Objects do not create each other anymore. They provide a way to inject the dependencies instead.
Before:
.. code-block:: python
import os
class ApiClient:
def __init__(self) -> None:
self.api_key = os.getenv("API_KEY") # <-- dependency
self.timeout = int(os.getenv("TIMEOUT")) # <-- dependency
class Service:
def __init__(self) -> None:
self.api_client = ApiClient() # <-- dependency
def main() -> None:
service = Service() # <-- dependency
...
if __name__ == "__main__":
main()
After:
.. code-block:: python
import os
class ApiClient:
def __init__(self, api_key: str, timeout: int) -> None:
self.api_key = api_key # <-- dependency is injected
self.timeout = timeout # <-- dependency is injected
class Service:
def __init__(self, api_client: ApiClient) -> None:
self.api_client = api_client # <-- dependency is injected
def main(service: Service) -> None: # <-- dependency is injected
...
if __name__ == "__main__":
main(
service=Service(
api_client=ApiClient(
api_key=os.getenv("API_KEY"),
timeout=int(os.getenv("TIMEOUT")),
),
),
)
``ApiClient`` is decoupled from knowing where the options come from. You can read a key and a
timeout from a configuration file or even get them from a database.
``Service`` is decoupled from the ``ApiClient``. It does not create it anymore. You can provide a
stub or other compatible object.
Function ``main()`` is decoupled from ``Service``. It receives it as an argument.
Flexibility comes with a price.
Now you need to assemble and inject the objects like this:
.. code-block:: python
main(
service=Service(
api_client=ApiClient(
api_key=os.getenv("API_KEY"),
timeout=int(os.getenv("TIMEOUT")),
),
),
)
The assembly code might get duplicated and it'll become harder to change the application structure.
Here comes the ``Dependency Injector``.
What does the Dependency Injector do?
-------------------------------------
With the dependency injection pattern, objects lose the responsibility of assembling
the dependencies. The ``Dependency Injector`` absorbs that responsibility.
``Dependency Injector`` helps to assemble and inject the dependencies.
It provides a container and providers that help you with the objects assembly.
When you need an object you place a ``Provide`` marker as a default value of a
function argument. When you call this function, framework assembles and injects
the dependency.
.. code-block:: python
from dependency_injector import containers, providers
from dependency_injector.wiring import Provide, inject
class Container(containers.DeclarativeContainer):
config = providers.Configuration()
api_client = providers.Singleton(
ApiClient,
api_key=config.api_key,
timeout=config.timeout,
)
service = providers.Factory(
Service,
api_client=api_client,
)
@inject
def main(service: Service = Provide[Container.service]) -> None:
...
if __name__ == "__main__":
container = Container()
container.config.api_key.from_env("API_KEY", required=True)
container.config.timeout.from_env("TIMEOUT", as_=int, default=5)
container.wire(modules=[__name__])
main() # <-- dependency is injected automatically
with container.api_client.override(mock.Mock()):
main() # <-- overridden dependency is injected automatically
When you call the ``main()`` function the ``Service`` dependency is assembled and injected automatically.
When you do testing, you call the ``container.api_client.override()`` method to replace the real API
client with a mock. When you call ``main()``, the mock is injected.
You can override any provider with another provider.
It also helps you in a re-configuring project for different environments: replace an API client
with a stub on the dev or stage.
Objects assembling is consolidated in a container. Dependency injections are defined explicitly.
This makes it easier to understand and change how an application works.
Testing, Monkey-patching and dependency injection
-------------------------------------------------
The testability benefit is opposed to monkey-patching.
In Python, you can monkey-patch anything, anytime. The problem with monkey-patching is
that it's too fragile. The cause of it is that when you monkey-patch you do something that
wasn't intended to be done. You monkey-patch the implementation details. When implementation
changes the monkey-patching is broken.
With dependency injection, you patch the interface, not an implementation. This is a way more
stable approach.
Also, monkey-patching is way too dirty to be used outside of the testing code for
re-configuring the project for the different environments.
Conclusion
----------
Dependency injection provides you with three advantages:
- **Flexibility**. The components are loosely coupled. You can easily extend or change the
functionality of a system by combining the components in a different way. You even can do it on
the fly.
- **Testability**. Testing is easier because you can easily inject mocks instead of real objects
that use API or database, etc.
- **Clearness and maintainability**. Dependency injection helps you reveal the dependencies.
Implicit becomes explicit. And "Explicit is better than implicit" (PEP 20 - The Zen of Python).
You have all the components and dependencies defined explicitly in a container. This
provides an overview and control of the application structure. It is easier to understand and
change it.
Is it worth applying dependency injection in Python?
It depends on what you build. The advantages above are not too important if you use Python as a
scripting language. The picture is different when you use Python to create an application. The
larger the application the more significant the benefits.
Is it worth using a framework for applying dependency injection?
The complexity of the dependency injection pattern implementation in Python is
lower than in other languages but it's still in place. It doesn't mean you have to use a
framework but using a framework is beneficial because the framework is:
- Already implemented
- Tested on all platforms and versions of Python
- Documented
- Supported
- Other engineers are familiar with it
An advice at last:
- **Give it a try**. Dependency injection is counter-intuitive. Our nature is that
when we need something the first thought that comes to our mind is to go and get it. Dependency
injection is just like "Wait, I need to state a need instead of getting something right away".
It's like a little investment that will pay-off later. The advice is to just give it a try for
two weeks. This time will be enough for getting your own impression. If you don't like it you
won't lose too much.
- **Common sense first**. Use common sense when applying dependency injection. It is a good
principle, but not a silver bullet. If you do it too much you will reveal too many of the
implementation details. Experience comes with practice and time.
What's next?
------------
Choose one of the following as a next step:
- Look at the application examples:
- :ref:`application-single-container`
- :ref:`application-multiple-containers`
- :ref:`decoupled-packages`
- :ref:`boto3-example`
- :ref:`django-example`
- :ref:`flask-example`
- :ref:`flask-blueprints-example`
- :ref:`aiohttp-example`
- :ref:`sanic-example`
- :ref:`fastapi-example`
- :ref:`fastapi-redis-example`
- :ref:`fastapi-sqlalchemy-example`
- Pass the tutorials:
- :ref:`flask-tutorial`
- :ref:`aiohttp-tutorial`
- :ref:`asyncio-daemon-tutorial`
- :ref:`cli-tutorial`
- Know more about the ``Dependency Injector`` :ref:`key-features`
- Know more about the :ref:`providers`
- Know more about the :ref:`wiring`
- Go to the :ref:`contents`
.. literalinclude:: ../../examples/miniapps/engines_cars/example_ioc_containers.py
:language: python
:linenos:
Useful links
------------
~~~~~~~~~~~~
A few useful links related to a dependency injection design pattern for further reading:
There are some useful links related to dependency injection design pattern
that could be used for further reading:
+ https://en.wikipedia.org/wiki/Dependency_injection
+ https://martinfowler.com/articles/injection.html
+ https://github.com/ets-labs/python-dependency-injector
+ https://pypi.org/project/dependency-injector/
.. include:: ../sponsor.rst
.. disqus::

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

View File

@ -3,16 +3,18 @@ Introduction
.. meta::
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control
:description: Current section of the documentation is provides an
overview of the dependency injection, inversion of
control and Dependency Injector framework.
:description: Current section of documentation is designed to give some
overview about dependency injection pattern, inversion of
control principle and "Dependency Injector" framework.
The current section of the documentation provides an overview of the
dependency injection, inversion of control, and the ``Dependency Injector`` framework.
Current section of documentation is designed to give some overview about
dependency injection pattern, inversion of control principle and
*Dependency Injector* framework.
.. toctree::
:maxdepth: 2
what_is_di
di_in_python
key_features
installation
structure

View File

@ -1,42 +0,0 @@
Installation
============
``Dependency Injector`` is available on `PyPI <https://pypi.org/project/dependency-injector/>`_.
To install the latest version you can use ``pip``:
.. code-block:: bash
pip install dependency-injector
Some modules of the ``Dependency Injector`` are implemented as C extensions.
``Dependency Injector`` is distributed as a pre-compiled wheels. Wheels are
available for all supported Python versions on Linux, Windows, and MacOS.
Linux distribution uses `manylinux <https://github.com/pypa/manylinux>`_.
If there is no appropriate wheel for your environment (Python version and OS)
installer will compile the package from sources on your machine. You'll need
a C compiler and Python header files.
To verify the installed version:
.. code-block:: bash
>>> import dependency_injector
>>> dependency_injector.__version__
'4.39.0'
.. note::
When adding ``Dependency Injector`` to ``pyproject.toml`` or ``requirements.txt``
don't forget to pin the version to the current major:
.. code-block:: bash
dependency-injector>=4.0,<5.0
*The next major version can be incompatible.*
All releases are available on the `PyPI release history page <https://pypi.org/project/dependency-injector/#history>`_.
Each release has an appropriate tag. The tags are available on the
`GitHub releases page <https://github.com/ets-labs/python-dependency-injector/releases>`_.
.. disqus::

View File

@ -1,43 +1,49 @@
.. _key-features:
Key features
------------
Key features of Dependency Injector
-----------------------------------
.. meta::
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control
:description: This article describes key features of the Dependency Injector
framework.
:description: This article describes key features of "Dependency Injector"
framework. It also provides some cases and recommendations
about usage of "Dependency Injector" framework.
Key features of the ``Dependency Injector``:
*Dependency Injector* is a dependency injection framework for Python projects.
It was designed to be unified, developer-friendly tool for managing any kind
of Python objects and their dependencies in formal, pretty way.
- **Providers**. Provides ``Factory``, ``Singleton``, ``Callable``, ``Coroutine``, ``Object``,
``List``, ``Dict``, ``Configuration``, ``Resource``, ``Dependency``, and ``Selector`` providers
that help assemble your objects. See :ref:`providers`.
- **Overriding**. Can override any provider by another provider on the fly. This helps in testing
and configuring dev/stage environment to replace API clients with stubs etc. See
:ref:`provider-overriding`.
- **Configuration**. Reads configuration from ``yaml``, ``ini``, and ``json`` files, ``pydantic`` settings,
environment variables, and dictionaries. See :ref:`configuration-provider`.
- **Resources**. Helps with initialization and configuring of logging, event loop, thread
or process pool, etc. Can be used for per-function execution scope in tandem with wiring.
See :ref:`resource-provider`.
- **Containers**. Provides declarative and dynamic containers. See :ref:`containers`.
- **Wiring**. Injects dependencies into functions and methods. Helps integrate with
other frameworks: Django, Flask, Aiohttp, Sanic, FastAPI, etc. See :ref:`wiring`.
- **Asynchronous**. Supports asynchronous injections. See :ref:`async-injections`.
- **Typing**. Provides typing stubs, ``mypy``-friendly. See :ref:`provider-typing`.
- **Performance**. Fast. Written in ``Cython``.
- **Maturity**. Mature and production-ready. Well-tested, documented, and supported.
*Dependency Injector* framework key features are:
The framework stands on the `PEP20 (The Zen of Python) <https://www.python.org/dev/peps/pep-0020/>`_ principle:
+ Easy, smart, and pythonic style.
+ Obvious and clear structure.
+ Extensibility and flexibility.
+ High performance.
+ Memory efficiency.
+ Thread safety.
+ Documented.
+ Semantically versioned.
.. code-block:: text
*Dependency Injector* framework could be used in different application types:
Explicit is better than implicit
+ Web applications based on Flask, Django or any other web framework.
+ Asynchronous applications based on asyncio, Tornado and Twisted.
+ Standalone frameworks and libraries.
+ GUI applications.
You need to specify how to assemble and where to inject the dependencies explicitly.
*Dependency Injector* framework could be integrated on different project
stages:
+ It could be used in the beginning of development of new applications.
+ It could be integrated into applications that are in active development
stage.
+ It could be used for refactoring of legacy applications.
Components of *Dependency Injector* framework could be used:
+ In composition with each other.
+ Separately between each other.
Main idea of *Dependency Injector* framework is to be useful tool for the
right thing.
The power of the framework is in its simplicity.
``Dependency Injector`` is a simple tool for the powerful concept.
.. disqus::

View File

@ -0,0 +1,50 @@
Structure of Dependency Injector
--------------------------------
.. meta::
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control
:description: This article describes "Dependency Injector" framework
components and their interaction between each other.
Providers and containers are the former components of
the framework.
Current section describes *Dependency Injector* main entities and their
interaction between each other.
.. image:: /images/internals.png
:width: 100%
:align: center
There are 2 main entities: providers & containers.
Providers
~~~~~~~~~
Providers are strategies of accessing objects. For example,
:py:class:`dependency_injector.providers.Factory` creates new instance
of provided class every time it is called.
:py:class:`dependency_injector.providers.Singleton` creates provided
instance once and returns it on every next call. Base class is -
:py:class:`dependency_injector.providers.Provider`.
Providers could be:
+ Injected into each other.
+ Overridden by each other.
+ Extended.
Containers
~~~~~~~~~~
Containers are collections of providers. They are used for grouping
of providers by some principles. Base class is -
:py:class:`dependency_injector.containers.DeclarativeContainer`.
Containers could be:
+ Overridden by each other.
+ Copied from each other.
+ Extended.
.. disqus::

View File

@ -0,0 +1,128 @@
What is dependency injection and inversion of control?
------------------------------------------------------
.. meta::
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control
:description: This article provides definition of dependency injection,
inversion of control and dependency inversion. It contains
example code in Python that is refactored to be following
inversion of control principle.
Definition
~~~~~~~~~~
Wikipedia provides quite good definitions of dependency injection pattern
and related principles:
.. glossary::
`Dependency injection`_
In software engineering, dependency injection is a software design
pattern that implements inversion of control for resolving
dependencies. A dependency is an object that can be used (a service).
An injection is the passing of a dependency to a dependent object (a
client) that would use it. The service is made part of the client's
state. Passing the service to the client, rather than allowing a
client to build or find the service, is the fundamental requirement of
the pattern.
Dependency injection allows a program design to follow the dependency
inversion principle. The client delegates to external code (the
injector) the responsibility of providing its dependencies. The client
is not allowed to call the injector code. It is the injecting code
that constructs the services and calls the client to inject them. This
means the client code does not need to know about the injecting code.
The client does not need to know how to construct the services. The
client does not need to know which actual services it is using. The
client only needs to know about the intrinsic interfaces of the
services because these define how the client may use the services.
This separates the responsibilities of use and construction.
`Inversion of control`_
In software engineering, inversion of control (IoC) describes a design
in which custom-written portions of a computer program receive the
flow of control from a generic, reusable library. A software
architecture with this design inverts control as compared to
traditional procedural programming: in traditional programming, the
custom code that expresses the purpose of the program calls into
reusable libraries to take care of generic tasks, but with inversion
of control, it is the reusable code that calls into the custom, or
task-specific, code.
Inversion of control is used to increase modularity of the program and
make it extensible, and has applications in object-oriented
programming and other programming paradigms. The term was popularized
by Robert C. Martin and Martin Fowler.
The term is related to, but different from, the dependency inversion
principle, which concerns itself with decoupling dependencies between
high-level and low-level layers through shared abstractions.
`Dependency inversion`_
In object-oriented programming, the dependency inversion principle
refers to a specific form of decoupling software modules. When
following this principle, the conventional dependency relationships
established from high-level, policy-setting modules to low-level,
dependency modules are reversed, thus rendering high-level modules
independent of the low-level module implementation details. The
principle states:
+ High-level modules should not depend on low-level modules.
Both should depend on abstractions.
+ Abstractions should not depend on details.
Details should depend on abstractions.
The principle inverts the way some people may think about
object-oriented design, dictating that both high- and low-level
objects must depend on the same abstraction.
Example
~~~~~~~
Let's go through the code of ``example.py``:
.. literalinclude:: ../../examples/di_demo/example.py
:language: python
:linenos:
At some point, things defined above mean, that the code from ``example.py``,
could look different, like in ``example_di.py``:
.. literalinclude:: ../../examples/di_demo/example_di.py
:language: python
:linenos:
Best explanation, ever
~~~~~~~~~~~~~~~~~~~~~~
Some times ago `user198313`_ posted awesome `question`_ about dependency
injection on `StackOverflow`_:
.. note::
How to explain dependency injection to a 5-year-old?
And `John Munsch`_ provided absolutely Great answer:
.. note::
When you go and get things out of the refrigerator for yourself, you can
cause problems. You might leave the door open, you might get something
Mommy or Daddy doesn't want you to have. You might even be looking for
something we don't even have or which has expired.
What you should be doing is stating a need, "I need something to drink
with lunch," and then we will make sure you have something when you sit
down to eat.
.. disqus::
.. _Dependency injection: http://en.wikipedia.org/wiki/Dependency_injection
.. _Inversion of control: https://en.wikipedia.org/wiki/Inversion_of_control
.. _Dependency inversion: https://en.wikipedia.org/wiki/Dependency_inversion_principle
.. _StackOverflow: http://stackoverflow.com/
.. _question: http://stackoverflow.com/questions/1638919/how-to-explain-dependency-injection-to-a-5-year-old/1639186
.. _user198313: http://stackoverflow.com/users/198313/user198313
.. _John Munsch: http://stackoverflow.com/users/31899/john-munsch

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,12 @@
Feedback
========
To post a question, bug report, a feature proposal or get some help open a
`Github Issue <https://github.com/ets-labs/python-dependency-injector/issues>`_ or leave a comment
below.
Feel free to post questions, bugs, feature requests, proposals etc. on
*Dependency Injector* GitHub Issues:
https://github.com/ets-labs/python-dependency-injector/issues
Your feedback is quite important!
.. disqus::

Some files were not shown because too many files have changed in this diff Show More