mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2025-07-14 10:02:23 +03:00
Compare commits
No commits in common. "master" and "4.41.0" have entirely different histories.
10
.coveragerc
Normal file
10
.coveragerc
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
[run]
|
||||||
|
source = src/dependency_injector
|
||||||
|
omit = tests/unit
|
||||||
|
plugins = Cython.Coverage
|
||||||
|
|
||||||
|
[report]
|
||||||
|
show_missing = true
|
||||||
|
|
||||||
|
[html]
|
||||||
|
directory=reports/unittests/
|
|
@ -1,29 +0,0 @@
|
||||||
---
|
|
||||||
description: Code in Python and Cython
|
|
||||||
globs:
|
|
||||||
alwaysApply: false
|
|
||||||
---
|
|
||||||
- Follow PEP 8 rules
|
|
||||||
- When you write imports, split system, 3rd-party, and local imports with a new line
|
|
||||||
- Have two empty lines between the import block and the rest of the code
|
|
||||||
- Have an empty line (\n) at the end of every file
|
|
||||||
- If a file is supposed to be run, always add ``if __name__ == 'main'``
|
|
||||||
- Always follow a consistent pattern of using double or single quotes
|
|
||||||
- When there is a class without a docblock, leave one blank line before its members, e.g.:
|
|
||||||
```python
|
|
||||||
class Container(containers.DeclarativeContainer):
|
|
||||||
|
|
||||||
service = providers.Factory(Service)
|
|
||||||
```
|
|
||||||
|
|
||||||
- Avoid shortcuts in names unless absolutely necessary, exceptions:
|
|
||||||
```
|
|
||||||
arg
|
|
||||||
args
|
|
||||||
kwarg
|
|
||||||
kwargs
|
|
||||||
obj
|
|
||||||
cls
|
|
||||||
```
|
|
||||||
|
|
||||||
- Avoid inline comments unless absolutely necessary
|
|
|
@ -1,7 +0,0 @@
|
||||||
---
|
|
||||||
description: Build and run tests
|
|
||||||
globs:
|
|
||||||
alwaysApply: false
|
|
||||||
---
|
|
||||||
- Use Makefile commands to build, test, lint and other similar operations when they are available.
|
|
||||||
- Activate virtualenv before running any commands by ``. venv/bin/actvate``
|
|
|
@ -1,8 +0,0 @@
|
||||||
---
|
|
||||||
description: Run examples
|
|
||||||
globs:
|
|
||||||
alwaysApply: false
|
|
||||||
---
|
|
||||||
- When you run an example from the ``examples/`` folder, switch to the example folder and run it from there.
|
|
||||||
- If there are instructions on running the examples or its tests in readme, follow them
|
|
||||||
- Activate virtualenv before running any commands by ``. venv/bin/actvate``
|
|
|
@ -1,9 +0,0 @@
|
||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
end_of_line = lf
|
|
||||||
insert_final_newline = true
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
|
|
||||||
[*.{py,pyi,pxd,pyx}]
|
|
||||||
ij_visual_guides = 80,88
|
|
1
.github/FUNDING.yml
vendored
1
.github/FUNDING.yml
vendored
|
@ -1 +0,0 @@
|
||||||
github: rmk135
|
|
88
.github/workflows/publishing.yml
vendored
88
.github/workflows/publishing.yml
vendored
|
@ -10,20 +10,20 @@ jobs:
|
||||||
|
|
||||||
tests:
|
tests:
|
||||||
name: Run tests
|
name: Run tests
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: 3.13
|
python-version: 3.11
|
||||||
- run: pip install tox
|
- run: pip install tox
|
||||||
- run: tox
|
- run: tox
|
||||||
env:
|
env:
|
||||||
TOXENV: 3.13
|
TOXENV: 3.11
|
||||||
|
|
||||||
linters:
|
linters:
|
||||||
name: Run linters
|
name: Run linters
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-22.04
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
toxenv: [flake8, pydocstyle, mypy, pylint]
|
toxenv: [flake8, pydocstyle, mypy, pylint]
|
||||||
|
@ -31,7 +31,7 @@ jobs:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: 3.13
|
python-version: 3.11
|
||||||
- run: pip install tox
|
- run: pip install tox
|
||||||
- run: tox
|
- run: tox
|
||||||
env:
|
env:
|
||||||
|
@ -40,18 +40,15 @@ jobs:
|
||||||
build-sdist:
|
build-sdist:
|
||||||
name: Build source tarball
|
name: Build source tarball
|
||||||
needs: [tests, linters]
|
needs: [tests, linters]
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: 3.13
|
python-version: 3.11
|
||||||
- run: |
|
- run: python setup.py sdist
|
||||||
python -m pip install --upgrade build
|
- uses: actions/upload-artifact@v3
|
||||||
python -m build --sdist
|
|
||||||
- uses: actions/upload-artifact@v4
|
|
||||||
with:
|
with:
|
||||||
name: cibw-sdist
|
|
||||||
path: ./dist/*
|
path: ./dist/*
|
||||||
|
|
||||||
build-wheels:
|
build-wheels:
|
||||||
|
@ -60,65 +57,62 @@ jobs:
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-24.04, ubuntu-24.04-arm, windows-2022, macos-14]
|
os: [ubuntu-22.04, windows-2019, macos-11]
|
||||||
env:
|
env:
|
||||||
CIBW_ENABLE: pypy
|
CIBW_SKIP: cp27-win*
|
||||||
CIBW_ENVIRONMENT: >-
|
|
||||||
PIP_CONFIG_SETTINGS="build_ext=-j4"
|
|
||||||
DEPENDENCY_INJECTOR_LIMITED_API="1"
|
|
||||||
CFLAGS="-g0"
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Build wheels
|
- name: Build wheels
|
||||||
uses: pypa/cibuildwheel@v3.0.0
|
uses: pypa/cibuildwheel@v2.11.3
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
|
|
||||||
path: ./wheelhouse/*.whl
|
path: ./wheelhouse/*.whl
|
||||||
|
|
||||||
test-publish:
|
build-wheels-linux-aarch64:
|
||||||
name: Upload release to TestPyPI
|
name: Build wheels (ubuntu-22.04-aarch64)
|
||||||
needs: [build-sdist, build-wheels]
|
needs: [tests, linters]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
environment: test-pypi
|
|
||||||
permissions:
|
|
||||||
id-token: write
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/download-artifact@v4
|
- uses: actions/checkout@v3
|
||||||
|
- name: Set up QEMU
|
||||||
|
if: runner.os == 'Linux'
|
||||||
|
uses: docker/setup-qemu-action@v2
|
||||||
|
- name: Build wheels
|
||||||
|
uses: pypa/cibuildwheel@v2.11.3
|
||||||
|
env:
|
||||||
|
CIBW_ARCHS_LINUX: aarch64
|
||||||
|
- uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
pattern: cibw-*
|
path: ./wheelhouse/*.whl
|
||||||
path: dist
|
|
||||||
merge-multiple: true
|
|
||||||
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
||||||
with:
|
|
||||||
repository-url: https://test.pypi.org/legacy/
|
|
||||||
|
|
||||||
publish:
|
publish:
|
||||||
name: Upload release to PyPI
|
name: Publish on PyPI
|
||||||
needs: [build-sdist, build-wheels, test-publish]
|
needs: [build-sdist, build-wheels, build-wheels-linux-aarch64]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
environment: pypi
|
|
||||||
permissions:
|
|
||||||
id-token: write
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/download-artifact@v4
|
- uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
pattern: cibw-*
|
name: artifact
|
||||||
path: dist
|
path: dist
|
||||||
merge-multiple: true
|
|
||||||
- uses: pypa/gh-action-pypi-publish@release/v1
|
- 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:
|
publish-docs:
|
||||||
name: Publish docs
|
name: Publish docs
|
||||||
needs: [publish]
|
needs: [publish]
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: 3.13
|
python-version: 3.11
|
||||||
- run: pip install awscli
|
|
||||||
- run: pip install -r requirements-doc.txt
|
- run: pip install -r requirements-doc.txt
|
||||||
|
- run: pip install awscli
|
||||||
- run: pip install -e .
|
- run: pip install -e .
|
||||||
- run: (cd docs && make clean html)
|
- run: (cd docs && make clean html)
|
||||||
- run: |
|
- run: |
|
||||||
|
|
32
.github/workflows/tests-and-linters.yml
vendored
32
.github/workflows/tests-and-linters.yml
vendored
|
@ -4,12 +4,12 @@ on: [push, pull_request, workflow_dispatch]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
test-on-different-versions:
|
tests-on-legacy-versions:
|
||||||
name: Run tests
|
name: Run tests on legacy versions
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-20.04
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
|
python-version: [2.7, 3.5, 3.6, 3.7, pypy2.7, pypy3.9]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
|
@ -18,35 +18,39 @@ jobs:
|
||||||
- run: pip install tox
|
- run: pip install tox
|
||||||
- run: tox
|
- run: tox
|
||||||
env:
|
env:
|
||||||
DEPENDENCY_INJECTOR_LIMITED_API: 1
|
|
||||||
TOXENV: ${{ matrix.python-version }}
|
TOXENV: ${{ matrix.python-version }}
|
||||||
|
|
||||||
test-different-pydantic-versions:
|
test-on-different-versions:
|
||||||
name: Run tests with different pydantic versions
|
name: Run tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
python-version: [3.8, 3.9, "3.10", 3.11]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: "3.12"
|
python-version: ${{ matrix.python-version }}
|
||||||
- run: pip install tox
|
- run: pip install tox
|
||||||
- run: tox -e pydantic-v1,pydantic-v2
|
- run: tox
|
||||||
|
env:
|
||||||
|
TOXENV: ${{ matrix.python-version }}
|
||||||
|
|
||||||
test-coverage:
|
test-coverage:
|
||||||
name: Run tests with coverage
|
name: Run tests with coverage
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
DEPENDENCY_INJECTOR_DEBUG_MODE: 1
|
DEPENDENCY_INJECTOR_DEBUG_MODE: 1
|
||||||
PIP_VERBOSE: 1
|
|
||||||
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
|
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: 3.12
|
python-version: 3.11
|
||||||
- run: pip install tox
|
- run: pip install tox cython
|
||||||
- run: tox -vv
|
- run: make cythonize
|
||||||
|
- run: tox
|
||||||
env:
|
env:
|
||||||
TOXENV: coveralls
|
TOXENV: coveralls
|
||||||
|
|
||||||
|
@ -60,7 +64,7 @@ jobs:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: 3.13
|
python-version: 3.11
|
||||||
- run: pip install tox
|
- run: pip install tox
|
||||||
- run: tox
|
- run: tox
|
||||||
env:
|
env:
|
||||||
|
|
15
.gitignore
vendored
15
.gitignore
vendored
|
@ -15,7 +15,6 @@ lib64/
|
||||||
parts/
|
parts/
|
||||||
sdist/
|
sdist/
|
||||||
var/
|
var/
|
||||||
wheelhouse/
|
|
||||||
*.egg-info/
|
*.egg-info/
|
||||||
.installed.cfg
|
.installed.cfg
|
||||||
*.egg
|
*.egg
|
||||||
|
@ -64,13 +63,13 @@ venv*/
|
||||||
# Vim Rope
|
# Vim Rope
|
||||||
.ropeproject/
|
.ropeproject/
|
||||||
|
|
||||||
# Cython artifacts
|
# C extensions
|
||||||
src/**/*.c
|
src/dependency_injector/*.h
|
||||||
src/**/*.h
|
src/dependency_injector/*.so
|
||||||
src/**/*.so
|
src/dependency_injector/containers/*.h
|
||||||
src/**/*.html
|
src/dependency_injector/containers/*.so
|
||||||
|
src/dependency_injector/providers/*.h
|
||||||
|
src/dependency_injector/providers/*.so
|
||||||
|
|
||||||
# Workspace for samples
|
# Workspace for samples
|
||||||
.workspace/
|
.workspace/
|
||||||
|
|
||||||
.vscode/
|
|
||||||
|
|
49
.pylintrc
Normal file
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,tests
|
||||||
|
|
||||||
|
[MESSAGES CONTROL]
|
||||||
|
|
||||||
|
# Disable the message(s) with the given id(s).
|
||||||
|
# disable-msg=
|
||||||
|
|
||||||
|
[SIMILARITIES]
|
||||||
|
|
||||||
|
# Minimum lines number of a similarity.
|
||||||
|
min-similarity-lines=5
|
||||||
|
|
||||||
|
[TYPECHECK]
|
||||||
|
ignore-mixin-members=yes
|
||||||
|
# ignored-classes=
|
||||||
|
zope=no
|
||||||
|
# generated-members=providedBy,implementedBy,rawDataReceived
|
||||||
|
|
||||||
|
[DESIGN]
|
||||||
|
# Maximum number of arguments for function / method
|
||||||
|
max-args=10
|
||||||
|
|
||||||
|
# Maximum number of locals for function / method body
|
||||||
|
max-locals=20
|
||||||
|
|
||||||
|
# Maximum number of return / yield for function / method body
|
||||||
|
max-returns=10
|
||||||
|
|
||||||
|
# Maximum number of branch for function / method body
|
||||||
|
max-branchs=10
|
||||||
|
|
||||||
|
# Maximum number of statements in function / method body
|
||||||
|
max-statements=60
|
||||||
|
|
||||||
|
# Maximum number of parents for a class (see R0901).
|
||||||
|
max-parents=10
|
||||||
|
|
||||||
|
# Maximum number of attributes for a class (see R0902).
|
||||||
|
max-attributes=30
|
||||||
|
|
||||||
|
# Minimum number of public methods for a class (see R0903).
|
||||||
|
min-public-methods=0
|
||||||
|
|
||||||
|
# Maximum number of public methods for a class (see R0904).
|
||||||
|
max-public-methods=30
|
|
@ -20,5 +20,3 @@ Dependency Injector Contributors
|
||||||
+ Ngo Thanh Loi (Leonn) (loingo95)
|
+ Ngo Thanh Loi (Leonn) (loingo95)
|
||||||
+ Thiago Hiromi (thiromi)
|
+ Thiago Hiromi (thiromi)
|
||||||
+ Felipe Rubio (krouw)
|
+ Felipe Rubio (krouw)
|
||||||
+ Anton Petrov (anton-petrov)
|
|
||||||
+ ZipFile (ZipFile)
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
Copyright (c) 2024, Roman Mogylatov
|
Copyright (c) 2022, Roman Mogylatov
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
recursive-include src/dependency_injector *.py* *.c py.typed
|
recursive-include src/dependency_injector *.py* *.c
|
||||||
recursive-include tests *.py
|
recursive-include tests *.py
|
||||||
include README.rst
|
include README.rst
|
||||||
include CONTRIBUTORS.rst
|
include CONTRIBUTORS.rst
|
||||||
include LICENSE.rst
|
include LICENSE.rst
|
||||||
|
include requirements.txt
|
||||||
include setup.py
|
include setup.py
|
||||||
include tox.ini
|
include tox.ini
|
||||||
|
include py.typed
|
||||||
|
|
32
Makefile
32
Makefile
|
@ -1,6 +1,14 @@
|
||||||
VERSION := $(shell python setup.py --version)
|
VERSION := $(shell python setup.py --version)
|
||||||
|
|
||||||
export COVERAGE_RCFILE := pyproject.toml
|
CYTHON_SRC := $(shell find src/dependency_injector -name '*.pyx')
|
||||||
|
|
||||||
|
CYTHON_DIRECTIVES = -Xlanguage_level=2
|
||||||
|
|
||||||
|
ifdef DEPENDENCY_INJECTOR_DEBUG_MODE
|
||||||
|
CYTHON_DIRECTIVES += -Xprofile=True
|
||||||
|
CYTHON_DIRECTIVES += -Xlinetrace=True
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
# Clean sources
|
# Clean sources
|
||||||
|
@ -17,17 +25,21 @@ clean:
|
||||||
find examples -name '*.py[co]' -delete
|
find examples -name '*.py[co]' -delete
|
||||||
find examples -name '__pycache__' -delete
|
find examples -name '__pycache__' -delete
|
||||||
|
|
||||||
build: clean
|
cythonize:
|
||||||
# Compile C extensions
|
# Compile Cython to C
|
||||||
python setup.py build_ext --inplace
|
cython -a $(CYTHON_DIRECTIVES) $(CYTHON_SRC)
|
||||||
# Move all Cython html reports
|
# Move all Cython html reports
|
||||||
mkdir -p reports/cython/
|
mkdir -p reports/cython/
|
||||||
find src -name '*.html' -exec mv {} reports/cython/ \;
|
find src -name '*.html' -exec mv {} reports/cython/ \;
|
||||||
|
|
||||||
|
build: clean cythonize
|
||||||
|
# Compile C extensions
|
||||||
|
python setup.py build_ext --inplace
|
||||||
|
|
||||||
docs-live:
|
docs-live:
|
||||||
sphinx-autobuild docs docs/_build/html
|
sphinx-autobuild docs docs/_build/html
|
||||||
|
|
||||||
install: uninstall clean build
|
install: uninstall clean cythonize
|
||||||
pip install -ve .
|
pip install -ve .
|
||||||
|
|
||||||
uninstall:
|
uninstall:
|
||||||
|
@ -36,9 +48,9 @@ uninstall:
|
||||||
test:
|
test:
|
||||||
# Unit tests with coverage report
|
# Unit tests with coverage report
|
||||||
coverage erase
|
coverage erase
|
||||||
coverage run -m pytest
|
coverage run --rcfile=./.coveragerc -m pytest -c tests/.configs/pytest.ini
|
||||||
coverage report
|
coverage report --rcfile=./.coveragerc
|
||||||
coverage html
|
coverage html --rcfile=./.coveragerc
|
||||||
|
|
||||||
check:
|
check:
|
||||||
flake8 src/dependency_injector/
|
flake8 src/dependency_injector/
|
||||||
|
@ -49,9 +61,9 @@ check:
|
||||||
|
|
||||||
mypy tests/typing
|
mypy tests/typing
|
||||||
|
|
||||||
test-publish: build
|
test-publish: cythonize
|
||||||
# Create distributions
|
# Create distributions
|
||||||
python -m build --sdist
|
python setup.py sdist
|
||||||
# Upload distributions to PyPI
|
# Upload distributions to PyPI
|
||||||
twine upload --repository testpypi dist/dependency-injector-$(VERSION)*
|
twine upload --repository testpypi dist/dependency-injector-$(VERSION)*
|
||||||
|
|
||||||
|
|
9
docs/_static/custom.css
vendored
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;
|
|
||||||
}
|
|
11
docs/_static/disqus.js
vendored
Normal file
11
docs/_static/disqus.js
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
var disqus_shortname;
|
||||||
|
var disqus_identifier;
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
var disqus_thread = $("#disqus_thread");
|
||||||
|
disqus_shortname = disqus_thread.data('disqus-shortname');
|
||||||
|
disqus_identifier = disqus_thread.data('disqus-identifier');
|
||||||
|
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
|
||||||
|
dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
|
||||||
|
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
|
||||||
|
});
|
1
docs/_static/sponsor.html
vendored
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>
|
|
|
@ -1,9 +0,0 @@
|
||||||
dependency_injector.ext.starlette
|
|
||||||
=================================
|
|
||||||
|
|
||||||
.. automodule:: dependency_injector.ext.starlette
|
|
||||||
:members:
|
|
||||||
:inherited-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
.. disqus::
|
|
|
@ -2,11 +2,10 @@ API Documentation
|
||||||
=================
|
=================
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|
||||||
top-level
|
top-level
|
||||||
providers
|
providers
|
||||||
containers
|
containers
|
||||||
wiring
|
wiring
|
||||||
errors
|
errors
|
||||||
asgi-lifespan
|
|
||||||
|
|
10
docs/conf.py
10
docs/conf.py
|
@ -33,7 +33,7 @@ sys.path.insert(0, os.path.abspath(".."))
|
||||||
extensions = [
|
extensions = [
|
||||||
"alabaster",
|
"alabaster",
|
||||||
"sphinx.ext.autodoc",
|
"sphinx.ext.autodoc",
|
||||||
"sphinx_disqus.disqus",
|
"sphinxcontrib.disqus",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
|
@ -52,7 +52,7 @@ master_doc = "index"
|
||||||
|
|
||||||
# General information about the project.
|
# General information about the project.
|
||||||
project = "Dependency Injector"
|
project = "Dependency Injector"
|
||||||
copyright = "2024, Roman Mogylatov"
|
copyright = "2022, Roman Mogylatov"
|
||||||
author = "Roman Mogylatov"
|
author = "Roman Mogylatov"
|
||||||
|
|
||||||
# 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
|
||||||
|
@ -72,7 +72,7 @@ release = version
|
||||||
#
|
#
|
||||||
# This is also used if you do content translation via gettext catalogs.
|
# This is also used if you do content translation via gettext catalogs.
|
||||||
# Usually you set "language" from the command line for these cases.
|
# Usually you set "language" from the command line for these cases.
|
||||||
language = "en"
|
language = None
|
||||||
|
|
||||||
# There are two options for replacing |today|: either, you set today to some
|
# There are two options for replacing |today|: either, you set today to some
|
||||||
# non-false value, then it is used:
|
# non-false value, then it is used:
|
||||||
|
@ -147,9 +147,6 @@ html_favicon = "favicon.ico"
|
||||||
# relative to this directory. They are copied after the builtin static files,
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||||
html_static_path = ["_static"]
|
html_static_path = ["_static"]
|
||||||
html_css_files = [
|
|
||||||
"custom.css",
|
|
||||||
]
|
|
||||||
|
|
||||||
# Add any extra paths that contain custom files (such as robots.txt or
|
# Add any extra paths that contain custom files (such as robots.txt or
|
||||||
# .htaccess) here, relative to this directory. These files are copied
|
# .htaccess) here, relative to this directory. These files are copied
|
||||||
|
@ -309,5 +306,4 @@ html_theme_options = {
|
||||||
"description": "Dependency injection framework for Python by Roman Mogylatov",
|
"description": "Dependency injection framework for Python by Roman Mogylatov",
|
||||||
"code_font_size": "10pt",
|
"code_font_size": "10pt",
|
||||||
"analytics_id": "UA-67012059-1",
|
"analytics_id": "UA-67012059-1",
|
||||||
"donate_url": "https://github.com/sponsors/rmk135",
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,6 +78,4 @@ Sources
|
||||||
|
|
||||||
Explore the sources on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/aiohttp>`_.
|
Explore the sources on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/aiohttp>`_.
|
||||||
|
|
||||||
.. include:: ../sponsor.rst
|
|
||||||
|
|
||||||
.. disqus::
|
.. disqus::
|
||||||
|
|
|
@ -84,6 +84,4 @@ 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>`_.
|
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::
|
.. disqus::
|
|
@ -90,6 +90,4 @@ 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>`_.
|
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::
|
.. disqus::
|
||||||
|
|
|
@ -17,6 +17,4 @@ Listing of ``boto3_session_example.py``:
|
||||||
.. literalinclude:: ../../examples/miniapps/boto3-session/boto3_session_example.py
|
.. literalinclude:: ../../examples/miniapps/boto3-session/boto3_session_example.py
|
||||||
:language: python
|
:language: python
|
||||||
|
|
||||||
.. include:: ../sponsor.rst
|
|
||||||
|
|
||||||
.. disqus::
|
.. disqus::
|
||||||
|
|
|
@ -129,6 +129,4 @@ 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>`_.
|
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::
|
.. disqus::
|
||||||
|
|
|
@ -78,7 +78,7 @@ Container is wired to the ``views`` module in the app config ``web/apps.py``:
|
||||||
|
|
||||||
.. literalinclude:: ../../examples/miniapps/django/web/apps.py
|
.. literalinclude:: ../../examples/miniapps/django/web/apps.py
|
||||||
:language: python
|
:language: python
|
||||||
:emphasize-lines: 12
|
:emphasize-lines: 13
|
||||||
|
|
||||||
Tests
|
Tests
|
||||||
-----
|
-----
|
||||||
|
@ -94,6 +94,4 @@ Sources
|
||||||
|
|
||||||
Explore the sources on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/django>`_.
|
Explore the sources on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/django>`_.
|
||||||
|
|
||||||
.. include:: ../sponsor.rst
|
|
||||||
|
|
||||||
.. disqus::
|
.. disqus::
|
||||||
|
|
|
@ -95,6 +95,4 @@ See also:
|
||||||
- Resource provider :ref:`resource-async-initializers`
|
- Resource provider :ref:`resource-async-initializers`
|
||||||
- Wiring :ref:`async-injections-wiring`
|
- Wiring :ref:`async-injections-wiring`
|
||||||
|
|
||||||
.. include:: ../sponsor.rst
|
|
||||||
|
|
||||||
.. disqus::
|
.. disqus::
|
||||||
|
|
|
@ -116,6 +116,4 @@ Sources
|
||||||
|
|
||||||
The source code is available on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/fastapi-sqlalchemy>`_.
|
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::
|
.. disqus::
|
||||||
|
|
|
@ -76,6 +76,4 @@ Sources
|
||||||
|
|
||||||
Explore the sources on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/fastapi>`_.
|
Explore the sources on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/fastapi>`_.
|
||||||
|
|
||||||
.. include:: ../sponsor.rst
|
|
||||||
|
|
||||||
.. disqus::
|
.. disqus::
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
.. _fastdepends-example:
|
|
||||||
|
|
||||||
FastDepends example
|
|
||||||
===================
|
|
||||||
|
|
||||||
.. meta::
|
|
||||||
:keywords: Python,Dependency Injection,FastDepends,Example
|
|
||||||
:description: This example demonstrates a usage of the FastDepends and Dependency Injector.
|
|
||||||
|
|
||||||
|
|
||||||
This example demonstrates how to use ``Dependency Injector`` with `FastDepends <https://github.com/Lancetnik/FastDepends>`_, a lightweight dependency injection framework inspired by FastAPI's dependency system, but without the web framework components.
|
|
||||||
|
|
||||||
Basic Usage
|
|
||||||
-----------
|
|
||||||
|
|
||||||
The integration between FastDepends and Dependency Injector is straightforward. Simply use Dependency Injector's ``Provide`` marker within FastDepends' ``Depends`` function:
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from dependency_injector import containers, providers
|
|
||||||
from dependency_injector.wiring import inject, Provide
|
|
||||||
from fast_depends import Depends
|
|
||||||
|
|
||||||
|
|
||||||
class CoefficientService:
|
|
||||||
@staticmethod
|
|
||||||
def get_coefficient() -> float:
|
|
||||||
return 1.2
|
|
||||||
|
|
||||||
|
|
||||||
class Container(containers.DeclarativeContainer):
|
|
||||||
service = providers.Factory(CoefficientService)
|
|
||||||
|
|
||||||
|
|
||||||
@inject
|
|
||||||
def apply_coefficient(
|
|
||||||
a: int,
|
|
||||||
coefficient_provider: CoefficientService = Depends(Provide[Container.service]),
|
|
||||||
) -> float:
|
|
||||||
return a * coefficient_provider.get_coefficient()
|
|
||||||
|
|
||||||
|
|
||||||
container = Container()
|
|
||||||
container.wire(modules=[sys.modules[__name__]])
|
|
||||||
|
|
||||||
apply_coefficient(100) == 120.0
|
|
|
@ -86,6 +86,4 @@ Sources
|
||||||
|
|
||||||
Explore the sources on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/flask-blueprints>`_.
|
Explore the sources on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/flask-blueprints>`_.
|
||||||
|
|
||||||
.. include:: ../sponsor.rst
|
|
||||||
|
|
||||||
.. disqus::
|
.. disqus::
|
||||||
|
|
|
@ -84,6 +84,4 @@ Sources
|
||||||
|
|
||||||
Explore the sources on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/flask>`_.
|
Explore the sources on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/flask>`_.
|
||||||
|
|
||||||
.. include:: ../sponsor.rst
|
|
||||||
|
|
||||||
.. disqus::
|
.. disqus::
|
||||||
|
|
|
@ -22,6 +22,5 @@ Explore the examples to see the ``Dependency Injector`` in action.
|
||||||
fastapi
|
fastapi
|
||||||
fastapi-redis
|
fastapi-redis
|
||||||
fastapi-sqlalchemy
|
fastapi-sqlalchemy
|
||||||
fastdepends
|
|
||||||
|
|
||||||
.. disqus::
|
.. disqus::
|
||||||
|
|
|
@ -77,6 +77,4 @@ Sources
|
||||||
|
|
||||||
Explore the sources on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/sanic>`_.
|
Explore the sources on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/sanic>`_.
|
||||||
|
|
||||||
.. include:: ../sponsor.rst
|
|
||||||
|
|
||||||
.. disqus::
|
.. disqus::
|
||||||
|
|
|
@ -310,6 +310,4 @@ A few useful links related to a dependency injection design pattern for further
|
||||||
+ https://github.com/ets-labs/python-dependency-injector
|
+ https://github.com/ets-labs/python-dependency-injector
|
||||||
+ https://pypi.org/project/dependency-injector/
|
+ https://pypi.org/project/dependency-injector/
|
||||||
|
|
||||||
.. include:: ../sponsor.rst
|
|
||||||
|
|
||||||
.. disqus::
|
.. disqus::
|
||||||
|
|
|
@ -31,7 +31,7 @@ Key features of the ``Dependency Injector``:
|
||||||
|
|
||||||
The framework stands on the `PEP20 (The Zen of Python) <https://www.python.org/dev/peps/pep-0020/>`_ principle:
|
The framework stands on the `PEP20 (The Zen of Python) <https://www.python.org/dev/peps/pep-0020/>`_ principle:
|
||||||
|
|
||||||
.. code-block:: text
|
.. code-block:: plain
|
||||||
|
|
||||||
Explicit is better than implicit
|
Explicit is better than implicit
|
||||||
|
|
||||||
|
|
|
@ -7,103 +7,6 @@ that were made in every particular version.
|
||||||
From version 0.7.6 *Dependency Injector* framework strictly
|
From version 0.7.6 *Dependency Injector* framework strictly
|
||||||
follows `Semantic versioning`_
|
follows `Semantic versioning`_
|
||||||
|
|
||||||
4.48.1
|
|
||||||
------
|
|
||||||
|
|
||||||
* Improve performance of ``dependency_injector._cwiring.DependencyResolver``
|
|
||||||
* Add ``typing-extensions`` as a dependency for older Python versions (<3.11)
|
|
||||||
* Produce warning on ``@inject``s without ``Provide[...]`` marks
|
|
||||||
* Add support for `resource_type` in ``Lifespan``s
|
|
||||||
|
|
||||||
4.48.0
|
|
||||||
------
|
|
||||||
|
|
||||||
- Improve performance of wiring (`#897 <https://github.com/ets-labs/python-dependency-injector/pull/897>`_)
|
|
||||||
- Add Context Manager support to Resource provider (`#899 <https://github.com/ets-labs/python-dependency-injector/pull/899>`_)
|
|
||||||
- Add support for async generator injections (`#900 <https://github.com/ets-labs/python-dependency-injector/pull/900>`_)
|
|
||||||
- Fix unintended dependency on ``typing_extensions`` (`#902 <https://github.com/ets-labs/python-dependency-injector/pull/902>`_)
|
|
||||||
- Add support for Fast Depends (`#898 <https://github.com/ets-labs/python-dependency-injector/pull/898>`_)
|
|
||||||
- Add ``resource_type`` parameter to init and shutdown resources using specialized providers (`#858 <https://github.com/ets-labs/python-dependency-injector/pull/858>`_)
|
|
||||||
|
|
||||||
4.47.1
|
|
||||||
------
|
|
||||||
|
|
||||||
- Fix typing for wiring marker (`#892 <https://github.com/ets-labs/python-dependency-injector/pull/896>`_)
|
|
||||||
- Strip debug symbols in wheels
|
|
||||||
|
|
||||||
4.47.0
|
|
||||||
------
|
|
||||||
|
|
||||||
- Add support for ``Annotated`` type for module and class attribute injection in wiring,
|
|
||||||
with updated documentation and examples.
|
|
||||||
See discussion:
|
|
||||||
https://github.com/ets-labs/python-dependency-injector/pull/721#issuecomment-2025263718
|
|
||||||
- Fix ``root`` property shadowing in ``ConfigurationOption`` (`#875 <https://github.com/ets-labs/python-dependency-injector/pull/875>`_)
|
|
||||||
- Fix incorrect monkeypatching during ``wire()`` that could violate MRO in some classes (`#886 <https://github.com/ets-labs/python-dependency-injector/pull/886>`_)
|
|
||||||
- ABI3 wheels are now published for CPython.
|
|
||||||
- Drop support of Python 3.7.
|
|
||||||
|
|
||||||
4.46.0
|
|
||||||
------
|
|
||||||
|
|
||||||
- Add option to disable env var interpolation in configs (`#861 <https://github.com/ets-labs/python-dependency-injector/pull/861>`_)
|
|
||||||
- Fix ``Closing`` dependency resolution (`#852 <https://github.com/ets-labs/python-dependency-injector/pull/852>`_)
|
|
||||||
- Add support for ``inspect.iscoroutinefunction()`` in ``Coroutine`` provider (`#830 <https://github.com/ets-labs/python-dependency-injector/pull/830>`_)
|
|
||||||
- Fix broken wiring of sync inject-decorated methods (`#673 <https://github.com/ets-labs/python-dependency-injector/pull/673>`_)
|
|
||||||
- Add support for ``typing.Annotated`` (`#721 <https://github.com/ets-labs/python-dependency-injector/pull/721>`_, `#853 <https://github.com/ets-labs/python-dependency-injector/pull/853>`_)
|
|
||||||
- Documentation updates for movie-lister example (`#747 <https://github.com/ets-labs/python-dependency-injector/pull/747>`_)
|
|
||||||
- Fix type propagation in ``Provider.provider`` (`#744 <https://github.com/ets-labs/python-dependency-injector/pull/744>`_)
|
|
||||||
|
|
||||||
Many thanks for the contributions to:
|
|
||||||
- `ZipFile <https://github.com/ZipFile>`_
|
|
||||||
- `Yegor Statkevich <https://github.com/jazzthief>`_
|
|
||||||
- `Federico Tomasi <https://github.com/federinik>`_
|
|
||||||
- `Martin Lafrance <https://github.com/martlaf>`_
|
|
||||||
- `Philip Bjorge <https://github.com/philipbjorge>`_
|
|
||||||
- `Ilya Kazakov <https://github.com/mrKazzila>`_
|
|
||||||
|
|
||||||
4.45.0
|
|
||||||
--------
|
|
||||||
- Add Starlette lifespan handler implementation (`#683 <https://github.com/ets-labs/python-dependency-injector/pull/683>`_).
|
|
||||||
- Raise exception in ``ThreadLocalSingleton`` instead of hiding it in finally (`#845 <https://github.com/ets-labs/python-dependency-injector/pull/845>`_).
|
|
||||||
- Improve debuggability of ``deepcopy`` errors (`#839 <https://github.com/ets-labs/python-dependency-injector/pull/839>`_).
|
|
||||||
- Update examples (`#838 <https://github.com/ets-labs/python-dependency-injector/pull/838>`_).
|
|
||||||
- Upgrade testing dependencies (`#837 <https://github.com/ets-labs/python-dependency-injector/pull/837>`_).
|
|
||||||
- Add minor fixes to the documentation (`#709 <https://github.com/ets-labs/python-dependency-injector/pull/709>`_).
|
|
||||||
- Remove ``six`` from the dependencies (`3ba4704 <https://github.com/ets-labs/python-dependency-injector/commit/3ba4704bc1cb00310749fd2eda0c8221167c313c>`_).
|
|
||||||
|
|
||||||
Many thanks for the contributions to:
|
|
||||||
- `ZipFile <https://github.com/ZipFile>`_
|
|
||||||
- `František Trebuňa <https://github.com/gortibaldik>`_
|
|
||||||
- `JC (Jonathan Chen) <https://github.com/dijonkitchen>`_
|
|
||||||
|
|
||||||
4.44.0
|
|
||||||
--------
|
|
||||||
- Implement support for Pydantic 2. PR: `#832 <https://github.com/ets-labs/python-dependency-injector/pull/832>`_.
|
|
||||||
- Implement `PEP-517 <https://peps.python.org/pep-0517/>`_, `PEP-518 <https://peps.python.org/pep-0518/>`_, and
|
|
||||||
`PEP-621 <https://peps.python.org/pep-0621/>`_. PR: `#829 <https://github.com/ets-labs/python-dependency-injector/pull/829>`_.
|
|
||||||
|
|
||||||
Many thanks to `ZipFile <https://github.com/ZipFile>`_ for both contributions.
|
|
||||||
|
|
||||||
4.43.0
|
|
||||||
--------
|
|
||||||
- Add support for Python 3.13.
|
|
||||||
- Migrate to Cython 3 (version 3.0.11). Many thanks to `ZipFile <https://github.com/ZipFile>`_ for
|
|
||||||
this contribution `#813 <https://github.com/ets-labs/python-dependency-injector/pull/813>`_.
|
|
||||||
|
|
||||||
4.42.0
|
|
||||||
--------
|
|
||||||
- Promote release ``4.42.0b1`` to a production release.
|
|
||||||
- Fix the Disqus comment widget.
|
|
||||||
|
|
||||||
4.42.0b1
|
|
||||||
--------
|
|
||||||
|
|
||||||
- Add support of Python 3.12.
|
|
||||||
- Drop support of Python 2.7, 3.5, and 3.6.
|
|
||||||
- Regenerate C sources using Cython 0.29.37.
|
|
||||||
- Update ``cibuildwheel`` to version ``2.20.0``.
|
|
||||||
|
|
||||||
4.41.0
|
4.41.0
|
||||||
------
|
------
|
||||||
- Add support of Python 3.11.
|
- Add support of Python 3.11.
|
||||||
|
@ -395,8 +298,8 @@ Many thanks to `ZipFile <https://github.com/ZipFile>`_ for both contributions.
|
||||||
- Make refactoring of wiring module and tests.
|
- Make refactoring of wiring module and tests.
|
||||||
See PR # `#406 <https://github.com/ets-labs/python-dependency-injector/issues/406>`_.
|
See PR # `#406 <https://github.com/ets-labs/python-dependency-injector/issues/406>`_.
|
||||||
Thanks to `@withshubh <https://github.com/withshubh>`_ for the contribution:
|
Thanks to `@withshubh <https://github.com/withshubh>`_ for the contribution:
|
||||||
- Remove unused imports in tests.
|
- Remove unused imports in tests.
|
||||||
- Use literal syntax to create data structure in tests.
|
- Use literal syntax to create data structure in tests.
|
||||||
- Add integration with a static analysis tool `DeepSource <https://deepsource.io/>`_.
|
- Add integration with a static analysis tool `DeepSource <https://deepsource.io/>`_.
|
||||||
|
|
||||||
4.26.0
|
4.26.0
|
||||||
|
|
|
@ -183,22 +183,22 @@ See also: :ref:`configuration-envs-interpolation`.
|
||||||
Loading from a Pydantic settings
|
Loading from a Pydantic settings
|
||||||
--------------------------------
|
--------------------------------
|
||||||
|
|
||||||
``Configuration`` provider can load configuration from a ``pydantic_settings.BaseSettings`` object using the
|
``Configuration`` provider can load configuration from a ``pydantic`` settings object using the
|
||||||
:py:meth:`Configuration.from_pydantic` method:
|
:py:meth:`Configuration.from_pydantic` method:
|
||||||
|
|
||||||
.. literalinclude:: ../../examples/providers/configuration/configuration_pydantic.py
|
.. literalinclude:: ../../examples/providers/configuration/configuration_pydantic.py
|
||||||
:language: python
|
:language: python
|
||||||
:lines: 3-
|
:lines: 3-
|
||||||
:emphasize-lines: 32
|
:emphasize-lines: 31
|
||||||
|
|
||||||
To get the data from pydantic settings ``Configuration`` provider calls its ``model_dump()`` method.
|
To get the data from pydantic settings ``Configuration`` provider calls ``Settings.dict()`` method.
|
||||||
If you need to pass an argument to this call, use ``.from_pydantic()`` keyword arguments.
|
If you need to pass an argument to this call, use ``.from_pydantic()`` keyword arguments.
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
container.config.from_pydantic(Settings(), exclude={"optional"})
|
container.config.from_pydantic(Settings(), exclude={"optional"})
|
||||||
|
|
||||||
Alternatively, you can provide a ``pydantic_settings.BaseSettings`` object over the configuration provider argument. In that case,
|
Alternatively, you can provide a ``pydantic`` settings object over the configuration provider argument. In that case,
|
||||||
the container will call ``config.from_pydantic()`` automatically:
|
the container will call ``config.from_pydantic()`` automatically:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
@ -215,23 +215,18 @@ the container will call ``config.from_pydantic()`` automatically:
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
``Dependency Injector`` doesn't install ``pydantic-settings`` by default.
|
``Dependency Injector`` doesn't install ``pydantic`` by default.
|
||||||
|
|
||||||
You can install the ``Dependency Injector`` with an extra dependency::
|
You can install the ``Dependency Injector`` with an extra dependency::
|
||||||
|
|
||||||
pip install dependency-injector[pydantic2]
|
pip install dependency-injector[pydantic]
|
||||||
|
|
||||||
or install ``pydantic-settings`` directly::
|
or install ``pydantic`` directly::
|
||||||
|
|
||||||
pip install pydantic-settings
|
pip install pydantic
|
||||||
|
|
||||||
*Don't forget to mirror the changes in the requirements file.*
|
*Don't forget to mirror the changes in the requirements file.*
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
For backward-compatibility, Pydantic v1 is still supported.
|
|
||||||
Passing ``pydantic.BaseSettings`` instances will work just as fine as ``pydantic_settings.BaseSettings``.
|
|
||||||
|
|
||||||
Loading from a dictionary
|
Loading from a dictionary
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
|
@ -366,19 +361,6 @@ See also: :ref:`configuration-strict-mode`.
|
||||||
|
|
||||||
assert container.config.section.option() is None
|
assert container.config.section.option() is None
|
||||||
|
|
||||||
If you want to disable environment variables interpolation, pass ``envs_required=None``:
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
:caption: templates.yml
|
|
||||||
|
|
||||||
template_string: 'Hello, ${name}!'
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
>>> container.config.from_yaml("templates.yml", envs_required=None)
|
|
||||||
>>> container.config.template_string()
|
|
||||||
'Hello, ${name}!'
|
|
||||||
|
|
||||||
Mandatory and optional sources
|
Mandatory and optional sources
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
|
|
|
@ -61,12 +61,11 @@ When you call ``.shutdown()`` method on a resource provider, it will remove the
|
||||||
if any, and switch to uninitialized state. Some of resource initializer types support specifying custom
|
if any, and switch to uninitialized state. Some of resource initializer types support specifying custom
|
||||||
resource shutdown.
|
resource shutdown.
|
||||||
|
|
||||||
Resource provider supports 4 types of initializers:
|
Resource provider supports 3 types of initializers:
|
||||||
|
|
||||||
- Function
|
- Function
|
||||||
- Context Manager
|
- Generator
|
||||||
- Generator (legacy)
|
- Subclass of ``resources.Resource``
|
||||||
- Subclass of ``resources.Resource`` (legacy)
|
|
||||||
|
|
||||||
Function initializer
|
Function initializer
|
||||||
--------------------
|
--------------------
|
||||||
|
@ -104,44 +103,8 @@ you configure global resource:
|
||||||
|
|
||||||
Function initializer does not provide a way to specify custom resource shutdown.
|
Function initializer does not provide a way to specify custom resource shutdown.
|
||||||
|
|
||||||
Context Manager initializer
|
Generator initializer
|
||||||
---------------------------
|
---------------------
|
||||||
|
|
||||||
This is an extension to the Function initializer. Resource provider automatically detects if the initializer returns a
|
|
||||||
context manager and uses it to manage the resource lifecycle.
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
from dependency_injector import containers, providers
|
|
||||||
|
|
||||||
class DatabaseConnection:
|
|
||||||
def __init__(self, host, port, user, password):
|
|
||||||
self.host = host
|
|
||||||
self.port = port
|
|
||||||
self.user = user
|
|
||||||
self.password = password
|
|
||||||
|
|
||||||
def __enter__(self):
|
|
||||||
print(f"Connecting to {self.host}:{self.port} as {self.user}")
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
||||||
print("Closing connection")
|
|
||||||
|
|
||||||
|
|
||||||
class Container(containers.DeclarativeContainer):
|
|
||||||
|
|
||||||
config = providers.Configuration()
|
|
||||||
db = providers.Resource(
|
|
||||||
DatabaseConnection,
|
|
||||||
host=config.db.host,
|
|
||||||
port=config.db.port,
|
|
||||||
user=config.db.user,
|
|
||||||
password=config.db.password,
|
|
||||||
)
|
|
||||||
|
|
||||||
Generator initializer (legacy)
|
|
||||||
------------------------------
|
|
||||||
|
|
||||||
Resource provider can use 2-step generators:
|
Resource provider can use 2-step generators:
|
||||||
|
|
||||||
|
@ -191,13 +154,8 @@ object is not mandatory. You can leave ``yield`` statement empty:
|
||||||
argument2=...,
|
argument2=...,
|
||||||
)
|
)
|
||||||
|
|
||||||
.. note::
|
Subclass initializer
|
||||||
|
--------------------
|
||||||
Generator initializers are automatically wrapped with ``contextmanager`` or ``asynccontextmanager`` decorator when
|
|
||||||
provided to a ``Resource`` provider.
|
|
||||||
|
|
||||||
Subclass initializer (legacy)
|
|
||||||
-----------------------------
|
|
||||||
|
|
||||||
You can create resource initializer by implementing a subclass of the ``resources.Resource``:
|
You can create resource initializer by implementing a subclass of the ``resources.Resource``:
|
||||||
|
|
||||||
|
@ -252,72 +210,6 @@ first argument.
|
||||||
|
|
||||||
.. _resource-provider-wiring-closing:
|
.. _resource-provider-wiring-closing:
|
||||||
|
|
||||||
Scoping Resources using specialized subclasses
|
|
||||||
----------------------------------------------
|
|
||||||
|
|
||||||
You can use specialized subclasses of ``Resource`` provider to initialize and shutdown resources by type.
|
|
||||||
Allowing for example to only initialize a subgroup of resources.
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
class ScopedResource(resources.Resource):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def init_service(name) -> Service:
|
|
||||||
print(f"Init {name}")
|
|
||||||
yield Service()
|
|
||||||
print(f"Shutdown {name}")
|
|
||||||
|
|
||||||
class Container(containers.DeclarativeContainer):
|
|
||||||
|
|
||||||
scoped = ScopedResource(
|
|
||||||
init_service,
|
|
||||||
"scoped",
|
|
||||||
)
|
|
||||||
|
|
||||||
generic = providers.Resource(
|
|
||||||
init_service,
|
|
||||||
"generic",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
To initialize resources by type you can use ``init_resources(resource_type)`` and ``shutdown_resources(resource_type)``
|
|
||||||
methods adding the resource type as an argument:
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
def main():
|
|
||||||
container = Container()
|
|
||||||
container.init_resources(ScopedResource)
|
|
||||||
# Generates:
|
|
||||||
# >>> Init scoped
|
|
||||||
|
|
||||||
container.shutdown_resources(ScopedResource)
|
|
||||||
# Generates:
|
|
||||||
# >>> Shutdown scoped
|
|
||||||
|
|
||||||
|
|
||||||
And to initialize all resources you can use ``init_resources()`` and ``shutdown_resources()`` without arguments:
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
def main():
|
|
||||||
container = Container()
|
|
||||||
container.init_resources()
|
|
||||||
# Generates:
|
|
||||||
# >>> Init scoped
|
|
||||||
# >>> Init generic
|
|
||||||
|
|
||||||
container.shutdown_resources()
|
|
||||||
# Generates:
|
|
||||||
# >>> Shutdown scoped
|
|
||||||
# >>> Shutdown generic
|
|
||||||
|
|
||||||
|
|
||||||
It works using the ``traverse()`` method to find all resources of the specified type, selecting all resources
|
|
||||||
which are instances of the specified type.
|
|
||||||
|
|
||||||
|
|
||||||
Resources, wiring, and per-function execution scope
|
Resources, wiring, and per-function execution scope
|
||||||
---------------------------------------------------
|
---------------------------------------------------
|
||||||
|
|
||||||
|
@ -371,11 +263,10 @@ Asynchronous function initializer:
|
||||||
argument2=...,
|
argument2=...,
|
||||||
)
|
)
|
||||||
|
|
||||||
Asynchronous Context Manager initializer:
|
Asynchronous generator initializer:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@asynccontextmanager
|
|
||||||
async def init_async_resource(argument1=..., argument2=...):
|
async def init_async_resource(argument1=..., argument2=...):
|
||||||
connection = await connect()
|
connection = await connect()
|
||||||
yield connection
|
yield connection
|
||||||
|
@ -467,54 +358,5 @@ See also:
|
||||||
- Wiring :ref:`async-injections-wiring`
|
- Wiring :ref:`async-injections-wiring`
|
||||||
- :ref:`fastapi-redis-example`
|
- :ref:`fastapi-redis-example`
|
||||||
|
|
||||||
ASGI Lifespan Protocol Support
|
|
||||||
------------------------------
|
|
||||||
|
|
||||||
The :mod:`dependency_injector.ext.starlette` module provides a :class:`~dependency_injector.ext.starlette.Lifespan`
|
|
||||||
class that integrates resource providers with ASGI applications using the `Lifespan Protocol`_. This allows resources to
|
|
||||||
be automatically initialized at application startup and properly shut down when the application stops.
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
from contextlib import asynccontextmanager
|
|
||||||
from dependency_injector import containers, providers
|
|
||||||
from dependency_injector.wiring import Provide, inject
|
|
||||||
from dependency_injector.ext.starlette import Lifespan
|
|
||||||
from fastapi import FastAPI, Request, Depends, APIRouter
|
|
||||||
|
|
||||||
class Connection: ...
|
|
||||||
|
|
||||||
@asynccontextmanager
|
|
||||||
async def init_database():
|
|
||||||
print("opening database connection")
|
|
||||||
yield Connection()
|
|
||||||
print("closing database connection")
|
|
||||||
|
|
||||||
router = APIRouter()
|
|
||||||
|
|
||||||
@router.get("/")
|
|
||||||
@inject
|
|
||||||
async def index(request: Request, db: Connection = Depends(Provide["db"])):
|
|
||||||
# use the database connection here
|
|
||||||
return "OK!"
|
|
||||||
|
|
||||||
class Container(containers.DeclarativeContainer):
|
|
||||||
__self__ = providers.Self()
|
|
||||||
db = providers.Resource(init_database)
|
|
||||||
lifespan = providers.Singleton(Lifespan, __self__)
|
|
||||||
app = providers.Singleton(FastAPI, lifespan=lifespan)
|
|
||||||
_include_router = providers.Resource(
|
|
||||||
app.provided.include_router.call(),
|
|
||||||
router,
|
|
||||||
)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
import uvicorn
|
|
||||||
|
|
||||||
container = Container()
|
|
||||||
app = container.app()
|
|
||||||
uvicorn.run(app, host="localhost", port=8000)
|
|
||||||
|
|
||||||
.. _Lifespan Protocol: https://asgi.readthedocs.io/en/latest/specs/lifespan.html
|
|
||||||
|
|
||||||
.. disqus::
|
.. disqus::
|
||||||
|
|
|
@ -24,7 +24,7 @@ returns it on the rest of the calls.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
``Singleton`` provider makes dependencies injection only when it creates an object. When an object
|
``Singleton`` provider makes dependencies injection only when creates an object. When an object
|
||||||
is created and memorized ``Singleton`` provider just returns it without applying injections.
|
is created and memorized ``Singleton`` provider just returns it without applying injections.
|
||||||
|
|
||||||
Specialization of the provided type and abstract singletons work the same like like for the
|
Specialization of the provided type and abstract singletons work the same like like for the
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
.. list-table::
|
|
||||||
:class: no-border
|
|
||||||
:align: left
|
|
||||||
|
|
||||||
* - Sponsor the project on GitHub:
|
|
||||||
- .. raw:: html
|
|
||||||
:file: _static/sponsor.html
|
|
|
@ -257,7 +257,7 @@ Let's check that it works. Open another terminal session and use ``httpie``:
|
||||||
|
|
||||||
You should see:
|
You should see:
|
||||||
|
|
||||||
.. code-block:: http
|
.. code-block:: json
|
||||||
|
|
||||||
HTTP/1.1 200 OK
|
HTTP/1.1 200 OK
|
||||||
Content-Length: 844
|
Content-Length: 844
|
||||||
|
@ -596,7 +596,7 @@ and make a request to the API in the terminal:
|
||||||
|
|
||||||
You should see:
|
You should see:
|
||||||
|
|
||||||
.. code-block:: http
|
.. code-block:: json
|
||||||
|
|
||||||
HTTP/1.1 200 OK
|
HTTP/1.1 200 OK
|
||||||
Content-Length: 492
|
Content-Length: 492
|
||||||
|
@ -859,6 +859,4 @@ What's next?
|
||||||
- Know more about the :ref:`providers`
|
- Know more about the :ref:`providers`
|
||||||
- Go to the :ref:`contents`
|
- Go to the :ref:`contents`
|
||||||
|
|
||||||
.. include:: ../sponsor.rst
|
|
||||||
|
|
||||||
.. disqus::
|
.. disqus::
|
||||||
|
|
|
@ -18,7 +18,7 @@ In this tutorial we will use:
|
||||||
|
|
||||||
- Python 3
|
- Python 3
|
||||||
- Docker
|
- Docker
|
||||||
- Docker Compose
|
- Docker-compose
|
||||||
|
|
||||||
Start from the scratch or jump to the section:
|
Start from the scratch or jump to the section:
|
||||||
|
|
||||||
|
@ -47,27 +47,28 @@ response it will log:
|
||||||
Prerequisites
|
Prerequisites
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
We will use `docker compose <https://docs.docker.com/compose/>`_ in this tutorial. Let's check the versions:
|
We will use `Docker <https://www.docker.com/>`_ and
|
||||||
|
`docker-compose <https://docs.docker.com/compose/>`_ in this tutorial. Let's check the versions:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
docker --version
|
docker --version
|
||||||
docker compose version
|
docker-compose --version
|
||||||
|
|
||||||
The output should look something like:
|
The output should look something like:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
Docker version 27.3.1, build ce12230
|
Docker version 20.10.5, build 55c4c88
|
||||||
Docker Compose version v2.29.7
|
docker-compose version 1.29.0, build 07737305
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
If you don't have ``Docker`` or ``docker compose`` you need to install them before proceeding.
|
If you don't have ``Docker`` or ``docker-compose`` you need to install them before proceeding.
|
||||||
Follow these installation guides:
|
Follow these installation guides:
|
||||||
|
|
||||||
- `Install Docker <https://docs.docker.com/get-docker/>`_
|
- `Install Docker <https://docs.docker.com/get-docker/>`_
|
||||||
- `Install docker compose <https://docs.docker.com/compose/install/>`_
|
- `Install docker-compose <https://docs.docker.com/compose/install/>`_
|
||||||
|
|
||||||
The prerequisites are satisfied. Let's get started with the project layout.
|
The prerequisites are satisfied. Let's get started with the project layout.
|
||||||
|
|
||||||
|
@ -128,13 +129,13 @@ Put next lines into the ``requirements.txt`` file:
|
||||||
pytest-cov
|
pytest-cov
|
||||||
|
|
||||||
Second, we need to create the ``Dockerfile``. It will describe the daemon's build process and
|
Second, we need to create the ``Dockerfile``. It will describe the daemon's build process and
|
||||||
specify how to run it. We will use ``python:3.13-bookworm`` as a base image.
|
specify how to run it. We will use ``python:3.9-buster`` as a base image.
|
||||||
|
|
||||||
Put next lines into the ``Dockerfile`` file:
|
Put next lines into the ``Dockerfile`` file:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
FROM python:3.13-bookworm
|
FROM python:3.10-buster
|
||||||
|
|
||||||
ENV PYTHONUNBUFFERED=1
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
|
@ -154,6 +155,8 @@ Put next lines into the ``docker-compose.yml`` file:
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
version: "3.7"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
|
|
||||||
monitor:
|
monitor:
|
||||||
|
@ -168,7 +171,7 @@ Run in the terminal:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
docker compose build
|
docker-compose build
|
||||||
|
|
||||||
The build process may take a couple of minutes. You should see something like this in the end:
|
The build process may take a couple of minutes. You should see something like this in the end:
|
||||||
|
|
||||||
|
@ -181,7 +184,7 @@ After the build is done run the container:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
docker compose up
|
docker-compose up
|
||||||
|
|
||||||
The output should look like:
|
The output should look like:
|
||||||
|
|
||||||
|
@ -458,7 +461,7 @@ Run in the terminal:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
docker compose up
|
docker-compose up
|
||||||
|
|
||||||
The output should look like:
|
The output should look like:
|
||||||
|
|
||||||
|
@ -702,7 +705,7 @@ Run in the terminal:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
docker compose up
|
docker-compose up
|
||||||
|
|
||||||
You should see:
|
You should see:
|
||||||
|
|
||||||
|
@ -810,7 +813,7 @@ Run in the terminal:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
docker compose up
|
docker-compose up
|
||||||
|
|
||||||
You should see:
|
You should see:
|
||||||
|
|
||||||
|
@ -962,16 +965,15 @@ Run in the terminal:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
docker compose run --rm monitor py.test monitoringdaemon/tests.py --cov=monitoringdaemon
|
docker-compose run --rm monitor py.test monitoringdaemon/tests.py --cov=monitoringdaemon
|
||||||
|
|
||||||
You should see:
|
You should see:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
platform linux -- Python 3.13.1, pytest-8.3.4, pluggy-1.5.0
|
platform linux -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
|
||||||
rootdir: /code
|
rootdir: /code
|
||||||
plugins: cov-6.0.0, asyncio-0.24.0
|
plugins: asyncio-0.16.0, cov-3.0.0
|
||||||
asyncio: mode=Mode.STRICT, default_loop_scope=None
|
|
||||||
collected 2 items
|
collected 2 items
|
||||||
|
|
||||||
monitoringdaemon/tests.py .. [100%]
|
monitoringdaemon/tests.py .. [100%]
|
||||||
|
@ -1026,6 +1028,4 @@ What's next?
|
||||||
- Know more about the :ref:`providers`
|
- Know more about the :ref:`providers`
|
||||||
- Go to the :ref:`contents`
|
- Go to the :ref:`contents`
|
||||||
|
|
||||||
.. include:: ../sponsor.rst
|
|
||||||
|
|
||||||
.. disqus::
|
.. disqus::
|
||||||
|
|
|
@ -84,7 +84,7 @@ Create next structure in the project root directory. All files are empty. That's
|
||||||
|
|
||||||
Initial project layout:
|
Initial project layout:
|
||||||
|
|
||||||
.. code-block:: text
|
.. code-block:: bash
|
||||||
|
|
||||||
./
|
./
|
||||||
├── movies/
|
├── movies/
|
||||||
|
@ -109,7 +109,7 @@ Now it's time to install the project requirements. We will use next packages:
|
||||||
|
|
||||||
Put next lines into the ``requirements.txt`` file:
|
Put next lines into the ``requirements.txt`` file:
|
||||||
|
|
||||||
.. code-block:: text
|
.. code-block:: bash
|
||||||
|
|
||||||
dependency-injector
|
dependency-injector
|
||||||
pyyaml
|
pyyaml
|
||||||
|
@ -134,7 +134,7 @@ We will create a script that creates database files.
|
||||||
First add the folder ``data/`` in the root of the project and then add the file
|
First add the folder ``data/`` in the root of the project and then add the file
|
||||||
``fixtures.py`` inside of it:
|
``fixtures.py`` inside of it:
|
||||||
|
|
||||||
.. code-block:: text
|
.. code-block:: bash
|
||||||
:emphasize-lines: 2-3
|
:emphasize-lines: 2-3
|
||||||
|
|
||||||
./
|
./
|
||||||
|
@ -205,13 +205,13 @@ Now run in the terminal:
|
||||||
|
|
||||||
You should see:
|
You should see:
|
||||||
|
|
||||||
.. code-block:: text
|
.. code-block:: bash
|
||||||
|
|
||||||
OK
|
OK
|
||||||
|
|
||||||
Check that files ``movies.csv`` and ``movies.db`` have appeared in the ``data/`` folder:
|
Check that files ``movies.csv`` and ``movies.db`` have appeared in the ``data/`` folder:
|
||||||
|
|
||||||
.. code-block:: text
|
.. code-block:: bash
|
||||||
:emphasize-lines: 4-5
|
:emphasize-lines: 4-5
|
||||||
|
|
||||||
./
|
./
|
||||||
|
@ -289,7 +289,7 @@ After each step we will add the provider to the container.
|
||||||
|
|
||||||
Create the ``entities.py`` in the ``movies`` package:
|
Create the ``entities.py`` in the ``movies`` package:
|
||||||
|
|
||||||
.. code-block:: text
|
.. code-block:: bash
|
||||||
:emphasize-lines: 10
|
:emphasize-lines: 10
|
||||||
|
|
||||||
./
|
./
|
||||||
|
@ -356,7 +356,7 @@ Let's move on to the finders.
|
||||||
|
|
||||||
Create the ``finders.py`` in the ``movies`` package:
|
Create the ``finders.py`` in the ``movies`` package:
|
||||||
|
|
||||||
.. code-block:: text
|
.. code-block:: bash
|
||||||
:emphasize-lines: 11
|
:emphasize-lines: 11
|
||||||
|
|
||||||
./
|
./
|
||||||
|
@ -465,7 +465,7 @@ The configuration file is ready. Move on to the lister.
|
||||||
|
|
||||||
Create the ``listers.py`` in the ``movies`` package:
|
Create the ``listers.py`` in the ``movies`` package:
|
||||||
|
|
||||||
.. code-block:: text
|
.. code-block:: bash
|
||||||
:emphasize-lines: 12
|
:emphasize-lines: 12
|
||||||
|
|
||||||
./
|
./
|
||||||
|
@ -613,7 +613,7 @@ Run in the terminal:
|
||||||
|
|
||||||
You should see:
|
You should see:
|
||||||
|
|
||||||
.. code-block:: text
|
.. code-block:: plain
|
||||||
|
|
||||||
Francis Lawrence movies:
|
Francis Lawrence movies:
|
||||||
- Movie(title='The Hunger Games: Mockingjay - Part 2', year=2015, director='Francis Lawrence')
|
- Movie(title='The Hunger Games: Mockingjay - Part 2', year=2015, director='Francis Lawrence')
|
||||||
|
@ -752,7 +752,7 @@ Run in the terminal:
|
||||||
|
|
||||||
You should see:
|
You should see:
|
||||||
|
|
||||||
.. code-block:: text
|
.. code-block:: plain
|
||||||
|
|
||||||
Francis Lawrence movies:
|
Francis Lawrence movies:
|
||||||
- Movie(title='The Hunger Games: Mockingjay - Part 2', year=2015, director='Francis Lawrence')
|
- Movie(title='The Hunger Games: Mockingjay - Part 2', year=2015, director='Francis Lawrence')
|
||||||
|
@ -868,7 +868,7 @@ Run in the terminal line by line:
|
||||||
|
|
||||||
The output should be similar for each command:
|
The output should be similar for each command:
|
||||||
|
|
||||||
.. code-block:: text
|
.. code-block:: plain
|
||||||
|
|
||||||
Francis Lawrence movies:
|
Francis Lawrence movies:
|
||||||
- Movie(title='The Hunger Games: Mockingjay - Part 2', year=2015, director='Francis Lawrence')
|
- Movie(title='The Hunger Games: Mockingjay - Part 2', year=2015, director='Francis Lawrence')
|
||||||
|
@ -888,7 +888,7 @@ We will use `pytest <https://docs.pytest.org/en/stable/>`_ and
|
||||||
|
|
||||||
Create ``tests.py`` in the ``movies`` package:
|
Create ``tests.py`` in the ``movies`` package:
|
||||||
|
|
||||||
.. code-block:: text
|
.. code-block:: bash
|
||||||
:emphasize-lines: 13
|
:emphasize-lines: 13
|
||||||
|
|
||||||
./
|
./
|
||||||
|
@ -911,7 +911,7 @@ Create ``tests.py`` in the ``movies`` package:
|
||||||
and put next into it:
|
and put next into it:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
:emphasize-lines: 41,50
|
:emphasize-lines: 36,51
|
||||||
|
|
||||||
"""Tests module."""
|
"""Tests module."""
|
||||||
|
|
||||||
|
@ -941,18 +941,13 @@ and put next into it:
|
||||||
return container
|
return container
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
def test_movies_directed_by(container):
|
||||||
def finder_mock(container):
|
|
||||||
finder_mock = mock.Mock()
|
finder_mock = mock.Mock()
|
||||||
finder_mock.find_all.return_value = [
|
finder_mock.find_all.return_value = [
|
||||||
container.movie("The 33", 2015, "Patricia Riggen"),
|
container.movie("The 33", 2015, "Patricia Riggen"),
|
||||||
container.movie("The Jungle Book", 2016, "Jon Favreau"),
|
container.movie("The Jungle Book", 2016, "Jon Favreau"),
|
||||||
]
|
]
|
||||||
|
|
||||||
return finder_mock
|
|
||||||
|
|
||||||
|
|
||||||
def test_movies_directed_by(container, finder_mock):
|
|
||||||
with container.finder.override(finder_mock):
|
with container.finder.override(finder_mock):
|
||||||
lister = container.lister()
|
lister = container.lister()
|
||||||
movies = lister.movies_directed_by("Jon Favreau")
|
movies = lister.movies_directed_by("Jon Favreau")
|
||||||
|
@ -961,7 +956,13 @@ and put next into it:
|
||||||
assert movies[0].title == "The Jungle Book"
|
assert movies[0].title == "The Jungle Book"
|
||||||
|
|
||||||
|
|
||||||
def test_movies_released_in(container, finder_mock):
|
def test_movies_released_in(container):
|
||||||
|
finder_mock = mock.Mock()
|
||||||
|
finder_mock.find_all.return_value = [
|
||||||
|
container.movie("The 33", 2015, "Patricia Riggen"),
|
||||||
|
container.movie("The Jungle Book", 2016, "Jon Favreau"),
|
||||||
|
]
|
||||||
|
|
||||||
with container.finder.override(finder_mock):
|
with container.finder.override(finder_mock):
|
||||||
lister = container.lister()
|
lister = container.lister()
|
||||||
movies = lister.movies_released_in(2015)
|
movies = lister.movies_released_in(2015)
|
||||||
|
@ -977,7 +978,7 @@ Run in the terminal:
|
||||||
|
|
||||||
You should see:
|
You should see:
|
||||||
|
|
||||||
.. code-block:: text
|
.. code-block::
|
||||||
|
|
||||||
platform darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
|
platform darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
|
||||||
plugins: cov-3.0.0
|
plugins: cov-3.0.0
|
||||||
|
@ -994,9 +995,9 @@ You should see:
|
||||||
movies/entities.py 7 1 86%
|
movies/entities.py 7 1 86%
|
||||||
movies/finders.py 26 13 50%
|
movies/finders.py 26 13 50%
|
||||||
movies/listers.py 8 0 100%
|
movies/listers.py 8 0 100%
|
||||||
movies/tests.py 24 0 100%
|
movies/tests.py 23 0 100%
|
||||||
------------------------------------------
|
------------------------------------------
|
||||||
TOTAL 90 30 67%
|
TOTAL 89 30 66%
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
|
@ -1033,6 +1034,4 @@ What's next?
|
||||||
- Know more about the :ref:`providers`
|
- Know more about the :ref:`providers`
|
||||||
- Go to the :ref:`contents`
|
- Go to the :ref:`contents`
|
||||||
|
|
||||||
.. include:: ../sponsor.rst
|
|
||||||
|
|
||||||
.. disqus::
|
.. disqus::
|
||||||
|
|
|
@ -280,7 +280,7 @@ Now let's fill in the layout.
|
||||||
|
|
||||||
Put next into the ``base.html``:
|
Put next into the ``base.html``:
|
||||||
|
|
||||||
.. code-block:: jinja
|
.. code-block:: html
|
||||||
|
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
@ -313,7 +313,7 @@ And put something to the index page.
|
||||||
|
|
||||||
Put next into the ``index.html``:
|
Put next into the ``index.html``:
|
||||||
|
|
||||||
.. code-block:: jinja
|
.. code-block:: html
|
||||||
|
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
@ -998,6 +998,5 @@ What's next?
|
||||||
- Know more about the :ref:`providers`
|
- Know more about the :ref:`providers`
|
||||||
- Go to the :ref:`contents`
|
- Go to the :ref:`contents`
|
||||||
|
|
||||||
.. include:: ../sponsor.rst
|
|
||||||
|
|
||||||
.. disqus::
|
.. disqus::
|
||||||
|
|
|
@ -64,7 +64,7 @@ FastAPI example:
|
||||||
|
|
||||||
@app.api_route("/")
|
@app.api_route("/")
|
||||||
@inject
|
@inject
|
||||||
async def index(service: Annotated[Service, Depends(Provide[Container.service])]):
|
async def index(service: Service = Depends(Provide[Container.service])):
|
||||||
value = await service.process()
|
value = await service.process()
|
||||||
return {"result": value}
|
return {"result": value}
|
||||||
|
|
||||||
|
@ -127,7 +127,6 @@ To inject the provider itself use ``Provide[foo.provider]``:
|
||||||
def foo(bar_provider: Factory[Bar] = Provide[Container.bar.provider]):
|
def foo(bar_provider: Factory[Bar] = Provide[Container.bar.provider]):
|
||||||
bar = bar_provider(argument="baz")
|
bar = bar_provider(argument="baz")
|
||||||
...
|
...
|
||||||
|
|
||||||
You can also use ``Provider[foo]`` for injecting the provider itself:
|
You can also use ``Provider[foo]`` for injecting the provider itself:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
@ -255,43 +254,13 @@ To inject a container use special identifier ``<container>``:
|
||||||
Making injections into modules and class attributes
|
Making injections into modules and class attributes
|
||||||
---------------------------------------------------
|
---------------------------------------------------
|
||||||
|
|
||||||
You can use wiring to make injections into modules and class attributes. Both the classic marker
|
You can use wiring to make injections into modules and class attributes.
|
||||||
syntax and the ``Annotated`` form are supported.
|
|
||||||
|
|
||||||
Classic marker syntax:
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
service: Service = Provide[Container.service]
|
|
||||||
|
|
||||||
class Main:
|
|
||||||
service: Service = Provide[Container.service]
|
|
||||||
|
|
||||||
Full example of the classic marker syntax:
|
|
||||||
|
|
||||||
.. literalinclude:: ../examples/wiring/example_attribute.py
|
.. literalinclude:: ../examples/wiring/example_attribute.py
|
||||||
:language: python
|
:language: python
|
||||||
:lines: 3-
|
:lines: 3-
|
||||||
:emphasize-lines: 14,19
|
:emphasize-lines: 14,19
|
||||||
|
|
||||||
Annotated form (Python 3.9+):
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
from typing import Annotated
|
|
||||||
|
|
||||||
service: Annotated[Service, Provide[Container.service]]
|
|
||||||
|
|
||||||
class Main:
|
|
||||||
service: Annotated[Service, Provide[Container.service]]
|
|
||||||
|
|
||||||
Full example of the annotated form:
|
|
||||||
|
|
||||||
.. literalinclude:: ../examples/wiring/example_attribute_annotated.py
|
|
||||||
:language: python
|
|
||||||
:lines: 3-
|
|
||||||
:emphasize-lines: 16,21
|
|
||||||
|
|
||||||
You could also use string identifiers to avoid a dependency on a container:
|
You could also use string identifiers to avoid a dependency on a container:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
@ -632,36 +601,6 @@ or with a single container ``register_loader_containers(container)`` multiple ti
|
||||||
To unregister a container use ``unregister_loader_containers(container)``.
|
To unregister a container use ``unregister_loader_containers(container)``.
|
||||||
Wiring module will uninstall the import hook when unregister last container.
|
Wiring module will uninstall the import hook when unregister last container.
|
||||||
|
|
||||||
Few notes on performance
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
``.wire()`` utilize caching to speed up the wiring process. At the end it clears the cache to avoid memory leaks.
|
|
||||||
But this may not always be desirable, when you want to keep the cache for the next wiring
|
|
||||||
(e.g. due to usage of multiple containers or during unit tests).
|
|
||||||
|
|
||||||
To keep the cache after wiring, you can set flag ``keep_cache=True`` (works with ``WiringConfiguration`` too):
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
container1.wire(
|
|
||||||
modules=["yourapp.module1", "yourapp.module2"],
|
|
||||||
keep_cache=True,
|
|
||||||
)
|
|
||||||
container2.wire(
|
|
||||||
modules=["yourapp.module2", "yourapp.module3"],
|
|
||||||
keep_cache=True,
|
|
||||||
)
|
|
||||||
...
|
|
||||||
|
|
||||||
and then clear it manually when you need it:
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
from dependency_injector.wiring import clear_cache
|
|
||||||
|
|
||||||
clear_cache()
|
|
||||||
|
|
||||||
|
|
||||||
Integration with other frameworks
|
Integration with other frameworks
|
||||||
---------------------------------
|
---------------------------------
|
||||||
|
|
||||||
|
@ -693,6 +632,5 @@ Take a look at other application examples:
|
||||||
- :ref:`fastapi-example`
|
- :ref:`fastapi-example`
|
||||||
- :ref:`fastapi-redis-example`
|
- :ref:`fastapi-redis-example`
|
||||||
- :ref:`fastapi-sqlalchemy-example`
|
- :ref:`fastapi-sqlalchemy-example`
|
||||||
- :ref:`fastdepends-example`
|
|
||||||
|
|
||||||
.. disqus::
|
.. disqus::
|
||||||
|
|
|
@ -98,9 +98,8 @@ The output should be something like:
|
||||||
|
|
||||||
.. code-block::
|
.. code-block::
|
||||||
|
|
||||||
platform linux -- Python 3.12.3, pytest-8.3.2, pluggy-1.5.0
|
platform darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
|
||||||
plugins: cov-6.0.0, anyio-4.4.0, asyncio-0.24.0, aiohttp-1.0.5
|
plugins: asyncio-0.16.0, anyio-3.3.4, aiohttp-0.3.0, cov-3.0.0
|
||||||
asyncio: mode=Mode.STRICT, default_loop_scope=None
|
|
||||||
collected 3 items
|
collected 3 items
|
||||||
|
|
||||||
giphynavigator/tests.py ... [100%]
|
giphynavigator/tests.py ... [100%]
|
||||||
|
|
|
@ -3,15 +3,11 @@
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import pytest_asyncio
|
|
||||||
|
|
||||||
from giphynavigator.application import create_app
|
from giphynavigator.application import create_app
|
||||||
from giphynavigator.giphy import GiphyClient
|
from giphynavigator.giphy import GiphyClient
|
||||||
|
|
||||||
|
|
||||||
pytestmark = pytest.mark.asyncio
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def app():
|
def app():
|
||||||
app = create_app()
|
app = create_app()
|
||||||
|
@ -19,9 +15,9 @@ def app():
|
||||||
app.container.unwire()
|
app.container.unwire()
|
||||||
|
|
||||||
|
|
||||||
@pytest_asyncio.fixture
|
@pytest.fixture
|
||||||
async def client(app, aiohttp_client):
|
def client(app, aiohttp_client, loop):
|
||||||
return await aiohttp_client(app)
|
return loop.run_until_complete(aiohttp_client(app))
|
||||||
|
|
||||||
|
|
||||||
async def test_index(client, app):
|
async def test_index(client, app):
|
||||||
|
|
|
@ -2,5 +2,4 @@ dependency-injector
|
||||||
aiohttp
|
aiohttp
|
||||||
pyyaml
|
pyyaml
|
||||||
pytest-aiohttp
|
pytest-aiohttp
|
||||||
pytest-asyncio
|
|
||||||
pytest-cov
|
pytest-cov
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM python:3.13-bookworm
|
FROM python:3.10-buster
|
||||||
|
|
||||||
ENV PYTHONUNBUFFERED=1
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
|
|
|
@ -13,13 +13,13 @@ Build the Docker image:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
docker compose build
|
docker-compose build
|
||||||
|
|
||||||
Run the docker-compose environment:
|
Run the docker-compose environment:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
docker compose up
|
docker-compose up
|
||||||
|
|
||||||
The output should be something like:
|
The output should be something like:
|
||||||
|
|
||||||
|
@ -59,16 +59,15 @@ To run the tests do:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
docker compose run --rm monitor py.test monitoringdaemon/tests.py --cov=monitoringdaemon
|
docker-compose run --rm monitor py.test monitoringdaemon/tests.py --cov=monitoringdaemon
|
||||||
|
|
||||||
The output should be something like:
|
The output should be something like:
|
||||||
|
|
||||||
.. code-block::
|
.. code-block::
|
||||||
|
|
||||||
platform linux -- Python 3.13.1, pytest-8.3.4, pluggy-1.5.0
|
platform linux -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
|
||||||
rootdir: /code
|
rootdir: /code
|
||||||
plugins: cov-6.0.0, asyncio-0.24.0
|
plugins: asyncio-0.16.0, cov-3.0.0
|
||||||
asyncio: mode=Mode.STRICT, default_loop_scope=None
|
|
||||||
collected 2 items
|
collected 2 items
|
||||||
|
|
||||||
monitoringdaemon/tests.py .. [100%]
|
monitoringdaemon/tests.py .. [100%]
|
||||||
|
|
|
@ -61,7 +61,7 @@ async def test_example_monitor(container, caplog):
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_dispatcher(container, caplog):
|
async def test_dispatcher(container, caplog, event_loop):
|
||||||
caplog.set_level("INFO")
|
caplog.set_level("INFO")
|
||||||
|
|
||||||
example_monitor_mock = mock.AsyncMock()
|
example_monitor_mock = mock.AsyncMock()
|
||||||
|
@ -72,7 +72,6 @@ async def test_dispatcher(container, caplog):
|
||||||
httpbin_monitor=httpbin_monitor_mock,
|
httpbin_monitor=httpbin_monitor_mock,
|
||||||
):
|
):
|
||||||
dispatcher = container.dispatcher()
|
dispatcher = container.dispatcher()
|
||||||
event_loop = asyncio.get_running_loop()
|
|
||||||
event_loop.create_task(dispatcher.start())
|
event_loop.create_task(dispatcher.start())
|
||||||
await asyncio.sleep(0.1)
|
await asyncio.sleep(0.1)
|
||||||
dispatcher.stop()
|
dispatcher.stop()
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM python:3.13-bookworm
|
FROM python:3.10-buster
|
||||||
|
|
||||||
ENV PYTHONUNBUFFERED=1
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
|
|
|
@ -12,13 +12,13 @@ Build the Docker image:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
docker compose build
|
docker-compose build
|
||||||
|
|
||||||
Run the docker-compose environment:
|
Run the docker-compose environment:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
docker compose up
|
docker-compose up
|
||||||
|
|
||||||
The output should be something like:
|
The output should be something like:
|
||||||
|
|
||||||
|
@ -54,16 +54,16 @@ To run the tests do:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
docker compose run --rm example py.test fastapiredis/tests.py --cov=fastapiredis
|
docker-compose run --rm example py.test fastapiredis/tests.py --cov=fastapiredis
|
||||||
|
|
||||||
The output should be something like:
|
The output should be something like:
|
||||||
|
|
||||||
.. code-block::
|
.. code-block::
|
||||||
|
|
||||||
platform linux -- Python 3.13.1, pytest-8.3.4, pluggy-1.5.0
|
platform linux -- Python 3.10.9, pytest-7.2.0, pluggy-1.0.0
|
||||||
rootdir: /code
|
rootdir: /code
|
||||||
plugins: cov-6.0.0, asyncio-0.24.0, anyio-4.7.0
|
plugins: cov-4.0.0, asyncio-0.20.3
|
||||||
asyncio: mode=Mode.STRICT, default_loop_scope=None
|
collected 1 item
|
||||||
|
|
||||||
fastapiredis/tests.py . [100%]
|
fastapiredis/tests.py . [100%]
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,18 @@
|
||||||
"""Application module."""
|
"""Application module."""
|
||||||
|
|
||||||
from typing import Annotated
|
from dependency_injector.wiring import inject, Provide
|
||||||
|
from fastapi import FastAPI, Depends
|
||||||
from fastapi import Depends, FastAPI
|
|
||||||
|
|
||||||
from dependency_injector.wiring import Provide, inject
|
|
||||||
|
|
||||||
from .containers import Container
|
from .containers import Container
|
||||||
from .services import Service
|
from .services import Service
|
||||||
|
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
@app.api_route("/")
|
@app.api_route("/")
|
||||||
@inject
|
@inject
|
||||||
async def index(
|
async def index(service: Service = Depends(Provide[Container.service])):
|
||||||
service: Annotated[Service, Depends(Provide[Container.service])]
|
|
||||||
) -> dict[str, str]:
|
|
||||||
value = await service.process()
|
value = await service.process()
|
||||||
return {"result": value}
|
return {"result": value}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from typing import AsyncIterator
|
from typing import AsyncIterator
|
||||||
|
|
||||||
from redis.asyncio import from_url, Redis
|
from aioredis import from_url, Redis
|
||||||
|
|
||||||
|
|
||||||
async def init_redis_pool(host: str, password: str) -> AsyncIterator[Redis]:
|
async def init_redis_pool(host: str, password: str) -> AsyncIterator[Redis]:
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
"""Services module."""
|
"""Services module."""
|
||||||
|
|
||||||
from redis.asyncio import Redis
|
from aioredis import Redis
|
||||||
|
|
||||||
|
|
||||||
class Service:
|
class Service:
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from httpx import ASGITransport, AsyncClient
|
from httpx import AsyncClient
|
||||||
|
|
||||||
from .application import app, container
|
from .application import app, container
|
||||||
from .services import Service
|
from .services import Service
|
||||||
|
@ -11,10 +11,7 @@ from .services import Service
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def client(event_loop):
|
def client(event_loop):
|
||||||
client = AsyncClient(
|
client = AsyncClient(app=app, base_url="http://test")
|
||||||
transport=ASGITransport(app=app),
|
|
||||||
base_url="http://test",
|
|
||||||
)
|
|
||||||
yield client
|
yield client
|
||||||
event_loop.run_until_complete(client.aclose())
|
event_loop.run_until_complete(client.aclose())
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
dependency-injector
|
dependency-injector
|
||||||
fastapi
|
fastapi
|
||||||
uvicorn
|
uvicorn
|
||||||
redis>=4.2
|
aioredis
|
||||||
|
|
||||||
# For testing:
|
# For testing:
|
||||||
pytest
|
pytest
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
from typing import Annotated
|
from fastapi import FastAPI, Depends
|
||||||
|
|
||||||
from fastapi import Depends, FastAPI
|
|
||||||
|
|
||||||
from dependency_injector import containers, providers
|
from dependency_injector import containers, providers
|
||||||
from dependency_injector.wiring import Provide, inject
|
from dependency_injector.wiring import Provide, inject
|
||||||
|
|
||||||
|
@ -21,9 +18,7 @@ app = FastAPI()
|
||||||
|
|
||||||
@app.api_route("/")
|
@app.api_route("/")
|
||||||
@inject
|
@inject
|
||||||
async def index(
|
async def index(service: Service = Depends(Provide[Container.service])):
|
||||||
service: Annotated[Service, Depends(Provide[Container.service])]
|
|
||||||
) -> dict[str, str]:
|
|
||||||
result = await service.process()
|
result = await service.process()
|
||||||
return {"result": result}
|
return {"result": result}
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,14 @@
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import pytest_asyncio
|
from httpx import AsyncClient
|
||||||
from httpx import ASGITransport, AsyncClient
|
|
||||||
|
|
||||||
from fastapi_di_example import app, container, Service
|
from fastapi_di_example import app, container, Service
|
||||||
|
|
||||||
|
|
||||||
@pytest_asyncio.fixture
|
@pytest.fixture
|
||||||
async def client():
|
async def client(event_loop):
|
||||||
async with AsyncClient(
|
async with AsyncClient(app=app, base_url="http://test") as client:
|
||||||
transport=ASGITransport(app=app),
|
|
||||||
base_url="http://test",
|
|
||||||
) as client:
|
|
||||||
yield client
|
yield client
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM python:3.13-bookworm
|
FROM python:3.10-buster
|
||||||
|
|
||||||
ENV PYTHONUNBUFFERED=1
|
ENV PYTHONUNBUFFERED=1
|
||||||
ENV HOST=0.0.0.0
|
ENV HOST=0.0.0.0
|
||||||
|
|
|
@ -15,13 +15,13 @@ Build the Docker image:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
docker compose build
|
docker-compose build
|
||||||
|
|
||||||
Run the docker-compose environment:
|
Run the docker-compose environment:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
docker compose up
|
docker-compose up
|
||||||
|
|
||||||
The output should be something like:
|
The output should be something like:
|
||||||
|
|
||||||
|
@ -67,15 +67,15 @@ To run the tests do:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
docker compose run --rm webapp py.test webapp/tests.py --cov=webapp
|
docker-compose run --rm webapp py.test webapp/tests.py --cov=webapp
|
||||||
|
|
||||||
The output should be something like:
|
The output should be something like:
|
||||||
|
|
||||||
.. code-block::
|
.. code-block::
|
||||||
|
|
||||||
platform linux -- Python 3.13.1, pytest-8.3.4, pluggy-1.5.0
|
platform linux -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
|
||||||
rootdir: /code
|
rootdir: /code
|
||||||
plugins: cov-6.0.0, anyio-4.7.0
|
plugins: cov-3.0.0
|
||||||
collected 7 items
|
collected 7 items
|
||||||
|
|
||||||
webapp/tests.py ....... [100%]
|
webapp/tests.py ....... [100%]
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
dependency-injector
|
dependency-injector
|
||||||
fastapi[standard]
|
fastapi
|
||||||
uvicorn
|
uvicorn
|
||||||
pyyaml
|
pyyaml
|
||||||
sqlalchemy
|
sqlalchemy
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
"""Endpoints module."""
|
"""Endpoints module."""
|
||||||
|
|
||||||
from typing import Annotated
|
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, Response, status
|
from fastapi import APIRouter, Depends, Response, status
|
||||||
|
from dependency_injector.wiring import inject, Provide
|
||||||
from dependency_injector.wiring import Provide, inject
|
|
||||||
|
|
||||||
from .containers import Container
|
from .containers import Container
|
||||||
from .repositories import NotFoundError
|
|
||||||
from .services import UserService
|
from .services import UserService
|
||||||
|
from .repositories import NotFoundError
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
@ -16,7 +13,7 @@ router = APIRouter()
|
||||||
@router.get("/users")
|
@router.get("/users")
|
||||||
@inject
|
@inject
|
||||||
def get_list(
|
def get_list(
|
||||||
user_service: Annotated[UserService, Depends(Provide[Container.user_service])],
|
user_service: UserService = Depends(Provide[Container.user_service]),
|
||||||
):
|
):
|
||||||
return user_service.get_users()
|
return user_service.get_users()
|
||||||
|
|
||||||
|
@ -24,8 +21,8 @@ def get_list(
|
||||||
@router.get("/users/{user_id}")
|
@router.get("/users/{user_id}")
|
||||||
@inject
|
@inject
|
||||||
def get_by_id(
|
def get_by_id(
|
||||||
user_id: int,
|
user_id: int,
|
||||||
user_service: Annotated[UserService, Depends(Provide[Container.user_service])],
|
user_service: UserService = Depends(Provide[Container.user_service]),
|
||||||
):
|
):
|
||||||
try:
|
try:
|
||||||
return user_service.get_user_by_id(user_id)
|
return user_service.get_user_by_id(user_id)
|
||||||
|
@ -36,7 +33,7 @@ def get_by_id(
|
||||||
@router.post("/users", status_code=status.HTTP_201_CREATED)
|
@router.post("/users", status_code=status.HTTP_201_CREATED)
|
||||||
@inject
|
@inject
|
||||||
def add(
|
def add(
|
||||||
user_service: Annotated[UserService, Depends(Provide[Container.user_service])],
|
user_service: UserService = Depends(Provide[Container.user_service]),
|
||||||
):
|
):
|
||||||
return user_service.create_user()
|
return user_service.create_user()
|
||||||
|
|
||||||
|
@ -44,9 +41,9 @@ def add(
|
||||||
@router.delete("/users/{user_id}", status_code=status.HTTP_204_NO_CONTENT)
|
@router.delete("/users/{user_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||||
@inject
|
@inject
|
||||||
def remove(
|
def remove(
|
||||||
user_id: int,
|
user_id: int,
|
||||||
user_service: Annotated[UserService, Depends(Provide[Container.user_service])],
|
user_service: UserService = Depends(Provide[Container.user_service]),
|
||||||
) -> Response:
|
):
|
||||||
try:
|
try:
|
||||||
user_service.delete_user_by_id(user_id)
|
user_service.delete_user_by_id(user_id)
|
||||||
except NotFoundError:
|
except NotFoundError:
|
||||||
|
|
|
@ -101,9 +101,9 @@ The output should be something like:
|
||||||
|
|
||||||
.. code-block::
|
.. code-block::
|
||||||
|
|
||||||
platform linux -- Python 3.12.3, pytest-8.3.2, pluggy-1.5.0
|
platform darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
|
||||||
plugins: cov-6.0.0, anyio-4.4.0, asyncio-0.24.0, aiohttp-1.0.5
|
plugins: asyncio-0.16.0, cov-3.0.0
|
||||||
asyncio: mode=Mode.STRICT, default_loop_scope=None
|
collected 3 items
|
||||||
|
|
||||||
giphynavigator/tests.py ... [100%]
|
giphynavigator/tests.py ... [100%]
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
"""Endpoints module."""
|
"""Endpoints module."""
|
||||||
|
|
||||||
from typing import Annotated, List
|
from typing import Optional, List
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends
|
from fastapi import APIRouter, Depends
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
from dependency_injector.wiring import inject, Provide
|
||||||
|
|
||||||
from dependency_injector.wiring import Provide, inject
|
|
||||||
|
|
||||||
from .containers import Container
|
|
||||||
from .services import SearchService
|
from .services import SearchService
|
||||||
|
from .containers import Container
|
||||||
|
|
||||||
|
|
||||||
class Gif(BaseModel):
|
class Gif(BaseModel):
|
||||||
|
@ -27,15 +26,11 @@ router = APIRouter()
|
||||||
@router.get("/", response_model=Response)
|
@router.get("/", response_model=Response)
|
||||||
@inject
|
@inject
|
||||||
async def index(
|
async def index(
|
||||||
default_query: Annotated[str, Depends(Provide[Container.config.default.query])],
|
query: Optional[str] = None,
|
||||||
default_limit: Annotated[
|
limit: Optional[str] = None,
|
||||||
int, Depends(Provide[Container.config.default.limit.as_int()])
|
default_query: str = Depends(Provide[Container.config.default.query]),
|
||||||
],
|
default_limit: int = Depends(Provide[Container.config.default.limit.as_int()]),
|
||||||
search_service: Annotated[
|
search_service: SearchService = Depends(Provide[Container.search_service]),
|
||||||
SearchService, Depends(Provide[Container.search_service])
|
|
||||||
],
|
|
||||||
query: str | None = None,
|
|
||||||
limit: int | None = None,
|
|
||||||
):
|
):
|
||||||
query = query or default_query
|
query = query or default_query
|
||||||
limit = limit or default_limit
|
limit = limit or default_limit
|
||||||
|
|
|
@ -3,19 +3,15 @@
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import pytest_asyncio
|
from httpx import AsyncClient
|
||||||
from httpx import ASGITransport, AsyncClient
|
|
||||||
|
|
||||||
from giphynavigator.application import app
|
from giphynavigator.application import app
|
||||||
from giphynavigator.giphy import GiphyClient
|
from giphynavigator.giphy import GiphyClient
|
||||||
|
|
||||||
|
|
||||||
@pytest_asyncio.fixture
|
@pytest.fixture
|
||||||
async def client():
|
async def client():
|
||||||
async with AsyncClient(
|
async with AsyncClient(app=app, base_url="http://test") as client:
|
||||||
transport=ASGITransport(app=app),
|
|
||||||
base_url="http://test",
|
|
||||||
) as client:
|
|
||||||
yield client
|
yield client
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -81,9 +81,8 @@ The output should be something like:
|
||||||
|
|
||||||
.. code-block::
|
.. code-block::
|
||||||
|
|
||||||
platform linux -- Python 3.12.3, pytest-8.3.2, pluggy-1.5.0
|
platform darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
|
||||||
plugins: cov-6.0.0, flask-1.3.0
|
plugins: cov-3.0.0, flask-1.2.0
|
||||||
asyncio: mode=Mode.STRICT, default_loop_scope=None
|
|
||||||
collected 2 items
|
collected 2 items
|
||||||
|
|
||||||
githubnavigator/tests.py .. [100%]
|
githubnavigator/tests.py .. [100%]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
"""Application module."""
|
"""Application module."""
|
||||||
|
|
||||||
from flask import Flask
|
from flask import Flask
|
||||||
from flask_bootstrap import Bootstrap4
|
from flask_bootstrap import Bootstrap
|
||||||
|
|
||||||
from .containers import Container
|
from .containers import Container
|
||||||
from .blueprints import example
|
from .blueprints import example
|
||||||
|
@ -15,7 +15,7 @@ def create_app() -> Flask:
|
||||||
app.container = container
|
app.container = container
|
||||||
app.register_blueprint(example.blueprint)
|
app.register_blueprint(example.blueprint)
|
||||||
|
|
||||||
bootstrap = Bootstrap4()
|
bootstrap = Bootstrap()
|
||||||
bootstrap.init_app(app)
|
bootstrap.init_app(app)
|
||||||
|
|
||||||
return app
|
return app
|
||||||
|
|
|
@ -81,9 +81,8 @@ The output should be something like:
|
||||||
|
|
||||||
.. code-block::
|
.. code-block::
|
||||||
|
|
||||||
platform linux -- Python 3.12.3, pytest-8.3.2, pluggy-1.5.0
|
platform darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
|
||||||
plugins: cov-6.0.0, flask-1.3.0
|
plugins: cov-3.0.0, flask-1.2.0
|
||||||
asyncio: mode=Mode.STRICT, default_loop_scope=None
|
|
||||||
collected 2 items
|
collected 2 items
|
||||||
|
|
||||||
githubnavigator/tests.py .. [100%]
|
githubnavigator/tests.py .. [100%]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
"""Application module."""
|
"""Application module."""
|
||||||
|
|
||||||
from flask import Flask
|
from flask import Flask
|
||||||
from flask_bootstrap import Bootstrap4
|
from flask_bootstrap import Bootstrap
|
||||||
|
|
||||||
from .containers import Container
|
from .containers import Container
|
||||||
from . import views
|
from . import views
|
||||||
|
@ -15,7 +15,7 @@ def create_app() -> Flask:
|
||||||
app.container = container
|
app.container = container
|
||||||
app.add_url_rule("/", "index", views.index)
|
app.add_url_rule("/", "index", views.index)
|
||||||
|
|
||||||
bootstrap = Bootstrap4()
|
bootstrap = Bootstrap()
|
||||||
bootstrap.init_app(app)
|
bootstrap.init_app(app)
|
||||||
|
|
||||||
return app
|
return app
|
||||||
|
|
|
@ -58,8 +58,8 @@ The output should be something like:
|
||||||
|
|
||||||
.. code-block::
|
.. code-block::
|
||||||
|
|
||||||
platform linux -- Python 3.12.3, pytest-8.3.2, pluggy-1.5.0
|
platform darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
|
||||||
plugins: cov-6.0.0
|
plugins: cov-3.0.0
|
||||||
collected 2 items
|
collected 2 items
|
||||||
|
|
||||||
movies/tests.py .. [100%]
|
movies/tests.py .. [100%]
|
||||||
|
|
|
@ -18,9 +18,10 @@ SQLITE_FILE = DIR / "movies.db"
|
||||||
|
|
||||||
|
|
||||||
def create_csv(movies_data, path):
|
def create_csv(movies_data, path):
|
||||||
with open(path, "w", newline="") as opened_file:
|
with open(path, "w") as opened_file:
|
||||||
writer = csv.writer(opened_file)
|
writer = csv.writer(opened_file)
|
||||||
writer.writerows(movies_data)
|
for row in movies_data:
|
||||||
|
writer.writerow(row)
|
||||||
|
|
||||||
|
|
||||||
def create_sqlite(movies_data, path):
|
def create_sqlite(movies_data, path):
|
||||||
|
|
|
@ -29,7 +29,7 @@ class CsvMovieFinder(MovieFinder):
|
||||||
super().__init__(movie_factory)
|
super().__init__(movie_factory)
|
||||||
|
|
||||||
def find_all(self) -> List[Movie]:
|
def find_all(self) -> List[Movie]:
|
||||||
with open(self._csv_file_path, newline="") as csv_file:
|
with open(self._csv_file_path) as csv_file:
|
||||||
csv_reader = csv.reader(csv_file, delimiter=self._delimiter)
|
csv_reader = csv.reader(csv_file, delimiter=self._delimiter)
|
||||||
return [self._movie_factory(*row) for row in csv_reader]
|
return [self._movie_factory(*row) for row in csv_reader]
|
||||||
|
|
||||||
|
|
|
@ -26,18 +26,13 @@ def container():
|
||||||
return container
|
return container
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
def test_movies_directed_by(container):
|
||||||
def finder_mock(container):
|
|
||||||
finder_mock = mock.Mock()
|
finder_mock = mock.Mock()
|
||||||
finder_mock.find_all.return_value = [
|
finder_mock.find_all.return_value = [
|
||||||
container.movie("The 33", 2015, "Patricia Riggen"),
|
container.movie("The 33", 2015, "Patricia Riggen"),
|
||||||
container.movie("The Jungle Book", 2016, "Jon Favreau"),
|
container.movie("The Jungle Book", 2016, "Jon Favreau"),
|
||||||
]
|
]
|
||||||
|
|
||||||
return finder_mock
|
|
||||||
|
|
||||||
|
|
||||||
def test_movies_directed_by(container, finder_mock):
|
|
||||||
with container.finder.override(finder_mock):
|
with container.finder.override(finder_mock):
|
||||||
lister = container.lister()
|
lister = container.lister()
|
||||||
movies = lister.movies_directed_by("Jon Favreau")
|
movies = lister.movies_directed_by("Jon Favreau")
|
||||||
|
@ -46,7 +41,13 @@ def test_movies_directed_by(container, finder_mock):
|
||||||
assert movies[0].title == "The Jungle Book"
|
assert movies[0].title == "The Jungle Book"
|
||||||
|
|
||||||
|
|
||||||
def test_movies_released_in(container, finder_mock):
|
def test_movies_released_in(container):
|
||||||
|
finder_mock = mock.Mock()
|
||||||
|
finder_mock.find_all.return_value = [
|
||||||
|
container.movie("The 33", 2015, "Patricia Riggen"),
|
||||||
|
container.movie("The Jungle Book", 2016, "Jon Favreau"),
|
||||||
|
]
|
||||||
|
|
||||||
with container.finder.override(finder_mock):
|
with container.finder.override(finder_mock):
|
||||||
lister = container.lister()
|
lister = container.lister()
|
||||||
movies = lister.movies_released_in(2015)
|
movies = lister.movies_released_in(2015)
|
||||||
|
|
|
@ -27,7 +27,7 @@ To run the application do:
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
export GIPHY_API_KEY=wBJ2wZG7SRqfrU9nPgPiWvORmloDyuL0
|
export GIPHY_API_KEY=wBJ2wZG7SRqfrU9nPgPiWvORmloDyuL0
|
||||||
sanic giphynavigator.application:create_app
|
python -m giphynavigator
|
||||||
|
|
||||||
The output should be something like:
|
The output should be something like:
|
||||||
|
|
||||||
|
@ -98,9 +98,8 @@ The output should be something like:
|
||||||
|
|
||||||
.. code-block::
|
.. code-block::
|
||||||
|
|
||||||
platform linux -- Python 3.12.3, pytest-8.3.2, pluggy-1.5.0
|
platform darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
|
||||||
plugins: cov-6.0.0, anyio-4.4.0, asyncio-0.24.0
|
plugins: sanic-1.9.1, anyio-3.3.4, cov-3.0.0
|
||||||
asyncio: mode=Mode.STRICT, default_loop_scope=None
|
|
||||||
collected 3 items
|
collected 3 items
|
||||||
|
|
||||||
giphynavigator/tests.py ... [100%]
|
giphynavigator/tests.py ... [100%]
|
||||||
|
|
|
@ -8,8 +8,6 @@ from sanic import Sanic
|
||||||
from giphynavigator.application import create_app
|
from giphynavigator.application import create_app
|
||||||
from giphynavigator.giphy import GiphyClient
|
from giphynavigator.giphy import GiphyClient
|
||||||
|
|
||||||
pytestmark = pytest.mark.asyncio
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def app():
|
def app():
|
||||||
|
@ -19,7 +17,12 @@ def app():
|
||||||
app.ctx.container.unwire()
|
app.ctx.container.unwire()
|
||||||
|
|
||||||
|
|
||||||
async def test_index(app):
|
@pytest.fixture
|
||||||
|
def test_client(loop, app, sanic_client):
|
||||||
|
return loop.run_until_complete(sanic_client(app))
|
||||||
|
|
||||||
|
|
||||||
|
async def test_index(app, test_client):
|
||||||
giphy_client_mock = mock.AsyncMock(spec=GiphyClient)
|
giphy_client_mock = mock.AsyncMock(spec=GiphyClient)
|
||||||
giphy_client_mock.search.return_value = {
|
giphy_client_mock.search.return_value = {
|
||||||
"data": [
|
"data": [
|
||||||
|
@ -29,7 +32,7 @@ async def test_index(app):
|
||||||
}
|
}
|
||||||
|
|
||||||
with app.ctx.container.giphy_client.override(giphy_client_mock):
|
with app.ctx.container.giphy_client.override(giphy_client_mock):
|
||||||
_, response = await app.asgi_client.get(
|
response = await test_client.get(
|
||||||
"/",
|
"/",
|
||||||
params={
|
params={
|
||||||
"query": "test",
|
"query": "test",
|
||||||
|
@ -38,7 +41,7 @@ async def test_index(app):
|
||||||
)
|
)
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
data = response.json
|
data = response.json()
|
||||||
assert data == {
|
assert data == {
|
||||||
"query": "test",
|
"query": "test",
|
||||||
"limit": 10,
|
"limit": 10,
|
||||||
|
@ -49,30 +52,30 @@ async def test_index(app):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async def test_index_no_data(app):
|
async def test_index_no_data(app, test_client):
|
||||||
giphy_client_mock = mock.AsyncMock(spec=GiphyClient)
|
giphy_client_mock = mock.AsyncMock(spec=GiphyClient)
|
||||||
giphy_client_mock.search.return_value = {
|
giphy_client_mock.search.return_value = {
|
||||||
"data": [],
|
"data": [],
|
||||||
}
|
}
|
||||||
|
|
||||||
with app.ctx.container.giphy_client.override(giphy_client_mock):
|
with app.ctx.container.giphy_client.override(giphy_client_mock):
|
||||||
_, response = await app.asgi_client.get("/")
|
response = await test_client.get("/")
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
data = response.json
|
data = response.json()
|
||||||
assert data["gifs"] == []
|
assert data["gifs"] == []
|
||||||
|
|
||||||
|
|
||||||
async def test_index_default_params(app):
|
async def test_index_default_params(app, test_client):
|
||||||
giphy_client_mock = mock.AsyncMock(spec=GiphyClient)
|
giphy_client_mock = mock.AsyncMock(spec=GiphyClient)
|
||||||
giphy_client_mock.search.return_value = {
|
giphy_client_mock.search.return_value = {
|
||||||
"data": [],
|
"data": [],
|
||||||
}
|
}
|
||||||
|
|
||||||
with app.ctx.container.giphy_client.override(giphy_client_mock):
|
with app.ctx.container.giphy_client.override(giphy_client_mock):
|
||||||
_, response = await app.asgi_client.get("/")
|
response = await test_client.get("/")
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
data = response.json
|
data = response.json()
|
||||||
assert data["query"] == app.ctx.container.config.default.query()
|
assert data["query"] == app.ctx.container.config.default.query()
|
||||||
assert data["limit"] == app.ctx.container.config.default.limit()
|
assert data["limit"] == app.ctx.container.config.default.limit()
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
dependency-injector
|
dependency-injector
|
||||||
sanic
|
sanic<=21.6
|
||||||
sanic-testing
|
|
||||||
aiohttp
|
aiohttp
|
||||||
pyyaml
|
pyyaml
|
||||||
|
pytest-sanic
|
||||||
pytest-cov
|
pytest-cov
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
Integration With Starlette-based Frameworks
|
|
||||||
===========================================
|
|
||||||
|
|
||||||
This is a `Starlette <https://www.starlette.io/>`_ +
|
|
||||||
`Dependency Injector <https://python-dependency-injector.ets-labs.org/>`_ example application
|
|
||||||
utilizing `lifespan API <https://www.starlette.io/lifespan/>`_.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
Pretty much `any framework built on top of Starlette <https://www.starlette.io/third-party-packages/#frameworks>`_
|
|
||||||
supports this feature (`FastAPI <https://fastapi.tiangolo.com/advanced/events/#lifespan>`_,
|
|
||||||
`Xpresso <https://xpresso-api.dev/latest/tutorial/lifespan/>`_, etc...).
|
|
||||||
|
|
||||||
Run
|
|
||||||
---
|
|
||||||
|
|
||||||
Create virtual environment:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
python -m venv env
|
|
||||||
. env/bin/activate
|
|
||||||
|
|
||||||
Install requirements:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
pip install -r requirements.txt
|
|
||||||
|
|
||||||
To run the application do:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
python example.py
|
|
||||||
# or (logging won't be configured):
|
|
||||||
uvicorn --factory example:container.app
|
|
||||||
|
|
||||||
After that visit http://127.0.0.1:8000/ in your browser or use CLI command (``curl``, ``httpie``,
|
|
||||||
etc).
|
|
|
@ -1,59 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
from logging import basicConfig, getLogger
|
|
||||||
|
|
||||||
from dependency_injector.containers import DeclarativeContainer
|
|
||||||
from dependency_injector.ext.starlette import Lifespan
|
|
||||||
from dependency_injector.providers import Factory, Resource, Self, Singleton
|
|
||||||
from starlette.applications import Starlette
|
|
||||||
from starlette.requests import Request
|
|
||||||
from starlette.responses import JSONResponse
|
|
||||||
from starlette.routing import Route
|
|
||||||
|
|
||||||
count = 0
|
|
||||||
|
|
||||||
|
|
||||||
def init():
|
|
||||||
log = getLogger(__name__)
|
|
||||||
log.info("Inittializing resources")
|
|
||||||
yield
|
|
||||||
log.info("Cleaning up resources")
|
|
||||||
|
|
||||||
|
|
||||||
async def homepage(request: Request) -> JSONResponse:
|
|
||||||
global count
|
|
||||||
response = JSONResponse({"hello": "world", "count": count})
|
|
||||||
count += 1
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
class Container(DeclarativeContainer):
|
|
||||||
__self__ = Self()
|
|
||||||
lifespan = Singleton(Lifespan, __self__)
|
|
||||||
logging = Resource(
|
|
||||||
basicConfig,
|
|
||||||
level="DEBUG",
|
|
||||||
datefmt="%Y-%m-%d %H:%M",
|
|
||||||
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
|
|
||||||
)
|
|
||||||
init = Resource(init)
|
|
||||||
app = Factory(
|
|
||||||
Starlette,
|
|
||||||
debug=True,
|
|
||||||
lifespan=lifespan,
|
|
||||||
routes=[Route("/", homepage)],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
container = Container()
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
import uvicorn
|
|
||||||
|
|
||||||
uvicorn.run(
|
|
||||||
container.app,
|
|
||||||
factory=True,
|
|
||||||
# NOTE: `None` prevents uvicorn from configuring logging, which is
|
|
||||||
# impossible via CLI
|
|
||||||
log_config=None,
|
|
||||||
)
|
|
|
@ -1,3 +0,0 @@
|
||||||
dependency-injector
|
|
||||||
starlette
|
|
||||||
uvicorn
|
|
|
@ -3,7 +3,7 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from dependency_injector import containers, providers
|
from dependency_injector import containers, providers
|
||||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
from pydantic import BaseSettings, Field
|
||||||
|
|
||||||
# Emulate environment variables
|
# Emulate environment variables
|
||||||
os.environ["AWS_ACCESS_KEY_ID"] = "KEY"
|
os.environ["AWS_ACCESS_KEY_ID"] = "KEY"
|
||||||
|
@ -11,16 +11,15 @@ os.environ["AWS_SECRET_ACCESS_KEY"] = "SECRET"
|
||||||
|
|
||||||
|
|
||||||
class AwsSettings(BaseSettings):
|
class AwsSettings(BaseSettings):
|
||||||
model_config = SettingsConfigDict(env_prefix="aws_")
|
|
||||||
|
|
||||||
access_key_id: str
|
access_key_id: str = Field(env="aws_access_key_id")
|
||||||
secret_access_key: str
|
secret_access_key: str = Field(env="aws_secret_access_key")
|
||||||
|
|
||||||
|
|
||||||
class Settings(BaseSettings):
|
class Settings(BaseSettings):
|
||||||
|
|
||||||
aws: AwsSettings = AwsSettings()
|
aws: AwsSettings = AwsSettings()
|
||||||
optional: str = "default_value"
|
optional: str = Field(default="default_value")
|
||||||
|
|
||||||
|
|
||||||
class Container(containers.DeclarativeContainer):
|
class Container(containers.DeclarativeContainer):
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from dependency_injector import containers, providers
|
from dependency_injector import containers, providers
|
||||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
from pydantic import BaseSettings, Field
|
||||||
|
|
||||||
# Emulate environment variables
|
# Emulate environment variables
|
||||||
os.environ["AWS_ACCESS_KEY_ID"] = "KEY"
|
os.environ["AWS_ACCESS_KEY_ID"] = "KEY"
|
||||||
|
@ -11,16 +11,15 @@ os.environ["AWS_SECRET_ACCESS_KEY"] = "SECRET"
|
||||||
|
|
||||||
|
|
||||||
class AwsSettings(BaseSettings):
|
class AwsSettings(BaseSettings):
|
||||||
model_config = SettingsConfigDict(env_prefix="aws_")
|
|
||||||
|
|
||||||
access_key_id: str
|
access_key_id: str = Field(env="aws_access_key_id")
|
||||||
secret_access_key: str
|
secret_access_key: str = Field(env="aws_secret_access_key")
|
||||||
|
|
||||||
|
|
||||||
class Settings(BaseSettings):
|
class Settings(BaseSettings):
|
||||||
|
|
||||||
aws: AwsSettings = AwsSettings()
|
aws: AwsSettings = AwsSettings()
|
||||||
optional: str = "default_value"
|
optional: str = Field(default="default_value")
|
||||||
|
|
||||||
|
|
||||||
class Container(containers.DeclarativeContainer):
|
class Container(containers.DeclarativeContainer):
|
||||||
|
|
|
@ -3,12 +3,10 @@
|
||||||
import sys
|
import sys
|
||||||
import logging
|
import logging
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
from contextlib import contextmanager
|
|
||||||
|
|
||||||
from dependency_injector import containers, providers
|
from dependency_injector import containers, providers
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
|
||||||
def init_thread_pool(max_workers: int):
|
def init_thread_pool(max_workers: int):
|
||||||
thread_pool = ThreadPoolExecutor(max_workers=max_workers)
|
thread_pool = ThreadPoolExecutor(max_workers=max_workers)
|
||||||
yield thread_pool
|
yield thread_pool
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
from dependency_injector import containers, providers
|
from dependency_injector import containers, providers
|
||||||
from dependency_injector.wiring import Provide, inject
|
from dependency_injector.wiring import Provide, inject
|
||||||
from typing import Annotated
|
|
||||||
|
|
||||||
|
|
||||||
class Service: ...
|
class Service:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
class Container(containers.DeclarativeContainer):
|
class Container(containers.DeclarativeContainer):
|
||||||
|
@ -13,16 +13,9 @@ class Container(containers.DeclarativeContainer):
|
||||||
service = providers.Factory(Service)
|
service = providers.Factory(Service)
|
||||||
|
|
||||||
|
|
||||||
# You can place marker on parameter default value
|
|
||||||
@inject
|
@inject
|
||||||
def main(service: Service = Provide[Container.service]) -> None: ...
|
def main(service: Service = Provide[Container.service]) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
# Also, you can place marker with typing.Annotated
|
|
||||||
@inject
|
|
||||||
def main_with_annotated(
|
|
||||||
service: Annotated[Service, Provide[Container.service]]
|
|
||||||
) -> None: ...
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
"""Wiring attribute example with Annotated."""
|
|
||||||
|
|
||||||
from typing import Annotated
|
|
||||||
|
|
||||||
from dependency_injector import containers, providers
|
|
||||||
from dependency_injector.wiring import Provide
|
|
||||||
|
|
||||||
|
|
||||||
class Service:
|
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
class Container(containers.DeclarativeContainer):
|
|
||||||
|
|
||||||
service = providers.Factory(Service)
|
|
||||||
|
|
||||||
|
|
||||||
service: Annotated[Service, Provide[Container.service]]
|
|
||||||
|
|
||||||
|
|
||||||
class Main:
|
|
||||||
|
|
||||||
service: Annotated[Service, Provide[Container.service]]
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
container = Container()
|
|
||||||
container.wire(modules=[__name__])
|
|
||||||
|
|
||||||
assert isinstance(service, Service)
|
|
||||||
assert isinstance(Main.service, Service)
|
|
122
pyproject.toml
122
pyproject.toml
|
@ -1,122 +0,0 @@
|
||||||
[build-system]
|
|
||||||
requires = ["setuptools", "Cython>=3.1.1"]
|
|
||||||
build-backend = "setuptools.build_meta"
|
|
||||||
|
|
||||||
[project]
|
|
||||||
name = "dependency-injector"
|
|
||||||
authors = [
|
|
||||||
{name = "Roman Mogylatov", email = "rmogilatov@gmail.com"},
|
|
||||||
]
|
|
||||||
maintainers = [
|
|
||||||
{name = "Roman Mogylatov", email = "rmogilatov@gmail.com"},
|
|
||||||
]
|
|
||||||
description = "Dependency injection framework for Python"
|
|
||||||
readme = {file = "README.rst", content-type = "text/x-rst"}
|
|
||||||
license = {file = "LICENSE.rst", content-type = "text/x-rst"}
|
|
||||||
requires-python = ">=3.8"
|
|
||||||
keywords = [
|
|
||||||
"Dependency injection",
|
|
||||||
"DI",
|
|
||||||
"Inversion of Control",
|
|
||||||
"IoC",
|
|
||||||
"Factory",
|
|
||||||
"Singleton",
|
|
||||||
"Design patterns",
|
|
||||||
"Flask",
|
|
||||||
]
|
|
||||||
classifiers = [
|
|
||||||
"Development Status :: 5 - Production/Stable",
|
|
||||||
"Intended Audience :: Developers",
|
|
||||||
"License :: OSI Approved :: BSD License",
|
|
||||||
"Operating System :: OS Independent",
|
|
||||||
"Programming Language :: Python",
|
|
||||||
"Programming Language :: Python :: 3",
|
|
||||||
"Programming Language :: Python :: 3.8",
|
|
||||||
"Programming Language :: Python :: 3.9",
|
|
||||||
"Programming Language :: Python :: 3.10",
|
|
||||||
"Programming Language :: Python :: 3.11",
|
|
||||||
"Programming Language :: Python :: 3.12",
|
|
||||||
"Programming Language :: Python :: 3.13",
|
|
||||||
"Programming Language :: Python :: Implementation :: CPython",
|
|
||||||
"Programming Language :: Python :: Implementation :: PyPy",
|
|
||||||
"Framework :: AsyncIO",
|
|
||||||
"Framework :: Bottle",
|
|
||||||
"Framework :: Django",
|
|
||||||
"Framework :: Flask",
|
|
||||||
"Framework :: Pylons",
|
|
||||||
"Framework :: Pyramid",
|
|
||||||
"Framework :: Pytest",
|
|
||||||
"Framework :: TurboGears",
|
|
||||||
"Topic :: Software Development",
|
|
||||||
"Topic :: Software Development :: Libraries",
|
|
||||||
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
||||||
]
|
|
||||||
dynamic = ["version"]
|
|
||||||
dependencies = [
|
|
||||||
# typing.Annotated since v3.9
|
|
||||||
# typing.Self since v3.11
|
|
||||||
"typing-extensions; python_version<'3.11'",
|
|
||||||
]
|
|
||||||
|
|
||||||
[project.optional-dependencies]
|
|
||||||
yaml = ["pyyaml"]
|
|
||||||
pydantic = ["pydantic"]
|
|
||||||
pydantic2 = ["pydantic-settings"]
|
|
||||||
flask = ["flask"]
|
|
||||||
aiohttp = ["aiohttp"]
|
|
||||||
|
|
||||||
[project.urls]
|
|
||||||
Homepage = "https://github.com/ets-labs/python-dependency-injector"
|
|
||||||
Documentation = "https://python-dependency-injector.ets-labs.org/"
|
|
||||||
Download = "https://pypi.python.org/pypi/dependency_injector"
|
|
||||||
|
|
||||||
[tool.setuptools]
|
|
||||||
package-dir = {"" = "src"}
|
|
||||||
|
|
||||||
[tool.setuptools.packages.find]
|
|
||||||
where = ["src"]
|
|
||||||
|
|
||||||
[tool.setuptools.package-data]
|
|
||||||
dependency_injector = ["*.pxd", "*.pyi", "py.typed"]
|
|
||||||
|
|
||||||
[tool.setuptools.dynamic]
|
|
||||||
version = {attr = "dependency_injector.__version__"}
|
|
||||||
|
|
||||||
[tool.coverage.run]
|
|
||||||
branch = false
|
|
||||||
relative_files = true
|
|
||||||
source_pkgs = ["dependency_injector"]
|
|
||||||
plugins = ["Cython.Coverage"]
|
|
||||||
|
|
||||||
[tool.coverage.html]
|
|
||||||
directory = "reports/unittests/"
|
|
||||||
|
|
||||||
[tool.coverage.report]
|
|
||||||
show_missing = true
|
|
||||||
|
|
||||||
[tool.isort]
|
|
||||||
profile = "black"
|
|
||||||
combine_as_imports = true
|
|
||||||
|
|
||||||
[tool.pylint.main]
|
|
||||||
ignore = ["tests"]
|
|
||||||
|
|
||||||
[tool.pylint.design]
|
|
||||||
min-public-methods = 0
|
|
||||||
max-public-methods = 30
|
|
||||||
|
|
||||||
[tool.pytest.ini_options]
|
|
||||||
testpaths = ["tests/unit/"]
|
|
||||||
asyncio_mode = "auto"
|
|
||||||
asyncio_default_fixture_loop_scope = "function"
|
|
||||||
markers = [
|
|
||||||
"pydantic: Tests with Pydantic as a dependency",
|
|
||||||
]
|
|
||||||
filterwarnings = [
|
|
||||||
"ignore::dependency_injector.wiring.DIWiringWarning",
|
|
||||||
"ignore:Module \"dependency_injector.ext.aiohttp\" is deprecated since version 4\\.0\\.0:DeprecationWarning",
|
|
||||||
"ignore:Module \"dependency_injector.ext.flask\" is deprecated since version 4\\.0\\.0:DeprecationWarning",
|
|
||||||
"ignore:Please use \\`.*?\\` from the \\`scipy.*?\\`(.*?)namespace is deprecated\\.:DeprecationWarning",
|
|
||||||
"ignore:Please import \\`.*?\\` from the \\`scipy(.*?)\\` namespace(.*):DeprecationWarning",
|
|
||||||
"ignore:\\`scipy(.*?)\\` is deprecated(.*):DeprecationWarning",
|
|
||||||
]
|
|
|
@ -1,11 +1,9 @@
|
||||||
cython==3.1.1
|
cython==0.29.32
|
||||||
setuptools
|
|
||||||
pytest
|
pytest
|
||||||
pytest-asyncio
|
pytest-asyncio
|
||||||
tox
|
tox
|
||||||
coverage
|
coverage
|
||||||
flake8
|
flake8
|
||||||
flake8-pyproject
|
|
||||||
pydocstyle
|
pydocstyle
|
||||||
sphinx_autobuild
|
sphinx_autobuild
|
||||||
pip
|
pip
|
||||||
|
@ -14,12 +12,9 @@ pyyaml
|
||||||
httpx
|
httpx
|
||||||
fastapi
|
fastapi
|
||||||
pydantic
|
pydantic
|
||||||
pydantic-settings
|
|
||||||
numpy
|
numpy
|
||||||
scipy
|
scipy
|
||||||
boto3
|
boto3
|
||||||
mypy_boto3_s3
|
mypy_boto3_s3
|
||||||
typing_extensions
|
|
||||||
fast-depends
|
|
||||||
|
|
||||||
-r requirements-ext.txt
|
-r requirements-ext.txt
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
# TODO: unpin 3.5.0 when this bug is fixed: https://github.com/sphinx-doc/sphinx/issues/8885
|
# TODO: unpin 3.5.0 when this bug is fixed: https://github.com/sphinx-doc/sphinx/issues/8885
|
||||||
sphinx
|
sphinx<3.5.0
|
||||||
|
|
||||||
# TODO: unpin jinja2 after sphinx update to 4+
|
# TODO: unpin jinja2 after sphinx update to 4+
|
||||||
jinja2
|
jinja2<3.1
|
||||||
|
|
||||||
sphinx-disqus==1.3.0
|
-e git+https://github.com/rmk135/sphinxcontrib-disqus.git#egg=sphinxcontrib-disqus
|
||||||
|
|
||||||
-r requirements-ext.txt
|
-r requirements-ext.txt
|
||||||
|
|
|
@ -1,3 +1,2 @@
|
||||||
flask
|
flask
|
||||||
werkzeug
|
|
||||||
aiohttp
|
aiohttp
|
||||||
|
|
1
requirements.txt
Normal file
1
requirements.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
six>=1.7.0,<=1.16.0
|
|
@ -2,13 +2,12 @@
|
||||||
max_line_length = 120
|
max_line_length = 120
|
||||||
max_complexity = 10
|
max_complexity = 10
|
||||||
exclude = types.py
|
exclude = types.py
|
||||||
extend-ignore = E203,E701
|
|
||||||
per-file-ignores =
|
per-file-ignores =
|
||||||
examples/demo/*: F841
|
examples/demo/*: F841
|
||||||
examples/containers/traverse.py: E501
|
examples/containers/traverse.py: E501
|
||||||
examples/providers/async.py: F841
|
examples/providers/async.py: F841
|
||||||
examples/providers/async_overriding.py: F841
|
examples/providers/async_overriding.py: F841
|
||||||
examples/wiring/*: F821,F841
|
examples/wiring/*: F841
|
||||||
|
|
||||||
[pydocstyle]
|
[pydocstyle]
|
||||||
ignore = D100,D101,D102,D105,D106,D107,D203,D213
|
ignore = D100,D101,D102,D105,D106,D107,D203,D213
|
||||||
|
|
165
setup.py
165
setup.py
|
@ -1,55 +1,130 @@
|
||||||
"""`Dependency injector` setup script."""
|
"""`Dependency injector` setup script."""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from Cython.Build import cythonize
|
from setuptools import setup, Extension
|
||||||
from Cython.Compiler import Options
|
|
||||||
from setuptools import Extension, setup
|
|
||||||
|
|
||||||
debug = os.environ.get("DEPENDENCY_INJECTOR_DEBUG_MODE") == "1"
|
|
||||||
limited_api = (
|
def _open(filename):
|
||||||
os.environ.get("DEPENDENCY_INJECTOR_LIMITED_API") == "1"
|
if sys.version_info[0] == 2:
|
||||||
and sys.implementation.name == "cpython"
|
return open(filename)
|
||||||
)
|
return open(filename, encoding="utf-8")
|
||||||
defined_macros = []
|
|
||||||
options = {}
|
|
||||||
compiler_directives = {
|
# Defining setup variables:
|
||||||
"language_level": 3,
|
defined_macros = dict()
|
||||||
"profile": debug,
|
defined_macros["CYTHON_CLINE_IN_TRACEBACK"] = 0
|
||||||
"linetrace": debug,
|
|
||||||
}
|
# Getting description:
|
||||||
Options.annotate = debug
|
with _open("README.rst") as readme_file:
|
||||||
|
description = readme_file.read()
|
||||||
|
|
||||||
|
# Getting requirements:
|
||||||
|
with _open("requirements.txt") as requirements_file:
|
||||||
|
requirements = requirements_file.readlines()
|
||||||
|
|
||||||
|
# Getting version:
|
||||||
|
with _open("src/dependency_injector/__init__.py") as init_file:
|
||||||
|
version = re.search("__version__ = \"(.*?)\"", init_file.read()).group(1)
|
||||||
|
|
||||||
# Adding debug options:
|
# Adding debug options:
|
||||||
if debug:
|
if os.environ.get("DEPENDENCY_INJECTOR_DEBUG_MODE") == "1":
|
||||||
limited_api = False # line tracing is not part of the Limited API
|
defined_macros["CYTHON_TRACE"] = 1
|
||||||
defined_macros.extend(
|
defined_macros["CYTHON_TRACE_NOGIL"] = 1
|
||||||
[
|
defined_macros["CYTHON_CLINE_IN_TRACEBACK"] = 1
|
||||||
("CYTHON_TRACE", "1"),
|
|
||||||
("CYTHON_TRACE_NOGIL", "1"),
|
|
||||||
("CYTHON_CLINE_IN_TRACEBACK", "1"),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
if limited_api:
|
|
||||||
options.setdefault("bdist_wheel", {})
|
|
||||||
options["bdist_wheel"]["py_limited_api"] = "cp38"
|
|
||||||
defined_macros.append(("Py_LIMITED_API", "0x03080000"))
|
|
||||||
|
|
||||||
setup(
|
setup(name="dependency-injector",
|
||||||
options=options,
|
version=version,
|
||||||
ext_modules=cythonize(
|
description="Dependency injection framework for Python",
|
||||||
[
|
long_description=description,
|
||||||
Extension(
|
author="Roman Mogylatov",
|
||||||
"*",
|
author_email="rmogilatov@gmail.com",
|
||||||
["src/**/*.pyx"],
|
maintainer="Roman Mogylatov",
|
||||||
define_macros=defined_macros,
|
maintainer_email="rmogilatov@gmail.com",
|
||||||
py_limited_api=limited_api,
|
url="https://github.com/ets-labs/python-dependency-injector",
|
||||||
),
|
download_url="https://pypi.python.org/pypi/dependency_injector",
|
||||||
],
|
packages=[
|
||||||
annotate=debug,
|
"dependency_injector",
|
||||||
show_all_warnings=True,
|
"dependency_injector.ext",
|
||||||
compiler_directives=compiler_directives,
|
],
|
||||||
),
|
package_dir={
|
||||||
)
|
"": "src",
|
||||||
|
},
|
||||||
|
package_data={
|
||||||
|
"dependency_injector": ["*.pxd", "*.pyi", "py.typed"],
|
||||||
|
},
|
||||||
|
ext_modules=[
|
||||||
|
Extension("dependency_injector.containers",
|
||||||
|
["src/dependency_injector/containers.c"],
|
||||||
|
define_macros=list(defined_macros.items()),
|
||||||
|
extra_compile_args=["-O2"]),
|
||||||
|
Extension("dependency_injector.providers",
|
||||||
|
["src/dependency_injector/providers.c"],
|
||||||
|
define_macros=list(defined_macros.items()),
|
||||||
|
extra_compile_args=["-O2"]),
|
||||||
|
Extension("dependency_injector._cwiring",
|
||||||
|
["src/dependency_injector/_cwiring.c"],
|
||||||
|
define_macros=list(defined_macros.items()),
|
||||||
|
extra_compile_args=["-O2"]),
|
||||||
|
],
|
||||||
|
install_requires=requirements,
|
||||||
|
extras_require={
|
||||||
|
"yaml": [
|
||||||
|
"pyyaml",
|
||||||
|
],
|
||||||
|
"pydantic": [
|
||||||
|
"pydantic",
|
||||||
|
],
|
||||||
|
"flask": [
|
||||||
|
"flask",
|
||||||
|
],
|
||||||
|
"aiohttp": [
|
||||||
|
"aiohttp",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
zip_safe=True,
|
||||||
|
license="BSD New",
|
||||||
|
platforms=["any"],
|
||||||
|
keywords=[
|
||||||
|
"Dependency injection",
|
||||||
|
"DI",
|
||||||
|
"Inversion of Control",
|
||||||
|
"IoC",
|
||||||
|
"Factory",
|
||||||
|
"Singleton",
|
||||||
|
"Design patterns",
|
||||||
|
"Flask",
|
||||||
|
],
|
||||||
|
classifiers=[
|
||||||
|
"Development Status :: 5 - Production/Stable",
|
||||||
|
"Intended Audience :: Developers",
|
||||||
|
"License :: OSI Approved :: BSD License",
|
||||||
|
"Operating System :: OS Independent",
|
||||||
|
"Programming Language :: Python",
|
||||||
|
"Programming Language :: Python :: 2",
|
||||||
|
"Programming Language :: Python :: 2.7",
|
||||||
|
"Programming Language :: Python :: 3",
|
||||||
|
"Programming Language :: Python :: 3.5",
|
||||||
|
"Programming Language :: Python :: 3.6",
|
||||||
|
"Programming Language :: Python :: 3.7",
|
||||||
|
"Programming Language :: Python :: 3.8",
|
||||||
|
"Programming Language :: Python :: 3.9",
|
||||||
|
"Programming Language :: Python :: 3.10",
|
||||||
|
"Programming Language :: Python :: 3.11",
|
||||||
|
"Programming Language :: Python :: Implementation :: CPython",
|
||||||
|
"Programming Language :: Python :: Implementation :: PyPy",
|
||||||
|
"Framework :: AsyncIO",
|
||||||
|
"Framework :: Bottle",
|
||||||
|
"Framework :: Django",
|
||||||
|
"Framework :: Flask",
|
||||||
|
"Framework :: Pylons",
|
||||||
|
"Framework :: Pyramid",
|
||||||
|
"Framework :: Pytest",
|
||||||
|
"Framework :: TurboGears",
|
||||||
|
"Topic :: Software Development",
|
||||||
|
"Topic :: Software Development :: Libraries",
|
||||||
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||||
|
])
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
"""Top-level package."""
|
"""Top-level package."""
|
||||||
|
|
||||||
__version__ = "4.48.1"
|
__version__ = "4.41.0"
|
||||||
"""Version number.
|
"""Version number.
|
||||||
|
|
||||||
:type: str
|
:type: str
|
||||||
|
|
0
src/dependency_injector/__init__.pyi
Normal file
0
src/dependency_injector/__init__.pyi
Normal file
18402
src/dependency_injector/_cwiring.c
Normal file
18402
src/dependency_injector/_cwiring.c
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -1,18 +0,0 @@
|
||||||
from typing import Any, Dict
|
|
||||||
|
|
||||||
from .providers import Provider
|
|
||||||
|
|
||||||
class DependencyResolver:
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
kwargs: Dict[str, Any],
|
|
||||||
injections: Dict[str, Provider[Any]],
|
|
||||||
closings: Dict[str, Provider[Any]],
|
|
||||||
/,
|
|
||||||
) -> None: ...
|
|
||||||
def __enter__(self) -> Dict[str, Any]: ...
|
|
||||||
def __exit__(self, *exc_info: Any) -> None: ...
|
|
||||||
async def __aenter__(self) -> Dict[str, Any]: ...
|
|
||||||
async def __aexit__(self, *exc_info: Any) -> None: ...
|
|
||||||
|
|
||||||
def _isawaitable(instance: Any) -> bool: ...
|
|
|
@ -1,93 +1,88 @@
|
||||||
"""Wiring optimizations module."""
|
"""Wiring optimizations module."""
|
||||||
|
|
||||||
from asyncio import gather
|
import asyncio
|
||||||
from collections.abc import Awaitable
|
import collections.abc
|
||||||
from inspect import CO_ITERABLE_COROUTINE
|
import functools
|
||||||
from types import CoroutineType, GeneratorType
|
import inspect
|
||||||
|
import types
|
||||||
|
|
||||||
from .providers cimport Provider, Resource
|
from . import providers
|
||||||
from .wiring import _Marker
|
from .wiring import _Marker, PatchedCallable
|
||||||
|
|
||||||
|
from .providers cimport Provider
|
||||||
|
|
||||||
|
|
||||||
cdef inline bint _is_injectable(dict kwargs, object name):
|
def _get_sync_patched(fn, patched: PatchedCallable):
|
||||||
return name not in kwargs or isinstance(kwargs[name], _Marker)
|
@functools.wraps(fn)
|
||||||
|
def _patched(*args, **kwargs):
|
||||||
|
cdef object result
|
||||||
cdef class DependencyResolver:
|
cdef dict to_inject
|
||||||
cdef dict kwargs
|
cdef object arg_key
|
||||||
cdef dict to_inject
|
|
||||||
cdef dict injections
|
|
||||||
cdef dict closings
|
|
||||||
|
|
||||||
def __init__(self, dict kwargs, dict injections, dict closings, /):
|
|
||||||
self.kwargs = kwargs
|
|
||||||
self.to_inject = kwargs.copy()
|
|
||||||
self.injections = injections
|
|
||||||
self.closings = closings
|
|
||||||
|
|
||||||
async def _await_injection(self, name: str, value: object, /) -> None:
|
|
||||||
self.to_inject[name] = await value
|
|
||||||
|
|
||||||
cdef void _handle_injections_sync(self):
|
|
||||||
cdef Provider provider
|
cdef Provider provider
|
||||||
|
|
||||||
for name, provider in self.injections.items():
|
to_inject = kwargs.copy()
|
||||||
if _is_injectable(self.kwargs, name):
|
for arg_key, provider in patched.injections.items():
|
||||||
self.to_inject[name] = provider()
|
if arg_key not in kwargs or isinstance(kwargs[arg_key], _Marker):
|
||||||
|
to_inject[arg_key] = provider()
|
||||||
|
|
||||||
cdef list _handle_injections_async(self):
|
result = fn(*args, **to_inject)
|
||||||
cdef list to_await = []
|
|
||||||
cdef Provider provider
|
|
||||||
|
|
||||||
for name, provider in self.injections.items():
|
if patched.closing:
|
||||||
if _is_injectable(self.kwargs, name):
|
for arg_key, provider in patched.closing.items():
|
||||||
provide = provider()
|
if arg_key in kwargs and not isinstance(kwargs[arg_key], _Marker):
|
||||||
|
continue
|
||||||
if provider.is_async_mode_enabled() or _isawaitable(provide):
|
if not isinstance(provider, providers.Resource):
|
||||||
to_await.append(self._await_injection(name, provide))
|
continue
|
||||||
else:
|
|
||||||
self.to_inject[name] = provide
|
|
||||||
|
|
||||||
return to_await
|
|
||||||
|
|
||||||
cdef void _handle_closings_sync(self):
|
|
||||||
cdef Provider provider
|
|
||||||
|
|
||||||
for name, provider in self.closings.items():
|
|
||||||
if _is_injectable(self.kwargs, name) and isinstance(provider, Resource):
|
|
||||||
provider.shutdown()
|
provider.shutdown()
|
||||||
|
|
||||||
cdef list _handle_closings_async(self):
|
return result
|
||||||
cdef list to_await = []
|
return _patched
|
||||||
cdef Provider provider
|
|
||||||
|
|
||||||
for name, provider in self.closings.items():
|
|
||||||
if _is_injectable(self.kwargs, name) and isinstance(provider, Resource):
|
|
||||||
if _isawaitable(shutdown := provider.shutdown()):
|
|
||||||
to_await.append(shutdown)
|
|
||||||
|
|
||||||
return to_await
|
async def _async_inject(object fn, tuple args, dict kwargs, dict injections, dict closings):
|
||||||
|
cdef object result
|
||||||
|
cdef dict to_inject
|
||||||
|
cdef list to_inject_await = []
|
||||||
|
cdef list to_close_await = []
|
||||||
|
cdef object arg_key
|
||||||
|
cdef Provider provider
|
||||||
|
|
||||||
def __enter__(self):
|
to_inject = kwargs.copy()
|
||||||
self._handle_injections_sync()
|
for arg_key, provider in injections.items():
|
||||||
return self.to_inject
|
if arg_key not in kwargs or isinstance(kwargs[arg_key], _Marker):
|
||||||
|
provide = provider()
|
||||||
|
if provider.is_async_mode_enabled():
|
||||||
|
to_inject_await.append((arg_key, provide))
|
||||||
|
elif _isawaitable(provide):
|
||||||
|
to_inject_await.append((arg_key, provide))
|
||||||
|
else:
|
||||||
|
to_inject[arg_key] = provide
|
||||||
|
|
||||||
def __exit__(self, *_):
|
if to_inject_await:
|
||||||
self._handle_closings_sync()
|
async_to_inject = await asyncio.gather(*(provide for _, provide in to_inject_await))
|
||||||
|
for provide, (injection, _) in zip(async_to_inject, to_inject_await):
|
||||||
|
to_inject[injection] = provide
|
||||||
|
|
||||||
async def __aenter__(self):
|
result = await fn(*args, **to_inject)
|
||||||
if to_await := self._handle_injections_async():
|
|
||||||
await gather(*to_await)
|
|
||||||
return self.to_inject
|
|
||||||
|
|
||||||
async def __aexit__(self, *_):
|
if closings:
|
||||||
if to_await := self._handle_closings_async():
|
for arg_key, provider in closings.items():
|
||||||
await gather(*to_await)
|
if arg_key in kwargs and isinstance(kwargs[arg_key], _Marker):
|
||||||
|
continue
|
||||||
|
if not isinstance(provider, providers.Resource):
|
||||||
|
continue
|
||||||
|
shutdown = provider.shutdown()
|
||||||
|
if _isawaitable(shutdown):
|
||||||
|
to_close_await.append(shutdown)
|
||||||
|
|
||||||
|
await asyncio.gather(*to_close_await)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
cdef bint _isawaitable(object instance):
|
cdef bint _isawaitable(object instance):
|
||||||
"""Return true if object can be passed to an ``await`` expression."""
|
"""Return true if object can be passed to an ``await`` expression."""
|
||||||
return (isinstance(instance, CoroutineType) or
|
return (isinstance(instance, types.CoroutineType) or
|
||||||
isinstance(instance, GeneratorType) and
|
isinstance(instance, types.GeneratorType) and
|
||||||
bool(instance.gi_code.co_flags & CO_ITERABLE_COROUTINE) or
|
bool(instance.gi_code.co_flags & inspect.CO_ITERABLE_COROUTINE) or
|
||||||
isinstance(instance, Awaitable))
|
isinstance(instance, collections.abc.Awaitable))
|
||||||
|
|
39152
src/dependency_injector/containers.c
Normal file
39152
src/dependency_injector/containers.c
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user