Compare commits

..

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

153 changed files with 211335 additions and 4762 deletions

10
.coveragerc Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -10,20 +10,20 @@ jobs:
tests:
name: Run tests
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: 3.13
python-version: 3.12
- run: pip install tox
- run: tox
env:
TOXENV: 3.13
TOXENV: 3.12
linters:
name: Run linters
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
strategy:
matrix:
toxenv: [flake8, pydocstyle, mypy, pylint]
@ -31,7 +31,7 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: 3.13
python-version: 3.12
- run: pip install tox
- run: tox
env:
@ -40,18 +40,17 @@ jobs:
build-sdist:
name: Build source tarball
needs: [tests, linters]
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: 3.13
python-version: 3.12
- run: |
python -m pip install --upgrade build
python -m build --sdist
- uses: actions/upload-artifact@v4
python -m pip install --upgrade pip setuptools
python setup.py sdist
- uses: actions/upload-artifact@v3
with:
name: cibw-sdist
path: ./dist/*
build-wheels:
@ -60,63 +59,62 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-24.04, ubuntu-24.04-arm, windows-2022, macos-14]
os: [ubuntu-22.04, windows-2019, macos-14]
env:
CIBW_ENABLE: pypy
CIBW_ENVIRONMENT: >-
PIP_CONFIG_SETTINGS="build_ext=-j4"
DEPENDENCY_INJECTOR_LIMITED_API="1"
CFLAGS="-g0"
CIBW_SKIP: cp27-* cp313-*
steps:
- uses: actions/checkout@v3
- name: Build wheels
uses: pypa/cibuildwheel@v3.0.0
- uses: actions/upload-artifact@v4
uses: pypa/cibuildwheel@v2.20.0
- uses: actions/upload-artifact@v3
with:
name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
path: ./wheelhouse/*.whl
test-publish:
name: Upload release to TestPyPI
needs: [build-sdist, build-wheels]
runs-on: ubuntu-latest
environment: test-pypi
permissions:
id-token: write
build-wheels-linux-aarch64:
name: Build wheels (ubuntu-22.04-aarch64)
needs: [tests, linters]
runs-on: ubuntu-22.04
env:
CIBW_SKIP: cp27-* cp313-*
steps:
- uses: actions/download-artifact@v4
- uses: actions/checkout@v3
- name: Set up QEMU
if: runner.os == 'Linux'
uses: docker/setup-qemu-action@v2
- name: Build wheels
uses: pypa/cibuildwheel@v2.20.0
env:
CIBW_ARCHS_LINUX: aarch64
- uses: actions/upload-artifact@v3
with:
pattern: cibw-*
path: dist
merge-multiple: true
- uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
path: ./wheelhouse/*.whl
publish:
name: Upload release to PyPI
needs: [build-sdist, build-wheels, test-publish]
runs-on: ubuntu-latest
environment: pypi
permissions:
id-token: write
name: Publish on PyPI
needs: [build-sdist, build-wheels, build-wheels-linux-aarch64]
runs-on: ubuntu-22.04
steps:
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v3
with:
pattern: cibw-*
name: artifact
path: dist
merge-multiple: true
- uses: pypa/gh-action-pypi-publish@release/v1
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
# For publishing to Test PyPI, uncomment next two lines:
# password: ${{ secrets.TEST_PYPI_API_TOKEN }}
# repository_url: https://test.pypi.org/legacy/
publish-docs:
name: Publish docs
needs: [publish]
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: 3.13
python-version: 3.12
- run: pip install awscli
- run: pip install -r requirements-doc.txt
- run: pip install -e .

View File

@ -4,12 +4,12 @@ on: [push, pull_request, workflow_dispatch]
jobs:
test-on-different-versions:
name: Run tests
runs-on: ubuntu-latest
tests-on-legacy-versions:
name: Run tests on legacy versions
runs-on: ubuntu-20.04
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
python-version: [3.7]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
@ -18,26 +18,30 @@ jobs:
- run: pip install tox
- run: tox
env:
DEPENDENCY_INJECTOR_LIMITED_API: 1
TOXENV: ${{ matrix.python-version }}
test-different-pydantic-versions:
name: Run tests with different pydantic versions
test-on-different-versions:
name: Run tests
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.8, 3.9, "3.10", 3.11, 3.12]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: "3.12"
python-version: ${{ matrix.python-version }}
- run: pip install tox
- run: tox -e pydantic-v1,pydantic-v2
- run: tox
env:
TOXENV: ${{ matrix.python-version }}
test-coverage:
name: Run tests with coverage
runs-on: ubuntu-latest
env:
DEPENDENCY_INJECTOR_DEBUG_MODE: 1
PIP_VERBOSE: 1
# Cython's version <3 issue with tracing: "error: no member named 'use_tracing' in 'struct _PyCFrame'"
# DEPENDENCY_INJECTOR_DEBUG_MODE: 1
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
@ -45,8 +49,9 @@ jobs:
- uses: actions/setup-python@v4
with:
python-version: 3.12
- run: pip install tox
- run: tox -vv
- run: pip install tox cython==0.29.37
- run: make cythonize
- run: tox
env:
TOXENV: coveralls
@ -60,7 +65,7 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: 3.13
python-version: 3.12
- run: pip install tox
- run: tox
env:

13
.gitignore vendored
View File

@ -15,7 +15,6 @@ lib64/
parts/
sdist/
var/
wheelhouse/
*.egg-info/
.installed.cfg
*.egg
@ -64,11 +63,13 @@ venv*/
# Vim Rope
.ropeproject/
# Cython artifacts
src/**/*.c
src/**/*.h
src/**/*.so
src/**/*.html
# C extensions
src/dependency_injector/*.h
src/dependency_injector/*.so
src/dependency_injector/containers/*.h
src/dependency_injector/containers/*.so
src/dependency_injector/providers/*.h
src/dependency_injector/providers/*.so
# Workspace for samples
.workspace/

49
.pylintrc Normal file
View File

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

View File

@ -21,4 +21,3 @@ Dependency Injector Contributors
+ Thiago Hiromi (thiromi)
+ Felipe Rubio (krouw)
+ Anton Petrov (anton-petrov)
+ ZipFile (ZipFile)

View File

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

View File

@ -1,10 +1,14 @@
VERSION := $(shell python setup.py --version)
export COVERAGE_RCFILE := pyproject.toml
export CIBW_ENVIRONMENT_PASS_LINUX := CFLAGS PIP_CONFIG_SETTINGS DEPENDENCY_INJECTOR_LIMITED_API
export PIP_CONFIG_SETTINGS ?= build_ext=-j4
export DEPENDENCY_INJECTOR_LIMITED_API ?= 1
export CFLAGS ?= -g0
CYTHON_SRC := $(shell find src/dependency_injector -name '*.pyx')
CYTHON_DIRECTIVES = -Xlanguage_level=2
ifdef DEPENDENCY_INJECTOR_DEBUG_MODE
CYTHON_DIRECTIVES += -Xprofile=True
CYTHON_DIRECTIVES += -Xlinetrace=True
endif
clean:
# Clean sources
@ -21,17 +25,21 @@ clean:
find examples -name '*.py[co]' -delete
find examples -name '__pycache__' -delete
build: clean
# Compile C extensions
python setup.py build_ext --inplace
cythonize:
# Compile Cython to C
cython -a $(CYTHON_DIRECTIVES) $(CYTHON_SRC)
# Move all Cython html reports
mkdir -p reports/cython/
find src -name '*.html' -exec mv {} reports/cython/ \;
build: clean cythonize
# Compile C extensions
python setup.py build_ext --inplace
docs-live:
sphinx-autobuild docs docs/_build/html
install: uninstall clean build
install: uninstall clean cythonize
pip install -ve .
uninstall:
@ -40,9 +48,9 @@ uninstall:
test:
# Unit tests with coverage report
coverage erase
coverage run -m pytest
coverage report
coverage html
coverage run --rcfile=./.coveragerc -m pytest -c tests/.configs/pytest.ini
coverage report --rcfile=./.coveragerc
coverage html --rcfile=./.coveragerc
check:
flake8 src/dependency_injector/
@ -53,9 +61,9 @@ check:
mypy tests/typing
test-publish: build
test-publish: cythonize
# Create distributions
python -m build --sdist
python setup.py sdist
# Upload distributions to PyPI
twine upload --repository testpypi dist/dependency-injector-$(VERSION)*
@ -67,6 +75,3 @@ publish:
# Create and upload tag
git tag -a $(VERSION) -m 'version $(VERSION)'
git push --tags
wheels:
cibuildwheel --output-dir wheelhouse

View File

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

View File

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

View File

@ -72,7 +72,7 @@ release = version
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = "en"
language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:

View File

@ -16,7 +16,7 @@ The declarative container providers should only be used when you have the contai
Working with the providers of the container on the class level will influence all further
instances.
A declarative container cannot have any methods or attributes other than providers.
The declarative container can not have any methods or any other attributes then providers.
The container class provides next attributes:

View File

@ -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
:language: python
:emphasize-lines: 12
:emphasize-lines: 13
Tests
-----

View File

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

View File

@ -22,6 +22,5 @@ Explore the examples to see the ``Dependency Injector`` in action.
fastapi
fastapi-redis
fastapi-sqlalchemy
fastdepends
.. disqus::

View File

@ -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:
.. code-block:: text
.. code-block:: plain
Explicit is better than implicit

View File

@ -7,104 +7,6 @@ that were made in every particular version.
From version 0.7.6 *Dependency Injector* framework strictly
follows `Semantic versioning`_
4.48.3
------
- Allow annotated marker to be anywhere in the annotation list. Thanks to `@BrianPugh <https://github.com/BrianPugh>`_ for `#939 <https://github.com/ets-labs/python-dependency-injector/issues/939>`_.
- Fix FastDepends v3 compatibility. Thanks to `@AndrianEquestrian <https://github.com/AndrianEquestrian>`_ for `#933 <https://github.com/ets-labs/python-dependency-injector/issues/933>`_.
- Various type annotation improvements for providers. Thanks to `@leonarduschen <https://github.com/leonarduschen>`_ for `#927 <https://github.com/ets-labs/python-dependency-injector/pull/927>`_, `#932 <https://github.com/ets-labs/python-dependency-injector/pull/932>`_ and `#935 <https://github.com/ets-labs/python-dependency-injector/pull/935>`_.
4.48.2
------
- Add ``warn_unresolved=True`` to ``WiringConfiguration`` and ``container.wire()``
to produce warnings on unresolved string identifiers.
- ABI3 wheels are now built only for CPython version >=3.10 (see issue `#919 <https://github.com/ets-labs/python-dependency-injector/issues/919>`_).
4.48.1
------
* Improve performance of ``dependency_injector._cwiring.DependencyResolver``
* Add ``typing-extensions`` as a dependency for older Python versions (<3.11)
* Produce warning on ``@inject``s without ``Provide[...]`` marks
* Add support for `resource_type` in ``Lifespan``s
4.48.0
------
- Improve performance of wiring (`#897 <https://github.com/ets-labs/python-dependency-injector/pull/897>`_)
- Add Context Manager support to Resource provider (`#899 <https://github.com/ets-labs/python-dependency-injector/pull/899>`_)
- Add support for async generator injections (`#900 <https://github.com/ets-labs/python-dependency-injector/pull/900>`_)
- Fix unintended dependency on ``typing_extensions`` (`#902 <https://github.com/ets-labs/python-dependency-injector/pull/902>`_)
- Add support for Fast Depends (`#898 <https://github.com/ets-labs/python-dependency-injector/pull/898>`_)
- Add ``resource_type`` parameter to init and shutdown resources using specialized providers (`#858 <https://github.com/ets-labs/python-dependency-injector/pull/858>`_)
4.47.1
------
- Fix typing for wiring marker (`#892 <https://github.com/ets-labs/python-dependency-injector/pull/896>`_)
- Strip debug symbols in wheels
4.47.0
------
- Add support for ``Annotated`` type for module and class attribute injection in wiring,
with updated documentation and examples.
See discussion:
https://github.com/ets-labs/python-dependency-injector/pull/721#issuecomment-2025263718
- Fix ``root`` property shadowing in ``ConfigurationOption`` (`#875 <https://github.com/ets-labs/python-dependency-injector/pull/875>`_)
- Fix incorrect monkeypatching during ``wire()`` that could violate MRO in some classes (`#886 <https://github.com/ets-labs/python-dependency-injector/pull/886>`_)
- ABI3 wheels are now published for CPython.
- Drop support of Python 3.7.
4.46.0
------
- Add option to disable env var interpolation in configs (`#861 <https://github.com/ets-labs/python-dependency-injector/pull/861>`_)
- Fix ``Closing`` dependency resolution (`#852 <https://github.com/ets-labs/python-dependency-injector/pull/852>`_)
- Add support for ``inspect.iscoroutinefunction()`` in ``Coroutine`` provider (`#830 <https://github.com/ets-labs/python-dependency-injector/pull/830>`_)
- Fix broken wiring of sync inject-decorated methods (`#673 <https://github.com/ets-labs/python-dependency-injector/pull/673>`_)
- Add support for ``typing.Annotated`` (`#721 <https://github.com/ets-labs/python-dependency-injector/pull/721>`_, `#853 <https://github.com/ets-labs/python-dependency-injector/pull/853>`_)
- Documentation updates for movie-lister example (`#747 <https://github.com/ets-labs/python-dependency-injector/pull/747>`_)
- Fix type propagation in ``Provider.provider`` (`#744 <https://github.com/ets-labs/python-dependency-injector/pull/744>`_)
Many thanks for the contributions to:
- `ZipFile <https://github.com/ZipFile>`_
- `Yegor Statkevich <https://github.com/jazzthief>`_
- `Federico Tomasi <https://github.com/federinik>`_
- `Martin Lafrance <https://github.com/martlaf>`_
- `Philip Bjorge <https://github.com/philipbjorge>`_
- `Ilya Kazakov <https://github.com/mrKazzila>`_
4.45.0
--------
- Add Starlette lifespan handler implementation (`#683 <https://github.com/ets-labs/python-dependency-injector/pull/683>`_).
- Raise exception in ``ThreadLocalSingleton`` instead of hiding it in finally (`#845 <https://github.com/ets-labs/python-dependency-injector/pull/845>`_).
- Improve debuggability of ``deepcopy`` errors (`#839 <https://github.com/ets-labs/python-dependency-injector/pull/839>`_).
- Update examples (`#838 <https://github.com/ets-labs/python-dependency-injector/pull/838>`_).
- Upgrade testing dependencies (`#837 <https://github.com/ets-labs/python-dependency-injector/pull/837>`_).
- Add minor fixes to the documentation (`#709 <https://github.com/ets-labs/python-dependency-injector/pull/709>`_).
- Remove ``six`` from the dependencies (`3ba4704 <https://github.com/ets-labs/python-dependency-injector/commit/3ba4704bc1cb00310749fd2eda0c8221167c313c>`_).
Many thanks for the contributions to:
- `ZipFile <https://github.com/ZipFile>`_
- `František Trebuňa <https://github.com/gortibaldik>`_
- `JC (Jonathan Chen) <https://github.com/dijonkitchen>`_
4.44.0
--------
- Implement support for Pydantic 2. PR: `#832 <https://github.com/ets-labs/python-dependency-injector/pull/832>`_.
- Implement `PEP-517 <https://peps.python.org/pep-0517/>`_, `PEP-518 <https://peps.python.org/pep-0518/>`_, and
`PEP-621 <https://peps.python.org/pep-0621/>`_. PR: `#829 <https://github.com/ets-labs/python-dependency-injector/pull/829>`_.
Many thanks to `ZipFile <https://github.com/ZipFile>`_ for both contributions.
4.43.0
--------
- Add support for Python 3.13.
- Migrate to Cython 3 (version 3.0.11). Many thanks to `ZipFile <https://github.com/ZipFile>`_ for
this contribution `#813 <https://github.com/ets-labs/python-dependency-injector/pull/813>`_.
4.42.0
--------
- Promote release ``4.42.0b1`` to a production release.
@ -409,8 +311,8 @@ Many thanks to `ZipFile <https://github.com/ZipFile>`_ for both contributions.
- Make refactoring of wiring module and tests.
See PR # `#406 <https://github.com/ets-labs/python-dependency-injector/issues/406>`_.
Thanks to `@withshubh <https://github.com/withshubh>`_ for the contribution:
- Remove unused imports in tests.
- Use literal syntax to create data structure in tests.
- Remove unused imports in tests.
- Use literal syntax to create data structure in tests.
- Add integration with a static analysis tool `DeepSource <https://deepsource.io/>`_.
4.26.0

View File

@ -183,22 +183,22 @@ See also: :ref:`configuration-envs-interpolation`.
Loading from a Pydantic settings
--------------------------------
``Configuration`` provider can load configuration from a ``pydantic_settings.BaseSettings`` object using the
``Configuration`` provider can load configuration from a ``pydantic`` settings object using the
:py:meth:`Configuration.from_pydantic` method:
.. literalinclude:: ../../examples/providers/configuration/configuration_pydantic.py
:language: python
:lines: 3-
:emphasize-lines: 32
:emphasize-lines: 31
To get the data from pydantic settings ``Configuration`` provider calls its ``model_dump()`` method.
To get the data from pydantic settings ``Configuration`` provider calls ``Settings.dict()`` method.
If you need to pass an argument to this call, use ``.from_pydantic()`` keyword arguments.
.. code-block:: python
container.config.from_pydantic(Settings(), exclude={"optional"})
Alternatively, you can provide a ``pydantic_settings.BaseSettings`` object over the configuration provider argument. In that case,
Alternatively, you can provide a ``pydantic`` settings object over the configuration provider argument. In that case,
the container will call ``config.from_pydantic()`` automatically:
.. code-block:: python
@ -215,23 +215,18 @@ the container will call ``config.from_pydantic()`` automatically:
.. note::
``Dependency Injector`` doesn't install ``pydantic-settings`` by default.
``Dependency Injector`` doesn't install ``pydantic`` by default.
You can install the ``Dependency Injector`` with an extra dependency::
pip install dependency-injector[pydantic2]
pip install dependency-injector[pydantic]
or install ``pydantic-settings`` directly::
or install ``pydantic`` directly::
pip install pydantic-settings
pip install pydantic
*Don't forget to mirror the changes in the requirements file.*
.. 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
-------------------------
@ -366,19 +361,6 @@ See also: :ref:`configuration-strict-mode`.
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
------------------------------

View File

@ -61,12 +61,11 @@ When you call ``.shutdown()`` method on a resource provider, it will remove the
if any, and switch to uninitialized state. Some of resource initializer types support specifying custom
resource shutdown.
Resource provider supports 4 types of initializers:
Resource provider supports 3 types of initializers:
- Function
- Context Manager
- Generator (legacy)
- Subclass of ``resources.Resource`` (legacy)
- Generator
- Subclass of ``resources.Resource``
Function initializer
--------------------
@ -104,44 +103,8 @@ you configure global resource:
Function initializer does not provide a way to specify custom resource shutdown.
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)
------------------------------
Generator initializer
---------------------
Resource provider can use 2-step generators:
@ -191,13 +154,8 @@ object is not mandatory. You can leave ``yield`` statement empty:
argument2=...,
)
.. note::
Generator initializers are automatically wrapped with ``contextmanager`` or ``asynccontextmanager`` decorator when
provided to a ``Resource`` provider.
Subclass initializer (legacy)
-----------------------------
Subclass initializer
--------------------
You can create resource initializer by implementing a subclass of the ``resources.Resource``:
@ -252,72 +210,6 @@ first argument.
.. _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
---------------------------------------------------
@ -371,11 +263,10 @@ Asynchronous function initializer:
argument2=...,
)
Asynchronous Context Manager initializer:
Asynchronous generator initializer:
.. code-block:: python
@asynccontextmanager
async def init_async_resource(argument1=..., argument2=...):
connection = await connect()
yield connection
@ -467,54 +358,5 @@ See also:
- Wiring :ref:`async-injections-wiring`
- :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::

View File

@ -24,7 +24,7 @@ returns it on the rest of the calls.
.. note::
``Singleton`` provider makes dependencies injection only when it creates an object. When an object
``Singleton`` provider makes dependencies injection only when creates an object. When an object
is created and memorized ``Singleton`` provider just returns it without applying injections.
Specialization of the provided type and abstract singletons work the same like like for the
@ -33,7 +33,7 @@ factories:
- :ref:`factory-specialize-provided-type`
- :ref:`abstract-factory`
``Singleton`` provider scope is tied to the container. Two different containers will provide
``Singleton`` provider scope is tied to the container. Two different containers will provider
two different singleton objects:
.. literalinclude:: ../../examples/providers/singleton_multiple_containers.py

View File

@ -257,7 +257,7 @@ Let's check that it works. Open another terminal session and use ``httpie``:
You should see:
.. code-block:: http
.. code-block:: json
HTTP/1.1 200 OK
Content-Length: 844
@ -596,7 +596,7 @@ and make a request to the API in the terminal:
You should see:
.. code-block:: http
.. code-block:: json
HTTP/1.1 200 OK
Content-Length: 492

View File

@ -18,7 +18,7 @@ In this tutorial we will use:
- Python 3
- Docker
- Docker Compose
- Docker-compose
Start from the scratch or jump to the section:
@ -47,27 +47,28 @@ response it will log:
Prerequisites
-------------
We will use `docker compose <https://docs.docker.com/compose/>`_ in this tutorial. Let's check the versions:
We will use `Docker <https://www.docker.com/>`_ and
`docker-compose <https://docs.docker.com/compose/>`_ in this tutorial. Let's check the versions:
.. code-block:: bash
docker --version
docker compose version
docker-compose --version
The output should look something like:
.. code-block:: bash
Docker version 27.3.1, build ce12230
Docker Compose version v2.29.7
Docker version 20.10.5, build 55c4c88
docker-compose version 1.29.0, build 07737305
.. 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:
- `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.
@ -128,13 +129,13 @@ Put next lines into the ``requirements.txt`` file:
pytest-cov
Second, we need to create the ``Dockerfile``. It will describe the daemon's build process and
specify how to run it. We will use ``python:3.13-bookworm`` as a base image.
specify how to run it. We will use ``python:3.9-buster`` as a base image.
Put next lines into the ``Dockerfile`` file:
.. code-block:: bash
FROM python:3.13-bookworm
FROM python:3.10-buster
ENV PYTHONUNBUFFERED=1
@ -154,6 +155,8 @@ Put next lines into the ``docker-compose.yml`` file:
.. code-block:: yaml
version: "3.7"
services:
monitor:
@ -168,7 +171,7 @@ Run in the terminal:
.. 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:
@ -181,7 +184,7 @@ After the build is done run the container:
.. code-block:: bash
docker compose up
docker-compose up
The output should look like:
@ -458,7 +461,7 @@ Run in the terminal:
.. code-block:: bash
docker compose up
docker-compose up
The output should look like:
@ -702,7 +705,7 @@ Run in the terminal:
.. code-block:: bash
docker compose up
docker-compose up
You should see:
@ -810,7 +813,7 @@ Run in the terminal:
.. code-block:: bash
docker compose up
docker-compose up
You should see:
@ -962,16 +965,15 @@ Run in the terminal:
.. 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:
.. code-block:: bash
platform linux -- Python 3.13.1, pytest-8.3.4, pluggy-1.5.0
platform linux -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
rootdir: /code
plugins: cov-6.0.0, asyncio-0.24.0
asyncio: mode=Mode.STRICT, default_loop_scope=None
plugins: asyncio-0.16.0, cov-3.0.0
collected 2 items
monitoringdaemon/tests.py .. [100%]

View File

@ -84,7 +84,7 @@ Create next structure in the project root directory. All files are empty. That's
Initial project layout:
.. code-block:: text
.. code-block:: bash
./
├── 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:
.. code-block:: text
.. code-block:: bash
dependency-injector
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
``fixtures.py`` inside of it:
.. code-block:: text
.. code-block:: bash
:emphasize-lines: 2-3
./
@ -205,13 +205,13 @@ Now run in the terminal:
You should see:
.. code-block:: text
.. code-block:: bash
OK
Check that files ``movies.csv`` and ``movies.db`` have appeared in the ``data/`` folder:
.. code-block:: text
.. code-block:: bash
: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:
.. code-block:: text
.. code-block:: bash
:emphasize-lines: 10
./
@ -356,7 +356,7 @@ Let's move on to the finders.
Create the ``finders.py`` in the ``movies`` package:
.. code-block:: text
.. code-block:: bash
: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:
.. code-block:: text
.. code-block:: bash
:emphasize-lines: 12
./
@ -613,7 +613,7 @@ Run in the terminal:
You should see:
.. code-block:: text
.. code-block:: plain
Francis Lawrence movies:
- Movie(title='The Hunger Games: Mockingjay - Part 2', year=2015, director='Francis Lawrence')
@ -752,7 +752,7 @@ Run in the terminal:
You should see:
.. code-block:: text
.. code-block:: plain
Francis Lawrence movies:
- 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:
.. code-block:: text
.. code-block:: plain
Francis Lawrence movies:
- 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:
.. code-block:: text
.. code-block:: bash
:emphasize-lines: 13
./
@ -911,7 +911,7 @@ Create ``tests.py`` in the ``movies`` package:
and put next into it:
.. code-block:: python
:emphasize-lines: 41,50
:emphasize-lines: 36,51
"""Tests module."""
@ -941,18 +941,13 @@ and put next into it:
return container
@pytest.fixture
def finder_mock(container):
def test_movies_directed_by(container):
finder_mock = mock.Mock()
finder_mock.find_all.return_value = [
container.movie("The 33", 2015, "Patricia Riggen"),
container.movie("The Jungle Book", 2016, "Jon Favreau"),
]
return finder_mock
def test_movies_directed_by(container, finder_mock):
with container.finder.override(finder_mock):
lister = container.lister()
movies = lister.movies_directed_by("Jon Favreau")
@ -961,7 +956,13 @@ and put next into it:
assert movies[0].title == "The Jungle Book"
def test_movies_released_in(container, finder_mock):
def test_movies_released_in(container):
finder_mock = mock.Mock()
finder_mock.find_all.return_value = [
container.movie("The 33", 2015, "Patricia Riggen"),
container.movie("The Jungle Book", 2016, "Jon Favreau"),
]
with container.finder.override(finder_mock):
lister = container.lister()
movies = lister.movies_released_in(2015)
@ -977,7 +978,7 @@ Run in the terminal:
You should see:
.. code-block:: text
.. code-block::
platform darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
plugins: cov-3.0.0
@ -994,9 +995,9 @@ You should see:
movies/entities.py 7 1 86%
movies/finders.py 26 13 50%
movies/listers.py 8 0 100%
movies/tests.py 24 0 100%
movies/tests.py 23 0 100%
------------------------------------------
TOTAL 90 30 67%
TOTAL 89 30 66%
.. note::

View File

@ -280,7 +280,7 @@ Now let's fill in the layout.
Put next into the ``base.html``:
.. code-block:: jinja
.. code-block:: html
<!doctype html>
<html lang="en">
@ -313,7 +313,7 @@ And put something to the index page.
Put next into the ``index.html``:
.. code-block:: jinja
.. code-block:: html
{% extends "base.html" %}

View File

@ -64,7 +64,7 @@ FastAPI example:
@app.api_route("/")
@inject
async def index(service: Annotated[Service, Depends(Provide[Container.service])]):
async def index(service: Service = Depends(Provide[Container.service])):
value = await service.process()
return {"result": value}
@ -127,7 +127,6 @@ To inject the provider itself use ``Provide[foo.provider]``:
def foo(bar_provider: Factory[Bar] = Provide[Container.bar.provider]):
bar = bar_provider(argument="baz")
...
You can also use ``Provider[foo]`` for injecting the provider itself:
.. code-block:: python
@ -251,73 +250,17 @@ To inject a container use special identifier ``<container>``:
def foo(container: Container = Provide["<container>"]) -> None:
...
Caveats
~~~~~~~
While using string identifiers you may not notice a typo in the identifier until the code is executed.
In order to aid with catching such errors early, you may pass `warn_unresolved=True` to the ``wire`` method and/or :class:`WiringConfiguration`:
.. code-block:: python
:emphasize-lines: 4
class Container(containers.DeclarativeContainer):
wiring_config = containers.WiringConfiguration(
modules=["yourapp.module"],
warn_unresolved=True,
)
Or:
.. code-block:: python
:emphasize-lines: 4
container = Container()
container.wire(
modules=["yourapp.module"],
warn_unresolved=True,
)
Making 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:
You can use wiring to make injections into modules and class attributes.
.. literalinclude:: ../examples/wiring/example_attribute.py
:language: python
:lines: 3-
: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:
.. code-block:: python
@ -658,36 +601,6 @@ or with a single container ``register_loader_containers(container)`` multiple ti
To unregister a container use ``unregister_loader_containers(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
---------------------------------
@ -719,6 +632,5 @@ Take a look at other application examples:
- :ref:`fastapi-example`
- :ref:`fastapi-redis-example`
- :ref:`fastapi-sqlalchemy-example`
- :ref:`fastdepends-example`
.. disqus::

View File

@ -98,9 +98,8 @@ The output should be something like:
.. code-block::
platform linux -- Python 3.12.3, pytest-8.3.2, pluggy-1.5.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
platform darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
plugins: asyncio-0.16.0, anyio-3.3.4, aiohttp-0.3.0, cov-3.0.0
collected 3 items
giphynavigator/tests.py ... [100%]

View File

@ -3,15 +3,11 @@
from unittest import mock
import pytest
import pytest_asyncio
from giphynavigator.application import create_app
from giphynavigator.giphy import GiphyClient
pytestmark = pytest.mark.asyncio
@pytest.fixture
def app():
app = create_app()
@ -19,9 +15,9 @@ def app():
app.container.unwire()
@pytest_asyncio.fixture
async def client(app, aiohttp_client):
return await aiohttp_client(app)
@pytest.fixture
def client(app, aiohttp_client, loop):
return loop.run_until_complete(aiohttp_client(app))
async def test_index(client, app):

View File

@ -2,5 +2,4 @@ dependency-injector
aiohttp
pyyaml
pytest-aiohttp
pytest-asyncio
pytest-cov

View File

@ -1,4 +1,4 @@
FROM python:3.13-bookworm
FROM python:3.10-buster
ENV PYTHONUNBUFFERED=1

View File

@ -13,13 +13,13 @@ Build the Docker image:
.. code-block:: bash
docker compose build
docker-compose build
Run the docker-compose environment:
.. code-block:: bash
docker compose up
docker-compose up
The output should be something like:
@ -59,16 +59,15 @@ To run the tests do:
.. 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:
.. code-block::
platform linux -- Python 3.13.1, pytest-8.3.4, pluggy-1.5.0
platform linux -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
rootdir: /code
plugins: cov-6.0.0, asyncio-0.24.0
asyncio: mode=Mode.STRICT, default_loop_scope=None
plugins: asyncio-0.16.0, cov-3.0.0
collected 2 items
monitoringdaemon/tests.py .. [100%]

View File

@ -61,7 +61,7 @@ async def test_example_monitor(container, caplog):
@pytest.mark.asyncio
async def test_dispatcher(container, caplog):
async def test_dispatcher(container, caplog, event_loop):
caplog.set_level("INFO")
example_monitor_mock = mock.AsyncMock()
@ -72,7 +72,6 @@ async def test_dispatcher(container, caplog):
httpbin_monitor=httpbin_monitor_mock,
):
dispatcher = container.dispatcher()
event_loop = asyncio.get_running_loop()
event_loop.create_task(dispatcher.start())
await asyncio.sleep(0.1)
dispatcher.stop()

View File

@ -1,4 +1,4 @@
FROM python:3.13-bookworm
FROM python:3.10-buster
ENV PYTHONUNBUFFERED=1

View File

@ -12,13 +12,13 @@ Build the Docker image:
.. code-block:: bash
docker compose build
docker-compose build
Run the docker-compose environment:
.. code-block:: bash
docker compose up
docker-compose up
The output should be something like:
@ -54,16 +54,16 @@ To run the tests do:
.. 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:
.. code-block::
platform linux -- Python 3.13.1, pytest-8.3.4, pluggy-1.5.0
platform linux -- Python 3.10.9, pytest-7.2.0, pluggy-1.0.0
rootdir: /code
plugins: cov-6.0.0, asyncio-0.24.0, anyio-4.7.0
asyncio: mode=Mode.STRICT, default_loop_scope=None
plugins: cov-4.0.0, asyncio-0.20.3
collected 1 item
fastapiredis/tests.py . [100%]
@ -77,4 +77,4 @@ The output should be something like:
fastapiredis/services.py 7 3 57%
fastapiredis/tests.py 18 0 100%
-------------------------------------------------
TOTAL 52 7 87%
TOTAL 52 7 87%

View File

@ -1,22 +1,18 @@
"""Application module."""
from typing import Annotated
from fastapi import Depends, FastAPI
from dependency_injector.wiring import Provide, inject
from dependency_injector.wiring import inject, Provide
from fastapi import FastAPI, Depends
from .containers import Container
from .services import Service
app = FastAPI()
@app.api_route("/")
@inject
async def index(
service: Annotated[Service, Depends(Provide[Container.service])]
) -> dict[str, str]:
async def index(service: Service = Depends(Provide[Container.service])):
value = await service.process()
return {"result": value}

View File

@ -1,6 +1,6 @@
from typing import AsyncIterator
from redis.asyncio import from_url, Redis
from aioredis import from_url, Redis
async def init_redis_pool(host: str, password: str) -> AsyncIterator[Redis]:

View File

@ -1,6 +1,6 @@
"""Services module."""
from redis.asyncio import Redis
from aioredis import Redis
class Service:

View File

@ -3,7 +3,7 @@
from unittest import mock
import pytest
from httpx import ASGITransport, AsyncClient
from httpx import AsyncClient
from .application import app, container
from .services import Service
@ -11,10 +11,7 @@ from .services import Service
@pytest.fixture
def client(event_loop):
client = AsyncClient(
transport=ASGITransport(app=app),
base_url="http://test",
)
client = AsyncClient(app=app, base_url="http://test")
yield client
event_loop.run_until_complete(client.aclose())

View File

@ -1,7 +1,7 @@
dependency-injector
fastapi
uvicorn
redis>=4.2
aioredis
# For testing:
pytest

View File

@ -1,7 +1,4 @@
from typing import Annotated
from fastapi import Depends, FastAPI
from fastapi import FastAPI, Depends
from dependency_injector import containers, providers
from dependency_injector.wiring import Provide, inject
@ -21,9 +18,7 @@ app = FastAPI()
@app.api_route("/")
@inject
async def index(
service: Annotated[Service, Depends(Provide[Container.service])]
) -> dict[str, str]:
async def index(service: Service = Depends(Provide[Container.service])):
result = await service.process()
return {"result": result}

View File

@ -1,18 +1,14 @@
from unittest import mock
import pytest
import pytest_asyncio
from httpx import ASGITransport, AsyncClient
from httpx import AsyncClient
from fastapi_di_example import app, container, Service
@pytest_asyncio.fixture
async def client():
async with AsyncClient(
transport=ASGITransport(app=app),
base_url="http://test",
) as client:
@pytest.fixture
async def client(event_loop):
async with AsyncClient(app=app, base_url="http://test") as client:
yield client

View File

@ -1,4 +1,4 @@
FROM python:3.13-bookworm
FROM python:3.10-buster
ENV PYTHONUNBUFFERED=1
ENV HOST=0.0.0.0

View File

@ -15,13 +15,13 @@ Build the Docker image:
.. code-block:: bash
docker compose build
docker-compose build
Run the docker-compose environment:
.. code-block:: bash
docker compose up
docker-compose up
The output should be something like:
@ -67,15 +67,15 @@ To run the tests do:
.. 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:
.. code-block::
platform linux -- Python 3.13.1, pytest-8.3.4, pluggy-1.5.0
platform linux -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
rootdir: /code
plugins: cov-6.0.0, anyio-4.7.0
plugins: cov-3.0.0
collected 7 items
webapp/tests.py ....... [100%]

View File

@ -1,5 +1,5 @@
dependency-injector
fastapi[standard]
fastapi
uvicorn
pyyaml
sqlalchemy

View File

@ -1,14 +1,11 @@
"""Endpoints module."""
from typing import Annotated
from fastapi import APIRouter, Depends, Response, status
from dependency_injector.wiring import Provide, inject
from dependency_injector.wiring import inject, Provide
from .containers import Container
from .repositories import NotFoundError
from .services import UserService
from .repositories import NotFoundError
router = APIRouter()
@ -16,7 +13,7 @@ router = APIRouter()
@router.get("/users")
@inject
def get_list(
user_service: Annotated[UserService, Depends(Provide[Container.user_service])],
user_service: UserService = Depends(Provide[Container.user_service]),
):
return user_service.get_users()
@ -24,8 +21,8 @@ def get_list(
@router.get("/users/{user_id}")
@inject
def get_by_id(
user_id: int,
user_service: Annotated[UserService, Depends(Provide[Container.user_service])],
user_id: int,
user_service: UserService = Depends(Provide[Container.user_service]),
):
try:
return user_service.get_user_by_id(user_id)
@ -36,7 +33,7 @@ def get_by_id(
@router.post("/users", status_code=status.HTTP_201_CREATED)
@inject
def add(
user_service: Annotated[UserService, Depends(Provide[Container.user_service])],
user_service: UserService = Depends(Provide[Container.user_service]),
):
return user_service.create_user()
@ -44,9 +41,9 @@ def add(
@router.delete("/users/{user_id}", status_code=status.HTTP_204_NO_CONTENT)
@inject
def remove(
user_id: int,
user_service: Annotated[UserService, Depends(Provide[Container.user_service])],
) -> Response:
user_id: int,
user_service: UserService = Depends(Provide[Container.user_service]),
):
try:
user_service.delete_user_by_id(user_id)
except NotFoundError:

View File

@ -101,9 +101,9 @@ The output should be something like:
.. code-block::
platform linux -- Python 3.12.3, pytest-8.3.2, pluggy-1.5.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
platform darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
plugins: asyncio-0.16.0, cov-3.0.0
collected 3 items
giphynavigator/tests.py ... [100%]

View File

@ -1,14 +1,13 @@
"""Endpoints module."""
from typing import Annotated, List
from typing import Optional, List
from fastapi import APIRouter, Depends
from pydantic import BaseModel
from dependency_injector.wiring import inject, Provide
from dependency_injector.wiring import Provide, inject
from .containers import Container
from .services import SearchService
from .containers import Container
class Gif(BaseModel):
@ -27,15 +26,11 @@ router = APIRouter()
@router.get("/", response_model=Response)
@inject
async def index(
default_query: Annotated[str, Depends(Provide[Container.config.default.query])],
default_limit: Annotated[
int, Depends(Provide[Container.config.default.limit.as_int()])
],
search_service: Annotated[
SearchService, Depends(Provide[Container.search_service])
],
query: str | None = None,
limit: int | None = None,
query: Optional[str] = None,
limit: Optional[str] = None,
default_query: str = Depends(Provide[Container.config.default.query]),
default_limit: int = Depends(Provide[Container.config.default.limit.as_int()]),
search_service: SearchService = Depends(Provide[Container.search_service]),
):
query = query or default_query
limit = limit or default_limit

View File

@ -3,19 +3,15 @@
from unittest import mock
import pytest
import pytest_asyncio
from httpx import ASGITransport, AsyncClient
from httpx import AsyncClient
from giphynavigator.application import app
from giphynavigator.giphy import GiphyClient
@pytest_asyncio.fixture
@pytest.fixture
async def client():
async with AsyncClient(
transport=ASGITransport(app=app),
base_url="http://test",
) as client:
async with AsyncClient(app=app, base_url="http://test") as client:
yield client

View File

@ -81,9 +81,8 @@ The output should be something like:
.. code-block::
platform linux -- Python 3.12.3, pytest-8.3.2, pluggy-1.5.0
plugins: cov-6.0.0, flask-1.3.0
asyncio: mode=Mode.STRICT, default_loop_scope=None
platform darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
plugins: cov-3.0.0, flask-1.2.0
collected 2 items
githubnavigator/tests.py .. [100%]

View File

@ -1,7 +1,7 @@
"""Application module."""
from flask import Flask
from flask_bootstrap import Bootstrap4
from flask_bootstrap import Bootstrap
from .containers import Container
from .blueprints import example
@ -15,7 +15,7 @@ def create_app() -> Flask:
app.container = container
app.register_blueprint(example.blueprint)
bootstrap = Bootstrap4()
bootstrap = Bootstrap()
bootstrap.init_app(app)
return app

View File

@ -81,9 +81,8 @@ The output should be something like:
.. code-block::
platform linux -- Python 3.12.3, pytest-8.3.2, pluggy-1.5.0
plugins: cov-6.0.0, flask-1.3.0
asyncio: mode=Mode.STRICT, default_loop_scope=None
platform darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
plugins: cov-3.0.0, flask-1.2.0
collected 2 items
githubnavigator/tests.py .. [100%]

View File

@ -1,7 +1,7 @@
"""Application module."""
from flask import Flask
from flask_bootstrap import Bootstrap4
from flask_bootstrap import Bootstrap
from .containers import Container
from . import views
@ -15,7 +15,7 @@ def create_app() -> Flask:
app.container = container
app.add_url_rule("/", "index", views.index)
bootstrap = Bootstrap4()
bootstrap = Bootstrap()
bootstrap.init_app(app)
return app

View File

@ -58,8 +58,8 @@ The output should be something like:
.. code-block::
platform linux -- Python 3.12.3, pytest-8.3.2, pluggy-1.5.0
plugins: cov-6.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
collected 2 items
movies/tests.py .. [100%]

View File

@ -18,9 +18,10 @@ SQLITE_FILE = DIR / "movies.db"
def create_csv(movies_data, path):
with open(path, "w", newline="") as opened_file:
with open(path, "w") as opened_file:
writer = csv.writer(opened_file)
writer.writerows(movies_data)
for row in movies_data:
writer.writerow(row)
def create_sqlite(movies_data, path):

View File

@ -29,7 +29,7 @@ class CsvMovieFinder(MovieFinder):
super().__init__(movie_factory)
def find_all(self) -> List[Movie]:
with open(self._csv_file_path, newline="") as csv_file:
with open(self._csv_file_path) as csv_file:
csv_reader = csv.reader(csv_file, delimiter=self._delimiter)
return [self._movie_factory(*row) for row in csv_reader]

View File

@ -26,18 +26,13 @@ def container():
return container
@pytest.fixture
def finder_mock(container):
def test_movies_directed_by(container):
finder_mock = mock.Mock()
finder_mock.find_all.return_value = [
container.movie("The 33", 2015, "Patricia Riggen"),
container.movie("The Jungle Book", 2016, "Jon Favreau"),
]
return finder_mock
def test_movies_directed_by(container, finder_mock):
with container.finder.override(finder_mock):
lister = container.lister()
movies = lister.movies_directed_by("Jon Favreau")
@ -46,7 +41,13 @@ def test_movies_directed_by(container, finder_mock):
assert movies[0].title == "The Jungle Book"
def test_movies_released_in(container, finder_mock):
def test_movies_released_in(container):
finder_mock = mock.Mock()
finder_mock.find_all.return_value = [
container.movie("The 33", 2015, "Patricia Riggen"),
container.movie("The Jungle Book", 2016, "Jon Favreau"),
]
with container.finder.override(finder_mock):
lister = container.lister()
movies = lister.movies_released_in(2015)

View File

@ -27,7 +27,7 @@ To run the application do:
.. code-block:: bash
export GIPHY_API_KEY=wBJ2wZG7SRqfrU9nPgPiWvORmloDyuL0
sanic giphynavigator.application:create_app
python -m giphynavigator
The output should be something like:
@ -98,9 +98,8 @@ The output should be something like:
.. code-block::
platform linux -- Python 3.12.3, pytest-8.3.2, pluggy-1.5.0
plugins: cov-6.0.0, anyio-4.4.0, asyncio-0.24.0
asyncio: mode=Mode.STRICT, default_loop_scope=None
platform darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
plugins: sanic-1.9.1, anyio-3.3.4, cov-3.0.0
collected 3 items
giphynavigator/tests.py ... [100%]

View File

@ -8,8 +8,6 @@ from sanic import Sanic
from giphynavigator.application import create_app
from giphynavigator.giphy import GiphyClient
pytestmark = pytest.mark.asyncio
@pytest.fixture
def app():
@ -19,7 +17,12 @@ def app():
app.ctx.container.unwire()
async def test_index(app):
@pytest.fixture
def test_client(loop, app, sanic_client):
return loop.run_until_complete(sanic_client(app))
async def test_index(app, test_client):
giphy_client_mock = mock.AsyncMock(spec=GiphyClient)
giphy_client_mock.search.return_value = {
"data": [
@ -29,7 +32,7 @@ async def test_index(app):
}
with app.ctx.container.giphy_client.override(giphy_client_mock):
_, response = await app.asgi_client.get(
response = await test_client.get(
"/",
params={
"query": "test",
@ -38,7 +41,7 @@ async def test_index(app):
)
assert response.status_code == 200
data = response.json
data = response.json()
assert data == {
"query": "test",
"limit": 10,
@ -49,30 +52,30 @@ async def test_index(app):
}
async def test_index_no_data(app):
async def test_index_no_data(app, test_client):
giphy_client_mock = mock.AsyncMock(spec=GiphyClient)
giphy_client_mock.search.return_value = {
"data": [],
}
with app.ctx.container.giphy_client.override(giphy_client_mock):
_, response = await app.asgi_client.get("/")
response = await test_client.get("/")
assert response.status_code == 200
data = response.json
data = response.json()
assert data["gifs"] == []
async def test_index_default_params(app):
async def test_index_default_params(app, test_client):
giphy_client_mock = mock.AsyncMock(spec=GiphyClient)
giphy_client_mock.search.return_value = {
"data": [],
}
with app.ctx.container.giphy_client.override(giphy_client_mock):
_, response = await app.asgi_client.get("/")
response = await test_client.get("/")
assert response.status_code == 200
data = response.json
data = response.json()
assert data["query"] == app.ctx.container.config.default.query()
assert data["limit"] == app.ctx.container.config.default.limit()

View File

@ -1,6 +1,6 @@
dependency-injector
sanic
sanic-testing
sanic<=21.6
aiohttp
pyyaml
pytest-sanic
pytest-cov

View File

@ -1,39 +0,0 @@
Integration With Starlette-based Frameworks
===========================================
This is a `Starlette <https://www.starlette.io/>`_ +
`Dependency Injector <https://python-dependency-injector.ets-labs.org/>`_ example application
utilizing `lifespan API <https://www.starlette.io/lifespan/>`_.
.. note::
Pretty much `any framework built on top of Starlette <https://www.starlette.io/third-party-packages/#frameworks>`_
supports this feature (`FastAPI <https://fastapi.tiangolo.com/advanced/events/#lifespan>`_,
`Xpresso <https://xpresso-api.dev/latest/tutorial/lifespan/>`_, etc...).
Run
---
Create virtual environment:
.. code-block:: bash
python -m venv env
. env/bin/activate
Install requirements:
.. code-block:: bash
pip install -r requirements.txt
To run the application do:
.. code-block:: bash
python example.py
# or (logging won't be configured):
uvicorn --factory example:container.app
After that visit http://127.0.0.1:8000/ in your browser or use CLI command (``curl``, ``httpie``,
etc).

View File

@ -1,59 +0,0 @@
#!/usr/bin/env python
from logging import basicConfig, getLogger
from dependency_injector.containers import DeclarativeContainer
from dependency_injector.ext.starlette import Lifespan
from dependency_injector.providers import Factory, Resource, Self, Singleton
from starlette.applications import Starlette
from starlette.requests import Request
from starlette.responses import JSONResponse
from starlette.routing import Route
count = 0
def init():
log = getLogger(__name__)
log.info("Inittializing resources")
yield
log.info("Cleaning up resources")
async def homepage(request: Request) -> JSONResponse:
global count
response = JSONResponse({"hello": "world", "count": count})
count += 1
return response
class Container(DeclarativeContainer):
__self__ = Self()
lifespan = Singleton(Lifespan, __self__)
logging = Resource(
basicConfig,
level="DEBUG",
datefmt="%Y-%m-%d %H:%M",
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
)
init = Resource(init)
app = Factory(
Starlette,
debug=True,
lifespan=lifespan,
routes=[Route("/", homepage)],
)
container = Container()
if __name__ == "__main__":
import uvicorn
uvicorn.run(
container.app,
factory=True,
# NOTE: `None` prevents uvicorn from configuring logging, which is
# impossible via CLI
log_config=None,
)

View File

@ -1,3 +0,0 @@
dependency-injector
starlette
uvicorn

View File

@ -3,7 +3,7 @@
import os
from dependency_injector import containers, providers
from pydantic_settings import BaseSettings, SettingsConfigDict
from pydantic import BaseSettings, Field
# Emulate environment variables
os.environ["AWS_ACCESS_KEY_ID"] = "KEY"
@ -11,16 +11,15 @@ os.environ["AWS_SECRET_ACCESS_KEY"] = "SECRET"
class AwsSettings(BaseSettings):
model_config = SettingsConfigDict(env_prefix="aws_")
access_key_id: str
secret_access_key: str
access_key_id: str = Field(env="aws_access_key_id")
secret_access_key: str = Field(env="aws_secret_access_key")
class Settings(BaseSettings):
aws: AwsSettings = AwsSettings()
optional: str = "default_value"
optional: str = Field(default="default_value")
class Container(containers.DeclarativeContainer):

View File

@ -3,7 +3,7 @@
import os
from dependency_injector import containers, providers
from pydantic_settings import BaseSettings, SettingsConfigDict
from pydantic import BaseSettings, Field
# Emulate environment variables
os.environ["AWS_ACCESS_KEY_ID"] = "KEY"
@ -11,16 +11,15 @@ os.environ["AWS_SECRET_ACCESS_KEY"] = "SECRET"
class AwsSettings(BaseSettings):
model_config = SettingsConfigDict(env_prefix="aws_")
access_key_id: str
secret_access_key: str
access_key_id: str = Field(env="aws_access_key_id")
secret_access_key: str = Field(env="aws_secret_access_key")
class Settings(BaseSettings):
aws: AwsSettings = AwsSettings()
optional: str = "default_value"
optional: str = Field(default="default_value")
class Container(containers.DeclarativeContainer):

View File

@ -3,12 +3,10 @@
import sys
import logging
from concurrent.futures import ThreadPoolExecutor
from contextlib import contextmanager
from dependency_injector import containers, providers
@contextmanager
def init_thread_pool(max_workers: int):
thread_pool = ThreadPoolExecutor(max_workers=max_workers)
yield thread_pool

View File

@ -2,10 +2,10 @@
from dependency_injector import containers, providers
from dependency_injector.wiring import Provide, inject
from typing import Annotated
class Service: ...
class Service:
...
class Container(containers.DeclarativeContainer):
@ -13,16 +13,9 @@ class Container(containers.DeclarativeContainer):
service = providers.Factory(Service)
# You can place marker on parameter default value
@inject
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: ...
def main(service: Service = Provide[Container.service]) -> None:
...
if __name__ == "__main__":

View File

@ -1,31 +0,0 @@
"""Wiring attribute example with Annotated."""
from typing import Annotated
from dependency_injector import containers, providers
from dependency_injector.wiring import Provide
class Service:
...
class Container(containers.DeclarativeContainer):
service = providers.Factory(Service)
service: Annotated[Service, Provide[Container.service]]
class Main:
service: Annotated[Service, Provide[Container.service]]
if __name__ == "__main__":
container = Container()
container.wire(modules=[__name__])
assert isinstance(service, Service)
assert isinstance(Main.service, Service)

View File

@ -1,123 +0,0 @@
[build-system]
requires = ["setuptools", "Cython>=3.1.4"]
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 and typing.assert_never since v3.11
# typing.TypeVar default since v3.13
"typing-extensions; python_version<'3.13'",
]
[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",
]

View File

@ -1,11 +1,9 @@
cython==3.1.4
setuptools
cython==0.29.37
pytest
pytest-asyncio
tox
coverage
flake8
flake8-pyproject
pydocstyle
sphinx_autobuild
pip
@ -13,13 +11,10 @@ mypy
pyyaml
httpx
fastapi
pydantic
pydantic-settings
pydantic==1.10.17
numpy
scipy
boto3
mypy_boto3_s3
typing_extensions
fast-depends
-r requirements-ext.txt

View File

@ -1,3 +1,3 @@
flask
werkzeug
aiohttp
flask==2.1.3
werkzeug==2.2.2
aiohttp==3.9.0b1

1
requirements.txt Normal file
View File

@ -0,0 +1 @@
six>=1.7.0,<=1.16.0

View File

@ -2,13 +2,12 @@
max_line_length = 120
max_complexity = 10
exclude = types.py
extend-ignore = E203,E701
per-file-ignores =
examples/demo/*: F841
examples/containers/traverse.py: E501
examples/providers/async.py: F841
examples/providers/async_overriding.py: F841
examples/wiring/*: F821,F841
examples/wiring/*: F841
[pydocstyle]
ignore = D100,D101,D102,D105,D106,D107,D203,D213

165
setup.py
View File

@ -1,58 +1,127 @@
"""`Dependency injector` setup script."""
import os
import re
import sys
import sysconfig
from Cython.Build import cythonize
from Cython.Compiler import Options
from setuptools import Extension, setup
from setuptools import setup, Extension
debug = os.environ.get("DEPENDENCY_INJECTOR_DEBUG_MODE") == "1"
limited_api = (
os.environ.get("DEPENDENCY_INJECTOR_LIMITED_API") == "1"
and sys.implementation.name == "cpython"
and sys.version_info >= (3, 10)
and not sysconfig.get_config_var("Py_GIL_DISABLED")
)
defined_macros = []
options = {}
compiler_directives = {
"language_level": 3,
"profile": debug,
"linetrace": debug,
}
Options.annotate = debug
def _open(filename):
if sys.version_info[0] == 2:
return open(filename)
return open(filename, encoding="utf-8")
# Defining setup variables:
defined_macros = dict()
defined_macros["CYTHON_CLINE_IN_TRACEBACK"] = 0
# Getting description:
with _open("README.rst") as readme_file:
description = readme_file.read()
# Getting requirements:
with _open("requirements.txt") as requirements_file:
requirements = requirements_file.readlines()
# Getting version:
with _open("src/dependency_injector/__init__.py") as init_file:
version = re.search("__version__ = \"(.*?)\"", init_file.read()).group(1)
# Adding debug options:
if debug:
limited_api = False # line tracing is not part of the Limited API
defined_macros.extend(
[
("CYTHON_TRACE", "1"),
("CYTHON_TRACE_NOGIL", "1"),
("CYTHON_CLINE_IN_TRACEBACK", "1"),
]
)
if os.environ.get("DEPENDENCY_INJECTOR_DEBUG_MODE") == "1":
defined_macros["CYTHON_TRACE"] = 1
defined_macros["CYTHON_TRACE_NOGIL"] = 1
defined_macros["CYTHON_CLINE_IN_TRACEBACK"] = 1
if limited_api:
options.setdefault("bdist_wheel", {})
options["bdist_wheel"]["py_limited_api"] = "cp310"
defined_macros.append(("Py_LIMITED_API", "0x030A0000"))
setup(
options=options,
ext_modules=cythonize(
[
Extension(
"*",
["src/**/*.pyx"],
define_macros=defined_macros,
py_limited_api=limited_api,
),
],
annotate=debug,
show_all_warnings=True,
compiler_directives=compiler_directives,
),
)
setup(name="dependency-injector",
version=version,
description="Dependency injection framework for Python",
long_description=description,
author="Roman Mogylatov",
author_email="rmogilatov@gmail.com",
maintainer="Roman Mogylatov",
maintainer_email="rmogilatov@gmail.com",
url="https://github.com/ets-labs/python-dependency-injector",
download_url="https://pypi.python.org/pypi/dependency_injector",
packages=[
"dependency_injector",
"dependency_injector.ext",
],
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",
])

