Compare commits
No commits in common. "master" and "1.16.7" 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
|
18
.travis.yml
Normal file
|
@ -0,0 +1,18 @@
|
|||
sudo: false
|
||||
language: python
|
||||
install: pip install tox
|
||||
script: tox
|
||||
python:
|
||||
- 3.5
|
||||
env:
|
||||
- TOXENV=coveralls
|
||||
- TOXENV=pylint
|
||||
- TOXENV=flake8
|
||||
- TOXENV=pydocstyle
|
||||
- TOXENV=py26
|
||||
- TOXENV=py27
|
||||
- TOXENV=py33
|
||||
- TOXENV=py34
|
||||
- TOXENV=py35
|
||||
- TOXENV=pypy
|
||||
- TOXENV=pypy3
|
|
@ -1,24 +1,4 @@
|
|||
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)
|
||||
+ Roman Mogilatov
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2024, Roman Mogylatov
|
||||
Copyright (c) 2015, ETS Labs
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
|
|
@ -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 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
|
332
README.rst
|
@ -1,230 +1,144 @@
|
|||
.. 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 - Python dependency injection framework
|
||||
===========================================================
|
||||
|
||||
|
|
||||
*Dependency Injector* is a Python dependency injection framework. It was
|
||||
designed to be unified, developer-friendly tool for managing any kind
|
||||
of Python objects and their dependencies in formal, pretty way.
|
||||
|
||||
.. image:: https://img.shields.io/pypi/v/dependency_injector.svg
|
||||
:target: https://pypi.org/project/dependency-injector/
|
||||
:alt: Latest Version
|
||||
|
||||
.. image:: https://img.shields.io/pypi/l/dependency_injector.svg
|
||||
:target: https://pypi.org/project/dependency-injector/
|
||||
:alt: License
|
||||
*Dependency Injector* framework key features are:
|
||||
|
||||
.. image:: https://img.shields.io/pypi/pyversions/dependency_injector.svg
|
||||
:target: https://pypi.org/project/dependency-injector/
|
||||
:alt: Supported Python versions
|
||||
|
||||
.. image:: https://img.shields.io/pypi/implementation/dependency_injector.svg
|
||||
:target: https://pypi.org/project/dependency-injector/
|
||||
:alt: Supported Python implementations
|
||||
+ Easy, smart, pythonic style.
|
||||
+ Obvious, clear structure.
|
||||
+ Extensibility and flexibility.
|
||||
+ Memory efficiency.
|
||||
+ Thread safety.
|
||||
+ Documentation.
|
||||
+ Semantic versioning.
|
||||
|
||||
.. image:: https://pepy.tech/badge/dependency-injector
|
||||
:target: https://pepy.tech/project/dependency-injector
|
||||
:alt: Downloads
|
||||
Status
|
||||
------
|
||||
|
||||
.. image:: https://pepy.tech/badge/dependency-injector/month
|
||||
:target: https://pepy.tech/project/dependency-injector
|
||||
:alt: Downloads
|
||||
|
||||
.. image:: https://pepy.tech/badge/dependency-injector/week
|
||||
:target: https://pepy.tech/project/dependency-injector
|
||||
:alt: Downloads
|
||||
|
||||
.. image:: https://img.shields.io/pypi/wheel/dependency-injector.svg
|
||||
:target: https://pypi.org/project/dependency-injector/
|
||||
:alt: Wheel
|
||||
|
||||
.. image:: https://img.shields.io/github/actions/workflow/status/ets-labs/python-dependency-injector/tests-and-linters.yml?branch=master
|
||||
:target: https://github.com/ets-labs/python-dependency-injector/actions
|
||||
:alt: Build Status
|
||||
|
||||
.. image:: https://coveralls.io/repos/github/ets-labs/python-dependency-injector/badge.svg?branch=master
|
||||
:target: https://coveralls.io/github/ets-labs/python-dependency-injector?branch=master
|
||||
:alt: Coverage Status
|
||||
|
||||
What is ``Dependency Injector``?
|
||||
================================
|
||||
|
||||
``Dependency Injector`` is a dependency injection framework for Python.
|
||||
|
||||
It helps implement the dependency injection principle.
|
||||
|
||||
Key features of the ``Dependency Injector``:
|
||||
|
||||
- **Providers**. Provides ``Factory``, ``Singleton``, ``Callable``, ``Coroutine``, ``Object``,
|
||||
``List``, ``Dict``, ``Configuration``, ``Resource``, ``Dependency``, and ``Selector`` providers
|
||||
that help assemble your objects.
|
||||
See `Providers <https://python-dependency-injector.ets-labs.org/providers/index.html>`_.
|
||||
- **Overriding**. Can override any provider by another provider on the fly. This helps in testing
|
||||
and configuring dev/stage environment to replace API clients with stubs etc. See
|
||||
`Provider overriding <https://python-dependency-injector.ets-labs.org/providers/overriding.html>`_.
|
||||
- **Configuration**. Reads configuration from ``yaml``, ``ini``, and ``json`` files, ``pydantic`` settings,
|
||||
environment variables, and dictionaries.
|
||||
See `Configuration provider <https://python-dependency-injector.ets-labs.org/providers/configuration.html>`_.
|
||||
- **Resources**. Helps with initialization and configuring of logging, event loop, thread
|
||||
or process pool, etc. Can be used for per-function execution scope in tandem with wiring.
|
||||
See `Resource provider <https://python-dependency-injector.ets-labs.org/providers/resource.html>`_.
|
||||
- **Containers**. Provides declarative and dynamic containers.
|
||||
See `Containers <https://python-dependency-injector.ets-labs.org/containers/index.html>`_.
|
||||
- **Wiring**. Injects dependencies into functions and methods. Helps integrate with
|
||||
other frameworks: Django, Flask, Aiohttp, Sanic, FastAPI, etc.
|
||||
See `Wiring <https://python-dependency-injector.ets-labs.org/wiring.html>`_.
|
||||
- **Asynchronous**. Supports asynchronous injections.
|
||||
See `Asynchronous injections <https://python-dependency-injector.ets-labs.org/providers/async.html>`_.
|
||||
- **Typing**. Provides typing stubs, ``mypy``-friendly.
|
||||
See `Typing and mypy <https://python-dependency-injector.ets-labs.org/providers/typing_mypy.html>`_.
|
||||
- **Performance**. Fast. Written in ``Cython``.
|
||||
- **Maturity**. Mature and production-ready. Well-tested, documented, and supported.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from dependency_injector import containers, providers
|
||||
from dependency_injector.wiring import Provide, inject
|
||||
|
||||
|
||||
class Container(containers.DeclarativeContainer):
|
||||
|
||||
config = providers.Configuration()
|
||||
|
||||
api_client = providers.Singleton(
|
||||
ApiClient,
|
||||
api_key=config.api_key,
|
||||
timeout=config.timeout,
|
||||
)
|
||||
|
||||
service = providers.Factory(
|
||||
Service,
|
||||
api_client=api_client,
|
||||
)
|
||||
|
||||
|
||||
@inject
|
||||
def main(service: Service = Provide[Container.service]) -> None:
|
||||
...
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
container = Container()
|
||||
container.config.api_key.from_env("API_KEY", required=True)
|
||||
container.config.timeout.from_env("TIMEOUT", as_=int, default=5)
|
||||
container.wire(modules=[__name__])
|
||||
|
||||
main() # <-- dependency is injected automatically
|
||||
|
||||
with container.api_client.override(mock.Mock()):
|
||||
main() # <-- overridden dependency is injected automatically
|
||||
|
||||
When you call the ``main()`` function the ``Service`` dependency is assembled and injected automatically.
|
||||
|
||||
When you do testing, you call the ``container.api_client.override()`` method to replace the real API
|
||||
client with a mock. When you call ``main()``, the mock is injected.
|
||||
|
||||
You can override any provider with another provider.
|
||||
|
||||
It also helps you in a re-configuring project for different environments: replace an API client
|
||||
with a stub on the dev or stage.
|
||||
|
||||
With the ``Dependency Injector``, object assembling is consolidated in a container. Dependency injections are defined explicitly.
|
||||
This makes it easier to understand and change how an application works.
|
||||
|
||||
.. figure:: https://raw.githubusercontent.com/wiki/ets-labs/python-dependency-injector/img/di-readme.svg
|
||||
:target: https://github.com/ets-labs/python-dependency-injector
|
||||
|
||||
Visit the docs to know more about the
|
||||
`Dependency injection and inversion of control in Python <https://python-dependency-injector.ets-labs.org/introduction/di_in_python.html>`_.
|
||||
+---------------------------------------+----------------------------------------------------------------------------------------+
|
||||
| *PyPi* | .. image:: https://img.shields.io/pypi/v/dependency_injector.svg |
|
||||
| | :target: https://pypi.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/ets-labs/python-dependency-injector.svg?branch=master |
|
||||
| | :target: https://travis-ci.org/ets-labs/python-dependency-injector |
|
||||
| | :alt: Build Status |
|
||||
| | .. image:: https://coveralls.io/repos/ets-labs/python-dependency-injector/badge.svg |
|
||||
| | :target: https://coveralls.io/r/ets-labs/python-dependency-injector |
|
||||
| | :alt: Coverage Status |
|
||||
+---------------------------------------+----------------------------------------------------------------------------------------+
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
The package is available on the `PyPi`_::
|
||||
*Dependency Injector* library is available on PyPi_::
|
||||
|
||||
pip install dependency-injector
|
||||
pip install dependency_injector
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
"""Dependency Injector example."""
|
||||
|
||||
import sys
|
||||
import sqlite3
|
||||
|
||||
from boto.s3.connection import S3Connection
|
||||
|
||||
from dependency_injector import catalogs
|
||||
from dependency_injector import providers
|
||||
from dependency_injector import injections
|
||||
|
||||
from example import services
|
||||
|
||||
|
||||
class Platform(catalogs.DeclarativeCatalog):
|
||||
"""Catalog of platform service providers."""
|
||||
|
||||
database = providers.Singleton(sqlite3.connect, ':memory:')
|
||||
|
||||
s3 = providers.Singleton(S3Connection,
|
||||
aws_access_key_id='KEY',
|
||||
aws_secret_access_key='SECRET')
|
||||
|
||||
|
||||
class Services(catalogs.DeclarativeCatalog):
|
||||
"""Catalog of business service providers."""
|
||||
|
||||
users = providers.Factory(services.Users,
|
||||
db=Platform.database)
|
||||
|
||||
photos = providers.Factory(services.Photos,
|
||||
db=Platform.database,
|
||||
s3=Platform.s3)
|
||||
|
||||
auth = providers.Factory(services.Auth,
|
||||
db=Platform.database,
|
||||
token_ttl=3600)
|
||||
|
||||
|
||||
@injections.inject(users_service=Services.users)
|
||||
@injections.inject(auth_service=Services.auth)
|
||||
@injections.inject(photos_service=Services.photos)
|
||||
def main(argv, users_service, auth_service, photos_service):
|
||||
"""Main function."""
|
||||
login, password, photo_path = argv[1:]
|
||||
|
||||
user = users_service.get_user(login)
|
||||
auth_service.authenticate(user, password)
|
||||
photos_service.upload_photo(user['id'], photo_path)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv)
|
||||
|
||||
You can get more *Dependency Injector* examples in ``/examples`` directory on
|
||||
GitHub:
|
||||
|
||||
https://github.com/ets-labs/python-dependency-injector
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
The documentation is available `here <https://python-dependency-injector.ets-labs.org/>`_.
|
||||
*Dependency Injector* documentation is hosted on ReadTheDocs:
|
||||
|
||||
Examples
|
||||
- `User's guide`_
|
||||
- `API docs`_
|
||||
|
||||
Feedback
|
||||
--------
|
||||
|
||||
Choose one of the following:
|
||||
Feel free to post questions, bugs, feature requests, proposals etc. on
|
||||
*Dependency Injector* GitHub Issues:
|
||||
|
||||
- `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>`_
|
||||
https://github.com/ets-labs/python-dependency-injector/issues
|
||||
|
||||
Tutorials
|
||||
---------
|
||||
Your feedback is quite important!
|
||||
|
||||
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>`_
|
||||
|
||||
Concept
|
||||
-------
|
||||
|
||||
The framework stands on the `PEP20 (The Zen of Python) <https://www.python.org/dev/peps/pep-0020/>`_ principle:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
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.
|
||||
|
||||
Frequently asked questions
|
||||
--------------------------
|
||||
|
||||
What is dependency injection?
|
||||
- dependency injection is a principle that decreases coupling and increases cohesion
|
||||
|
||||
Why should I do the dependency injection?
|
||||
- your code becomes more flexible, testable, and clear 😎
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
Have a question?
|
||||
- Open a `Github Issue <https://github.com/ets-labs/python-dependency-injector/issues>`_
|
||||
|
||||
Found a bug?
|
||||
- Open a `Github Issue <https://github.com/ets-labs/python-dependency-injector/issues>`_
|
||||
|
||||
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``
|
||||
|
||||
Want to contribute?
|
||||
- |fork| Fork the project
|
||||
- |pull| Open a pull request to the ``develop`` branch
|
||||
|
||||
.. _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
|
||||
.. _PyPi: https://pypi.python.org/pypi/dependency_injector
|
||||
.. _User's guide: http://python-dependency-injector.ets-labs.org/en/stable/
|
||||
.. _API docs: http://python-dependency-injector.ets-labs.org/en/stable/api/
|
||||
.. _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
|
||||
|
|
126
dependency_injector/__init__.py
Normal file
|
@ -0,0 +1,126 @@
|
|||
"""Dependency injector."""
|
||||
|
||||
from dependency_injector.catalogs import (
|
||||
DeclarativeCatalog,
|
||||
AbstractCatalog,
|
||||
DynamicCatalog,
|
||||
CatalogBundle,
|
||||
override,
|
||||
)
|
||||
|
||||
from dependency_injector.providers import (
|
||||
Provider,
|
||||
Delegate,
|
||||
Callable,
|
||||
DelegatedCallable,
|
||||
Factory,
|
||||
DelegatedFactory,
|
||||
Singleton,
|
||||
DelegatedSingleton,
|
||||
ExternalDependency,
|
||||
StaticProvider,
|
||||
Class,
|
||||
Object,
|
||||
Function,
|
||||
Value,
|
||||
Config,
|
||||
)
|
||||
|
||||
from dependency_injector.injections import (
|
||||
Injection,
|
||||
Arg,
|
||||
KwArg,
|
||||
Attribute,
|
||||
Method,
|
||||
inject,
|
||||
)
|
||||
|
||||
from dependency_injector.utils import (
|
||||
is_provider,
|
||||
ensure_is_provider,
|
||||
is_delegated_provider,
|
||||
is_injection,
|
||||
ensure_is_injection,
|
||||
is_arg_injection,
|
||||
is_kwarg_injection,
|
||||
is_attribute_injection,
|
||||
is_method_injection,
|
||||
is_catalog,
|
||||
is_dynamic_catalog,
|
||||
is_declarative_catalog,
|
||||
is_catalog_bundle,
|
||||
ensure_is_catalog_bundle,
|
||||
)
|
||||
|
||||
from dependency_injector.errors import (
|
||||
Error,
|
||||
UndefinedProviderError,
|
||||
)
|
||||
|
||||
# Backward compatibility for versions < 0.11.*
|
||||
from dependency_injector import catalogs
|
||||
catalog = catalogs
|
||||
|
||||
VERSION = '1.16.7'
|
||||
"""Version number that follows semantic versioning.
|
||||
|
||||
:type: str
|
||||
"""
|
||||
|
||||
|
||||
__all__ = (
|
||||
# Catalogs
|
||||
'DeclarativeCatalog',
|
||||
'AbstractCatalog',
|
||||
'DynamicCatalog',
|
||||
'CatalogBundle',
|
||||
'override',
|
||||
|
||||
# Providers
|
||||
'Provider',
|
||||
'Delegate',
|
||||
'Callable',
|
||||
'DelegatedCallable',
|
||||
'Factory',
|
||||
'DelegatedFactory',
|
||||
'Singleton',
|
||||
'DelegatedSingleton',
|
||||
'ExternalDependency',
|
||||
'StaticProvider',
|
||||
'Class',
|
||||
'Object',
|
||||
'Function',
|
||||
'Value',
|
||||
'Config',
|
||||
|
||||
# Injections
|
||||
'Injection',
|
||||
'Arg',
|
||||
'KwArg',
|
||||
'Attribute',
|
||||
'Method',
|
||||
'inject',
|
||||
|
||||
# Utils
|
||||
'is_provider',
|
||||
'ensure_is_provider',
|
||||
'is_delegated_provider',
|
||||
'is_injection',
|
||||
'ensure_is_injection',
|
||||
'is_arg_injection',
|
||||
'is_kwarg_injection',
|
||||
'is_attribute_injection',
|
||||
'is_method_injection',
|
||||
'is_catalog',
|
||||
'is_dynamic_catalog',
|
||||
'is_declarative_catalog',
|
||||
'is_catalog_bundle',
|
||||
'ensure_is_catalog_bundle',
|
||||
|
||||
# Errors
|
||||
'Error',
|
||||
'UndefinedProviderError',
|
||||
|
||||
# Version
|
||||
'VERSION'
|
||||
)
|
24
dependency_injector/catalogs/__init__.py
Normal file
|
@ -0,0 +1,24 @@
|
|||
"""Dependency injector catalogs package."""
|
||||
|
||||
from dependency_injector.catalogs.bundle import CatalogBundle
|
||||
from dependency_injector.catalogs.dynamic import DynamicCatalog
|
||||
from dependency_injector.catalogs.declarative import (
|
||||
DeclarativeCatalogMetaClass,
|
||||
DeclarativeCatalog,
|
||||
AbstractCatalog,
|
||||
)
|
||||
from dependency_injector.catalogs.utils import (
|
||||
copy,
|
||||
override
|
||||
)
|
||||
|
||||
|
||||
__all__ = (
|
||||
'CatalogBundle',
|
||||
'DynamicCatalog',
|
||||
'DeclarativeCatalogMetaClass',
|
||||
'DeclarativeCatalog',
|
||||
'AbstractCatalog',
|
||||
'copy',
|
||||
'override',
|
||||
)
|
118
dependency_injector/catalogs/bundle.py
Normal file
|
@ -0,0 +1,118 @@
|
|||
"""Dependency injector catalogs bundle module."""
|
||||
|
||||
import six
|
||||
|
||||
from dependency_injector.errors import (
|
||||
Error,
|
||||
UndefinedProviderError,
|
||||
)
|
||||
|
||||
|
||||
@six.python_2_unicode_compatible
|
||||
class CatalogBundle(object):
|
||||
"""Bundle of catalog providers.
|
||||
|
||||
:py:class:`CatalogBundle` is a frozen, limited collection of catalog
|
||||
providers. While catalog could be used as a centralized place for
|
||||
particular providers group, such bundles of catalog providers can be used
|
||||
for creating several frozen, limited scopes that could be passed to
|
||||
different subsystems.
|
||||
|
||||
:py:class:`CatalogBundle` has API's parity with catalogs
|
||||
(:py:class:`DeclarativeCatalog` or :py:class:`DynamicCatalog`) in terms of
|
||||
retrieving the providers, but it is "frozen" in terms of modification
|
||||
provider's list.
|
||||
|
||||
:py:class:`CatalogBundle` is considered to be dependable on catalogs
|
||||
(:py:class:`DeclarativeCatalog` or :py:class:`DynamicCatalog`) entity by
|
||||
its design.
|
||||
|
||||
.. py:attribute:: catalog
|
||||
|
||||
Bundle's catalog.
|
||||
|
||||
:type: :py:class:`DeclarativeCatalog` | :py:class:`DynamicCatalog`
|
||||
|
||||
.. py:attribute:: providers
|
||||
|
||||
Dictionary of all providers.
|
||||
|
||||
:type: dict[str, :py:class:`dependency_injector.providers.Provider`]
|
||||
"""
|
||||
|
||||
catalog = None
|
||||
|
||||
__IS_CATALOG_BUNDLE__ = True
|
||||
__slots__ = ('providers', '__dict__')
|
||||
|
||||
@classmethod
|
||||
def sub_cls_factory(cls, catalog):
|
||||
"""Create bundle subclass for catalog.
|
||||
|
||||
:return: Subclass of :py:class:`CatalogBundle`.
|
||||
:rtype: :py:class:`CatalogBundle`
|
||||
"""
|
||||
return type('BundleSubclass', (cls,), dict(catalog=catalog))
|
||||
|
||||
def __init__(self, *providers):
|
||||
"""Initializer.
|
||||
|
||||
:param providers: Tuple of catalog's bundle providers.
|
||||
:type providers: tuple[
|
||||
:py:class:`dependency_injector.providers.Provider`]
|
||||
"""
|
||||
self.providers = dict((self.catalog.get_provider_bind_name(provider),
|
||||
provider)
|
||||
for provider in providers)
|
||||
self.__dict__.update(self.providers)
|
||||
super(CatalogBundle, self).__init__()
|
||||
|
||||
def get_provider(self, name):
|
||||
"""Return provider with specified name or raise an error.
|
||||
|
||||
:param name: Provider's name.
|
||||
:type name: str
|
||||
|
||||
:raise: :py:exc:`dependency_injector.errors.UndefinedProviderError`
|
||||
|
||||
:return: Provider with specified name.
|
||||
:rtype: :py:class:`dependency_injector.providers.Provider`
|
||||
"""
|
||||
try:
|
||||
return self.providers[name]
|
||||
except KeyError:
|
||||
raise Error('Provider "{0}" is not a part of {1}'.format(name,
|
||||
self))
|
||||
|
||||
def has_provider(self, name):
|
||||
"""Check if there is provider with certain name.
|
||||
|
||||
:param name: Provider's name.
|
||||
:type name: str
|
||||
|
||||
:rtype: bool
|
||||
"""
|
||||
return name in self.providers
|
||||
|
||||
def __getattr__(self, item):
|
||||
"""Return provider with specified name or raise en error.
|
||||
|
||||
:param name: Attribute's name.
|
||||
:type name: str
|
||||
|
||||
:raise: :py:exc:`dependency_injector.errors.UndefinedProviderError`
|
||||
"""
|
||||
if item.startswith('__') and item.endswith('__'):
|
||||
return super(CatalogBundle, self).__getattr__(item)
|
||||
raise UndefinedProviderError('Provider "{0}" is not a part '
|
||||
'of {1}'.format(item, self))
|
||||
|
||||
def __repr__(self):
|
||||
"""Return string representation of catalog's bundle.
|
||||
|
||||
:rtype: str
|
||||
"""
|
||||
return '<{0}.Bundle({1})>'.format(
|
||||
self.catalog.name, ', '.join(six.iterkeys(self.providers)))
|
||||
|
||||
__str__ = __repr__
|
468
dependency_injector/catalogs/declarative.py
Normal file
|
@ -0,0 +1,468 @@
|
|||
"""Dependency injector declarative catalog module."""
|
||||
|
||||
import six
|
||||
|
||||
from dependency_injector.catalogs.dynamic import DynamicCatalog
|
||||
from dependency_injector.catalogs.bundle import CatalogBundle
|
||||
from dependency_injector.utils import (
|
||||
is_provider,
|
||||
is_catalog,
|
||||
is_declarative_catalog,
|
||||
)
|
||||
from dependency_injector.errors import (
|
||||
Error,
|
||||
UndefinedProviderError,
|
||||
)
|
||||
|
||||
|
||||
@six.python_2_unicode_compatible
|
||||
class DeclarativeCatalogMetaClass(type):
|
||||
"""Declarative catalog meta class."""
|
||||
|
||||
def __new__(mcs, class_name, bases, attributes):
|
||||
"""Declarative catalog class factory."""
|
||||
cls_providers = tuple((name, provider)
|
||||
for name, provider in six.iteritems(attributes)
|
||||
if is_provider(provider))
|
||||
|
||||
inherited_providers = tuple((name, provider)
|
||||
for base in bases if is_catalog(base)
|
||||
for name, provider in six.iteritems(
|
||||
base.providers))
|
||||
|
||||
providers = cls_providers + inherited_providers
|
||||
|
||||
cls = type.__new__(mcs, class_name, bases, attributes)
|
||||
|
||||
if cls.provider_type:
|
||||
cls._catalog = type('DynamicCatalog',
|
||||
(DynamicCatalog,),
|
||||
dict(provider_type=cls.provider_type))()
|
||||
else:
|
||||
cls._catalog = DynamicCatalog()
|
||||
|
||||
cls._catalog.name = '.'.join((cls.__module__, cls.__name__))
|
||||
cls._catalog.bind_providers(dict(providers))
|
||||
|
||||
cls.cls_providers = dict(cls_providers)
|
||||
cls.inherited_providers = dict(inherited_providers)
|
||||
|
||||
cls.Bundle = cls._catalog.Bundle
|
||||
|
||||
return cls
|
||||
|
||||
@property
|
||||
def name(cls):
|
||||
"""Read-only property that represents catalog's name.
|
||||
|
||||
Catalog's name is catalog's module + catalog's class name.
|
||||
|
||||
:type: str
|
||||
"""
|
||||
return cls._catalog.name
|
||||
|
||||
@property
|
||||
def providers(cls):
|
||||
"""Read-only dictionary of all providers.
|
||||
|
||||
:type: dict[str, :py:class:`dependency_injector.providers.Provider`]
|
||||
"""
|
||||
return cls._catalog.providers
|
||||
|
||||
@property
|
||||
def overridden_by(cls):
|
||||
"""Tuple of overriding catalogs.
|
||||
|
||||
:type: tuple[
|
||||
:py:class:`DeclarativeCatalog` |
|
||||
:py:class:`DynamicCatalog`]
|
||||
"""
|
||||
return cls._catalog.overridden_by
|
||||
|
||||
@property
|
||||
def is_overridden(cls):
|
||||
"""Read-only property that is set to ``True`` if catalog is overridden.
|
||||
|
||||
:rtype: bool
|
||||
"""
|
||||
return cls._catalog.is_overridden
|
||||
|
||||
@property
|
||||
def last_overriding(cls):
|
||||
"""Read-only reference to the last overriding catalog, if any.
|
||||
|
||||
:type: :py:class:`DeclarativeCatalog` | :py:class:`DynamicCatalog` |
|
||||
None
|
||||
"""
|
||||
return cls._catalog.last_overriding
|
||||
|
||||
def __getattr__(cls, name):
|
||||
"""Return provider with specified name or raise en error.
|
||||
|
||||
:param name: Attribute's name.
|
||||
:type name: str
|
||||
|
||||
:raise: :py:exc:`dependency_injector.errors.UndefinedProviderError`
|
||||
"""
|
||||
raise UndefinedProviderError('There is no provider "{0}" in '
|
||||
'catalog {1}'.format(name, cls))
|
||||
|
||||
def __setattr__(cls, name, value):
|
||||
"""Handle setting of catalog attributes.
|
||||
|
||||
Setting of attributes works as usual, but if value of attribute is
|
||||
provider, this provider will be bound to catalog.
|
||||
|
||||
:param name: Attribute's name.
|
||||
:type name: str
|
||||
|
||||
:param value: Attribute's value.
|
||||
:type value: :py:class:`dependency_injector.providers.Provider` |
|
||||
object
|
||||
|
||||
:rtype: None
|
||||
"""
|
||||
if is_provider(value):
|
||||
cls.bind_provider(name, value, _set_as_attribute=False)
|
||||
return super(DeclarativeCatalogMetaClass, cls).__setattr__(name, value)
|
||||
|
||||
def __delattr__(cls, name):
|
||||
"""Handle deleting of catalog attibute.
|
||||
|
||||
Deleting of attributes works as usual, but if value of attribute is
|
||||
provider, this provider will be unbound from catalog.
|
||||
|
||||
:param name: Attribute's name.
|
||||
:type name: str
|
||||
|
||||
:rtype: None
|
||||
"""
|
||||
if is_provider(getattr(cls, name)):
|
||||
delattr(cls._catalog, name)
|
||||
return super(DeclarativeCatalogMetaClass, cls).__delattr__(name)
|
||||
|
||||
def __repr__(cls):
|
||||
"""Return string representation of the catalog.
|
||||
|
||||
:rtype: str
|
||||
"""
|
||||
return '<{0}({1})>'.format(cls.name,
|
||||
', '.join(six.iterkeys(cls.providers)))
|
||||
|
||||
__str__ = __repr__
|
||||
|
||||
|
||||
@six.add_metaclass(DeclarativeCatalogMetaClass)
|
||||
class DeclarativeCatalog(object):
|
||||
"""Declarative catalog of providers.
|
||||
|
||||
:py:class:`DeclarativeCatalog` is a catalog of providers that could be
|
||||
defined in declarative manner. It should cover most of the cases when list
|
||||
of providers that would be included in catalog is deterministic (catalog
|
||||
will not change its structure in runtime).
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class Services(DeclarativeCatalog):
|
||||
|
||||
auth = providers.Factory(AuthService)
|
||||
|
||||
users = providers.Factory(UsersService)
|
||||
|
||||
users_service = Services.users()
|
||||
|
||||
.. py:attribute:: Bundle
|
||||
|
||||
Catalog's bundle class.
|
||||
|
||||
:type: :py:class:`CatalogBundle`
|
||||
|
||||
.. py:attribute:: name
|
||||
|
||||
Read-only property that represents catalog's name.
|
||||
|
||||
Catalog's name is catalog's module + catalog's class name.
|
||||
|
||||
:type: str
|
||||
|
||||
.. py:attribute:: cls_providers
|
||||
|
||||
Read-only dictionary of current catalog providers.
|
||||
|
||||
:type: dict[str, :py:class:`dependency_injector.providers.Provider`]
|
||||
|
||||
.. py:attribute:: inherited_providers
|
||||
|
||||
Read-only dictionary of inherited providers.
|
||||
|
||||
:type: dict[str, :py:class:`dependency_injector.providers.Provider`]
|
||||
|
||||
.. py:attribute:: providers
|
||||
|
||||
Read-only dictionary of all providers.
|
||||
|
||||
:type: dict[str, :py:class:`dependency_injector.providers.Provider`]
|
||||
|
||||
.. py:attribute:: overridden_by
|
||||
|
||||
Tuple of overriding catalogs.
|
||||
|
||||
:type: tuple[:py:class:`DeclarativeCatalog` |
|
||||
:py:class:`DynamicCatalog`]
|
||||
|
||||
.. py:attribute:: is_overridden
|
||||
|
||||
Read-only property that is set to ``True`` if catalog is overridden.
|
||||
|
||||
:type: bool
|
||||
|
||||
.. py:attribute:: is_overridden
|
||||
|
||||
Read-only reference to the last overriding catalog, if any.
|
||||
|
||||
:type: :py:class:`DeclarativeCatalog` | :py:class:`DynamicCatalog` |
|
||||
None
|
||||
|
||||
.. py:attribute:: provider_type
|
||||
|
||||
If provider type is defined, :py:class:`DeclarativeCatalog` checks that
|
||||
all of its providers are instances of
|
||||
:py:attr:`DeclarativeCatalog.provider_type`.
|
||||
|
||||
:type: type | None
|
||||
"""
|
||||
|
||||
Bundle = CatalogBundle
|
||||
|
||||
name = str()
|
||||
|
||||
cls_providers = dict()
|
||||
inherited_providers = dict()
|
||||
providers = dict()
|
||||
|
||||
overridden_by = tuple()
|
||||
is_overridden = bool
|
||||
last_overriding = None
|
||||
|
||||
provider_type = None
|
||||
|
||||
_catalog = DynamicCatalog
|
||||
|
||||
__IS_CATALOG__ = True
|
||||
|
||||
@classmethod
|
||||
def is_bundle_owner(cls, bundle):
|
||||
"""Check if catalog is bundle owner.
|
||||
|
||||
:param bundle: Catalog's bundle instance.
|
||||
:type bundle: :py:class:`CatalogBundle`
|
||||
|
||||
:rtype: bool
|
||||
"""
|
||||
return cls._catalog.is_bundle_owner(bundle)
|
||||
|
||||
@classmethod
|
||||
def get_provider_bind_name(cls, provider):
|
||||
"""Return provider's name in catalog.
|
||||
|
||||
:param provider: Provider instance.
|
||||
:type provider: :py:class:`dependency_injector.providers.Provider`
|
||||
|
||||
:raise: :py:exc:`dependency_injector.errors.UndefinedProviderError`
|
||||
|
||||
:return: Provider's name.
|
||||
:rtype: str
|
||||
"""
|
||||
return cls._catalog.get_provider_bind_name(provider)
|
||||
|
||||
@classmethod
|
||||
def is_provider_bound(cls, provider):
|
||||
"""Check if provider is bound to the catalog.
|
||||
|
||||
:param provider: Provider instance.
|
||||
:type provider: :py:class:`dependency_injector.providers.Provider`
|
||||
|
||||
:rtype: bool
|
||||
"""
|
||||
return cls._catalog.is_provider_bound(provider)
|
||||
|
||||
@classmethod
|
||||
def filter(cls, provider_type):
|
||||
"""Return dictionary of providers, that are instance of provided type.
|
||||
|
||||
:param provider_type: Provider's type.
|
||||
:type provider_type: :py:class:`dependency_injector.providers.Provider`
|
||||
|
||||
:rtype: dict[str, :py:class:`dependency_injector.providers.Provider`]
|
||||
"""
|
||||
return cls._catalog.filter(provider_type)
|
||||
|
||||
@classmethod
|
||||
def override(cls, overriding):
|
||||
"""Override current catalog providers by overriding catalog providers.
|
||||
|
||||
:param overriding: Overriding catalog.
|
||||
:type overriding: :py:class:`DeclarativeCatalog` |
|
||||
:py:class:`DynamicCatalog`
|
||||
|
||||
:raise: :py:exc:`dependency_injector.errors.Error` if trying to
|
||||
override catalog by itself or its subclasses
|
||||
|
||||
:rtype: None
|
||||
"""
|
||||
if is_declarative_catalog(overriding) and issubclass(cls, overriding):
|
||||
raise Error('Catalog {0} could not be overridden '
|
||||
'with itself or its subclasses'.format(cls))
|
||||
return cls._catalog.override(overriding)
|
||||
|
||||
@classmethod
|
||||
def reset_last_overriding(cls):
|
||||
"""Reset last overriding catalog.
|
||||
|
||||
:rtype: None
|
||||
"""
|
||||
cls._catalog.reset_last_overriding()
|
||||
|
||||
@classmethod
|
||||
def reset_override(cls):
|
||||
"""Reset all overridings for all catalog providers.
|
||||
|
||||
:rtype: None
|
||||
"""
|
||||
cls._catalog.reset_override()
|
||||
|
||||
@classmethod
|
||||
def get_provider(cls, name):
|
||||
"""Return provider with specified name or raise an error.
|
||||
|
||||
:param name: Provider's name.
|
||||
:type name: str
|
||||
|
||||
:raise: :py:exc:`dependency_injector.errors.UndefinedProviderError`
|
||||
|
||||
:return: Provider with specified name.
|
||||
:rtype: :py:class:`dependency_injector.providers.Provider`
|
||||
"""
|
||||
return cls._catalog.get_provider(name)
|
||||
|
||||
get = get_provider # Backward compatibility for versions < 0.11.*
|
||||
|
||||
@classmethod
|
||||
def bind_provider(cls, name, provider, force=False,
|
||||
_set_as_attribute=True):
|
||||
"""Bind provider to catalog with specified name.
|
||||
|
||||
:param name: Name of the provider.
|
||||
:type name: str
|
||||
|
||||
:param provider: Provider instance.
|
||||
:type provider: :py:class:`dependency_injector.providers.Provider`
|
||||
|
||||
:param force: Force binding of provider.
|
||||
:type force: bool
|
||||
|
||||
:raise: :py:exc:`dependency_injector.errors.Error`
|
||||
|
||||
:rtype: None
|
||||
"""
|
||||
if cls._catalog.is_provider_bound(provider):
|
||||
bindind_name = cls._catalog.get_provider_bind_name(provider)
|
||||
if bindind_name == name and not force:
|
||||
return
|
||||
|
||||
cls._catalog.bind_provider(name, provider, force)
|
||||
cls.cls_providers[name] = provider
|
||||
|
||||
if _set_as_attribute:
|
||||
setattr(cls, name, provider)
|
||||
|
||||
@classmethod
|
||||
def bind_providers(cls, providers, force=False):
|
||||
"""Bind providers dictionary to catalog.
|
||||
|
||||
:param providers: Dictionary of providers, where key is a name
|
||||
and value is a provider.
|
||||
:type providers:
|
||||
dict[str, :py:class:`dependency_injector.providers.Provider`]
|
||||
|
||||
:param force: Force binding of providers.
|
||||
:type force: bool
|
||||
|
||||
:raise: :py:exc:`dependency_injector.errors.Error`
|
||||
|
||||
:rtype: None
|
||||
"""
|
||||
for name, provider in six.iteritems(providers):
|
||||
cls.bind_provider(name, provider, force=force)
|
||||
|
||||
@classmethod
|
||||
def has_provider(cls, name):
|
||||
"""Check if there is provider with certain name.
|
||||
|
||||
:param name: Provider's name.
|
||||
:type name: str
|
||||
|
||||
:rtype: bool
|
||||
"""
|
||||
return hasattr(cls, name)
|
||||
|
||||
has = has_provider # Backward compatibility for versions < 0.11.*
|
||||
|
||||
@classmethod
|
||||
def unbind_provider(cls, name):
|
||||
"""Remove provider binding.
|
||||
|
||||
:param name: Provider's name.
|
||||
:type name: str
|
||||
|
||||
:rtype: None
|
||||
"""
|
||||
delattr(cls, name)
|
||||
del cls.cls_providers[name]
|
||||
|
||||
@classmethod
|
||||
def __getattr__(cls, name): # pragma: no cover
|
||||
"""Return provider with specified name or raise en error.
|
||||
|
||||
:param name: Attribute's name.
|
||||
:type name: str
|
||||
|
||||
:raise: :py:exc:`dependency_injector.errors.UndefinedProviderError`
|
||||
"""
|
||||
raise NotImplementedError('Implementated in metaclass')
|
||||
|
||||
@classmethod
|
||||
def __setattr__(cls, name, value): # pragma: no cover
|
||||
"""Handle setting of catalog attributes.
|
||||
|
||||
Setting of attributes works as usual, but if value of attribute is
|
||||
provider, this provider will be bound to catalog.
|
||||
|
||||
:param name: Attribute's name.
|
||||
:type name: str
|
||||
|
||||
:param value: Attribute's value.
|
||||
:type value: :py:class:`dependency_injector.providers.Provider` |
|
||||
object
|
||||
|
||||
:rtype: None
|
||||
"""
|
||||
raise NotImplementedError('Implementated in metaclass')
|
||||
|
||||
@classmethod
|
||||
def __delattr__(cls, name): # pragma: no cover
|
||||
"""Handle deleting of catalog attibute.
|
||||
|
||||
Deleting of attributes works as usual, but if value of attribute is
|
||||
provider, this provider will be unbound from catalog.
|
||||
|
||||
:param name: Attribute's name.
|
||||
:type name: str
|
||||
|
||||
:rtype: None
|
||||
"""
|
||||
raise NotImplementedError('Implementated in metaclass')
|
||||
|
||||
|
||||
# Backward compatibility for versions < 0.11.*
|
||||
AbstractCatalog = DeclarativeCatalog
|
338
dependency_injector/catalogs/dynamic.py
Normal file
|
@ -0,0 +1,338 @@
|
|||
"""Dependency injector dynamic catalog module."""
|
||||
|
||||
import six
|
||||
|
||||
from dependency_injector.catalogs.bundle import CatalogBundle
|
||||
from dependency_injector.utils import (
|
||||
is_provider,
|
||||
ensure_is_provider,
|
||||
ensure_is_catalog_bundle,
|
||||
)
|
||||
from dependency_injector.errors import (
|
||||
Error,
|
||||
UndefinedProviderError,
|
||||
)
|
||||
|
||||
|
||||
@six.python_2_unicode_compatible
|
||||
class DynamicCatalog(object):
|
||||
"""Dynamic catalog of providers.
|
||||
|
||||
:py:class:`DynamicCatalog` is a catalog of providers that could be created
|
||||
in application's runtime. It should cover most of the cases when list of
|
||||
providers that would be included in catalog is non-deterministic in terms
|
||||
of apllication code (catalog's structure could be determined just after
|
||||
application will be started and will do some initial work, like parsing
|
||||
list of catalog's providers from the configuration).
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
services = DynamicCatalog(auth=providers.Factory(AuthService),
|
||||
users=providers.Factory(UsersService))
|
||||
|
||||
users_service = services.users()
|
||||
|
||||
.. py:attribute:: Bundle
|
||||
|
||||
Catalog's bundle class.
|
||||
|
||||
:type: :py:class:`CatalogBundle`
|
||||
|
||||
.. py:attribute:: name
|
||||
|
||||
Catalog's name.
|
||||
|
||||
By default, it is catalog's module + catalog's class name.
|
||||
|
||||
:type: str
|
||||
|
||||
.. py:attribute:: providers
|
||||
|
||||
Dictionary of all providers.
|
||||
|
||||
:type: dict[str, :py:class:`dependency_injector.providers.Provider`]
|
||||
|
||||
.. py:attribute:: overridden_by
|
||||
|
||||
Tuple of overriding catalogs.
|
||||
|
||||
:type: tuple[
|
||||
:py:class:`DeclarativeCatalog` | :py:class:`DynamicCatalog`]
|
||||
|
||||
.. py:attribute:: provider_type
|
||||
|
||||
If provider type is defined, :py:class:`DynamicCatalog` checks that
|
||||
all of its providers are instances of
|
||||
:py:attr:`DynamicCatalog.provider_type`.
|
||||
|
||||
:type: type | None
|
||||
"""
|
||||
|
||||
provider_type = None
|
||||
|
||||
__IS_CATALOG__ = True
|
||||
__slots__ = ('name', 'providers', 'provider_names', 'overridden_by',
|
||||
'Bundle')
|
||||
|
||||
def __init__(self, **providers):
|
||||
"""Initializer.
|
||||
|
||||
:param providers: Dictionary of catalog providers.
|
||||
:type providers:
|
||||
dict[str, :py:class:`dependency_injector.providers.Provider`]
|
||||
"""
|
||||
self.Bundle = CatalogBundle.sub_cls_factory(self)
|
||||
self.name = '.'.join((self.__class__.__module__,
|
||||
self.__class__.__name__))
|
||||
self.providers = dict()
|
||||
self.provider_names = dict()
|
||||
self.overridden_by = tuple()
|
||||
self.bind_providers(providers)
|
||||
super(DynamicCatalog, self).__init__()
|
||||
|
||||
def is_bundle_owner(self, bundle):
|
||||
"""Check if catalog is bundle owner.
|
||||
|
||||
:param bundle: Catalog's bundle instance.
|
||||
:type bundle: :py:class:`CatalogBundle`
|
||||
|
||||
:rtype: bool
|
||||
"""
|
||||
return ensure_is_catalog_bundle(bundle) and bundle.catalog is self
|
||||
|
||||
def get_provider_bind_name(self, provider):
|
||||
"""Return provider's name in catalog.
|
||||
|
||||
:param provider: Provider instance.
|
||||
:type provider: :py:class:`dependency_injector.providers.Provider`
|
||||
|
||||
:raise: :py:exc:`dependency_injector.errors.UndefinedProviderError`
|
||||
|
||||
:return: Provider's name.
|
||||
:rtype: str
|
||||
"""
|
||||
if not self.is_provider_bound(provider):
|
||||
raise Error('Can not find bind name for {0} in catalog {1}'.format(
|
||||
provider, self))
|
||||
return self.provider_names[provider]
|
||||
|
||||
def is_provider_bound(self, provider):
|
||||
"""Check if provider is bound to the catalog.
|
||||
|
||||
:param provider: Provider instance.
|
||||
:type provider: :py:class:`dependency_injector.providers.Provider`
|
||||
|
||||
:rtype: bool
|
||||
"""
|
||||
return provider in self.provider_names
|
||||
|
||||
def filter(self, provider_type):
|
||||
"""Return dictionary of providers, that are instance of provided type.
|
||||
|
||||
:param provider_type: Provider's type.
|
||||
:type provider_type: :py:class:`dependency_injector.providers.Provider`
|
||||
|
||||
:rtype: dict[str, :py:class:`dependency_injector.providers.Provider`]
|
||||
"""
|
||||
return dict((name, provider)
|
||||
for name, provider in six.iteritems(self.providers)
|
||||
if isinstance(provider, provider_type))
|
||||
|
||||
@property
|
||||
def is_overridden(self):
|
||||
"""Read-only property that is set to ``True`` if catalog is overridden.
|
||||
|
||||
:rtype: bool
|
||||
"""
|
||||
return bool(self.overridden_by)
|
||||
|
||||
@property
|
||||
def last_overriding(self):
|
||||
"""Read-only reference to the last overriding catalog, if any.
|
||||
|
||||
:type: :py:class:`DeclarativeCatalog` | :py:class:`DynamicCatalog` |
|
||||
None
|
||||
"""
|
||||
return self.overridden_by[-1] if self.overridden_by else None
|
||||
|
||||
def override(self, overriding):
|
||||
"""Override current catalog providers by overriding catalog providers.
|
||||
|
||||
:param overriding: Overriding catalog.
|
||||
:type overriding: :py:class:`DeclarativeCatalog` |
|
||||
:py:class:`DynamicCatalog`
|
||||
|
||||
:raise: :py:exc:`dependency_injector.errors.Error` if trying to
|
||||
override catalog by itself
|
||||
|
||||
:rtype: None
|
||||
"""
|
||||
if overriding is self:
|
||||
raise Error('Catalog {0} could not be overridden '
|
||||
'with itself'.format(self))
|
||||
self.overridden_by += (overriding,)
|
||||
for name, provider in six.iteritems(overriding.providers):
|
||||
self.get_provider(name).override(provider)
|
||||
|
||||
def reset_last_overriding(self):
|
||||
"""Reset last overriding catalog.
|
||||
|
||||
:rtype: None
|
||||
"""
|
||||
if not self.is_overridden:
|
||||
raise Error('Catalog {0} is not overridden'.format(self))
|
||||
self.overridden_by = self.overridden_by[:-1]
|
||||
for provider in six.itervalues(self.providers):
|
||||
provider.reset_last_overriding()
|
||||
|
||||
def reset_override(self):
|
||||
"""Reset all overridings for all catalog providers.
|
||||
|
||||
:rtype: None
|
||||
"""
|
||||
self.overridden_by = tuple()
|
||||
for provider in six.itervalues(self.providers):
|
||||
provider.reset_override()
|
||||
|
||||
def get_provider(self, name):
|
||||
"""Return provider with specified name or raise an error.
|
||||
|
||||
:param name: Provider's name.
|
||||
:type name: str
|
||||
|
||||
:raise: :py:exc:`dependency_injector.errors.UndefinedProviderError`
|
||||
|
||||
:return: Provider with specified name.
|
||||
:rtype: :py:class:`dependency_injector.providers.Provider`
|
||||
"""
|
||||
try:
|
||||
return self.providers[name]
|
||||
except KeyError:
|
||||
raise UndefinedProviderError('{0} has no provider with such '
|
||||
'name - {1}'.format(self, name))
|
||||
|
||||
def bind_provider(self, name, provider, force=False):
|
||||
"""Bind provider to catalog with specified name.
|
||||
|
||||
:param name: Name of the provider.
|
||||
:type name: str
|
||||
|
||||
:param provider: Provider instance.
|
||||
:type provider: :py:class:`dependency_injector.providers.Provider`
|
||||
|
||||
:param force: Force binding of provider.
|
||||
:type force: bool
|
||||
|
||||
:raise: :py:exc:`dependency_injector.errors.Error`
|
||||
|
||||
:rtype: None
|
||||
"""
|
||||
provider = ensure_is_provider(provider)
|
||||
|
||||
if (self.__class__.provider_type and
|
||||
not isinstance(provider, self.__class__.provider_type)):
|
||||
raise Error('{0} can contain only {1} instances'.format(
|
||||
self, self.__class__.provider_type))
|
||||
|
||||
if not force:
|
||||
if name in self.providers:
|
||||
raise Error('Catalog {0} already has provider with '
|
||||
'such name - {1}'.format(self, name))
|
||||
if provider in self.provider_names:
|
||||
raise Error('Catalog {0} already has such provider '
|
||||
'instance - {1}'.format(self, provider))
|
||||
|
||||
self.providers[name] = provider
|
||||
self.provider_names[provider] = name
|
||||
|
||||
def bind_providers(self, providers, force=False):
|
||||
"""Bind providers dictionary to catalog.
|
||||
|
||||
:param providers: Dictionary of providers, where key is a name
|
||||
and value is a provider.
|
||||
:type providers:
|
||||
dict[str, :py:class:`dependency_injector.providers.Provider`]
|
||||
|
||||
:param force: Force binding of providers.
|
||||
:type force: bool
|
||||
|
||||
:raise: :py:exc:`dependency_injector.errors.Error`
|
||||
|
||||
:rtype: None
|
||||
"""
|
||||
for name, provider in six.iteritems(providers):
|
||||
self.bind_provider(name, provider, force)
|
||||
|
||||
def has_provider(self, name):
|
||||
"""Check if there is provider with certain name.
|
||||
|
||||
:param name: Provider's name.
|
||||
:type name: str
|
||||
|
||||
:rtype: bool
|
||||
"""
|
||||
return name in self.providers
|
||||
|
||||
def unbind_provider(self, name):
|
||||
"""Remove provider binding.
|
||||
|
||||
:param name: Provider's name.
|
||||
:type name: str
|
||||
|
||||
:rtype: None
|
||||
"""
|
||||
provider = self.get_provider(name)
|
||||
del self.providers[name]
|
||||
del self.provider_names[provider]
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""Return provider with specified name or raise en error.
|
||||
|
||||
:param name: Attribute's name.
|
||||
:type name: str
|
||||
|
||||
:raise: :py:exc:`dependency_injector.errors.UndefinedProviderError`
|
||||
"""
|
||||
return self.get_provider(name)
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
"""Handle setting of catalog attributes.
|
||||
|
||||
Setting of attributes works as usual, but if value of attribute is
|
||||
provider, this provider will be bound to catalog.
|
||||
|
||||
:param name: Attribute's name.
|
||||
:type name: str
|
||||
|
||||
:param value: Attribute's value.
|
||||
:type value: :py:class:`dependency_injector.providers.Provider` |
|
||||
object
|
||||
|
||||
:rtype: None
|
||||
"""
|
||||
if is_provider(value):
|
||||
return self.bind_provider(name, value)
|
||||
return super(DynamicCatalog, self).__setattr__(name, value)
|
||||
|
||||
def __delattr__(self, name):
|
||||
"""Handle deleting of catalog attibute.
|
||||
|
||||
Deleting of attributes works as usual, but if value of attribute is
|
||||
provider, this provider will be unbound from catalog.
|
||||
|
||||
:param name: Attribute's name.
|
||||
:type name: str
|
||||
|
||||
:rtype: None
|
||||
"""
|
||||
self.unbind_provider(name)
|
||||
|
||||
def __repr__(self):
|
||||
"""Return Python representation of catalog.
|
||||
|
||||
:rtype: str
|
||||
"""
|
||||
return '<{0}({1})>'.format(self.name,
|
||||
', '.join(six.iterkeys(self.providers)))
|
||||
|
||||
__str__ = __repr__
|
62
dependency_injector/catalogs/utils.py
Normal file
|
@ -0,0 +1,62 @@
|
|||
"""Dependency injector catalog utils."""
|
||||
|
||||
import six
|
||||
|
||||
from dependency_injector.utils import _copy_providers
|
||||
from dependency_injector.errors import UndefinedProviderError
|
||||
|
||||
|
||||
def copy(catalog):
|
||||
""":py:class:`DeclarativeCatalog` copying decorator.
|
||||
|
||||
This decorator copy all providers from provided catalog to decorated one.
|
||||
If one of the decorated catalog providers matches to source catalog
|
||||
providers by name, it would be replaced by reference.
|
||||
|
||||
:param catalog: Catalog that should be copied by decorated catalog.
|
||||
:type catalog: :py:class:`DeclarativeCatalog`
|
||||
|
||||
:return: Declarative catalog's copying decorator.
|
||||
:rtype:
|
||||
callable(:py:class:`DeclarativeCatalog`)
|
||||
"""
|
||||
def decorator(copied_catalog):
|
||||
"""Copying decorator.
|
||||
|
||||
:param copied_catalog: Decorated catalog.
|
||||
:type copied_catalog: :py:class:`DeclarativeCatalog`
|
||||
|
||||
:return: Decorated catalog.
|
||||
:rtype:
|
||||
:py:class:`DeclarativeCatalog`
|
||||
"""
|
||||
memo = dict()
|
||||
for name, provider in six.iteritems(copied_catalog.cls_providers):
|
||||
try:
|
||||
source_provider = catalog.get_provider(name)
|
||||
except UndefinedProviderError:
|
||||
pass
|
||||
else:
|
||||
memo[id(source_provider)] = provider
|
||||
|
||||
copied_catalog.bind_providers(_copy_providers(catalog.providers, memo),
|
||||
force=True)
|
||||
|
||||
return copied_catalog
|
||||
return decorator
|
||||
|
||||
|
||||
def override(catalog):
|
||||
""":py:class:`DeclarativeCatalog` overriding decorator.
|
||||
|
||||
:param catalog: Catalog that should be overridden by decorated catalog.
|
||||
:type catalog: :py:class:`DeclarativeCatalog`
|
||||
|
||||
:return: Declarative catalog's overriding decorator.
|
||||
:rtype: callable(:py:class:`DeclarativeCatalog`)
|
||||
"""
|
||||
def decorator(overriding_catalog):
|
||||
"""Overriding decorator."""
|
||||
catalog.override(overriding_catalog)
|
||||
return overriding_catalog
|
||||
return decorator
|
22
dependency_injector/errors.py
Normal file
|
@ -0,0 +1,22 @@
|
|||
"""Errors module."""
|
||||
|
||||
|
||||
class Error(Exception):
|
||||
"""Base error.
|
||||
|
||||
All dependency injector errors extend this error class.
|
||||
"""
|
||||
|
||||
|
||||
class UndefinedProviderError(Error, AttributeError):
|
||||
"""Undefined provider error.
|
||||
|
||||
This error is used when provider could not be defined, for example:
|
||||
|
||||
- provider with certain name could not be defined
|
||||
- catalog's name of the certain provider could not be defined
|
||||
- etc...
|
||||
|
||||
Also this error extends standard :py:class:`AttributeError`. This gives
|
||||
possibility to use it correctly with ``__getattr__()``.
|
||||
"""
|
276
dependency_injector/injections.py
Normal file
|
@ -0,0 +1,276 @@
|
|||
"""Injections module."""
|
||||
|
||||
import six
|
||||
|
||||
from dependency_injector.utils import (
|
||||
is_provider,
|
||||
is_delegated_provider,
|
||||
is_injection,
|
||||
is_arg_injection,
|
||||
is_kwarg_injection,
|
||||
fetch_cls_init,
|
||||
)
|
||||
|
||||
from dependency_injector.errors import Error
|
||||
|
||||
|
||||
@six.python_2_unicode_compatible
|
||||
class Injection(object):
|
||||
"""Base injection class.
|
||||
|
||||
All injections extend this class.
|
||||
|
||||
.. py:attribute:: injectable
|
||||
|
||||
Injectable value, could be provider or any other object.
|
||||
|
||||
:type: object | :py:class:`dependency_injector.providers.Provider`
|
||||
|
||||
.. py:attribute:: call_injectable
|
||||
|
||||
Flag that is set to ``True`` if it is needed to call injectable.
|
||||
|
||||
Injectable needs to be called if it is not delegated provider.
|
||||
|
||||
:type: bool
|
||||
"""
|
||||
|
||||
__IS_INJECTION__ = True
|
||||
__slots__ = ('injectable', 'call_injectable')
|
||||
|
||||
def __init__(self, injectable):
|
||||
"""Initializer.
|
||||
|
||||
:param injectable: Injectable value, could be provider or any
|
||||
other object.
|
||||
:type injectable: object |
|
||||
:py:class:`dependency_injector.providers.Provider`
|
||||
"""
|
||||
self.injectable = injectable
|
||||
self.call_injectable = (is_provider(injectable) and
|
||||
not is_delegated_provider(injectable))
|
||||
super(Injection, self).__init__()
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
"""Read-only property that represents injectable value.
|
||||
|
||||
Injectable values and delegated providers are provided "as is".
|
||||
Other providers will be called every time, when injection needs to
|
||||
be done.
|
||||
|
||||
:rtype: object
|
||||
"""
|
||||
if self.call_injectable:
|
||||
return self.injectable.provide()
|
||||
return self.injectable
|
||||
|
||||
def __str__(self):
|
||||
"""Return string representation of provider.
|
||||
|
||||
:rtype: str
|
||||
"""
|
||||
return '<{injection}({injectable}) at {address}>'.format(
|
||||
injection='.'.join((self.__class__.__module__,
|
||||
self.__class__.__name__)),
|
||||
injectable=repr(self.injectable),
|
||||
address=hex(id(self)))
|
||||
|
||||
__repr__ = __str__
|
||||
|
||||
|
||||
class Arg(Injection):
|
||||
"""Positional argument injection."""
|
||||
|
||||
__IS_ARG_INJECTION__ = True
|
||||
|
||||
|
||||
@six.python_2_unicode_compatible
|
||||
class _NamedInjection(Injection):
|
||||
"""Base class of named injections.
|
||||
|
||||
.. py:attribute:: name
|
||||
|
||||
Injection target's name (keyword argument, attribute, method).
|
||||
|
||||
:type: str
|
||||
"""
|
||||
|
||||
__slots__ = ('name',)
|
||||
|
||||
def __init__(self, name, injectable):
|
||||
"""Initializer.
|
||||
|
||||
:param name: Injection target's name.
|
||||
:type name: str
|
||||
|
||||
:param injectable: Injectable value, could be provider or any
|
||||
other object.
|
||||
:type injectable: object |
|
||||
:py:class:`dependency_injector.providers.Provider`
|
||||
"""
|
||||
self.name = name
|
||||
super(_NamedInjection, self).__init__(injectable)
|
||||
|
||||
def __str__(self):
|
||||
"""Return string representation of provider.
|
||||
|
||||
:rtype: str
|
||||
"""
|
||||
return '<{injection}({name}, {injectable}) at {address}>'.format(
|
||||
name=repr(self.name),
|
||||
injection='.'.join((self.__class__.__module__,
|
||||
self.__class__.__name__)),
|
||||
injectable=repr(self.injectable),
|
||||
address=hex(id(self)))
|
||||
|
||||
__repr__ = __str__
|
||||
|
||||
|
||||
class KwArg(_NamedInjection):
|
||||
"""Keyword argument injection.
|
||||
|
||||
.. py:attribute:: name
|
||||
|
||||
Keyword argument's name.
|
||||
|
||||
:type: str
|
||||
"""
|
||||
|
||||
__IS_KWARG_INJECTION__ = True
|
||||
|
||||
|
||||
class Attribute(_NamedInjection):
|
||||
"""Attribute injection.
|
||||
|
||||
.. py:attribute:: name
|
||||
|
||||
Attribute's name.
|
||||
|
||||
:type: str
|
||||
"""
|
||||
|
||||
__IS_ATTRIBUTE_INJECTION__ = True
|
||||
|
||||
|
||||
class Method(_NamedInjection):
|
||||
"""Method injection.
|
||||
|
||||
.. py:attribute:: name
|
||||
|
||||
Method's name.
|
||||
|
||||
:type: str
|
||||
"""
|
||||
|
||||
__IS_METHOD_INJECTION__ = True
|
||||
|
||||
|
||||
def inject(*args, **kwargs):
|
||||
"""Dependency injection decorator.
|
||||
|
||||
:py:func:`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.
|
||||
|
||||
:py:func:`inject` decorator supports different syntaxes of passing
|
||||
injections:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# Positional arguments injections (simplified syntax):
|
||||
@inject(1, 2)
|
||||
def some_function(arg1, arg2):
|
||||
pass
|
||||
|
||||
# Keyword arguments injections (simplified syntax):
|
||||
@inject(arg1=1)
|
||||
@inject(arg2=2)
|
||||
def some_function(arg1, arg2):
|
||||
pass
|
||||
|
||||
# Keyword arguments injections (extended (full) syntax):
|
||||
@inject(KwArg('arg1', 1))
|
||||
@inject(KwArg('arg2', 2))
|
||||
def some_function(arg1, arg2):
|
||||
pass
|
||||
|
||||
# Keyword arguments injections into class init (simplified syntax):
|
||||
@inject(arg1=1)
|
||||
@inject(arg2=2)
|
||||
class SomeClass(object):
|
||||
|
||||
def __init__(self, arg1, arg2):
|
||||
pass
|
||||
|
||||
:param args: Tuple of context positional arguments.
|
||||
:type args: tuple[object]
|
||||
|
||||
:param kwargs: Dictionary of context keyword arguments.
|
||||
:type kwargs: dict[str, object]
|
||||
|
||||
:return: Class / callable decorator
|
||||
:rtype: (callable) -> (type | callable)
|
||||
"""
|
||||
arg_injections = _parse_args_injections(args)
|
||||
kwarg_injections = _parse_kwargs_injections(args, kwargs)
|
||||
|
||||
def decorator(callback_or_cls):
|
||||
"""Dependency injection decorator."""
|
||||
if isinstance(callback_or_cls, six.class_types):
|
||||
cls = callback_or_cls
|
||||
cls_init = fetch_cls_init(cls)
|
||||
if not cls_init:
|
||||
raise Error(
|
||||
'Class {0}.{1} has no __init__() '.format(cls.__module__,
|
||||
cls.__name__) +
|
||||
'method and could not be decorated with @inject decorator')
|
||||
cls.__init__ = decorator(cls_init)
|
||||
return cls
|
||||
|
||||
callback = callback_or_cls
|
||||
|
||||
if hasattr(callback, '__INJECT_DECORATED__'):
|
||||
callback.args += arg_injections
|
||||
callback.kwargs += kwarg_injections
|
||||
callback.injections += arg_injections + kwarg_injections
|
||||
return callback
|
||||
|
||||
@six.wraps(callback)
|
||||
def decorated(*args, **kwargs):
|
||||
"""Decorated with dependency injection callback."""
|
||||
if decorated.args:
|
||||
args = tuple(arg.value for arg in decorated.args) + args
|
||||
|
||||
for kwarg in decorated.kwargs:
|
||||
if kwarg.name not in kwargs:
|
||||
kwargs[kwarg.name] = kwarg.value
|
||||
|
||||
return callback(*args, **kwargs)
|
||||
|
||||
decorated.__INJECT_DECORATED__ = True
|
||||
decorated.origin = callback
|
||||
decorated.args = arg_injections
|
||||
decorated.kwargs = kwarg_injections
|
||||
decorated.injections = arg_injections + kwarg_injections
|
||||
|
||||
return decorated
|
||||
return decorator
|
||||
|
||||
|
||||
def _parse_args_injections(args):
|
||||
"""Parse positional argument injections according to current syntax."""
|
||||
return tuple(Arg(arg) if not is_injection(arg) else arg
|
||||
for arg in args
|
||||
if not is_injection(arg) or is_arg_injection(arg))
|
||||
|
||||
|
||||
def _parse_kwargs_injections(args, kwargs):
|
||||
"""Parse keyword argument injections according to current syntax."""
|
||||
kwarg_injections = tuple(injection
|
||||
for injection in args
|
||||
if is_kwarg_injection(injection))
|
||||
if kwargs:
|
||||
kwarg_injections += tuple(KwArg(name, value)
|
||||
for name, value in six.iteritems(kwargs))
|
||||
return kwarg_injections
|
60
dependency_injector/providers/__init__.py
Normal file
|
@ -0,0 +1,60 @@
|
|||
"""Dependency injector providers package."""
|
||||
|
||||
from dependency_injector.providers.base import (
|
||||
Provider,
|
||||
Delegate,
|
||||
Static,
|
||||
StaticProvider,
|
||||
ExternalDependency,
|
||||
)
|
||||
from dependency_injector.providers.callable import (
|
||||
Callable,
|
||||
DelegatedCallable,
|
||||
)
|
||||
from dependency_injector.providers.creational import (
|
||||
Factory,
|
||||
DelegatedFactory,
|
||||
Singleton,
|
||||
DelegatedSingleton,
|
||||
)
|
||||
from dependency_injector.providers.static import (
|
||||
Object,
|
||||
Value,
|
||||
Class,
|
||||
Function,
|
||||
)
|
||||
from dependency_injector.providers.config import (
|
||||
Config,
|
||||
ChildConfig,
|
||||
)
|
||||
from dependency_injector.providers.utils import (
|
||||
OverridingContext,
|
||||
override,
|
||||
)
|
||||
|
||||
|
||||
__all__ = (
|
||||
'Provider',
|
||||
'Delegate',
|
||||
'Static', 'StaticProvider',
|
||||
'ExternalDependency',
|
||||
|
||||
'Callable',
|
||||
'DelegatedCallable',
|
||||
|
||||
'Factory',
|
||||
'DelegatedFactory',
|
||||
'Singleton',
|
||||
'DelegatedSingleton',
|
||||
|
||||
'Object',
|
||||
'Value',
|
||||
'Class',
|
||||
'Function',
|
||||
|
||||
'Config',
|
||||
'ChildConfig',
|
||||
|
||||
'OverridingContext',
|
||||
'override',
|
||||
)
|
358
dependency_injector/providers/base.py
Normal file
|
@ -0,0 +1,358 @@
|
|||
"""Dependency injector base providers."""
|
||||
|
||||
import six
|
||||
|
||||
from dependency_injector.providers.utils import OverridingContext
|
||||
from dependency_injector.errors import Error
|
||||
from dependency_injector.utils import (
|
||||
is_provider,
|
||||
ensure_is_provider,
|
||||
represent_provider,
|
||||
)
|
||||
|
||||
|
||||
@six.python_2_unicode_compatible
|
||||
class Provider(object):
|
||||
"""Base provider class.
|
||||
|
||||
:py:class:`Provider` is callable (implements ``__call__`` method). Every
|
||||
call to provider object returns provided result, according to the providing
|
||||
strategy of particular provider. This ``callable`` functionality is a
|
||||
regular part of providers API and it should be the same for all provider's
|
||||
subclasses.
|
||||
|
||||
Implementation of particular providing strategy should be done in
|
||||
:py:meth:`Provider._provide` of :py:class:`Provider` subclass. Current
|
||||
method is called every time when not overridden provider is called.
|
||||
|
||||
:py:class:`Provider` implements provider overriding logic that should be
|
||||
also common for all providers:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
provider1 = Factory(SomeClass)
|
||||
provider2 = Factory(ChildSomeClass)
|
||||
|
||||
provider1.override(provider2)
|
||||
|
||||
some_instance = provider1()
|
||||
assert isinstance(some_instance, ChildSomeClass)
|
||||
|
||||
Also :py:class:`Provider` implements helper function for creating its
|
||||
delegates:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
provider = Factory(object)
|
||||
delegate = provider.delegate()
|
||||
|
||||
delegated = delegate()
|
||||
|
||||
assert provider is delegated
|
||||
|
||||
All providers should extend this class.
|
||||
|
||||
.. py:attribute:: overridden_by
|
||||
|
||||
Tuple of overriding providers, if any.
|
||||
|
||||
:type: tuple[:py:class:`Provider`] | None
|
||||
"""
|
||||
|
||||
__IS_PROVIDER__ = True
|
||||
__OPTIMIZED_CALLS__ = True
|
||||
__slots__ = ('overridden_by', 'provide', '__call__')
|
||||
|
||||
def __init__(self):
|
||||
"""Initializer."""
|
||||
self.overridden_by = None
|
||||
super(Provider, self).__init__()
|
||||
# Enable __call__() / _provide() optimization
|
||||
if self.__class__.__OPTIMIZED_CALLS__:
|
||||
self.__call__ = self.provide = self._provide
|
||||
|
||||
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 _call_last_overriding(self, *args, **kwargs):
|
||||
"""Call last overriding provider and return result."""
|
||||
return self.last_overriding(*args, **kwargs)
|
||||
|
||||
@property
|
||||
def is_overridden(self):
|
||||
"""Read-only property that is set to ``True`` if provider is overridden.
|
||||
|
||||
:rtype: bool
|
||||
"""
|
||||
return bool(self.overridden_by)
|
||||
|
||||
@property
|
||||
def last_overriding(self):
|
||||
"""Read-only reference to the last overriding provider, if any.
|
||||
|
||||
:type: :py:class:`Provider` | None
|
||||
"""
|
||||
return self.overridden_by[-1] if self.overridden_by else None
|
||||
|
||||
def override(self, provider):
|
||||
"""Override provider with another provider.
|
||||
|
||||
:param provider: Overriding provider.
|
||||
:type provider: :py:class:`Provider`
|
||||
|
||||
:raise: :py:exc:`dependency_injector.errors.Error`
|
||||
|
||||
:return: Overriding provider.
|
||||
:rtype: :py:class:`Provider`
|
||||
"""
|
||||
if provider is self:
|
||||
raise Error('Provider {0} could not be overridden '
|
||||
'with itself'.format(self))
|
||||
|
||||
if not is_provider(provider):
|
||||
provider = Static(provider)
|
||||
|
||||
if not self.is_overridden:
|
||||
self.overridden_by = (ensure_is_provider(provider),)
|
||||
else:
|
||||
self.overridden_by += (ensure_is_provider(provider),)
|
||||
|
||||
# Disable __call__() / _provide() optimization
|
||||
if self.__class__.__OPTIMIZED_CALLS__:
|
||||
self.__call__ = self.provide = self._call_last_overriding
|
||||
|
||||
return OverridingContext(self, provider)
|
||||
|
||||
def reset_last_overriding(self):
|
||||
"""Reset last overriding provider.
|
||||
|
||||
:raise: :py:exc:`dependency_injector.errors.Error` if provider is not
|
||||
overridden.
|
||||
|
||||
:rtype: None
|
||||
"""
|
||||
if not self.overridden_by:
|
||||
raise Error('Provider {0} is not overridden'.format(str(self)))
|
||||
self.overridden_by = self.overridden_by[:-1]
|
||||
|
||||
if not self.is_overridden:
|
||||
# Enable __call__() / _provide() optimization
|
||||
if self.__class__.__OPTIMIZED_CALLS__:
|
||||
self.__call__ = self.provide = self._provide
|
||||
|
||||
def reset_override(self):
|
||||
"""Reset all overriding providers.
|
||||
|
||||
:rtype: None
|
||||
"""
|
||||
self.overridden_by = None
|
||||
|
||||
# Enable __call__() / _provide() optimization
|
||||
if self.__class__.__OPTIMIZED_CALLS__:
|
||||
self.__call__ = self.provide = self._provide
|
||||
|
||||
def delegate(self):
|
||||
"""Return provider's delegate.
|
||||
|
||||
:rtype: :py:class:`Delegate`
|
||||
"""
|
||||
return Delegate(self)
|
||||
|
||||
def __str__(self):
|
||||
"""Return string representation of provider.
|
||||
|
||||
:rtype: str
|
||||
"""
|
||||
return represent_provider(provider=self, provides=None)
|
||||
|
||||
__repr__ = __str__
|
||||
|
||||
|
||||
@six.python_2_unicode_compatible
|
||||
class Delegate(Provider):
|
||||
""":py:class:`Delegate` provider delegates another provider.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
provider = Factory(object)
|
||||
delegate = Delegate(provider)
|
||||
|
||||
delegated = delegate()
|
||||
|
||||
assert provider is delegated
|
||||
|
||||
.. py:attribute:: delegated
|
||||
|
||||
Delegated provider.
|
||||
|
||||
:type: :py:class:`Provider`
|
||||
"""
|
||||
|
||||
__slots__ = ('delegated',)
|
||||
|
||||
def __init__(self, delegated):
|
||||
"""Initializer.
|
||||
|
||||
:provider delegated: Delegated provider.
|
||||
:type delegated: :py:class:`Provider`
|
||||
"""
|
||||
self.delegated = ensure_is_provider(delegated)
|
||||
super(Delegate, self).__init__()
|
||||
|
||||
def _provide(self, *args, **kwargs):
|
||||
"""Return provided instance.
|
||||
|
||||
:param args: Tuple of context positional arguments.
|
||||
:type args: tuple[object]
|
||||
|
||||
:param kwargs: Dictionary of context keyword arguments.
|
||||
:type kwargs: dict[str, object]
|
||||
|
||||
:rtype: object
|
||||
"""
|
||||
return self.delegated
|
||||
|
||||
def __str__(self):
|
||||
"""Return string representation of provider.
|
||||
|
||||
:rtype: str
|
||||
"""
|
||||
return represent_provider(provider=self, provides=self.delegated)
|
||||
|
||||
__repr__ = __str__
|
||||
|
||||
|
||||
@six.python_2_unicode_compatible
|
||||
class Static(Provider):
|
||||
""":py:class:`Static` provider returns provided instance "as is".
|
||||
|
||||
:py:class:`Static` provider is base implementation that provides exactly
|
||||
the same as it got on input.
|
||||
|
||||
.. py:attribute:: provides
|
||||
|
||||
Value that have to be provided.
|
||||
|
||||
:type: object
|
||||
"""
|
||||
|
||||
__slots__ = ('provides',)
|
||||
|
||||
def __init__(self, provides):
|
||||
"""Initializer.
|
||||
|
||||
:param provides: Value that have to be provided.
|
||||
:type provides: object
|
||||
"""
|
||||
self.provides = provides
|
||||
super(Static, self).__init__()
|
||||
|
||||
def _provide(self, *args, **kwargs):
|
||||
"""Return provided instance.
|
||||
|
||||
:param args: Tuple of context positional arguments.
|
||||
:type args: tuple[object]
|
||||
|
||||
:param kwargs: Dictionary of context keyword arguments.
|
||||
:type kwargs: dict[str, object]
|
||||
|
||||
:rtype: object
|
||||
"""
|
||||
return self.provides
|
||||
|
||||
def __str__(self):
|
||||
"""Return string representation of provider.
|
||||
|
||||
:rtype: str
|
||||
"""
|
||||
return represent_provider(provider=self, provides=self.provides)
|
||||
|
||||
__repr__ = __str__
|
||||
|
||||
|
||||
StaticProvider = Static
|
||||
# Backward compatibility for versions < 1.11.1
|
||||
|
||||
|
||||
@six.python_2_unicode_compatible
|
||||
class ExternalDependency(Provider):
|
||||
""":py:class:`ExternalDependency` provider describes dependency interface.
|
||||
|
||||
This provider is used for description of dependency interface. That might
|
||||
be useful when dependency could be provided in the client's code only,
|
||||
but it's interface is known. Such situations could happen when required
|
||||
dependency has non-determenistic list of dependencies itself.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
database_provider = ExternalDependency(sqlite3.dbapi2.Connection)
|
||||
database_provider.override(Factory(sqlite3.connect, ':memory:'))
|
||||
|
||||
database = database_provider()
|
||||
|
||||
.. py:attribute:: instance_of
|
||||
|
||||
Class of required dependency.
|
||||
|
||||
:type: type
|
||||
"""
|
||||
|
||||
__OPTIMIZED_CALLS__ = False
|
||||
__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.
|
||||
|
||||
:param args: Tuple of context positional arguments.
|
||||
:type args: tuple[object]
|
||||
|
||||
:param kwargs: Dictionary of context keyword arguments.
|
||||
:type kwargs: dict[str, object]
|
||||
|
||||
:raise: :py:exc:`dependency_injector.errors.Error`
|
||||
|
||||
:rtype: object
|
||||
"""
|
||||
if not self.is_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.
|
||||
|
||||
:param provider: Provider that provides required dependency.
|
||||
:type provider: :py:class:`Provider`
|
||||
|
||||
:rtype: None
|
||||
"""
|
||||
return self.override(provider)
|
||||
|
||||
def __str__(self):
|
||||
"""Return string representation of provider.
|
||||
|
||||
:rtype: str
|
||||
"""
|
||||
return represent_provider(provider=self, provides=self.instance_of)
|
||||
|
||||
__repr__ = __str__
|
147
dependency_injector/providers/callable.py
Normal file
|
@ -0,0 +1,147 @@
|
|||
"""Dependency injector callable providers."""
|
||||
|
||||
import six
|
||||
|
||||
from dependency_injector.providers.base import Provider
|
||||
from dependency_injector.injections import (
|
||||
_parse_args_injections,
|
||||
_parse_kwargs_injections,
|
||||
)
|
||||
from dependency_injector.utils import represent_provider
|
||||
from dependency_injector.errors import Error
|
||||
|
||||
|
||||
@six.python_2_unicode_compatible
|
||||
class Callable(Provider):
|
||||
""":py:class:`Callable` provider calls wrapped callable on every call.
|
||||
|
||||
:py:class:`Callable` provider provides callable that is called on every
|
||||
provider call with some predefined dependency injections.
|
||||
|
||||
:py:class:`Callable` syntax of passing injections is the same like
|
||||
:py:class:`Factory` one:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# simplified syntax for passing positional and keyword argument
|
||||
# injections:
|
||||
some_function = Callable(some_function, 'arg1', 'arg2', arg3=3, arg4=4)
|
||||
|
||||
# extended (full) syntax for passing positional and keyword argument
|
||||
# injections:
|
||||
some_function = Callable(some_function,
|
||||
injections.Arg(1),
|
||||
injections.Arg(2),
|
||||
injections.KwArg('some_arg', 3),
|
||||
injections.KwArg('other_arg', 4))
|
||||
|
||||
.. py:attribute:: provides
|
||||
|
||||
Provided callable.
|
||||
|
||||
:type: callable
|
||||
|
||||
.. py:attribute:: args
|
||||
|
||||
Tuple of positional argument injections.
|
||||
|
||||
:type: tuple[:py:class:`dependency_injector.injections.Arg`]
|
||||
|
||||
.. py:attribute:: kwargs
|
||||
|
||||
Tuple of keyword argument injections.
|
||||
|
||||
:type: tuple[:py:class:`dependency_injector.injections.KwArg`]
|
||||
"""
|
||||
|
||||
__slots__ = ('provides', 'args', 'kwargs')
|
||||
|
||||
def __init__(self, provides, *args, **kwargs):
|
||||
"""Initializer.
|
||||
|
||||
:param provides: Wrapped callable.
|
||||
:type provides: callable
|
||||
|
||||
:param args: Tuple of injections.
|
||||
:type args: tuple
|
||||
|
||||
:param kwargs: Dictionary of injections.
|
||||
:type kwargs: dict
|
||||
"""
|
||||
if not callable(provides):
|
||||
raise Error('Provider {0} expected to get callable, '
|
||||
'got {0}'.format('.'.join((self.__class__.__module__,
|
||||
self.__class__.__name__)),
|
||||
provides))
|
||||
|
||||
self.provides = provides
|
||||
|
||||
self.args = _parse_args_injections(args)
|
||||
self.kwargs = _parse_kwargs_injections(args, kwargs)
|
||||
|
||||
super(Callable, self).__init__()
|
||||
|
||||
@property
|
||||
def injections(self):
|
||||
"""Read-only tuple of all injections.
|
||||
|
||||
:rtype: tuple[:py:class:`dependency_injector.injections.Injection`]
|
||||
"""
|
||||
return self.args + self.kwargs
|
||||
|
||||
def _provide(self, *args, **kwargs):
|
||||
"""Return provided instance.
|
||||
|
||||
:param args: Tuple of context positional arguments.
|
||||
:type args: tuple[object]
|
||||
|
||||
:param kwargs: Dictionary of context keyword arguments.
|
||||
:type kwargs: dict[str, object]
|
||||
|
||||
:rtype: object
|
||||
"""
|
||||
if self.args:
|
||||
args = tuple(arg.value for arg in self.args) + args
|
||||
|
||||
for kwarg in self.kwargs:
|
||||
if kwarg.name not in kwargs:
|
||||
kwargs[kwarg.name] = kwarg.value
|
||||
|
||||
return self.provides(*args, **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
"""Return string representation of provider.
|
||||
|
||||
:rtype: str
|
||||
"""
|
||||
return represent_provider(provider=self, provides=self.provides)
|
||||
|
||||
__repr__ = __str__
|
||||
|
||||
|
||||
class DelegatedCallable(Callable):
|
||||
""":py:class:`DelegatedCallable` is a delegated :py:class:`Callable`.
|
||||
|
||||
:py:class:`DelegatedCallable` is a :py:class:`Callable`, that is injected
|
||||
"as is".
|
||||
|
||||
.. py:attribute:: provides
|
||||
|
||||
Provided callable.
|
||||
|
||||
:type: callable
|
||||
|
||||
.. py:attribute:: args
|
||||
|
||||
Tuple of positional argument injections.
|
||||
|
||||
:type: tuple[:py:class:`dependency_injector.injections.Arg`]
|
||||
|
||||
.. py:attribute:: kwargs
|
||||
|
||||
Tuple of keyword argument injections.
|
||||
|
||||
:type: tuple[:py:class:`dependency_injector.injections.KwArg`]
|
||||
"""
|
||||
|
||||
__IS_DELEGATED__ = True
|
138
dependency_injector/providers/config.py
Normal file
|
@ -0,0 +1,138 @@
|
|||
"""Dependency injector config providers."""
|
||||
|
||||
import six
|
||||
|
||||
from dependency_injector.providers.base import Provider
|
||||
from dependency_injector.utils import represent_provider
|
||||
from dependency_injector.errors import Error
|
||||
|
||||
|
||||
@six.python_2_unicode_compatible
|
||||
class Config(Provider):
|
||||
""":py:class:`Config` provider provide dict values.
|
||||
|
||||
:py:class:`Config` provider creates :py:class:`ChildConfig` objects for all
|
||||
undefined attribute calls. It makes possible to create deferred config
|
||||
value providers. It might be useful in cases where it is needed to
|
||||
define / pass some configuration in declarative manner, while
|
||||
configuration values will be loaded / updated in application's runtime.
|
||||
"""
|
||||
|
||||
__slots__ = ('value',)
|
||||
|
||||
def __init__(self, value=None):
|
||||
"""Initializer.
|
||||
|
||||
:param value: Configuration dictionary.
|
||||
:type value: dict[str, object]
|
||||
"""
|
||||
if not value:
|
||||
value = dict()
|
||||
self.value = value
|
||||
super(Config, self).__init__()
|
||||
|
||||
def __getattr__(self, item):
|
||||
"""Return instance of deferred config.
|
||||
|
||||
:param item: Name of configuration option or section.
|
||||
:type item: str
|
||||
|
||||
:rtype: :py:class:`ChildConfig`
|
||||
"""
|
||||
return ChildConfig(parents=(item,), root_config=self)
|
||||
|
||||
def _provide(self, paths=None):
|
||||
"""Return provided instance.
|
||||
|
||||
:param paths: Tuple of pieces of configuration option / section path.
|
||||
:type args: tuple[str]
|
||||
|
||||
:rtype: object
|
||||
"""
|
||||
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.
|
||||
|
||||
:param value: Configuration dictionary.
|
||||
:type value: dict[str, object]
|
||||
|
||||
:rtype: None
|
||||
"""
|
||||
self.value.update(value)
|
||||
|
||||
def __str__(self):
|
||||
"""Return string representation of provider.
|
||||
|
||||
:rtype: str
|
||||
"""
|
||||
return represent_provider(provider=self, provides=self.value)
|
||||
|
||||
__repr__ = __str__
|
||||
|
||||
|
||||
@six.python_2_unicode_compatible
|
||||
class ChildConfig(Provider):
|
||||
""":py:class:`ChildConfig` provider provides value from :py:class:`Config`.
|
||||
|
||||
:py:class:`ChildConfig` provides 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.
|
||||
|
||||
:param parents: Tuple of pieces of configuration option / section
|
||||
parent path.
|
||||
:type parents: tuple[str]
|
||||
|
||||
:param root_config: Root configuration object.
|
||||
:type root_config: :py:class:`Config`
|
||||
"""
|
||||
self.parents = parents
|
||||
self.root_config = root_config
|
||||
super(ChildConfig, self).__init__()
|
||||
|
||||
def __getattr__(self, item):
|
||||
"""Return instance of deferred config.
|
||||
|
||||
:param item: Name of configuration option or section.
|
||||
:type item: str
|
||||
|
||||
:rtype: :py:class:`ChildConfig`
|
||||
"""
|
||||
return ChildConfig(parents=self.parents + (item,),
|
||||
root_config=self.root_config)
|
||||
|
||||
def _provide(self, *args, **kwargs):
|
||||
"""Return provided instance.
|
||||
|
||||
:param args: Tuple of context positional arguments.
|
||||
:type args: tuple[object]
|
||||
|
||||
:param kwargs: Dictionary of context keyword arguments.
|
||||
:type kwargs: dict[str, object]
|
||||
|
||||
:rtype: object
|
||||
"""
|
||||
return self.root_config(self.parents)
|
||||
|
||||
def __str__(self):
|
||||
"""Return string representation of provider.
|
||||
|
||||
:rtype: str
|
||||
"""
|
||||
return represent_provider(provider=self,
|
||||
provides='.'.join(self.parents))
|
||||
|
||||
__repr__ = __str__
|
387
dependency_injector/providers/creational.py
Normal file
|
@ -0,0 +1,387 @@
|
|||
"""Dependency injector creational providers."""
|
||||
|
||||
from dependency_injector.providers.callable import Callable
|
||||
from dependency_injector.utils import (
|
||||
is_attribute_injection,
|
||||
is_method_injection,
|
||||
GLOBAL_LOCK,
|
||||
)
|
||||
from dependency_injector.errors import Error
|
||||
|
||||
|
||||
class Factory(Callable):
|
||||
""":py:class:`Factory` provider creates new instance on every call.
|
||||
|
||||
:py:class:`Factory` supports different syntaxes of passing injections:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# simplified syntax for passing positional and keyword argument
|
||||
# injections only:
|
||||
factory = Factory(SomeClass, 'arg1', 'arg2', arg3=3, arg4=4)
|
||||
|
||||
# extended (full) syntax for passing any type of injections:
|
||||
factory = Factory(SomeClass,
|
||||
injections.Arg(1),
|
||||
injections.Arg(2),
|
||||
injections.KwArg('some_arg', 3),
|
||||
injections.KwArg('other_arg', 4),
|
||||
injections.Attribute('some_attribute', 5))
|
||||
|
||||
Retrieving of provided instance can be performed via calling
|
||||
:py:class:`Factory` object:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
factory = Factory(SomeClass,
|
||||
some_arg1=1,
|
||||
some_arg2=2)
|
||||
some_object = factory()
|
||||
|
||||
.. py:attribute:: provided_type
|
||||
|
||||
If provided type is defined, :py:class:`Factory` checks that
|
||||
:py:attr:`Factory.provides` is subclass of
|
||||
:py:attr:`Factory.provided_type`.
|
||||
|
||||
:type: type | None
|
||||
|
||||
.. py:attribute:: provides
|
||||
|
||||
Class or other callable that provides object.
|
||||
|
||||
:type: type | callable
|
||||
|
||||
.. py:attribute:: cls
|
||||
|
||||
Class that provides object.
|
||||
Alias for :py:attr:`provides`.
|
||||
|
||||
:type: type
|
||||
|
||||
.. py:attribute:: args
|
||||
|
||||
Tuple of positional argument injections.
|
||||
|
||||
:type: tuple[:py:class:`dependency_injector.injections.Arg`]
|
||||
|
||||
.. py:attribute:: kwargs
|
||||
|
||||
Tuple of keyword argument injections.
|
||||
|
||||
:type: tuple[:py:class:`dependency_injector.injections.KwArg`]
|
||||
|
||||
.. py:attribute:: attributes
|
||||
|
||||
Tuple of attribute injections.
|
||||
|
||||
:type: tuple[:py:class:`dependency_injector.injections.Attribute`]
|
||||
|
||||
.. py:attribute:: methods
|
||||
|
||||
Tuple of method injections.
|
||||
|
||||
:type: tuple[:py:class:`dependency_injector.injections.Method`]
|
||||
"""
|
||||
|
||||
provided_type = None
|
||||
|
||||
__slots__ = ('cls', 'attributes', 'methods')
|
||||
|
||||
def __init__(self, provides, *args, **kwargs):
|
||||
"""Initializer.
|
||||
|
||||
:param provides: Class or other callable that provides object
|
||||
for creation.
|
||||
:type provides: type | callable
|
||||
|
||||
:param args: Tuple of injections.
|
||||
:type args: tuple
|
||||
|
||||
:param kwargs: Dictionary of injections.
|
||||
:type kwargs: dict
|
||||
"""
|
||||
if (self.__class__.provided_type and
|
||||
not issubclass(provides, self.__class__.provided_type)):
|
||||
raise Error('{0} can provide only {1} instances'.format(
|
||||
self.__class__, self.__class__.provided_type))
|
||||
|
||||
self.attributes = tuple(injection
|
||||
for injection in args
|
||||
if is_attribute_injection(injection))
|
||||
|
||||
self.methods = tuple(injection
|
||||
for injection in args
|
||||
if is_method_injection(injection))
|
||||
|
||||
super(Factory, self).__init__(provides, *args, **kwargs)
|
||||
|
||||
self.cls = self.provides
|
||||
|
||||
@property
|
||||
def injections(self):
|
||||
"""Read-only tuple of all injections.
|
||||
|
||||
:rtype: tuple[:py:class:`dependency_injector.injections.Injection`]
|
||||
"""
|
||||
return self.args + self.kwargs + self.attributes + self.methods
|
||||
|
||||
def _provide(self, *args, **kwargs):
|
||||
"""Return provided instance.
|
||||
|
||||
:param args: Tuple of context positional arguments.
|
||||
:type args: tuple[object]
|
||||
|
||||
:param kwargs: Dictionary of context keyword arguments.
|
||||
:type kwargs: dict[str, object]
|
||||
|
||||
:rtype: object
|
||||
"""
|
||||
if self.args:
|
||||
args = tuple(arg.value for arg in self.args) + args
|
||||
|
||||
for kwarg in self.kwargs:
|
||||
if kwarg.name not in kwargs:
|
||||
kwargs[kwarg.name] = kwarg.value
|
||||
|
||||
instance = self.provides(*args, **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 DelegatedFactory(Factory):
|
||||
""":py:class:`DelegatedFactory` is a delegated :py:class:`Factory`.
|
||||
|
||||
:py:class:`DelegatedFactory` is a :py:class:`Factory`, that is injected
|
||||
"as is".
|
||||
|
||||
.. py:attribute:: provided_type
|
||||
|
||||
If provided type is defined, :py:class:`Factory` checks that
|
||||
:py:attr:`Factory.provides` is subclass of
|
||||
:py:attr:`Factory.provided_type`.
|
||||
|
||||
:type: type | None
|
||||
|
||||
.. py:attribute:: provides
|
||||
|
||||
Class or other callable that provides object.
|
||||
|
||||
:type: type | callable
|
||||
|
||||
.. py:attribute:: cls
|
||||
|
||||
Class that provides object.
|
||||
Alias for :py:attr:`provides`.
|
||||
|
||||
:type: type
|
||||
|
||||
.. py:attribute:: args
|
||||
|
||||
Tuple of positional argument injections.
|
||||
|
||||
:type: tuple[:py:class:`dependency_injector.injections.Arg`]
|
||||
|
||||
.. py:attribute:: kwargs
|
||||
|
||||
Tuple of keyword argument injections.
|
||||
|
||||
:type: tuple[:py:class:`dependency_injector.injections.KwArg`]
|
||||
|
||||
.. py:attribute:: attributes
|
||||
|
||||
Tuple of attribute injections.
|
||||
|
||||
:type: tuple[:py:class:`dependency_injector.injections.Attribute`]
|
||||
|
||||
.. py:attribute:: methods
|
||||
|
||||
Tuple of method injections.
|
||||
|
||||
:type: tuple[:py:class:`dependency_injector.injections.Method`]
|
||||
"""
|
||||
|
||||
__IS_DELEGATED__ = True
|
||||
|
||||
|
||||
class Singleton(Factory):
|
||||
""":py:class:`Singleton` provider returns same instance on every call.
|
||||
|
||||
:py:class:`Singleton` provider creates instance once and return it on every
|
||||
call. :py:class:`Singleton` extends :py:class:`Factory`, so, please follow
|
||||
:py:class:`Factory` documentation to go inside with injections syntax.
|
||||
|
||||
:py:class:`Singleton` is thread-safe and could be used in multithreading
|
||||
environment without any negative impact.
|
||||
|
||||
Retrieving of provided instance can be performed via calling
|
||||
:py:class:`Singleton` object:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
singleton = Singleton(SomeClass,
|
||||
some_arg1=1,
|
||||
some_arg2=2)
|
||||
some_object = singleton()
|
||||
|
||||
.. py:attribute:: provided_type
|
||||
|
||||
If provided type is defined, :py:class:`Factory` checks that
|
||||
:py:attr:`Factory.provides` is subclass of
|
||||
:py:attr:`Factory.provided_type`.
|
||||
|
||||
:type: type | None
|
||||
|
||||
.. py:attribute:: instance
|
||||
|
||||
Read-only reference to singleton's instance.
|
||||
|
||||
:type: object
|
||||
|
||||
.. py:attribute:: provides
|
||||
|
||||
Class or other callable that provides object.
|
||||
|
||||
:type: type | callable
|
||||
|
||||
.. py:attribute:: cls
|
||||
|
||||
Class that provides object.
|
||||
Alias for :py:attr:`provides`.
|
||||
|
||||
:type: type
|
||||
|
||||
.. py:attribute:: args
|
||||
|
||||
Tuple of positional argument injections.
|
||||
|
||||
:type: tuple[:py:class:`dependency_injector.injections.Arg`]
|
||||
|
||||
.. py:attribute:: kwargs
|
||||
|
||||
Tuple of keyword argument injections.
|
||||
|
||||
:type: tuple[:py:class:`dependency_injector.injections.KwArg`]
|
||||
|
||||
.. py:attribute:: attributes
|
||||
|
||||
Tuple of attribute injections.
|
||||
|
||||
:type: tuple[:py:class:`dependency_injector.injections.Attribute`]
|
||||
|
||||
.. py:attribute:: methods
|
||||
|
||||
Tuple of method injections.
|
||||
|
||||
:type: tuple[:py:class:`dependency_injector.injections.Method`]
|
||||
"""
|
||||
|
||||
__slots__ = ('instance',)
|
||||
|
||||
def __init__(self, provides, *args, **kwargs):
|
||||
"""Initializer.
|
||||
|
||||
:param provides: Class or other callable that provides object
|
||||
for creation.
|
||||
:type provides: type | callable
|
||||
|
||||
:param args: Tuple of injections.
|
||||
:type args: tuple
|
||||
|
||||
:param kwargs: Dictionary of injections.
|
||||
:type kwargs: dict
|
||||
"""
|
||||
self.instance = None
|
||||
super(Singleton, self).__init__(provides, *args, **kwargs)
|
||||
|
||||
def reset(self):
|
||||
"""Reset cached instance, if any.
|
||||
|
||||
:rtype: None
|
||||
"""
|
||||
self.instance = None
|
||||
|
||||
def _provide(self, *args, **kwargs):
|
||||
"""Return provided instance.
|
||||
|
||||
:param args: Tuple of context positional arguments.
|
||||
:type args: tuple[object]
|
||||
|
||||
:param kwargs: Dictionary of context keyword arguments.
|
||||
:type kwargs: dict[str, object]
|
||||
|
||||
:rtype: object
|
||||
"""
|
||||
if self.instance:
|
||||
return self.instance
|
||||
|
||||
with GLOBAL_LOCK:
|
||||
self.instance = super(Singleton, self)._provide(*args, **kwargs)
|
||||
|
||||
return self.instance
|
||||
|
||||
|
||||
class DelegatedSingleton(Singleton):
|
||||
""":py:class:`DelegatedSingleton` is a delegated :py:class:`Singleton`.
|
||||
|
||||
:py:class:`DelegatedSingleton` is a :py:class:`Singleton`, that is injected
|
||||
"as is".
|
||||
|
||||
.. py:attribute:: provided_type
|
||||
|
||||
If provided type is defined, :py:class:`Factory` checks that
|
||||
:py:attr:`Factory.provides` is subclass of
|
||||
:py:attr:`Factory.provided_type`.
|
||||
|
||||
:type: type | None
|
||||
|
||||
.. py:attribute:: instance
|
||||
|
||||
Read-only reference to singleton's instance.
|
||||
|
||||
:type: object
|
||||
|
||||
.. py:attribute:: provides
|
||||
|
||||
Class or other callable that provides object.
|
||||
|
||||
:type: type | callable
|
||||
|
||||
.. py:attribute:: cls
|
||||
|
||||
Class that provides object.
|
||||
Alias for :py:attr:`provides`.
|
||||
|
||||
:type: type
|
||||
|
||||
.. py:attribute:: args
|
||||
|
||||
Tuple of positional argument injections.
|
||||
|
||||
:type: tuple[:py:class:`dependency_injector.injections.Arg`]
|
||||
|
||||
.. py:attribute:: kwargs
|
||||
|
||||
Tuple of keyword argument injections.
|
||||
|
||||
:type: tuple[:py:class:`dependency_injector.injections.KwArg`]
|
||||
|
||||
.. py:attribute:: attributes
|
||||
|
||||
Tuple of attribute injections.
|
||||
|
||||
:type: tuple[:py:class:`dependency_injector.injections.Attribute`]
|
||||
|
||||
.. py:attribute:: methods
|
||||
|
||||
Tuple of method injections.
|
||||
|
||||
:type: tuple[:py:class:`dependency_injector.injections.Method`]
|
||||
"""
|
||||
|
||||
__IS_DELEGATED__ = True
|
43
dependency_injector/providers/static.py
Normal file
|
@ -0,0 +1,43 @@
|
|||
"""Dependency injector static providers."""
|
||||
|
||||
from dependency_injector.providers.base import Static
|
||||
|
||||
|
||||
class Class(Static):
|
||||
""":py:class:`Class` returns provided class "as is".
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
cls_provider = Class(object)
|
||||
object_cls = cls_provider()
|
||||
"""
|
||||
|
||||
|
||||
class Object(Static):
|
||||
""":py:class:`Object` returns provided object "as is".
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
object_provider = Object(object())
|
||||
object_instance = object_provider()
|
||||
"""
|
||||
|
||||
|
||||
class Function(Static):
|
||||
""":py:class:`Function` returns provided function "as is".
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
function_provider = Function(len)
|
||||
len_function = function_provider()
|
||||
"""
|
||||
|
||||
|
||||
class Value(Static):
|
||||
""":py:class:`Value` returns provided value "as is".
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
value_provider = Value(31337)
|
||||
value = value_provider()
|
||||
"""
|
65
dependency_injector/providers/utils.py
Normal file
|
@ -0,0 +1,65 @@
|
|||
"""Dependency injector provider utils."""
|
||||
|
||||
|
||||
class OverridingContext(object):
|
||||
"""Provider overriding context.
|
||||
|
||||
:py:class:`OverridingContext` is used by :py:meth:`Provider.override` for
|
||||
implemeting ``with`` contexts. When :py:class:`OverridingContext` is
|
||||
closed, overriding that was created in this context is dropped also.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
with provider.override(another_provider):
|
||||
assert provider.is_overridden
|
||||
assert not provider.is_overridden
|
||||
"""
|
||||
|
||||
def __init__(self, overridden, overriding):
|
||||
"""Initializer.
|
||||
|
||||
:param overridden: Overridden provider.
|
||||
:type overridden: :py:class:`Provider`
|
||||
|
||||
:param overriding: Overriding provider.
|
||||
:type overriding: :py:class:`Provider`
|
||||
"""
|
||||
self.overridden = overridden
|
||||
self.overriding = overriding
|
||||
|
||||
def __enter__(self):
|
||||
"""Do nothing."""
|
||||
return self.overriding
|
||||
|
||||
def __exit__(self, *_):
|
||||
"""Exit overriding context."""
|
||||
self.overridden.reset_last_overriding()
|
||||
|
||||
|
||||
def override(overridden):
|
||||
"""Decorator for overriding providers.
|
||||
|
||||
This decorator overrides ``overridden`` provider by decorated one.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@Factory
|
||||
class SomeClass(object):
|
||||
pass
|
||||
|
||||
|
||||
@override(SomeClass)
|
||||
@Factory
|
||||
class ExtendedSomeClass(SomeClass.cls):
|
||||
pass
|
||||
|
||||
:param overridden: Provider that should be overridden.
|
||||
:type overridden: :py:class:`Provider`
|
||||
|
||||
:return: Overriding provider.
|
||||
:rtype: :py:class:`Provider`
|
||||
"""
|
||||
def decorator(overriding):
|
||||
overridden.override(overriding)
|
||||
return overriding
|
||||
return decorator
|
260
dependency_injector/utils.py
Normal file
|
@ -0,0 +1,260 @@
|
|||
"""Utils module."""
|
||||
|
||||
import sys
|
||||
import copy
|
||||
import types
|
||||
import threading
|
||||
|
||||
import six
|
||||
|
||||
from dependency_injector.errors import Error
|
||||
|
||||
|
||||
GLOBAL_LOCK = threading.RLock()
|
||||
"""Dependency injector global reentrant lock.
|
||||
|
||||
:type: :py:class:`threading.RLock`
|
||||
"""
|
||||
|
||||
_IS_PYPY = '__pypy__' in sys.builtin_module_names
|
||||
if _IS_PYPY or six.PY3: # pragma: no cover
|
||||
_OBJECT_INIT = six.get_unbound_function(object.__init__)
|
||||
else: # pragma: no cover
|
||||
_OBJECT_INIT = None
|
||||
|
||||
if six.PY2: # pragma: no cover
|
||||
copy._deepcopy_dispatch[types.MethodType] = \
|
||||
lambda obj, memo: type(obj)(obj.im_func,
|
||||
copy.deepcopy(obj.im_self, memo),
|
||||
obj.im_class)
|
||||
|
||||
|
||||
def is_provider(instance):
|
||||
"""Check if instance is provider instance.
|
||||
|
||||
:param instance: Instance to be checked.
|
||||
:type instance: object
|
||||
|
||||
:rtype: bool
|
||||
"""
|
||||
return (not isinstance(instance, six.class_types) and
|
||||
hasattr(instance, '__IS_PROVIDER__') and
|
||||
getattr(instance, '__IS_PROVIDER__') is True)
|
||||
|
||||
|
||||
def ensure_is_provider(instance):
|
||||
"""Check if instance is provider instance and return it.
|
||||
|
||||
:param instance: Instance to be checked.
|
||||
:type instance: object
|
||||
|
||||
:raise: :py:exc:`dependency_injector.errors.Error` if provided instance is
|
||||
not provider.
|
||||
|
||||
:rtype: :py:class:`dependency_injector.providers.Provider`
|
||||
"""
|
||||
if not is_provider(instance):
|
||||
raise Error('Expected provider instance, '
|
||||
'got {0}'.format(str(instance)))
|
||||
return instance
|
||||
|
||||
|
||||
def is_delegated_provider(instance):
|
||||
"""Check if instance is delegated provider instance.
|
||||
|
||||
:param instance: Instance to be checked.
|
||||
:type instance: object
|
||||
|
||||
:rtype: bool
|
||||
"""
|
||||
return (is_provider(instance) and
|
||||
hasattr(instance, '__IS_DELEGATED__') and
|
||||
getattr(instance, '__IS_DELEGATED__') is True)
|
||||
|
||||
|
||||
def is_injection(instance):
|
||||
"""Check if instance is injection instance.
|
||||
|
||||
:param instance: Instance to be checked.
|
||||
:type instance: object
|
||||
|
||||
:rtype: bool
|
||||
"""
|
||||
return (not isinstance(instance, six.class_types) and
|
||||
hasattr(instance, '__IS_INJECTION__') and
|
||||
getattr(instance, '__IS_INJECTION__') is True)
|
||||
|
||||
|
||||
def ensure_is_injection(instance):
|
||||
"""Check if instance is injection instance and return it.
|
||||
|
||||
:param instance: Instance to be checked.
|
||||
:type instance: object
|
||||
|
||||
:raise: :py:exc:`dependency_injector.errors.Error` if provided instance is
|
||||
not injection.
|
||||
|
||||
:rtype: :py:class:`dependency_injector.injections.Injection`
|
||||
"""
|
||||
if not is_injection(instance):
|
||||
raise Error('Expected injection instance, '
|
||||
'got {0}'.format(str(instance)))
|
||||
return instance
|
||||
|
||||
|
||||
def is_arg_injection(instance):
|
||||
"""Check if instance is positional argument injection instance.
|
||||
|
||||
:param instance: Instance to be checked.
|
||||
:type instance: object
|
||||
|
||||
:rtype: bool
|
||||
"""
|
||||
return (not isinstance(instance, six.class_types) and
|
||||
hasattr(instance, '__IS_ARG_INJECTION__') and
|
||||
getattr(instance, '__IS_ARG_INJECTION__', False) is True)
|
||||
|
||||
|
||||
def is_kwarg_injection(instance):
|
||||
"""Check if instance is keyword argument injection instance.
|
||||
|
||||
:param instance: Instance to be checked.
|
||||
:type instance: object
|
||||
|
||||
:rtype: bool
|
||||
"""
|
||||
return (not isinstance(instance, six.class_types) and
|
||||
hasattr(instance, '__IS_KWARG_INJECTION__') and
|
||||
getattr(instance, '__IS_KWARG_INJECTION__', False) is True)
|
||||
|
||||
|
||||
def is_attribute_injection(instance):
|
||||
"""Check if instance is attribute injection instance.
|
||||
|
||||
:param instance: Instance to be checked.
|
||||
:type instance: object
|
||||
|
||||
:rtype: bool
|
||||
"""
|
||||
return (not isinstance(instance, six.class_types) and
|
||||
hasattr(instance, '__IS_ATTRIBUTE_INJECTION__') and
|
||||
getattr(instance, '__IS_ATTRIBUTE_INJECTION__', False) is True)
|
||||
|
||||
|
||||
def is_method_injection(instance):
|
||||
"""Check if instance is method injection instance.
|
||||
|
||||
:param instance: Instance to be checked.
|
||||
:type instance: object
|
||||
|
||||
:rtype: bool
|
||||
"""
|
||||
return (not isinstance(instance, six.class_types) and
|
||||
hasattr(instance, '__IS_METHOD_INJECTION__') and
|
||||
getattr(instance, '__IS_METHOD_INJECTION__', False) is True)
|
||||
|
||||
|
||||
def is_catalog(instance):
|
||||
"""Check if instance is catalog instance.
|
||||
|
||||
:param instance: Instance to be checked.
|
||||
:type instance: object
|
||||
|
||||
:rtype: bool
|
||||
"""
|
||||
return (hasattr(instance, '__IS_CATALOG__') and
|
||||
getattr(instance, '__IS_CATALOG__', False) is True)
|
||||
|
||||
|
||||
def is_dynamic_catalog(instance):
|
||||
"""Check if instance is dynamic catalog instance.
|
||||
|
||||
:param instance: Instance to be checked.
|
||||
:type instance: object
|
||||
|
||||
:rtype: bool
|
||||
"""
|
||||
return (not isinstance(instance, six.class_types) and is_catalog(instance))
|
||||
|
||||
|
||||
def is_declarative_catalog(instance):
|
||||
"""Check if instance is declarative catalog instance.
|
||||
|
||||
:param instance: Instance to be checked.
|
||||
:type instance: object
|
||||
|
||||
:rtype: bool
|
||||
"""
|
||||
return (isinstance(instance, six.class_types) and is_catalog(instance))
|
||||
|
||||
|
||||
def is_catalog_bundle(instance):
|
||||
"""Check if instance is catalog bundle instance.
|
||||
|
||||
:param instance: Instance to be checked.
|
||||
:type instance: object
|
||||
|
||||
:rtype: bool
|
||||
"""
|
||||
return (not isinstance(instance, six.class_types) and
|
||||
hasattr(instance, '__IS_CATALOG_BUNDLE__') and
|
||||
getattr(instance, '__IS_CATALOG_BUNDLE__', False) is True)
|
||||
|
||||
|
||||
def ensure_is_catalog_bundle(instance):
|
||||
"""Check if instance is catalog bundle instance and return it.
|
||||
|
||||
:param instance: Instance to be checked.
|
||||
:type instance: object
|
||||
|
||||
:raise: :py:exc:`dependency_injector.errors.Error` if provided instance
|
||||
is not catalog bundle.
|
||||
|
||||
:rtype: :py:class:`dependency_injector.catalogs.CatalogBundle`
|
||||
"""
|
||||
if not is_catalog_bundle(instance):
|
||||
raise Error('Expected catalog bundle instance, '
|
||||
'got {0}'.format(str(instance)))
|
||||
return instance
|
||||
|
||||
|
||||
def represent_provider(provider, provides):
|
||||
"""Return string representation of provider.
|
||||
|
||||
:param provider: Provider object
|
||||
:type provider: :py:class:`dependency_injector.providers.Provider`
|
||||
|
||||
:param provides: Object that provider provides
|
||||
:type provider: object
|
||||
|
||||
:return: String representation of provider
|
||||
:rtype: str
|
||||
"""
|
||||
return '<{provider}({provides}) at {address}>'.format(
|
||||
provider='.'.join((provider.__class__.__module__,
|
||||
provider.__class__.__name__)),
|
||||
provides=repr(provides) if provides is not None else '',
|
||||
address=hex(id(provider)))
|
||||
|
||||
|
||||
def fetch_cls_init(cls):
|
||||
"""Return reference to the class.__init__() method if it is defined.
|
||||
|
||||
:param cls: Class instance
|
||||
:type cls: type
|
||||
|
||||
:return: Reference to the class.__init__() if any, or None otherwise.
|
||||
:rtype: unbound method | None
|
||||
"""
|
||||
try:
|
||||
cls_init = six.get_unbound_function(cls.__init__)
|
||||
assert cls_init is not _OBJECT_INIT
|
||||
except (AttributeError, AssertionError):
|
||||
return None
|
||||
else:
|
||||
return cls_init
|
||||
|
||||
|
||||
def _copy_providers(providers, memo=None):
|
||||
"""Make full copy of providers dictionary."""
|
||||
return copy.deepcopy(providers, memo)
|
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>
|
66
docs/advanced_usage/index.rst
Normal file
|
@ -0,0 +1,66 @@
|
|||
Advanced usage
|
||||
==============
|
||||
|
||||
Current section of documentation describes advanced usage of
|
||||
*Dependency Injector*.
|
||||
|
||||
@inject decorator
|
||||
-----------------
|
||||
|
||||
.. currentmodule:: dependency_injector.injections
|
||||
|
||||
:py:func:`inject` decorator is a part of
|
||||
:py:mod:`dependency_injector.injections` module.
|
||||
|
||||
:py:func:`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.
|
||||
|
||||
:py:func:`inject` takes a various number of positional and keyword arguments
|
||||
that are used as decorated callable injections. Every time, when
|
||||
:py:func:`inject` is called, positional and keyword argument injections would
|
||||
be passed as an callable arguments.
|
||||
|
||||
Such behaviour is very similar to the standard Python ``functools.partial``
|
||||
object, except of one thing: all injectable values are provided
|
||||
*"as is"*, except of providers (subclasses of
|
||||
:py:class:`dependency_injector.providers.Provider`). Providers
|
||||
will be called every time, when injection needs to be done. For example,
|
||||
if injectable value of injection is a
|
||||
:py:class:`dependency_injector.providers.Factory`, it will provide new one
|
||||
instance (as a result of its call) every time, when injection needs to be done.
|
||||
|
||||
:py:func:`inject` behaviour with context positional and keyword arguments is
|
||||
very like a standard Python ``functools.partial``:
|
||||
|
||||
- Positional context arguments will be appended after :py:func:`inject`
|
||||
positional injections.
|
||||
- Keyword context arguments have priority on :py:func:`inject` keyword
|
||||
injections and will be merged over them.
|
||||
|
||||
Example:
|
||||
|
||||
.. literalinclude:: ../../examples/advanced_usage/inject_simple.py
|
||||
:language: python
|
||||
:linenos:
|
||||
|
||||
Example of usage :py:func:`inject` decorator with Flask:
|
||||
|
||||
.. literalinclude:: ../../examples/advanced_usage/inject_flask.py
|
||||
:language: python
|
||||
:linenos:
|
||||
|
||||
|
||||
@inject decorator with classes
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
:py:func:`inject` could be applied for classes. In such case, it will look for
|
||||
class ``__init__()`` method and pass injection to it. If decorated class has
|
||||
no ``__init__()`` method, appropriate
|
||||
:py:exc:`dependency_injector.errors.Error` will be raised.
|
||||
|
||||
Example of usage :py:func:`inject` with Flask class-based view:
|
||||
|
||||
.. literalinclude:: ../../examples/advanced_usage/inject_flask_class_based.py
|
||||
:language: python
|
||||
:linenos:
|
6
docs/api/catalogs.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
``dependency_injector.catalogs``
|
||||
--------------------------------
|
||||
|
||||
.. automodule:: dependency_injector.catalogs
|
||||
:members:
|
||||
:special-members:
|
|
@ -1,9 +0,0 @@
|
|||
dependency_injector.containers
|
||||
==============================
|
||||
|
||||
.. automodule:: dependency_injector.containers
|
||||
:members:
|
||||
:inherited-members:
|
||||
:show-inheritance:
|
||||
|
||||
.. disqus::
|
|
@ -1,8 +1,5 @@
|
|||
dependency_injector.errors
|
||||
==========================
|
||||
``dependency_injector.errors``
|
||||
------------------------------
|
||||
|
||||
.. automodule:: dependency_injector.errors
|
||||
:members:
|
||||
|
||||
|
||||
.. disqus::
|
||||
|
|
|
@ -4,8 +4,9 @@ API Documentation
|
|||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
top-level
|
||||
providers
|
||||
containers
|
||||
wiring
|
||||
errors
|
||||
top_level
|
||||
providers
|
||||
injections
|
||||
catalogs
|
||||
utils
|
||||
errors
|
||||
|
|
6
docs/api/injections.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
``dependency_injector.injections``
|
||||
----------------------------------
|
||||
|
||||
.. automodule:: dependency_injector.injections
|
||||
:members:
|
||||
:inherited-members:
|
|
@ -1,10 +1,7 @@
|
|||
dependency_injector.providers
|
||||
=============================
|
||||
``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::
|
5
docs/api/top_level.rst
Normal file
|
@ -0,0 +1,5 @@
|
|||
``dependency_injector``
|
||||
-----------------------
|
||||
|
||||
.. automodule:: dependency_injector
|
||||
:members: VERSION
|
5
docs/api/utils.rst
Normal file
|
@ -0,0 +1,5 @@
|
|||
``dependency_injector.utils``
|
||||
-----------------------------
|
||||
|
||||
.. automodule:: dependency_injector.utils
|
||||
:members:
|
|
@ -1,7 +0,0 @@
|
|||
dependency_injector.wiring
|
||||
=============================
|
||||
|
||||
.. automodule:: dependency_injector.wiring
|
||||
:members:
|
||||
|
||||
.. disqus::
|
63
docs/catalogs/bundles.rst
Normal file
|
@ -0,0 +1,63 @@
|
|||
Catalog provider bundles
|
||||
------------------------
|
||||
|
||||
.. currentmodule:: dependency_injector.catalogs
|
||||
|
||||
:py:class:`CatalogBundle` is a frozen, limited collection of catalog
|
||||
providers. While catalog could be used as a centralized place for
|
||||
particular providers group, such bundles of catalog providers can be used
|
||||
for creating several frozen, limited scopes that could be passed to different
|
||||
subsystems.
|
||||
|
||||
:py:class:`CatalogBundle` has API's parity with catalogs
|
||||
(:py:class:`DeclarativeCatalog` or :py:class:`DynamicCatalog`) in terms of
|
||||
retrieving the providers, but it is "frozen" in terms of modification
|
||||
provider's list.
|
||||
|
||||
:py:class:`CatalogBundle` is considered to be dependable on catalogs
|
||||
(:py:class:`DeclarativeCatalog` or :py:class:`DynamicCatalog`) entity by
|
||||
its design.
|
||||
|
||||
Each catalog (:py:class:`DeclarativeCatalog` or :py:class:`DynamicCatalog`)
|
||||
has a reference to its bundle class - :py:attr:`DeclarativeCatalog.Bundle`
|
||||
(or :py:attr:`DynamicCatalog.Bundle` consequently). For example, subclass of
|
||||
:py:class:`CatalogBundle` for some concrete declarative catalog
|
||||
``SomeCatalog`` could be reached as ``SomeCatalog.Bundle``.
|
||||
|
||||
:py:class:`CatalogBundle` expects to get the list of its catalog providers
|
||||
as positional arguments and will limit the scope of created bundle to this
|
||||
list.
|
||||
|
||||
.. note::
|
||||
|
||||
Some notes about :py:class:`CatalogBundle` design.
|
||||
|
||||
Design and syntax of :py:class:`CatalogBundle` was developed with the idea
|
||||
of keeping full functionalities of type-hinting and introspection of
|
||||
modern IDE's. This design came from some practical experience of using
|
||||
:py:class:`CatalogBundle` and considered to be the most comfortable for
|
||||
developer.
|
||||
|
||||
Example:
|
||||
|
||||
.. image:: /images/catalogs/bundles.png
|
||||
:width: 100%
|
||||
:align: center
|
||||
|
||||
Listing of `services.py`:
|
||||
|
||||
.. literalinclude:: ../../examples/catalogs/bundles/services.py
|
||||
:language: python
|
||||
:linenos:
|
||||
|
||||
Listing of `views.py`:
|
||||
|
||||
.. literalinclude:: ../../examples/catalogs/bundles/views.py
|
||||
:language: python
|
||||
:linenos:
|
||||
|
||||
Listing of `catalogs.py`:
|
||||
|
||||
.. literalinclude:: ../../examples/catalogs/bundles/catalogs.py
|
||||
:language: python
|
||||
:linenos:
|
64
docs/catalogs/declarative.rst
Normal file
|
@ -0,0 +1,64 @@
|
|||
Declarative catalogs
|
||||
--------------------
|
||||
|
||||
.. currentmodule:: dependency_injector.catalogs
|
||||
|
||||
:py:class:`DeclarativeCatalog` is a catalog of providers that could be
|
||||
defined in declarative manner. It should cover most of the cases when list
|
||||
of providers that would be included in catalog is deterministic (catalog
|
||||
will not change its structure in runtime).
|
||||
|
||||
Declarative catalogs have to extend base declarative catalog class -
|
||||
:py:class:`dependency_injector.catalogs.DeclarativeCatalog`.
|
||||
|
||||
Declarative catalog's providers have to be defined like catalog's class
|
||||
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::
|
||||
|
||||
Declarative catalogs have several features that could be useful
|
||||
for some kind of operations on catalog's providers, please visit API
|
||||
documentation for getting full list of features -
|
||||
:py:class:`dependency_injector.catalogs.DeclarativeCatalog`.
|
||||
|
||||
.. note::
|
||||
|
||||
It might be useful to add such
|
||||
``""":type: dependency_injector.providers.Provider -> Object1"""``
|
||||
docstrings just on the next line after provider's definition. It will
|
||||
help code analyzing tools and IDE's to understand that variable above
|
||||
contains some callable object, that returns particular instance as a
|
||||
result of its call.
|
||||
|
||||
Here is an simple example of defining declarative catalog with several
|
||||
factories:
|
||||
|
||||
.. image:: /images/catalogs/declarative.png
|
||||
:width: 85%
|
||||
:align: center
|
||||
|
||||
.. literalinclude:: ../../examples/catalogs/declarative.py
|
||||
:language: python
|
||||
:linenos:
|
||||
|
||||
Example of declarative catalogs inheritance:
|
||||
|
||||
.. image:: /images/catalogs/declarative_inheritance.png
|
||||
:width: 100%
|
||||
:align: center
|
||||
|
||||
.. literalinclude:: ../../examples/catalogs/declarative_inheritance.py
|
||||
:language: python
|
||||
:linenos:
|
||||
|
||||
Example of declarative catalog's provider injections:
|
||||
|
||||
.. image:: /images/catalogs/declarative_injections.png
|
||||
:width: 100%
|
||||
:align: center
|
||||
|
||||
.. literalinclude:: ../../examples/catalogs/declarative_injections.py
|
||||
:language: python
|
||||
:linenos:
|
31
docs/catalogs/dynamic.rst
Normal file
|
@ -0,0 +1,31 @@
|
|||
Dynamic catalogs
|
||||
----------------
|
||||
|
||||
.. currentmodule:: dependency_injector.catalogs
|
||||
|
||||
:py:class:`DynamicCatalog` is a catalog of providers that could be created in
|
||||
application's runtime. It should cover most of the cases when list of
|
||||
providers that would be included in catalog is non-deterministic in terms of
|
||||
apllication code (catalog's structure could be determined just after
|
||||
application will be started and will do some initial work, like parsing list
|
||||
of catalog's providers from the configuration).
|
||||
|
||||
:py:class:`DeclarativeCatalog` and :py:class:`DynamicCatalog` have
|
||||
100% API parity.
|
||||
|
||||
Main difference between :py:class:`DeclarativeCatalog` and
|
||||
:py:class:`DynamicCatalog` is that :py:class:`DeclarativeCatalog` acts on
|
||||
class-level, while :py:class:`DynamicCatalog` do the same on
|
||||
instance-level.
|
||||
|
||||
Here is an simple example of defining dynamic catalog with several factories:
|
||||
|
||||
.. literalinclude:: ../../examples/catalogs/dynamic.py
|
||||
:language: python
|
||||
:linenos:
|
||||
|
||||
Next one example demonstrates creation and runtime filling of dynamic catalog:
|
||||
|
||||
.. literalinclude:: ../../examples/catalogs/dynamic_runtime_creation.py
|
||||
:language: python
|
||||
:linenos:
|
28
docs/catalogs/index.rst
Normal file
|
@ -0,0 +1,28 @@
|
|||
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.
|
||||
|
||||
Catalogs module API docs - :py:mod:`dependency_injector.catalogs`.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
declarative
|
||||
dynamic
|
||||
bundles
|
||||
specialization
|
||||
overriding
|
52
docs/catalogs/overriding.rst
Normal file
|
@ -0,0 +1,52 @@
|
|||
Overriding of catalogs
|
||||
----------------------
|
||||
|
||||
.. currentmodule:: dependency_injector.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 :py:class:`DeclarativeCatalog` with another
|
||||
catalog:
|
||||
|
||||
- Use :py:meth:`DeclarativeCatalog.override` method.
|
||||
- Use :py:func:`override` class decorator.
|
||||
|
||||
Example of overriding catalog using :py:meth:`DeclarativeCatalog.override`
|
||||
method:
|
||||
|
||||
.. literalinclude:: ../../examples/catalogs/override_declarative.py
|
||||
:language: python
|
||||
:linenos:
|
||||
|
||||
Example of overriding catalog using :py:func:`override` decorator:
|
||||
|
||||
.. literalinclude:: ../../examples/catalogs/override_declarative_decorator.py
|
||||
:language: python
|
||||
:linenos:
|
||||
|
||||
Also there are several useful :py:class:`DeclarativeCatalog` methods and
|
||||
properties that help to work with catalog overridings:
|
||||
|
||||
- :py:attr:`DeclarativeCatalog.is_overridden` - read-only property that is set
|
||||
to ``True`` if catalog is overridden.
|
||||
- :py:attr:`DeclarativeCatalog.last_overriding` - read-only reference to
|
||||
the last overriding catalog, if any.
|
||||
- :py:attr:`DeclarativeCatalog.overridden_by` - tuple of all overriding
|
||||
catalogs.
|
||||
- :py:meth:`DeclarativeCatalog.reset_last_overriding()` - reset last
|
||||
overriding catalog.
|
||||
- :py:meth:`DeclarativeCatalog.reset_override()` - reset all overridings for
|
||||
all catalog providers.
|
||||
|
||||
:py:class:`DynamicCatalog` has exactly the same functionality, except of
|
||||
:py:func:`override` decorator. Also :py:class:`DynamicCatalog` can override
|
||||
:py:class:`DeclarativeCatalog` and vise versa.
|
||||
|
||||
Example of overriding :py:class:`DeclarativeCatalog` by
|
||||
:py:class:`DynamicCatalog`:
|
||||
|
||||
.. literalinclude:: ../../examples/catalogs/override_declarative_by_dynamic.py
|
||||
:language: python
|
||||
:linenos:
|
45
docs/catalogs/specialization.rst
Normal file
|
@ -0,0 +1,45 @@
|
|||
Specialization of catalogs
|
||||
--------------------------
|
||||
|
||||
.. currentmodule:: dependency_injector.catalogs
|
||||
|
||||
:py:class:`DeclarativeCatalog` and :py:class:`DynamicCatalog` could be
|
||||
specialized for any kind of needs via declaring its subclasses.
|
||||
|
||||
One of such `builtin` features is a limitation to
|
||||
:py:class:`DeclarativeCatalog` (and :py:class:`DynamicCatalog`) provider type.
|
||||
|
||||
Next example shows usage of this feature with :py:class:`DeclarativeCatalog`
|
||||
in couple with feature of :py:class:`dependency_injector.providers.Factory`
|
||||
for limitation of its provided type:
|
||||
|
||||
|
||||
Listing of `services.py`:
|
||||
|
||||
.. literalinclude:: ../../examples/catalogs/declarative_provider_type/services.py
|
||||
:language: python
|
||||
:linenos:
|
||||
|
||||
Listing of `catalog.py`:
|
||||
|
||||
.. literalinclude:: ../../examples/catalogs/declarative_provider_type/catalog.py
|
||||
:language: python
|
||||
:linenos:
|
||||
|
||||
Limitation to provider type could be used with :py:class:`DynamicCatalog`
|
||||
as well.
|
||||
|
||||
Next example does the same that previous one, but use
|
||||
:py:class:`DynamicCatalog` instead of :py:class:`DeclarativeCatalog`:
|
||||
|
||||
Listing of `services.py`:
|
||||
|
||||
.. literalinclude:: ../../examples/catalogs/dynamic_provider_type/services.py
|
||||
:language: python
|
||||
:linenos:
|
||||
|
||||
Listing of `catalog.py`:
|
||||
|
||||
.. literalinclude:: ../../examples/catalogs/dynamic_provider_type/catalog.py
|
||||
:language: python
|
||||
:linenos:
|
137
docs/conf.py
|
@ -15,54 +15,49 @@
|
|||
import os
|
||||
import re
|
||||
import sys
|
||||
import alabaster
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
sys.path.insert(0, os.path.abspath(".."))
|
||||
sys.path.insert(0, os.path.abspath('..'))
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#needs_sphinx = "1.0"
|
||||
#needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named "sphinx.ext.*") or your custom
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
"alabaster",
|
||||
"sphinx.ext.autodoc",
|
||||
"sphinx_disqus.disqus",
|
||||
]
|
||||
extensions = ['sphinx.ext.autodoc']
|
||||
|
||||
# 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'2016, ETS Labs'
|
||||
author = u'ETS Labs'
|
||||
|
||||
# The version info for the project you"re documenting, acts as replacement for
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
# Getting version:
|
||||
with open("../src/dependency_injector/__init__.py") as init_file:
|
||||
version = re.search("__version__ = \"(.*?)\"", init_file.read()).group(1)
|
||||
with open('../dependency_injector/__init__.py') as init_file:
|
||||
version = re.search('VERSION = \'(.*?)\'', init_file.read()).group(1)
|
||||
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = version
|
||||
|
@ -76,19 +71,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 +95,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 +111,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 +135,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 = []
|
||||
|
||||
# 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 +183,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'ETS Labs', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
|
@ -264,7 +266,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 +280,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', 'Python dependency injection framework',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
|
@ -289,25 +291,10 @@ 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",
|
||||
}
|
||||
autodoc_member_order = 'bysource'
|
||||
|
|
|
@ -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 |
|
@ -2,25 +2,17 @@ Examples
|
|||
========
|
||||
|
||||
.. meta::
|
||||
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Example
|
||||
:description: Python dependency injection examples.
|
||||
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control
|
||||
:description: Current section of documentation is designed to provide
|
||||
several example mini applications that are built on the top
|
||||
of inversion of control principle and powered by
|
||||
"Dependency Injector" framework.
|
||||
|
||||
Explore the examples to see the ``Dependency Injector`` in action.
|
||||
Current section of documentation is designed to provide several example mini
|
||||
applications that are built on the top of inversion of control principle and
|
||||
powered by *Dependency Injector* framework.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
application-single-container
|
||||
application-multiple-containers
|
||||
decoupled-packages
|
||||
boto3
|
||||
django
|
||||
flask
|
||||
flask-blueprints
|
||||
aiohttp
|
||||
sanic
|
||||
fastapi
|
||||
fastapi-redis
|
||||
fastapi-sqlalchemy
|
||||
|
||||
.. disqus::
|
||||
movie_lister
|
||||
|
|
104
docs/examples/movie_lister.rst
Normal file
|
@ -0,0 +1,104 @@
|
|||
Movie lister naive example
|
||||
--------------------------
|
||||
|
||||
.. meta::
|
||||
:description: Movie lister - is a naive example of dependency injection and
|
||||
inversion of control containers on Python. Original example
|
||||
was taken from Martin Fowler's article about dependency
|
||||
injection and inversion of control.
|
||||
|
||||
This naive example was taken from Martin Fowler's article about dependency
|
||||
injection and inversion of control: http://www.martinfowler.com/articles/injection.html
|
||||
|
||||
Like Martin says:
|
||||
|
||||
.. pull-quote::
|
||||
|
||||
*Like all of my examples it's one of those super-simple examples;
|
||||
small enough to be unreal, but hopefully enough for you to visualize
|
||||
what's going on without falling into the bog of a real example.*
|
||||
|
||||
While original Martin's MovieLister example was a bit modified here, it
|
||||
makes sense to provide some description. So, the idea of this example is to
|
||||
create ``movies`` library that can be configurable to work with different
|
||||
movie databases (csv, sqlite) and provide 2 main features:
|
||||
|
||||
1. List all movies that were directed by certain person.
|
||||
2. List all movies that were released in certain year.
|
||||
|
||||
Also this example contains 3 mini applications that are based on ``movies``
|
||||
library :
|
||||
|
||||
1. ``app_csv.py`` - list movies by certain criteria from csv file database.
|
||||
2. ``app_db.py`` - list movies by certain criteria from sqlite database.
|
||||
3. ``app_db_csv.py`` - list movies by certain criteria from csv file and
|
||||
sqlite databases.
|
||||
|
||||
Instructions for running:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python create_db.py
|
||||
|
||||
python app_csv.py
|
||||
python app_db.py
|
||||
python app_db_csv.py
|
||||
|
||||
|
||||
Full code of example could be found on GitHub_.
|
||||
|
||||
Movies library
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Classes diagram:
|
||||
|
||||
.. image:: /images/miniapps/movie_lister/classes.png
|
||||
:width: 100%
|
||||
:align: center
|
||||
|
||||
|
||||
Movies library structure:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
/movies
|
||||
/__init__.py
|
||||
/finders.py
|
||||
/listers.py
|
||||
/models.py
|
||||
|
||||
|
||||
Listing of ``movies/__init__.py``:
|
||||
|
||||
.. literalinclude:: ../../examples/miniapps/movie_lister/movies/__init__.py
|
||||
:language: python
|
||||
:linenos:
|
||||
|
||||
Csv application
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Listing of ``app_csv.py``:
|
||||
|
||||
.. literalinclude:: ../../examples/miniapps/movie_lister/app_csv.py
|
||||
:language: python
|
||||
:linenos:
|
||||
|
||||
Database application
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Listing of ``app_db.py``:
|
||||
|
||||
.. literalinclude:: ../../examples/miniapps/movie_lister/app_db.py
|
||||
:language: python
|
||||
:linenos:
|
||||
|
||||
Csv and database application
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Listing of ``app_db_csv.py``:
|
||||
|
||||
.. literalinclude:: ../../examples/miniapps/movie_lister/app_db_csv.py
|
||||
:language: python
|
||||
:linenos:
|
||||
|
||||
.. _GitHub: https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/movie_lister
|
|
@ -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/bundles.png
Normal file
After Width: | Height: | Size: 295 KiB |
BIN
docs/images/catalogs/declarative.png
Normal file
After Width: | Height: | Size: 49 KiB |
BIN
docs/images/catalogs/declarative_inheritance.png
Normal file
After Width: | Height: | Size: 78 KiB |
BIN
docs/images/catalogs/declarative_injections.png
Normal file
After Width: | Height: | Size: 231 KiB |
BIN
docs/images/internals.png
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
docs/images/miniapps/movie_lister/classes.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
docs/images/providers/callable.png
Normal file
After Width: | Height: | Size: 82 KiB |
BIN
docs/images/providers/custom_provider.png
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
docs/images/providers/external_dependency.png
Normal file
After Width: | Height: | Size: 90 KiB |
BIN
docs/images/providers/factory.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
docs/images/providers/factory_attribute_injection.png
Normal file
After Width: | Height: | Size: 66 KiB |
BIN
docs/images/providers/factory_attribute_injections.png
Normal file
After Width: | Height: | Size: 66 KiB |
BIN
docs/images/providers/factory_delegation.png
Normal file
After Width: | Height: | Size: 47 KiB |
BIN
docs/images/providers/factory_init_injections.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
docs/images/providers/factory_init_injections_and_contexts.png
Normal file
After Width: | Height: | Size: 68 KiB |
BIN
docs/images/providers/factory_method_injections.png
Normal file
After Width: | Height: | Size: 72 KiB |
BIN
docs/images/providers/overriding_simple.png
Normal file
After Width: | Height: | Size: 36 KiB |