mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2025-07-14 10:02:23 +03:00
Compare commits
99 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
2c0aede4aa | ||
|
84a14f2ca7 | ||
|
0c58064a36 | ||
|
eb74b1e9d0 | ||
|
be7d25518d | ||
|
04b5907f21 | ||
|
e6cc12762f | ||
|
bf2ddbce32 | ||
|
9d6994391f | ||
|
dd84a1b5d6 | ||
|
31a98f7731 | ||
|
b261251b34 | ||
|
4bfe64563e | ||
|
b411807572 | ||
|
f2da51e0d4 | ||
|
2293251986 | ||
|
1b4b3d349f | ||
|
d8e49f7dd5 | ||
|
c1f14a876a | ||
|
c97a0cc515 | ||
|
0ada62acbf | ||
|
67827a36d1 | ||
|
ceed6a8222 | ||
|
6766ef3eba | ||
|
a8914e54e0 | ||
|
cdd9ce5048 | ||
|
51c7db771d | ||
|
a322584308 | ||
|
4b3476cb48 | ||
|
8460465b5e | ||
|
16f444b230 | ||
|
1ae96e3eeb | ||
|
7fcf1ac7ad | ||
|
1271d0fa79 | ||
|
b9df88eea7 | ||
|
99489afa3f | ||
|
193249f7ec | ||
|
01349c43e1 | ||
|
41ed07a210 | ||
|
8be79126ad | ||
|
dfee54932b | ||
|
a61749c68d | ||
|
14be69371b | ||
|
cfeb018ca7 | ||
|
183b2ae7ff | ||
|
561ff46658 | ||
|
49cc8ed827 | ||
|
383e95faed | ||
|
e7e64f6ae0 | ||
|
f50cc95405 | ||
|
8814db3fb3 | ||
|
8bf9ed04c8 | ||
|
dbf86e4eb4 | ||
|
9a08bfcede | ||
|
4ae79ac21f | ||
|
35bfafdfe2 | ||
|
6685c5a141 | ||
|
57123cebaa | ||
|
6e4794bab1 | ||
|
f3b3b1baa4 | ||
|
9b66d4bf16 | ||
|
7d4ebecd19 | ||
|
09efbffab1 | ||
|
8b625d81ad | ||
|
23acf01c15 | ||
|
0d6fdb5b78 | ||
|
2330122de6 | ||
|
29ae3e1337 | ||
|
50643e0dfb | ||
|
3893e1df81 | ||
|
0fd35baee6 | ||
|
3df95847d5 | ||
|
6d9d34c0f6 | ||
|
de50666a13 | ||
|
ccbd5bbb80 | ||
|
00326e9a22 | ||
|
46646b1acf | ||
|
9f38db6ef3 | ||
|
9f4e2839d2 | ||
|
41e18dfa90 | ||
|
f9db578c59 | ||
|
d82d9fb822 | ||
|
3ba4704bc1 | ||
|
aa56b70dc8 | ||
|
7f586246b4 | ||
|
87741edb53 | ||
|
be7abb3ec7 | ||
|
15400dea7d | ||
|
704e36a642 | ||
|
83d71acb70 | ||
|
c61fc16b8d | ||
|
cab75cb9c7 | ||
|
494c457643 | ||
|
abf2a2577c | ||
|
3777a947ea | ||
|
c92129dcb0 | ||
|
37486900cd | ||
|
9071583981 | ||
|
595daebd3a |
10
.coveragerc
10
.coveragerc
|
@ -1,10 +0,0 @@
|
||||||
[run]
|
|
||||||
source = dependency_injector
|
|
||||||
omit = tests/unit
|
|
||||||
plugins = Cython.Coverage
|
|
||||||
|
|
||||||
[report]
|
|
||||||
show_missing = true
|
|
||||||
|
|
||||||
[html]
|
|
||||||
directory=reports/unittests/
|
|
29
.cursor/rules/coding-guide.mdc
Normal file
29
.cursor/rules/coding-guide.mdc
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
---
|
||||||
|
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
|
7
.cursor/rules/makefile-commands.mdc
Normal file
7
.cursor/rules/makefile-commands.mdc
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
---
|
||||||
|
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``
|
8
.cursor/rules/run-examples.mdc
Normal file
8
.cursor/rules/run-examples.mdc
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
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``
|
9
.editorconfig
Normal file
9
.editorconfig
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.{py,pyi,pxd,pyx}]
|
||||||
|
ij_visual_guides = 80,88
|
94
.github/workflows/publishing.yml
vendored
94
.github/workflows/publishing.yml
vendored
|
@ -10,20 +10,20 @@ jobs:
|
||||||
|
|
||||||
tests:
|
tests:
|
||||||
name: Run tests
|
name: Run tests
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.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.12
|
python-version: 3.13
|
||||||
- run: pip install tox
|
- run: pip install tox
|
||||||
- run: tox
|
- run: tox
|
||||||
env:
|
env:
|
||||||
TOXENV: 3.12
|
TOXENV: 3.13
|
||||||
|
|
||||||
linters:
|
linters:
|
||||||
name: Run linters
|
name: Run linters
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.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.12
|
python-version: 3.13
|
||||||
- run: pip install tox
|
- run: pip install tox
|
||||||
- run: tox
|
- run: tox
|
||||||
env:
|
env:
|
||||||
|
@ -40,17 +40,18 @@ jobs:
|
||||||
build-sdist:
|
build-sdist:
|
||||||
name: Build source tarball
|
name: Build source tarball
|
||||||
needs: [tests, linters]
|
needs: [tests, linters]
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.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.12
|
python-version: 3.13
|
||||||
- run: |
|
- run: |
|
||||||
python -m pip install --upgrade pip setuptools
|
python -m pip install --upgrade build
|
||||||
python setup.py sdist
|
python -m build --sdist
|
||||||
- uses: actions/upload-artifact@v3
|
- uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
|
name: cibw-sdist
|
||||||
path: ./dist/*
|
path: ./dist/*
|
||||||
|
|
||||||
build-wheels:
|
build-wheels:
|
||||||
|
@ -59,62 +60,63 @@ jobs:
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-22.04, windows-2019, macos-14]
|
os: [ubuntu-24.04, ubuntu-24.04-arm, windows-2022, macos-14]
|
||||||
env:
|
env:
|
||||||
CIBW_SKIP: cp27-* cp313-*
|
CIBW_ENABLE: pypy
|
||||||
|
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@v2.20.0
|
uses: pypa/cibuildwheel@v3.0.0
|
||||||
- uses: actions/upload-artifact@v3
|
- uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
|
name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
|
||||||
path: ./wheelhouse/*.whl
|
path: ./wheelhouse/*.whl
|
||||||
|
|
||||||
build-wheels-linux-aarch64:
|
test-publish:
|
||||||
name: Build wheels (ubuntu-22.04-aarch64)
|
name: Upload release to TestPyPI
|
||||||
needs: [tests, linters]
|
needs: [build-sdist, build-wheels]
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-latest
|
||||||
env:
|
environment: test-pypi
|
||||||
CIBW_SKIP: cp27-* cp313-*
|
permissions:
|
||||||
|
id-token: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/download-artifact@v4
|
||||||
- name: Set up QEMU
|
|
||||||
if: runner.os == 'Linux'
|
|
||||||
uses: docker/setup-qemu-action@v2
|
|
||||||
- name: Build wheels
|
|
||||||
uses: pypa/cibuildwheel@v2.20.0
|
|
||||||
env:
|
|
||||||
CIBW_ARCHS_LINUX: aarch64
|
|
||||||
- uses: actions/upload-artifact@v3
|
|
||||||
with:
|
with:
|
||||||
path: ./wheelhouse/*.whl
|
pattern: cibw-*
|
||||||
|
|
||||||
publish:
|
|
||||||
name: Publish on PyPI
|
|
||||||
needs: [build-sdist, build-wheels, build-wheels-linux-aarch64]
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
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:
|
with:
|
||||||
user: __token__
|
repository-url: https://test.pypi.org/legacy/
|
||||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
|
||||||
# For publishing to Test PyPI, uncomment next two lines:
|
publish:
|
||||||
# password: ${{ secrets.TEST_PYPI_API_TOKEN }}
|
name: Upload release to PyPI
|
||||||
# repository_url: https://test.pypi.org/legacy/
|
needs: [build-sdist, build-wheels, test-publish]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
environment: pypi
|
||||||
|
permissions:
|
||||||
|
id-token: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
pattern: cibw-*
|
||||||
|
path: dist
|
||||||
|
merge-multiple: true
|
||||||
|
- uses: pypa/gh-action-pypi-publish@release/v1
|
||||||
|
|
||||||
publish-docs:
|
publish-docs:
|
||||||
name: Publish docs
|
name: Publish docs
|
||||||
needs: [publish]
|
needs: [publish]
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.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.12
|
python-version: 3.13
|
||||||
- run: pip install awscli
|
- run: pip install awscli
|
||||||
- run: pip install -r requirements-doc.txt
|
- run: pip install -r requirements-doc.txt
|
||||||
- run: pip install -e .
|
- run: pip install -e .
|
||||||
|
|
41
.github/workflows/tests-and-linters.yml
vendored
41
.github/workflows/tests-and-linters.yml
vendored
|
@ -4,28 +4,12 @@ on: [push, pull_request, workflow_dispatch]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
tests-on-legacy-versions:
|
|
||||||
name: Run tests on legacy versions
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
python-version: [3.7]
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- uses: actions/setup-python@v4
|
|
||||||
with:
|
|
||||||
python-version: ${{ matrix.python-version }}
|
|
||||||
- run: pip install tox
|
|
||||||
- run: tox
|
|
||||||
env:
|
|
||||||
TOXENV: ${{ matrix.python-version }}
|
|
||||||
|
|
||||||
test-on-different-versions:
|
test-on-different-versions:
|
||||||
name: Run tests
|
name: Run tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
python-version: [3.8, 3.9, "3.10", 3.11, 3.12]
|
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
|
@ -34,14 +18,26 @@ 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:
|
||||||
|
name: Run tests with different pydantic versions
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: "3.12"
|
||||||
|
- run: pip install tox
|
||||||
|
- run: tox -e pydantic-v1,pydantic-v2
|
||||||
|
|
||||||
test-coverage:
|
test-coverage:
|
||||||
name: Run tests with coverage
|
name: Run tests with coverage
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
# Cython's version <3 issue with tracing: "error: no member named 'use_tracing' in 'struct _PyCFrame'"
|
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:
|
||||||
|
@ -49,9 +45,8 @@ jobs:
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: 3.12
|
python-version: 3.12
|
||||||
- run: pip install tox cython==0.29.37
|
- run: pip install tox
|
||||||
- run: make cythonize
|
- run: tox -vv
|
||||||
- run: tox
|
|
||||||
env:
|
env:
|
||||||
TOXENV: coveralls
|
TOXENV: coveralls
|
||||||
|
|
||||||
|
@ -65,7 +60,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.12
|
python-version: 3.13
|
||||||
- run: pip install tox
|
- run: pip install tox
|
||||||
- run: tox
|
- run: tox
|
||||||
env:
|
env:
|
||||||
|
|
13
.gitignore
vendored
13
.gitignore
vendored
|
@ -15,6 +15,7 @@ lib64/
|
||||||
parts/
|
parts/
|
||||||
sdist/
|
sdist/
|
||||||
var/
|
var/
|
||||||
|
wheelhouse/
|
||||||
*.egg-info/
|
*.egg-info/
|
||||||
.installed.cfg
|
.installed.cfg
|
||||||
*.egg
|
*.egg
|
||||||
|
@ -63,13 +64,11 @@ venv*/
|
||||||
# Vim Rope
|
# Vim Rope
|
||||||
.ropeproject/
|
.ropeproject/
|
||||||
|
|
||||||
# C extensions
|
# Cython artifacts
|
||||||
src/dependency_injector/*.h
|
src/**/*.c
|
||||||
src/dependency_injector/*.so
|
src/**/*.h
|
||||||
src/dependency_injector/containers/*.h
|
src/**/*.so
|
||||||
src/dependency_injector/containers/*.so
|
src/**/*.html
|
||||||
src/dependency_injector/providers/*.h
|
|
||||||
src/dependency_injector/providers/*.so
|
|
||||||
|
|
||||||
# Workspace for samples
|
# Workspace for samples
|
||||||
.workspace/
|
.workspace/
|
||||||
|
|
49
.pylintrc
49
.pylintrc
|
@ -1,49 +0,0 @@
|
||||||
[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
|
|
|
@ -21,3 +21,4 @@ Dependency Injector Contributors
|
||||||
+ Thiago Hiromi (thiromi)
|
+ Thiago Hiromi (thiromi)
|
||||||
+ Felipe Rubio (krouw)
|
+ Felipe Rubio (krouw)
|
||||||
+ Anton Petrov (anton-petrov)
|
+ Anton Petrov (anton-petrov)
|
||||||
|
+ ZipFile (ZipFile)
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
recursive-include src/dependency_injector *.py* *.c
|
recursive-include src/dependency_injector *.py* *.c py.typed
|
||||||
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,14 +1,6 @@
|
||||||
VERSION := $(shell python setup.py --version)
|
VERSION := $(shell python setup.py --version)
|
||||||
|
|
||||||
CYTHON_SRC := $(shell find src/dependency_injector -name '*.pyx')
|
export COVERAGE_RCFILE := pyproject.toml
|
||||||
|
|
||||||
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
|
||||||
|
@ -25,21 +17,17 @@ clean:
|
||||||
find examples -name '*.py[co]' -delete
|
find examples -name '*.py[co]' -delete
|
||||||
find examples -name '__pycache__' -delete
|
find examples -name '__pycache__' -delete
|
||||||
|
|
||||||
cythonize:
|
build: clean
|
||||||
# Compile Cython to C
|
# Compile C extensions
|
||||||
cython -a $(CYTHON_DIRECTIVES) $(CYTHON_SRC)
|
python setup.py build_ext --inplace
|
||||||
# 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 cythonize
|
install: uninstall clean build
|
||||||
pip install -ve .
|
pip install -ve .
|
||||||
|
|
||||||
uninstall:
|
uninstall:
|
||||||
|
@ -48,9 +36,9 @@ uninstall:
|
||||||
test:
|
test:
|
||||||
# Unit tests with coverage report
|
# Unit tests with coverage report
|
||||||
coverage erase
|
coverage erase
|
||||||
coverage run --rcfile=./.coveragerc -m pytest -c tests/.configs/pytest.ini
|
coverage run -m pytest
|
||||||
coverage report --rcfile=./.coveragerc
|
coverage report
|
||||||
coverage html --rcfile=./.coveragerc
|
coverage html
|
||||||
|
|
||||||
check:
|
check:
|
||||||
flake8 src/dependency_injector/
|
flake8 src/dependency_injector/
|
||||||
|
@ -61,9 +49,9 @@ check:
|
||||||
|
|
||||||
mypy tests/typing
|
mypy tests/typing
|
||||||
|
|
||||||
test-publish: cythonize
|
test-publish: build
|
||||||
# Create distributions
|
# Create distributions
|
||||||
python setup.py sdist
|
python -m build --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/api/asgi-lifespan.rst
Normal file
9
docs/api/asgi-lifespan.rst
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
dependency_injector.ext.starlette
|
||||||
|
=================================
|
||||||
|
|
||||||
|
.. automodule:: dependency_injector.ext.starlette
|
||||||
|
:members:
|
||||||
|
:inherited-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
.. disqus::
|
|
@ -2,10 +2,11 @@ API Documentation
|
||||||
=================
|
=================
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|
||||||
top-level
|
top-level
|
||||||
providers
|
providers
|
||||||
containers
|
containers
|
||||||
wiring
|
wiring
|
||||||
errors
|
errors
|
||||||
|
asgi-lifespan
|
||||||
|
|
|
@ -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 = None
|
language = "en"
|
||||||
|
|
||||||
# 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:
|
||||||
|
|
|
@ -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: 13
|
:emphasize-lines: 12
|
||||||
|
|
||||||
Tests
|
Tests
|
||||||
-----
|
-----
|
||||||
|
|
48
docs/examples/fastdepends.rst
Normal file
48
docs/examples/fastdepends.rst
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
.. _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
|
|
@ -22,5 +22,6 @@ Explore the examples to see the ``Dependency Injector`` in action.
|
||||||
fastapi
|
fastapi
|
||||||
fastapi-redis
|
fastapi-redis
|
||||||
fastapi-sqlalchemy
|
fastapi-sqlalchemy
|
||||||
|
fastdepends
|
||||||
|
|
||||||
.. 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:: plain
|
.. code-block:: text
|
||||||
|
|
||||||
Explicit is better than implicit
|
Explicit is better than implicit
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,90 @@ 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
|
4.42.0
|
||||||
--------
|
--------
|
||||||
- Promote release ``4.42.0b1`` to a production release.
|
- Promote release ``4.42.0b1`` to a production release.
|
||||||
|
@ -311,8 +395,8 @@ follows `Semantic versioning`_
|
||||||
- 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 object using the
|
``Configuration`` provider can load configuration from a ``pydantic_settings.BaseSettings`` 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: 31
|
:emphasize-lines: 32
|
||||||
|
|
||||||
To get the data from pydantic settings ``Configuration`` provider calls ``Settings.dict()`` method.
|
To get the data from pydantic settings ``Configuration`` provider calls its ``model_dump()`` method.
|
||||||
If you need to pass an argument to this call, use ``.from_pydantic()`` keyword arguments.
|
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 object over the configuration provider argument. In that case,
|
Alternatively, you can provide a ``pydantic_settings.BaseSettings`` object over the configuration provider argument. In that case,
|
||||||
the container will call ``config.from_pydantic()`` automatically:
|
the container will call ``config.from_pydantic()`` automatically:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
@ -215,18 +215,23 @@ the container will call ``config.from_pydantic()`` automatically:
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
``Dependency Injector`` doesn't install ``pydantic`` by default.
|
``Dependency Injector`` doesn't install ``pydantic-settings`` 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[pydantic]
|
pip install dependency-injector[pydantic2]
|
||||||
|
|
||||||
or install ``pydantic`` directly::
|
or install ``pydantic-settings`` directly::
|
||||||
|
|
||||||
pip install pydantic
|
pip install pydantic-settings
|
||||||
|
|
||||||
*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
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
|
@ -361,6 +366,19 @@ 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,11 +61,12 @@ 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 3 types of initializers:
|
Resource provider supports 4 types of initializers:
|
||||||
|
|
||||||
- Function
|
- Function
|
||||||
- Generator
|
- Context Manager
|
||||||
- Subclass of ``resources.Resource``
|
- Generator (legacy)
|
||||||
|
- Subclass of ``resources.Resource`` (legacy)
|
||||||
|
|
||||||
Function initializer
|
Function initializer
|
||||||
--------------------
|
--------------------
|
||||||
|
@ -103,8 +104,44 @@ 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.
|
||||||
|
|
||||||
Generator initializer
|
Context Manager 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:
|
||||||
|
|
||||||
|
@ -154,8 +191,13 @@ object is not mandatory. You can leave ``yield`` statement empty:
|
||||||
argument2=...,
|
argument2=...,
|
||||||
)
|
)
|
||||||
|
|
||||||
Subclass initializer
|
.. note::
|
||||||
--------------------
|
|
||||||
|
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``:
|
||||||
|
|
||||||
|
@ -210,6 +252,72 @@ 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
|
||||||
---------------------------------------------------
|
---------------------------------------------------
|
||||||
|
|
||||||
|
@ -263,10 +371,11 @@ Asynchronous function initializer:
|
||||||
argument2=...,
|
argument2=...,
|
||||||
)
|
)
|
||||||
|
|
||||||
Asynchronous generator initializer:
|
Asynchronous Context Manager 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
|
||||||
|
@ -358,5 +467,54 @@ 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 creates an object. When an object
|
``Singleton`` provider makes dependencies injection only when it 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
|
||||||
|
|
|
@ -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:: json
|
.. code-block:: http
|
||||||
|
|
||||||
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:: json
|
.. code-block:: http
|
||||||
|
|
||||||
HTTP/1.1 200 OK
|
HTTP/1.1 200 OK
|
||||||
Content-Length: 492
|
Content-Length: 492
|
||||||
|
|
|
@ -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,28 +47,27 @@ response it will log:
|
||||||
Prerequisites
|
Prerequisites
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
We will use `Docker <https://www.docker.com/>`_ and
|
We will use `docker compose <https://docs.docker.com/compose/>`_ in this tutorial. Let's check the versions:
|
||||||
`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 20.10.5, build 55c4c88
|
Docker version 27.3.1, build ce12230
|
||||||
docker-compose version 1.29.0, build 07737305
|
Docker Compose version v2.29.7
|
||||||
|
|
||||||
.. 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.
|
||||||
|
|
||||||
|
@ -129,13 +128,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.9-buster`` as a base image.
|
specify how to run it. We will use ``python:3.13-bookworm`` 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.10-buster
|
FROM python:3.13-bookworm
|
||||||
|
|
||||||
ENV PYTHONUNBUFFERED=1
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
|
@ -155,8 +154,6 @@ Put next lines into the ``docker-compose.yml`` file:
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
version: "3.7"
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
|
|
||||||
monitor:
|
monitor:
|
||||||
|
@ -171,7 +168,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:
|
||||||
|
|
||||||
|
@ -184,7 +181,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:
|
||||||
|
|
||||||
|
@ -461,7 +458,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:
|
||||||
|
|
||||||
|
@ -705,7 +702,7 @@ Run in the terminal:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
docker-compose up
|
docker compose up
|
||||||
|
|
||||||
You should see:
|
You should see:
|
||||||
|
|
||||||
|
@ -813,7 +810,7 @@ Run in the terminal:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
docker-compose up
|
docker compose up
|
||||||
|
|
||||||
You should see:
|
You should see:
|
||||||
|
|
||||||
|
@ -965,15 +962,16 @@ 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.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
|
platform linux -- Python 3.13.1, pytest-8.3.4, pluggy-1.5.0
|
||||||
rootdir: /code
|
rootdir: /code
|
||||||
plugins: asyncio-0.16.0, cov-3.0.0
|
plugins: cov-6.0.0, asyncio-0.24.0
|
||||||
|
asyncio: mode=Mode.STRICT, default_loop_scope=None
|
||||||
collected 2 items
|
collected 2 items
|
||||||
|
|
||||||
monitoringdaemon/tests.py .. [100%]
|
monitoringdaemon/tests.py .. [100%]
|
||||||
|
|
|
@ -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:: bash
|
.. code-block:: text
|
||||||
|
|
||||||
./
|
./
|
||||||
├── 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:: bash
|
.. code-block:: text
|
||||||
|
|
||||||
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:: bash
|
.. code-block:: text
|
||||||
: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:: bash
|
.. code-block:: text
|
||||||
|
|
||||||
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:: bash
|
.. code-block:: text
|
||||||
: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:: bash
|
.. code-block:: text
|
||||||
: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:: bash
|
.. code-block:: text
|
||||||
: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:: bash
|
.. code-block:: text
|
||||||
:emphasize-lines: 12
|
:emphasize-lines: 12
|
||||||
|
|
||||||
./
|
./
|
||||||
|
@ -613,7 +613,7 @@ Run in the terminal:
|
||||||
|
|
||||||
You should see:
|
You should see:
|
||||||
|
|
||||||
.. code-block:: plain
|
.. code-block:: text
|
||||||
|
|
||||||
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:: plain
|
.. code-block:: text
|
||||||
|
|
||||||
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:: plain
|
.. code-block:: text
|
||||||
|
|
||||||
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:: bash
|
.. code-block:: text
|
||||||
: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: 36,51
|
:emphasize-lines: 41,50
|
||||||
|
|
||||||
"""Tests module."""
|
"""Tests module."""
|
||||||
|
|
||||||
|
@ -941,13 +941,18 @@ and put next into it:
|
||||||
return container
|
return container
|
||||||
|
|
||||||
|
|
||||||
def test_movies_directed_by(container):
|
@pytest.fixture
|
||||||
|
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")
|
||||||
|
@ -956,13 +961,7 @@ 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):
|
def test_movies_released_in(container, finder_mock):
|
||||||
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)
|
||||||
|
@ -978,7 +977,7 @@ Run in the terminal:
|
||||||
|
|
||||||
You should see:
|
You should see:
|
||||||
|
|
||||||
.. code-block::
|
.. code-block:: text
|
||||||
|
|
||||||
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
|
||||||
|
@ -995,9 +994,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 23 0 100%
|
movies/tests.py 24 0 100%
|
||||||
------------------------------------------
|
------------------------------------------
|
||||||
TOTAL 89 30 66%
|
TOTAL 90 30 67%
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
|
|
|
@ -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:: html
|
.. code-block:: jinja
|
||||||
|
|
||||||
<!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:: html
|
.. code-block:: jinja
|
||||||
|
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ FastAPI example:
|
||||||
|
|
||||||
@app.api_route("/")
|
@app.api_route("/")
|
||||||
@inject
|
@inject
|
||||||
async def index(service: Service = Depends(Provide[Container.service])):
|
async def index(service: Annotated[Service, Depends(Provide[Container.service])]):
|
||||||
value = await service.process()
|
value = await service.process()
|
||||||
return {"result": value}
|
return {"result": value}
|
||||||
|
|
||||||
|
@ -127,6 +127,7 @@ 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
|
||||||
|
@ -254,13 +255,43 @@ 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.
|
You can use wiring to make injections into modules and class attributes. Both the classic marker
|
||||||
|
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
|
||||||
|
@ -601,6 +632,36 @@ 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
|
||||||
---------------------------------
|
---------------------------------
|
||||||
|
|
||||||
|
@ -632,5 +693,6 @@ 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,8 +98,9 @@ The output should be something like:
|
||||||
|
|
||||||
.. code-block::
|
.. code-block::
|
||||||
|
|
||||||
platform darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
|
platform linux -- Python 3.12.3, pytest-8.3.2, pluggy-1.5.0
|
||||||
plugins: asyncio-0.16.0, anyio-3.3.4, aiohttp-0.3.0, cov-3.0.0
|
plugins: cov-6.0.0, anyio-4.4.0, asyncio-0.24.0, aiohttp-1.0.5
|
||||||
|
asyncio: mode=Mode.STRICT, default_loop_scope=None
|
||||||
collected 3 items
|
collected 3 items
|
||||||
|
|
||||||
giphynavigator/tests.py ... [100%]
|
giphynavigator/tests.py ... [100%]
|
||||||
|
|
|
@ -3,11 +3,15 @@
|
||||||
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()
|
||||||
|
@ -15,9 +19,9 @@ def app():
|
||||||
app.container.unwire()
|
app.container.unwire()
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest_asyncio.fixture
|
||||||
def client(app, aiohttp_client, loop):
|
async def client(app, aiohttp_client):
|
||||||
return loop.run_until_complete(aiohttp_client(app))
|
return await aiohttp_client(app)
|
||||||
|
|
||||||
|
|
||||||
async def test_index(client, app):
|
async def test_index(client, app):
|
||||||
|
|
|
@ -2,4 +2,5 @@ dependency-injector
|
||||||
aiohttp
|
aiohttp
|
||||||
pyyaml
|
pyyaml
|
||||||
pytest-aiohttp
|
pytest-aiohttp
|
||||||
|
pytest-asyncio
|
||||||
pytest-cov
|
pytest-cov
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM python:3.10-buster
|
FROM python:3.13-bookworm
|
||||||
|
|
||||||
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,15 +59,16 @@ 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.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
|
platform linux -- Python 3.13.1, pytest-8.3.4, pluggy-1.5.0
|
||||||
rootdir: /code
|
rootdir: /code
|
||||||
plugins: asyncio-0.16.0, cov-3.0.0
|
plugins: cov-6.0.0, asyncio-0.24.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, event_loop):
|
async def test_dispatcher(container, caplog):
|
||||||
caplog.set_level("INFO")
|
caplog.set_level("INFO")
|
||||||
|
|
||||||
example_monitor_mock = mock.AsyncMock()
|
example_monitor_mock = mock.AsyncMock()
|
||||||
|
@ -72,6 +72,7 @@ async def test_dispatcher(container, caplog, event_loop):
|
||||||
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.10-buster
|
FROM python:3.13-bookworm
|
||||||
|
|
||||||
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.10.9, pytest-7.2.0, pluggy-1.0.0
|
platform linux -- Python 3.13.1, pytest-8.3.4, pluggy-1.5.0
|
||||||
rootdir: /code
|
rootdir: /code
|
||||||
plugins: cov-4.0.0, asyncio-0.20.3
|
plugins: cov-6.0.0, asyncio-0.24.0, anyio-4.7.0
|
||||||
collected 1 item
|
asyncio: mode=Mode.STRICT, default_loop_scope=None
|
||||||
|
|
||||||
fastapiredis/tests.py . [100%]
|
fastapiredis/tests.py . [100%]
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,22 @@
|
||||||
"""Application module."""
|
"""Application module."""
|
||||||
|
|
||||||
from dependency_injector.wiring import inject, Provide
|
from typing import Annotated
|
||||||
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(service: Service = Depends(Provide[Container.service])):
|
async def index(
|
||||||
|
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 aioredis import from_url, Redis
|
from redis.asyncio 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 aioredis import Redis
|
from redis.asyncio 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 AsyncClient
|
from httpx import ASGITransport, AsyncClient
|
||||||
|
|
||||||
from .application import app, container
|
from .application import app, container
|
||||||
from .services import Service
|
from .services import Service
|
||||||
|
@ -11,7 +11,10 @@ from .services import Service
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def client(event_loop):
|
def client(event_loop):
|
||||||
client = AsyncClient(app=app, base_url="http://test")
|
client = AsyncClient(
|
||||||
|
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
|
||||||
aioredis
|
redis>=4.2
|
||||||
|
|
||||||
# For testing:
|
# For testing:
|
||||||
pytest
|
pytest
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
from fastapi import FastAPI, Depends
|
from typing import Annotated
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
@ -18,7 +21,9 @@ app = FastAPI()
|
||||||
|
|
||||||
@app.api_route("/")
|
@app.api_route("/")
|
||||||
@inject
|
@inject
|
||||||
async def index(service: Service = Depends(Provide[Container.service])):
|
async def index(
|
||||||
|
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,14 +1,18 @@
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from httpx import AsyncClient
|
import pytest_asyncio
|
||||||
|
from httpx import ASGITransport, AsyncClient
|
||||||
|
|
||||||
from fastapi_di_example import app, container, Service
|
from fastapi_di_example import app, container, Service
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest_asyncio.fixture
|
||||||
async def client(event_loop):
|
async def client():
|
||||||
async with AsyncClient(app=app, base_url="http://test") as client:
|
async with AsyncClient(
|
||||||
|
transport=ASGITransport(app=app),
|
||||||
|
base_url="http://test",
|
||||||
|
) as client:
|
||||||
yield client
|
yield client
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM python:3.10-buster
|
FROM python:3.13-bookworm
|
||||||
|
|
||||||
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.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
|
platform linux -- Python 3.13.1, pytest-8.3.4, pluggy-1.5.0
|
||||||
rootdir: /code
|
rootdir: /code
|
||||||
plugins: cov-3.0.0
|
plugins: cov-6.0.0, anyio-4.7.0
|
||||||
collected 7 items
|
collected 7 items
|
||||||
|
|
||||||
webapp/tests.py ....... [100%]
|
webapp/tests.py ....... [100%]
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
dependency-injector
|
dependency-injector
|
||||||
fastapi
|
fastapi[standard]
|
||||||
uvicorn
|
uvicorn
|
||||||
pyyaml
|
pyyaml
|
||||||
sqlalchemy
|
sqlalchemy
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
"""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 .services import UserService
|
|
||||||
from .repositories import NotFoundError
|
from .repositories import NotFoundError
|
||||||
|
from .services import UserService
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
@ -13,7 +16,7 @@ router = APIRouter()
|
||||||
@router.get("/users")
|
@router.get("/users")
|
||||||
@inject
|
@inject
|
||||||
def get_list(
|
def get_list(
|
||||||
user_service: UserService = Depends(Provide[Container.user_service]),
|
user_service: Annotated[UserService, Depends(Provide[Container.user_service])],
|
||||||
):
|
):
|
||||||
return user_service.get_users()
|
return user_service.get_users()
|
||||||
|
|
||||||
|
@ -21,8 +24,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: UserService = Depends(Provide[Container.user_service]),
|
user_service: Annotated[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)
|
||||||
|
@ -33,7 +36,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: UserService = Depends(Provide[Container.user_service]),
|
user_service: Annotated[UserService, Depends(Provide[Container.user_service])],
|
||||||
):
|
):
|
||||||
return user_service.create_user()
|
return user_service.create_user()
|
||||||
|
|
||||||
|
@ -41,9 +44,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: UserService = Depends(Provide[Container.user_service]),
|
user_service: Annotated[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 darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
|
platform linux -- Python 3.12.3, pytest-8.3.2, pluggy-1.5.0
|
||||||
plugins: asyncio-0.16.0, cov-3.0.0
|
plugins: cov-6.0.0, anyio-4.4.0, asyncio-0.24.0, aiohttp-1.0.5
|
||||||
collected 3 items
|
asyncio: mode=Mode.STRICT, default_loop_scope=None
|
||||||
|
|
||||||
giphynavigator/tests.py ... [100%]
|
giphynavigator/tests.py ... [100%]
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
"""Endpoints module."""
|
"""Endpoints module."""
|
||||||
|
|
||||||
from typing import Optional, List
|
from typing import Annotated, 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 .services import SearchService
|
from dependency_injector.wiring import Provide, inject
|
||||||
|
|
||||||
from .containers import Container
|
from .containers import Container
|
||||||
|
from .services import SearchService
|
||||||
|
|
||||||
|
|
||||||
class Gif(BaseModel):
|
class Gif(BaseModel):
|
||||||
|
@ -26,11 +27,15 @@ router = APIRouter()
|
||||||
@router.get("/", response_model=Response)
|
@router.get("/", response_model=Response)
|
||||||
@inject
|
@inject
|
||||||
async def index(
|
async def index(
|
||||||
query: Optional[str] = None,
|
default_query: Annotated[str, Depends(Provide[Container.config.default.query])],
|
||||||
limit: Optional[str] = None,
|
default_limit: Annotated[
|
||||||
default_query: str = Depends(Provide[Container.config.default.query]),
|
int, Depends(Provide[Container.config.default.limit.as_int()])
|
||||||
default_limit: int = Depends(Provide[Container.config.default.limit.as_int()]),
|
],
|
||||||
search_service: SearchService = Depends(Provide[Container.search_service]),
|
search_service: Annotated[
|
||||||
|
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,15 +3,19 @@
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from httpx import AsyncClient
|
import pytest_asyncio
|
||||||
|
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.fixture
|
@pytest_asyncio.fixture
|
||||||
async def client():
|
async def client():
|
||||||
async with AsyncClient(app=app, base_url="http://test") as client:
|
async with AsyncClient(
|
||||||
|
transport=ASGITransport(app=app),
|
||||||
|
base_url="http://test",
|
||||||
|
) as client:
|
||||||
yield client
|
yield client
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -81,8 +81,9 @@ The output should be something like:
|
||||||
|
|
||||||
.. code-block::
|
.. code-block::
|
||||||
|
|
||||||
platform darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
|
platform linux -- Python 3.12.3, pytest-8.3.2, pluggy-1.5.0
|
||||||
plugins: cov-3.0.0, flask-1.2.0
|
plugins: cov-6.0.0, flask-1.3.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 Bootstrap
|
from flask_bootstrap import Bootstrap4
|
||||||
|
|
||||||
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 = Bootstrap()
|
bootstrap = Bootstrap4()
|
||||||
bootstrap.init_app(app)
|
bootstrap.init_app(app)
|
||||||
|
|
||||||
return app
|
return app
|
||||||
|
|
|
@ -81,8 +81,9 @@ The output should be something like:
|
||||||
|
|
||||||
.. code-block::
|
.. code-block::
|
||||||
|
|
||||||
platform darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
|
platform linux -- Python 3.12.3, pytest-8.3.2, pluggy-1.5.0
|
||||||
plugins: cov-3.0.0, flask-1.2.0
|
plugins: cov-6.0.0, flask-1.3.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 Bootstrap
|
from flask_bootstrap import Bootstrap4
|
||||||
|
|
||||||
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 = Bootstrap()
|
bootstrap = Bootstrap4()
|
||||||
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 darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
|
platform linux -- Python 3.12.3, pytest-8.3.2, pluggy-1.5.0
|
||||||
plugins: cov-3.0.0
|
plugins: cov-6.0.0
|
||||||
collected 2 items
|
collected 2 items
|
||||||
|
|
||||||
movies/tests.py .. [100%]
|
movies/tests.py .. [100%]
|
||||||
|
|
|
@ -18,10 +18,9 @@ SQLITE_FILE = DIR / "movies.db"
|
||||||
|
|
||||||
|
|
||||||
def create_csv(movies_data, path):
|
def create_csv(movies_data, path):
|
||||||
with open(path, "w") as opened_file:
|
with open(path, "w", newline="") as opened_file:
|
||||||
writer = csv.writer(opened_file)
|
writer = csv.writer(opened_file)
|
||||||
for row in movies_data:
|
writer.writerows(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) as csv_file:
|
with open(self._csv_file_path, newline="") 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,13 +26,18 @@ def container():
|
||||||
return container
|
return container
|
||||||
|
|
||||||
|
|
||||||
def test_movies_directed_by(container):
|
@pytest.fixture
|
||||||
|
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")
|
||||||
|
@ -41,13 +46,7 @@ def test_movies_directed_by(container):
|
||||||
assert movies[0].title == "The Jungle Book"
|
assert movies[0].title == "The Jungle Book"
|
||||||
|
|
||||||
|
|
||||||
def test_movies_released_in(container):
|
def test_movies_released_in(container, finder_mock):
|
||||||
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
|
||||||
python -m giphynavigator
|
sanic giphynavigator.application:create_app
|
||||||
|
|
||||||
The output should be something like:
|
The output should be something like:
|
||||||
|
|
||||||
|
@ -98,8 +98,9 @@ The output should be something like:
|
||||||
|
|
||||||
.. code-block::
|
.. code-block::
|
||||||
|
|
||||||
platform darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
|
platform linux -- Python 3.12.3, pytest-8.3.2, pluggy-1.5.0
|
||||||
plugins: sanic-1.9.1, anyio-3.3.4, cov-3.0.0
|
plugins: cov-6.0.0, anyio-4.4.0, asyncio-0.24.0
|
||||||
|
asyncio: mode=Mode.STRICT, default_loop_scope=None
|
||||||
collected 3 items
|
collected 3 items
|
||||||
|
|
||||||
giphynavigator/tests.py ... [100%]
|
giphynavigator/tests.py ... [100%]
|
||||||
|
|
|
@ -8,6 +8,8 @@ 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():
|
||||||
|
@ -17,12 +19,7 @@ def app():
|
||||||
app.ctx.container.unwire()
|
app.ctx.container.unwire()
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
async def test_index(app):
|
||||||
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": [
|
||||||
|
@ -32,7 +29,7 @@ async def test_index(app, test_client):
|
||||||
}
|
}
|
||||||
|
|
||||||
with app.ctx.container.giphy_client.override(giphy_client_mock):
|
with app.ctx.container.giphy_client.override(giphy_client_mock):
|
||||||
response = await test_client.get(
|
_, response = await app.asgi_client.get(
|
||||||
"/",
|
"/",
|
||||||
params={
|
params={
|
||||||
"query": "test",
|
"query": "test",
|
||||||
|
@ -41,7 +38,7 @@ async def test_index(app, test_client):
|
||||||
)
|
)
|
||||||
|
|
||||||
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,
|
||||||
|
@ -52,30 +49,30 @@ async def test_index(app, test_client):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async def test_index_no_data(app, test_client):
|
async def test_index_no_data(app):
|
||||||
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 test_client.get("/")
|
_, response = await app.asgi_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, test_client):
|
async def test_index_default_params(app):
|
||||||
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 test_client.get("/")
|
_, response = await app.asgi_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<=21.6
|
sanic
|
||||||
|
sanic-testing
|
||||||
aiohttp
|
aiohttp
|
||||||
pyyaml
|
pyyaml
|
||||||
pytest-sanic
|
|
||||||
pytest-cov
|
pytest-cov
|
||||||
|
|
39
examples/miniapps/starlette-lifespan/README.rst
Normal file
39
examples/miniapps/starlette-lifespan/README.rst
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
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).
|
59
examples/miniapps/starlette-lifespan/example.py
Executable file
59
examples/miniapps/starlette-lifespan/example.py
Executable file
|
@ -0,0 +1,59 @@
|
||||||
|
#!/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,
|
||||||
|
)
|
3
examples/miniapps/starlette-lifespan/requirements.txt
Normal file
3
examples/miniapps/starlette-lifespan/requirements.txt
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
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 import BaseSettings, Field
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||||
|
|
||||||
# Emulate environment variables
|
# Emulate environment variables
|
||||||
os.environ["AWS_ACCESS_KEY_ID"] = "KEY"
|
os.environ["AWS_ACCESS_KEY_ID"] = "KEY"
|
||||||
|
@ -11,15 +11,16 @@ os.environ["AWS_SECRET_ACCESS_KEY"] = "SECRET"
|
||||||
|
|
||||||
|
|
||||||
class AwsSettings(BaseSettings):
|
class AwsSettings(BaseSettings):
|
||||||
|
model_config = SettingsConfigDict(env_prefix="aws_")
|
||||||
|
|
||||||
access_key_id: str = Field(env="aws_access_key_id")
|
access_key_id: str
|
||||||
secret_access_key: str = Field(env="aws_secret_access_key")
|
secret_access_key: str
|
||||||
|
|
||||||
|
|
||||||
class Settings(BaseSettings):
|
class Settings(BaseSettings):
|
||||||
|
|
||||||
aws: AwsSettings = AwsSettings()
|
aws: AwsSettings = AwsSettings()
|
||||||
optional: str = Field(default="default_value")
|
optional: str = "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 import BaseSettings, Field
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||||
|
|
||||||
# Emulate environment variables
|
# Emulate environment variables
|
||||||
os.environ["AWS_ACCESS_KEY_ID"] = "KEY"
|
os.environ["AWS_ACCESS_KEY_ID"] = "KEY"
|
||||||
|
@ -11,15 +11,16 @@ os.environ["AWS_SECRET_ACCESS_KEY"] = "SECRET"
|
||||||
|
|
||||||
|
|
||||||
class AwsSettings(BaseSettings):
|
class AwsSettings(BaseSettings):
|
||||||
|
model_config = SettingsConfigDict(env_prefix="aws_")
|
||||||
|
|
||||||
access_key_id: str = Field(env="aws_access_key_id")
|
access_key_id: str
|
||||||
secret_access_key: str = Field(env="aws_secret_access_key")
|
secret_access_key: str
|
||||||
|
|
||||||
|
|
||||||
class Settings(BaseSettings):
|
class Settings(BaseSettings):
|
||||||
|
|
||||||
aws: AwsSettings = AwsSettings()
|
aws: AwsSettings = AwsSettings()
|
||||||
optional: str = Field(default="default_value")
|
optional: str = "default_value"
|
||||||
|
|
||||||
|
|
||||||
class Container(containers.DeclarativeContainer):
|
class Container(containers.DeclarativeContainer):
|
||||||
|
|
|
@ -3,10 +3,12 @@
|
||||||
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,9 +13,16 @@ 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__":
|
||||||
|
|
31
examples/wiring/example_attribute_annotated.py
Normal file
31
examples/wiring/example_attribute_annotated.py
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
"""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
Normal file
122
pyproject.toml
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
[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,9 +1,11 @@
|
||||||
cython==0.29.37
|
cython==3.1.1
|
||||||
|
setuptools
|
||||||
pytest
|
pytest
|
||||||
pytest-asyncio
|
pytest-asyncio
|
||||||
tox
|
tox
|
||||||
coverage
|
coverage
|
||||||
flake8
|
flake8
|
||||||
|
flake8-pyproject
|
||||||
pydocstyle
|
pydocstyle
|
||||||
sphinx_autobuild
|
sphinx_autobuild
|
||||||
pip
|
pip
|
||||||
|
@ -11,10 +13,13 @@ mypy
|
||||||
pyyaml
|
pyyaml
|
||||||
httpx
|
httpx
|
||||||
fastapi
|
fastapi
|
||||||
pydantic==1.10.17
|
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,3 +1,3 @@
|
||||||
flask==2.1.3
|
flask
|
||||||
werkzeug==2.2.2
|
werkzeug
|
||||||
aiohttp==3.9.0b1
|
aiohttp
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
six>=1.7.0,<=1.16.0
|
|
|
@ -2,12 +2,13 @@
|
||||||
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/*: F841
|
examples/wiring/*: F821,F841
|
||||||
|
|
||||||
[pydocstyle]
|
[pydocstyle]
|
||||||
ignore = D100,D101,D102,D105,D106,D107,D203,D213
|
ignore = D100,D101,D102,D105,D106,D107,D203,D213
|
||||||
|
|
162
setup.py
162
setup.py
|
@ -1,127 +1,55 @@
|
||||||
"""`Dependency injector` setup script."""
|
"""`Dependency injector` setup script."""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from setuptools import setup, Extension
|
from Cython.Build import cythonize
|
||||||
|
from Cython.Compiler import Options
|
||||||
|
from setuptools import Extension, setup
|
||||||
|
|
||||||
|
debug = os.environ.get("DEPENDENCY_INJECTOR_DEBUG_MODE") == "1"
|
||||||
def _open(filename):
|
limited_api = (
|
||||||
if sys.version_info[0] == 2:
|
os.environ.get("DEPENDENCY_INJECTOR_LIMITED_API") == "1"
|
||||||
return open(filename)
|
and sys.implementation.name == "cpython"
|
||||||
return open(filename, encoding="utf-8")
|
)
|
||||||
|
defined_macros = []
|
||||||
|
options = {}
|
||||||
# Defining setup variables:
|
compiler_directives = {
|
||||||
defined_macros = dict()
|
"language_level": 3,
|
||||||
defined_macros["CYTHON_CLINE_IN_TRACEBACK"] = 0
|
"profile": debug,
|
||||||
|
"linetrace": debug,
|
||||||
# Getting description:
|
}
|
||||||
with _open("README.rst") as readme_file:
|
Options.annotate = debug
|
||||||
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 os.environ.get("DEPENDENCY_INJECTOR_DEBUG_MODE") == "1":
|
if debug:
|
||||||
defined_macros["CYTHON_TRACE"] = 1
|
limited_api = False # line tracing is not part of the Limited API
|
||||||
defined_macros["CYTHON_TRACE_NOGIL"] = 1
|
defined_macros.extend(
|
||||||
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(name="dependency-injector",
|
setup(
|
||||||
version=version,
|
options=options,
|
||||||
description="Dependency injection framework for Python",
|
ext_modules=cythonize(
|
||||||
long_description=description,
|
[
|
||||||
author="Roman Mogylatov",
|
Extension(
|
||||||
author_email="rmogilatov@gmail.com",
|
"*",
|
||||||
maintainer="Roman Mogylatov",
|
["src/**/*.pyx"],
|
||||||
maintainer_email="rmogilatov@gmail.com",
|
define_macros=defined_macros,
|
||||||
url="https://github.com/ets-labs/python-dependency-injector",
|
py_limited_api=limited_api,
|
||||||
download_url="https://pypi.python.org/pypi/dependency_injector",
|
),
|
||||||
packages=[
|
],
|
||||||
"dependency_injector",
|
annotate=debug,
|
||||||
"dependency_injector.ext",
|
show_all_warnings=True,
|
||||||
],
|
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 :: 3",
|
|
||||||
"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 :: 3.12",
|
|
||||||
"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.42.0"
|
__version__ = "4.48.1"
|
||||||
"""Version number.
|
"""Version number.
|
||||||
|
|
||||||
:type: str
|
:type: str
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
18
src/dependency_injector/_cwiring.pyi
Normal file
18
src/dependency_injector/_cwiring.pyi
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
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,88 +1,93 @@
|
||||||
"""Wiring optimizations module."""
|
"""Wiring optimizations module."""
|
||||||
|
|
||||||
import asyncio
|
from asyncio import gather
|
||||||
import collections.abc
|
from collections.abc import Awaitable
|
||||||
import functools
|
from inspect import CO_ITERABLE_COROUTINE
|
||||||
import inspect
|
from types import CoroutineType, GeneratorType
|
||||||
import types
|
|
||||||
|
|
||||||
from . import providers
|
from .providers cimport Provider, Resource
|
||||||
from .wiring import _Marker, PatchedCallable
|
from .wiring import _Marker
|
||||||
|
|
||||||
from .providers cimport Provider
|
|
||||||
|
|
||||||
|
|
||||||
def _get_sync_patched(fn, patched: PatchedCallable):
|
cdef inline bint _is_injectable(dict kwargs, object name):
|
||||||
@functools.wraps(fn)
|
return name not in kwargs or isinstance(kwargs[name], _Marker)
|
||||||
def _patched(*args, **kwargs):
|
|
||||||
cdef object result
|
|
||||||
cdef dict to_inject
|
cdef class DependencyResolver:
|
||||||
cdef object arg_key
|
cdef dict kwargs
|
||||||
|
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
|
||||||
|
|
||||||
to_inject = kwargs.copy()
|
for name, provider in self.injections.items():
|
||||||
for arg_key, provider in patched.injections.items():
|
if _is_injectable(self.kwargs, name):
|
||||||
if arg_key not in kwargs or isinstance(kwargs[arg_key], _Marker):
|
self.to_inject[name] = provider()
|
||||||
to_inject[arg_key] = provider()
|
|
||||||
|
|
||||||
result = fn(*args, **to_inject)
|
cdef list _handle_injections_async(self):
|
||||||
|
cdef list to_await = []
|
||||||
|
cdef Provider provider
|
||||||
|
|
||||||
if patched.closing:
|
for name, provider in self.injections.items():
|
||||||
for arg_key, provider in patched.closing.items():
|
if _is_injectable(self.kwargs, name):
|
||||||
if arg_key in kwargs and not isinstance(kwargs[arg_key], _Marker):
|
provide = provider()
|
||||||
continue
|
|
||||||
if not isinstance(provider, providers.Resource):
|
if provider.is_async_mode_enabled() or _isawaitable(provide):
|
||||||
continue
|
to_await.append(self._await_injection(name, provide))
|
||||||
|
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()
|
||||||
|
|
||||||
return result
|
cdef list _handle_closings_async(self):
|
||||||
return _patched
|
cdef list to_await = []
|
||||||
|
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)
|
||||||
|
|
||||||
async def _async_inject(object fn, tuple args, dict kwargs, dict injections, dict closings):
|
return to_await
|
||||||
cdef object result
|
|
||||||
cdef dict to_inject
|
|
||||||
cdef list to_inject_await = []
|
|
||||||
cdef list to_close_await = []
|
|
||||||
cdef object arg_key
|
|
||||||
cdef Provider provider
|
|
||||||
|
|
||||||
to_inject = kwargs.copy()
|
def __enter__(self):
|
||||||
for arg_key, provider in injections.items():
|
self._handle_injections_sync()
|
||||||
if arg_key not in kwargs or isinstance(kwargs[arg_key], _Marker):
|
return self.to_inject
|
||||||
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
|
|
||||||
|
|
||||||
if to_inject_await:
|
def __exit__(self, *_):
|
||||||
async_to_inject = await asyncio.gather(*(provide for _, provide in to_inject_await))
|
self._handle_closings_sync()
|
||||||
for provide, (injection, _) in zip(async_to_inject, to_inject_await):
|
|
||||||
to_inject[injection] = provide
|
|
||||||
|
|
||||||
result = await fn(*args, **to_inject)
|
async def __aenter__(self):
|
||||||
|
if to_await := self._handle_injections_async():
|
||||||
|
await gather(*to_await)
|
||||||
|
return self.to_inject
|
||||||
|
|
||||||
if closings:
|
async def __aexit__(self, *_):
|
||||||
for arg_key, provider in closings.items():
|
if to_await := self._handle_closings_async():
|
||||||
if arg_key in kwargs and isinstance(kwargs[arg_key], _Marker):
|
await gather(*to_await)
|
||||||
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, types.CoroutineType) or
|
return (isinstance(instance, CoroutineType) or
|
||||||
isinstance(instance, types.GeneratorType) and
|
isinstance(instance, GeneratorType) and
|
||||||
bool(instance.gi_code.co_flags & inspect.CO_ITERABLE_COROUTINE) or
|
bool(instance.gi_code.co_flags & CO_ITERABLE_COROUTINE) or
|
||||||
isinstance(instance, collections.abc.Awaitable))
|
isinstance(instance, Awaitable))
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,24 +1,28 @@
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import (
|
from typing import (
|
||||||
Generic,
|
|
||||||
Type,
|
|
||||||
Dict,
|
|
||||||
List,
|
|
||||||
Tuple,
|
|
||||||
Optional,
|
|
||||||
Any,
|
Any,
|
||||||
Union,
|
Awaitable,
|
||||||
ClassVar,
|
|
||||||
Callable as _Callable,
|
Callable as _Callable,
|
||||||
|
ClassVar,
|
||||||
|
Dict,
|
||||||
|
Generic,
|
||||||
Iterable,
|
Iterable,
|
||||||
Iterator,
|
Iterator,
|
||||||
|
List,
|
||||||
|
Optional,
|
||||||
|
Tuple,
|
||||||
|
Type,
|
||||||
TypeVar,
|
TypeVar,
|
||||||
Awaitable,
|
Union,
|
||||||
overload,
|
overload,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .providers import Provider, Self, ProviderParent
|
try:
|
||||||
|
from typing import Self as _Self
|
||||||
|
except ImportError:
|
||||||
|
from typing_extensions import Self as _Self
|
||||||
|
|
||||||
|
from .providers import Provider, Resource, Self, ProviderParent
|
||||||
|
|
||||||
C_Base = TypeVar("C_Base", bound="Container")
|
C_Base = TypeVar("C_Base", bound="Container")
|
||||||
C = TypeVar("C", bound="DeclarativeContainer")
|
C = TypeVar("C", bound="DeclarativeContainer")
|
||||||
|
@ -26,51 +30,66 @@ C_Overriding = TypeVar("C_Overriding", bound="DeclarativeContainer")
|
||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
TT = TypeVar("TT")
|
TT = TypeVar("TT")
|
||||||
|
|
||||||
|
|
||||||
class WiringConfiguration:
|
class WiringConfiguration:
|
||||||
modules: List[Any]
|
modules: List[Any]
|
||||||
packages: List[Any]
|
packages: List[Any]
|
||||||
from_package: Optional[str]
|
from_package: Optional[str]
|
||||||
auto_wire: bool
|
auto_wire: bool
|
||||||
def __init__(self, modules: Optional[Iterable[Any]] = None, packages: Optional[Iterable[Any]] = None, from_package: Optional[str] = None, auto_wire: bool = True) -> None: ...
|
keep_cache: bool
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
modules: Optional[Iterable[Any]] = None,
|
||||||
|
packages: Optional[Iterable[Any]] = None,
|
||||||
|
from_package: Optional[str] = None,
|
||||||
|
auto_wire: bool = True,
|
||||||
|
keep_cache: bool = False,
|
||||||
|
) -> None: ...
|
||||||
|
|
||||||
class Container:
|
class Container:
|
||||||
provider_type: Type[Provider] = Provider
|
provider_type: Type[Provider[Any]] = Provider
|
||||||
providers: Dict[str, Provider]
|
providers: Dict[str, Provider[Any]]
|
||||||
dependencies: Dict[str, Provider]
|
dependencies: Dict[str, Provider[Any]]
|
||||||
overridden: Tuple[Provider]
|
overridden: Tuple[Provider[Any], ...]
|
||||||
wiring_config: WiringConfiguration
|
wiring_config: WiringConfiguration
|
||||||
auto_load_config: bool = True
|
auto_load_config: bool = True
|
||||||
__self__: Self
|
__self__: Self
|
||||||
def __init__(self) -> None: ...
|
def __init__(self) -> None: ...
|
||||||
def __deepcopy__(self, memo: Optional[Dict[str, Any]]) -> Provider: ...
|
def __deepcopy__(self, memo: Optional[Dict[str, Any]]) -> _Self: ...
|
||||||
def __setattr__(self, name: str, value: Union[Provider, Any]) -> None: ...
|
def __setattr__(self, name: str, value: Union[Provider[Any], Any]) -> None: ...
|
||||||
def __getattr__(self, name: str) -> Provider: ...
|
def __getattr__(self, name: str) -> Provider[Any]: ...
|
||||||
def __delattr__(self, name: str) -> None: ...
|
def __delattr__(self, name: str) -> None: ...
|
||||||
def set_providers(self, **providers: Provider): ...
|
def set_providers(self, **providers: Provider[Any]) -> None: ...
|
||||||
def set_provider(self, name: str, provider: Provider) -> None: ...
|
def set_provider(self, name: str, provider: Provider[Any]) -> None: ...
|
||||||
def override(self, overriding: Union[Container, Type[Container]]) -> None: ...
|
def override(self, overriding: Union[Container, Type[Container]]) -> None: ...
|
||||||
def override_providers(self, **overriding_providers: Union[Provider, Any]) -> ProvidersOverridingContext[C_Base]: ...
|
def override_providers(
|
||||||
|
self, **overriding_providers: Union[Provider[Any], Any]
|
||||||
|
) -> ProvidersOverridingContext[C_Base]: ...
|
||||||
def reset_last_overriding(self) -> None: ...
|
def reset_last_overriding(self) -> None: ...
|
||||||
def reset_override(self) -> None: ...
|
def reset_override(self) -> None: ...
|
||||||
def is_auto_wiring_enabled(self) -> bool: ...
|
def is_auto_wiring_enabled(self) -> bool: ...
|
||||||
def wire(self, modules: Optional[Iterable[Any]] = None, packages: Optional[Iterable[Any]] = None, from_package: Optional[str] = None) -> None: ...
|
def wire(
|
||||||
|
self,
|
||||||
|
modules: Optional[Iterable[Any]] = None,
|
||||||
|
packages: Optional[Iterable[Any]] = None,
|
||||||
|
from_package: Optional[str] = None,
|
||||||
|
) -> None: ...
|
||||||
def unwire(self) -> None: ...
|
def unwire(self) -> None: ...
|
||||||
def init_resources(self) -> Optional[Awaitable]: ...
|
def init_resources(self, resource_type: Type[Resource[Any]] = Resource) -> Optional[Awaitable[None]]: ...
|
||||||
def shutdown_resources(self) -> Optional[Awaitable]: ...
|
def shutdown_resources(self, resource_type: Type[Resource[Any]] = Resource) -> Optional[Awaitable[None]]: ...
|
||||||
def load_config(self) -> None: ...
|
def load_config(self) -> None: ...
|
||||||
def apply_container_providers_overridings(self) -> None: ...
|
def apply_container_providers_overridings(self) -> None: ...
|
||||||
def reset_singletons(self) -> SingletonResetContext[C_Base]: ...
|
def reset_singletons(self) -> SingletonResetContext[C_Base]: ...
|
||||||
def check_dependencies(self) -> None: ...
|
def check_dependencies(self) -> None: ...
|
||||||
def from_schema(self, schema: Dict[Any, Any]) -> None: ...
|
def from_schema(self, schema: Dict[Any, Any]) -> None: ...
|
||||||
def from_yaml_schema(self, filepath: Union[Path, str], loader: Optional[Any]=None) -> None: ...
|
def from_yaml_schema(
|
||||||
|
self, filepath: Union[Path, str], loader: Optional[Any] = None
|
||||||
|
) -> None: ...
|
||||||
def from_json_schema(self, filepath: Union[Path, str]) -> None: ...
|
def from_json_schema(self, filepath: Union[Path, str]) -> None: ...
|
||||||
@overload
|
@overload
|
||||||
def resolve_provider_name(self, provider: Provider) -> str: ...
|
def resolve_provider_name(self, provider: Provider[Any]) -> str: ...
|
||||||
@classmethod
|
@classmethod
|
||||||
@overload
|
@overload
|
||||||
def resolve_provider_name(cls, provider: Provider) -> str: ...
|
def resolve_provider_name(cls, provider: Provider[Any]) -> str: ...
|
||||||
@property
|
@property
|
||||||
def parent(self) -> Optional[ProviderParent]: ...
|
def parent(self) -> Optional[ProviderParent]: ...
|
||||||
@property
|
@property
|
||||||
|
@ -82,40 +101,37 @@ class Container:
|
||||||
@overload
|
@overload
|
||||||
def traverse(cls, types: Optional[Iterable[Type[TT]]] = None) -> Iterator[TT]: ...
|
def traverse(cls, types: Optional[Iterable[Type[TT]]] = None) -> Iterator[TT]: ...
|
||||||
|
|
||||||
|
|
||||||
class DynamicContainer(Container): ...
|
class DynamicContainer(Container): ...
|
||||||
|
|
||||||
|
|
||||||
class DeclarativeContainer(Container):
|
class DeclarativeContainer(Container):
|
||||||
cls_providers: ClassVar[Dict[str, Provider]]
|
cls_providers: ClassVar[Dict[str, Provider[Any]]]
|
||||||
inherited_providers: ClassVar[Dict[str, Provider]]
|
inherited_providers: ClassVar[Dict[str, Provider[Any]]]
|
||||||
def __init__(self, **overriding_providers: Union[Provider, Any]) -> None: ...
|
def __init__(self, **overriding_providers: Union[Provider[Any], Any]) -> None: ...
|
||||||
@classmethod
|
@classmethod
|
||||||
def override(cls, overriding: Union[Container, Type[Container]]) -> None: ...
|
def override(cls, overriding: Union[Container, Type[Container]]) -> None: ...
|
||||||
@classmethod
|
@classmethod
|
||||||
def override_providers(cls, **overriding_providers: Union[Provider, Any]) -> ProvidersOverridingContext[C_Base]: ...
|
def override_providers(
|
||||||
|
cls, **overriding_providers: Union[Provider[Any], Any]
|
||||||
|
) -> ProvidersOverridingContext[C_Base]: ...
|
||||||
@classmethod
|
@classmethod
|
||||||
def reset_last_overriding(cls) -> None: ...
|
def reset_last_overriding(cls) -> None: ...
|
||||||
@classmethod
|
@classmethod
|
||||||
def reset_override(cls) -> None: ...
|
def reset_override(cls) -> None: ...
|
||||||
|
|
||||||
|
|
||||||
class ProvidersOverridingContext(Generic[T]):
|
class ProvidersOverridingContext(Generic[T]):
|
||||||
def __init__(self, container: T, overridden_providers: Iterable[Union[Provider, Any]]) -> None: ...
|
def __init__(
|
||||||
|
self, container: T, overridden_providers: Iterable[Union[Provider[Any], Any]]
|
||||||
|
) -> None: ...
|
||||||
def __enter__(self) -> T: ...
|
def __enter__(self) -> T: ...
|
||||||
def __exit__(self, *_: Any) -> None: ...
|
def __exit__(self, *_: Any) -> None: ...
|
||||||
|
|
||||||
|
|
||||||
class SingletonResetContext(Generic[T]):
|
class SingletonResetContext(Generic[T]):
|
||||||
def __init__(self, container: T): ...
|
def __init__(self, container: T): ...
|
||||||
def __enter__(self) -> T: ...
|
def __enter__(self) -> T: ...
|
||||||
def __exit__(self, *_: Any) -> None: ...
|
def __exit__(self, *_: Any) -> None: ...
|
||||||
|
|
||||||
|
def override(
|
||||||
def override(container: Type[C]) -> _Callable[[Type[C_Overriding]], Type[C_Overriding]]: ...
|
container: Type[C],
|
||||||
|
) -> _Callable[[Type[C_Overriding]], Type[C_Overriding]]: ...
|
||||||
|
|
||||||
def copy(container: Type[C]) -> _Callable[[Type[C_Overriding]], Type[C_Overriding]]: ...
|
def copy(container: Type[C]) -> _Callable[[Type[C_Overriding]], Type[C_Overriding]]: ...
|
||||||
|
|
||||||
|
|
||||||
def is_container(instance: Any) -> bool: ...
|
def is_container(instance: Any) -> bool: ...
|
||||||
|
|
|
@ -1,61 +1,37 @@
|
||||||
"""Containers module."""
|
"""Containers module."""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
import contextlib
|
import contextlib
|
||||||
import copy as copy_module
|
import copy as copy_module
|
||||||
import json
|
import json
|
||||||
import sys
|
|
||||||
import importlib
|
import importlib
|
||||||
import inspect
|
import inspect
|
||||||
import warnings
|
|
||||||
|
|
||||||
try:
|
|
||||||
import asyncio
|
|
||||||
except ImportError:
|
|
||||||
asyncio = None
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import yaml
|
import yaml
|
||||||
except ImportError:
|
except ImportError:
|
||||||
yaml = None
|
yaml = None
|
||||||
|
|
||||||
import six
|
|
||||||
|
|
||||||
from . import providers, errors
|
from . import providers, errors
|
||||||
from .providers cimport __is_future_or_coroutine
|
from .providers cimport __is_future_or_coroutine
|
||||||
|
from .wiring import wire, unwire
|
||||||
|
|
||||||
if sys.version_info[:2] >= (3, 6):
|
|
||||||
from .wiring import wire, unwire
|
|
||||||
else:
|
|
||||||
def wire(*args, **kwargs):
|
|
||||||
raise NotImplementedError("Wiring requires Python 3.6 or above")
|
|
||||||
|
|
||||||
def unwire(*args, **kwargs):
|
|
||||||
raise NotImplementedError("Wiring requires Python 3.6 or above")
|
|
||||||
|
|
||||||
if sys.version_info[:2] == (3, 5):
|
|
||||||
warnings.warn(
|
|
||||||
"Dependency Injector will drop support of Python 3.5 after Jan 1st of 2022. "
|
|
||||||
"This does not mean that there will be any immediate breaking changes, "
|
|
||||||
"but tests will no longer be executed on Python 3.5, and bugs will not be addressed.",
|
|
||||||
category=DeprecationWarning,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class WiringConfiguration:
|
class WiringConfiguration:
|
||||||
"""Container wiring configuration."""
|
"""Container wiring configuration."""
|
||||||
|
|
||||||
def __init__(self, modules=None, packages=None, from_package=None, auto_wire=True):
|
def __init__(self, modules=None, packages=None, from_package=None, auto_wire=True, keep_cache=False):
|
||||||
self.modules = [*modules] if modules else []
|
self.modules = [*modules] if modules else []
|
||||||
self.packages = [*packages] if packages else []
|
self.packages = [*packages] if packages else []
|
||||||
self.from_package = from_package
|
self.from_package = from_package
|
||||||
self.auto_wire = auto_wire
|
self.auto_wire = auto_wire
|
||||||
|
self.keep_cache = keep_cache
|
||||||
|
|
||||||
def __deepcopy__(self, memo=None):
|
def __deepcopy__(self, memo=None):
|
||||||
return self.__class__(self.modules, self.packages, self.from_package, self.auto_wire)
|
return self.__class__(self.modules, self.packages, self.from_package, self.auto_wire, self.keep_cache)
|
||||||
|
|
||||||
|
|
||||||
class Container(object):
|
class Container:
|
||||||
"""Abstract container."""
|
"""Abstract container."""
|
||||||
|
|
||||||
|
|
||||||
|
@ -201,7 +177,7 @@ class DynamicContainer(Container):
|
||||||
|
|
||||||
:rtype: None
|
:rtype: None
|
||||||
"""
|
"""
|
||||||
for name, provider in six.iteritems(providers):
|
for name, provider in providers.items():
|
||||||
setattr(self, name, provider)
|
setattr(self, name, provider)
|
||||||
|
|
||||||
def set_provider(self, name, provider):
|
def set_provider(self, name, provider):
|
||||||
|
@ -234,7 +210,7 @@ class DynamicContainer(Container):
|
||||||
|
|
||||||
self.overridden += (overriding,)
|
self.overridden += (overriding,)
|
||||||
|
|
||||||
for name, provider in six.iteritems(overriding.providers):
|
for name, provider in overriding.providers.items():
|
||||||
try:
|
try:
|
||||||
getattr(self, name).override(provider)
|
getattr(self, name).override(provider)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
|
@ -250,7 +226,7 @@ class DynamicContainer(Container):
|
||||||
:rtype: None
|
:rtype: None
|
||||||
"""
|
"""
|
||||||
overridden_providers = []
|
overridden_providers = []
|
||||||
for name, overriding_provider in six.iteritems(overriding_providers):
|
for name, overriding_provider in overriding_providers.items():
|
||||||
container_provider = getattr(self, name)
|
container_provider = getattr(self, name)
|
||||||
container_provider.override(overriding_provider)
|
container_provider.override(overriding_provider)
|
||||||
overridden_providers.append(container_provider)
|
overridden_providers.append(container_provider)
|
||||||
|
@ -266,7 +242,7 @@ class DynamicContainer(Container):
|
||||||
|
|
||||||
self.overridden = self.overridden[:-1]
|
self.overridden = self.overridden[:-1]
|
||||||
|
|
||||||
for provider in six.itervalues(self.providers):
|
for provider in self.providers.values():
|
||||||
provider.reset_last_overriding()
|
provider.reset_last_overriding()
|
||||||
|
|
||||||
def reset_override(self):
|
def reset_override(self):
|
||||||
|
@ -276,14 +252,14 @@ class DynamicContainer(Container):
|
||||||
"""
|
"""
|
||||||
self.overridden = tuple()
|
self.overridden = tuple()
|
||||||
|
|
||||||
for provider in six.itervalues(self.providers):
|
for provider in self.providers.values():
|
||||||
provider.reset_override()
|
provider.reset_override()
|
||||||
|
|
||||||
def is_auto_wiring_enabled(self):
|
def is_auto_wiring_enabled(self):
|
||||||
"""Check if auto wiring is needed."""
|
"""Check if auto wiring is needed."""
|
||||||
return self.wiring_config.auto_wire is True
|
return self.wiring_config.auto_wire is True
|
||||||
|
|
||||||
def wire(self, modules=None, packages=None, from_package=None):
|
def wire(self, modules=None, packages=None, from_package=None, keep_cache=None):
|
||||||
"""Wire container providers with provided packages and modules.
|
"""Wire container providers with provided packages and modules.
|
||||||
|
|
||||||
:rtype: None
|
:rtype: None
|
||||||
|
@ -314,10 +290,14 @@ class DynamicContainer(Container):
|
||||||
if not modules and not packages:
|
if not modules and not packages:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if keep_cache is None:
|
||||||
|
keep_cache = self.wiring_config.keep_cache
|
||||||
|
|
||||||
wire(
|
wire(
|
||||||
container=self,
|
container=self,
|
||||||
modules=modules,
|
modules=modules,
|
||||||
packages=packages,
|
packages=packages,
|
||||||
|
keep_cache=keep_cache,
|
||||||
)
|
)
|
||||||
|
|
||||||
if modules:
|
if modules:
|
||||||
|
@ -335,11 +315,15 @@ class DynamicContainer(Container):
|
||||||
self.wired_to_modules.clear()
|
self.wired_to_modules.clear()
|
||||||
self.wired_to_packages.clear()
|
self.wired_to_packages.clear()
|
||||||
|
|
||||||
def init_resources(self):
|
def init_resources(self, resource_type=providers.Resource):
|
||||||
"""Initialize all container resources."""
|
"""Initialize all container resources."""
|
||||||
|
|
||||||
|
if not issubclass(resource_type, providers.Resource):
|
||||||
|
raise TypeError("resource_type must be a subclass of Resource provider")
|
||||||
|
|
||||||
futures = []
|
futures = []
|
||||||
|
|
||||||
for provider in self.traverse(types=[providers.Resource]):
|
for provider in self.traverse(types=[resource_type]):
|
||||||
resource = provider.init()
|
resource = provider.init()
|
||||||
|
|
||||||
if __is_future_or_coroutine(resource):
|
if __is_future_or_coroutine(resource):
|
||||||
|
@ -348,8 +332,12 @@ class DynamicContainer(Container):
|
||||||
if futures:
|
if futures:
|
||||||
return asyncio.gather(*futures)
|
return asyncio.gather(*futures)
|
||||||
|
|
||||||
def shutdown_resources(self):
|
def shutdown_resources(self, resource_type=providers.Resource):
|
||||||
"""Shutdown all container resources."""
|
"""Shutdown all container resources."""
|
||||||
|
|
||||||
|
if not issubclass(resource_type, providers.Resource):
|
||||||
|
raise TypeError("resource_type must be a subclass of Resource provider")
|
||||||
|
|
||||||
def _independent_resources(resources):
|
def _independent_resources(resources):
|
||||||
for resource in resources:
|
for resource in resources:
|
||||||
for other_resource in resources:
|
for other_resource in resources:
|
||||||
|
@ -380,7 +368,7 @@ class DynamicContainer(Container):
|
||||||
for resource in resources_to_shutdown:
|
for resource in resources_to_shutdown:
|
||||||
resource.shutdown()
|
resource.shutdown()
|
||||||
|
|
||||||
resources = list(self.traverse(types=[providers.Resource]))
|
resources = list(self.traverse(types=[resource_type]))
|
||||||
if any(resource.is_async_mode_enabled() for resource in resources):
|
if any(resource.is_async_mode_enabled() for resource in resources):
|
||||||
return _async_ordered_shutdown(resources)
|
return _async_ordered_shutdown(resources)
|
||||||
else:
|
else:
|
||||||
|
@ -495,13 +483,13 @@ class DeclarativeContainerMetaClass(type):
|
||||||
|
|
||||||
containers = {
|
containers = {
|
||||||
name: container
|
name: container
|
||||||
for name, container in six.iteritems(attributes)
|
for name, container in attributes.items()
|
||||||
if is_container(container)
|
if is_container(container)
|
||||||
}
|
}
|
||||||
|
|
||||||
cls_providers = {
|
cls_providers = {
|
||||||
name: provider
|
name: provider
|
||||||
for name, provider in six.iteritems(attributes)
|
for name, provider in attributes.items()
|
||||||
if isinstance(provider, providers.Provider) and not isinstance(provider, providers.Self)
|
if isinstance(provider, providers.Provider) and not isinstance(provider, providers.Self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -509,7 +497,7 @@ class DeclarativeContainerMetaClass(type):
|
||||||
name: provider
|
name: provider
|
||||||
for base in bases
|
for base in bases
|
||||||
if is_container(base) and base is not DynamicContainer
|
if is_container(base) and base is not DynamicContainer
|
||||||
for name, provider in six.iteritems(base.providers)
|
for name, provider in base.providers.items()
|
||||||
}
|
}
|
||||||
|
|
||||||
all_providers = {}
|
all_providers = {}
|
||||||
|
@ -536,10 +524,10 @@ class DeclarativeContainerMetaClass(type):
|
||||||
self.set_container(cls)
|
self.set_container(cls)
|
||||||
cls.__self__ = self
|
cls.__self__ = self
|
||||||
|
|
||||||
for provider in six.itervalues(cls.providers):
|
for provider in cls.providers.values():
|
||||||
_check_provider_type(cls, provider)
|
_check_provider_type(cls, provider)
|
||||||
|
|
||||||
for provider in six.itervalues(cls.cls_providers):
|
for provider in cls.cls_providers.values():
|
||||||
if isinstance(provider, providers.CHILD_PROVIDERS):
|
if isinstance(provider, providers.CHILD_PROVIDERS):
|
||||||
provider.assign_parent(cls)
|
provider.assign_parent(cls)
|
||||||
|
|
||||||
|
@ -641,8 +629,7 @@ class DeclarativeContainerMetaClass(type):
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
@six.add_metaclass(DeclarativeContainerMetaClass)
|
class DeclarativeContainer(Container, metaclass=DeclarativeContainerMetaClass):
|
||||||
class DeclarativeContainer(Container):
|
|
||||||
"""Declarative inversion of control container.
|
"""Declarative inversion of control container.
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
@ -767,7 +754,7 @@ class DeclarativeContainer(Container):
|
||||||
|
|
||||||
cls.overridden += (overriding,)
|
cls.overridden += (overriding,)
|
||||||
|
|
||||||
for name, provider in six.iteritems(overriding.cls_providers):
|
for name, provider in overriding.cls_providers.items():
|
||||||
try:
|
try:
|
||||||
getattr(cls, name).override(provider)
|
getattr(cls, name).override(provider)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
|
@ -784,7 +771,7 @@ class DeclarativeContainer(Container):
|
||||||
|
|
||||||
cls.overridden = cls.overridden[:-1]
|
cls.overridden = cls.overridden[:-1]
|
||||||
|
|
||||||
for provider in six.itervalues(cls.providers):
|
for provider in cls.providers.values():
|
||||||
provider.reset_last_overriding()
|
provider.reset_last_overriding()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -795,7 +782,7 @@ class DeclarativeContainer(Container):
|
||||||
"""
|
"""
|
||||||
cls.overridden = tuple()
|
cls.overridden = tuple()
|
||||||
|
|
||||||
for provider in six.itervalues(cls.providers):
|
for provider in cls.providers.values():
|
||||||
provider.reset_override()
|
provider.reset_override()
|
||||||
|
|
||||||
|
|
||||||
|
@ -858,7 +845,7 @@ def copy(object base_container):
|
||||||
"""
|
"""
|
||||||
def _get_memo_for_matching_names(new_providers, base_providers):
|
def _get_memo_for_matching_names(new_providers, base_providers):
|
||||||
memo = {}
|
memo = {}
|
||||||
for new_provider_name, new_provider in six.iteritems(new_providers):
|
for new_provider_name, new_provider in new_providers.items():
|
||||||
if new_provider_name not in base_providers:
|
if new_provider_name not in base_providers:
|
||||||
continue
|
continue
|
||||||
source_provider = base_providers[new_provider_name]
|
source_provider = base_providers[new_provider_name]
|
||||||
|
@ -877,7 +864,7 @@ def copy(object base_container):
|
||||||
new_providers.update(providers.deepcopy(base_container.providers, memo))
|
new_providers.update(providers.deepcopy(base_container.providers, memo))
|
||||||
new_providers.update(providers.deepcopy(new_container.cls_providers, memo))
|
new_providers.update(providers.deepcopy(new_container.cls_providers, memo))
|
||||||
|
|
||||||
for name, provider in six.iteritems(new_providers):
|
for name, provider in new_providers.items():
|
||||||
setattr(new_container, name, provider)
|
setattr(new_container, name, provider)
|
||||||
return new_container
|
return new_container
|
||||||
|
|
||||||
|
|
|
@ -10,3 +10,24 @@ class Error(Exception):
|
||||||
|
|
||||||
class NoSuchProviderError(Error, AttributeError):
|
class NoSuchProviderError(Error, AttributeError):
|
||||||
"""Error that is raised when provider lookup is failed."""
|
"""Error that is raised when provider lookup is failed."""
|
||||||
|
|
||||||
|
|
||||||
|
class NonCopyableArgumentError(Error):
|
||||||
|
"""Error that is raised when provider argument is not deep-copyable."""
|
||||||
|
|
||||||
|
index: int
|
||||||
|
keyword: str
|
||||||
|
provider: object
|
||||||
|
|
||||||
|
def __init__(self, provider: object, index: int = -1, keyword: str = "") -> None:
|
||||||
|
self.provider = provider
|
||||||
|
self.index = index
|
||||||
|
self.keyword = keyword
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
s = (
|
||||||
|
f"keyword argument {self.keyword}"
|
||||||
|
if self.keyword
|
||||||
|
else f"argument at index {self.index}"
|
||||||
|
)
|
||||||
|
return f"Couldn't copy {s} for provider {self.provider!r}"
|
||||||
|
|
|
@ -7,7 +7,6 @@ import warnings
|
||||||
|
|
||||||
from dependency_injector import providers
|
from dependency_injector import providers
|
||||||
|
|
||||||
|
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
'Module "dependency_injector.ext.aiohttp" is deprecated since '
|
'Module "dependency_injector.ext.aiohttp" is deprecated since '
|
||||||
'version 4.0.0. Use "dependency_injector.wiring" module instead.',
|
'version 4.0.0. Use "dependency_injector.wiring" module instead.',
|
||||||
|
@ -38,9 +37,11 @@ class View(providers.Callable):
|
||||||
|
|
||||||
def as_view(self):
|
def as_view(self):
|
||||||
"""Return aiohttp view function."""
|
"""Return aiohttp view function."""
|
||||||
|
|
||||||
@functools.wraps(self.provides)
|
@functools.wraps(self.provides)
|
||||||
async def _view(request, *args, **kwargs):
|
async def _view(request, *args, **kwargs):
|
||||||
return await self.__call__(request, *args, **kwargs)
|
return await self.__call__(request, *args, **kwargs)
|
||||||
|
|
||||||
return _view
|
return _view
|
||||||
|
|
||||||
|
|
||||||
|
@ -49,6 +50,8 @@ class ClassBasedView(providers.Factory):
|
||||||
|
|
||||||
def as_view(self):
|
def as_view(self):
|
||||||
"""Return aiohttp view function."""
|
"""Return aiohttp view function."""
|
||||||
|
|
||||||
async def _view(request, *args, **kwargs):
|
async def _view(request, *args, **kwargs):
|
||||||
return await self.__call__(request, *args, **kwargs)
|
return await self.__call__(request, *args, **kwargs)
|
||||||
|
|
||||||
return _view
|
return _view
|
||||||
|
|
|
@ -1,23 +1,16 @@
|
||||||
from typing import Awaitable as _Awaitable
|
from typing import Any, Awaitable as _Awaitable, TypeVar
|
||||||
|
|
||||||
from dependency_injector import providers
|
from dependency_injector import providers
|
||||||
|
|
||||||
|
T = TypeVar("T")
|
||||||
|
|
||||||
class Application(providers.Singleton): ...
|
class Application(providers.Singleton[T]): ...
|
||||||
|
class Extension(providers.Singleton[T]): ...
|
||||||
|
class Middleware(providers.DelegatedCallable[T]): ...
|
||||||
|
class MiddlewareFactory(providers.Factory[T]): ...
|
||||||
|
|
||||||
|
class View(providers.Callable[T]):
|
||||||
|
def as_view(self) -> _Awaitable[T]: ...
|
||||||
|
|
||||||
class Extension(providers.Singleton): ...
|
class ClassBasedView(providers.Factory[T]):
|
||||||
|
def as_view(self) -> _Awaitable[T]: ...
|
||||||
|
|
||||||
class Middleware(providers.DelegatedCallable): ...
|
|
||||||
|
|
||||||
|
|
||||||
class MiddlewareFactory(providers.Factory): ...
|
|
||||||
|
|
||||||
|
|
||||||
class View(providers.Callable):
|
|
||||||
def as_view(self) -> _Awaitable: ...
|
|
||||||
|
|
||||||
|
|
||||||
class ClassBasedView(providers.Factory):
|
|
||||||
def as_view(self) -> _Awaitable: ...
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
"""Flask extension module."""
|
"""Flask extension module."""
|
||||||
|
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from flask import request as flask_request
|
from flask import request as flask_request
|
||||||
|
|
||||||
from dependency_injector import providers, errors
|
from dependency_injector import errors, providers
|
||||||
|
|
||||||
|
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
'Module "dependency_injector.ext.flask" is deprecated since '
|
'Module "dependency_injector.ext.flask" is deprecated since '
|
||||||
|
@ -45,6 +45,7 @@ class ClassBasedView(providers.Factory):
|
||||||
def as_view(provider, name=None):
|
def as_view(provider, name=None):
|
||||||
"""Transform class-based view provider to view function."""
|
"""Transform class-based view provider to view function."""
|
||||||
if isinstance(provider, providers.Factory):
|
if isinstance(provider, providers.Factory):
|
||||||
|
|
||||||
def view(*args, **kwargs):
|
def view(*args, **kwargs):
|
||||||
self = provider()
|
self = provider()
|
||||||
return self.dispatch_request(*args, **kwargs)
|
return self.dispatch_request(*args, **kwargs)
|
||||||
|
@ -52,12 +53,13 @@ def as_view(provider, name=None):
|
||||||
assert name, 'Argument "endpoint" is required for class-based views'
|
assert name, 'Argument "endpoint" is required for class-based views'
|
||||||
view.__name__ = name
|
view.__name__ = name
|
||||||
elif isinstance(provider, providers.Callable):
|
elif isinstance(provider, providers.Callable):
|
||||||
|
|
||||||
def view(*args, **kwargs):
|
def view(*args, **kwargs):
|
||||||
return provider(*args, **kwargs)
|
return provider(*args, **kwargs)
|
||||||
|
|
||||||
view.__name__ = provider.provides.__name__
|
view.__name__ = provider.provides.__name__
|
||||||
else:
|
else:
|
||||||
raise errors.Error('Undefined provider type')
|
raise errors.Error("Undefined provider type")
|
||||||
|
|
||||||
view.__doc__ = provider.provides.__doc__
|
view.__doc__ = provider.provides.__doc__
|
||||||
view.__module__ = provider.provides.__module__
|
view.__module__ = provider.provides.__module__
|
||||||
|
@ -65,14 +67,14 @@ def as_view(provider, name=None):
|
||||||
if isinstance(provider.provides, type):
|
if isinstance(provider.provides, type):
|
||||||
view.view_class = provider.provides
|
view.view_class = provider.provides
|
||||||
|
|
||||||
if hasattr(provider.provides, 'decorators'):
|
if hasattr(provider.provides, "decorators"):
|
||||||
for decorator in provider.provides.decorators:
|
for decorator in provider.provides.decorators:
|
||||||
view = decorator(view)
|
view = decorator(view)
|
||||||
|
|
||||||
if hasattr(provider.provides, 'methods'):
|
if hasattr(provider.provides, "methods"):
|
||||||
view.methods = provider.provides.methods
|
view.methods = provider.provides.methods
|
||||||
|
|
||||||
if hasattr(provider.provides, 'provide_automatic_options'):
|
if hasattr(provider.provides, "provide_automatic_options"):
|
||||||
view.provide_automatic_options = provider.provides.provide_automatic_options
|
view.provide_automatic_options = provider.provides.provide_automatic_options
|
||||||
|
|
||||||
return view
|
return view
|
||||||
|
|
|
@ -1,24 +1,21 @@
|
||||||
from typing import Union, Optional, Callable as _Callable, Any
|
from typing import Any, Callable as _Callable, Optional, TypeVar, Union
|
||||||
|
|
||||||
|
from flask.wrappers import Request
|
||||||
|
|
||||||
from flask import request as flask_request
|
|
||||||
from dependency_injector import providers
|
from dependency_injector import providers
|
||||||
|
|
||||||
|
request: providers.Object[Request]
|
||||||
|
T = TypeVar("T")
|
||||||
|
|
||||||
request: providers.Object[flask_request]
|
class Application(providers.Singleton[T]): ...
|
||||||
|
class Extension(providers.Singleton[T]): ...
|
||||||
|
|
||||||
|
class View(providers.Callable[T]):
|
||||||
|
def as_view(self) -> _Callable[..., T]: ...
|
||||||
|
|
||||||
class Application(providers.Singleton): ...
|
class ClassBasedView(providers.Factory[T]):
|
||||||
|
def as_view(self, name: str) -> _Callable[..., T]: ...
|
||||||
|
|
||||||
|
def as_view(
|
||||||
class Extension(providers.Singleton): ...
|
provider: Union[View[T], ClassBasedView[T]], name: Optional[str] = None
|
||||||
|
) -> _Callable[..., T]: ...
|
||||||
|
|
||||||
class View(providers.Callable):
|
|
||||||
def as_view(self) -> _Callable[..., Any]: ...
|
|
||||||
|
|
||||||
|
|
||||||
class ClassBasedView(providers.Factory):
|
|
||||||
def as_view(self, name: str) -> _Callable[..., Any]: ...
|
|
||||||
|
|
||||||
|
|
||||||
def as_view(provider: Union[View, ClassBasedView], name: Optional[str] = None) -> _Callable[..., Any]: ...
|
|
||||||
|
|
61
src/dependency_injector/ext/starlette.py
Normal file
61
src/dependency_injector/ext/starlette.py
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
import sys
|
||||||
|
from typing import Any, Type
|
||||||
|
|
||||||
|
if sys.version_info >= (3, 11): # pragma: no cover
|
||||||
|
from typing import Self
|
||||||
|
else: # pragma: no cover
|
||||||
|
from typing_extensions import Self
|
||||||
|
|
||||||
|
from dependency_injector.containers import Container
|
||||||
|
from dependency_injector.providers import Resource
|
||||||
|
|
||||||
|
|
||||||
|
class Lifespan:
|
||||||
|
"""A starlette lifespan handler performing container resource initialization and shutdown.
|
||||||
|
|
||||||
|
See https://www.starlette.io/lifespan/ for details.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from dependency_injector.containers import DeclarativeContainer
|
||||||
|
from dependency_injector.ext.starlette import Lifespan
|
||||||
|
from dependency_injector.providers import Factory, Self, Singleton
|
||||||
|
from starlette.applications import Starlette
|
||||||
|
|
||||||
|
class Container(DeclarativeContainer):
|
||||||
|
__self__ = Self()
|
||||||
|
lifespan = Singleton(Lifespan, __self__)
|
||||||
|
app = Factory(Starlette, lifespan=lifespan)
|
||||||
|
|
||||||
|
:param container: container instance
|
||||||
|
:param resource_type: A :py:class:`~dependency_injector.resources.Resource`
|
||||||
|
subclass. Limits the resources to be initialized and shutdown.
|
||||||
|
"""
|
||||||
|
|
||||||
|
container: Container
|
||||||
|
resource_type: Type[Resource[Any]]
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
container: Container,
|
||||||
|
resource_type: Type[Resource[Any]] = Resource,
|
||||||
|
) -> None:
|
||||||
|
self.container = container
|
||||||
|
self.resource_type = resource_type
|
||||||
|
|
||||||
|
def __call__(self, app: Any) -> Self:
|
||||||
|
return self
|
||||||
|
|
||||||
|
async def __aenter__(self) -> None:
|
||||||
|
result = self.container.init_resources(self.resource_type)
|
||||||
|
|
||||||
|
if result is not None:
|
||||||
|
await result
|
||||||
|
|
||||||
|
async def __aexit__(self, *exc_info: Any) -> None:
|
||||||
|
result = self.container.shutdown_resources(self.resource_type)
|
||||||
|
|
||||||
|
if result is not None:
|
||||||
|
await result
|
File diff suppressed because it is too large
Load Diff
|
@ -1,10 +1,6 @@
|
||||||
"""Providers module."""
|
"""Providers module."""
|
||||||
|
|
||||||
try:
|
import asyncio
|
||||||
import asyncio
|
|
||||||
except ImportError:
|
|
||||||
asyncio = None
|
|
||||||
|
|
||||||
import functools
|
import functools
|
||||||
|
|
||||||
cimport cython
|
cimport cython
|
||||||
|
@ -19,11 +15,11 @@ cdef tuple __COROUTINE_TYPES
|
||||||
|
|
||||||
|
|
||||||
# Base providers
|
# Base providers
|
||||||
cdef class Provider(object):
|
cdef class Provider:
|
||||||
cdef tuple __overridden
|
cdef tuple _overridden
|
||||||
cdef Provider __last_overriding
|
cdef Provider _last_overriding
|
||||||
cdef tuple __overrides
|
cdef tuple _overrides
|
||||||
cdef int __async_mode
|
cdef int _async_mode
|
||||||
|
|
||||||
cpdef bint is_async_mode_enabled(self)
|
cpdef bint is_async_mode_enabled(self)
|
||||||
cpdef bint is_async_mode_disabled(self)
|
cpdef bint is_async_mode_disabled(self)
|
||||||
|
@ -34,32 +30,32 @@ cdef class Provider(object):
|
||||||
|
|
||||||
|
|
||||||
cdef class Object(Provider):
|
cdef class Object(Provider):
|
||||||
cdef object __provides
|
cdef object _provides
|
||||||
|
|
||||||
cpdef object _provide(self, tuple args, dict kwargs)
|
cpdef object _provide(self, tuple args, dict kwargs)
|
||||||
|
|
||||||
|
|
||||||
cdef class Self(Provider):
|
cdef class Self(Provider):
|
||||||
cdef object __container
|
cdef object _container
|
||||||
cdef tuple __alt_names
|
cdef tuple _alt_names
|
||||||
|
|
||||||
|
|
||||||
cdef class Delegate(Provider):
|
cdef class Delegate(Provider):
|
||||||
cdef object __provides
|
cdef object _provides
|
||||||
|
|
||||||
cpdef object _provide(self, tuple args, dict kwargs)
|
cpdef object _provide(self, tuple args, dict kwargs)
|
||||||
|
|
||||||
|
|
||||||
cdef class Aggregate(Provider):
|
cdef class Aggregate(Provider):
|
||||||
cdef dict __providers
|
cdef dict _providers
|
||||||
|
|
||||||
cdef Provider __get_provider(self, object provider_name)
|
cdef Provider __get_provider(self, object provider_name)
|
||||||
|
|
||||||
|
|
||||||
cdef class Dependency(Provider):
|
cdef class Dependency(Provider):
|
||||||
cdef object __instance_of
|
cdef object _instance_of
|
||||||
cdef object __default
|
cdef object _default
|
||||||
cdef object __parent
|
cdef object _parent
|
||||||
|
|
||||||
|
|
||||||
cdef class ExternalDependency(Dependency):
|
cdef class ExternalDependency(Dependency):
|
||||||
|
@ -67,21 +63,21 @@ cdef class ExternalDependency(Dependency):
|
||||||
|
|
||||||
|
|
||||||
cdef class DependenciesContainer(Object):
|
cdef class DependenciesContainer(Object):
|
||||||
cdef dict __providers
|
cdef dict _providers
|
||||||
cdef object __parent
|
cdef object _parent
|
||||||
|
|
||||||
cpdef object _override_providers(self, object container)
|
cpdef object _override_providers(self, object container)
|
||||||
|
|
||||||
|
|
||||||
# Callable providers
|
# Callable providers
|
||||||
cdef class Callable(Provider):
|
cdef class Callable(Provider):
|
||||||
cdef object __provides
|
cdef object _provides
|
||||||
|
|
||||||
cdef tuple __args
|
cdef tuple _args
|
||||||
cdef int __args_len
|
cdef int _args_len
|
||||||
|
|
||||||
cdef tuple __kwargs
|
cdef tuple _kwargs
|
||||||
cdef int __kwargs_len
|
cdef int _kwargs_len
|
||||||
|
|
||||||
cpdef object _provide(self, tuple args, dict kwargs)
|
cpdef object _provide(self, tuple args, dict kwargs)
|
||||||
|
|
||||||
|
@ -117,11 +113,11 @@ cdef class CoroutineDelegate(Delegate):
|
||||||
|
|
||||||
# Configuration providers
|
# Configuration providers
|
||||||
cdef class ConfigurationOption(Provider):
|
cdef class ConfigurationOption(Provider):
|
||||||
cdef tuple __name
|
cdef tuple _name
|
||||||
cdef Configuration __root
|
cdef Configuration _root
|
||||||
cdef dict __children
|
cdef dict _children
|
||||||
cdef bint __required
|
cdef bint _required
|
||||||
cdef object __cache
|
cdef object _cache
|
||||||
|
|
||||||
|
|
||||||
cdef class TypedConfigurationOption(Callable):
|
cdef class TypedConfigurationOption(Callable):
|
||||||
|
@ -129,22 +125,22 @@ cdef class TypedConfigurationOption(Callable):
|
||||||
|
|
||||||
|
|
||||||
cdef class Configuration(Object):
|
cdef class Configuration(Object):
|
||||||
cdef str __name
|
cdef str _name
|
||||||
cdef bint __strict
|
cdef bint __strict
|
||||||
cdef dict __children
|
cdef dict _children
|
||||||
cdef list __ini_files
|
cdef list _ini_files
|
||||||
cdef list __yaml_files
|
cdef list _yaml_files
|
||||||
cdef list __json_files
|
cdef list _json_files
|
||||||
cdef list __pydantic_settings
|
cdef list _pydantic_settings
|
||||||
cdef object __weakref__
|
cdef object __weakref__
|
||||||
|
|
||||||
|
|
||||||
# Factory providers
|
# Factory providers
|
||||||
cdef class Factory(Provider):
|
cdef class Factory(Provider):
|
||||||
cdef Callable __instantiator
|
cdef Callable _instantiator
|
||||||
|
|
||||||
cdef tuple __attributes
|
cdef tuple _attributes
|
||||||
cdef int __attributes_len
|
cdef int _attributes_len
|
||||||
|
|
||||||
cpdef object _provide(self, tuple args, dict kwargs)
|
cpdef object _provide(self, tuple args, dict kwargs)
|
||||||
|
|
||||||
|
@ -167,8 +163,8 @@ cdef class FactoryAggregate(Aggregate):
|
||||||
|
|
||||||
# Singleton providers
|
# Singleton providers
|
||||||
cdef class BaseSingleton(Provider):
|
cdef class BaseSingleton(Provider):
|
||||||
cdef Factory __instantiator
|
cdef Factory _instantiator
|
||||||
cdef object __storage
|
cdef object _storage
|
||||||
|
|
||||||
|
|
||||||
cdef class Singleton(BaseSingleton):
|
cdef class Singleton(BaseSingleton):
|
||||||
|
@ -181,7 +177,7 @@ cdef class DelegatedSingleton(Singleton):
|
||||||
|
|
||||||
|
|
||||||
cdef class ThreadSafeSingleton(BaseSingleton):
|
cdef class ThreadSafeSingleton(BaseSingleton):
|
||||||
cdef object __storage_lock
|
cdef object _storage_lock
|
||||||
|
|
||||||
cpdef object _provide(self, tuple args, dict kwargs)
|
cpdef object _provide(self, tuple args, dict kwargs)
|
||||||
|
|
||||||
|
@ -215,87 +211,87 @@ cdef class SingletonDelegate(Delegate):
|
||||||
# Miscellaneous providers
|
# Miscellaneous providers
|
||||||
|
|
||||||
cdef class List(Provider):
|
cdef class List(Provider):
|
||||||
cdef tuple __args
|
cdef tuple _args
|
||||||
cdef int __args_len
|
cdef int _args_len
|
||||||
|
|
||||||
cpdef object _provide(self, tuple args, dict kwargs)
|
cpdef object _provide(self, tuple args, dict kwargs)
|
||||||
|
|
||||||
|
|
||||||
cdef class Dict(Provider):
|
cdef class Dict(Provider):
|
||||||
cdef tuple __kwargs
|
cdef tuple _kwargs
|
||||||
cdef int __kwargs_len
|
cdef int _kwargs_len
|
||||||
|
|
||||||
cpdef object _provide(self, tuple args, dict kwargs)
|
cpdef object _provide(self, tuple args, dict kwargs)
|
||||||
|
|
||||||
|
|
||||||
cdef class Resource(Provider):
|
cdef class Resource(Provider):
|
||||||
cdef object __provides
|
cdef object _provides
|
||||||
cdef bint __initialized
|
cdef bint _initialized
|
||||||
cdef object __shutdowner
|
cdef object _shutdowner
|
||||||
cdef object __resource
|
cdef object _resource
|
||||||
|
|
||||||
cdef tuple __args
|
cdef tuple _args
|
||||||
cdef int __args_len
|
cdef int _args_len
|
||||||
|
|
||||||
cdef tuple __kwargs
|
cdef tuple _kwargs
|
||||||
cdef int __kwargs_len
|
cdef int _kwargs_len
|
||||||
|
|
||||||
cpdef object _provide(self, tuple args, dict kwargs)
|
cpdef object _provide(self, tuple args, dict kwargs)
|
||||||
|
|
||||||
|
|
||||||
cdef class Container(Provider):
|
cdef class Container(Provider):
|
||||||
cdef object __container_cls
|
cdef object _container_cls
|
||||||
cdef dict __overriding_providers
|
cdef dict _overriding_providers
|
||||||
cdef object __container
|
cdef object _container
|
||||||
cdef object __parent
|
cdef object _parent
|
||||||
|
|
||||||
cpdef object _provide(self, tuple args, dict kwargs)
|
cpdef object _provide(self, tuple args, dict kwargs)
|
||||||
|
|
||||||
|
|
||||||
cdef class Selector(Provider):
|
cdef class Selector(Provider):
|
||||||
cdef object __selector
|
cdef object _selector
|
||||||
cdef dict __providers
|
cdef dict _providers
|
||||||
|
|
||||||
cpdef object _provide(self, tuple args, dict kwargs)
|
cpdef object _provide(self, tuple args, dict kwargs)
|
||||||
|
|
||||||
# Provided instance
|
# Provided instance
|
||||||
|
|
||||||
cdef class ProvidedInstance(Provider):
|
cdef class ProvidedInstance(Provider):
|
||||||
cdef object __provides
|
cdef object _provides
|
||||||
|
|
||||||
cpdef object _provide(self, tuple args, dict kwargs)
|
cpdef object _provide(self, tuple args, dict kwargs)
|
||||||
|
|
||||||
|
|
||||||
cdef class AttributeGetter(Provider):
|
cdef class AttributeGetter(Provider):
|
||||||
cdef object __provides
|
cdef object _provides
|
||||||
cdef object __name
|
cdef object _name
|
||||||
|
|
||||||
cpdef object _provide(self, tuple args, dict kwargs)
|
cpdef object _provide(self, tuple args, dict kwargs)
|
||||||
|
|
||||||
|
|
||||||
cdef class ItemGetter(Provider):
|
cdef class ItemGetter(Provider):
|
||||||
cdef object __provides
|
cdef object _provides
|
||||||
cdef object __name
|
cdef object _name
|
||||||
|
|
||||||
cpdef object _provide(self, tuple args, dict kwargs)
|
cpdef object _provide(self, tuple args, dict kwargs)
|
||||||
|
|
||||||
|
|
||||||
cdef class MethodCaller(Provider):
|
cdef class MethodCaller(Provider):
|
||||||
cdef object __provides
|
cdef object _provides
|
||||||
cdef tuple __args
|
cdef tuple _args
|
||||||
cdef int __args_len
|
cdef int _args_len
|
||||||
cdef tuple __kwargs
|
cdef tuple _kwargs
|
||||||
cdef int __kwargs_len
|
cdef int _kwargs_len
|
||||||
|
|
||||||
cpdef object _provide(self, tuple args, dict kwargs)
|
cpdef object _provide(self, tuple args, dict kwargs)
|
||||||
|
|
||||||
|
|
||||||
# Injections
|
# Injections
|
||||||
cdef class Injection(object):
|
cdef class Injection:
|
||||||
cdef object __value
|
cdef object _value
|
||||||
cdef int __is_provider
|
cdef int _is_provider
|
||||||
cdef int __is_delegated
|
cdef int _is_delegated
|
||||||
cdef int __call
|
cdef int _call
|
||||||
|
|
||||||
|
|
||||||
cdef class PositionalInjection(Injection):
|
cdef class PositionalInjection(Injection):
|
||||||
|
@ -303,7 +299,7 @@ cdef class PositionalInjection(Injection):
|
||||||
|
|
||||||
|
|
||||||
cdef class NamedInjection(Injection):
|
cdef class NamedInjection(Injection):
|
||||||
cdef object __name
|
cdef object _name
|
||||||
|
|
||||||
|
|
||||||
cpdef tuple parse_positional_injections(tuple args)
|
cpdef tuple parse_positional_injections(tuple args)
|
||||||
|
@ -313,13 +309,13 @@ cpdef tuple parse_named_injections(dict kwargs)
|
||||||
|
|
||||||
|
|
||||||
# Utils
|
# Utils
|
||||||
cdef class OverridingContext(object):
|
cdef class OverridingContext:
|
||||||
cdef Provider __overridden
|
cdef Provider _overridden
|
||||||
cdef Provider __overriding
|
cdef Provider _overriding
|
||||||
|
|
||||||
|
|
||||||
cdef class BaseSingletonResetContext(object):
|
cdef class BaseSingletonResetContext:
|
||||||
cdef object __singleton
|
cdef object _singleton
|
||||||
|
|
||||||
|
|
||||||
cdef class SingletonResetContext(BaseSingletonResetContext):
|
cdef class SingletonResetContext(BaseSingletonResetContext):
|
||||||
|
@ -356,19 +352,19 @@ cpdef object deepcopy(object instance, dict memo=*)
|
||||||
|
|
||||||
# Inline helper functions
|
# Inline helper functions
|
||||||
cdef inline object __get_name(NamedInjection self):
|
cdef inline object __get_name(NamedInjection self):
|
||||||
return self.__name
|
return self._name
|
||||||
|
|
||||||
|
|
||||||
cdef inline object __get_value(Injection self):
|
cdef inline object __get_value(Injection self):
|
||||||
if self.__call == 0:
|
if self._call == 0:
|
||||||
return self.__value
|
return self._value
|
||||||
return self.__value()
|
return self._value()
|
||||||
|
|
||||||
|
|
||||||
cdef inline object __get_value_kwargs(Injection self, dict kwargs):
|
cdef inline object __get_value_kwargs(Injection self, dict kwargs):
|
||||||
if self.__call == 0:
|
if self._call == 0:
|
||||||
return self.__value
|
return self._value
|
||||||
return self.__value(**kwargs)
|
return self._value(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
cdef inline tuple __separate_prefixed_kwargs(dict kwargs):
|
cdef inline tuple __separate_prefixed_kwargs(dict kwargs):
|
||||||
|
@ -633,14 +629,14 @@ cdef inline object __async_result_callback(object future_result, object future):
|
||||||
|
|
||||||
cdef inline object __callable_call(Callable self, tuple args, dict kwargs, ):
|
cdef inline object __callable_call(Callable self, tuple args, dict kwargs, ):
|
||||||
return __call(
|
return __call(
|
||||||
self.__provides,
|
self._provides,
|
||||||
args,
|
args,
|
||||||
self.__args,
|
self._args,
|
||||||
self.__args_len,
|
self._args_len,
|
||||||
kwargs,
|
kwargs,
|
||||||
self.__kwargs,
|
self._kwargs,
|
||||||
self.__kwargs_len,
|
self._kwargs_len,
|
||||||
self.__async_mode,
|
self._async_mode,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -648,18 +644,18 @@ cdef inline object __factory_call(Factory self, tuple args, dict kwargs):
|
||||||
cdef object instance
|
cdef object instance
|
||||||
|
|
||||||
instance = __call(
|
instance = __call(
|
||||||
self.__instantiator.__provides,
|
self._instantiator._provides,
|
||||||
args,
|
args,
|
||||||
self.__instantiator.__args,
|
self._instantiator._args,
|
||||||
self.__instantiator.__args_len,
|
self._instantiator._args_len,
|
||||||
kwargs,
|
kwargs,
|
||||||
self.__instantiator.__kwargs,
|
self._instantiator._kwargs,
|
||||||
self.__instantiator.__kwargs_len,
|
self._instantiator._kwargs_len,
|
||||||
self.__async_mode,
|
self._async_mode,
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.__attributes_len > 0:
|
if self._attributes_len > 0:
|
||||||
attributes = __provide_attributes(self.__attributes, self.__attributes_len)
|
attributes = __provide_attributes(self._attributes, self._attributes_len)
|
||||||
|
|
||||||
is_future_instance = __is_future_or_coroutine(instance)
|
is_future_instance = __is_future_or_coroutine(instance)
|
||||||
is_future_attributes = __is_future_or_coroutine(attributes)
|
is_future_attributes = __is_future_or_coroutine(attributes)
|
||||||
|
@ -701,3 +697,10 @@ cdef inline object __future_result(object instance):
|
||||||
future_result = asyncio.Future()
|
future_result = asyncio.Future()
|
||||||
future_result.set_result(instance)
|
future_result.set_result(instance)
|
||||||
return future_result
|
return future_result
|
||||||
|
|
||||||
|
|
||||||
|
cdef class NullAwaitable:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
cdef NullAwaitable NULL_AWAITABLE
|
||||||
|
|
|
@ -33,7 +33,6 @@ except ImportError:
|
||||||
|
|
||||||
from . import resources
|
from . import resources
|
||||||
|
|
||||||
|
|
||||||
Injection = Any
|
Injection = Any
|
||||||
ProviderParent = Union["Provider", Any]
|
ProviderParent = Union["Provider", Any]
|
||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
|
@ -41,16 +40,13 @@ TT = TypeVar("TT")
|
||||||
P = TypeVar("P", bound="Provider")
|
P = TypeVar("P", bound="Provider")
|
||||||
BS = TypeVar("BS", bound="BaseSingleton")
|
BS = TypeVar("BS", bound="BaseSingleton")
|
||||||
|
|
||||||
|
|
||||||
class Provider(Generic[T]):
|
class Provider(Generic[T]):
|
||||||
def __init__(self) -> None: ...
|
def __init__(self) -> None: ...
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def __call__(self, *args: Injection, **kwargs: Injection) -> T: ...
|
def __call__(self, *args: Injection, **kwargs: Injection) -> T: ...
|
||||||
@overload
|
@overload
|
||||||
def __call__(self, *args: Injection, **kwargs: Injection) -> Awaitable[T]: ...
|
def __call__(self, *args: Injection, **kwargs: Injection) -> Awaitable[T]: ...
|
||||||
def async_(self, *args: Injection, **kwargs: Injection) -> Awaitable[T]: ...
|
def async_(self, *args: Injection, **kwargs: Injection) -> Awaitable[T]: ...
|
||||||
|
|
||||||
def __deepcopy__(self, memo: Optional[_Dict[Any, Any]]) -> Provider: ...
|
def __deepcopy__(self, memo: Optional[_Dict[Any, Any]]) -> Provider: ...
|
||||||
def __str__(self) -> str: ...
|
def __str__(self) -> str: ...
|
||||||
def __repr__(self) -> str: ...
|
def __repr__(self) -> str: ...
|
||||||
|
@ -67,9 +63,9 @@ class Provider(Generic[T]):
|
||||||
def unregister_overrides(self, provider: Union[Provider, Any]) -> None: ...
|
def unregister_overrides(self, provider: Union[Provider, Any]) -> None: ...
|
||||||
def delegate(self) -> Provider: ...
|
def delegate(self) -> Provider: ...
|
||||||
@property
|
@property
|
||||||
def provider(self) -> Provider: ...
|
def provider(self) -> Provider[T]: ...
|
||||||
@property
|
@property
|
||||||
def provided(self) -> ProvidedInstance: ...
|
def provided(self) -> ProvidedInstance[T]: ...
|
||||||
def enable_async_mode(self) -> None: ...
|
def enable_async_mode(self) -> None: ...
|
||||||
def disable_async_mode(self) -> None: ...
|
def disable_async_mode(self) -> None: ...
|
||||||
def reset_async_mode(self) -> None: ...
|
def reset_async_mode(self) -> None: ...
|
||||||
|
@ -78,9 +74,12 @@ class Provider(Generic[T]):
|
||||||
def is_async_mode_undefined(self) -> bool: ...
|
def is_async_mode_undefined(self) -> bool: ...
|
||||||
@property
|
@property
|
||||||
def related(self) -> _Iterator[Provider]: ...
|
def related(self) -> _Iterator[Provider]: ...
|
||||||
def traverse(self, types: Optional[_Iterable[Type[TT]]] = None) -> _Iterator[TT]: ...
|
def traverse(
|
||||||
def _copy_overridings(self, copied: Provider, memo: Optional[_Dict[Any, Any]]) -> None: ...
|
self, types: Optional[_Iterable[Type[TT]]] = None
|
||||||
|
) -> _Iterator[TT]: ...
|
||||||
|
def _copy_overridings(
|
||||||
|
self, copied: Provider, memo: Optional[_Dict[Any, Any]]
|
||||||
|
) -> None: ...
|
||||||
|
|
||||||
class Object(Provider[T]):
|
class Object(Provider[T]):
|
||||||
def __init__(self, provides: Optional[T] = None) -> None: ...
|
def __init__(self, provides: Optional[T] = None) -> None: ...
|
||||||
|
@ -88,7 +87,6 @@ class Object(Provider[T]):
|
||||||
def provides(self) -> Optional[T]: ...
|
def provides(self) -> Optional[T]: ...
|
||||||
def set_provides(self, provides: Optional[T]) -> Object: ...
|
def set_provides(self, provides: Optional[T]) -> Object: ...
|
||||||
|
|
||||||
|
|
||||||
class Self(Provider[T]):
|
class Self(Provider[T]):
|
||||||
def __init__(self, container: Optional[T] = None) -> None: ...
|
def __init__(self, container: Optional[T] = None) -> None: ...
|
||||||
def set_container(self, container: T) -> None: ...
|
def set_container(self, container: T) -> None: ...
|
||||||
|
@ -96,41 +94,51 @@ class Self(Provider[T]):
|
||||||
@property
|
@property
|
||||||
def alt_names(self) -> Tuple[Any]: ...
|
def alt_names(self) -> Tuple[Any]: ...
|
||||||
|
|
||||||
|
|
||||||
class Delegate(Provider[Provider]):
|
class Delegate(Provider[Provider]):
|
||||||
def __init__(self, provides: Optional[Provider] = None) -> None: ...
|
def __init__(self, provides: Optional[Provider] = None) -> None: ...
|
||||||
@property
|
@property
|
||||||
def provides(self) -> Optional[Provider]: ...
|
def provides(self) -> Optional[Provider]: ...
|
||||||
def set_provides(self, provides: Optional[Provider]) -> Delegate: ...
|
def set_provides(self, provides: Optional[Provider]) -> Delegate: ...
|
||||||
|
|
||||||
|
|
||||||
class Aggregate(Provider[T]):
|
class Aggregate(Provider[T]):
|
||||||
def __init__(self, provider_dict: Optional[_Dict[Any, Provider[T]]] = None, **provider_kwargs: Provider[T]): ...
|
def __init__(
|
||||||
|
self,
|
||||||
|
provider_dict: Optional[_Dict[Any, Provider[T]]] = None,
|
||||||
|
**provider_kwargs: Provider[T],
|
||||||
|
): ...
|
||||||
def __getattr__(self, provider_name: Any) -> Provider[T]: ...
|
def __getattr__(self, provider_name: Any) -> Provider[T]: ...
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def __call__(self, provider_name: Optional[Any] = None, *args: Injection, **kwargs: Injection) -> T: ...
|
def __call__(
|
||||||
|
self, provider_name: Optional[Any] = None, *args: Injection, **kwargs: Injection
|
||||||
|
) -> T: ...
|
||||||
@overload
|
@overload
|
||||||
def __call__(self, provider_name: Optional[Any] = None, *args: Injection, **kwargs: Injection) -> Awaitable[T]: ...
|
def __call__(
|
||||||
def async_(self, provider_name: Optional[Any] = None, *args: Injection, **kwargs: Injection) -> Awaitable[T]: ...
|
self, provider_name: Optional[Any] = None, *args: Injection, **kwargs: Injection
|
||||||
|
) -> Awaitable[T]: ...
|
||||||
|
def async_(
|
||||||
|
self, provider_name: Optional[Any] = None, *args: Injection, **kwargs: Injection
|
||||||
|
) -> Awaitable[T]: ...
|
||||||
@property
|
@property
|
||||||
def providers(self) -> _Dict[Any, Provider[T]]: ...
|
def providers(self) -> _Dict[Any, Provider[T]]: ...
|
||||||
def set_providers(self, provider_dict: Optional[_Dict[Any, Provider[T]]] = None, **provider_kwargs: Provider[T]) -> Aggregate[T]: ...
|
def set_providers(
|
||||||
|
self,
|
||||||
|
provider_dict: Optional[_Dict[Any, Provider[T]]] = None,
|
||||||
|
**provider_kwargs: Provider[T],
|
||||||
|
) -> Aggregate[T]: ...
|
||||||
|
|
||||||
class Dependency(Provider[T]):
|
class Dependency(Provider[T]):
|
||||||
def __init__(self, instance_of: Type[T] = object, default: Optional[Union[Provider, Any]] = None) -> None: ...
|
def __init__(
|
||||||
|
self,
|
||||||
|
instance_of: Type[T] = object,
|
||||||
|
default: Optional[Union[Provider, Any]] = None,
|
||||||
|
) -> None: ...
|
||||||
def __getattr__(self, name: str) -> Any: ...
|
def __getattr__(self, name: str) -> Any: ...
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def instance_of(self) -> Type[T]: ...
|
def instance_of(self) -> Type[T]: ...
|
||||||
def set_instance_of(self, instance_of: Type[T]) -> Dependency[T]: ...
|
def set_instance_of(self, instance_of: Type[T]) -> Dependency[T]: ...
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def default(self) -> Provider[T]: ...
|
def default(self) -> Provider[T]: ...
|
||||||
def set_default(self, default: Optional[Union[Provider, Any]]) -> Dependency[T]: ...
|
def set_default(self, default: Optional[Union[Provider, Any]]) -> Dependency[T]: ...
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_defined(self) -> bool: ...
|
def is_defined(self) -> bool: ...
|
||||||
def provided_by(self, provider: Provider) -> OverridingContext[P]: ...
|
def provided_by(self, provider: Provider) -> OverridingContext[P]: ...
|
||||||
|
@ -140,10 +148,8 @@ class Dependency(Provider[T]):
|
||||||
def parent_name(self) -> Optional[str]: ...
|
def parent_name(self) -> Optional[str]: ...
|
||||||
def assign_parent(self, parent: ProviderParent) -> None: ...
|
def assign_parent(self, parent: ProviderParent) -> None: ...
|
||||||
|
|
||||||
|
|
||||||
class ExternalDependency(Dependency[T]): ...
|
class ExternalDependency(Dependency[T]): ...
|
||||||
|
|
||||||
|
|
||||||
class DependenciesContainer(Object):
|
class DependenciesContainer(Object):
|
||||||
def __init__(self, **dependencies: Provider) -> None: ...
|
def __init__(self, **dependencies: Provider) -> None: ...
|
||||||
def __getattr__(self, name: str) -> Provider: ...
|
def __getattr__(self, name: str) -> Provider: ...
|
||||||
|
@ -156,12 +162,18 @@ class DependenciesContainer(Object):
|
||||||
def parent_name(self) -> Optional[str]: ...
|
def parent_name(self) -> Optional[str]: ...
|
||||||
def assign_parent(self, parent: ProviderParent) -> None: ...
|
def assign_parent(self, parent: ProviderParent) -> None: ...
|
||||||
|
|
||||||
|
|
||||||
class Callable(Provider[T]):
|
class Callable(Provider[T]):
|
||||||
def __init__(self, provides: Optional[Union[_Callable[..., T], str]] = None, *args: Injection, **kwargs: Injection) -> None: ...
|
def __init__(
|
||||||
|
self,
|
||||||
|
provides: Optional[Union[_Callable[..., T], str]] = None,
|
||||||
|
*args: Injection,
|
||||||
|
**kwargs: Injection,
|
||||||
|
) -> None: ...
|
||||||
@property
|
@property
|
||||||
def provides(self) -> Optional[_Callable[..., T]]: ...
|
def provides(self) -> Optional[_Callable[..., T]]: ...
|
||||||
def set_provides(self, provides: Optional[Union[_Callable[..., T], str]]) -> Callable[T]: ...
|
def set_provides(
|
||||||
|
self, provides: Optional[Union[_Callable[..., T], str]]
|
||||||
|
) -> Callable[T]: ...
|
||||||
@property
|
@property
|
||||||
def args(self) -> Tuple[Injection]: ...
|
def args(self) -> Tuple[Injection]: ...
|
||||||
def add_args(self, *args: Injection) -> Callable[T]: ...
|
def add_args(self, *args: Injection) -> Callable[T]: ...
|
||||||
|
@ -173,32 +185,23 @@ class Callable(Provider[T]):
|
||||||
def set_kwargs(self, **kwargs: Injection) -> Callable[T]: ...
|
def set_kwargs(self, **kwargs: Injection) -> Callable[T]: ...
|
||||||
def clear_kwargs(self) -> Callable[T]: ...
|
def clear_kwargs(self) -> Callable[T]: ...
|
||||||
|
|
||||||
|
|
||||||
class DelegatedCallable(Callable[T]): ...
|
class DelegatedCallable(Callable[T]): ...
|
||||||
|
|
||||||
|
|
||||||
class AbstractCallable(Callable[T]):
|
class AbstractCallable(Callable[T]):
|
||||||
def override(self, provider: Callable) -> OverridingContext[P]: ...
|
def override(self, provider: Callable) -> OverridingContext[P]: ...
|
||||||
|
|
||||||
|
|
||||||
class CallableDelegate(Delegate):
|
class CallableDelegate(Delegate):
|
||||||
def __init__(self, callable: Callable) -> None: ...
|
def __init__(self, callable: Callable) -> None: ...
|
||||||
|
|
||||||
|
|
||||||
class Coroutine(Callable[T]): ...
|
class Coroutine(Callable[T]): ...
|
||||||
|
|
||||||
|
|
||||||
class DelegatedCoroutine(Coroutine[T]): ...
|
class DelegatedCoroutine(Coroutine[T]): ...
|
||||||
|
|
||||||
|
|
||||||
class AbstractCoroutine(Coroutine[T]):
|
class AbstractCoroutine(Coroutine[T]):
|
||||||
def override(self, provider: Coroutine) -> OverridingContext[P]: ...
|
def override(self, provider: Coroutine) -> OverridingContext[P]: ...
|
||||||
|
|
||||||
|
|
||||||
class CoroutineDelegate(Delegate):
|
class CoroutineDelegate(Delegate):
|
||||||
def __init__(self, coroutine: Coroutine) -> None: ...
|
def __init__(self, coroutine: Coroutine) -> None: ...
|
||||||
|
|
||||||
|
|
||||||
class ConfigurationOption(Provider[Any]):
|
class ConfigurationOption(Provider[Any]):
|
||||||
UNDEFINED: object
|
UNDEFINED: object
|
||||||
def __init__(self, name: Tuple[str], root: Configuration) -> None: ...
|
def __init__(self, name: Tuple[str], root: Configuration) -> None: ...
|
||||||
|
@ -212,89 +215,137 @@ class ConfigurationOption(Provider[Any]):
|
||||||
def get_name_segments(self) -> Tuple[Union[str, Provider]]: ...
|
def get_name_segments(self) -> Tuple[Union[str, Provider]]: ...
|
||||||
def as_int(self) -> TypedConfigurationOption[int]: ...
|
def as_int(self) -> TypedConfigurationOption[int]: ...
|
||||||
def as_float(self) -> TypedConfigurationOption[float]: ...
|
def as_float(self) -> TypedConfigurationOption[float]: ...
|
||||||
def as_(self, callback: _Callable[..., T], *args: Injection, **kwargs: Injection) -> TypedConfigurationOption[T]: ...
|
def as_(
|
||||||
|
self, callback: _Callable[..., T], *args: Injection, **kwargs: Injection
|
||||||
|
) -> TypedConfigurationOption[T]: ...
|
||||||
def required(self) -> ConfigurationOption: ...
|
def required(self) -> ConfigurationOption: ...
|
||||||
def is_required(self) -> bool: ...
|
def is_required(self) -> bool: ...
|
||||||
def update(self, value: Any) -> None: ...
|
def update(self, value: Any) -> None: ...
|
||||||
def from_ini(self, filepath: Union[Path, str], required: bool = False, envs_required: bool = False) -> None: ...
|
def from_ini(
|
||||||
def from_yaml(self, filepath: Union[Path, str], required: bool = False, loader: Optional[Any] = None, envs_required: bool = False) -> None: ...
|
self,
|
||||||
def from_json(self, filepath: Union[Path, str], required: bool = False, envs_required: bool = False) -> None: ...
|
filepath: Union[Path, str],
|
||||||
def from_pydantic(self, settings: PydanticSettings, required: bool = False, **kwargs: Any) -> None: ...
|
required: bool = False,
|
||||||
|
envs_required: Optional[bool] = False,
|
||||||
|
) -> None: ...
|
||||||
|
def from_yaml(
|
||||||
|
self,
|
||||||
|
filepath: Union[Path, str],
|
||||||
|
required: bool = False,
|
||||||
|
loader: Optional[Any] = None,
|
||||||
|
envs_required: Optional[bool] = False,
|
||||||
|
) -> None: ...
|
||||||
|
def from_json(
|
||||||
|
self,
|
||||||
|
filepath: Union[Path, str],
|
||||||
|
required: bool = False,
|
||||||
|
envs_required: Optional[bool] = False,
|
||||||
|
) -> None: ...
|
||||||
|
def from_pydantic(
|
||||||
|
self, settings: PydanticSettings, required: bool = False, **kwargs: Any
|
||||||
|
) -> None: ...
|
||||||
def from_dict(self, options: _Dict[str, Any], required: bool = False) -> None: ...
|
def from_dict(self, options: _Dict[str, Any], required: bool = False) -> None: ...
|
||||||
def from_env(self, name: str, default: Optional[Any] = None, required: bool = False, as_: Optional[_Callable[..., Any]] = None) -> None: ...
|
def from_env(
|
||||||
|
self,
|
||||||
|
name: str,
|
||||||
|
default: Optional[Any] = None,
|
||||||
|
required: bool = False,
|
||||||
|
as_: Optional[_Callable[..., Any]] = None,
|
||||||
|
) -> None: ...
|
||||||
def from_value(self, value: Any) -> None: ...
|
def from_value(self, value: Any) -> None: ...
|
||||||
|
|
||||||
|
|
||||||
class TypedConfigurationOption(Callable[T]):
|
class TypedConfigurationOption(Callable[T]):
|
||||||
@property
|
@property
|
||||||
def option(self) -> ConfigurationOption: ...
|
def option(self) -> ConfigurationOption: ...
|
||||||
|
|
||||||
|
|
||||||
class Configuration(Object[Any]):
|
class Configuration(Object[Any]):
|
||||||
DEFAULT_NAME: str = "config"
|
DEFAULT_NAME: str = "config"
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
name: str = DEFAULT_NAME,
|
name: str = DEFAULT_NAME,
|
||||||
default: Optional[Any] = None,
|
default: Optional[Any] = None,
|
||||||
*,
|
*,
|
||||||
strict: bool = False,
|
strict: bool = False,
|
||||||
ini_files: Optional[_Iterable[Union[Path, str]]] = None,
|
ini_files: Optional[_Iterable[Union[Path, str]]] = None,
|
||||||
yaml_files: Optional[_Iterable[Union[Path, str]]] = None,
|
yaml_files: Optional[_Iterable[Union[Path, str]]] = None,
|
||||||
json_files: Optional[_Iterable[Union[Path, str]]] = None,
|
json_files: Optional[_Iterable[Union[Path, str]]] = None,
|
||||||
pydantic_settings: Optional[_Iterable[PydanticSettings]] = None,
|
pydantic_settings: Optional[_Iterable[PydanticSettings]] = None,
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def __enter__(self) -> Configuration : ...
|
def __enter__(self) -> Configuration: ...
|
||||||
def __exit__(self, *exc_info: Any) -> None: ...
|
def __exit__(self, *exc_info: Any) -> None: ...
|
||||||
def __getattr__(self, item: str) -> ConfigurationOption: ...
|
def __getattr__(self, item: str) -> ConfigurationOption: ...
|
||||||
def __getitem__(self, item: Union[str, Provider]) -> ConfigurationOption: ...
|
def __getitem__(self, item: Union[str, Provider]) -> ConfigurationOption: ...
|
||||||
|
|
||||||
def get_name(self) -> str: ...
|
def get_name(self) -> str: ...
|
||||||
def set_name(self, name: str) -> Configuration: ...
|
def set_name(self, name: str) -> Configuration: ...
|
||||||
|
|
||||||
def get_default(self) -> _Dict[Any, Any]: ...
|
def get_default(self) -> _Dict[Any, Any]: ...
|
||||||
def set_default(self, default: _Dict[Any, Any]): ...
|
def set_default(self, default: _Dict[Any, Any]): ...
|
||||||
|
|
||||||
def get_strict(self) -> bool: ...
|
def get_strict(self) -> bool: ...
|
||||||
def set_strict(self, strict: bool) -> Configuration: ...
|
def set_strict(self, strict: bool) -> Configuration: ...
|
||||||
|
|
||||||
def get_children(self) -> _Dict[str, ConfigurationOption]: ...
|
def get_children(self) -> _Dict[str, ConfigurationOption]: ...
|
||||||
def set_children(self, children: _Dict[str, ConfigurationOption]) -> Configuration: ...
|
def set_children(
|
||||||
|
self, children: _Dict[str, ConfigurationOption]
|
||||||
|
) -> Configuration: ...
|
||||||
def get_ini_files(self) -> _List[Union[Path, str]]: ...
|
def get_ini_files(self) -> _List[Union[Path, str]]: ...
|
||||||
def set_ini_files(self, files: _Iterable[Union[Path, str]]) -> Configuration: ...
|
def set_ini_files(self, files: _Iterable[Union[Path, str]]) -> Configuration: ...
|
||||||
|
|
||||||
def get_yaml_files(self) -> _List[Union[Path, str]]: ...
|
def get_yaml_files(self) -> _List[Union[Path, str]]: ...
|
||||||
def set_yaml_files(self, files: _Iterable[Union[Path, str]]) -> Configuration: ...
|
def set_yaml_files(self, files: _Iterable[Union[Path, str]]) -> Configuration: ...
|
||||||
|
|
||||||
def get_json_files(self) -> _List[Union[Path, str]]: ...
|
def get_json_files(self) -> _List[Union[Path, str]]: ...
|
||||||
def set_json_files(self, files: _Iterable[Union[Path, str]]) -> Configuration: ...
|
def set_json_files(self, files: _Iterable[Union[Path, str]]) -> Configuration: ...
|
||||||
|
|
||||||
def get_pydantic_settings(self) -> _List[PydanticSettings]: ...
|
def get_pydantic_settings(self) -> _List[PydanticSettings]: ...
|
||||||
def set_pydantic_settings(self, settings: _Iterable[PydanticSettings]) -> Configuration: ...
|
def set_pydantic_settings(
|
||||||
|
self, settings: _Iterable[PydanticSettings]
|
||||||
|
) -> Configuration: ...
|
||||||
def load(self, required: bool = False, envs_required: bool = False) -> None: ...
|
def load(self, required: bool = False, envs_required: bool = False) -> None: ...
|
||||||
|
|
||||||
def get(self, selector: str) -> Any: ...
|
def get(self, selector: str) -> Any: ...
|
||||||
def set(self, selector: str, value: Any) -> OverridingContext[P]: ...
|
def set(self, selector: str, value: Any) -> OverridingContext[P]: ...
|
||||||
def reset_cache(self) -> None: ...
|
def reset_cache(self) -> None: ...
|
||||||
def update(self, value: Any) -> None: ...
|
def update(self, value: Any) -> None: ...
|
||||||
def from_ini(self, filepath: Union[Path, str], required: bool = False, envs_required: bool = False) -> None: ...
|
def from_ini(
|
||||||
def from_yaml(self, filepath: Union[Path, str], required: bool = False, loader: Optional[Any] = None, envs_required: bool = False) -> None: ...
|
self,
|
||||||
def from_json(self, filepath: Union[Path, str], required: bool = False, envs_required: bool = False) -> None: ...
|
filepath: Union[Path, str],
|
||||||
def from_pydantic(self, settings: PydanticSettings, required: bool = False, **kwargs: Any) -> None: ...
|
required: bool = False,
|
||||||
|
envs_required: bool = False,
|
||||||
|
) -> None: ...
|
||||||
|
def from_yaml(
|
||||||
|
self,
|
||||||
|
filepath: Union[Path, str],
|
||||||
|
required: bool = False,
|
||||||
|
loader: Optional[Any] = None,
|
||||||
|
envs_required: bool = False,
|
||||||
|
) -> None: ...
|
||||||
|
def from_json(
|
||||||
|
self,
|
||||||
|
filepath: Union[Path, str],
|
||||||
|
required: bool = False,
|
||||||
|
envs_required: bool = False,
|
||||||
|
) -> None: ...
|
||||||
|
def from_pydantic(
|
||||||
|
self, settings: PydanticSettings, required: bool = False, **kwargs: Any
|
||||||
|
) -> None: ...
|
||||||
def from_dict(self, options: _Dict[str, Any], required: bool = False) -> None: ...
|
def from_dict(self, options: _Dict[str, Any], required: bool = False) -> None: ...
|
||||||
def from_env(self, name: str, default: Optional[Any] = None, required: bool = False, as_: Optional[_Callable[..., Any]] = None) -> None: ...
|
def from_env(
|
||||||
|
self,
|
||||||
|
name: str,
|
||||||
|
default: Optional[Any] = None,
|
||||||
|
required: bool = False,
|
||||||
|
as_: Optional[_Callable[..., Any]] = None,
|
||||||
|
) -> None: ...
|
||||||
def from_value(self, value: Any) -> None: ...
|
def from_value(self, value: Any) -> None: ...
|
||||||
|
|
||||||
|
|
||||||
class Factory(Provider[T]):
|
class Factory(Provider[T]):
|
||||||
provided_type: Optional[Type]
|
provided_type: Optional[Type]
|
||||||
def __init__(self, provides: Optional[Union[_Callable[..., T], str]] = None, *args: Injection, **kwargs: Injection) -> None: ...
|
def __init__(
|
||||||
|
self,
|
||||||
|
provides: Optional[Union[_Callable[..., T], str]] = None,
|
||||||
|
*args: Injection,
|
||||||
|
**kwargs: Injection,
|
||||||
|
) -> None: ...
|
||||||
@property
|
@property
|
||||||
def cls(self) -> Type[T]: ...
|
def cls(self) -> Type[T]: ...
|
||||||
@property
|
@property
|
||||||
def provides(self) -> Optional[_Callable[..., T]]: ...
|
def provides(self) -> Optional[_Callable[..., T]]: ...
|
||||||
def set_provides(self, provides: Optional[Union[_Callable[..., T], str]]) -> Factory[T]: ...
|
def set_provides(
|
||||||
|
self, provides: Optional[Union[_Callable[..., T], str]]
|
||||||
|
) -> Factory[T]: ...
|
||||||
@property
|
@property
|
||||||
def args(self) -> Tuple[Injection]: ...
|
def args(self) -> Tuple[Injection]: ...
|
||||||
def add_args(self, *args: Injection) -> Factory[T]: ...
|
def add_args(self, *args: Injection) -> Factory[T]: ...
|
||||||
|
@ -311,33 +362,39 @@ class Factory(Provider[T]):
|
||||||
def set_attributes(self, **kwargs: Injection) -> Factory[T]: ...
|
def set_attributes(self, **kwargs: Injection) -> Factory[T]: ...
|
||||||
def clear_attributes(self) -> Factory[T]: ...
|
def clear_attributes(self) -> Factory[T]: ...
|
||||||
|
|
||||||
|
|
||||||
class DelegatedFactory(Factory[T]): ...
|
class DelegatedFactory(Factory[T]): ...
|
||||||
|
|
||||||
|
|
||||||
class AbstractFactory(Factory[T]):
|
class AbstractFactory(Factory[T]):
|
||||||
def override(self, provider: Factory) -> OverridingContext[P]: ...
|
def override(self, provider: Factory) -> OverridingContext[P]: ...
|
||||||
|
|
||||||
|
|
||||||
class FactoryDelegate(Delegate):
|
class FactoryDelegate(Delegate):
|
||||||
def __init__(self, factory: Factory): ...
|
def __init__(self, factory: Factory): ...
|
||||||
|
|
||||||
|
|
||||||
class FactoryAggregate(Aggregate[T]):
|
class FactoryAggregate(Aggregate[T]):
|
||||||
def __getattr__(self, provider_name: Any) -> Factory[T]: ...
|
def __getattr__(self, provider_name: Any) -> Factory[T]: ...
|
||||||
@property
|
@property
|
||||||
def factories(self) -> _Dict[Any, Factory[T]]: ...
|
def factories(self) -> _Dict[Any, Factory[T]]: ...
|
||||||
def set_factories(self, provider_dict: Optional[_Dict[Any, Factory[T]]] = None, **provider_kwargs: Factory[T]) -> FactoryAggregate[T]: ...
|
def set_factories(
|
||||||
|
self,
|
||||||
|
provider_dict: Optional[_Dict[Any, Factory[T]]] = None,
|
||||||
|
**provider_kwargs: Factory[T],
|
||||||
|
) -> FactoryAggregate[T]: ...
|
||||||
|
|
||||||
class BaseSingleton(Provider[T]):
|
class BaseSingleton(Provider[T]):
|
||||||
provided_type = Optional[Type]
|
provided_type = Optional[Type]
|
||||||
def __init__(self, provides: Optional[Union[_Callable[..., T], str]] = None, *args: Injection, **kwargs: Injection) -> None: ...
|
def __init__(
|
||||||
|
self,
|
||||||
|
provides: Optional[Union[_Callable[..., T], str]] = None,
|
||||||
|
*args: Injection,
|
||||||
|
**kwargs: Injection,
|
||||||
|
) -> None: ...
|
||||||
@property
|
@property
|
||||||
def cls(self) -> Type[T]: ...
|
def cls(self) -> Type[T]: ...
|
||||||
@property
|
@property
|
||||||
def provides(self) -> Optional[_Callable[..., T]]: ...
|
def provides(self) -> Optional[_Callable[..., T]]: ...
|
||||||
def set_provides(self, provides: Optional[Union[_Callable[..., T], str]]) -> BaseSingleton[T]: ...
|
def set_provides(
|
||||||
|
self, provides: Optional[Union[_Callable[..., T], str]]
|
||||||
|
) -> BaseSingleton[T]: ...
|
||||||
@property
|
@property
|
||||||
def args(self) -> Tuple[Injection]: ...
|
def args(self) -> Tuple[Injection]: ...
|
||||||
def add_args(self, *args: Injection) -> BaseSingleton[T]: ...
|
def add_args(self, *args: Injection) -> BaseSingleton[T]: ...
|
||||||
|
@ -356,36 +413,20 @@ class BaseSingleton(Provider[T]):
|
||||||
def reset(self) -> SingletonResetContext[BS]: ...
|
def reset(self) -> SingletonResetContext[BS]: ...
|
||||||
def full_reset(self) -> SingletonFullResetContext[BS]: ...
|
def full_reset(self) -> SingletonFullResetContext[BS]: ...
|
||||||
|
|
||||||
|
|
||||||
class Singleton(BaseSingleton[T]): ...
|
class Singleton(BaseSingleton[T]): ...
|
||||||
|
|
||||||
|
|
||||||
class DelegatedSingleton(Singleton[T]): ...
|
class DelegatedSingleton(Singleton[T]): ...
|
||||||
|
|
||||||
|
|
||||||
class ThreadSafeSingleton(Singleton[T]): ...
|
class ThreadSafeSingleton(Singleton[T]): ...
|
||||||
|
|
||||||
|
|
||||||
class DelegatedThreadSafeSingleton(ThreadSafeSingleton[T]): ...
|
class DelegatedThreadSafeSingleton(ThreadSafeSingleton[T]): ...
|
||||||
|
|
||||||
|
|
||||||
class ThreadLocalSingleton(BaseSingleton[T]): ...
|
class ThreadLocalSingleton(BaseSingleton[T]): ...
|
||||||
|
|
||||||
|
|
||||||
class ContextLocalSingleton(BaseSingleton[T]): ...
|
class ContextLocalSingleton(BaseSingleton[T]): ...
|
||||||
|
|
||||||
|
|
||||||
class DelegatedThreadLocalSingleton(ThreadLocalSingleton[T]): ...
|
class DelegatedThreadLocalSingleton(ThreadLocalSingleton[T]): ...
|
||||||
|
|
||||||
|
|
||||||
class AbstractSingleton(BaseSingleton[T]):
|
class AbstractSingleton(BaseSingleton[T]):
|
||||||
def override(self, provider: BaseSingleton) -> OverridingContext[P]: ...
|
def override(self, provider: BaseSingleton) -> OverridingContext[P]: ...
|
||||||
|
|
||||||
|
|
||||||
class SingletonDelegate(Delegate):
|
class SingletonDelegate(Delegate):
|
||||||
def __init__(self, singleton: BaseSingleton): ...
|
def __init__(self, singleton: BaseSingleton): ...
|
||||||
|
|
||||||
|
|
||||||
class List(Provider[_List]):
|
class List(Provider[_List]):
|
||||||
def __init__(self, *args: Injection): ...
|
def __init__(self, *args: Injection): ...
|
||||||
@property
|
@property
|
||||||
|
@ -394,29 +435,63 @@ class List(Provider[_List]):
|
||||||
def set_args(self, *args: Injection) -> List[T]: ...
|
def set_args(self, *args: Injection) -> List[T]: ...
|
||||||
def clear_args(self) -> List[T]: ...
|
def clear_args(self) -> List[T]: ...
|
||||||
|
|
||||||
|
|
||||||
class Dict(Provider[_Dict]):
|
class Dict(Provider[_Dict]):
|
||||||
def __init__(self, dict_: Optional[_Dict[Any, Injection]] = None, **kwargs: Injection): ...
|
def __init__(
|
||||||
|
self, dict_: Optional[_Dict[Any, Injection]] = None, **kwargs: Injection
|
||||||
|
): ...
|
||||||
@property
|
@property
|
||||||
def kwargs(self) -> _Dict[Any, Injection]: ...
|
def kwargs(self) -> _Dict[Any, Injection]: ...
|
||||||
def add_kwargs(self, dict_: Optional[_Dict[Any, Injection]] = None, **kwargs: Injection) -> Dict: ...
|
def add_kwargs(
|
||||||
def set_kwargs(self, dict_: Optional[_Dict[Any, Injection]] = None, **kwargs: Injection) -> Dict: ...
|
self, dict_: Optional[_Dict[Any, Injection]] = None, **kwargs: Injection
|
||||||
|
) -> Dict: ...
|
||||||
|
def set_kwargs(
|
||||||
|
self, dict_: Optional[_Dict[Any, Injection]] = None, **kwargs: Injection
|
||||||
|
) -> Dict: ...
|
||||||
def clear_kwargs(self) -> Dict: ...
|
def clear_kwargs(self) -> Dict: ...
|
||||||
|
|
||||||
|
|
||||||
class Resource(Provider[T]):
|
class Resource(Provider[T]):
|
||||||
@overload
|
@overload
|
||||||
def __init__(self, provides: Optional[Type[resources.Resource[T]]] = None, *args: Injection, **kwargs: Injection) -> None: ...
|
def __init__(
|
||||||
|
self,
|
||||||
|
provides: Optional[Type[resources.Resource[T]]] = None,
|
||||||
|
*args: Injection,
|
||||||
|
**kwargs: Injection,
|
||||||
|
) -> None: ...
|
||||||
@overload
|
@overload
|
||||||
def __init__(self, provides: Optional[Type[resources.AsyncResource[T]]] = None, *args: Injection, **kwargs: Injection) -> None: ...
|
def __init__(
|
||||||
|
self,
|
||||||
|
provides: Optional[Type[resources.AsyncResource[T]]] = None,
|
||||||
|
*args: Injection,
|
||||||
|
**kwargs: Injection,
|
||||||
|
) -> None: ...
|
||||||
@overload
|
@overload
|
||||||
def __init__(self, provides: Optional[_Callable[..., _Iterator[T]]] = None, *args: Injection, **kwargs: Injection) -> None: ...
|
def __init__(
|
||||||
|
self,
|
||||||
|
provides: Optional[_Callable[..., _Iterator[T]]] = None,
|
||||||
|
*args: Injection,
|
||||||
|
**kwargs: Injection,
|
||||||
|
) -> None: ...
|
||||||
@overload
|
@overload
|
||||||
def __init__(self, provides: Optional[_Callable[..., _AsyncIterator[T]]] = None, *args: Injection, **kwargs: Injection) -> None: ...
|
def __init__(
|
||||||
|
self,
|
||||||
|
provides: Optional[_Callable[..., _AsyncIterator[T]]] = None,
|
||||||
|
*args: Injection,
|
||||||
|
**kwargs: Injection,
|
||||||
|
) -> None: ...
|
||||||
@overload
|
@overload
|
||||||
def __init__(self, provides: Optional[_Callable[..., _Coroutine[Injection, Injection, T]]] = None, *args: Injection, **kwargs: Injection) -> None: ...
|
def __init__(
|
||||||
|
self,
|
||||||
|
provides: Optional[_Callable[..., _Coroutine[Injection, Injection, T]]] = None,
|
||||||
|
*args: Injection,
|
||||||
|
**kwargs: Injection,
|
||||||
|
) -> None: ...
|
||||||
@overload
|
@overload
|
||||||
def __init__(self, provides: Optional[Union[_Callable[..., T], str]] = None, *args: Injection, **kwargs: Injection) -> None: ...
|
def __init__(
|
||||||
|
self,
|
||||||
|
provides: Optional[Union[_Callable[..., T], str]] = None,
|
||||||
|
*args: Injection,
|
||||||
|
**kwargs: Injection,
|
||||||
|
) -> None: ...
|
||||||
@property
|
@property
|
||||||
def provides(self) -> Optional[_Callable[..., Any]]: ...
|
def provides(self) -> Optional[_Callable[..., Any]]: ...
|
||||||
def set_provides(self, provides: Optional[Any]) -> Resource[T]: ...
|
def set_provides(self, provides: Optional[Any]) -> Resource[T]: ...
|
||||||
|
@ -435,9 +510,13 @@ class Resource(Provider[T]):
|
||||||
def init(self) -> Optional[Awaitable[T]]: ...
|
def init(self) -> Optional[Awaitable[T]]: ...
|
||||||
def shutdown(self) -> Optional[Awaitable]: ...
|
def shutdown(self) -> Optional[Awaitable]: ...
|
||||||
|
|
||||||
|
|
||||||
class Container(Provider[T]):
|
class Container(Provider[T]):
|
||||||
def __init__(self, container_cls: Type[T], container: Optional[T] = None, **overriding_providers: Union[Provider, Any]) -> None: ...
|
def __init__(
|
||||||
|
self,
|
||||||
|
container_cls: Type[T],
|
||||||
|
container: Optional[T] = None,
|
||||||
|
**overriding_providers: Union[Provider, Any],
|
||||||
|
) -> None: ...
|
||||||
def __getattr__(self, name: str) -> Provider: ...
|
def __getattr__(self, name: str) -> Provider: ...
|
||||||
@property
|
@property
|
||||||
def container(self) -> T: ...
|
def container(self) -> T: ...
|
||||||
|
@ -448,50 +527,51 @@ class Container(Provider[T]):
|
||||||
def parent_name(self) -> Optional[str]: ...
|
def parent_name(self) -> Optional[str]: ...
|
||||||
def assign_parent(self, parent: ProviderParent) -> None: ...
|
def assign_parent(self, parent: ProviderParent) -> None: ...
|
||||||
|
|
||||||
|
|
||||||
class Selector(Provider[Any]):
|
class Selector(Provider[Any]):
|
||||||
def __init__(self, selector: Optional[_Callable[..., Any]] = None, **providers: Provider): ...
|
def __init__(
|
||||||
|
self, selector: Optional[_Callable[..., Any]] = None, **providers: Provider
|
||||||
|
): ...
|
||||||
def __getattr__(self, name: str) -> Provider: ...
|
def __getattr__(self, name: str) -> Provider: ...
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def selector(self) -> Optional[_Callable[..., Any]]: ...
|
def selector(self) -> Optional[_Callable[..., Any]]: ...
|
||||||
def set_selector(self, selector: Optional[_Callable[..., Any]]) -> Selector: ...
|
def set_selector(self, selector: Optional[_Callable[..., Any]]) -> Selector: ...
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def providers(self) -> _Dict[str, Provider]: ...
|
def providers(self) -> _Dict[str, Provider]: ...
|
||||||
def set_providers(self, **providers: Provider) -> Selector: ...
|
def set_providers(self, **providers: Provider) -> Selector: ...
|
||||||
|
|
||||||
|
|
||||||
class ProvidedInstanceFluentInterface:
|
class ProvidedInstanceFluentInterface:
|
||||||
def __getattr__(self, item: Any) -> AttributeGetter: ...
|
def __getattr__(self, item: Any) -> AttributeGetter: ...
|
||||||
def __getitem__(self, item: Any) -> ItemGetter: ...
|
def __getitem__(self, item: Any) -> ItemGetter: ...
|
||||||
def call(self, *args: Injection, **kwargs: Injection) -> MethodCaller: ...
|
def call(self, *args: Injection, **kwargs: Injection) -> MethodCaller: ...
|
||||||
@property
|
@property
|
||||||
def provides(self) -> Optional[Provider]: ...
|
def provides(self) -> Optional[Provider]: ...
|
||||||
def set_provides(self, provides: Optional[Provider]) -> ProvidedInstanceFluentInterface: ...
|
def set_provides(
|
||||||
|
self, provides: Optional[Provider]
|
||||||
|
) -> ProvidedInstanceFluentInterface: ...
|
||||||
|
|
||||||
class ProvidedInstance(Provider, ProvidedInstanceFluentInterface):
|
class ProvidedInstance(Provider, ProvidedInstanceFluentInterface):
|
||||||
def __init__(self, provides: Optional[Provider] = None) -> None: ...
|
def __init__(self, provides: Optional[Provider] = None) -> None: ...
|
||||||
|
|
||||||
|
|
||||||
class AttributeGetter(Provider, ProvidedInstanceFluentInterface):
|
class AttributeGetter(Provider, ProvidedInstanceFluentInterface):
|
||||||
def __init__(self, provides: Optional[Provider] = None, name: Optional[str] = None) -> None: ...
|
def __init__(
|
||||||
|
self, provides: Optional[Provider] = None, name: Optional[str] = None
|
||||||
|
) -> None: ...
|
||||||
@property
|
@property
|
||||||
def name(self) -> Optional[str]: ...
|
def name(self) -> Optional[str]: ...
|
||||||
def set_name(self, name: Optional[str]) -> ProvidedInstanceFluentInterface: ...
|
def set_name(self, name: Optional[str]) -> ProvidedInstanceFluentInterface: ...
|
||||||
|
|
||||||
|
|
||||||
class ItemGetter(Provider, ProvidedInstanceFluentInterface):
|
class ItemGetter(Provider, ProvidedInstanceFluentInterface):
|
||||||
def __init__(self, provides: Optional[Provider] = None, name: Optional[str] = None) -> None: ...
|
def __init__(
|
||||||
|
self, provides: Optional[Provider] = None, name: Optional[str] = None
|
||||||
|
) -> None: ...
|
||||||
@property
|
@property
|
||||||
def name(self) -> Optional[str]: ...
|
def name(self) -> Optional[str]: ...
|
||||||
def set_name(self, name: Optional[str]) -> ProvidedInstanceFluentInterface: ...
|
def set_name(self, name: Optional[str]) -> ProvidedInstanceFluentInterface: ...
|
||||||
|
|
||||||
|
|
||||||
class MethodCaller(Provider, ProvidedInstanceFluentInterface):
|
class MethodCaller(Provider, ProvidedInstanceFluentInterface):
|
||||||
def __init__(self, provides: Optional[Provider] = None, *args: Injection, **kwargs: Injection) -> None: ...
|
def __init__(
|
||||||
|
self, provides: Optional[Provider] = None, *args: Injection, **kwargs: Injection
|
||||||
|
) -> None: ...
|
||||||
|
|
||||||
class OverridingContext(Generic[T]):
|
class OverridingContext(Generic[T]):
|
||||||
def __init__(self, overridden: Provider, overriding: Provider): ...
|
def __init__(self, overridden: Provider, overriding: Provider): ...
|
||||||
|
@ -500,47 +580,39 @@ class OverridingContext(Generic[T]):
|
||||||
pass
|
pass
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
class BaseSingletonResetContext(Generic[T]):
|
class BaseSingletonResetContext(Generic[T]):
|
||||||
def __init__(self, provider: T): ...
|
def __init__(self, provider: T): ...
|
||||||
def __enter__(self) -> T: ...
|
def __enter__(self) -> T: ...
|
||||||
def __exit__(self, *_: Any) -> None: ...
|
def __exit__(self, *_: Any) -> None: ...
|
||||||
|
|
||||||
|
class SingletonResetContext(BaseSingletonResetContext): ...
|
||||||
class SingletonResetContext(BaseSingletonResetContext):
|
class SingletonFullResetContext(BaseSingletonResetContext): ...
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
class SingletonFullResetContext(BaseSingletonResetContext):
|
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
CHILD_PROVIDERS: Tuple[Provider]
|
CHILD_PROVIDERS: Tuple[Provider]
|
||||||
|
|
||||||
|
|
||||||
def is_provider(instance: Any) -> bool: ...
|
def is_provider(instance: Any) -> bool: ...
|
||||||
|
|
||||||
|
|
||||||
def ensure_is_provider(instance: Any) -> Provider: ...
|
def ensure_is_provider(instance: Any) -> Provider: ...
|
||||||
|
|
||||||
|
|
||||||
def is_delegated(instance: Any) -> bool: ...
|
def is_delegated(instance: Any) -> bool: ...
|
||||||
|
|
||||||
|
|
||||||
def represent_provider(provider: Provider, provides: Any) -> str: ...
|
def represent_provider(provider: Provider, provides: Any) -> str: ...
|
||||||
|
def deepcopy(instance: Any, memo: Optional[_Dict[Any, Any]] = None) -> Any: ...
|
||||||
|
def deepcopy_args(
|
||||||
def deepcopy(instance: Any, memo: Optional[_Dict[Any, Any]] = None): Any: ...
|
provider: Provider[Any],
|
||||||
|
args: Tuple[Any, ...],
|
||||||
|
memo: Optional[_Dict[int, Any]] = None,
|
||||||
|
) -> Tuple[Any, ...]: ...
|
||||||
|
def deepcopy_kwargs(
|
||||||
|
provider: Provider[Any],
|
||||||
|
kwargs: _Dict[str, Any],
|
||||||
|
memo: Optional[_Dict[int, Any]] = None,
|
||||||
|
) -> Dict[str, Any]: ...
|
||||||
def merge_dicts(dict1: _Dict[Any, Any], dict2: _Dict[Any, Any]) -> _Dict[Any, Any]: ...
|
def merge_dicts(dict1: _Dict[Any, Any], dict2: _Dict[Any, Any]) -> _Dict[Any, Any]: ...
|
||||||
|
def traverse(
|
||||||
|
*providers: Provider, types: Optional[_Iterable[Type]] = None
|
||||||
def traverse(*providers: Provider, types: Optional[_Iterable[Type]]=None) -> _Iterator[Provider]: ...
|
) -> _Iterator[Provider]: ...
|
||||||
|
|
||||||
|
|
||||||
if yaml:
|
if yaml:
|
||||||
class YamlLoader(yaml.SafeLoader): ...
|
class YamlLoader(yaml.SafeLoader): ...
|
||||||
|
|
||||||
else:
|
else:
|
||||||
class YamlLoader: ...
|
class YamlLoader: ...
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,27 +1,54 @@
|
||||||
"""Resources module."""
|
"""Resources module."""
|
||||||
|
|
||||||
import abc
|
from abc import ABCMeta, abstractmethod
|
||||||
from typing import TypeVar, Generic, Optional
|
from typing import Any, ClassVar, Generic, Optional, Tuple, TypeVar
|
||||||
|
|
||||||
|
|
||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
|
||||||
class Resource(Generic[T], metaclass=abc.ABCMeta):
|
class Resource(Generic[T], metaclass=ABCMeta):
|
||||||
|
__slots__: ClassVar[Tuple[str, ...]] = ("args", "kwargs", "obj")
|
||||||
|
|
||||||
@abc.abstractmethod
|
obj: Optional[T]
|
||||||
def init(self, *args, **kwargs) -> Optional[T]:
|
|
||||||
...
|
|
||||||
|
|
||||||
def shutdown(self, resource: Optional[T]) -> None:
|
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||||
...
|
self.args = args
|
||||||
|
self.kwargs = kwargs
|
||||||
|
self.obj = None
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def init(self, *args: Any, **kwargs: Any) -> Optional[T]: ...
|
||||||
|
|
||||||
|
def shutdown(self, resource: Optional[T]) -> None: ...
|
||||||
|
|
||||||
|
def __enter__(self) -> Optional[T]:
|
||||||
|
self.obj = obj = self.init(*self.args, **self.kwargs)
|
||||||
|
return obj
|
||||||
|
|
||||||
|
def __exit__(self, *exc_info: Any) -> None:
|
||||||
|
self.shutdown(self.obj)
|
||||||
|
self.obj = None
|
||||||
|
|
||||||
|
|
||||||
class AsyncResource(Generic[T], metaclass=abc.ABCMeta):
|
class AsyncResource(Generic[T], metaclass=ABCMeta):
|
||||||
|
__slots__: ClassVar[Tuple[str, ...]] = ("args", "kwargs", "obj")
|
||||||
|
|
||||||
@abc.abstractmethod
|
obj: Optional[T]
|
||||||
async def init(self, *args, **kwargs) -> Optional[T]:
|
|
||||||
...
|
|
||||||
|
|
||||||
async def shutdown(self, resource: Optional[T]) -> None:
|
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||||
...
|
self.args = args
|
||||||
|
self.kwargs = kwargs
|
||||||
|
self.obj = None
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
async def init(self, *args: Any, **kwargs: Any) -> Optional[T]: ...
|
||||||
|
|
||||||
|
async def shutdown(self, resource: Optional[T]) -> None: ...
|
||||||
|
|
||||||
|
async def __aenter__(self) -> Optional[T]:
|
||||||
|
self.obj = obj = await self.init(*self.args, **self.kwargs)
|
||||||
|
return obj
|
||||||
|
|
||||||
|
async def __aexit__(self, *exc_info: Any) -> None:
|
||||||
|
await self.shutdown(self.obj)
|
||||||
|
self.obj = None
|
||||||
|
|
|
@ -27,9 +27,9 @@ class SchemaProcessorV1:
|
||||||
return self._container.providers
|
return self._container.providers
|
||||||
|
|
||||||
def _create_providers(
|
def _create_providers(
|
||||||
self,
|
self,
|
||||||
provider_schema: ProviderSchema,
|
provider_schema: ProviderSchema,
|
||||||
container: Optional[containers.Container] = None,
|
container: Optional[containers.Container] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
if container is None:
|
if container is None:
|
||||||
container = self._container
|
container = self._container
|
||||||
|
@ -57,9 +57,9 @@ class SchemaProcessorV1:
|
||||||
self._create_providers(provider_schema=data, container=provider)
|
self._create_providers(provider_schema=data, container=provider)
|
||||||
|
|
||||||
def _setup_injections( # noqa: C901
|
def _setup_injections( # noqa: C901
|
||||||
self,
|
self,
|
||||||
provider_schema: ProviderSchema,
|
provider_schema: ProviderSchema,
|
||||||
container: Optional[containers.Container] = None,
|
container: Optional[containers.Container] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
if container is None:
|
if container is None:
|
||||||
container = self._container
|
container = self._container
|
||||||
|
@ -72,7 +72,7 @@ class SchemaProcessorV1:
|
||||||
provides = data.get("provides")
|
provides = data.get("provides")
|
||||||
if provides:
|
if provides:
|
||||||
if isinstance(provides, str) and provides.startswith("container."):
|
if isinstance(provides, str) and provides.startswith("container."):
|
||||||
provides = self._resolve_provider(provides[len("container."):])
|
provides = self._resolve_provider(provides[len("container.") :])
|
||||||
else:
|
else:
|
||||||
provides = _import_string(provides)
|
provides = _import_string(provides)
|
||||||
provider.set_provides(provides)
|
provider.set_provides(provides)
|
||||||
|
@ -83,7 +83,7 @@ class SchemaProcessorV1:
|
||||||
injection = None
|
injection = None
|
||||||
|
|
||||||
if isinstance(arg, str) and arg.startswith("container."):
|
if isinstance(arg, str) and arg.startswith("container."):
|
||||||
injection = self._resolve_provider(arg[len("container."):])
|
injection = self._resolve_provider(arg[len("container.") :])
|
||||||
|
|
||||||
# TODO: refactoring
|
# TODO: refactoring
|
||||||
if isinstance(arg, dict):
|
if isinstance(arg, dict):
|
||||||
|
@ -91,16 +91,23 @@ class SchemaProcessorV1:
|
||||||
provider_type = _get_provider_cls(arg.get("provider"))
|
provider_type = _get_provider_cls(arg.get("provider"))
|
||||||
provides = arg.get("provides")
|
provides = arg.get("provides")
|
||||||
if provides:
|
if provides:
|
||||||
if isinstance(provides, str) and provides.startswith("container."):
|
if isinstance(provides, str) and provides.startswith(
|
||||||
provides = self._resolve_provider(provides[len("container."):])
|
"container."
|
||||||
|
):
|
||||||
|
provides = self._resolve_provider(
|
||||||
|
provides[len("container.") :]
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
provides = _import_string(provides)
|
provides = _import_string(provides)
|
||||||
provider_args.append(provides)
|
provider_args.append(provides)
|
||||||
for provider_arg in arg.get("args", []):
|
for provider_arg in arg.get("args", []):
|
||||||
if isinstance(provider_arg, str) \
|
if isinstance(
|
||||||
and provider_arg.startswith("container."):
|
provider_arg, str
|
||||||
|
) and provider_arg.startswith("container."):
|
||||||
provider_args.append(
|
provider_args.append(
|
||||||
self._resolve_provider(provider_arg[len("container."):]),
|
self._resolve_provider(
|
||||||
|
provider_arg[len("container.") :]
|
||||||
|
),
|
||||||
)
|
)
|
||||||
injection = provider_type(*provider_args)
|
injection = provider_type(*provider_args)
|
||||||
|
|
||||||
|
@ -117,7 +124,7 @@ class SchemaProcessorV1:
|
||||||
injection = None
|
injection = None
|
||||||
|
|
||||||
if isinstance(arg, str) and arg.startswith("container."):
|
if isinstance(arg, str) and arg.startswith("container."):
|
||||||
injection = self._resolve_provider(arg[len("container."):])
|
injection = self._resolve_provider(arg[len("container.") :])
|
||||||
|
|
||||||
# TODO: refactoring
|
# TODO: refactoring
|
||||||
if isinstance(arg, dict):
|
if isinstance(arg, dict):
|
||||||
|
@ -125,16 +132,23 @@ class SchemaProcessorV1:
|
||||||
provider_type = _get_provider_cls(arg.get("provider"))
|
provider_type = _get_provider_cls(arg.get("provider"))
|
||||||
provides = arg.get("provides")
|
provides = arg.get("provides")
|
||||||
if provides:
|
if provides:
|
||||||
if isinstance(provides, str) and provides.startswith("container."):
|
if isinstance(provides, str) and provides.startswith(
|
||||||
provides = self._resolve_provider(provides[len("container."):])
|
"container."
|
||||||
|
):
|
||||||
|
provides = self._resolve_provider(
|
||||||
|
provides[len("container.") :]
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
provides = _import_string(provides)
|
provides = _import_string(provides)
|
||||||
provider_args.append(provides)
|
provider_args.append(provides)
|
||||||
for provider_arg in arg.get("args", []):
|
for provider_arg in arg.get("args", []):
|
||||||
if isinstance(provider_arg, str) \
|
if isinstance(
|
||||||
and provider_arg.startswith("container."):
|
provider_arg, str
|
||||||
|
) and provider_arg.startswith("container."):
|
||||||
provider_args.append(
|
provider_args.append(
|
||||||
self._resolve_provider(provider_arg[len("container."):]),
|
self._resolve_provider(
|
||||||
|
provider_arg[len("container.") :]
|
||||||
|
),
|
||||||
)
|
)
|
||||||
injection = provider_type(*provider_args)
|
injection = provider_type(*provider_args)
|
||||||
|
|
||||||
|
@ -158,7 +172,7 @@ class SchemaProcessorV1:
|
||||||
for segment in segments[1:]:
|
for segment in segments[1:]:
|
||||||
parentheses = ""
|
parentheses = ""
|
||||||
if "(" in segment and ")" in segment:
|
if "(" in segment and ")" in segment:
|
||||||
parentheses = segment[segment.find("("):segment.rfind(")")+1]
|
parentheses = segment[segment.find("(") : segment.rfind(")") + 1]
|
||||||
segment = segment.replace(parentheses, "")
|
segment = segment.replace(parentheses, "")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -190,10 +204,12 @@ def _get_provider_cls(provider_cls_name: str) -> Type[providers.Provider]:
|
||||||
if custom_provider_type:
|
if custom_provider_type:
|
||||||
return custom_provider_type
|
return custom_provider_type
|
||||||
|
|
||||||
raise SchemaError(f"Undefined provider class \"{provider_cls_name}\"")
|
raise SchemaError(f'Undefined provider class "{provider_cls_name}"')
|
||||||
|
|
||||||
|
|
||||||
def _fetch_provider_cls_from_std(provider_cls_name: str) -> Optional[Type[providers.Provider]]:
|
def _fetch_provider_cls_from_std(
|
||||||
|
provider_cls_name: str,
|
||||||
|
) -> Optional[Type[providers.Provider]]:
|
||||||
return getattr(providers, provider_cls_name, None)
|
return getattr(providers, provider_cls_name, None)
|
||||||
|
|
||||||
|
|
||||||
|
@ -201,12 +217,16 @@ def _import_provider_cls(provider_cls_name: str) -> Optional[Type[providers.Prov
|
||||||
try:
|
try:
|
||||||
cls = _import_string(provider_cls_name)
|
cls = _import_string(provider_cls_name)
|
||||||
except (ImportError, ValueError) as exception:
|
except (ImportError, ValueError) as exception:
|
||||||
raise SchemaError(f"Can not import provider \"{provider_cls_name}\"") from exception
|
raise SchemaError(
|
||||||
|
f'Can not import provider "{provider_cls_name}"'
|
||||||
|
) from exception
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
if isinstance(cls, type) and not issubclass(cls, providers.Provider):
|
if isinstance(cls, type) and not issubclass(cls, providers.Provider):
|
||||||
raise SchemaError(f"Provider class \"{cls}\" is not a subclass of providers base class")
|
raise SchemaError(
|
||||||
|
f'Provider class "{cls}" is not a subclass of providers base class'
|
||||||
|
)
|
||||||
return cls
|
return cls
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,34 +1,45 @@
|
||||||
"""Wiring module."""
|
"""Wiring module."""
|
||||||
|
|
||||||
import functools
|
import functools
|
||||||
import inspect
|
|
||||||
import importlib
|
import importlib
|
||||||
import importlib.machinery
|
import importlib.machinery
|
||||||
|
import inspect
|
||||||
import pkgutil
|
import pkgutil
|
||||||
import warnings
|
|
||||||
import sys
|
import sys
|
||||||
|
from contextlib import suppress
|
||||||
|
from inspect import isbuiltin, isclass
|
||||||
from types import ModuleType
|
from types import ModuleType
|
||||||
from typing import (
|
from typing import (
|
||||||
Optional,
|
TYPE_CHECKING,
|
||||||
|
Any,
|
||||||
|
AsyncIterator,
|
||||||
|
Callable,
|
||||||
|
Dict,
|
||||||
Iterable,
|
Iterable,
|
||||||
Iterator,
|
Iterator,
|
||||||
Callable,
|
List,
|
||||||
Any,
|
Optional,
|
||||||
Tuple,
|
Protocol,
|
||||||
Dict,
|
|
||||||
Generic,
|
|
||||||
TypeVar,
|
|
||||||
Type,
|
|
||||||
Union,
|
|
||||||
Set,
|
Set,
|
||||||
|
Tuple,
|
||||||
|
Type,
|
||||||
|
TypeVar,
|
||||||
|
Union,
|
||||||
cast,
|
cast,
|
||||||
)
|
)
|
||||||
|
from warnings import warn
|
||||||
|
|
||||||
if sys.version_info < (3, 7):
|
try:
|
||||||
from typing import GenericMeta
|
from typing import Self
|
||||||
else:
|
except ImportError:
|
||||||
class GenericMeta(type):
|
from typing_extensions import Self
|
||||||
...
|
|
||||||
|
try:
|
||||||
|
from functools import cache
|
||||||
|
except ImportError:
|
||||||
|
from functools import lru_cache
|
||||||
|
|
||||||
|
cache = lru_cache(maxsize=None)
|
||||||
|
|
||||||
# Hotfix, see: https://github.com/ets-labs/python-dependency-injector/issues/362
|
# Hotfix, see: https://github.com/ets-labs/python-dependency-injector/issues/362
|
||||||
if sys.version_info >= (3, 9):
|
if sys.version_info >= (3, 9):
|
||||||
|
@ -36,34 +47,64 @@ if sys.version_info >= (3, 9):
|
||||||
else:
|
else:
|
||||||
GenericAlias = None
|
GenericAlias = None
|
||||||
|
|
||||||
|
if sys.version_info >= (3, 9):
|
||||||
|
from typing import Annotated, get_args, get_origin
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
from typing_extensions import Annotated, get_args, get_origin
|
||||||
|
except ImportError:
|
||||||
|
Annotated = object()
|
||||||
|
|
||||||
try:
|
# For preventing NameError. Never executes
|
||||||
import fastapi.params
|
def get_args(hint):
|
||||||
except ImportError:
|
return ()
|
||||||
fastapi = None
|
|
||||||
|
def get_origin(tp):
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
try:
|
MARKER_EXTRACTORS: List[Callable[[Any], Any]] = []
|
||||||
import starlette.requests
|
INSPECT_EXCLUSION_FILTERS: List[Callable[[Any], bool]] = [isbuiltin]
|
||||||
except ImportError:
|
|
||||||
starlette = None
|
with suppress(ImportError):
|
||||||
|
from fastapi.params import Depends as FastAPIDepends
|
||||||
|
|
||||||
|
def extract_marker_from_fastapi(param: Any) -> Any:
|
||||||
|
if isinstance(param, FastAPIDepends):
|
||||||
|
return param.dependency
|
||||||
|
return None
|
||||||
|
|
||||||
|
MARKER_EXTRACTORS.append(extract_marker_from_fastapi)
|
||||||
|
|
||||||
|
with suppress(ImportError):
|
||||||
|
from fast_depends.dependencies import Depends as FastDepends
|
||||||
|
|
||||||
|
def extract_marker_from_fast_depends(param: Any) -> Any:
|
||||||
|
if isinstance(param, FastDepends):
|
||||||
|
return param.dependency
|
||||||
|
return None
|
||||||
|
|
||||||
|
MARKER_EXTRACTORS.append(extract_marker_from_fast_depends)
|
||||||
|
|
||||||
|
|
||||||
try:
|
with suppress(ImportError):
|
||||||
import werkzeug.local
|
from starlette.requests import Request as StarletteRequest
|
||||||
except ImportError:
|
|
||||||
werkzeug = None
|
def is_starlette_request_cls(obj: Any) -> bool:
|
||||||
|
return isclass(obj) and _safe_is_subclass(obj, StarletteRequest)
|
||||||
|
|
||||||
|
INSPECT_EXCLUSION_FILTERS.append(is_starlette_request_cls)
|
||||||
|
|
||||||
|
|
||||||
from . import providers
|
with suppress(ImportError):
|
||||||
|
from werkzeug.local import LocalProxy as WerkzeugLocalProxy
|
||||||
|
|
||||||
if sys.version_info[:2] == (3, 5):
|
def is_werkzeug_local_proxy(obj: Any) -> bool:
|
||||||
warnings.warn(
|
return isinstance(obj, WerkzeugLocalProxy)
|
||||||
"Dependency Injector will drop support of Python 3.5 after Jan 1st of 2022. "
|
|
||||||
"This does not mean that there will be any immediate breaking changes, "
|
INSPECT_EXCLUSION_FILTERS.append(is_werkzeug_local_proxy)
|
||||||
"but tests will no longer be executed on Python 3.5, and bugs will not be addressed.",
|
|
||||||
category=DeprecationWarning,
|
from . import providers # noqa: E402
|
||||||
)
|
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
"wire",
|
"wire",
|
||||||
|
@ -87,7 +128,15 @@ __all__ = (
|
||||||
|
|
||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
F = TypeVar("F", bound=Callable[..., Any])
|
F = TypeVar("F", bound=Callable[..., Any])
|
||||||
Container = Any
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from .containers import Container
|
||||||
|
else:
|
||||||
|
Container = Any
|
||||||
|
|
||||||
|
|
||||||
|
class DIWiringWarning(RuntimeWarning):
|
||||||
|
"""Base class for all warnings raised by the wiring module."""
|
||||||
|
|
||||||
|
|
||||||
class PatchedRegistry:
|
class PatchedRegistry:
|
||||||
|
@ -99,7 +148,9 @@ class PatchedRegistry:
|
||||||
def register_callable(self, patched: "PatchedCallable") -> None:
|
def register_callable(self, patched: "PatchedCallable") -> None:
|
||||||
self._callables[patched.patched] = patched
|
self._callables[patched.patched] = patched
|
||||||
|
|
||||||
def get_callables_from_module(self, module: ModuleType) -> Iterator[Callable[..., Any]]:
|
def get_callables_from_module(
|
||||||
|
self, module: ModuleType
|
||||||
|
) -> Iterator[Callable[..., Any]]:
|
||||||
for patched_callable in self._callables.values():
|
for patched_callable in self._callables.values():
|
||||||
if not patched_callable.is_in_module(module):
|
if not patched_callable.is_in_module(module):
|
||||||
continue
|
continue
|
||||||
|
@ -114,7 +165,9 @@ class PatchedRegistry:
|
||||||
def register_attribute(self, patched: "PatchedAttribute") -> None:
|
def register_attribute(self, patched: "PatchedAttribute") -> None:
|
||||||
self._attributes.add(patched)
|
self._attributes.add(patched)
|
||||||
|
|
||||||
def get_attributes_from_module(self, module: ModuleType) -> Iterator["PatchedAttribute"]:
|
def get_attributes_from_module(
|
||||||
|
self, module: ModuleType
|
||||||
|
) -> Iterator["PatchedAttribute"]:
|
||||||
for attribute in self._attributes:
|
for attribute in self._attributes:
|
||||||
if not attribute.is_in_module(module):
|
if not attribute.is_in_module(module):
|
||||||
continue
|
continue
|
||||||
|
@ -139,11 +192,11 @@ class PatchedCallable:
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
patched: Optional[Callable[..., Any]] = None,
|
patched: Optional[Callable[..., Any]] = None,
|
||||||
original: Optional[Callable[..., Any]] = None,
|
original: Optional[Callable[..., Any]] = None,
|
||||||
reference_injections: Optional[Dict[Any, Any]] = None,
|
reference_injections: Optional[Dict[Any, Any]] = None,
|
||||||
reference_closing: Optional[Dict[Any, Any]] = None,
|
reference_closing: Optional[Dict[Any, Any]] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.patched = patched
|
self.patched = patched
|
||||||
self.original = original
|
self.original = original
|
||||||
|
@ -214,18 +267,21 @@ class ProvidersMap:
|
||||||
)
|
)
|
||||||
|
|
||||||
def resolve_provider(
|
def resolve_provider(
|
||||||
self,
|
self,
|
||||||
provider: Union[providers.Provider, str],
|
provider: Union[providers.Provider, str],
|
||||||
modifier: Optional["Modifier"] = None,
|
modifier: Optional["Modifier"] = None,
|
||||||
) -> Optional[providers.Provider]:
|
) -> Optional[providers.Provider]:
|
||||||
if isinstance(provider, providers.Delegate):
|
if isinstance(provider, providers.Delegate):
|
||||||
return self._resolve_delegate(provider)
|
return self._resolve_delegate(provider)
|
||||||
elif isinstance(provider, (
|
elif isinstance(
|
||||||
providers.ProvidedInstance,
|
provider,
|
||||||
providers.AttributeGetter,
|
(
|
||||||
providers.ItemGetter,
|
providers.ProvidedInstance,
|
||||||
providers.MethodCaller,
|
providers.AttributeGetter,
|
||||||
)):
|
providers.ItemGetter,
|
||||||
|
providers.MethodCaller,
|
||||||
|
),
|
||||||
|
):
|
||||||
return self._resolve_provided_instance(provider)
|
return self._resolve_provided_instance(provider)
|
||||||
elif isinstance(provider, providers.ConfigurationOption):
|
elif isinstance(provider, providers.ConfigurationOption):
|
||||||
return self._resolve_config_option(provider)
|
return self._resolve_config_option(provider)
|
||||||
|
@ -237,9 +293,9 @@ class ProvidersMap:
|
||||||
return self._resolve_provider(provider)
|
return self._resolve_provider(provider)
|
||||||
|
|
||||||
def _resolve_string_id(
|
def _resolve_string_id(
|
||||||
self,
|
self,
|
||||||
id: str,
|
id: str,
|
||||||
modifier: Optional["Modifier"] = None,
|
modifier: Optional["Modifier"] = None,
|
||||||
) -> Optional[providers.Provider]:
|
) -> Optional[providers.Provider]:
|
||||||
if id == self.CONTAINER_STRING_ID:
|
if id == self.CONTAINER_STRING_ID:
|
||||||
return self._container.__self__
|
return self._container.__self__
|
||||||
|
@ -256,16 +312,19 @@ class ProvidersMap:
|
||||||
return provider
|
return provider
|
||||||
|
|
||||||
def _resolve_provided_instance(
|
def _resolve_provided_instance(
|
||||||
self,
|
self,
|
||||||
original: providers.Provider,
|
original: providers.Provider,
|
||||||
) -> Optional[providers.Provider]:
|
) -> Optional[providers.Provider]:
|
||||||
modifiers = []
|
modifiers = []
|
||||||
while isinstance(original, (
|
while isinstance(
|
||||||
|
original,
|
||||||
|
(
|
||||||
providers.ProvidedInstance,
|
providers.ProvidedInstance,
|
||||||
providers.AttributeGetter,
|
providers.AttributeGetter,
|
||||||
providers.ItemGetter,
|
providers.ItemGetter,
|
||||||
providers.MethodCaller,
|
providers.MethodCaller,
|
||||||
)):
|
),
|
||||||
|
):
|
||||||
modifiers.insert(0, original)
|
modifiers.insert(0, original)
|
||||||
original = original.provides
|
original = original.provides
|
||||||
|
|
||||||
|
@ -289,8 +348,8 @@ class ProvidersMap:
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def _resolve_delegate(
|
def _resolve_delegate(
|
||||||
self,
|
self,
|
||||||
original: providers.Delegate,
|
original: providers.Delegate,
|
||||||
) -> Optional[providers.Provider]:
|
) -> Optional[providers.Provider]:
|
||||||
provider = self._resolve_provider(original.provides)
|
provider = self._resolve_provider(original.provides)
|
||||||
if provider:
|
if provider:
|
||||||
|
@ -298,11 +357,11 @@ class ProvidersMap:
|
||||||
return provider
|
return provider
|
||||||
|
|
||||||
def _resolve_config_option(
|
def _resolve_config_option(
|
||||||
self,
|
self,
|
||||||
original: providers.ConfigurationOption,
|
original: providers.ConfigurationOption,
|
||||||
as_: Any = None,
|
as_: Any = None,
|
||||||
) -> Optional[providers.Provider]:
|
) -> Optional[providers.Provider]:
|
||||||
original_root = original.root
|
original_root = original._get_root()
|
||||||
new = self._resolve_provider(original_root)
|
new = self._resolve_provider(original_root)
|
||||||
if new is None:
|
if new is None:
|
||||||
return None
|
return None
|
||||||
|
@ -324,8 +383,8 @@ class ProvidersMap:
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def _resolve_provider(
|
def _resolve_provider(
|
||||||
self,
|
self,
|
||||||
original: providers.Provider,
|
original: providers.Provider,
|
||||||
) -> Optional[providers.Provider]:
|
) -> Optional[providers.Provider]:
|
||||||
try:
|
try:
|
||||||
return self._map[original]
|
return self._map[original]
|
||||||
|
@ -334,9 +393,9 @@ class ProvidersMap:
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _create_providers_map(
|
def _create_providers_map(
|
||||||
cls,
|
cls,
|
||||||
current_container: Container,
|
current_container: Container,
|
||||||
original_container: Container,
|
original_container: Container,
|
||||||
) -> Dict[providers.Provider, providers.Provider]:
|
) -> Dict[providers.Provider, providers.Provider]:
|
||||||
current_providers = current_container.providers
|
current_providers = current_container.providers
|
||||||
current_providers["__self__"] = current_container.__self__
|
current_providers["__self__"] = current_container.__self__
|
||||||
|
@ -349,8 +408,9 @@ class ProvidersMap:
|
||||||
original_provider = original_providers[provider_name]
|
original_provider = original_providers[provider_name]
|
||||||
providers_map[original_provider] = current_provider
|
providers_map[original_provider] = current_provider
|
||||||
|
|
||||||
if isinstance(current_provider, providers.Container) \
|
if isinstance(current_provider, providers.Container) and isinstance(
|
||||||
and isinstance(original_provider, providers.Container):
|
original_provider, providers.Container
|
||||||
|
):
|
||||||
subcontainer_map = cls._create_providers_map(
|
subcontainer_map = cls._create_providers_map(
|
||||||
current_container=current_provider.container,
|
current_container=current_provider.container,
|
||||||
original_container=original_provider.container,
|
original_container=original_provider.container,
|
||||||
|
@ -360,35 +420,19 @@ class ProvidersMap:
|
||||||
return providers_map
|
return providers_map
|
||||||
|
|
||||||
|
|
||||||
class InspectFilter:
|
def is_excluded_from_inspect(obj: Any) -> bool:
|
||||||
|
for is_excluded in INSPECT_EXCLUSION_FILTERS:
|
||||||
def is_excluded(self, instance: object) -> bool:
|
if is_excluded(obj):
|
||||||
if self._is_werkzeug_local_proxy(instance):
|
|
||||||
return True
|
return True
|
||||||
elif self._is_starlette_request_cls(instance):
|
return False
|
||||||
return True
|
|
||||||
elif self._is_builtin(instance):
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _is_werkzeug_local_proxy(self, instance: object) -> bool:
|
|
||||||
return werkzeug and isinstance(instance, werkzeug.local.LocalProxy)
|
|
||||||
|
|
||||||
def _is_starlette_request_cls(self, instance: object) -> bool:
|
|
||||||
return starlette \
|
|
||||||
and isinstance(instance, type) \
|
|
||||||
and _safe_is_subclass(instance, starlette.requests.Request)
|
|
||||||
|
|
||||||
def _is_builtin(self, instance: object) -> bool:
|
|
||||||
return inspect.isbuiltin(instance)
|
|
||||||
|
|
||||||
|
|
||||||
def wire( # noqa: C901
|
def wire( # noqa: C901
|
||||||
container: Container,
|
container: Container,
|
||||||
*,
|
*,
|
||||||
modules: Optional[Iterable[ModuleType]] = None,
|
modules: Optional[Iterable[ModuleType]] = None,
|
||||||
packages: Optional[Iterable[ModuleType]] = None,
|
packages: Optional[Iterable[ModuleType]] = None,
|
||||||
|
keep_cache: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Wire container providers with provided packages and modules."""
|
"""Wire container providers with provided packages and modules."""
|
||||||
modules = [*modules] if modules else []
|
modules = [*modules] if modules else []
|
||||||
|
@ -400,8 +444,8 @@ def wire( # noqa: C901
|
||||||
providers_map = ProvidersMap(container)
|
providers_map = ProvidersMap(container)
|
||||||
|
|
||||||
for module in modules:
|
for module in modules:
|
||||||
for member_name, member in inspect.getmembers(module):
|
for member_name, member in _get_members_and_annotated(module):
|
||||||
if _inspect_filter.is_excluded(member):
|
if is_excluded_from_inspect(member):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if _is_marker(member):
|
if _is_marker(member):
|
||||||
|
@ -411,25 +455,32 @@ def wire( # noqa: C901
|
||||||
elif inspect.isclass(member):
|
elif inspect.isclass(member):
|
||||||
cls = member
|
cls = member
|
||||||
try:
|
try:
|
||||||
cls_members = inspect.getmembers(cls)
|
cls_members = _get_members_and_annotated(cls)
|
||||||
except Exception: # noqa
|
except Exception: # noqa
|
||||||
# Hotfix, see: https://github.com/ets-labs/python-dependency-injector/issues/441
|
# Hotfix, see: https://github.com/ets-labs/python-dependency-injector/issues/441
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
for cls_member_name, cls_member in cls_members:
|
for cls_member_name, cls_member in cls_members:
|
||||||
if _is_marker(cls_member):
|
if _is_marker(cls_member):
|
||||||
_patch_attribute(cls, cls_member_name, cls_member, providers_map)
|
_patch_attribute(
|
||||||
|
cls, cls_member_name, cls_member, providers_map
|
||||||
|
)
|
||||||
elif _is_method(cls_member):
|
elif _is_method(cls_member):
|
||||||
_patch_method(cls, cls_member_name, cls_member, providers_map)
|
_patch_method(
|
||||||
|
cls, cls_member_name, cls_member, providers_map
|
||||||
|
)
|
||||||
|
|
||||||
for patched in _patched_registry.get_callables_from_module(module):
|
for patched in _patched_registry.get_callables_from_module(module):
|
||||||
_bind_injections(patched, providers_map)
|
_bind_injections(patched, providers_map)
|
||||||
|
|
||||||
|
if not keep_cache:
|
||||||
|
clear_cache()
|
||||||
|
|
||||||
|
|
||||||
def unwire( # noqa: C901
|
def unwire( # noqa: C901
|
||||||
*,
|
*,
|
||||||
modules: Optional[Iterable[ModuleType]] = None,
|
modules: Optional[Iterable[ModuleType]] = None,
|
||||||
packages: Optional[Iterable[ModuleType]] = None,
|
packages: Optional[Iterable[ModuleType]] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Wire provided packages and modules with previous wired providers."""
|
"""Wire provided packages and modules with previous wired providers."""
|
||||||
modules = [*modules] if modules else []
|
modules = [*modules] if modules else []
|
||||||
|
@ -443,7 +494,9 @@ def unwire( # noqa: C901
|
||||||
if inspect.isfunction(member):
|
if inspect.isfunction(member):
|
||||||
_unpatch(module, name, member)
|
_unpatch(module, name, member)
|
||||||
elif inspect.isclass(member):
|
elif inspect.isclass(member):
|
||||||
for method_name, method in inspect.getmembers(member, inspect.isfunction):
|
for method_name, method in inspect.getmembers(
|
||||||
|
member, inspect.isfunction
|
||||||
|
):
|
||||||
_unpatch(member, method_name, method)
|
_unpatch(member, method_name, method)
|
||||||
|
|
||||||
for patched in _patched_registry.get_callables_from_module(module):
|
for patched in _patched_registry.get_callables_from_module(module):
|
||||||
|
@ -457,15 +510,20 @@ def unwire( # noqa: C901
|
||||||
def inject(fn: F) -> F:
|
def inject(fn: F) -> F:
|
||||||
"""Decorate callable with injecting decorator."""
|
"""Decorate callable with injecting decorator."""
|
||||||
reference_injections, reference_closing = _fetch_reference_injections(fn)
|
reference_injections, reference_closing = _fetch_reference_injections(fn)
|
||||||
|
|
||||||
|
if not reference_injections:
|
||||||
|
warn("@inject is not required here", DIWiringWarning, stacklevel=2)
|
||||||
|
return fn
|
||||||
|
|
||||||
patched = _get_patched(fn, reference_injections, reference_closing)
|
patched = _get_patched(fn, reference_injections, reference_closing)
|
||||||
return cast(F, patched)
|
return cast(F, patched)
|
||||||
|
|
||||||
|
|
||||||
def _patch_fn(
|
def _patch_fn(
|
||||||
module: ModuleType,
|
module: ModuleType,
|
||||||
name: str,
|
name: str,
|
||||||
fn: Callable[..., Any],
|
fn: Callable[..., Any],
|
||||||
providers_map: ProvidersMap,
|
providers_map: ProvidersMap,
|
||||||
) -> None:
|
) -> None:
|
||||||
if not _is_patched(fn):
|
if not _is_patched(fn):
|
||||||
reference_injections, reference_closing = _fetch_reference_injections(fn)
|
reference_injections, reference_closing = _fetch_reference_injections(fn)
|
||||||
|
@ -479,14 +537,16 @@ def _patch_fn(
|
||||||
|
|
||||||
|
|
||||||
def _patch_method(
|
def _patch_method(
|
||||||
cls: Type,
|
cls: Type,
|
||||||
name: str,
|
name: str,
|
||||||
method: Callable[..., Any],
|
method: Callable[..., Any],
|
||||||
providers_map: ProvidersMap,
|
providers_map: ProvidersMap,
|
||||||
) -> None:
|
) -> None:
|
||||||
if hasattr(cls, "__dict__") \
|
if (
|
||||||
and name in cls.__dict__ \
|
hasattr(cls, "__dict__")
|
||||||
and isinstance(cls.__dict__[name], (classmethod, staticmethod)):
|
and name in cls.__dict__
|
||||||
|
and isinstance(cls.__dict__[name], (classmethod, staticmethod))
|
||||||
|
):
|
||||||
method = cls.__dict__[name]
|
method = cls.__dict__[name]
|
||||||
fn = method.__func__
|
fn = method.__func__
|
||||||
else:
|
else:
|
||||||
|
@ -500,6 +560,10 @@ def _patch_method(
|
||||||
|
|
||||||
_bind_injections(fn, providers_map)
|
_bind_injections(fn, providers_map)
|
||||||
|
|
||||||
|
if fn is method:
|
||||||
|
# Hotfix, see: https://github.com/ets-labs/python-dependency-injector/issues/884
|
||||||
|
return
|
||||||
|
|
||||||
if isinstance(method, (classmethod, staticmethod)):
|
if isinstance(method, (classmethod, staticmethod)):
|
||||||
fn = type(method)(fn)
|
fn = type(method)(fn)
|
||||||
|
|
||||||
|
@ -507,13 +571,15 @@ def _patch_method(
|
||||||
|
|
||||||
|
|
||||||
def _unpatch(
|
def _unpatch(
|
||||||
module: ModuleType,
|
module: ModuleType,
|
||||||
name: str,
|
name: str,
|
||||||
fn: Callable[..., Any],
|
fn: Callable[..., Any],
|
||||||
) -> None:
|
) -> None:
|
||||||
if hasattr(module, "__dict__") \
|
if (
|
||||||
and name in module.__dict__ \
|
hasattr(module, "__dict__")
|
||||||
and isinstance(module.__dict__[name], (classmethod, staticmethod)):
|
and name in module.__dict__
|
||||||
|
and isinstance(module.__dict__[name], (classmethod, staticmethod))
|
||||||
|
):
|
||||||
method = module.__dict__[name]
|
method = module.__dict__[name]
|
||||||
fn = method.__func__
|
fn = method.__func__
|
||||||
|
|
||||||
|
@ -524,10 +590,10 @@ def _unpatch(
|
||||||
|
|
||||||
|
|
||||||
def _patch_attribute(
|
def _patch_attribute(
|
||||||
member: Any,
|
member: Any,
|
||||||
name: str,
|
name: str,
|
||||||
marker: "_Marker",
|
marker: "_Marker",
|
||||||
providers_map: ProvidersMap,
|
providers_map: ProvidersMap,
|
||||||
) -> None:
|
) -> None:
|
||||||
provider = providers_map.resolve_provider(marker.provider, marker.modifier)
|
provider = providers_map.resolve_provider(marker.provider, marker.modifier)
|
||||||
if provider is None:
|
if provider is None:
|
||||||
|
@ -548,16 +614,37 @@ def _unpatch_attribute(patched: PatchedAttribute) -> None:
|
||||||
setattr(patched.member, patched.name, patched.marker)
|
setattr(patched.member, patched.name, patched.marker)
|
||||||
|
|
||||||
|
|
||||||
|
def _extract_marker(parameter: inspect.Parameter) -> Optional["_Marker"]:
|
||||||
|
if get_origin(parameter.annotation) is Annotated:
|
||||||
|
args = get_args(parameter.annotation)
|
||||||
|
if len(args) > 1:
|
||||||
|
marker = args[1]
|
||||||
|
else:
|
||||||
|
marker = None
|
||||||
|
else:
|
||||||
|
marker = parameter.default
|
||||||
|
|
||||||
|
for marker_extractor in MARKER_EXTRACTORS:
|
||||||
|
if _marker := marker_extractor(marker):
|
||||||
|
marker = _marker
|
||||||
|
break
|
||||||
|
|
||||||
|
if not isinstance(marker, _Marker):
|
||||||
|
return None
|
||||||
|
|
||||||
|
return marker
|
||||||
|
|
||||||
|
|
||||||
|
@cache
|
||||||
def _fetch_reference_injections( # noqa: C901
|
def _fetch_reference_injections( # noqa: C901
|
||||||
fn: Callable[..., Any],
|
fn: Callable[..., Any],
|
||||||
) -> Tuple[Dict[str, Any], Dict[str, Any]]:
|
) -> Tuple[Dict[str, Any], Dict[str, Any]]:
|
||||||
# Hotfix, see:
|
# Hotfix, see:
|
||||||
# - https://github.com/ets-labs/python-dependency-injector/issues/362
|
# - https://github.com/ets-labs/python-dependency-injector/issues/362
|
||||||
# - https://github.com/ets-labs/python-dependency-injector/issues/398
|
# - https://github.com/ets-labs/python-dependency-injector/issues/398
|
||||||
if GenericAlias and any((
|
if GenericAlias and any(
|
||||||
fn is GenericAlias,
|
(fn is GenericAlias, getattr(fn, "__func__", None) is GenericAlias)
|
||||||
getattr(fn, "__func__", None) is GenericAlias
|
):
|
||||||
)):
|
|
||||||
fn = fn.__init__
|
fn = fn.__init__
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -573,18 +660,11 @@ def _fetch_reference_injections( # noqa: C901
|
||||||
injections = {}
|
injections = {}
|
||||||
closing = {}
|
closing = {}
|
||||||
for parameter_name, parameter in signature.parameters.items():
|
for parameter_name, parameter in signature.parameters.items():
|
||||||
if not isinstance(parameter.default, _Marker) \
|
marker = _extract_marker(parameter)
|
||||||
and not _is_fastapi_depends(parameter.default):
|
|
||||||
|
if marker is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
marker = parameter.default
|
|
||||||
|
|
||||||
if _is_fastapi_depends(marker):
|
|
||||||
marker = marker.dependency
|
|
||||||
|
|
||||||
if not isinstance(marker, _Marker):
|
|
||||||
continue
|
|
||||||
|
|
||||||
if isinstance(marker, Closing):
|
if isinstance(marker, Closing):
|
||||||
marker = marker.provider
|
marker = marker.provider
|
||||||
closing[parameter_name] = marker
|
closing[parameter_name] = marker
|
||||||
|
@ -593,22 +673,6 @@ def _fetch_reference_injections( # noqa: C901
|
||||||
return injections, closing
|
return injections, closing
|
||||||
|
|
||||||
|
|
||||||
def _locate_dependent_closing_args(provider: providers.Provider) -> Dict[str, providers.Provider]:
|
|
||||||
if not hasattr(provider, "args"):
|
|
||||||
return {}
|
|
||||||
|
|
||||||
closing_deps = {}
|
|
||||||
for arg in provider.args:
|
|
||||||
if not isinstance(arg, providers.Provider) or not hasattr(arg, "args"):
|
|
||||||
continue
|
|
||||||
|
|
||||||
if not arg.args and isinstance(arg, providers.Resource):
|
|
||||||
return {str(id(arg)): arg}
|
|
||||||
else:
|
|
||||||
closing_deps += _locate_dependent_closing_args(arg)
|
|
||||||
return closing_deps
|
|
||||||
|
|
||||||
|
|
||||||
def _bind_injections(fn: Callable[..., Any], providers_map: ProvidersMap) -> None:
|
def _bind_injections(fn: Callable[..., Any], providers_map: ProvidersMap) -> None:
|
||||||
patched_callable = _patched_registry.get_callable(fn)
|
patched_callable = _patched_registry.get_callable(fn)
|
||||||
if patched_callable is None:
|
if patched_callable is None:
|
||||||
|
@ -630,9 +694,9 @@ def _bind_injections(fn: Callable[..., Any], providers_map: ProvidersMap) -> Non
|
||||||
|
|
||||||
if injection in patched_callable.reference_closing:
|
if injection in patched_callable.reference_closing:
|
||||||
patched_callable.add_closing(injection, provider)
|
patched_callable.add_closing(injection, provider)
|
||||||
deps = _locate_dependent_closing_args(provider)
|
|
||||||
for key, dep in deps.items():
|
for resource in provider.traverse(types=[providers.Resource]):
|
||||||
patched_callable.add_closing(key, dep)
|
patched_callable.add_closing(str(id(resource)), resource)
|
||||||
|
|
||||||
|
|
||||||
def _unbind_injections(fn: Callable[..., Any]) -> None:
|
def _unbind_injections(fn: Callable[..., Any]) -> None:
|
||||||
|
@ -647,8 +711,8 @@ def _fetch_modules(package):
|
||||||
if not hasattr(package, "__path__") or not hasattr(package, "__name__"):
|
if not hasattr(package, "__path__") or not hasattr(package, "__name__"):
|
||||||
return modules
|
return modules
|
||||||
for module_info in pkgutil.walk_packages(
|
for module_info in pkgutil.walk_packages(
|
||||||
path=package.__path__,
|
path=package.__path__,
|
||||||
prefix=package.__name__ + ".",
|
prefix=package.__name__ + ".",
|
||||||
):
|
):
|
||||||
module = importlib.import_module(module_info.name)
|
module = importlib.import_module(module_info.name)
|
||||||
modules.append(module)
|
modules.append(module)
|
||||||
|
@ -664,9 +728,9 @@ def _is_marker(member) -> bool:
|
||||||
|
|
||||||
|
|
||||||
def _get_patched(
|
def _get_patched(
|
||||||
fn: F,
|
fn: F,
|
||||||
reference_injections: Dict[Any, Any],
|
reference_injections: Dict[Any, Any],
|
||||||
reference_closing: Dict[Any, Any],
|
reference_closing: Dict[Any, Any],
|
||||||
) -> F:
|
) -> F:
|
||||||
patched_object = PatchedCallable(
|
patched_object = PatchedCallable(
|
||||||
original=fn,
|
original=fn,
|
||||||
|
@ -676,6 +740,8 @@ def _get_patched(
|
||||||
|
|
||||||
if inspect.iscoroutinefunction(fn):
|
if inspect.iscoroutinefunction(fn):
|
||||||
patched = _get_async_patched(fn, patched_object)
|
patched = _get_async_patched(fn, patched_object)
|
||||||
|
elif inspect.isasyncgenfunction(fn):
|
||||||
|
patched = _get_async_gen_patched(fn, patched_object)
|
||||||
else:
|
else:
|
||||||
patched = _get_sync_patched(fn, patched_object)
|
patched = _get_sync_patched(fn, patched_object)
|
||||||
|
|
||||||
|
@ -685,18 +751,16 @@ def _get_patched(
|
||||||
return patched
|
return patched
|
||||||
|
|
||||||
|
|
||||||
def _is_fastapi_depends(param: Any) -> bool:
|
|
||||||
return fastapi and isinstance(param, fastapi.params.Depends)
|
|
||||||
|
|
||||||
|
|
||||||
def _is_patched(fn) -> bool:
|
def _is_patched(fn) -> bool:
|
||||||
return _patched_registry.has_callable(fn)
|
return _patched_registry.has_callable(fn)
|
||||||
|
|
||||||
|
|
||||||
def _is_declarative_container(instance: Any) -> bool:
|
def _is_declarative_container(instance: Any) -> bool:
|
||||||
return (isinstance(instance, type)
|
return (
|
||||||
and getattr(instance, "__IS_CONTAINER__", False) is True
|
isinstance(instance, type)
|
||||||
and getattr(instance, "declarative_parent", None) is None)
|
and getattr(instance, "__IS_CONTAINER__", False) is True
|
||||||
|
and getattr(instance, "declarative_parent", None) is None
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _safe_is_subclass(instance: Any, cls: Type) -> bool:
|
def _safe_is_subclass(instance: Any, cls: Type) -> bool:
|
||||||
|
@ -709,11 +773,10 @@ def _safe_is_subclass(instance: Any, cls: Type) -> bool:
|
||||||
class Modifier:
|
class Modifier:
|
||||||
|
|
||||||
def modify(
|
def modify(
|
||||||
self,
|
self,
|
||||||
provider: providers.ConfigurationOption,
|
provider: providers.ConfigurationOption,
|
||||||
providers_map: ProvidersMap,
|
providers_map: ProvidersMap,
|
||||||
) -> providers.Provider:
|
) -> providers.Provider: ...
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
class TypeModifier(Modifier):
|
class TypeModifier(Modifier):
|
||||||
|
@ -722,9 +785,9 @@ class TypeModifier(Modifier):
|
||||||
self.type_ = type_
|
self.type_ = type_
|
||||||
|
|
||||||
def modify(
|
def modify(
|
||||||
self,
|
self,
|
||||||
provider: providers.ConfigurationOption,
|
provider: providers.ConfigurationOption,
|
||||||
providers_map: ProvidersMap,
|
providers_map: ProvidersMap,
|
||||||
) -> providers.Provider:
|
) -> providers.Provider:
|
||||||
return provider.as_(self.type_)
|
return provider.as_(self.type_)
|
||||||
|
|
||||||
|
@ -749,22 +812,22 @@ class RequiredModifier(Modifier):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.type_modifier = None
|
self.type_modifier = None
|
||||||
|
|
||||||
def as_int(self) -> "RequiredModifier":
|
def as_int(self) -> Self:
|
||||||
self.type_modifier = TypeModifier(int)
|
self.type_modifier = TypeModifier(int)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def as_float(self) -> "RequiredModifier":
|
def as_float(self) -> Self:
|
||||||
self.type_modifier = TypeModifier(float)
|
self.type_modifier = TypeModifier(float)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def as_(self, type_: Type) -> "RequiredModifier":
|
def as_(self, type_: Type) -> Self:
|
||||||
self.type_modifier = TypeModifier(type_)
|
self.type_modifier = TypeModifier(type_)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def modify(
|
def modify(
|
||||||
self,
|
self,
|
||||||
provider: providers.ConfigurationOption,
|
provider: providers.ConfigurationOption,
|
||||||
providers_map: ProvidersMap,
|
providers_map: ProvidersMap,
|
||||||
) -> providers.Provider:
|
) -> providers.Provider:
|
||||||
provider = provider.required()
|
provider = provider.required()
|
||||||
if self.type_modifier:
|
if self.type_modifier:
|
||||||
|
@ -783,9 +846,9 @@ class InvariantModifier(Modifier):
|
||||||
self.id = id
|
self.id = id
|
||||||
|
|
||||||
def modify(
|
def modify(
|
||||||
self,
|
self,
|
||||||
provider: providers.ConfigurationOption,
|
provider: providers.ConfigurationOption,
|
||||||
providers_map: ProvidersMap,
|
providers_map: ProvidersMap,
|
||||||
) -> providers.Provider:
|
) -> providers.Provider:
|
||||||
invariant_segment = providers_map.resolve_provider(self.id)
|
invariant_segment = providers_map.resolve_provider(self.id)
|
||||||
return provider[invariant_segment]
|
return provider[invariant_segment]
|
||||||
|
@ -805,22 +868,22 @@ class ProvidedInstance(Modifier):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.segments = []
|
self.segments = []
|
||||||
|
|
||||||
def __getattr__(self, item):
|
def __getattr__(self, item: str) -> Self:
|
||||||
self.segments.append((self.TYPE_ATTRIBUTE, item))
|
self.segments.append((self.TYPE_ATTRIBUTE, item))
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __getitem__(self, item):
|
def __getitem__(self, item) -> Self:
|
||||||
self.segments.append((self.TYPE_ITEM, item))
|
self.segments.append((self.TYPE_ITEM, item))
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def call(self):
|
def call(self) -> Self:
|
||||||
self.segments.append((self.TYPE_CALL, None))
|
self.segments.append((self.TYPE_CALL, None))
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def modify(
|
def modify(
|
||||||
self,
|
self,
|
||||||
provider: providers.Provider,
|
provider: providers.Provider,
|
||||||
providers_map: ProvidersMap,
|
providers_map: ProvidersMap,
|
||||||
) -> providers.Provider:
|
) -> providers.Provider:
|
||||||
provider = provider.provided
|
provider = provider.provided
|
||||||
for type_, value in self.segments:
|
for type_, value in self.segments:
|
||||||
|
@ -838,47 +901,56 @@ def provided() -> ProvidedInstance:
|
||||||
return ProvidedInstance()
|
return ProvidedInstance()
|
||||||
|
|
||||||
|
|
||||||
class ClassGetItemMeta(GenericMeta):
|
MarkerItem = Union[
|
||||||
def __getitem__(cls, item):
|
str,
|
||||||
# Spike for Python 3.6
|
providers.Provider[Any],
|
||||||
if isinstance(item, tuple):
|
Tuple[str, TypeModifier],
|
||||||
return cls(*item)
|
Type[Container],
|
||||||
return cls(item)
|
"_Marker",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class _Marker(Generic[T], metaclass=ClassGetItemMeta):
|
if TYPE_CHECKING:
|
||||||
|
|
||||||
__IS_MARKER__ = True
|
class _Marker(Protocol):
|
||||||
|
__IS_MARKER__: bool
|
||||||
|
|
||||||
def __init__(
|
def __call__(self) -> Self: ...
|
||||||
|
def __getattr__(self, item: str) -> Self: ...
|
||||||
|
def __getitem__(self, item: Any) -> Any: ...
|
||||||
|
|
||||||
|
Provide: _Marker
|
||||||
|
Provider: _Marker
|
||||||
|
Closing: _Marker
|
||||||
|
else:
|
||||||
|
|
||||||
|
class _Marker:
|
||||||
|
|
||||||
|
__IS_MARKER__ = True
|
||||||
|
|
||||||
|
def __init__(
|
||||||
self,
|
self,
|
||||||
provider: Union[providers.Provider, Container, str],
|
provider: Union[providers.Provider, Container, str],
|
||||||
modifier: Optional[Modifier] = None,
|
modifier: Optional[Modifier] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
if _is_declarative_container(provider):
|
if _is_declarative_container(provider):
|
||||||
provider = provider.__self__
|
provider = provider.__self__
|
||||||
self.provider = provider
|
self.provider = provider
|
||||||
self.modifier = modifier
|
self.modifier = modifier
|
||||||
|
|
||||||
def __class_getitem__(cls, item) -> T:
|
def __class_getitem__(cls, item: MarkerItem) -> Self:
|
||||||
if isinstance(item, tuple):
|
if isinstance(item, tuple):
|
||||||
return cls(*item)
|
return cls(*item)
|
||||||
return cls(item)
|
return cls(item)
|
||||||
|
|
||||||
def __call__(self) -> T:
|
def __call__(self) -> Self:
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
class Provide(_Marker): ...
|
||||||
|
|
||||||
class Provide(_Marker):
|
class Provider(_Marker): ...
|
||||||
...
|
|
||||||
|
|
||||||
|
class Closing(_Marker): ...
|
||||||
class Provider(_Marker):
|
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
class Closing(_Marker):
|
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
class AutoLoader:
|
class AutoLoader:
|
||||||
|
@ -928,8 +1000,7 @@ class AutoLoader:
|
||||||
super().exec_module(module)
|
super().exec_module(module)
|
||||||
loader.wire_module(module)
|
loader.wire_module(module)
|
||||||
|
|
||||||
class ExtensionFileLoader(importlib.machinery.ExtensionFileLoader):
|
class ExtensionFileLoader(importlib.machinery.ExtensionFileLoader): ...
|
||||||
...
|
|
||||||
|
|
||||||
loader_details = [
|
loader_details = [
|
||||||
(SourcelessFileLoader, importlib.machinery.BYTECODE_SUFFIXES),
|
(SourcelessFileLoader, importlib.machinery.BYTECODE_SUFFIXES),
|
||||||
|
@ -978,24 +1049,71 @@ def is_loader_installed() -> bool:
|
||||||
|
|
||||||
|
|
||||||
_patched_registry = PatchedRegistry()
|
_patched_registry = PatchedRegistry()
|
||||||
_inspect_filter = InspectFilter()
|
|
||||||
_loader = AutoLoader()
|
_loader = AutoLoader()
|
||||||
|
|
||||||
# Optimizations
|
# Optimizations
|
||||||
from ._cwiring import _get_sync_patched # noqa
|
from ._cwiring import DependencyResolver # noqa: E402
|
||||||
from ._cwiring import _async_inject # noqa
|
|
||||||
|
|
||||||
|
|
||||||
# Wiring uses the following Python wrapper because there is
|
# Wiring uses the following Python wrapper because there is
|
||||||
# no possibility to compile a first-type citizen coroutine in Cython.
|
# no possibility to compile a first-type citizen coroutine in Cython.
|
||||||
def _get_async_patched(fn: F, patched: PatchedCallable) -> F:
|
def _get_async_patched(fn: F, patched: PatchedCallable) -> F:
|
||||||
@functools.wraps(fn)
|
@functools.wraps(fn)
|
||||||
async def _patched(*args, **kwargs):
|
async def _patched(*args: Any, **raw_kwargs: Any) -> Any:
|
||||||
return await _async_inject(
|
resolver = DependencyResolver(raw_kwargs, patched.injections, patched.closing)
|
||||||
fn,
|
|
||||||
args,
|
async with resolver as kwargs:
|
||||||
kwargs,
|
return await fn(*args, **kwargs)
|
||||||
patched.injections,
|
|
||||||
patched.closing,
|
return cast(F, _patched)
|
||||||
)
|
|
||||||
return _patched
|
|
||||||
|
def _get_async_gen_patched(fn: F, patched: PatchedCallable) -> F:
|
||||||
|
@functools.wraps(fn)
|
||||||
|
async def _patched(*args: Any, **raw_kwargs: Any) -> AsyncIterator[Any]:
|
||||||
|
resolver = DependencyResolver(raw_kwargs, patched.injections, patched.closing)
|
||||||
|
|
||||||
|
async with resolver as kwargs:
|
||||||
|
async for obj in fn(*args, **kwargs):
|
||||||
|
yield obj
|
||||||
|
|
||||||
|
return cast(F, _patched)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_sync_patched(fn: F, patched: PatchedCallable) -> F:
|
||||||
|
@functools.wraps(fn)
|
||||||
|
def _patched(*args: Any, **raw_kwargs: Any) -> Any:
|
||||||
|
resolver = DependencyResolver(raw_kwargs, patched.injections, patched.closing)
|
||||||
|
|
||||||
|
with resolver as kwargs:
|
||||||
|
return fn(*args, **kwargs)
|
||||||
|
|
||||||
|
return cast(F, _patched)
|
||||||
|
|
||||||
|
|
||||||
|
if sys.version_info >= (3, 10):
|
||||||
|
|
||||||
|
def _get_annotations(obj: Any) -> Dict[str, Any]:
|
||||||
|
return inspect.get_annotations(obj)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
def _get_annotations(obj: Any) -> Dict[str, Any]:
|
||||||
|
return getattr(obj, "__annotations__", {})
|
||||||
|
|
||||||
|
|
||||||
|
def _get_members_and_annotated(obj: Any) -> Iterable[Tuple[str, Any]]:
|
||||||
|
members = inspect.getmembers(obj)
|
||||||
|
annotations = _get_annotations(obj)
|
||||||
|
for annotation_name, annotation in annotations.items():
|
||||||
|
if get_origin(annotation) is Annotated:
|
||||||
|
args = get_args(annotation)
|
||||||
|
if len(args) > 1:
|
||||||
|
member = args[1]
|
||||||
|
members.append((annotation_name, member))
|
||||||
|
return members
|
||||||
|
|
||||||
|
|
||||||
|
def clear_cache() -> None:
|
||||||
|
"""Clear all caches used by :func:`wire`."""
|
||||||
|
_fetch_reference_injections.cache_clear()
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
[pytest]
|
|
||||||
testpaths = tests/unit/
|
|
||||||
python_files = test_*_py2_py3.py
|
|
||||||
asyncio_mode = auto
|
|
||||||
filterwarnings =
|
|
||||||
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:The \`scipy(.*?)\` namespace is deprecated(.*):DeprecationWarning
|
|
||||||
ignore:ssl\.PROTOCOL_TLS is deprecated:DeprecationWarning:botocore.*
|
|
|
@ -1,10 +0,0 @@
|
||||||
[pytest]
|
|
||||||
testpaths = tests/unit/
|
|
||||||
python_files = test_*_py3.py
|
|
||||||
asyncio_mode = auto
|
|
||||||
filterwarnings =
|
|
||||||
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:The \`scipy(.*?)\` namespace is deprecated(.*):DeprecationWarning
|
|
||||||
ignore:ssl\.PROTOCOL_TLS is deprecated:DeprecationWarning:botocore.*
|
|
|
@ -1,11 +0,0 @@
|
||||||
[pytest]
|
|
||||||
testpaths = tests/unit/
|
|
||||||
python_files = test_*_py3*.py
|
|
||||||
asyncio_mode = auto
|
|
||||||
filterwarnings =
|
|
||||||
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
|
|
||||||
ignore:ssl\.PROTOCOL_TLS is deprecated:DeprecationWarning:botocore.*
|
|
|
@ -1,12 +1,10 @@
|
||||||
from dependency_injector import providers
|
from dependency_injector import providers
|
||||||
|
|
||||||
|
|
||||||
class Animal:
|
class Animal: ...
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
class Cat(Animal):
|
class Cat(Animal): ...
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
# Test 1: to check Aggregate provider
|
# Test 1: to check Aggregate provider
|
||||||
|
@ -20,13 +18,19 @@ val1: str = provider1("a")
|
||||||
|
|
||||||
provider1_set_non_string_keys: providers.Aggregate[str] = providers.Aggregate()
|
provider1_set_non_string_keys: providers.Aggregate[str] = providers.Aggregate()
|
||||||
provider1_set_non_string_keys.set_providers({Cat: providers.Object("str")})
|
provider1_set_non_string_keys.set_providers({Cat: providers.Object("str")})
|
||||||
provider_set_non_string_1: providers.Provider[str] = provider1_set_non_string_keys.providers[Cat]
|
provider_set_non_string_1: providers.Provider[str] = (
|
||||||
|
provider1_set_non_string_keys.providers[Cat]
|
||||||
|
)
|
||||||
|
|
||||||
provider1_new_non_string_keys: providers.Aggregate[str] = providers.Aggregate(
|
provider1_new_non_string_keys: providers.Aggregate[str] = providers.Aggregate(
|
||||||
{Cat: providers.Object("str")},
|
{Cat: providers.Object("str")},
|
||||||
)
|
)
|
||||||
factory_new_non_string_1: providers.Provider[str] = provider1_new_non_string_keys.providers[Cat]
|
factory_new_non_string_1: providers.Provider[str] = (
|
||||||
|
provider1_new_non_string_keys.providers[Cat]
|
||||||
|
)
|
||||||
|
|
||||||
provider1_no_explicit_typing = providers.Aggregate(a=providers.Object("str"))
|
provider1_no_explicit_typing = providers.Aggregate(a=providers.Object("str"))
|
||||||
provider1_no_explicit_typing_factory: providers.Provider[str] = provider1_no_explicit_typing.providers["a"]
|
provider1_no_explicit_typing_factory: providers.Provider[str] = (
|
||||||
|
provider1_no_explicit_typing.providers["a"]
|
||||||
|
)
|
||||||
provider1_no_explicit_typing_object: str = provider1_no_explicit_typing("a")
|
provider1_no_explicit_typing_object: str = provider1_no_explicit_typing("a")
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user