View File

@ -1,6 +1,6 @@
"""Top-level package."""
__version__ = "4.48.3"
__version__ = "4.42.0"
"""Version number.
:type: str

View File

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +0,0 @@
from typing import Any, Dict
from .providers import Provider
class DependencyResolver:
def __init__(
self,
kwargs: Dict[str, Any],
injections: Dict[str, Provider[Any]],
closings: Dict[str, Provider[Any]],
/,
) -> None: ...
def __enter__(self) -> Dict[str, Any]: ...
def __exit__(self, *exc_info: Any) -> None: ...
async def __aenter__(self) -> Dict[str, Any]: ...
async def __aexit__(self, *exc_info: Any) -> None: ...
def _isawaitable(instance: Any) -> bool: ...

View File

@ -1,93 +1,88 @@
"""Wiring optimizations module."""
from asyncio import gather
from collections.abc import Awaitable
from inspect import CO_ITERABLE_COROUTINE
from types import CoroutineType, GeneratorType
import asyncio
import collections.abc
import functools
import inspect
import types
from .providers cimport Provider, Resource
from .wiring import _Marker
from . import providers
from .wiring import _Marker, PatchedCallable
from .providers cimport Provider
cdef inline bint _is_injectable(dict kwargs, object name):
return name not in kwargs or isinstance(kwargs[name], _Marker)
cdef class DependencyResolver:
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):
def _get_sync_patched(fn, patched: PatchedCallable):
@functools.wraps(fn)
def _patched(*args, **kwargs):
cdef object result
cdef dict to_inject
cdef object arg_key
cdef Provider provider
for name, provider in self.injections.items():
if _is_injectable(self.kwargs, name):
self.to_inject[name] = provider()
to_inject = kwargs.copy()
for arg_key, provider in patched.injections.items():
if arg_key not in kwargs or isinstance(kwargs[arg_key], _Marker):
to_inject[arg_key] = provider()
cdef list _handle_injections_async(self):
cdef list to_await = []
cdef Provider provider
result = fn(*args, **to_inject)
for name, provider in self.injections.items():
if _is_injectable(self.kwargs, name):
provide = provider()
if provider.is_async_mode_enabled() or _isawaitable(provide):
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):
if patched.closing:
for arg_key, provider in patched.closing.items():
if arg_key in kwargs and not isinstance(kwargs[arg_key], _Marker):
continue
if not isinstance(provider, providers.Resource):
continue
provider.shutdown()
cdef list _handle_closings_async(self):
cdef list to_await = []
cdef Provider provider
return result
return _patched
for name, provider in self.closings.items():
if _is_injectable(self.kwargs, name) and isinstance(provider, Resource):
if _isawaitable(shutdown := provider.shutdown()):
to_await.append(shutdown)
return to_await
async def _async_inject(object fn, tuple args, dict kwargs, dict injections, dict closings):
cdef object result
cdef dict to_inject
cdef list to_inject_await = []
cdef list to_close_await = []
cdef object arg_key
cdef Provider provider
def __enter__(self):
self._handle_injections_sync()
return self.to_inject
to_inject = kwargs.copy()
for arg_key, provider in injections.items():
if arg_key not in kwargs or isinstance(kwargs[arg_key], _Marker):
provide = provider()
if provider.is_async_mode_enabled():
to_inject_await.append((arg_key, provide))
elif _isawaitable(provide):
to_inject_await.append((arg_key, provide))
else:
to_inject[arg_key] = provide
def __exit__(self, *_):
self._handle_closings_sync()
if to_inject_await:
async_to_inject = await asyncio.gather(*(provide for _, provide in to_inject_await))
for provide, (injection, _) in zip(async_to_inject, to_inject_await):
to_inject[injection] = provide
async def __aenter__(self):
if to_await := self._handle_injections_async():
await gather(*to_await)
return self.to_inject
result = await fn(*args, **to_inject)
async def __aexit__(self, *_):
if to_await := self._handle_closings_async():
await gather(*to_await)
if closings:
for arg_key, provider in closings.items():
if arg_key in kwargs and isinstance(kwargs[arg_key], _Marker):
continue
if not isinstance(provider, providers.Resource):
continue
shutdown = provider.shutdown()
if _isawaitable(shutdown):
to_close_await.append(shutdown)
await asyncio.gather(*to_close_await)
return result
cdef bint _isawaitable(object instance):
"""Return true if object can be passed to an ``await`` expression."""
return (isinstance(instance, CoroutineType) or
isinstance(instance, GeneratorType) and
bool(instance.gi_code.co_flags & CO_ITERABLE_COROUTINE) or
isinstance(instance, Awaitable))
return (isinstance(instance, types.CoroutineType) or
isinstance(instance, types.GeneratorType) and
bool(instance.gi_code.co_flags & inspect.CO_ITERABLE_COROUTINE) or
isinstance(instance, collections.abc.Awaitable))

File diff suppressed because it is too large Load Diff

View File

@ -1,28 +1,24 @@
from pathlib import Path
from typing import (
Any,
Awaitable,
Callable as _Callable,
ClassVar,
Dict,
Generic,
Type,
Dict,
List,
Tuple,
Optional,
Any,
Union,
ClassVar,
Callable as _Callable,
Iterable,
Iterator,
List,
Optional,
Tuple,
Type,
TypeVar,
Union,
Awaitable,
overload,
)
try:
from typing import Self as _Self
except ImportError:
from typing_extensions import Self as _Self
from .providers import Provider, Self, ProviderParent
from .providers import Provider, Resource, Self, ProviderParent
C_Base = TypeVar("C_Base", bound="Container")
C = TypeVar("C", bound="DeclarativeContainer")
@ -30,67 +26,51 @@ C_Overriding = TypeVar("C_Overriding", bound="DeclarativeContainer")
T = TypeVar("T")
TT = TypeVar("TT")
class WiringConfiguration:
modules: List[Any]
packages: List[Any]
from_package: Optional[str]
auto_wire: bool
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: ...
def __init__(self, modules: Optional[Iterable[Any]] = None, packages: Optional[Iterable[Any]] = None, from_package: Optional[str] = None, auto_wire: bool = True) -> None: ...
class Container:
provider_type: Type[Provider[Any]] = Provider
providers: Dict[str, Provider[Any]]
dependencies: Dict[str, Provider[Any]]
overridden: Tuple[Provider[Any], ...]
provider_type: Type[Provider] = Provider
providers: Dict[str, Provider]
dependencies: Dict[str, Provider]
overridden: Tuple[Provider]
wiring_config: WiringConfiguration
auto_load_config: bool = True
__self__: Self
def __init__(self) -> None: ...
def __deepcopy__(self, memo: Optional[Dict[str, Any]]) -> _Self: ...
def __setattr__(self, name: str, value: Union[Provider[Any], Any]) -> None: ...
def __getattr__(self, name: str) -> Provider[Any]: ...
def __deepcopy__(self, memo: Optional[Dict[str, Any]]) -> Provider: ...
def __setattr__(self, name: str, value: Union[Provider, Any]) -> None: ...
def __getattr__(self, name: str) -> Provider: ...
def __delattr__(self, name: str) -> None: ...
def set_providers(self, **providers: Provider[Any]) -> None: ...
def set_provider(self, name: str, provider: Provider[Any]) -> None: ...
def set_providers(self, **providers: Provider): ...
def set_provider(self, name: str, provider: Provider) -> None: ...
def override(self, overriding: Union[Container, Type[Container]]) -> None: ...
def override_providers(
self, **overriding_providers: Union[Provider[Any], Any]
) -> ProvidersOverridingContext[C_Base]: ...
def override_providers(self, **overriding_providers: Union[Provider, Any]) -> ProvidersOverridingContext[C_Base]: ...
def reset_last_overriding(self) -> None: ...
def reset_override(self) -> None: ...
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,
warn_unresolved: bool = False,
) -> 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 init_resources(self, resource_type: Type[Resource[Any]] = Resource) -> Optional[Awaitable[None]]: ...
def shutdown_resources(self, resource_type: Type[Resource[Any]] = Resource) -> Optional[Awaitable[None]]: ...
def init_resources(self) -> Optional[Awaitable]: ...
def shutdown_resources(self) -> Optional[Awaitable]: ...
def load_config(self) -> None: ...
def apply_container_providers_overridings(self) -> None: ...
def reset_singletons(self) -> SingletonResetContext[C_Base]: ...
def check_dependencies(self) -> 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: ...
@overload
def resolve_provider_name(self, provider: Provider[Any]) -> str: ...
def resolve_provider_name(self, provider: Provider) -> str: ...
@classmethod
@overload
def resolve_provider_name(cls, provider: Provider[Any]) -> str: ...
def resolve_provider_name(cls, provider: Provider) -> str: ...
@property
def parent(self) -> Optional[ProviderParent]: ...
@property
@ -102,37 +82,40 @@ class Container:
@overload
def traverse(cls, types: Optional[Iterable[Type[TT]]] = None) -> Iterator[TT]: ...
class DynamicContainer(Container): ...
class DeclarativeContainer(Container):
cls_providers: ClassVar[Dict[str, Provider[Any]]]
inherited_providers: ClassVar[Dict[str, Provider[Any]]]
def __init__(self, **overriding_providers: Union[Provider[Any], Any]) -> None: ...
cls_providers: ClassVar[Dict[str, Provider]]
inherited_providers: ClassVar[Dict[str, Provider]]
def __init__(self, **overriding_providers: Union[Provider, Any]) -> None: ...
@classmethod
def override(cls, overriding: Union[Container, Type[Container]]) -> None: ...
@classmethod
def override_providers(
cls, **overriding_providers: Union[Provider[Any], Any]
) -> ProvidersOverridingContext[C_Base]: ...
def override_providers(cls, **overriding_providers: Union[Provider, Any]) -> ProvidersOverridingContext[C_Base]: ...
@classmethod
def reset_last_overriding(cls) -> None: ...
@classmethod
def reset_override(cls) -> None: ...
class ProvidersOverridingContext(Generic[T]):
def __init__(
self, container: T, overridden_providers: Iterable[Union[Provider[Any], Any]]
) -> None: ...
def __init__(self, container: T, overridden_providers: Iterable[Union[Provider, Any]]) -> None: ...
def __enter__(self) -> T: ...
def __exit__(self, *_: Any) -> None: ...
class SingletonResetContext(Generic[T]):
def __init__(self, container: T): ...
def __enter__(self) -> T: ...
def __exit__(self, *_: Any) -> None: ...
def override(
container: Type[C],
) -> _Callable[[Type[C_Overriding]], Type[C_Overriding]]: ...
def override(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: ...

View File

@ -1,53 +1,61 @@
"""Containers module."""
import asyncio
import contextlib
import copy as copy_module
import json
import sys
import importlib
import inspect
import warnings
try:
import asyncio
except ImportError:
asyncio = None
try:
import yaml
except ImportError:
yaml = None
import six
from . import providers, errors
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:
"""Container wiring configuration."""
def __init__(
self,
modules=None,
packages=None,
from_package=None,
auto_wire=True,
keep_cache=False,
warn_unresolved=False,
):
def __init__(self, modules=None, packages=None, from_package=None, auto_wire=True):
self.modules = [*modules] if modules else []
self.packages = [*packages] if packages else []
self.from_package = from_package
self.auto_wire = auto_wire
self.keep_cache = keep_cache
self.warn_unresolved = warn_unresolved
def __deepcopy__(self, memo=None):
return self.__class__(
self.modules,
self.packages,
self.from_package,
self.auto_wire,
self.keep_cache,
self.warn_unresolved,
)
return self.__class__(self.modules, self.packages, self.from_package, self.auto_wire)
class Container:
class Container(object):
"""Abstract container."""
@ -193,7 +201,7 @@ class DynamicContainer(Container):
:rtype: None
"""
for name, provider in providers.items():
for name, provider in six.iteritems(providers):
setattr(self, name, provider)
def set_provider(self, name, provider):
@ -226,7 +234,7 @@ class DynamicContainer(Container):
self.overridden += (overriding,)
for name, provider in overriding.providers.items():
for name, provider in six.iteritems(overriding.providers):
try:
getattr(self, name).override(provider)
except AttributeError:
@ -242,7 +250,7 @@ class DynamicContainer(Container):
:rtype: None
"""
overridden_providers = []
for name, overriding_provider in overriding_providers.items():
for name, overriding_provider in six.iteritems(overriding_providers):
container_provider = getattr(self, name)
container_provider.override(overriding_provider)
overridden_providers.append(container_provider)
@ -258,7 +266,7 @@ class DynamicContainer(Container):
self.overridden = self.overridden[:-1]
for provider in self.providers.values():
for provider in six.itervalues(self.providers):
provider.reset_last_overriding()
def reset_override(self):
@ -268,21 +276,14 @@ class DynamicContainer(Container):
"""
self.overridden = tuple()
for provider in self.providers.values():
for provider in six.itervalues(self.providers):
provider.reset_override()
def is_auto_wiring_enabled(self):
"""Check if auto wiring is needed."""
return self.wiring_config.auto_wire is True
def wire(
self,
modules=None,
packages=None,
from_package=None,
keep_cache=None,
warn_unresolved=False,
):
def wire(self, modules=None, packages=None, from_package=None):
"""Wire container providers with provided packages and modules.
:rtype: None
@ -313,15 +314,10 @@ class DynamicContainer(Container):
if not modules and not packages:
return
if keep_cache is None:
keep_cache = self.wiring_config.keep_cache
wire(
container=self,
modules=modules,
packages=packages,
keep_cache=keep_cache,
warn_unresolved=warn_unresolved,
)
if modules:
@ -339,15 +335,11 @@ class DynamicContainer(Container):
self.wired_to_modules.clear()
self.wired_to_packages.clear()
def init_resources(self, resource_type=providers.Resource):
def init_resources(self):
"""Initialize all container resources."""
if not issubclass(resource_type, providers.Resource):
raise TypeError("resource_type must be a subclass of Resource provider")
futures = []
for provider in self.traverse(types=[resource_type]):
for provider in self.traverse(types=[providers.Resource]):
resource = provider.init()
if __is_future_or_coroutine(resource):
@ -356,12 +348,8 @@ class DynamicContainer(Container):
if futures:
return asyncio.gather(*futures)
def shutdown_resources(self, resource_type=providers.Resource):
def shutdown_resources(self):
"""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):
for resource in resources:
for other_resource in resources:
@ -392,7 +380,7 @@ class DynamicContainer(Container):
for resource in resources_to_shutdown:
resource.shutdown()
resources = list(self.traverse(types=[resource_type]))
resources = list(self.traverse(types=[providers.Resource]))
if any(resource.is_async_mode_enabled() for resource in resources):
return _async_ordered_shutdown(resources)
else:
@ -507,13 +495,13 @@ class DeclarativeContainerMetaClass(type):
containers = {
name: container
for name, container in attributes.items()
for name, container in six.iteritems(attributes)
if is_container(container)
}
cls_providers = {
name: provider
for name, provider in attributes.items()
for name, provider in six.iteritems(attributes)
if isinstance(provider, providers.Provider) and not isinstance(provider, providers.Self)
}
@ -521,7 +509,7 @@ class DeclarativeContainerMetaClass(type):
name: provider
for base in bases
if is_container(base) and base is not DynamicContainer
for name, provider in base.providers.items()
for name, provider in six.iteritems(base.providers)
}
all_providers = {}
@ -548,10 +536,10 @@ class DeclarativeContainerMetaClass(type):
self.set_container(cls)
cls.__self__ = self
for provider in cls.providers.values():
for provider in six.itervalues(cls.providers):
_check_provider_type(cls, provider)
for provider in cls.cls_providers.values():
for provider in six.itervalues(cls.cls_providers):
if isinstance(provider, providers.CHILD_PROVIDERS):
provider.assign_parent(cls)
@ -653,7 +641,8 @@ class DeclarativeContainerMetaClass(type):
return self
class DeclarativeContainer(Container, metaclass=DeclarativeContainerMetaClass):
@six.add_metaclass(DeclarativeContainerMetaClass)
class DeclarativeContainer(Container):
"""Declarative inversion of control container.
.. code-block:: python
@ -778,7 +767,7 @@ class DeclarativeContainer(Container, metaclass=DeclarativeContainerMetaClass):
cls.overridden += (overriding,)
for name, provider in overriding.cls_providers.items():
for name, provider in six.iteritems(overriding.cls_providers):
try:
getattr(cls, name).override(provider)
except AttributeError:
@ -795,7 +784,7 @@ class DeclarativeContainer(Container, metaclass=DeclarativeContainerMetaClass):
cls.overridden = cls.overridden[:-1]
for provider in cls.providers.values():
for provider in six.itervalues(cls.providers):
provider.reset_last_overriding()
@classmethod
@ -806,7 +795,7 @@ class DeclarativeContainer(Container, metaclass=DeclarativeContainerMetaClass):
"""
cls.overridden = tuple()
for provider in cls.providers.values():
for provider in six.itervalues(cls.providers):
provider.reset_override()
@ -869,7 +858,7 @@ def copy(object base_container):
"""
def _get_memo_for_matching_names(new_providers, base_providers):
memo = {}
for new_provider_name, new_provider in new_providers.items():
for new_provider_name, new_provider in six.iteritems(new_providers):
if new_provider_name not in base_providers:
continue
source_provider = base_providers[new_provider_name]
@ -888,7 +877,7 @@ def copy(object base_container):
new_providers.update(providers.deepcopy(base_container.providers, memo))
new_providers.update(providers.deepcopy(new_container.cls_providers, memo))
for name, provider in new_providers.items():
for name, provider in six.iteritems(new_providers):
setattr(new_container, name, provider)
return new_container

View File

@ -10,24 +10,3 @@ class Error(Exception):
class NoSuchProviderError(Error, AttributeError):
"""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}"

