Compare commits
No commits in common. "master" and "0.9.3" have entirely different histories.
3
.coveragerc
Normal file
|
@ -0,0 +1,3 @@
|
|||
[run]
|
||||
include = dependency_injector/*
|
||||
omit = tests/*
|
|
@ -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"
|
1
.github/FUNDING.yml
vendored
|
@ -1 +0,0 @@
|
|||
github: rmk135
|
113
.github/workflows/publishing.yml
vendored
|
@ -1,113 +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-2019, macos-14]
|
||||
env:
|
||||
CIBW_SKIP: cp27-*
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Build wheels
|
||||
uses: pypa/cibuildwheel@v2.20.0
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: cibw-wheels-x86-${{ matrix.os }}-${{ strategy.job-index }}
|
||||
path: ./wheelhouse/*.whl
|
||||
|
||||
publish:
|
||||
name: Publish on PyPI
|
||||
needs: [build-sdist, build-wheels]
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: cibw-*
|
||||
path: dist
|
||||
merge-multiple: true
|
||||
- uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
user: __token__
|
||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||
# For publishing to Test PyPI, uncomment next two lines:
|
||||
# password: ${{ secrets.TEST_PYPI_API_TOKEN }}
|
||||
# repository_url: https://test.pypi.org/legacy/
|
||||
|
||||
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 }}
|
82
.github/workflows/tests-and-linters.yml
vendored
|
@ -1,82 +0,0 @@
|
|||
name: Tests and linters
|
||||
|
||||
on: [push, pull_request, workflow_dispatch]
|
||||
|
||||
jobs:
|
||||
|
||||
tests-on-legacy-versions:
|
||||
name: Run tests on legacy versions
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.7]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- run: pip install tox
|
||||
- run: tox
|
||||
env:
|
||||
TOXENV: ${{ matrix.python-version }}
|
||||
|
||||
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:
|
||||
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 'cython>=3,<4'
|
||||
- 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 }}
|
22
.gitignore
vendored
|
@ -2,6 +2,9 @@
|
|||
__pycache__/
|
||||
*.py[cod]
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
env/
|
||||
|
@ -30,13 +33,12 @@ pip-log.txt
|
|||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
reports/
|
||||
htmlcov/
|
||||
.tox/
|
||||
.coverage
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
.hypothesis/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
|
@ -55,21 +57,13 @@ target/
|
|||
.idea/
|
||||
|
||||
# Virtualenv
|
||||
venv*/
|
||||
venv/
|
||||
|
||||
# SQLite
|
||||
*.db
|
||||
|
||||
# JointJS Experiments
|
||||
jointjs/
|
||||
|
||||
# Vim Rope
|
||||
.ropeproject/
|
||||
|
||||
# Cython artifacts
|
||||
src/**/*.c
|
||||
src/**/*.h
|
||||
src/**/*.so
|
||||
src/**/*.html
|
||||
|
||||
# Workspace for samples
|
||||
.workspace/
|
||||
|
||||
.vscode/
|
||||
|
|
49
.pylintrc
Normal 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,test
|
||||
|
||||
[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
|
17
.travis.yml
Normal file
|
@ -0,0 +1,17 @@
|
|||
language: python
|
||||
install:
|
||||
- pip install tox
|
||||
script:
|
||||
- tox
|
||||
env:
|
||||
- TOXENV=coveralls
|
||||
- TOXENV=pylint
|
||||
- TOXENV=flake8
|
||||
- TOXENV=pep257
|
||||
- TOXENV=py26
|
||||
- TOXENV=py27
|
||||
- TOXENV=py32
|
||||
- TOXENV=py33
|
||||
- TOXENV=py34
|
||||
- TOXENV=pypy
|
||||
- TOXENV=pypy3
|
|
@ -1,24 +0,0 @@
|
|||
Dependency Injector Contributors
|
||||
================================
|
||||
|
||||
+ Roman Mogylatov (rmk135)
|
||||
+ Konstantin vz'One Enchant (sirkonst)
|
||||
+ Terrence Brannon (metaperl)
|
||||
+ Stanislav Lobanov (asyncee)
|
||||
+ James Lafa (jameslafa)
|
||||
+ Vlad Ghita (vlad-ghita)
|
||||
+ Jeroen Rietveld (jeroenrietveld)
|
||||
+ 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)
|
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2024, Roman Mogylatov
|
||||
Copyright (c) 2015, Roman Mogilatov
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
@ -25,3 +25,4 @@ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
|
@ -1,9 +1,6 @@
|
|||
recursive-include src/dependency_injector *.py* *.c
|
||||
recursive-include tests *.py
|
||||
include dependency_injector/*
|
||||
include README.rst
|
||||
include CONTRIBUTORS.rst
|
||||
include LICENSE.rst
|
||||
include LICENSE
|
||||
include VERSION
|
||||
include requirements.txt
|
||||
include setup.py
|
||||
include tox.ini
|
||||
include py.typed
|
||||
|
|
65
Makefile
|
@ -1,65 +0,0 @@
|
|||
VERSION := $(shell python setup.py --version)
|
||||
|
||||
export COVERAGE_RCFILE := pyproject.toml
|
||||
|
||||
clean:
|
||||
# Clean sources
|
||||
find src -name '*.py[cod]' -delete
|
||||
find src -name '__pycache__' -delete
|
||||
find src -name '*.c' -delete
|
||||
find src -name '*.h' -delete
|
||||
find src -name '*.so' -delete
|
||||
find src -name '*.html' -delete
|
||||
# Clean tests
|
||||
find tests -name '*.py[co]' -delete
|
||||
find tests -name '__pycache__' -delete
|
||||
# Clean examples
|
||||
find examples -name '*.py[co]' -delete
|
||||
find examples -name '__pycache__' -delete
|
||||
|
||||
build: clean
|
||||
# Compile C extensions
|
||||
python setup.py build_ext --inplace
|
||||
# Move all Cython html reports
|
||||
mkdir -p reports/cython/
|
||||
find src -name '*.html' -exec mv {} reports/cython/ \;
|
||||
|
||||
docs-live:
|
||||
sphinx-autobuild docs docs/_build/html
|
||||
|
||||
install: uninstall clean build
|
||||
pip install -ve .
|
||||
|
||||
uninstall:
|
||||
- pip uninstall -y -q dependency-injector 2> /dev/null
|
||||
|
||||
test:
|
||||
# Unit tests with coverage report
|
||||
coverage erase
|
||||
coverage run -m pytest -c tests/.configs/pytest.ini
|
||||
coverage report
|
||||
coverage html
|
||||
|
||||
check:
|
||||
flake8 src/dependency_injector/
|
||||
flake8 examples/
|
||||
|
||||
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:
|
||||
# Merge release to master branch
|
||||
git checkout master
|
||||
git merge --no-ff release/$(VERSION) -m "Merge branch 'release/$(VERSION)' into master"
|
||||
git push origin master
|
||||
# Create and upload tag
|
||||
git tag -a $(VERSION) -m 'version $(VERSION)'
|
||||
git push --tags
|
303
README.rst
|
@ -1,230 +1,153 @@
|
|||
.. 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 framework for Python projects.
|
||||
|
||||
.. 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
|
||||
+---------------------------------------+-------------------------------------------------------------------------------+
|
||||
| *PyPi* | .. image:: https://img.shields.io/pypi/v/dependency_injector.svg |
|
||||
| | :target: https://pypi.python.org/pypi/dependency_injector/ |
|
||||
| | :alt: Latest Version |
|
||||
| | .. image:: https://img.shields.io/pypi/dm/dependency_injector.svg |
|
||||
| | :target: https://pypi.python.org/pypi/dependency_injector/ |
|
||||
| | :alt: Downloads |
|
||||
| | .. image:: https://img.shields.io/pypi/l/dependency_injector.svg |
|
||||
| | :target: https://pypi.python.org/pypi/dependency_injector/ |
|
||||
| | :alt: License |
|
||||
+---------------------------------------+-------------------------------------------------------------------------------+
|
||||
| *Python versions and implementations* | .. image:: https://img.shields.io/pypi/pyversions/dependency_injector.svg |
|
||||
| | :target: https://pypi.python.org/pypi/dependency_injector/ |
|
||||
| | :alt: Supported Python versions |
|
||||
| | .. image:: https://img.shields.io/pypi/implementation/dependency_injector.svg |
|
||||
| | :target: https://pypi.python.org/pypi/dependency_injector/ |
|
||||
| | :alt: Supported Python implementations |
|
||||
+---------------------------------------+-------------------------------------------------------------------------------+
|
||||
| *Builds and tests coverage* | .. image:: https://travis-ci.org/rmk135/dependency_injector.svg?branch=master |
|
||||
| | :target: https://travis-ci.org/rmk135/dependency_injector |
|
||||
| | :alt: Build Status |
|
||||
| | .. image:: https://coveralls.io/repos/rmk135/dependency_injector/badge.svg |
|
||||
| | :target: https://coveralls.io/r/rmk135/dependency_injector |
|
||||
| | :alt: Coverage Status |
|
||||
+---------------------------------------+-------------------------------------------------------------------------------+
|
||||
|
||||
.. 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
|
||||
*Dependency Injector* is a dependency injection framework for Python projects.
|
||||
It was designed to be unified, developer's friendly tool for managing any kind
|
||||
of Python objects and their dependencies in formal, pretty way.
|
||||
|
||||
.. image:: https://pepy.tech/badge/dependency-injector
|
||||
:target: https://pepy.tech/project/dependency-injector
|
||||
:alt: Downloads
|
||||
Below is a list of some key features and points of *Dependency Injector*
|
||||
framework:
|
||||
|
||||
.. image:: https://pepy.tech/badge/dependency-injector/month
|
||||
:target: https://pepy.tech/project/dependency-injector
|
||||
:alt: Downloads
|
||||
- Easy, smart, pythonic style.
|
||||
- Obvious, clear structure.
|
||||
- Memory efficiency.
|
||||
- Thread safety.
|
||||
- Semantic versioning.
|
||||
|
||||
.. 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>`_.
|
||||
Main idea of *Dependency Injector* is to keep dependencies under control.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
The package is available on the `PyPi`_::
|
||||
*Dependency Injector* library is available on PyPi_::
|
||||
|
||||
pip install dependency-injector
|
||||
pip install dependency_injector
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
The documentation is available `here <https://python-dependency-injector.ets-labs.org/>`_.
|
||||
*Dependency Injector* documentation is hosted on ReadTheDocs:
|
||||
|
||||
- `Stable version`_
|
||||
- `Latest version`_
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
Choose one of the following:
|
||||
.. code-block:: python
|
||||
|
||||
- `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>`_
|
||||
"""Concept example of `Dependency Injector`."""
|
||||
|
||||
Tutorials
|
||||
---------
|
||||
import sqlite3
|
||||
import dependency_injector as di
|
||||
|
||||
Choose one of the following:
|
||||
|
||||
- `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>`_
|
||||
class ObjectA(object):
|
||||
|
||||
Concept
|
||||
-------
|
||||
"""Example class ObjectA, that has dependency on database."""
|
||||
|
||||
The framework stands on the `PEP20 (The Zen of Python) <https://www.python.org/dev/peps/pep-0020/>`_ principle:
|
||||
def __init__(self, db):
|
||||
"""Initializer."""
|
||||
self.db = db
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
Explicit is better than implicit
|
||||
class ObjectB(object):
|
||||
|
||||
You need to specify how to assemble and where to inject the dependencies explicitly.
|
||||
"""Example class ObjectB, that has dependencies on ObjectA and database."""
|
||||
|
||||
The power of the framework is in its simplicity.
|
||||
``Dependency Injector`` is a simple tool for the powerful concept.
|
||||
def __init__(self, a, db):
|
||||
"""Initializer."""
|
||||
self.a = a
|
||||
self.db = db
|
||||
|
||||
Frequently asked questions
|
||||
--------------------------
|
||||
|
||||
What is dependency injection?
|
||||
- dependency injection is a principle that decreases coupling and increases cohesion
|
||||
class Catalog(di.AbstractCatalog):
|
||||
|
||||
Why should I do the dependency injection?
|
||||
- your code becomes more flexible, testable, and clear 😎
|
||||
"""Catalog of providers."""
|
||||
|
||||
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
|
||||
database = di.Singleton(sqlite3.Connection,
|
||||
database=':memory:')
|
||||
""":type: (di.Provider) -> sqlite3.Connection"""
|
||||
|
||||
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
|
||||
object_a_factory = di.Factory(ObjectA,
|
||||
db=database)
|
||||
""":type: (di.Provider) -> ObjectA"""
|
||||
|
||||
Have a question?
|
||||
- Open a `Github Issue <https://github.com/ets-labs/python-dependency-injector/issues>`_
|
||||
object_b_factory = di.Factory(ObjectB,
|
||||
a=object_a_factory,
|
||||
db=database)
|
||||
""":type: (di.Provider) -> ObjectB"""
|
||||
|
||||
Found a bug?
|
||||
- Open a `Github Issue <https://github.com/ets-labs/python-dependency-injector/issues>`_
|
||||
|
||||
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``
|
||||
# Catalog static provides.
|
||||
a1, a2 = Catalog.object_a_factory(), Catalog.object_a_factory()
|
||||
b1, b2 = Catalog.object_b_factory(), Catalog.object_b_factory()
|
||||
|
||||
Want to contribute?
|
||||
- |fork| Fork the project
|
||||
- |pull| Open a pull request to the ``develop`` branch
|
||||
assert a1 is not a2
|
||||
assert b1 is not b2
|
||||
assert a1.db is a2.db is b1.db is b2.db is Catalog.database()
|
||||
|
||||
.. _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
|
||||
# Example of inline injections.
|
||||
@di.inject(a=Catalog.object_a_factory)
|
||||
@di.inject(b=Catalog.object_b_factory)
|
||||
@di.inject(database=Catalog.database)
|
||||
def example(a, b, database):
|
||||
"""Example callback."""
|
||||
assert a.db is b.db is database is Catalog.database()
|
||||
|
||||
|
||||
example()
|
||||
|
||||
You can get more *Dependency Injector* examples in ``/examples`` directory on
|
||||
GitHub:
|
||||
|
||||
https://github.com/rmk135/dependency_injector
|
||||
|
||||
|
||||
Feedback
|
||||
--------
|
||||
|
||||
Feel free to post questions, bugs, feature requests, proposals etc. on
|
||||
*Dependency Injector* GitHub Issues:
|
||||
|
||||
https://github.com/rmk135/dependency_injector/issues
|
||||
|
||||
Your feedback is quite important!
|
||||
|
||||
|
||||
.. _PyPi: https://pypi.python.org/pypi/dependency_injector
|
||||
.. _Stable version: http://dependency_injector.readthedocs.org/en/stable/
|
||||
.. _Latest version: http://dependency_injector.readthedocs.org/en/latest/
|
||||
.. _SLOC: http://en.wikipedia.org/wiki/Source_lines_of_code
|
||||
.. _SOLID: http://en.wikipedia.org/wiki/SOLID_%28object-oriented_design%29
|
||||
.. _IoC: http://en.wikipedia.org/wiki/Inversion_of_control
|
||||
.. _dependency injection: http://en.wikipedia.org/wiki/Dependency_injection
|
||||
|
|
71
dependency_injector/__init__.py
Normal file
|
@ -0,0 +1,71 @@
|
|||
"""Dependency injector."""
|
||||
|
||||
from .catalog import AbstractCatalog
|
||||
from .catalog import override
|
||||
|
||||
from .providers import Provider
|
||||
from .providers import Delegate
|
||||
from .providers import Factory
|
||||
from .providers import Singleton
|
||||
from .providers import ExternalDependency
|
||||
from .providers import Class
|
||||
from .providers import Object
|
||||
from .providers import Function
|
||||
from .providers import Value
|
||||
from .providers import Callable
|
||||
from .providers import Config
|
||||
|
||||
from .injections import Injection
|
||||
from .injections import KwArg
|
||||
from .injections import Attribute
|
||||
from .injections import Method
|
||||
from .injections import inject
|
||||
|
||||
from .utils import is_provider
|
||||
from .utils import ensure_is_provider
|
||||
from .utils import is_injection
|
||||
from .utils import ensure_is_injection
|
||||
from .utils import is_kwarg_injection
|
||||
from .utils import is_attribute_injection
|
||||
from .utils import is_method_injection
|
||||
|
||||
from .errors import Error
|
||||
|
||||
|
||||
__all__ = (
|
||||
# Catalogs
|
||||
'AbstractCatalog',
|
||||
'override',
|
||||
|
||||
# Providers
|
||||
'Provider',
|
||||
'Delegate',
|
||||
'Factory',
|
||||
'Singleton',
|
||||
'ExternalDependency',
|
||||
'Class',
|
||||
'Object',
|
||||
'Function',
|
||||
'Value',
|
||||
'Callable',
|
||||
'Config',
|
||||
|
||||
# Injections
|
||||
'Injection',
|
||||
'KwArg',
|
||||
'Attribute',
|
||||
'Method',
|
||||
'inject',
|
||||
|
||||
# Utils
|
||||
'is_provider',
|
||||
'ensure_is_provider',
|
||||
'is_injection',
|
||||
'ensure_is_injection',
|
||||
'is_kwarg_injection',
|
||||
'is_attribute_injection',
|
||||
'is_method_injection',
|
||||
|
||||
# Errors
|
||||
'Error',
|
||||
)
|
75
dependency_injector/catalog.py
Normal file
|
@ -0,0 +1,75 @@
|
|||
"""Catalog module."""
|
||||
|
||||
import six
|
||||
|
||||
from .errors import Error
|
||||
from .utils import is_provider
|
||||
|
||||
|
||||
class CatalogMetaClass(type):
|
||||
|
||||
"""Providers catalog meta class."""
|
||||
|
||||
def __new__(mcs, class_name, bases, attributes):
|
||||
"""Meta class factory."""
|
||||
providers = dict()
|
||||
new_attributes = dict()
|
||||
for name, value in six.iteritems(attributes):
|
||||
if is_provider(value):
|
||||
providers[name] = value
|
||||
new_attributes[name] = value
|
||||
|
||||
cls = type.__new__(mcs, class_name, bases, new_attributes)
|
||||
cls.providers = cls.providers.copy()
|
||||
cls.providers.update(providers)
|
||||
return cls
|
||||
|
||||
|
||||
@six.add_metaclass(CatalogMetaClass)
|
||||
class AbstractCatalog(object):
|
||||
|
||||
"""Abstract providers catalog."""
|
||||
|
||||
providers = dict()
|
||||
|
||||
__slots__ = ('_used_providers',)
|
||||
|
||||
def __init__(self, *used_providers):
|
||||
"""Initializer."""
|
||||
self._used_providers = set(used_providers)
|
||||
|
||||
def __getattribute__(self, item):
|
||||
"""Return providers."""
|
||||
attribute = super(AbstractCatalog, self).__getattribute__(item)
|
||||
if item in ('providers', '_used_providers',):
|
||||
return attribute
|
||||
|
||||
if attribute not in self._used_providers:
|
||||
raise Error('Provider \'{0}\' '.format(item) +
|
||||
'is not listed in dependencies')
|
||||
return attribute
|
||||
|
||||
@classmethod
|
||||
def filter(cls, provider_type):
|
||||
"""Return dict of providers, that are instance of provided type."""
|
||||
return dict((name, provider)
|
||||
for name, provider in six.iteritems(cls.providers)
|
||||
if isinstance(provider, provider_type))
|
||||
|
||||
@classmethod
|
||||
def override(cls, overriding):
|
||||
"""Override current catalog providers by overriding catalog providers.
|
||||
|
||||
:type overriding: AbstractCatalog
|
||||
"""
|
||||
for name, provider in six.iteritems(overriding.providers):
|
||||
cls.providers[name].override(provider)
|
||||
|
||||
|
||||
def override(catalog):
|
||||
"""Catalog overriding decorator."""
|
||||
def decorator(overriding_catalog):
|
||||
"""Overriding decorator."""
|
||||
catalog.override(overriding_catalog)
|
||||
return overriding_catalog
|
||||
return decorator
|
6
dependency_injector/errors.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
"""Errors module."""
|
||||
|
||||
|
||||
class Error(Exception):
|
||||
|
||||
"""Base error."""
|
79
dependency_injector/injections.py
Normal file
|
@ -0,0 +1,79 @@
|
|||
"""Injections module."""
|
||||
|
||||
import six
|
||||
|
||||
from .utils import is_provider
|
||||
from .utils import ensure_is_injection
|
||||
from .utils import get_injectable_kwargs
|
||||
|
||||
|
||||
class Injection(object):
|
||||
|
||||
"""Base injection class."""
|
||||
|
||||
__IS_INJECTION__ = True
|
||||
__slots__ = ('name', 'injectable')
|
||||
|
||||
def __init__(self, name, injectable):
|
||||
"""Initializer."""
|
||||
self.name = name
|
||||
self.injectable = injectable
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
"""Return injectable value."""
|
||||
if is_provider(self.injectable):
|
||||
return self.injectable()
|
||||
return self.injectable
|
||||
|
||||
|
||||
class KwArg(Injection):
|
||||
|
||||
"""Keyword argument injection."""
|
||||
|
||||
__IS_KWARG_INJECTION__ = True
|
||||
|
||||
|
||||
class Attribute(Injection):
|
||||
|
||||
"""Attribute injection."""
|
||||
|
||||
__IS_ATTRIBUTE_INJECTION__ = True
|
||||
|
||||
|
||||
class Method(Injection):
|
||||
|
||||
"""Method injection."""
|
||||
|
||||
__IS_METHOD_INJECTION__ = True
|
||||
|
||||
|
||||
def inject(*args, **kwargs):
|
||||
"""Dependency injection decorator.
|
||||
|
||||
:type injection: Injection
|
||||
:return: (callable) -> (callable)
|
||||
"""
|
||||
injections = tuple(KwArg(name, value)
|
||||
for name, value in six.iteritems(kwargs))
|
||||
if args:
|
||||
injections += tuple(ensure_is_injection(injection)
|
||||
for injection in args)
|
||||
|
||||
def decorator(callback):
|
||||
"""Dependency injection decorator."""
|
||||
if hasattr(callback, '_injections'):
|
||||
callback._injections += injections
|
||||
return callback
|
||||
|
||||
@six.wraps(callback)
|
||||
def decorated(*args, **kwargs):
|
||||
"""Decorated with dependency injection callback."""
|
||||
return callback(*args,
|
||||
**get_injectable_kwargs(kwargs,
|
||||
decorated._injections))
|
||||
|
||||
decorated._injections = injections
|
||||
|
||||
return decorated
|
||||
return decorator
|
339
dependency_injector/providers.py
Normal file
|
@ -0,0 +1,339 @@
|
|||
"""Providers module."""
|
||||
|
||||
import six
|
||||
|
||||
from .injections import KwArg
|
||||
|
||||
from .utils import ensure_is_provider
|
||||
from .utils import is_kwarg_injection
|
||||
from .utils import is_attribute_injection
|
||||
from .utils import is_method_injection
|
||||
from .utils import get_injectable_kwargs
|
||||
from .utils import GLOBAL_LOCK
|
||||
|
||||
from .errors import Error
|
||||
|
||||
|
||||
class Provider(object):
|
||||
|
||||
"""Base provider class."""
|
||||
|
||||
__IS_PROVIDER__ = True
|
||||
__slots__ = ('_overridden',)
|
||||
|
||||
def __init__(self):
|
||||
"""Initializer."""
|
||||
self._overridden = None
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
"""Return provided instance."""
|
||||
if self._overridden:
|
||||
return self.last_overriding(*args, **kwargs)
|
||||
return self._provide(*args, **kwargs)
|
||||
|
||||
def _provide(self, *args, **kwargs):
|
||||
"""Providing strategy implementation.
|
||||
|
||||
Abstract protected method that implements providing strategy of
|
||||
particular provider. Current method is called every time when not
|
||||
overridden provider is called. Need to be overridden in subclasses.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def delegate(self):
|
||||
"""Return provider's delegate."""
|
||||
return Delegate(self)
|
||||
|
||||
def override(self, provider):
|
||||
"""Override provider with another provider."""
|
||||
if not self._overridden:
|
||||
self._overridden = (ensure_is_provider(provider),)
|
||||
else:
|
||||
self._overridden += (ensure_is_provider(provider),)
|
||||
|
||||
@property
|
||||
def is_overridden(self):
|
||||
"""Check if provider is overridden by another provider."""
|
||||
return bool(self._overridden)
|
||||
|
||||
@property
|
||||
def last_overriding(self):
|
||||
"""Return last overriding provider."""
|
||||
try:
|
||||
return self._overridden[-1]
|
||||
except (TypeError, IndexError):
|
||||
raise Error('Provider {0} is not overridden'.format(str(self)))
|
||||
|
||||
def reset_last_overriding(self):
|
||||
"""Reset last overriding provider."""
|
||||
if not self._overridden:
|
||||
raise Error('Provider {0} is not overridden'.format(str(self)))
|
||||
self._overridden = self._overridden[:-1]
|
||||
|
||||
def reset_override(self):
|
||||
"""Reset all overriding providers."""
|
||||
self._overridden = None
|
||||
|
||||
|
||||
class Delegate(Provider):
|
||||
|
||||
"""Provider's delegate."""
|
||||
|
||||
__slots__ = ('_delegated',)
|
||||
|
||||
def __init__(self, delegated):
|
||||
"""Initializer.
|
||||
|
||||
:type delegated: Provider
|
||||
"""
|
||||
self._delegated = ensure_is_provider(delegated)
|
||||
super(Delegate, self).__init__()
|
||||
|
||||
def _provide(self, *args, **kwargs):
|
||||
"""Return provided instance."""
|
||||
return self._delegated
|
||||
|
||||
|
||||
class Factory(Provider):
|
||||
|
||||
"""Factory provider.
|
||||
|
||||
Factory provider creates new instance of specified class on every call.
|
||||
"""
|
||||
|
||||
__slots__ = ('_provides', '_kwargs', '_attributes', '_methods')
|
||||
|
||||
def __init__(self, provides, *injections, **kwargs):
|
||||
"""Initializer."""
|
||||
if not callable(provides):
|
||||
raise Error('Factory provider expects to get callable, ' +
|
||||
'got {0} instead'.format(str(provides)))
|
||||
self._provides = provides
|
||||
self._kwargs = tuple(injection
|
||||
for injection in injections
|
||||
if is_kwarg_injection(injection))
|
||||
if kwargs:
|
||||
self._kwargs += tuple(KwArg(name, value)
|
||||
for name, value in six.iteritems(kwargs))
|
||||
self._attributes = tuple(injection
|
||||
for injection in injections
|
||||
if is_attribute_injection(injection))
|
||||
self._methods = tuple(injection
|
||||
for injection in injections
|
||||
if is_method_injection(injection))
|
||||
super(Factory, self).__init__()
|
||||
|
||||
def _provide(self, *args, **kwargs):
|
||||
"""Return provided instance."""
|
||||
instance = self._provides(*args,
|
||||
**get_injectable_kwargs(kwargs,
|
||||
self._kwargs))
|
||||
for attribute in self._attributes:
|
||||
setattr(instance, attribute.name, attribute.value)
|
||||
for method in self._methods:
|
||||
getattr(instance, method.name)(method.value)
|
||||
|
||||
return instance
|
||||
|
||||
|
||||
class NewInstance(Factory):
|
||||
|
||||
"""NewInstance provider.
|
||||
|
||||
It is synonym of Factory provider. NewInstance provider is considered to
|
||||
be deprecated, but will be able to use for further backward
|
||||
compatibility.
|
||||
"""
|
||||
|
||||
|
||||
class Singleton(Provider):
|
||||
|
||||
"""Singleton provider.
|
||||
|
||||
Singleton provider will create instance once and return it on every call.
|
||||
"""
|
||||
|
||||
__slots__ = ('_instance', '_factory')
|
||||
|
||||
def __init__(self, provides, *injections, **kwargs):
|
||||
"""Initializer."""
|
||||
self._instance = None
|
||||
self._factory = Factory(provides, *injections, **kwargs)
|
||||
super(Singleton, self).__init__()
|
||||
|
||||
def _provide(self, *args, **kwargs):
|
||||
"""Return provided instance."""
|
||||
with GLOBAL_LOCK:
|
||||
if not self._instance:
|
||||
self._instance = self._factory(*args, **kwargs)
|
||||
return self._instance
|
||||
|
||||
def reset(self):
|
||||
"""Reset instance."""
|
||||
self._instance = None
|
||||
|
||||
|
||||
class ExternalDependency(Provider):
|
||||
|
||||
"""External dependency provider.
|
||||
|
||||
Those provider is used when dependency obviously have to be overridden by
|
||||
the client's code, but it's interface is known.
|
||||
"""
|
||||
|
||||
__slots__ = ('_instance_of',)
|
||||
|
||||
def __init__(self, instance_of):
|
||||
"""Initializer."""
|
||||
if not isinstance(instance_of, six.class_types):
|
||||
raise Error('ExternalDependency provider expects to get class, ' +
|
||||
'got {0} instead'.format(str(instance_of)))
|
||||
self._instance_of = instance_of
|
||||
super(ExternalDependency, self).__init__()
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
"""Return provided instance."""
|
||||
if not self._overridden:
|
||||
raise Error('Dependency is not defined')
|
||||
|
||||
instance = self.last_overriding(*args, **kwargs)
|
||||
|
||||
if not isinstance(instance, self._instance_of):
|
||||
raise Error('{0} is not an '.format(instance) +
|
||||
'instance of {0}'.format(self._instance_of))
|
||||
|
||||
return instance
|
||||
|
||||
def provided_by(self, provider):
|
||||
"""Set external dependency provider."""
|
||||
return self.override(provider)
|
||||
|
||||
|
||||
class _StaticProvider(Provider):
|
||||
|
||||
"""Static provider.
|
||||
|
||||
Static provider is base implementation that provides exactly the same as
|
||||
it got on input.
|
||||
"""
|
||||
|
||||
__slots__ = ('_provides',)
|
||||
|
||||
def __init__(self, provides):
|
||||
"""Initializer."""
|
||||
self._provides = provides
|
||||
super(_StaticProvider, self).__init__()
|
||||
|
||||
def _provide(self, *args, **kwargs):
|
||||
"""Return provided instance."""
|
||||
return self._provides
|
||||
|
||||
|
||||
class Class(_StaticProvider):
|
||||
|
||||
"""Class provider provides class."""
|
||||
|
||||
|
||||
class Object(_StaticProvider):
|
||||
|
||||
"""Object provider provides object."""
|
||||
|
||||
|
||||
class Function(_StaticProvider):
|
||||
|
||||
"""Function provider provides function."""
|
||||
|
||||
|
||||
class Value(_StaticProvider):
|
||||
|
||||
"""Value provider provides value."""
|
||||
|
||||
|
||||
class Callable(Provider):
|
||||
|
||||
"""Callable provider.
|
||||
|
||||
Callable provider provides callable that is called on every provider call
|
||||
with some predefined dependency injections.
|
||||
"""
|
||||
|
||||
__slots__ = ('_callback', '_kwargs')
|
||||
|
||||
def __init__(self, callback, **kwargs):
|
||||
"""Initializer."""
|
||||
if not callable(callback):
|
||||
raise Error('Callable expected, got {0}'.format(str(callback)))
|
||||
self._callback = callback
|
||||
self._kwargs = tuple(KwArg(name, value)
|
||||
for name, value in six.iteritems(kwargs))
|
||||
super(Callable, self).__init__()
|
||||
|
||||
def _provide(self, *args, **kwargs):
|
||||
"""Return provided instance."""
|
||||
return self._callback(*args, **get_injectable_kwargs(kwargs,
|
||||
self._kwargs))
|
||||
|
||||
|
||||
class Config(Provider):
|
||||
|
||||
"""Config provider.
|
||||
|
||||
Config provider provides dict values. Also config provider creates
|
||||
child config objects for all undefined attribute calls. It makes possible
|
||||
to create deferred config value provider.
|
||||
"""
|
||||
|
||||
__slots__ = ('_value',)
|
||||
|
||||
def __init__(self, value=None):
|
||||
"""Initializer."""
|
||||
if not value:
|
||||
value = dict()
|
||||
self._value = value
|
||||
super(Config, self).__init__()
|
||||
|
||||
def __getattr__(self, item):
|
||||
"""Return instance of deferred config."""
|
||||
return _ChildConfig(parents=(item,), root_config=self)
|
||||
|
||||
def _provide(self, paths=None):
|
||||
"""Return provided instance."""
|
||||
value = self._value
|
||||
if paths:
|
||||
for path in paths:
|
||||
try:
|
||||
value = value[path]
|
||||
except KeyError:
|
||||
raise Error('Config key '
|
||||
'"{0}" is undefined'.format('.'.join(paths)))
|
||||
return value
|
||||
|
||||
def update_from(self, value):
|
||||
"""Update current value from another one."""
|
||||
self._value.update(value)
|
||||
|
||||
|
||||
class _ChildConfig(Provider):
|
||||
|
||||
"""Child config provider.
|
||||
|
||||
Child config provide an value from the root config object according to
|
||||
the current path in the config tree.
|
||||
"""
|
||||
|
||||
__slots__ = ('_parents', '_root_config')
|
||||
|
||||
def __init__(self, parents, root_config):
|
||||
"""Initializer."""
|
||||
self._parents = parents
|
||||
self._root_config = root_config
|
||||
super(_ChildConfig, self).__init__()
|
||||
|
||||
def __getattr__(self, item):
|
||||
"""Return instance of deferred config."""
|
||||
return _ChildConfig(parents=self._parents + (item,),
|
||||
root_config=self._root_config)
|
||||
|
||||
def _provide(self, *args, **kwargs):
|
||||
"""Return provided instance."""
|
||||
return self._root_config(self._parents)
|
64
dependency_injector/utils.py
Normal file
|
@ -0,0 +1,64 @@
|
|||
"""Utils module."""
|
||||
|
||||
import threading
|
||||
|
||||
import six
|
||||
|
||||
from .errors import Error
|
||||
|
||||
|
||||
GLOBAL_LOCK = threading.RLock()
|
||||
|
||||
|
||||
def is_provider(instance):
|
||||
"""Check if instance is provider instance."""
|
||||
return (not isinstance(instance, six.class_types) and
|
||||
getattr(instance, '__IS_PROVIDER__', False) is True)
|
||||
|
||||
|
||||
def ensure_is_provider(instance):
|
||||
"""Check if instance is provider instance, otherwise raise and error."""
|
||||
if not is_provider(instance):
|
||||
raise Error('Expected provider instance, '
|
||||
'got {0}'.format(str(instance)))
|
||||
return instance
|
||||
|
||||
|
||||
def is_injection(instance):
|
||||
"""Check if instance is injection instance."""
|
||||
return (not isinstance(instance, six.class_types) and
|
||||
getattr(instance, '__IS_INJECTION__', False) is True)
|
||||
|
||||
|
||||
def ensure_is_injection(instance):
|
||||
"""Check if instance is injection instance, otherwise raise and error."""
|
||||
if not is_injection(instance):
|
||||
raise Error('Expected injection instance, '
|
||||
'got {0}'.format(str(instance)))
|
||||
return instance
|
||||
|
||||
|
||||
def is_kwarg_injection(instance):
|
||||
"""Check if instance is keyword argument injection instance."""
|
||||
return (not isinstance(instance, six.class_types) and
|
||||
getattr(instance, '__IS_KWARG_INJECTION__', False) is True)
|
||||
|
||||
|
||||
def is_attribute_injection(instance):
|
||||
"""Check if instance is attribute injection instance."""
|
||||
return (not isinstance(instance, six.class_types) and
|
||||
getattr(instance, '__IS_ATTRIBUTE_INJECTION__', False) is True)
|
||||
|
||||
|
||||
def is_method_injection(instance):
|
||||
"""Check if instance is method injection instance."""
|
||||
return (not isinstance(instance, six.class_types) and
|
||||
getattr(instance, '__IS_METHOD_INJECTION__', False) is True)
|
||||
|
||||
|
||||
def get_injectable_kwargs(kwargs, injections):
|
||||
"""Return dictionary of kwargs, patched with injections."""
|
||||
init_kwargs = dict(((injection.name, injection.value)
|
||||
for injection in injections))
|
||||
init_kwargs.update(kwargs)
|
||||
return init_kwargs
|
9
docs/_static/custom.css
vendored
|
@ -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;
|
||||
}
|
1
docs/_static/logo.svg
vendored
Before Width: | Height: | Size: 5.8 KiB |
1
docs/_static/sponsor.html
vendored
|
@ -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>
|
27
docs/advanced_usage/index.rst
Normal file
|
@ -0,0 +1,27 @@
|
|||
Advanced usage
|
||||
==============
|
||||
|
||||
Current section of documentation describes advanced usage of
|
||||
*Dependency Injector*.
|
||||
|
||||
@inject decorator
|
||||
-----------------
|
||||
|
||||
``@di.inject()`` decorator can be used for making *inline* dependency
|
||||
injections. It *patches* decorated callable in such way that dependency
|
||||
injection will be done during every call of decorated callable.
|
||||
|
||||
``@di.inject()`` decorator takes keyword argument, that will be injected
|
||||
during every next call of decorated callback with the same name. Any Python
|
||||
object will be injected *as is*, except ``di.Provider``'s, which will be
|
||||
called to provide injectable values.
|
||||
|
||||
Example:
|
||||
|
||||
.. literalinclude:: ../../examples/advanced_usage/inject_decorator_simple.py
|
||||
:language: python
|
||||
|
||||
Example of usage ``@di.inject()`` decorator with Flask:
|
||||
|
||||
.. literalinclude:: ../../examples/advanced_usage/inject_decorator_flask.py
|
||||
:language: python
|
|
@ -1,9 +0,0 @@
|
|||
dependency_injector.containers
|
||||
==============================
|
||||
|
||||
.. automodule:: dependency_injector.containers
|
||||
:members:
|
||||
:inherited-members:
|
||||
:show-inheritance:
|
||||
|
||||
.. disqus::
|
|
@ -1,8 +0,0 @@
|
|||
dependency_injector.errors
|
||||
==========================
|
||||
|
||||
.. automodule:: dependency_injector.errors
|
||||
:members:
|
||||
|
||||
|
||||
.. disqus::
|
|
@ -1,11 +0,0 @@
|
|||
API Documentation
|
||||
=================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
top-level
|
||||
providers
|
||||
containers
|
||||
wiring
|
||||
errors
|
|
@ -1,10 +0,0 @@
|
|||
dependency_injector.providers
|
||||
=============================
|
||||
|
||||
.. automodule:: dependency_injector.providers
|
||||
:members:
|
||||
:inherited-members:
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
.. disqus::
|
|
@ -1,8 +0,0 @@
|
|||
dependency_injector
|
||||
===================
|
||||
|
||||
.. automodule:: dependency_injector
|
||||
:members: __version__
|
||||
|
||||
|
||||
.. disqus::
|
|
@ -1,7 +0,0 @@
|
|||
dependency_injector.wiring
|
||||
=============================
|
||||
|
||||
.. automodule:: dependency_injector.wiring
|
||||
:members:
|
||||
|
||||
.. disqus::
|
84
docs/catalogs/index.rst
Normal file
|
@ -0,0 +1,84 @@
|
|||
Catalogs
|
||||
========
|
||||
|
||||
Catalogs are collections of providers. Main purpose of catalogs is to group
|
||||
providers.
|
||||
|
||||
There are, actually, several popular cases of catalogs usage:
|
||||
|
||||
- Grouping of providers from the same architectural layer (for example,
|
||||
``Services``, ``Models`` and ``Forms`` catalogs).
|
||||
- Grouping of providers from the same functional groups (for example,
|
||||
catalog ``Users``, that contains all functional parts of ``Users``
|
||||
component).
|
||||
|
||||
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.
|
||||
|
||||
Writing catalogs
|
||||
----------------
|
||||
|
||||
Catalogs have to extend base catalog class ``di.AbstractCatalog``.
|
||||
|
||||
Providers have to be defined like catalog's attributes. Every provider in
|
||||
catalog has name. This name should follow ``some_provider`` convention,
|
||||
that is standard naming convention for attribute names in Python.
|
||||
|
||||
.. note::
|
||||
|
||||
It might be useful to add such ``""":type: (di.Provider) -> Object1"""``
|
||||
documentation blocks one line after provider definition for every provider.
|
||||
It will help code analyzing tools and IDE's to understand that variable
|
||||
above contains some callable object, that returns particular instance
|
||||
in a result of call.
|
||||
|
||||
Example:
|
||||
|
||||
.. image:: /images/catalogs/simple.png
|
||||
:width: 100%
|
||||
:align: center
|
||||
|
||||
.. literalinclude:: ../../examples/catalogs/simple.py
|
||||
:language: python
|
||||
|
||||
Operating with catalog providers
|
||||
--------------------------------
|
||||
|
||||
There are several things that could be useful for operating with catalog
|
||||
providers:
|
||||
|
||||
- First of all, ``di.AbstractCatalog.providers`` attribute contains ``dict``
|
||||
with all catalog providers. This dictionary could be used for any kind of
|
||||
operations that could be done with providers. The only note, is that
|
||||
``di.AbstractCatalog.providers`` attribute is read-only.
|
||||
- Second one, ``di.AbstractCatalog.filter(provider_type=di.Provider)`` method
|
||||
could be used for filtering catalog providers by provider types (for example,
|
||||
for getting all ``di.Factory`` providers).
|
||||
|
||||
Example:
|
||||
|
||||
.. literalinclude:: ../../examples/catalogs/operating_with_providers.py
|
||||
:language: python
|
||||
|
||||
Overriding of catalogs
|
||||
----------------------
|
||||
|
||||
Catalogs can be overridden by other catalogs. This, actually, means that
|
||||
all of the providers from overriding catalog will override providers with the
|
||||
same names in overridden catalog.
|
||||
|
||||
There are two ways to override catalog by another catalog:
|
||||
|
||||
- Use ``di.AbstractCatalog.override(AnotherCatalog)`` method.
|
||||
- Use ``@di.override(AnotherCatalog)`` class decorator.
|
||||
|
||||
Example of overriding catalog using ``di.AbstractCatalog.override()`` method:
|
||||
|
||||
.. literalinclude:: ../../examples/catalogs/override.py
|
||||
:language: python
|
||||
|
||||
Example of overriding catalog using ``@di.override()`` decorator:
|
||||
|
||||
.. literalinclude:: ../../examples/catalogs/override_decorator.py
|
||||
:language: python
|
141
docs/conf.py
|
@ -13,56 +13,49 @@
|
|||
# serve to show the default.
|
||||
|
||||
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 = []
|
||||
|
||||
# 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'2015, Roman Mogilatov'
|
||||
author = u'Roman Mogilatov'
|
||||
|
||||
# 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)
|
||||
# Getting version.
|
||||
with open('../VERSION') as version:
|
||||
version = version.read().strip()
|
||||
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = version
|
||||
|
@ -76,19 +69,19 @@ 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 +93,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,16 +109,15 @@ 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 = 'default'
|
||||
|
||||
# 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
|
||||
# documentation.
|
||||
# html_context = {}
|
||||
#html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
html_theme_path = [alabaster.get_path()]
|
||||
#html_theme_path = []
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
|
@ -141,24 +133,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 +181,61 @@ 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'
|
||||
|
||||
# on_rtd is whether we are on readthedocs.org, this line of code grabbed from
|
||||
# docs.readthedocs.org
|
||||
on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
|
||||
|
||||
if not on_rtd: # only import and set the theme if we're building docs locally
|
||||
import sphinx_rtd_theme
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
|
||||
# otherwise, readthedocs.org uses their theme by default, so no need to
|
||||
# specify it
|
||||
|
||||
# -- 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'Roman Mogilatov', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
|
@ -264,7 +264,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 +278,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 management tool for Python projects.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
|
@ -289,25 +289,8 @@ 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"
|
||||
|
||||
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",
|
||||
}
|
||||
|
|
|
@ -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::
|
|
@ -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::
|
|
@ -1,72 +0,0 @@
|
|||
Declarative container
|
||||
---------------------
|
||||
|
||||
.. currentmodule:: dependency_injector.containers
|
||||
|
||||
:py:class:`DeclarativeContainer` is a class-based style of the providers definition.
|
||||
|
||||
You create the declarative container subclass, put the providers as attributes and create the
|
||||
container instance.
|
||||
|
||||
.. literalinclude:: ../../examples/containers/declarative.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
.. literalinclude:: ../../examples/containers/declarative_inheritance.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
|
||||
Injections in the declarative container are done the usual way:
|
||||
|
||||
.. literalinclude:: ../../examples/containers/declarative_injections.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
|
||||
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::
|
|
@ -1,25 +0,0 @@
|
|||
Dynamic container
|
||||
-----------------
|
||||
|
||||
.. currentmodule:: dependency_injector.containers
|
||||
|
||||
:py:class:`DynamicContainer` is a collection of the providers defined in the runtime.
|
||||
|
||||
You create the dynamic container instance and put the providers as attributes.
|
||||
|
||||
.. literalinclude:: ../../examples/containers/dynamic.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
|
||||
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:
|
||||
|
||||
.. literalinclude:: ../../examples/containers/dynamic_runtime_creation.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
|
||||
.. disqus::
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
.. _containers:
|
||||
|
||||
Containers
|
||||
==========
|
||||
|
||||
Containers are collections of the providers.
|
||||
|
||||
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,
|
||||
``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).
|
||||
|
||||
Containers module API docs - :py:mod:`dependency_injector.containers`.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
declarative
|
||||
dynamic
|
||||
specialization
|
||||
overriding
|
||||
copying
|
||||
reset_singletons
|
||||
check_dependencies
|
||||
traversal
|
|
@ -1,40 +0,0 @@
|
|||
Container overriding
|
||||
--------------------
|
||||
|
||||
.. 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.
|
||||
|
||||
.. literalinclude:: ../../examples/containers/override.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 12-16
|
||||
|
||||
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.
|
||||
|
||||
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::
|
|
@ -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::
|
|
@ -1,25 +0,0 @@
|
|||
Specialization of the container provider type
|
||||
---------------------------------------------
|
||||
|
||||
.. currentmodule:: dependency_injector.containers
|
||||
|
||||
You can make a restriction of the :py:class:`DeclarativeContainer` provider type:
|
||||
|
||||
.. literalinclude:: ../../examples/containers/declarative_provider_type.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 29-31
|
||||
|
||||
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`:
|
||||
|
||||
.. literalinclude:: ../../examples/containers/dynamic_provider_type.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 23
|
||||
|
||||
The emphasized line will also cause an error.
|
||||
|
||||
.. disqus::
|
|
@ -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::
|
|
@ -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::
|
|
@ -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::
|
|
@ -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
|
|
@ -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::
|
|
@ -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::
|
|
@ -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::
|
|
@ -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::
|
|
@ -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::
|
|
@ -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::
|
|
@ -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::
|
|
@ -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: 13
|
||||
|
||||
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::
|
|
@ -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::
|
|
@ -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::
|
|
@ -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::
|
|
@ -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::
|
|
@ -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::
|
Before Width: | Height: | Size: 66 KiB |
Before Width: | Height: | Size: 182 KiB |
Before Width: | Height: | Size: 382 KiB |
Before Width: | Height: | Size: 647 KiB |
|
@ -1,26 +0,0 @@
|
|||
Examples
|
||||
========
|
||||
|
||||
.. meta::
|
||||
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Example
|
||||
:description: Python dependency injection examples.
|
||||
|
||||
Explore the examples to see the ``Dependency Injector`` in action.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
application-single-container
|
||||
application-multiple-containers
|
||||
decoupled-packages
|
||||
boto3
|
||||
django
|
||||
flask
|
||||
flask-blueprints
|
||||
aiohttp
|
||||
sanic
|
||||
fastapi
|
||||
fastapi-redis
|
||||
fastapi-sqlalchemy
|
||||
|
||||
.. disqus::
|
|
@ -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::
|
BIN
docs/favicon.ico
Before Width: | Height: | Size: 15 KiB |
BIN
docs/images/catalogs/simple.png
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
docs/images/internals.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
docs/images/providers/callable.png
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
docs/images/providers/custom_provider.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
docs/images/providers/external_dependency.png
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
docs/images/providers/factory.png
Normal file
After Width: | Height: | Size: 8.2 KiB |
BIN
docs/images/providers/factory_attribute_injections.png
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
docs/images/providers/factory_delegation.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
docs/images/providers/factory_init_injections.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
docs/images/providers/factory_init_injections_and_contexts.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
docs/images/providers/factory_method_injections.png
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
docs/images/providers/overriding_simple.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
docs/images/providers/overriding_users_model.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
docs/images/providers/provider_override.png
Normal file
After Width: | Height: | Size: 9.4 KiB |
BIN
docs/images/providers/singleton.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
docs/images/providers/singleton_internals.png
Normal file
After Width: | Height: | Size: 10 KiB |
187
docs/index.rst
|
@ -1,150 +1,59 @@
|
|||
=================================================================
|
||||
Dependency Injector --- Dependency injection framework for Python
|
||||
=================================================================
|
||||
Dependency Injector
|
||||
===================
|
||||
|
||||
.. meta::
|
||||
:google-site-verification: V1hlKfpgL3AARAElwFcqP4qW1Smsx5bKSRU8O86i20Y
|
||||
: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
|
||||
using Cython.
|
||||
Dependency injection framework for Python projects.
|
||||
|
||||
.. _index:
|
||||
+---------------------------------------+-------------------------------------------------------------------------------+
|
||||
| *PyPi* | .. image:: https://img.shields.io/pypi/v/dependency_injector.svg |
|
||||
| | :target: https://pypi.python.org/pypi/dependency_injector/ |
|
||||
| | :alt: Latest Version |
|
||||
| | .. image:: https://img.shields.io/pypi/dm/dependency_injector.svg |
|
||||
| | :target: https://pypi.python.org/pypi/dependency_injector/ |
|
||||
| | :alt: Downloads |
|
||||
| | .. image:: https://img.shields.io/pypi/l/dependency_injector.svg |
|
||||
| | :target: https://pypi.python.org/pypi/dependency_injector/ |
|
||||
| | :alt: License |
|
||||
+---------------------------------------+-------------------------------------------------------------------------------+
|
||||
| *Python versions and implementations* | .. image:: https://img.shields.io/pypi/pyversions/dependency_injector.svg |
|
||||
| | :target: https://pypi.python.org/pypi/dependency_injector/ |
|
||||
| | :alt: Supported Python versions |
|
||||
| | .. image:: https://img.shields.io/pypi/implementation/dependency_injector.svg |
|
||||
| | :target: https://pypi.python.org/pypi/dependency_injector/ |
|
||||
| | :alt: Supported Python implementations |
|
||||
+---------------------------------------+-------------------------------------------------------------------------------+
|
||||
| *Builds and tests coverage* | .. image:: https://travis-ci.org/rmk135/dependency_injector.svg?branch=master |
|
||||
| | :target: https://travis-ci.org/rmk135/dependency_injector |
|
||||
| | :alt: Build Status |
|
||||
| | .. image:: https://coveralls.io/repos/rmk135/dependency_injector/badge.svg |
|
||||
| | :target: https://coveralls.io/r/rmk135/dependency_injector |
|
||||
| | :alt: Coverage Status |
|
||||
+---------------------------------------+-------------------------------------------------------------------------------+
|
||||
|
||||
.. 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 framework for Python projects.
|
||||
It was designed to be unified, developer's friendly tool for managing any kind
|
||||
of Python objects and their dependencies in formal, pretty way.
|
||||
|
||||
.. image:: https://img.shields.io/pypi/l/dependency_injector.svg
|
||||
:target: https://pypi.org/project/dependency-injector/
|
||||
:alt: License
|
||||
Below is a list of some key features and points of *Dependency Injector*
|
||||
framework:
|
||||
|
||||
.. image:: https://img.shields.io/pypi/pyversions/dependency_injector.svg
|
||||
:target: https://pypi.org/project/dependency-injector/
|
||||
:alt: Supported Python versions
|
||||
- Easy, smart, pythonic style.
|
||||
- Obvious, clear structure.
|
||||
- Memory efficiency.
|
||||
- Thread safety.
|
||||
- Semantic versioning.
|
||||
|
||||
.. image:: https://img.shields.io/pypi/implementation/dependency_injector.svg
|
||||
:target: https://pypi.org/project/dependency-injector/
|
||||
:alt: Supported Python implementations
|
||||
|
||||
.. image:: https://static.pepy.tech/badge/dependency-injector
|
||||
:target: https://pepy.tech/project/dependency-injector
|
||||
:alt: Downloads
|
||||
|
||||
.. 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:
|
||||
Main idea of *Dependency Injector* is to keep dependencies under control.
|
||||
|
||||
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
|
||||
main/introduction
|
||||
main/installation
|
||||
providers/index
|
||||
catalogs/index
|
||||
advanced_usage/index
|
||||
main/feedback
|
||||
main/changelog
|
||||
|
|
|
@ -1,315 +0,0 @@
|
|||
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.
|
||||
|
||||
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.
|
||||
|
||||
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`
|
||||
|
||||
Useful links
|
||||
------------
|
||||
|
||||
A few useful links related to a dependency injection design pattern 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::
|
Before Width: | Height: | Size: 8.2 KiB |
|
@ -1,18 +0,0 @@
|
|||
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.
|
||||
|
||||
The current section of the documentation provides an overview of the
|
||||
dependency injection, inversion of control, and the ``Dependency Injector`` framework.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
di_in_python
|
||||
key_features
|
||||
installation
|
|
@ -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::
|
|
@ -1,43 +0,0 @@
|
|||
.. _key-features:
|
||||
|
||||
Key features
|
||||
------------
|
||||
|
||||
.. meta::
|
||||
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control
|
||||
:description: This article describes key features of the Dependency Injector
|
||||
framework.
|
||||
|
||||
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.
|
||||
|
||||
The framework stands on the `PEP20 (The Zen of Python) <https://www.python.org/dev/peps/pep-0020/>`_ principle:
|
||||
|
||||
.. code-block:: plain
|
||||
|
||||
Explicit is better than implicit
|
||||
|
||||
You need to specify how to assemble and where to inject the dependencies explicitly.
|
||||
|
||||
The power of the framework is in its simplicity.
|
||||
``Dependency Injector`` is a simple tool for the powerful concept.
|
||||
|
||||
.. disqus::
|
|
@ -1,8 +1,9 @@
|
|||
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:
|
||||
|
||||
.. disqus::
|
||||
https://github.com/rmk135/dependency_injector/issues
|
||||
|
||||
Your feedback is quite important!
|
||||
|
|
28
docs/main/installation.rst
Normal file
|
@ -0,0 +1,28 @@
|
|||
Installation
|
||||
============
|
||||
|
||||
*Dependency Injector* framework is distributed by PyPi_.
|
||||
|
||||
Latest stable version (and all previous versions) of *Dependency Injector*
|
||||
framework can be installed from PyPi_:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Installing latest version:
|
||||
pip install dependency_injector
|
||||
|
||||
# Installing particular version:
|
||||
pip install dependency_injector==0.9.0
|
||||
|
||||
Sources can be cloned from GitHub_:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
git clone https://github.com/rmk135/dependency_injector.git
|
||||
|
||||
Also all *Dependency Injector* releases can be downloaded from
|
||||
`GitHub releases page`_.
|
||||
|
||||
.. _PyPi: https://pypi.python.org/pypi/dependency_injector
|
||||
.. _GitHub: https://github.com/rmk135/dependency_injector
|
||||
.. _GitHub releases page: https://github.com/rmk135/dependency_injector/releases
|
111
docs/main/introduction.rst
Normal file
|
@ -0,0 +1,111 @@
|
|||
Introduction
|
||||
============
|
||||
|
||||
Before you have started with *Dependency Injector* framework and dependecy
|
||||
injection, there are a couple of introduction notes that might be useful.
|
||||
|
||||
What is DI and why is it needed?
|
||||
--------------------------------
|
||||
|
||||
Python ecosystem consists of a big amount of various libraries that contain
|
||||
different classes and functions that could be used for applications
|
||||
development. Each of them has its own role.
|
||||
|
||||
Modern Python applications are mostly the composition of well-known open
|
||||
source systems / frameworks / libraries and some turnkey functionality.
|
||||
|
||||
When application goes bigger, its complexity and SLOC_ are also increased.
|
||||
Being driven by SOLID_ (for example), developers often start to split
|
||||
application's sources into not so big classes, functions and modules, that are
|
||||
less complex, could be reused several times and so on... It always helps, but
|
||||
there is another problem on the horizon.
|
||||
|
||||
The name of this problem is - "Dependency hell!". It sounds like "I have so
|
||||
many classes and functions! They are great, now I can understand each of them,
|
||||
but it is so hard to see the whole picture! How are they linked with each
|
||||
other? What dependencies does this class have?". And this is a key question:
|
||||
"What dependencies does certain class / function have?". To resolve this issues
|
||||
developers have to go inside with IoC_ principles and implementation patterns.
|
||||
|
||||
One of such IoC_ implementation patterns is called `dependency injection`_.
|
||||
|
||||
Dependency injection in Python
|
||||
------------------------------
|
||||
|
||||
Interesting but, dependency injection is not very popular topic in Python.
|
||||
The things are so because Python is an awesome language. Your eyes are opened
|
||||
and your hands are free while you are using Python. In practice this means that
|
||||
you can do dependency injection in Python in quite an easy way because language
|
||||
itself helps you to do this. At the same time, even the thins are so, you still
|
||||
have to do some work. Another one 'minor' problem is that there are several
|
||||
ways to do dependency injection container.
|
||||
|
||||
Key features
|
||||
------------
|
||||
|
||||
*Dependency Injector* is a dependency injection framework for Python projects.
|
||||
It was designed to be unified, developer's friendly tool for managing any kind
|
||||
of Python objects and their dependencies in formal, pretty way.
|
||||
|
||||
Below is a list of some key features and points of *Dependency Injector*
|
||||
framework:
|
||||
|
||||
- Easy, smart, pythonic style.
|
||||
- Obvious, clear structure.
|
||||
- Memory efficiency.
|
||||
- Semantic versioning.
|
||||
|
||||
Main idea of *Dependency Injector* is to keep dependencies under control.
|
||||
|
||||
Shortcuts
|
||||
---------
|
||||
|
||||
*Dependency Injector* recommends to use such kind of import shortcut:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import dependency_injector as di
|
||||
|
||||
- All *Dependency Injector* entities could be used just from top-level package
|
||||
(like ``di.Factory``, ``di.inject``, ``di.AbstractCatalog`` and so on).
|
||||
- Another one way is to use second level packages (like
|
||||
``di.providers.Factory``, ``di.injections.inject``,
|
||||
``di.catalog.AbstractCatalog`` and so on). It might be useful for improving
|
||||
of readability in some cases.
|
||||
|
||||
.. note::
|
||||
|
||||
``import dependency_injector as di`` shortcut is used among current
|
||||
documentation, images and examples.
|
||||
|
||||
Main entities
|
||||
-------------
|
||||
|
||||
Current section describes *Dependency Injector* main entities and their
|
||||
interaction with each other.
|
||||
|
||||
.. image:: /images/internals.png
|
||||
:width: 100%
|
||||
:align: center
|
||||
|
||||
There are 3 main entities:
|
||||
|
||||
- Providers. Providers are strategies of accesing objects. For example,
|
||||
``di.providers.Factory`` creates new instance of provided
|
||||
class every time it is called. ``di.providers.Singleton`` creates
|
||||
provided instance once and returns it on every next call. Providers
|
||||
could be overridden by another providers. Base class is -
|
||||
``di.providers.Provider``.
|
||||
- Injections. Injections are instructions for making dependency injections
|
||||
(there are several ways how they could be done). Injections are used mostly
|
||||
by ``di.providers.Factory`` and ``di.providers.Singleton`` providers, but
|
||||
these are not only cases. Base class is - ``di.injections.Injection``.
|
||||
- Catalogs. Catalogs are collections of providers. They are used for grouping
|
||||
of providers by some principles. Base class is -
|
||||
``di.catalog.AbstractCatalog``.
|
||||
|
||||
|
||||
.. _SLOC: http://en.wikipedia.org/wiki/Source_lines_of_code
|
||||
.. _SOLID: http://en.wikipedia.org/wiki/SOLID_%28object-oriented_design%29
|
||||
.. _IoC: http://en.wikipedia.org/wiki/Inversion_of_control
|
||||
.. _dependency injection: http://en.wikipedia.org/wiki/Dependency_injection
|
|
@ -1,72 +0,0 @@
|
|||
.. _aggregate-provider:
|
||||
|
||||
Aggregate provider
|
||||
==================
|
||||
|
||||
.. meta::
|
||||
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Configuration,Injection,
|
||||
Aggregate,Polymorphism,Environment Variable,Flexibility
|
||||
:description: Aggregate provider aggregates other providers.
|
||||
This page demonstrates how to implement the polymorphism and increase the
|
||||
flexibility of your application using the Aggregate provider.
|
||||
|
||||
:py:class:`Aggregate` provider aggregates a group of other providers.
|
||||
|
||||
.. currentmodule:: dependency_injector.providers
|
||||
|
||||
.. literalinclude:: ../../examples/providers/aggregate.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 24-27
|
||||
|
||||
Each provider in the ``Aggregate`` is associated with a key. You can call aggregated providers by providing
|
||||
their key as a first argument. All positional and keyword arguments following the key will be forwarded to
|
||||
the called provider:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
yaml_reader = container.config_readers("yaml", "./config.yml", foo=...)
|
||||
|
||||
You can also retrieve an aggregated provider by providing its key as an attribute name:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
yaml_reader = container.config_readers.yaml("./config.yml", foo=...)
|
||||
|
||||
To retrieve a dictionary of aggregated providers, use ``.providers`` attribute:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
container.config_readers.providers == {
|
||||
"yaml": <YAML provider>,
|
||||
"json": <JSON provider>,
|
||||
}
|
||||
|
||||
.. note::
|
||||
You can not override the ``Aggregate`` provider.
|
||||
|
||||
.. note::
|
||||
When you inject the ``Aggregate`` provider, it is passed "as is".
|
||||
|
||||
To use non-string keys or string keys with ``.`` and ``-``, provide a dictionary as a positional argument:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
aggregate = providers.Aggregate({
|
||||
SomeClass: providers.Factory(...),
|
||||
"key.with.periods": providers.Factory(...),
|
||||
"key-with-dashes": providers.Factory(...),
|
||||
})
|
||||
|
||||
.. seealso::
|
||||
:ref:`selector-provider` to make injections based on a configuration value, environment variable, or a result of a callable.
|
||||
|
||||
``Aggregate`` provider is different from the :ref:`selector-provider`. ``Aggregate`` provider doesn't select which provider
|
||||
to inject and doesn't have a selector. It is a group of providers and is always injected "as is". The rest of the interface
|
||||
of both providers is similar.
|
||||
|
||||
.. note::
|
||||
``Aggregate`` provider is a successor of :ref:`factory-aggregate-provider` provider. ``Aggregate`` provider doesn't have
|
||||
a restriction on the provider type, while ``FactoryAggregate`` aggregates only ``Factory`` providers.
|
||||
|
||||
.. disqus::
|
|
@ -1,110 +0,0 @@
|
|||
.. _async-injections:
|
||||
|
||||
Asynchronous injections
|
||||
=======================
|
||||
|
||||
.. meta::
|
||||
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Providers,Async,Injections,Asynchronous,Await,
|
||||
Asyncio
|
||||
:description: Dependency Injector providers support asynchronous injections. This page
|
||||
demonstrates how make asynchronous dependency injections in Python.
|
||||
|
||||
Providers support asynchronous injections.
|
||||
|
||||
.. literalinclude:: ../../examples/providers/async.py
|
||||
:language: python
|
||||
:emphasize-lines: 26-29
|
||||
:lines: 3-
|
||||
|
||||
If provider has any awaitable injections it switches into async mode. In async mode provider always returns awaitable.
|
||||
This causes a cascade effect:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
provider1() <── Async mode enabled <──┐
|
||||
│ │
|
||||
├──> provider2() │
|
||||
│ │
|
||||
├──> provider3() <── Async mode enabled <──┤
|
||||
│ │ │
|
||||
│ └──> provider4() <── Async provider ───────┘
|
||||
│
|
||||
└──> provider5()
|
||||
│
|
||||
└──> provider6()
|
||||
|
||||
In async mode provider prepares injections asynchronously.
|
||||
|
||||
If provider has multiple awaitable dependencies, it will run them concurrently. Provider will wait until all
|
||||
dependencies are ready and inject them afterwards.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
provider1()
|
||||
│
|
||||
├──> provider2() <── Async mode enabled
|
||||
│
|
||||
├──> provider3() <── Async mode enabled
|
||||
│
|
||||
└──> provider4() <── Async mode enabled
|
||||
|
||||
Here is what provider will do for the previous example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
injections = await asyncio.gather(
|
||||
provider2(),
|
||||
provider3(),
|
||||
provider4(),
|
||||
)
|
||||
await provider1(*injections)
|
||||
|
||||
Overriding behaviour
|
||||
--------------------
|
||||
|
||||
In async mode provider always returns awaitable. It applies to the overriding too. If provider in async mode is
|
||||
overridden by a provider that doesn't return awaitable result, the result will be wrapped into awaitable.
|
||||
|
||||
.. literalinclude:: ../../examples/providers/async_overriding.py
|
||||
:language: python
|
||||
:emphasize-lines: 19-24
|
||||
:lines: 3-
|
||||
|
||||
Async mode mechanics and API
|
||||
----------------------------
|
||||
|
||||
By default provider's async mode is undefined.
|
||||
|
||||
When provider async mode is undefined, provider will automatically select the mode during the next call.
|
||||
If the result is awaitable, provider will enable async mode, if not - disable it.
|
||||
|
||||
If provider async mode is enabled, provider always returns awaitable. If the result is not awaitable,
|
||||
provider wraps it into awaitable explicitly. You can safely ``await`` provider in async mode.
|
||||
|
||||
If provider async mode is disabled, provider behaves the regular way. It doesn't do async injections
|
||||
preparation or non-awaitables to awaitables conversion.
|
||||
|
||||
Once provider async mode is enabled or disabled, provider will stay in this state. No automatic switching
|
||||
will be done.
|
||||
|
||||
.. image:: images/async_mode.png
|
||||
|
||||
You can also use following methods to change provider's async mode manually:
|
||||
|
||||
- ``Provider.enable_async_mode()``
|
||||
- ``Provider.disable_async_mode()``
|
||||
- ``Provider.reset_async_mode()``
|
||||
|
||||
To check the state of provider's async mode use:
|
||||
|
||||
- ``Provider.is_async_mode_enabled()``
|
||||
- ``Provider.is_async_mode_disabled()``
|
||||
- ``Provider.is_async_mode_undefined()``
|
||||
|
||||
See also:
|
||||
|
||||
- Wiring :ref:`async-injections-wiring`
|
||||
- Resource provider :ref:`resource-async-initializers`
|
||||
- :ref:`fastapi-redis-example`
|
||||
|
||||
.. disqus::
|
|
@ -1,20 +1,41 @@
|
|||
Callable provider
|
||||
=================
|
||||
Callable providers
|
||||
------------------
|
||||
|
||||
.. meta::
|
||||
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Function,Method,Example
|
||||
:description: Callable provider helps to make dependencies injection into functions. This page
|
||||
demonstrates how to use a Callable provider.
|
||||
``di.Callable`` provider is a provider that wraps particular callable with
|
||||
some injections. Every call of this provider returns result of call of initial
|
||||
callable.
|
||||
|
||||
.. currentmodule:: dependency_injector.providers
|
||||
Callable providers and injections
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
:py:class:`Callable` provider calls a function, a method or another callable.
|
||||
``di.Callable`` provider uses keyword argument injections. Keyword argument
|
||||
injections are done by passing injectable values as keyword arguments during
|
||||
call time.
|
||||
|
||||
.. literalinclude:: ../../examples/providers/callable.py
|
||||
Context keyword arguments have higher priority than keyword argument
|
||||
injections.
|
||||
|
||||
Example:
|
||||
|
||||
.. image:: /images/providers/callable.png
|
||||
:width: 100%
|
||||
:align: center
|
||||
|
||||
.. literalinclude:: ../../examples/providers/callable_injections.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
|
||||
``Callable`` provider handles an injection of the dependencies the same way like a
|
||||
:ref:`factory-provider`.
|
||||
Callable providers delegation
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. disqus::
|
||||
``di.Callable`` provider could be delegated to any other provider via any kind
|
||||
of injection. Delegation of ``di.Callable`` providers is the same as
|
||||
``di.Factory`` and ``di.Singleton`` providers delegation, please follow
|
||||
*Factory providers delegation* section for example.
|
||||
|
||||
``di.Callable`` delegate could be created obviously using
|
||||
``di.Delegate(di.Callable())`` or by calling ``di.Callable.delegate()`` method.
|
||||
|
||||
Example:
|
||||
|
||||
.. literalinclude:: ../../examples/providers/callable_delegation.py
|
||||
:language: python
|
||||
|
|
|
@ -1,579 +0,0 @@
|
|||
.. _configuration-provider:
|
||||
|
||||
Configuration provider
|
||||
======================
|
||||
|
||||
.. meta::
|
||||
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Configuration,Injection,
|
||||
Option,Ini,Json,Yaml,Pydantic,Dict,Environment Variable Interpolation,
|
||||
Environment Variable Substitution,Environment Variable in Config,
|
||||
Environment Variable in YAML file,Environment Variable in INI file,Default,Load,Read
|
||||
:description: Configuration provides configuration options to the other providers. This page
|
||||
demonstrates how to use Configuration provider to inject the dependencies, load
|
||||
a configuration from an ini or yaml file, a dictionary, an environment variable,
|
||||
or a pydantic settings object. This page also describes how to substitute (interpolate)
|
||||
environment variables in YAML and INI configuration files.
|
||||
|
||||
.. currentmodule:: dependency_injector.providers
|
||||
|
||||
:py:class:`Configuration` provider provides configuration options to the other providers.
|
||||
|
||||
.. literalinclude:: ../../examples/providers/configuration/configuration.py
|
||||
:language: python
|
||||
:emphasize-lines: 7,12-13
|
||||
:lines: 3-
|
||||
|
||||
It implements the principle "use first, define later".
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
:backlinks: none
|
||||
|
||||
Loading from an INI file
|
||||
------------------------
|
||||
|
||||
``Configuration`` provider can load configuration from an ``ini`` file using the
|
||||
:py:meth:`Configuration.from_ini` method:
|
||||
|
||||
.. literalinclude:: ../../examples/providers/configuration/configuration_ini.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 12
|
||||
|
||||
where ``examples/providers/configuration/config.ini`` is:
|
||||
|
||||
.. literalinclude:: ../../examples/providers/configuration/config.ini
|
||||
:language: ini
|
||||
|
||||
Alternatively, you can provide a path to the INI file over the configuration provider argument. In that case,
|
||||
the container will call ``config.from_ini()`` automatically:
|
||||
|
||||
.. code-block:: python
|
||||
:emphasize-lines: 3
|
||||
|
||||
class Container(containers.DeclarativeContainer):
|
||||
|
||||
config = providers.Configuration(ini_files=["./config.ini"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
container = Container() # Config is loaded from ./config.ini
|
||||
|
||||
|
||||
:py:meth:`Configuration.from_ini` method supports environment variables interpolation.
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[section]
|
||||
option1 = ${ENV_VAR}
|
||||
option2 = ${ENV_VAR}/path
|
||||
option3 = ${ENV_VAR:default}
|
||||
|
||||
See also: :ref:`configuration-envs-interpolation`.
|
||||
|
||||
Loading from a YAML file
|
||||
------------------------
|
||||
|
||||
``Configuration`` provider can load configuration from a ``yaml`` file using the
|
||||
:py:meth:`Configuration.from_yaml` method:
|
||||
|
||||
.. literalinclude:: ../../examples/providers/configuration/configuration_yaml.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 12
|
||||
|
||||
where ``examples/providers/configuration/config.yml`` is:
|
||||
|
||||
.. literalinclude:: ../../examples/providers/configuration/config.yml
|
||||
:language: ini
|
||||
|
||||
Alternatively, you can provide a path to the YAML file over the configuration provider argument. In that case,
|
||||
the container will call ``config.from_yaml()`` automatically:
|
||||
|
||||
.. code-block:: python
|
||||
:emphasize-lines: 3
|
||||
|
||||
class Container(containers.DeclarativeContainer):
|
||||
|
||||
config = providers.Configuration(yaml_files=["./config.yml"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
container = Container() # Config is loaded from ./config.yml
|
||||
|
||||
:py:meth:`Configuration.from_yaml` method supports environment variables interpolation.
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
section:
|
||||
option1: ${ENV_VAR}
|
||||
option2: ${ENV_VAR}/path
|
||||
option3: ${ENV_VAR:default}
|
||||
|
||||
See also: :ref:`configuration-envs-interpolation`.
|
||||
|
||||
:py:meth:`Configuration.from_yaml` method uses custom version of ``yaml.SafeLoader``.
|
||||
To use another loader use ``loader`` argument:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import yaml
|
||||
|
||||
|
||||
container.config.from_yaml("config.yml", loader=yaml.UnsafeLoader)
|
||||
|
||||
.. note::
|
||||
|
||||
Loading of a yaml configuration requires ``PyYAML`` package.
|
||||
|
||||
You can install the ``Dependency Injector`` with an extra dependency::
|
||||
|
||||
pip install dependency-injector[yaml]
|
||||
|
||||
or install ``PyYAML`` directly::
|
||||
|
||||
pip install pyyaml
|
||||
|
||||
*Don't forget to mirror the changes in the requirements file.*
|
||||
|
||||
Loading from a JSON file
|
||||
------------------------
|
||||
|
||||
``Configuration`` provider can load configuration from a ``json`` file using the
|
||||
:py:meth:`Configuration.from_json` method:
|
||||
|
||||
.. literalinclude:: ../../examples/providers/configuration/configuration_json.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 12
|
||||
|
||||
where ``examples/providers/configuration/config.json`` is:
|
||||
|
||||
.. literalinclude:: ../../examples/providers/configuration/config.json
|
||||
:language: json
|
||||
|
||||
Alternatively, you can provide a path to a json file over the configuration provider argument. In that case,
|
||||
the container will call ``config.from_json()`` automatically:
|
||||
|
||||
.. code-block:: python
|
||||
:emphasize-lines: 3
|
||||
|
||||
class Container(containers.DeclarativeContainer):
|
||||
|
||||
config = providers.Configuration(json_files=["./config.json"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
container = Container() # Config is loaded from ./config.json
|
||||
|
||||
:py:meth:`Configuration.from_json` method supports environment variables interpolation.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"section": {
|
||||
"option1": "${ENV_VAR}",
|
||||
"option2": "${ENV_VAR}/path",
|
||||
"option3": "${ENV_VAR:default}"
|
||||
}
|
||||
}
|
||||
|
||||
See also: :ref:`configuration-envs-interpolation`.
|
||||
|
||||
Loading from a Pydantic settings
|
||||
--------------------------------
|
||||
|
||||
``Configuration`` provider can load configuration from a ``pydantic_settings.BaseSettings`` object using the
|
||||
:py:meth:`Configuration.from_pydantic` method:
|
||||
|
||||
.. literalinclude:: ../../examples/providers/configuration/configuration_pydantic.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 32
|
||||
|
||||
To get the data from pydantic settings ``Configuration`` provider calls its ``model_dump()`` method.
|
||||
If you need to pass an argument to this call, use ``.from_pydantic()`` keyword arguments.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
container.config.from_pydantic(Settings(), exclude={"optional"})
|
||||
|
||||
Alternatively, you can provide a ``pydantic_settings.BaseSettings`` object over the configuration provider argument. In that case,
|
||||
the container will call ``config.from_pydantic()`` automatically:
|
||||
|
||||
.. code-block:: python
|
||||
:emphasize-lines: 3
|
||||
|
||||
class Container(containers.DeclarativeContainer):
|
||||
|
||||
config = providers.Configuration(pydantic_settings=[Settings()])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
container = Container() # Config is loaded from Settings()
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
``Dependency Injector`` doesn't install ``pydantic-settings`` by default.
|
||||
|
||||
You can install the ``Dependency Injector`` with an extra dependency::
|
||||
|
||||
pip install dependency-injector[pydantic2]
|
||||
|
||||
or install ``pydantic-settings`` directly::
|
||||
|
||||
pip install pydantic-settings
|
||||
|
||||
*Don't forget to mirror the changes in the requirements file.*
|
||||
|
||||
.. note::
|
||||
|
||||
For backward-compatibility, Pydantic v1 is still supported.
|
||||
Passing ``pydantic.BaseSettings`` instances will work just as fine as ``pydantic_settings.BaseSettings``.
|
||||
|
||||
Loading from a dictionary
|
||||
-------------------------
|
||||
|
||||
``Configuration`` provider can load configuration from a Python ``dict`` using the
|
||||
:py:meth:`Configuration.from_dict` method:
|
||||
|
||||
.. literalinclude:: ../../examples/providers/configuration/configuration_dict.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 12-19
|
||||
|
||||
Loading from an environment variable
|
||||
------------------------------------
|
||||
|
||||
``Configuration`` provider can load configuration from an environment variable using the
|
||||
:py:meth:`Configuration.from_env` method:
|
||||
|
||||
.. literalinclude:: ../../examples/providers/configuration/configuration_env.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 18-20
|
||||
|
||||
You can use ``as_`` argument for the type casting of an environment variable value:
|
||||
|
||||
.. code-block:: python
|
||||
:emphasize-lines: 2,6,10
|
||||
|
||||
# API_KEY=secret
|
||||
container.config.api_key.from_env("API_KEY", as_=str, required=True)
|
||||
assert container.config.api_key() == "secret"
|
||||
|
||||
# SAMPLING_RATIO=0.5
|
||||
container.config.sampling.from_env("SAMPLING_RATIO", as_=float, required=True)
|
||||
assert container.config.sampling() == 0.5
|
||||
|
||||
# TIMEOUT undefined, default is used
|
||||
container.config.timeout.from_env("TIMEOUT", as_=int, default=5)
|
||||
assert container.config.timeout() == 5
|
||||
|
||||
|
||||
Loading a value
|
||||
---------------
|
||||
|
||||
``Configuration`` provider can load configuration value using the
|
||||
:py:meth:`Configuration.from_value` method:
|
||||
|
||||
.. literalinclude:: ../../examples/providers/configuration/configuration_value.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 14-15
|
||||
|
||||
Loading from the multiple sources
|
||||
---------------------------------
|
||||
|
||||
``Configuration`` provider can load configuration from the multiple sources. Loaded
|
||||
configuration is merged recursively over the existing configuration.
|
||||
|
||||
.. literalinclude:: ../../examples/providers/configuration/configuration_multiple.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 12-13
|
||||
|
||||
where ``examples/providers/configuration/config.local.yml`` is:
|
||||
|
||||
.. literalinclude:: ../../examples/providers/configuration/config.local.yml
|
||||
:language: ini
|
||||
|
||||
.. _configuration-envs-interpolation:
|
||||
|
||||
Using environment variables in configuration files
|
||||
--------------------------------------------------
|
||||
|
||||
``Configuration`` provider supports environment variables interpolation in configuration files.
|
||||
Use ``${ENV_NAME}`` in the configuration file to substitute value from environment
|
||||
variable ``ENV_NAME``.
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
section:
|
||||
option: ${ENV_NAME}
|
||||
|
||||
You can also specify a default value using ``${ENV_NAME:default}`` format. If environment
|
||||
variable ``ENV_NAME`` is undefined, configuration provider will substitute value ``default``.
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[section]
|
||||
option = ${ENV_NAME:default}
|
||||
|
||||
If you'd like to specify a default value for environment variable inside of the application you can use
|
||||
``os.environ.setdefault()``.
|
||||
|
||||
.. literalinclude:: ../../examples/providers/configuration/configuration_env_interpolation_os_default.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 12
|
||||
|
||||
If environment variable is undefined and doesn't have a default, ``Configuration`` provider
|
||||
will replace it with an empty value. This is a default behavior. To raise an error on
|
||||
undefined environment variable that doesn't have a default value, pass argument
|
||||
``envs_required=True`` to a configuration reading method:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
container.config.from_yaml("config.yml", envs_required=True)
|
||||
|
||||
See also: :ref:`configuration-strict-mode`.
|
||||
|
||||
.. note::
|
||||
``Configuration`` provider makes environment variables interpolation before parsing. This preserves
|
||||
original parser behavior. For instance, undefined environment variable in YAML configuration file
|
||||
will be replaced with an empty value and then YAML parser will load the file.
|
||||
|
||||
Original configuration file:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
section:
|
||||
option: ${ENV_NAME}
|
||||
|
||||
Configuration file after interpolation where ``ENV_NAME`` is undefined:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
section:
|
||||
option:
|
||||
|
||||
Configuration provider after parsing interpolated YAML file contains ``None`` in
|
||||
option ``section.option``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
assert container.config.section.option() is None
|
||||
|
||||
If you want to disable environment variables interpolation, pass ``envs_required=None``:
|
||||
|
||||
.. code-block:: yaml
|
||||
:caption: templates.yml
|
||||
|
||||
template_string: 'Hello, ${name}!'
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> container.config.from_yaml("templates.yml", envs_required=None)
|
||||
>>> container.config.template_string()
|
||||
'Hello, ${name}!'
|
||||
|
||||
Mandatory and optional sources
|
||||
------------------------------
|
||||
|
||||
By default, methods ``.from_yaml()`` and ``.from_ini()`` ignore errors if configuration file does not exist.
|
||||
You can use this to specify optional configuration files.
|
||||
|
||||
If configuration file is mandatory, use ``required`` argument. Configuration provider will raise an error
|
||||
if required file does not exist.
|
||||
|
||||
You can also use ``required`` argument when loading configuration from dictionaries and environment variables.
|
||||
|
||||
Mandatory YAML file:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
container.config.from_yaml("config.yaml", required=True)
|
||||
|
||||
Mandatory INI file:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
container.config.from_ini("config.ini", required=True)
|
||||
|
||||
Mandatory dictionary:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
container.config.from_dict(config_dict, required=True)
|
||||
|
||||
Mandatory environment variable:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
container.config.api_key.from_env("API_KEY", required=True)
|
||||
|
||||
See also: :ref:`configuration-strict-mode`.
|
||||
|
||||
Specifying the value type
|
||||
-------------------------
|
||||
|
||||
You can specify the type of the injected configuration value explicitly.
|
||||
|
||||
This helps when you read the value from an ini file or an environment variable and need to
|
||||
convert it into an ``int`` or a ``float``.
|
||||
|
||||
.. literalinclude:: ../../examples/providers/configuration/configuration_type.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 19
|
||||
|
||||
``Configuration`` provider has next helper methods:
|
||||
|
||||
- ``.as_int()``
|
||||
- ``.as_float()``
|
||||
- ``.as_(callback, *args, **kwargs)``
|
||||
|
||||
The last method ``.as_(callback, *args, **kwargs)`` helps to implement other conversions.
|
||||
|
||||
.. literalinclude:: ../../examples/providers/configuration/configuration_type_custom.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 18
|
||||
|
||||
With the ``.as_(callback, *args, **kwargs)`` you can specify a function that will be called
|
||||
before the injection. The value from the config will be passed as a first argument. The returned
|
||||
value will be injected. Parameters ``*args`` and ``**kwargs`` are handled as any other injections.
|
||||
|
||||
.. _configuration-strict-mode:
|
||||
|
||||
Strict mode and required options
|
||||
--------------------------------
|
||||
|
||||
You can use configuration provider in strict mode. In strict mode configuration provider raises an error
|
||||
on access to any undefined option.
|
||||
|
||||
.. literalinclude:: ../../examples/providers/configuration/configuration_strict.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 12
|
||||
|
||||
Methods ``.from_*()`` in strict mode raise an exception if configuration file does not exist or
|
||||
configuration data is undefined:
|
||||
|
||||
.. code-block:: python
|
||||
:emphasize-lines: 10,15,20,25,30
|
||||
|
||||
class Container(containers.DeclarativeContainer):
|
||||
|
||||
config = providers.Configuration(strict=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
container = Container()
|
||||
|
||||
try:
|
||||
container.config.from_yaml("does-not_exist.yml") # raise exception
|
||||
except FileNotFoundError:
|
||||
...
|
||||
|
||||
try:
|
||||
container.config.from_ini("does-not_exist.ini") # raise exception
|
||||
except FileNotFoundError:
|
||||
...
|
||||
|
||||
try:
|
||||
container.config.from_pydantic(EmptySettings()) # raise exception
|
||||
except ValueError:
|
||||
...
|
||||
|
||||
try:
|
||||
container.config.from_env("UNDEFINED_ENV_VAR") # raise exception
|
||||
except ValueError:
|
||||
...
|
||||
|
||||
try:
|
||||
container.config.from_dict({}) # raise exception
|
||||
except ValueError:
|
||||
...
|
||||
|
||||
Environment variables interpolation in strict mode raises an exception when encounters
|
||||
an undefined environment variable without a default value.
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
section:
|
||||
option: ${UNDEFINED}
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
try:
|
||||
container.config.from_yaml("undefined_env.yml") # raise exception
|
||||
except ValueError:
|
||||
...
|
||||
|
||||
You can override ``.from_*()`` methods behaviour in strict mode using ``required`` argument:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class Container(containers.DeclarativeContainer):
|
||||
|
||||
config = providers.Configuration(strict=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
container = Container()
|
||||
|
||||
container.config.from_yaml("config.yml")
|
||||
container.config.from_yaml("config.local.yml", required=False)
|
||||
|
||||
You can also use ``.required()`` option modifier when making an injection. It does not require to switch
|
||||
configuration provider to strict mode.
|
||||
|
||||
.. literalinclude:: ../../examples/providers/configuration/configuration_required.py
|
||||
:language: python
|
||||
:lines: 11-20
|
||||
:emphasize-lines: 8-9
|
||||
|
||||
.. note::
|
||||
|
||||
Modifier ``.required()`` should be specified before type modifier ``.as_*()``.
|
||||
|
||||
Aliases
|
||||
-------
|
||||
|
||||
You can use ``Configuration`` provider with a context manager to create aliases.
|
||||
|
||||
.. literalinclude:: ../../examples/providers/configuration/configuration_alias.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 14,22
|
||||
|
||||
.. note::
|
||||
|
||||
Library ``environs`` is a 3rd party library. You need to install it
|
||||
separately::
|
||||
|
||||
pip install environs
|
||||
|
||||
Documentation is available on GitHub: https://github.com/sloria/environs
|
||||
|
||||
Injecting invariants
|
||||
--------------------
|
||||
|
||||
You can inject invariant configuration options based on the value of the other configuration
|
||||
option.
|
||||
|
||||
To use that you should provide the switch-value as an item of the configuration option that
|
||||
contains sections ``config.options[config.switch]``:
|
||||
|
||||
- When the value of the ``config.switch`` is ``A``, the ``config.options.A`` is injected
|
||||
- When the value of the ``config.switch`` is ``B``, the ``config.options.B`` is injected
|
||||
|
||||
.. literalinclude:: ../../examples/providers/configuration/configuration_itemselector.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 15,30-31,38
|
||||
|
||||
.. disqus::
|
|
@ -1,27 +0,0 @@
|
|||
Coroutine provider
|
||||
==================
|
||||
|
||||
.. meta::
|
||||
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Coroutine,Asynchronous,
|
||||
Asyncio,Example
|
||||
:description: Coroutine provider creates a coroutine. This page demonstrates how to use a
|
||||
Coroutine provider.
|
||||
|
||||
.. currentmodule:: dependency_injector.providers
|
||||
|
||||
:py:class:`Coroutine` provider creates a coroutine.
|
||||
|
||||
.. literalinclude:: ../../examples/providers/coroutine.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
|
||||
.. note::
|
||||
The example works on Python 3.7+. For earlier versions use ``loop.run_until_complete()``.
|
||||
|
||||
``Coroutine`` provider handles an injection of the dependencies the same way like a
|
||||
:ref:`factory-provider`.
|
||||
|
||||
.. note::
|
||||
``Coroutine`` provider returns ``True`` for ``asyncio.iscoroutinefunction()`` check.
|
||||
|
||||
.. disqus::
|
|
@ -1,49 +1,33 @@
|
|||
.. _create-provider:
|
||||
Writing custom providers
|
||||
------------------------
|
||||
|
||||
Creating a custom provider
|
||||
==========================
|
||||
List of *Dependency Injector* providers could be widened with custom providers.
|
||||
|
||||
.. meta::
|
||||
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Custom provider, Create
|
||||
:description: This page demonstrates how to create a custom provider.
|
||||
Below are some tips and recommendations that have to be met:
|
||||
|
||||
.. currentmodule:: dependency_injector.providers
|
||||
1. Every custom provider has to extend base provider class -
|
||||
``di.Provider``.
|
||||
2. Cusom provider's ``__init__()`` could be overriden with only condition:
|
||||
parent initializer (``di.Provider.__init__()``) has to be called.
|
||||
3. Providing strategy has to be implemented in custom provider's
|
||||
``_provide()`` method. All ``*args`` & ``**kwargs`` that will be
|
||||
recieved by ``di.Provider.__call__()`` will be transefed to custom
|
||||
provider's ``_provide()``.
|
||||
4. If custom provider is based on some standard providers, it is better to
|
||||
use delegation of standard providers, then extending of them.
|
||||
5. If custom provider defines any attributes, it is good to list them in
|
||||
``__slots__`` attribute (as *Dependency Injector* does). It can save
|
||||
some memory.
|
||||
6. If custom provider deals with injections (e.g. ``di.Factory``,
|
||||
``di.Singleton`` providers), it is strongly recommended to be
|
||||
consistent with ``di.Factory``, ``di.Singleton`` and ``di.Callable``
|
||||
providers style.
|
||||
|
||||
You can create a custom provider.
|
||||
Example:
|
||||
|
||||
To create a custom provider you need to follow these rules:
|
||||
|
||||
1. New provider class should inherit :py:class:`Provider`.
|
||||
2. You need to implement the ``Provider._provide()`` method.
|
||||
3. You need to implement the ``Provider.__deepcopy__()`` method. It should return an
|
||||
equivalent copy of a provider. All providers must be copied with the ``deepcopy()`` function
|
||||
from the ``providers`` module. It's essential to pass ``memo`` into ``deepcopy`` in order to keep
|
||||
the preconfigured ``args`` and ``kwargs`` of stored providers. After the a new provider object
|
||||
is created, use ``Provider._copy_overriding()`` method to copy all overriding providers. See the
|
||||
example below.
|
||||
4. If new provider has a ``__init__()`` method, it should call the parent
|
||||
``Provider.__init__()``.
|
||||
5. If new provider stores any other providers, these providers should be listed in
|
||||
``.related`` property. Property ``.related`` also should yield providers from parent
|
||||
``.related`` property.
|
||||
.. image:: /images/providers/custom_provider.png
|
||||
:width: 100%
|
||||
:align: center
|
||||
|
||||
.. literalinclude:: ../../examples/providers/custom_factory.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
|
||||
.. note::
|
||||
1. Prefer delegation over inheritance. If you choose between inheriting a ``Factory`` or
|
||||
inheriting a ``Provider`` and use ``Factory`` internally - the last is better.
|
||||
2. When creating a new provider follow the ``Factory``-like injections style. Consistency matters.
|
||||
3. Use the ``__slots__`` attribute to make sure nothing could be attached to your provider. You
|
||||
will also save some memory.
|
||||
|
||||
.. note::
|
||||
If you don't find needed provider in the ``providers`` module and experience troubles creating
|
||||
one by your own - open a
|
||||
`Github Issue <https://github.com/ets-labs/python-dependency-injector/issues>`_.
|
||||
|
||||
I'll help you to resolve the issue if that's possible. If the new provider can be useful for
|
||||
others I'll include it into the ``providers`` module.
|
||||
|
||||
.. disqus::
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
.. _dependency-provider:
|
||||
|
||||
Dependency provider
|
||||
===================
|
||||
|
||||
.. currentmodule:: dependency_injector.providers
|
||||
|
||||
:py:class:`Dependency` provider is a placeholder for a dependency of a certain type.
|
||||
|
||||
To specify a type of the dependency use ``instance_of`` argument: ``Dependency(instance_of=SomeClass)``.
|
||||
Dependency provider will control that returned object is an instance of ``instance_of`` type.
|
||||
|
||||
.. literalinclude:: ../../examples/providers/dependency.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 26,35-36
|
||||
|
||||
To provide a dependency you need to override the ``Dependency`` provider. You can call
|
||||
provider ``.override()`` method or provide an overriding provider when creating a container.
|
||||
See :ref:`provider-overriding`. If you don't provide a dependency, ``Dependency`` provider
|
||||
will raise an error:
|
||||
|
||||
.. literalinclude:: ../../examples/providers/dependency_undefined_error.py
|
||||
:language: python
|
||||
:lines: 18-
|
||||
|
||||
You also can provide a default for the dependency. To provide a default use ``default`` argument:
|
||||
``Dependency(..., default=...)``. Default can be a value or a provider. If default is not a provider,
|
||||
dependency provider will wrap it into the ``Object`` provider.
|
||||
|
||||
.. literalinclude:: ../../examples/providers/dependency_default.py
|
||||
:language: python
|
||||
:lines: 16-23
|
||||
:emphasize-lines: 3
|
||||
|
||||
See also: :ref:`check-container-dependencies`.
|
||||
|
||||
.. disqus::
|
|
@ -1,37 +0,0 @@
|
|||
Dict provider
|
||||
=============
|
||||
|
||||
.. meta::
|
||||
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Dict,Injection
|
||||
:description: Dict provider helps to inject a dictionary of the dependencies. This page demonstrates
|
||||
how to use Dict provider.
|
||||
|
||||
.. currentmodule:: dependency_injector.providers
|
||||
|
||||
:py:class:`Dict` provider provides a dictionary of values.
|
||||
|
||||
.. literalinclude:: ../../examples/providers/dict.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 21-24
|
||||
|
||||
``Dict`` provider handles keyword arguments the same way as a :ref:`factory-provider`.
|
||||
|
||||
To use non-string keys or keys with ``.`` and ``-`` provide a dictionary as a positional argument:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
providers.Dict({
|
||||
SomeClass: providers.Factory(...),
|
||||
"key.with.periods": providers.Factory(...),
|
||||
"key-with-dashes": providers.Factory(...),
|
||||
})
|
||||
|
||||
Example:
|
||||
|
||||
.. literalinclude:: ../../examples/providers/dict_non_string_keys.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 40-43
|
||||
|
||||
.. disqus::
|
39
docs/providers/external_dependency.rst
Normal file
|
@ -0,0 +1,39 @@
|
|||
External dependency providers
|
||||
-----------------------------
|
||||
|
||||
``di.ExternalDependency`` provider can be useful for development of
|
||||
self-sufficient libraries / modules / applications that has required external
|
||||
dependencies.
|
||||
|
||||
For example, you have created self-sufficient library / module / application,
|
||||
that has dependency on *database connection*.
|
||||
|
||||
Second step you want to do is to make this software component to be easy
|
||||
reusable by wide amount of developers and to be easily integrated into many
|
||||
applications.
|
||||
|
||||
It may be good idea, to move all external dependencies (like
|
||||
*database connection*) to the top level and make them to be injected on your
|
||||
software component's initialization. It will make third party developers feel
|
||||
themselves free about integration of yours component in their applications,
|
||||
because they would be able to find right place / right way for doing this
|
||||
in their application's architectures.
|
||||
|
||||
At the same time, you can be sure, that your external dependency will be
|
||||
satisfied with appropriate instance.
|
||||
|
||||
Example:
|
||||
|
||||
.. note::
|
||||
|
||||
Class ``UserService`` is a part of some library. ``UserService`` has
|
||||
dependency on database connection, which can be satisfied with any
|
||||
DBAPI 2.0 database connection. Being a self-sufficient library,
|
||||
``UserService`` doesn't hardcode any kind of database management logic.
|
||||
Instead of this, ``UserService`` has external dependency, that has to
|
||||
be satisfied by cleint's code, out of library's scope.
|
||||
|
||||
.. image:: /images/providers/external_dependency.png
|
||||
|
||||
.. literalinclude:: ../../examples/providers/external_dependency.py
|
||||
:language: python
|
|
@ -1,242 +1,154 @@
|
|||
.. _factory-provider:
|
||||
Factory providers
|
||||
-----------------
|
||||
|
||||
Factory provider
|
||||
================
|
||||
``di.Factory`` provider creates new instance of specified class on every call.
|
||||
|
||||
.. meta::
|
||||
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Factory,Abstract Factory,
|
||||
Pattern,Example,Aggregate
|
||||
:description: Factory provider helps to implement dependency injection in Python. This page
|
||||
demonstrates how to use Factory provider, inject the dependencies, and assemble
|
||||
object graphs passing the injections deep inside. It also provides the examples
|
||||
of the Abstract Factory pattern & provider and Factories Aggregation pattern.
|
||||
Nothing could be better than brief example:
|
||||
|
||||
.. currentmodule:: dependency_injector.providers
|
||||
|
||||
:py:class:`Factory` provider creates new objects.
|
||||
.. image:: /images/providers/factory.png
|
||||
:width: 80%
|
||||
:align: center
|
||||
|
||||
.. literalinclude:: ../../examples/providers/factory.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
|
||||
The first argument of the ``Factory`` provider is a class, a factory function or a method
|
||||
that creates an object.
|
||||
Factory providers and __init__ injections
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The rest of the ``Factory`` positional and keyword arguments are the dependencies.
|
||||
``Factory`` injects the dependencies every time when creates a new object. The dependencies are
|
||||
injected following these rules:
|
||||
``di.Factory`` takes a various number of keyword arguments that are
|
||||
transformed into keyword argument injections. Every time, when ``di.Factory``
|
||||
creates new one instance, keyword argument injections would be passed as an
|
||||
instance's keyword arguments.
|
||||
|
||||
+ If the dependency is a provider, this provider is called and the result of the call is injected.
|
||||
+ If you need to inject the provider itself, you should use the ``.provider`` attribute. More at
|
||||
:ref:`factory_providers_delegation`.
|
||||
+ All other dependencies are injected *"as is"*.
|
||||
+ Positional context arguments are appended after ``Factory`` positional dependencies.
|
||||
+ Keyword context arguments have the priority over the ``Factory`` keyword dependencies with the
|
||||
same name.
|
||||
All injectable values are provided *"as is"*, except of providers (subclasses
|
||||
of ``di.Provider``). Providers will be called every time, when injection needs
|
||||
to be done. For example, if injectable value of keyword argument injection is a
|
||||
``di.Factory``, it will provide new one instance (as a result of its call) as
|
||||
an injectable value every time, when injection needs to be done.
|
||||
|
||||
.. image:: images/factory_init_injections.png
|
||||
Example below is a little bit more complicated. It shows how to create
|
||||
``di.Factory`` of particular class with ``__init__`` keyword argument
|
||||
injections which injectable values are also provided by another factories:
|
||||
|
||||
.. note::
|
||||
|
||||
Current keyword argument injections syntax (in an example below) is a
|
||||
**simplified one**. Full syntax and other types of injections could be
|
||||
found in sections below.
|
||||
|
||||
While keyword argument injections may be the best way of passing
|
||||
injections, current simplified syntax might be the preferable one and
|
||||
could be widely used.
|
||||
|
||||
.. image:: /images/providers/factory_init_injections.png
|
||||
:width: 90%
|
||||
:align: center
|
||||
|
||||
.. literalinclude:: ../../examples/providers/factory_init_injections.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
|
||||
``Factory`` provider can inject attributes. Use ``.add_attributes()`` method to specify
|
||||
attribute injections.
|
||||
Factory providers and __init__ injections priority
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. literalinclude:: ../../examples/providers/factory_attribute_injections.py
|
||||
Next example shows how ``di.Factory`` provider deals with positional and
|
||||
keyword ``__init__`` context arguments. In few words, ``di.Factory``
|
||||
provider fully passes positional context arguments to class's ``__init__``
|
||||
method, but keyword context arguments have priority on predefined keyword
|
||||
argument injections.
|
||||
|
||||
So, please, follow the example below:
|
||||
|
||||
.. image:: /images/providers/factory_init_injections_and_contexts.png
|
||||
|
||||
.. literalinclude:: ../../examples/providers/factory_init_injections_and_contexts.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 18-18
|
||||
|
||||
Passing arguments to the underlying providers
|
||||
---------------------------------------------
|
||||
|
||||
``Factory`` provider can pass the arguments to the underlying providers. This helps when you need
|
||||
to assemble a nested objects graph and pass the arguments deep inside.
|
||||
|
||||
Consider the example:
|
||||
|
||||
.. image:: images/factory_init_injections_underlying.png
|
||||
|
||||
To create an ``Algorithm`` you need to provide all the dependencies: ``ClassificationTask``,
|
||||
``Loss``, and ``Regularizer``. The last object in the chain, the ``Regularizer`` has a dependency
|
||||
on the ``alpha`` value. The ``alpha`` value varies from algorithm to algorithm:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
Algorithm(
|
||||
task=ClassificationTask(
|
||||
loss=Loss(
|
||||
regularizer=Regularizer(
|
||||
alpha=alpha, # <-- the dependency
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
``Factory`` provider helps to deal with the such assembly. You need to create the factories for
|
||||
all the classes and use special double-underscore ``__`` syntax for passing the ``alpha`` argument:
|
||||
Factory providers and other types of injections
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. literalinclude:: ../../examples/providers/factory_init_injections_underlying.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 44,49
|
||||
Objects can take dependencies in different forms(some objects take init
|
||||
arguments, other use attributes setting or method calls). It affects how
|
||||
such objects are created and initialized.
|
||||
|
||||
When you use ``__`` separator in the name of the keyword argument the ``Factory`` looks for
|
||||
the dependency with the same name as the left part of the ``__`` expression.
|
||||
``di.Factory`` provider takes various number of positional and keyword
|
||||
arguments, that define what kinds of dependency injections have to be used.
|
||||
|
||||
.. code-block:: none
|
||||
All of those instructions are defined in ``di.injections`` module and are
|
||||
subclasses of ``di.injections.Injection`` (shortcut ``di.Injection``). There
|
||||
are several types of injections that are used by ``di.Factory`` provider:
|
||||
|
||||
<dependency>__<keyword for the underlying provider>=<value>
|
||||
+ ``di.KwArg`` - injection is done by passing injectable value in object's
|
||||
``__init__()`` method in time of object's creation via keyword argument.
|
||||
Takes keyword name of ``__init__()`` argument and injectable value.
|
||||
+ ``di.Attribute`` - injection is done by setting specified attribute with
|
||||
injectable value right after object's creation. Takes attribute's name
|
||||
and injectable value.
|
||||
+ ``di.Method`` - injection is done by calling of specified method with
|
||||
injectable value right after object's creation and attribute injections
|
||||
are done. Takes method name and injectable value.
|
||||
|
||||
If ``<dependency>`` is found the underlying provider will receive the
|
||||
``<keyword for the underlying provider>=<value>`` as an argument.
|
||||
All ``di.Injection``'s injectable values are provided *"as is"*, except of
|
||||
providers (subclasses of ``di.Provider``). Providers will be called every time,
|
||||
when injection needs to be done.
|
||||
|
||||
.. _factory_providers_delegation:
|
||||
Factory providers and attribute injections
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Passing providers to the objects
|
||||
--------------------------------
|
||||
|
||||
When you need to inject the provider itself, but not the result of its call, use the ``.provider``
|
||||
attribute of the provider that you're going to inject.
|
||||
|
||||
.. image:: images/factory_delegation.png
|
||||
|
||||
.. literalinclude:: ../../examples/providers/factory_delegation.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 28
|
||||
|
||||
.. note:: Any provider has a ``.provider`` attribute.
|
||||
|
||||
.. _factory-string-imports:
|
||||
|
||||
String imports
|
||||
--------------
|
||||
|
||||
``Factory`` provider can handle string imports:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class Container(containers.DeclarativeContainer):
|
||||
|
||||
service = providers.Factory("myapp.mypackage.mymodule.Service")
|
||||
|
||||
You can also make a relative import:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# in myapp/container.py
|
||||
|
||||
class Container(containers.DeclarativeContainer):
|
||||
|
||||
service = providers.Factory(".mypackage.mymodule.Service")
|
||||
|
||||
or import a member of the current module just specifying its name:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class Service:
|
||||
...
|
||||
|
||||
|
||||
class Container(containers.DeclarativeContainer):
|
||||
|
||||
service = providers.Factory("Service")
|
||||
|
||||
.. note::
|
||||
``Singleton``, ``Callable``, ``Resource``, and ``Coroutine`` providers handle string imports
|
||||
the same way as a ``Factory`` provider.
|
||||
|
||||
.. _factory-specialize-provided-type:
|
||||
|
||||
Specializing the provided type
|
||||
------------------------------
|
||||
|
||||
You can create a specialized ``Factory`` provider that provides only specific type. For doing
|
||||
this you need to create a subclass of the ``Factory`` provider and define the ``provided_type``
|
||||
class attribute.
|
||||
|
||||
.. literalinclude:: ../../examples/providers/factory_provided_type.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 12-14
|
||||
|
||||
.. _abstract-factory:
|
||||
|
||||
Abstract factory
|
||||
----------------
|
||||
|
||||
:py:class:`AbstractFactory` provider helps when you need to create a provider of some base class
|
||||
and the particular implementation is not yet know. ``AbstractFactory`` provider is a ``Factory``
|
||||
provider with two peculiarities:
|
||||
|
||||
+ Provides only objects of a specified type.
|
||||
+ Must be overridden before usage.
|
||||
|
||||
.. image:: images/abstract_factory.png
|
||||
:width: 100%
|
||||
:align: center
|
||||
|
||||
.. literalinclude:: ../../examples/providers/abstract_factory.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 34
|
||||
|
||||
.. _factory-aggregate-provider:
|
||||
|
||||
Factory aggregate
|
||||
-----------------
|
||||
|
||||
:py:class:`FactoryAggregate` provider aggregates multiple factories.
|
||||
|
||||
.. seealso::
|
||||
:ref:`aggregate-provider` – it's a successor of ``FactoryAggregate`` provider that can aggregate
|
||||
any type of provider, not only ``Factory``.
|
||||
|
||||
The aggregated factories are associated with the string keys. When you call the
|
||||
``FactoryAggregate`` you have to provide one of the these keys as a first argument.
|
||||
``FactoryAggregate`` looks for the factory with a matching key and calls it with the rest of the arguments.
|
||||
|
||||
.. image:: images/factory_aggregate.png
|
||||
:width: 100%
|
||||
:align: center
|
||||
|
||||
.. literalinclude:: ../../examples/providers/factory_aggregate.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 33-37,47
|
||||
|
||||
You can get a dictionary of the aggregated providers using ``.providers`` attribute.
|
||||
To get a game provider dictionary from the previous example you can use
|
||||
``game_factory.providers`` attribute.
|
||||
|
||||
You can also access an aggregated factory as an attribute. To create the ``Chess`` object from the
|
||||
previous example you can do ``chess = game_factory.chess("John", "Jane")``.
|
||||
|
||||
.. note::
|
||||
You can not override the ``FactoryAggregate`` provider.
|
||||
|
||||
.. note::
|
||||
When you inject the ``FactoryAggregate`` provider it is passed "as is".
|
||||
|
||||
To use non-string keys or string keys with ``.`` and ``-``, you can provide a dictionary as a positional argument:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
providers.FactoryAggregate({
|
||||
SomeClass: providers.Factory(...),
|
||||
"key.with.periods": providers.Factory(...),
|
||||
"key-with-dashes": providers.Factory(...),
|
||||
})
|
||||
Example below shows how to create ``di.Factory`` of particular class with
|
||||
attribute injections. Those injections are done by setting specified attributes
|
||||
with injectable values right after object's creation.
|
||||
|
||||
Example:
|
||||
|
||||
.. literalinclude:: ../../examples/providers/factory_aggregate_non_string_keys.py
|
||||
.. image:: /images/providers/factory_attribute_injections.png
|
||||
|
||||
.. literalinclude:: ../../examples/providers/factory_attribute_injections.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 30-33,39-40
|
||||
|
||||
Factory providers and method injections
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Current example shows how to create ``di.Factory`` of particular class with
|
||||
method injections. Those injections are done by calling of specified method
|
||||
with injectable value right after object's creation and attribute injections
|
||||
are done.
|
||||
|
||||
Method injections are not very popular in Python due Python best practices
|
||||
(usage of public attributes instead of setter methods), but they may appear in
|
||||
some cases.
|
||||
|
||||
Example:
|
||||
|
||||
.. image:: /images/providers/factory_method_injections.png
|
||||
|
||||
.. literalinclude:: ../../examples/providers/factory_method_injections.py
|
||||
:language: python
|
||||
|
||||
Factory providers delegation
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
``di.Factory`` provider could be delegated to any other provider via any kind
|
||||
of injection. As it was mentioned earlier, if ``di.Factory`` is injectable
|
||||
value, it will be called every time when injection is done. ``di.Factory``
|
||||
delegation is performed by wrapping delegated ``di.Factory`` into special
|
||||
provider type - ``di.Delegate``, that just returns wrapped ``di.Factory``.
|
||||
Saying in other words, delegation of factories - is a way to inject factories
|
||||
themselves, instead of results of their calls.
|
||||
|
||||
|
||||
.. disqus::
|
||||
Actually, there are two ways of creating factory delegates:
|
||||
|
||||
+ ``di.Delegate(di.Factory(...))`` - obviously wrapping factory into
|
||||
``di.Delegate`` provider.
|
||||
+ ``di.Factory(...).delegate()`` - calling factory ``delegate()`` method, that
|
||||
returns delegate wrapper for current factory.
|
||||
|
||||
Example:
|
||||
|
||||
.. image:: /images/providers/factory_delegation.png
|
||||
:width: 85%
|
||||
:align: center
|
||||
|
||||
.. literalinclude:: ../../examples/providers/factory_delegation.py
|
||||
:language: python
|
||||
|
|