View File

@ -7,6 +7,7 @@ import warnings
from dependency_injector import providers
warnings.warn(
'Module "dependency_injector.ext.aiohttp" is deprecated since '
'version 4.0.0. Use "dependency_injector.wiring" module instead.',
@ -37,11 +38,9 @@ class View(providers.Callable):
def as_view(self):
"""Return aiohttp view function."""
@functools.wraps(self.provides)
async def _view(request, *args, **kwargs):
return await self.__call__(request, *args, **kwargs)
return _view
@ -50,8 +49,6 @@ class ClassBasedView(providers.Factory):
def as_view(self):
"""Return aiohttp view function."""
async def _view(request, *args, **kwargs):
return await self.__call__(request, *args, **kwargs)
return _view

View File

@ -1,16 +1,23 @@
from typing import Any, Awaitable as _Awaitable, TypeVar
from typing import Awaitable as _Awaitable
from dependency_injector import providers
T = TypeVar("T")
class Application(providers.Singleton[T]): ...
class Extension(providers.Singleton[T]): ...
class Middleware(providers.DelegatedCallable[T]): ...
class MiddlewareFactory(providers.Factory[T]): ...
class Application(providers.Singleton): ...
class View(providers.Callable[T]):
def as_view(self) -> _Awaitable[T]: ...
class ClassBasedView(providers.Factory[T]):
def as_view(self) -> _Awaitable[T]: ...
class Extension(providers.Singleton): ...
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: ...

View File

@ -1,12 +1,12 @@
"""Flask extension module."""
from __future__ import absolute_import
import warnings
from flask import request as flask_request
from dependency_injector import errors, providers
from dependency_injector import providers, errors
warnings.warn(
'Module "dependency_injector.ext.flask" is deprecated since '
@ -45,7 +45,6 @@ class ClassBasedView(providers.Factory):
def as_view(provider, name=None):
"""Transform class-based view provider to view function."""
if isinstance(provider, providers.Factory):
def view(*args, **kwargs):
self = provider()
return self.dispatch_request(*args, **kwargs)
@ -53,13 +52,12 @@ def as_view(provider, name=None):
assert name, 'Argument "endpoint" is required for class-based views'
view.__name__ = name
elif isinstance(provider, providers.Callable):
def view(*args, **kwargs):
return provider(*args, **kwargs)
view.__name__ = provider.provides.__name__
else:
raise errors.Error("Undefined provider type")
raise errors.Error('Undefined provider type')
view.__doc__ = provider.provides.__doc__
view.__module__ = provider.provides.__module__
@ -67,14 +65,14 @@ def as_view(provider, name=None):
if isinstance(provider.provides, type):
view.view_class = provider.provides
if hasattr(provider.provides, "decorators"):
if hasattr(provider.provides, 'decorators'):
for decorator in provider.provides.decorators:
view = decorator(view)
if hasattr(provider.provides, "methods"):
if hasattr(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
return view

View File

@ -1,21 +1,24 @@
from typing import Any, Callable as _Callable, Optional, TypeVar, Union
from flask.wrappers import Request
from typing import Union, Optional, Callable as _Callable, Any
from flask import request as flask_request
from dependency_injector import providers
request: providers.Object[Request]
T = TypeVar("T")
class Application(providers.Singleton[T]): ...
class Extension(providers.Singleton[T]): ...
request: providers.Object[flask_request]
class View(providers.Callable[T]):
def as_view(self) -> _Callable[..., T]: ...
class ClassBasedView(providers.Factory[T]):
def as_view(self, name: str) -> _Callable[..., T]: ...
class Application(providers.Singleton): ...
def as_view(
provider: Union[View[T], ClassBasedView[T]], name: Optional[str] = None
) -> _Callable[..., T]: ...
class Extension(providers.Singleton): ...
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]: ...

View File

@ -1,61 +0,0 @@
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

View File

@ -1,6 +1,10 @@
"""Providers module."""
import asyncio
try:
import asyncio
except ImportError:
asyncio = None
import functools
cimport cython
@ -15,11 +19,11 @@ cdef tuple __COROUTINE_TYPES
# Base providers
cdef class Provider:
cdef tuple _overridden
cdef Provider _last_overriding
cdef tuple _overrides
cdef int _async_mode
cdef class Provider(object):
cdef tuple __overridden
cdef Provider __last_overriding
cdef tuple __overrides
cdef int __async_mode
cpdef bint is_async_mode_enabled(self)
cpdef bint is_async_mode_disabled(self)
@ -30,32 +34,32 @@ cdef class Provider:
cdef class Object(Provider):
cdef object _provides
cdef object __provides
cpdef object _provide(self, tuple args, dict kwargs)
cdef class Self(Provider):
cdef object _container
cdef tuple _alt_names
cdef object __container
cdef tuple __alt_names
cdef class Delegate(Provider):
cdef object _provides
cdef object __provides
cpdef object _provide(self, tuple args, dict kwargs)
cdef class Aggregate(Provider):
cdef dict _providers
cdef dict __providers
cdef Provider __get_provider(self, object provider_name)
cdef class Dependency(Provider):
cdef object _instance_of
cdef object _default
cdef object _parent
cdef object __instance_of
cdef object __default
cdef object __parent
cdef class ExternalDependency(Dependency):
@ -63,21 +67,21 @@ cdef class ExternalDependency(Dependency):
cdef class DependenciesContainer(Object):
cdef dict _providers
cdef object _parent
cdef dict __providers
cdef object __parent
cpdef object _override_providers(self, object container)
# Callable providers
cdef class Callable(Provider):
cdef object _provides
cdef object __provides
cdef tuple _args
cdef int _args_len
cdef tuple __args
cdef int __args_len
cdef tuple _kwargs
cdef int _kwargs_len
cdef tuple __kwargs
cdef int __kwargs_len
cpdef object _provide(self, tuple args, dict kwargs)
@ -113,11 +117,11 @@ cdef class CoroutineDelegate(Delegate):
# Configuration providers
cdef class ConfigurationOption(Provider):
cdef tuple _name
cdef Configuration _root
cdef dict _children
cdef bint _required
cdef object _cache
cdef tuple __name
cdef Configuration __root
cdef dict __children
cdef bint __required
cdef object __cache
cdef class TypedConfigurationOption(Callable):
@ -125,22 +129,22 @@ cdef class TypedConfigurationOption(Callable):
cdef class Configuration(Object):
cdef str _name
cdef str __name
cdef bint __strict
cdef dict _children
cdef list _ini_files
cdef list _yaml_files
cdef list _json_files
cdef list _pydantic_settings
cdef dict __children
cdef list __ini_files
cdef list __yaml_files
cdef list __json_files
cdef list __pydantic_settings
cdef object __weakref__
# Factory providers
cdef class Factory(Provider):
cdef Callable _instantiator
cdef Callable __instantiator
cdef tuple _attributes
cdef int _attributes_len
cdef tuple __attributes
cdef int __attributes_len
cpdef object _provide(self, tuple args, dict kwargs)
@ -163,8 +167,8 @@ cdef class FactoryAggregate(Aggregate):
# Singleton providers
cdef class BaseSingleton(Provider):
cdef Factory _instantiator
cdef object _storage
cdef Factory __instantiator
cdef object __storage
cdef class Singleton(BaseSingleton):
@ -177,7 +181,7 @@ cdef class DelegatedSingleton(Singleton):
cdef class ThreadSafeSingleton(BaseSingleton):
cdef object _storage_lock
cdef object __storage_lock
cpdef object _provide(self, tuple args, dict kwargs)
@ -211,87 +215,87 @@ cdef class SingletonDelegate(Delegate):
# Miscellaneous providers
cdef class List(Provider):
cdef tuple _args
cdef int _args_len
cdef tuple __args
cdef int __args_len
cpdef object _provide(self, tuple args, dict kwargs)
cdef class Dict(Provider):
cdef tuple _kwargs
cdef int _kwargs_len
cdef tuple __kwargs
cdef int __kwargs_len
cpdef object _provide(self, tuple args, dict kwargs)
cdef class Resource(Provider):
cdef object _provides
cdef bint _initialized
cdef object _shutdowner
cdef object _resource
cdef object __provides
cdef bint __initialized
cdef object __shutdowner
cdef object __resource
cdef tuple _args
cdef int _args_len
cdef tuple __args
cdef int __args_len
cdef tuple _kwargs
cdef int _kwargs_len
cdef tuple __kwargs
cdef int __kwargs_len
cpdef object _provide(self, tuple args, dict kwargs)
cdef class Container(Provider):
cdef object _container_cls
cdef dict _overriding_providers
cdef object _container
cdef object _parent
cdef object __container_cls
cdef dict __overriding_providers
cdef object __container
cdef object __parent
cpdef object _provide(self, tuple args, dict kwargs)
cdef class Selector(Provider):
cdef object _selector
cdef dict _providers
cdef object __selector
cdef dict __providers
cpdef object _provide(self, tuple args, dict kwargs)
# Provided instance
cdef class ProvidedInstance(Provider):
cdef object _provides
cdef object __provides
cpdef object _provide(self, tuple args, dict kwargs)
cdef class AttributeGetter(Provider):
cdef object _provides
cdef object _name
cdef object __provides
cdef object __name
cpdef object _provide(self, tuple args, dict kwargs)
cdef class ItemGetter(Provider):
cdef object _provides
cdef object _name
cdef object __provides
cdef object __name
cpdef object _provide(self, tuple args, dict kwargs)
cdef class MethodCaller(Provider):
cdef object _provides
cdef tuple _args
cdef int _args_len
cdef tuple _kwargs
cdef int _kwargs_len
cdef object __provides
cdef tuple __args
cdef int __args_len
cdef tuple __kwargs
cdef int __kwargs_len
cpdef object _provide(self, tuple args, dict kwargs)
# Injections
cdef class Injection:
cdef object _value
cdef int _is_provider
cdef int _is_delegated
cdef int _call
cdef class Injection(object):
cdef object __value
cdef int __is_provider
cdef int __is_delegated
cdef int __call
cdef class PositionalInjection(Injection):
@ -299,7 +303,7 @@ cdef class PositionalInjection(Injection):
cdef class NamedInjection(Injection):
cdef object _name
cdef object __name
cpdef tuple parse_positional_injections(tuple args)
@ -309,13 +313,13 @@ cpdef tuple parse_named_injections(dict kwargs)
# Utils
cdef class OverridingContext:
cdef Provider _overridden
cdef Provider _overriding
cdef class OverridingContext(object):
cdef Provider __overridden
cdef Provider __overriding
cdef class BaseSingletonResetContext:
cdef object _singleton
cdef class BaseSingletonResetContext(object):
cdef object __singleton
cdef class SingletonResetContext(BaseSingletonResetContext):
@ -352,19 +356,19 @@ cpdef object deepcopy(object instance, dict memo=*)
# Inline helper functions
cdef inline object __get_name(NamedInjection self):
return self._name
return self.__name
cdef inline object __get_value(Injection self):
if self._call == 0:
return self._value
return self._value()
if self.__call == 0:
return self.__value
return self.__value()
cdef inline object __get_value_kwargs(Injection self, dict kwargs):
if self._call == 0:
return self._value
return self._value(**kwargs)
if self.__call == 0:
return self.__value
return self.__value(**kwargs)
cdef inline tuple __separate_prefixed_kwargs(dict kwargs):
@ -629,14 +633,14 @@ cdef inline object __async_result_callback(object future_result, object future):
cdef inline object __callable_call(Callable self, tuple args, dict kwargs, ):
return __call(
self._provides,
self.__provides,
args,
self._args,
self._args_len,
self.__args,
self.__args_len,
kwargs,
self._kwargs,
self._kwargs_len,
self._async_mode,
self.__kwargs,
self.__kwargs_len,
self.__async_mode,
)
@ -644,18 +648,18 @@ cdef inline object __factory_call(Factory self, tuple args, dict kwargs):
cdef object instance
instance = __call(
self._instantiator._provides,
self.__instantiator.__provides,
args,
self._instantiator._args,
self._instantiator._args_len,
self.__instantiator.__args,
self.__instantiator.__args_len,
kwargs,
self._instantiator._kwargs,
self._instantiator._kwargs_len,
self._async_mode,
self.__instantiator.__kwargs,
self.__instantiator.__kwargs_len,
self.__async_mode,
)
if self._attributes_len > 0:
attributes = __provide_attributes(self._attributes, self._attributes_len)
if self.__attributes_len > 0:
attributes = __provide_attributes(self.__attributes, self.__attributes_len)
is_future_instance = __is_future_or_coroutine(instance)
is_future_attributes = __is_future_or_coroutine(attributes)
@ -697,10 +701,3 @@ cdef inline object __future_result(object instance):
future_result = asyncio.Future()
future_result.set_result(instance)
return future_result
cdef class NullAwaitable:
pass
cdef NullAwaitable NULL_AWAITABLE

View File

@ -1,29 +1,26 @@
from __future__ import annotations
from contextlib import AbstractAsyncContextManager, AbstractContextManager
from pathlib import Path
from typing import (
Any,
AsyncIterator as _AsyncIterator,
Awaitable,
Callable as _Callable,
Coroutine as _Coroutine,
Dict as _Dict,
Generator as _Generator,
TypeVar,
Generic,
Type,
Callable as _Callable,
Any,
Tuple,
List as _List,
Dict as _Dict,
Optional,
Union,
Coroutine as _Coroutine,
Iterable as _Iterable,
Iterator as _Iterator,
List as _List,
Mapping,
Optional,
Tuple,
Type,
Union,
AsyncIterator as _AsyncIterator,
Generator as _Generator,
overload,
)
from typing_extensions import Self as _Self, TypeVar
try:
import yaml
except ImportError:
@ -36,21 +33,24 @@ except ImportError:
from . import resources
Injection = Any
ProviderParent = Union["Provider", Any]
T = TypeVar("T")
TT = TypeVar("TT")
T_Any = TypeVar("T_Any", default=Any)
P = TypeVar("P", bound="Provider")
BS = TypeVar("BS", bound="BaseSingleton")
class Provider(Generic[T]):
def __init__(self) -> None: ...
@overload
def __call__(self, *args: Injection, **kwargs: Injection) -> T: ...
@overload
def __call__(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 __str__(self) -> str: ...
def __repr__(self) -> str: ...
@ -67,7 +67,7 @@ class Provider(Generic[T]):
def unregister_overrides(self, provider: Union[Provider, Any]) -> None: ...
def delegate(self) -> Provider: ...
@property
def provider(self) -> Provider[T]: ...
def provider(self) -> Provider: ...
@property
def provided(self) -> ProvidedInstance: ...
def enable_async_mode(self) -> None: ...
@ -78,18 +78,16 @@ class Provider(Generic[T]):
def is_async_mode_undefined(self) -> bool: ...
@property
def related(self) -> _Iterator[Provider]: ...
def traverse(
self, types: Optional[_Iterable[Type[TT]]] = None
) -> _Iterator[TT]: ...
def _copy_overridings(
self, copied: Provider, memo: Optional[_Dict[Any, Any]]
) -> None: ...
def traverse(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]):
def __init__(self, provides: Optional[T] = None) -> None: ...
@property
def provides(self) -> Optional[T]: ...
def set_provides(self, provides: Optional[T]) -> _Self: ...
def set_provides(self, provides: Optional[T]) -> Object: ...
class Self(Provider[T]):
def __init__(self, container: Optional[T] = None) -> None: ...
@ -98,51 +96,41 @@ class Self(Provider[T]):
@property
def alt_names(self) -> Tuple[Any]: ...
class Delegate(Provider[Provider]):
def __init__(self, provides: Optional[Provider] = None) -> None: ...
@property
def provides(self) -> Optional[Provider]: ...
def set_provides(self, provides: Optional[Provider]) -> _Self: ...
def set_provides(self, provides: Optional[Provider]) -> Delegate: ...
class Aggregate(Provider[T]):
def __init__(
self,
provider_dict: Optional[Mapping[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]: ...
@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
def __call__(
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]: ...
def __call__(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
def providers(self) -> _Dict[Any, Provider[T]]: ...
def set_providers(
self,
provider_dict: Optional[Mapping[Any, Provider[T]]] = None,
**provider_kwargs: Provider[T],
) -> _Self: ...
def set_providers(self, provider_dict: Optional[_Dict[Any, Provider[T]]] = None, **provider_kwargs: Provider[T]) -> Aggregate[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: ...
@property
def instance_of(self) -> Type[T]: ...
def set_instance_of(self, instance_of: Type[T]) -> _Self: ...
def set_instance_of(self, instance_of: Type[T]) -> Dependency[T]: ...
@property
def default(self) -> Provider[T]: ...
def set_default(self, default: Optional[Union[Provider, Any]]) -> _Self: ...
def set_default(self, default: Optional[Union[Provider, Any]]) -> Dependency[T]: ...
@property
def is_defined(self) -> bool: ...
def provided_by(self, provider: Provider) -> OverridingContext[P]: ...
@ -152,8 +140,10 @@ class Dependency(Provider[T]):
def parent_name(self) -> Optional[str]: ...
def assign_parent(self, parent: ProviderParent) -> None: ...
class ExternalDependency(Dependency[T]): ...
class DependenciesContainer(Object):
def __init__(self, **dependencies: Provider) -> None: ...
def __getattr__(self, name: str) -> Provider: ...
@ -166,50 +156,53 @@ class DependenciesContainer(Object):
def parent_name(self) -> Optional[str]: ...
def assign_parent(self, parent: ProviderParent) -> None: ...
class Callable(Provider[T_Any]):
def __init__(
self,
provides: Optional[Union[_Callable[..., T_Any], str]] = None,
*args: Injection,
**kwargs: Injection,
) -> None: ...
class Callable(Provider[T]):
def __init__(self, provides: Optional[Union[_Callable[..., T], str]] = None, *args: Injection, **kwargs: Injection) -> None: ...
@property
def provides(self) -> Optional[_Callable[..., T_Any]]: ...
def set_provides(
self, provides: Optional[Union[_Callable[..., T_Any], str]]
) -> _Self: ...
def provides(self) -> Optional[_Callable[..., T]]: ...
def set_provides(self, provides: Optional[Union[_Callable[..., T], str]]) -> Callable[T]: ...
@property
def args(self) -> Tuple[Injection]: ...
def add_args(self, *args: Injection) -> _Self: ...
def set_args(self, *args: Injection) -> _Self: ...
def clear_args(self) -> _Self: ...
def add_args(self, *args: Injection) -> Callable[T]: ...
def set_args(self, *args: Injection) -> Callable[T]: ...
def clear_args(self) -> Callable[T]: ...
@property
def kwargs(self) -> _Dict[str, Injection]: ...
def add_kwargs(self, **kwargs: Injection) -> _Self: ...
def set_kwargs(self, **kwargs: Injection) -> _Self: ...
def clear_kwargs(self) -> _Self: ...
def kwargs(self) -> _Dict[Any, Injection]: ...
def add_kwargs(self, **kwargs: Injection) -> Callable[T]: ...
def set_kwargs(self, **kwargs: Injection) -> Callable[T]: ...
def clear_kwargs(self) -> Callable[T]: ...
class DelegatedCallable(Callable[T]): ...
class AbstractCallable(Callable[T]):
def override(self, provider: Callable) -> OverridingContext[P]: ...
class CallableDelegate(Delegate):
def __init__(self, callable: Callable) -> None: ...
class Coroutine(Callable[T]): ...
class DelegatedCoroutine(Coroutine[T]): ...
class AbstractCoroutine(Coroutine[T]):
def override(self, provider: Coroutine) -> OverridingContext[P]: ...
class CoroutineDelegate(Delegate):
def __init__(self, coroutine: Coroutine) -> None: ...
class ConfigurationOption(Provider[Any]):
UNDEFINED: object
def __init__(self, name: Tuple[str], root: Configuration) -> None: ...
def __enter__(self) -> _Self: ...
def __enter__(self) -> ConfigurationOption: ...
def __exit__(self, *exc_info: Any) -> None: ...
def __getattr__(self, item: str) -> ConfigurationOption: ...
def __getitem__(self, item: Union[str, Provider]) -> ConfigurationOption: ...
@ -219,318 +212,232 @@ class ConfigurationOption(Provider[Any]):
def get_name_segments(self) -> Tuple[Union[str, Provider]]: ...
def as_int(self) -> TypedConfigurationOption[int]: ...
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 is_required(self) -> bool: ...
def update(self, value: Any) -> None: ...
def from_ini(
self,
filepath: Union[Path, str],
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_ini(self, filepath: Union[Path, str], 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_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: ...
class TypedConfigurationOption(Callable[T]):
@property
def option(self) -> ConfigurationOption: ...
class Configuration(Object[Any]):
DEFAULT_NAME: str = "config"
def __init__(
self,
name: str = DEFAULT_NAME,
default: Optional[Any] = None,
*,
strict: bool = False,
ini_files: Optional[_Iterable[Union[Path, str]]] = None,
yaml_files: Optional[_Iterable[Union[Path, str]]] = None,
json_files: Optional[_Iterable[Union[Path, str]]] = None,
pydantic_settings: Optional[_Iterable[PydanticSettings]] = None,
self,
name: str = DEFAULT_NAME,
default: Optional[Any] = None,
*,
strict: bool = False,
ini_files: Optional[_Iterable[Union[Path, str]]] = None,
yaml_files: Optional[_Iterable[Union[Path, str]]] = None,
json_files: Optional[_Iterable[Union[Path, str]]] = None,
pydantic_settings: Optional[_Iterable[PydanticSettings]] = None,
) -> None: ...
def __enter__(self) -> _Self: ...
def __enter__(self) -> Configuration : ...
def __exit__(self, *exc_info: Any) -> None: ...
def __getattr__(self, item: str) -> ConfigurationOption: ...
def __getitem__(self, item: Union[str, Provider]) -> ConfigurationOption: ...
def get_name(self) -> str: ...
def set_name(self, name: str) -> _Self: ...
def set_name(self, name: str) -> Configuration: ...
def get_default(self) -> _Dict[Any, Any]: ...
def set_default(self, default: _Dict[Any, Any]) -> _Self: ...
def set_default(self, default: _Dict[Any, Any]): ...
def get_strict(self) -> bool: ...
def set_strict(self, strict: bool) -> _Self: ...
def set_strict(self, strict: bool) -> Configuration: ...
def get_children(self) -> _Dict[str, ConfigurationOption]: ...
def set_children(self, children: _Dict[str, ConfigurationOption]) -> _Self: ...
def set_children(self, children: _Dict[str, ConfigurationOption]) -> Configuration: ...
def get_ini_files(self) -> _List[Union[Path, str]]: ...
def set_ini_files(self, files: _Iterable[Union[Path, str]]) -> _Self: ...
def set_ini_files(self, files: _Iterable[Union[Path, str]]) -> Configuration: ...
def get_yaml_files(self) -> _List[Union[Path, str]]: ...
def set_yaml_files(self, files: _Iterable[Union[Path, str]]) -> _Self: ...
def set_yaml_files(self, files: _Iterable[Union[Path, str]]) -> Configuration: ...
def get_json_files(self) -> _List[Union[Path, str]]: ...
def set_json_files(self, files: _Iterable[Union[Path, str]]) -> _Self: ...
def set_json_files(self, files: _Iterable[Union[Path, str]]) -> Configuration: ...
def get_pydantic_settings(self) -> _List[PydanticSettings]: ...
def set_pydantic_settings(self, settings: _Iterable[PydanticSettings]) -> _Self: ...
def set_pydantic_settings(self, settings: _Iterable[PydanticSettings]) -> Configuration: ...
def load(self, required: bool = False, envs_required: bool = False) -> None: ...
def get(self, selector: str) -> Any: ...
def set(self, selector: str, value: Any) -> OverridingContext[P]: ...
def reset_cache(self) -> 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_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_ini(self, filepath: Union[Path, str], 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_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: ...
class Factory(Provider[T]):
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
def cls(self) -> Type[T]: ...
@property
def provides(self) -> Optional[_Callable[..., T]]: ...
def set_provides(
self, provides: Optional[Union[_Callable[..., T], str]]
) -> _Self: ...
def set_provides(self, provides: Optional[Union[_Callable[..., T], str]]) -> Factory[T]: ...
@property
def args(self) -> Tuple[Injection]: ...
def add_args(self, *args: Injection) -> _Self: ...
def set_args(self, *args: Injection) -> _Self: ...
def clear_args(self) -> _Self: ...
def add_args(self, *args: Injection) -> Factory[T]: ...
def set_args(self, *args: Injection) -> Factory[T]: ...
def clear_args(self) -> Factory[T]: ...
@property
def kwargs(self) -> _Dict[str, Injection]: ...
def add_kwargs(self, **kwargs: Injection) -> _Self: ...
def set_kwargs(self, **kwargs: Injection) -> _Self: ...
def clear_kwargs(self) -> _Self: ...
def kwargs(self) -> _Dict[Any, Injection]: ...
def add_kwargs(self, **kwargs: Injection) -> Factory[T]: ...
def set_kwargs(self, **kwargs: Injection) -> Factory[T]: ...
def clear_kwargs(self) -> Factory[T]: ...
@property
def attributes(self) -> _Dict[str, Injection]: ...
def add_attributes(self, **kwargs: Injection) -> _Self: ...
def set_attributes(self, **kwargs: Injection) -> _Self: ...
def clear_attributes(self) -> _Self: ...
def attributes(self) -> _Dict[Any, Injection]: ...
def add_attributes(self, **kwargs: Injection) -> Factory[T]: ...
def set_attributes(self, **kwargs: Injection) -> Factory[T]: ...
def clear_attributes(self) -> Factory[T]: ...
class DelegatedFactory(Factory[T]): ...
class AbstractFactory(Factory[T]):
def override(self, provider: Factory) -> OverridingContext[P]: ...
class FactoryDelegate(Delegate):
def __init__(self, factory: Factory): ...
class FactoryAggregate(Aggregate[T]):
def __getattr__(self, provider_name: Any) -> Factory[T]: ...
@property
def factories(self) -> _Dict[Any, Factory[T]]: ...
def set_factories(
self,
provider_dict: Optional[Mapping[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]):
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
def cls(self) -> Type[T]: ...
@property
def provides(self) -> Optional[_Callable[..., T]]: ...
def set_provides(
self, provides: Optional[Union[_Callable[..., T], str]]
) -> _Self: ...
def set_provides(self, provides: Optional[Union[_Callable[..., T], str]]) -> BaseSingleton[T]: ...
@property
def args(self) -> Tuple[Injection]: ...
def add_args(self, *args: Injection) -> _Self: ...
def set_args(self, *args: Injection) -> _Self: ...
def clear_args(self) -> _Self: ...
def add_args(self, *args: Injection) -> BaseSingleton[T]: ...
def set_args(self, *args: Injection) -> BaseSingleton[T]: ...
def clear_args(self) -> BaseSingleton[T]: ...
@property
def kwargs(self) -> _Dict[str, Injection]: ...
def add_kwargs(self, **kwargs: Injection) -> _Self: ...
def set_kwargs(self, **kwargs: Injection) -> _Self: ...
def clear_kwargs(self) -> _Self: ...
def kwargs(self) -> _Dict[Any, Injection]: ...
def add_kwargs(self, **kwargs: Injection) -> BaseSingleton[T]: ...
def set_kwargs(self, **kwargs: Injection) -> BaseSingleton[T]: ...
def clear_kwargs(self) -> BaseSingleton[T]: ...
@property
def attributes(self) -> _Dict[str, Injection]: ...
def add_attributes(self, **kwargs: Injection) -> _Self: ...
def set_attributes(self, **kwargs: Injection) -> _Self: ...
def clear_attributes(self) -> _Self: ...
def attributes(self) -> _Dict[Any, Injection]: ...
def add_attributes(self, **kwargs: Injection) -> BaseSingleton[T]: ...
def set_attributes(self, **kwargs: Injection) -> BaseSingleton[T]: ...
def clear_attributes(self) -> BaseSingleton[T]: ...
def reset(self) -> SingletonResetContext[BS]: ...
def full_reset(self) -> SingletonFullResetContext[BS]: ...
class Singleton(BaseSingleton[T]): ...
class DelegatedSingleton(Singleton[T]): ...
class ThreadSafeSingleton(Singleton[T]): ...
class DelegatedThreadSafeSingleton(ThreadSafeSingleton[T]): ...
class ThreadLocalSingleton(BaseSingleton[T]): ...
class ContextLocalSingleton(BaseSingleton[T]): ...
class DelegatedThreadLocalSingleton(ThreadLocalSingleton[T]): ...
class AbstractSingleton(BaseSingleton[T]):
def override(self, provider: BaseSingleton) -> OverridingContext[P]: ...
class SingletonDelegate(Delegate):
def __init__(self, singleton: BaseSingleton): ...
class List(Provider[_List]):
def __init__(self, *args: Injection): ...
@property
def args(self) -> Tuple[Injection]: ...
def add_args(self, *args: Injection) -> _Self: ...
def set_args(self, *args: Injection) -> _Self: ...
def clear_args(self) -> _Self: ...
def add_args(self, *args: Injection) -> List[T]: ...
def set_args(self, *args: Injection) -> List[T]: ...
def clear_args(self) -> List[T]: ...
class Dict(Provider[_Dict]):
def __init__(
self, dict_: Optional[Mapping[Any, Injection]] = None, **kwargs: Injection
): ...
def __init__(self, dict_: Optional[_Dict[Any, Injection]] = None, **kwargs: Injection): ...
@property
def kwargs(self) -> _Dict[Any, Injection]: ...
def add_kwargs(
self, dict_: Optional[Mapping[Any, Injection]] = None, **kwargs: Injection
) -> _Self: ...
def set_kwargs(
self, dict_: Optional[Mapping[Any, Injection]] = None, **kwargs: Injection
) -> _Self: ...
def clear_kwargs(self) -> _Self: ...
def add_kwargs(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: ...
class Resource(Provider[T]):
@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
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
def __init__(
self,
provides: Optional[_Callable[..., AbstractContextManager[T]]] = None,
*args: Injection,
**kwargs: Injection,
) -> None: ...
def __init__(self, provides: Optional[_Callable[..., _Iterator[T]]] = None, *args: Injection, **kwargs: Injection) -> None: ...
@overload
def __init__(
self,
provides: Optional[_Callable[..., AbstractAsyncContextManager[T]]] = None,
*args: Injection,
**kwargs: Injection,
) -> None: ...
def __init__(self, provides: Optional[_Callable[..., _AsyncIterator[T]]] = None, *args: Injection, **kwargs: Injection) -> None: ...
@overload
def __init__(
self,
provides: Optional[_Callable[..., _Iterator[T]]] = None,
*args: Injection,
**kwargs: Injection,
) -> None: ...
def __init__(self, provides: Optional[_Callable[..., _Coroutine[Injection, Injection, T]]] = None, *args: Injection, **kwargs: Injection) -> None: ...
@overload
def __init__(
self,
provides: Optional[_Callable[..., _AsyncIterator[T]]] = None,
*args: Injection,
**kwargs: Injection,
) -> None: ...
@overload
def __init__(
self,
provides: Optional[_Callable[..., _Coroutine[Injection, Injection, T]]] = None,
*args: Injection,
**kwargs: Injection,
) -> None: ...
@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
def provides(self) -> Optional[_Callable[..., Any]]: ...
def set_provides(self, provides: Optional[Any]) -> _Self: ...
def set_provides(self, provides: Optional[Any]) -> Resource[T]: ...
@property
def args(self) -> Tuple[Injection]: ...
def add_args(self, *args: Injection) -> _Self: ...
def set_args(self, *args: Injection) -> _Self: ...
def clear_args(self) -> _Self: ...
def add_args(self, *args: Injection) -> Resource[T]: ...
def set_args(self, *args: Injection) -> Resource[T]: ...
def clear_args(self) -> Resource[T]: ...
@property
def kwargs(self) -> _Dict[str, Injection]: ...
def add_kwargs(self, **kwargs: Injection) -> _Self: ...
def set_kwargs(self, **kwargs: Injection) -> _Self: ...
def clear_kwargs(self) -> _Self: ...
def kwargs(self) -> _Dict[Any, Injection]: ...
def add_kwargs(self, **kwargs: Injection) -> Resource[T]: ...
def set_kwargs(self, **kwargs: Injection) -> Resource[T]: ...
def clear_kwargs(self) -> Resource[T]: ...
@property
def initialized(self) -> bool: ...
def init(self) -> Optional[Awaitable[T]]: ...
def shutdown(self) -> Optional[Awaitable]: ...
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: ...
@property
def container(self) -> T: ...
@ -541,17 +448,19 @@ class Container(Provider[T]):
def parent_name(self) -> Optional[str]: ...
def assign_parent(self, parent: ProviderParent) -> None: ...
class Selector(Provider[T_Any]):
def __init__(
self, selector: Optional[_Callable[..., Any]] = None, **providers: Provider
): ...
def __getattr__(self, name: str) -> Provider[T_Any]: ...
class Selector(Provider[Any]):
def __init__(self, selector: Optional[_Callable[..., Any]] = None, **providers: Provider): ...
def __getattr__(self, name: str) -> Provider: ...
@property
def selector(self) -> Optional[_Callable[..., Any]]: ...
def set_selector(self, selector: Optional[_Callable[..., Any]]) -> _Self: ...
def set_selector(self, selector: Optional[_Callable[..., Any]]) -> Selector: ...
@property
def providers(self) -> _Dict[str, Provider[T_Any]]: ...
def set_providers(self, **providers: Provider) -> _Self: ...
def providers(self) -> _Dict[str, Provider]: ...
def set_providers(self, **providers: Provider) -> Selector: ...
class ProvidedInstanceFluentInterface:
def __getattr__(self, item: Any) -> AttributeGetter: ...
@ -559,31 +468,30 @@ class ProvidedInstanceFluentInterface:
def call(self, *args: Injection, **kwargs: Injection) -> MethodCaller: ...
@property
def provides(self) -> Optional[Provider]: ...
def set_provides(self, provides: Optional[Provider]) -> _Self: ...
def set_provides(self, provides: Optional[Provider]) -> ProvidedInstanceFluentInterface: ...
class ProvidedInstance(Provider, ProvidedInstanceFluentInterface):
def __init__(self, provides: Optional[Provider] = None) -> None: ...
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
def name(self) -> Optional[str]: ...
def set_name(self, name: Optional[str]) -> _Self: ...
def set_name(self, name: Optional[str]) -> 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
def name(self) -> Optional[str]: ...
def set_name(self, name: Optional[str]) -> _Self: ...
def set_name(self, name: Optional[str]) -> 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]):
def __init__(self, overridden: Provider, overriding: Provider): ...
@ -592,39 +500,47 @@ class OverridingContext(Generic[T]):
pass
...
class BaseSingletonResetContext(Generic[T]):
def __init__(self, provider: T): ...
def __enter__(self) -> T: ...
def __exit__(self, *_: Any) -> None: ...
class SingletonResetContext(BaseSingletonResetContext): ...
class SingletonFullResetContext(BaseSingletonResetContext): ...
class SingletonResetContext(BaseSingletonResetContext):
...
class SingletonFullResetContext(BaseSingletonResetContext):
...
CHILD_PROVIDERS: Tuple[Provider]
def is_provider(instance: Any) -> bool: ...
def ensure_is_provider(instance: Any) -> Provider: ...
def is_delegated(instance: Any) -> bool: ...
def represent_provider(provider: Provider, provides: Any) -> str: ...
def deepcopy(instance: Any, memo: Optional[_Dict[Any, Any]] = None) -> Any: ...
def deepcopy_args(
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 deepcopy(instance: Any, memo: Optional[_Dict[Any, Any]] = None): Any: ...
def merge_dicts(dict1: _Dict[Any, Any], dict2: _Dict[Any, Any]) -> _Dict[Any, Any]: ...
def traverse(
*providers: Provider, types: Optional[_Iterable[Type]] = None
) -> _Iterator[Provider]: ...
def traverse(*providers: Provider, types: Optional[_Iterable[Type]]=None) -> _Iterator[Provider]: ...
if yaml:
class YamlLoader(yaml.SafeLoader): ...
else:
class YamlLoader: ...

File diff suppressed because it is too large Load Diff

View File

@ -1,54 +1,27 @@
"""Resources module."""
from abc import ABCMeta, abstractmethod
from typing import Any, ClassVar, Generic, Optional, Tuple, TypeVar
import abc
from typing import TypeVar, Generic, Optional
T = TypeVar("T")
class Resource(Generic[T], metaclass=ABCMeta):
__slots__: ClassVar[Tuple[str, ...]] = ("args", "kwargs", "obj")
class Resource(Generic[T], metaclass=abc.ABCMeta):
obj: Optional[T]
@abc.abstractmethod
def init(self, *args, **kwargs) -> Optional[T]:
...
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
def shutdown(self, resource: Optional[T]) -> None:
...
class AsyncResource(Generic[T], metaclass=ABCMeta):
__slots__: ClassVar[Tuple[str, ...]] = ("args", "kwargs", "obj")
class AsyncResource(Generic[T], metaclass=abc.ABCMeta):
obj: Optional[T]
@abc.abstractmethod
async def init(self, *args, **kwargs) -> Optional[T]:
...
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
async def shutdown(self, resource: Optional[T]) -> None:
...

View File

@ -27,9 +27,9 @@ class SchemaProcessorV1:
return self._container.providers
def _create_providers(
self,
provider_schema: ProviderSchema,
container: Optional[containers.Container] = None,
self,
provider_schema: ProviderSchema,
container: Optional[containers.Container] = None,
) -> None:
if container is None:
container = self._container
@ -57,9 +57,9 @@ class SchemaProcessorV1:
self._create_providers(provider_schema=data, container=provider)
def _setup_injections( # noqa: C901
self,
provider_schema: ProviderSchema,
container: Optional[containers.Container] = None,
self,
provider_schema: ProviderSchema,
container: Optional[containers.Container] = None,
) -> None:
if container is None:
container = self._container
@ -72,7 +72,7 @@ class SchemaProcessorV1:
provides = data.get("provides")
if provides:
if isinstance(provides, str) and provides.startswith("container."):
provides = self._resolve_provider(provides[len("container.") :])
provides = self._resolve_provider(provides[len("container."):])
else:
provides = _import_string(provides)
provider.set_provides(provides)
@ -83,7 +83,7 @@ class SchemaProcessorV1:
injection = None
if isinstance(arg, str) and arg.startswith("container."):
injection = self._resolve_provider(arg[len("container.") :])
injection = self._resolve_provider(arg[len("container."):])
# TODO: refactoring
if isinstance(arg, dict):
@ -91,23 +91,16 @@ class SchemaProcessorV1:
provider_type = _get_provider_cls(arg.get("provider"))
provides = arg.get("provides")
if provides:
if isinstance(provides, str) and provides.startswith(
"container."
):
provides = self._resolve_provider(
provides[len("container.") :]
)
if isinstance(provides, str) and provides.startswith("container."):
provides = self._resolve_provider(provides[len("container."):])
else:
provides = _import_string(provides)
provider_args.append(provides)
for provider_arg in arg.get("args", []):
if isinstance(
provider_arg, str
) and provider_arg.startswith("container."):
if isinstance(provider_arg, str) \
and provider_arg.startswith("container."):
provider_args.append(
self._resolve_provider(
provider_arg[len("container.") :]
),
self._resolve_provider(provider_arg[len("container."):]),
)
injection = provider_type(*provider_args)
@ -124,7 +117,7 @@ class SchemaProcessorV1:
injection = None
if isinstance(arg, str) and arg.startswith("container."):
injection = self._resolve_provider(arg[len("container.") :])
injection = self._resolve_provider(arg[len("container."):])
# TODO: refactoring
if isinstance(arg, dict):
@ -132,23 +125,16 @@ class SchemaProcessorV1:
provider_type = _get_provider_cls(arg.get("provider"))
provides = arg.get("provides")
if provides:
if isinstance(provides, str) and provides.startswith(
"container."
):
provides = self._resolve_provider(
provides[len("container.") :]
)
if isinstance(provides, str) and provides.startswith("container."):
provides = self._resolve_provider(provides[len("container."):])
else:
provides = _import_string(provides)
provider_args.append(provides)
for provider_arg in arg.get("args", []):
if isinstance(
provider_arg, str
) and provider_arg.startswith("container."):
if isinstance(provider_arg, str) \
and provider_arg.startswith("container."):
provider_args.append(
self._resolve_provider(
provider_arg[len("container.") :]
),
self._resolve_provider(provider_arg[len("container."):]),
)
injection = provider_type(*provider_args)
@ -172,7 +158,7 @@ class SchemaProcessorV1:
for segment in segments[1:]:
parentheses = ""
if "(" in segment and ")" in segment:
parentheses = segment[segment.find("(") : segment.rfind(")") + 1]
parentheses = segment[segment.find("("):segment.rfind(")")+1]
segment = segment.replace(parentheses, "")
try:
@ -204,12 +190,10 @@ def _get_provider_cls(provider_cls_name: str) -> Type[providers.Provider]:
if 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)
@ -217,16 +201,12 @@ def _import_provider_cls(provider_cls_name: str) -> Optional[Type[providers.Prov
try:
cls = _import_string(provider_cls_name)
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:
return None
else:
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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,10 @@
[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.*

View File

@ -0,0 +1,10 @@
[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.*

11
tests/.configs/pytest.ini Normal file
View File

@ -0,0 +1,11 @@
[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.*

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