Compare commits

..

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

107 changed files with 1379 additions and 3307 deletions

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

View File

@ -4,12 +4,28 @@ on: [push, pull_request, workflow_dispatch]
jobs: jobs:
test-on-different-versions: tests-on-legacy-versions:
name: Run tests name: Run tests on legacy versions
runs-on: ubuntu-latest runs-on: ubuntu-20.04
strategy: strategy:
matrix: matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] python-version: [3.7]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- run: pip install tox
- run: tox
env:
TOXENV: ${{ matrix.python-version }}
test-on-different-versions:
name: Run tests
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.8, 3.9, "3.10", 3.11, 3.12, 3.13]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/setup-python@v4 - uses: actions/setup-python@v4
@ -18,7 +34,6 @@ jobs:
- run: pip install tox - run: pip install tox
- run: tox - run: tox
env: env:
DEPENDENCY_INJECTOR_LIMITED_API: 1
TOXENV: ${{ matrix.python-version }} TOXENV: ${{ matrix.python-version }}
test-different-pydantic-versions: test-different-pydantic-versions:
@ -45,7 +60,7 @@ jobs:
- uses: actions/setup-python@v4 - uses: actions/setup-python@v4
with: with:
python-version: 3.12 python-version: 3.12
- run: pip install tox - run: pip install tox 'cython>=3,<4'
- run: tox -vv - run: tox -vv
env: env:
TOXENV: coveralls TOXENV: coveralls

1
.gitignore vendored
View File

@ -15,7 +15,6 @@ lib64/
parts/ parts/
sdist/ sdist/
var/ var/
wheelhouse/
*.egg-info/ *.egg-info/
.installed.cfg .installed.cfg
*.egg *.egg

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 recursive-include tests *.py
include README.rst include README.rst
include CONTRIBUTORS.rst include CONTRIBUTORS.rst
include LICENSE.rst include LICENSE.rst
include requirements.txt
include setup.py include setup.py
include tox.ini include tox.ini
include py.typed

View File

@ -1,10 +1,6 @@
VERSION := $(shell python setup.py --version) VERSION := $(shell python setup.py --version)
export COVERAGE_RCFILE := pyproject.toml 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
clean: clean:
# Clean sources # Clean sources
@ -40,7 +36,7 @@ uninstall:
test: test:
# Unit tests with coverage report # Unit tests with coverage report
coverage erase coverage erase
coverage run -m pytest coverage run -m pytest -c tests/.configs/pytest.ini
coverage report coverage report
coverage html coverage html
@ -67,6 +63,3 @@ publish:
# Create and upload tag # Create and upload tag
git tag -a $(VERSION) -m 'version $(VERSION)' git tag -a $(VERSION) -m 'version $(VERSION)'
git push --tags 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:: .. toctree::
:maxdepth: 2 :maxdepth: 2
top-level top-level
providers providers
containers containers
wiring wiring
errors errors
asgi-lifespan

View File

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

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 Working with the providers of the container on the class level will influence all further
instances. 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: 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 .. literalinclude:: ../../examples/miniapps/django/web/apps.py
:language: python :language: python
:emphasize-lines: 12 :emphasize-lines: 13
Tests 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
fastapi-redis fastapi-redis
fastapi-sqlalchemy fastapi-sqlalchemy
fastdepends
.. disqus:: .. 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: The framework stands on the `PEP20 (The Zen of Python) <https://www.python.org/dev/peps/pep-0020/>`_ principle:
.. code-block:: text .. code-block:: plain
Explicit is better than implicit Explicit is better than implicit

View File

@ -7,75 +7,6 @@ that were made in every particular version.
From version 0.7.6 *Dependency Injector* framework strictly From version 0.7.6 *Dependency Injector* framework strictly
follows `Semantic versioning`_ follows `Semantic versioning`_
4.48.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 4.45.0
-------- --------
- Add Starlette lifespan handler implementation (`#683 <https://github.com/ets-labs/python-dependency-injector/pull/683>`_). - Add Starlette lifespan handler implementation (`#683 <https://github.com/ets-labs/python-dependency-injector/pull/683>`_).
@ -409,8 +340,8 @@ Many thanks to `ZipFile <https://github.com/ZipFile>`_ for both contributions.
- Make refactoring of wiring module and tests. - Make refactoring of wiring module and tests.
See PR # `#406 <https://github.com/ets-labs/python-dependency-injector/issues/406>`_. See PR # `#406 <https://github.com/ets-labs/python-dependency-injector/issues/406>`_.
Thanks to `@withshubh <https://github.com/withshubh>`_ for the contribution: Thanks to `@withshubh <https://github.com/withshubh>`_ for the contribution:
- Remove unused imports in tests. - Remove unused imports in tests.
- Use literal syntax to create data structure in tests. - Use literal syntax to create data structure in tests.
- Add integration with a static analysis tool `DeepSource <https://deepsource.io/>`_. - Add integration with a static analysis tool `DeepSource <https://deepsource.io/>`_.
4.26.0 4.26.0

View File

@ -366,19 +366,6 @@ See also: :ref:`configuration-strict-mode`.
assert container.config.section.option() is None assert container.config.section.option() is None
If you want to disable environment variables interpolation, pass ``envs_required=None``:
.. code-block:: yaml
:caption: templates.yml
template_string: 'Hello, ${name}!'
.. code-block:: python
>>> container.config.from_yaml("templates.yml", envs_required=None)
>>> container.config.template_string()
'Hello, ${name}!'
Mandatory and optional sources Mandatory and optional sources
------------------------------ ------------------------------

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 if any, and switch to uninitialized state. Some of resource initializer types support specifying custom
resource shutdown. resource shutdown.
Resource provider supports 4 types of initializers: Resource provider supports 3 types of initializers:
- Function - Function
- Context Manager - Generator
- Generator (legacy) - Subclass of ``resources.Resource``
- Subclass of ``resources.Resource`` (legacy)
Function initializer Function initializer
-------------------- --------------------
@ -104,44 +103,8 @@ you configure global resource:
Function initializer does not provide a way to specify custom resource shutdown. Function initializer does not provide a way to specify custom resource shutdown.
Context Manager initializer Generator initializer
--------------------------- ---------------------
This is an extension to the Function initializer. Resource provider automatically detects if the initializer returns a
context manager and uses it to manage the resource lifecycle.
.. code-block:: python
from dependency_injector import containers, providers
class DatabaseConnection:
def __init__(self, host, port, user, password):
self.host = host
self.port = port
self.user = user
self.password = password
def __enter__(self):
print(f"Connecting to {self.host}:{self.port} as {self.user}")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("Closing connection")
class Container(containers.DeclarativeContainer):
config = providers.Configuration()
db = providers.Resource(
DatabaseConnection,
host=config.db.host,
port=config.db.port,
user=config.db.user,
password=config.db.password,
)
Generator initializer (legacy)
------------------------------
Resource provider can use 2-step generators: Resource provider can use 2-step generators:
@ -191,13 +154,8 @@ object is not mandatory. You can leave ``yield`` statement empty:
argument2=..., argument2=...,
) )
.. note:: Subclass initializer
--------------------
Generator initializers are automatically wrapped with ``contextmanager`` or ``asynccontextmanager`` decorator when
provided to a ``Resource`` provider.
Subclass initializer (legacy)
-----------------------------
You can create resource initializer by implementing a subclass of the ``resources.Resource``: You can create resource initializer by implementing a subclass of the ``resources.Resource``:
@ -252,72 +210,6 @@ first argument.
.. _resource-provider-wiring-closing: .. _resource-provider-wiring-closing:
Scoping Resources using specialized subclasses
----------------------------------------------
You can use specialized subclasses of ``Resource`` provider to initialize and shutdown resources by type.
Allowing for example to only initialize a subgroup of resources.
.. code-block:: python
class ScopedResource(resources.Resource):
pass
def init_service(name) -> Service:
print(f"Init {name}")
yield Service()
print(f"Shutdown {name}")
class Container(containers.DeclarativeContainer):
scoped = ScopedResource(
init_service,
"scoped",
)
generic = providers.Resource(
init_service,
"generic",
)
To initialize resources by type you can use ``init_resources(resource_type)`` and ``shutdown_resources(resource_type)``
methods adding the resource type as an argument:
.. code-block:: python
def main():
container = Container()
container.init_resources(ScopedResource)
# Generates:
# >>> Init scoped
container.shutdown_resources(ScopedResource)
# Generates:
# >>> Shutdown scoped
And to initialize all resources you can use ``init_resources()`` and ``shutdown_resources()`` without arguments:
.. code-block:: python
def main():
container = Container()
container.init_resources()
# Generates:
# >>> Init scoped
# >>> Init generic
container.shutdown_resources()
# Generates:
# >>> Shutdown scoped
# >>> Shutdown generic
It works using the ``traverse()`` method to find all resources of the specified type, selecting all resources
which are instances of the specified type.
Resources, wiring, and per-function execution scope Resources, wiring, and per-function execution scope
--------------------------------------------------- ---------------------------------------------------
@ -371,11 +263,10 @@ Asynchronous function initializer:
argument2=..., argument2=...,
) )
Asynchronous Context Manager initializer: Asynchronous generator initializer:
.. code-block:: python .. code-block:: python
@asynccontextmanager
async def init_async_resource(argument1=..., argument2=...): async def init_async_resource(argument1=..., argument2=...):
connection = await connect() connection = await connect()
yield connection yield connection
@ -467,54 +358,5 @@ See also:
- Wiring :ref:`async-injections-wiring` - Wiring :ref:`async-injections-wiring`
- :ref:`fastapi-redis-example` - :ref:`fastapi-redis-example`
ASGI Lifespan Protocol Support
------------------------------
The :mod:`dependency_injector.ext.starlette` module provides a :class:`~dependency_injector.ext.starlette.Lifespan`
class that integrates resource providers with ASGI applications using the `Lifespan Protocol`_. This allows resources to
be automatically initialized at application startup and properly shut down when the application stops.
.. code-block:: python
from contextlib import asynccontextmanager
from dependency_injector import containers, providers
from dependency_injector.wiring import Provide, inject
from dependency_injector.ext.starlette import Lifespan
from fastapi import FastAPI, Request, Depends, APIRouter
class Connection: ...
@asynccontextmanager
async def init_database():
print("opening database connection")
yield Connection()
print("closing database connection")
router = APIRouter()
@router.get("/")
@inject
async def index(request: Request, db: Connection = Depends(Provide["db"])):
# use the database connection here
return "OK!"
class Container(containers.DeclarativeContainer):
__self__ = providers.Self()
db = providers.Resource(init_database)
lifespan = providers.Singleton(Lifespan, __self__)
app = providers.Singleton(FastAPI, lifespan=lifespan)
_include_router = providers.Resource(
app.provided.include_router.call(),
router,
)
if __name__ == "__main__":
import uvicorn
container = Container()
app = container.app()
uvicorn.run(app, host="localhost", port=8000)
.. _Lifespan Protocol: https://asgi.readthedocs.io/en/latest/specs/lifespan.html
.. disqus:: .. disqus::

View File

@ -33,7 +33,7 @@ factories:
- :ref:`factory-specialize-provided-type` - :ref:`factory-specialize-provided-type`
- :ref:`abstract-factory` - :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: two different singleton objects:
.. literalinclude:: ../../examples/providers/singleton_multiple_containers.py .. 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: You should see:
.. code-block:: http .. code-block:: json
HTTP/1.1 200 OK HTTP/1.1 200 OK
Content-Length: 844 Content-Length: 844
@ -596,7 +596,7 @@ and make a request to the API in the terminal:
You should see: You should see:
.. code-block:: http .. code-block:: json
HTTP/1.1 200 OK HTTP/1.1 200 OK
Content-Length: 492 Content-Length: 492

View File

@ -84,7 +84,7 @@ Create next structure in the project root directory. All files are empty. That's
Initial project layout: Initial project layout:
.. code-block:: text .. code-block:: bash
./ ./
├── movies/ ├── movies/
@ -109,7 +109,7 @@ Now it's time to install the project requirements. We will use next packages:
Put next lines into the ``requirements.txt`` file: Put next lines into the ``requirements.txt`` file:
.. code-block:: text .. code-block:: bash
dependency-injector dependency-injector
pyyaml pyyaml
@ -134,7 +134,7 @@ We will create a script that creates database files.
First add the folder ``data/`` in the root of the project and then add the file First add the folder ``data/`` in the root of the project and then add the file
``fixtures.py`` inside of it: ``fixtures.py`` inside of it:
.. code-block:: text .. code-block:: bash
:emphasize-lines: 2-3 :emphasize-lines: 2-3
./ ./
@ -205,13 +205,13 @@ Now run in the terminal:
You should see: You should see:
.. code-block:: text .. code-block:: bash
OK OK
Check that files ``movies.csv`` and ``movies.db`` have appeared in the ``data/`` folder: Check that files ``movies.csv`` and ``movies.db`` have appeared in the ``data/`` folder:
.. code-block:: text .. code-block:: bash
:emphasize-lines: 4-5 :emphasize-lines: 4-5
./ ./
@ -289,7 +289,7 @@ After each step we will add the provider to the container.
Create the ``entities.py`` in the ``movies`` package: Create the ``entities.py`` in the ``movies`` package:
.. code-block:: text .. code-block:: bash
:emphasize-lines: 10 :emphasize-lines: 10
./ ./
@ -356,7 +356,7 @@ Let's move on to the finders.
Create the ``finders.py`` in the ``movies`` package: Create the ``finders.py`` in the ``movies`` package:
.. code-block:: text .. code-block:: bash
:emphasize-lines: 11 :emphasize-lines: 11
./ ./
@ -465,7 +465,7 @@ The configuration file is ready. Move on to the lister.
Create the ``listers.py`` in the ``movies`` package: Create the ``listers.py`` in the ``movies`` package:
.. code-block:: text .. code-block:: bash
:emphasize-lines: 12 :emphasize-lines: 12
./ ./
@ -613,7 +613,7 @@ Run in the terminal:
You should see: You should see:
.. code-block:: text .. code-block:: plain
Francis Lawrence movies: Francis Lawrence movies:
- Movie(title='The Hunger Games: Mockingjay - Part 2', year=2015, director='Francis Lawrence') - Movie(title='The Hunger Games: Mockingjay - Part 2', year=2015, director='Francis Lawrence')
@ -752,7 +752,7 @@ Run in the terminal:
You should see: You should see:
.. code-block:: text .. code-block:: plain
Francis Lawrence movies: Francis Lawrence movies:
- Movie(title='The Hunger Games: Mockingjay - Part 2', year=2015, director='Francis Lawrence') - Movie(title='The Hunger Games: Mockingjay - Part 2', year=2015, director='Francis Lawrence')
@ -868,7 +868,7 @@ Run in the terminal line by line:
The output should be similar for each command: The output should be similar for each command:
.. code-block:: text .. code-block:: plain
Francis Lawrence movies: Francis Lawrence movies:
- Movie(title='The Hunger Games: Mockingjay - Part 2', year=2015, director='Francis Lawrence') - Movie(title='The Hunger Games: Mockingjay - Part 2', year=2015, director='Francis Lawrence')
@ -888,7 +888,7 @@ We will use `pytest <https://docs.pytest.org/en/stable/>`_ and
Create ``tests.py`` in the ``movies`` package: Create ``tests.py`` in the ``movies`` package:
.. code-block:: text .. code-block:: bash
:emphasize-lines: 13 :emphasize-lines: 13
./ ./
@ -911,7 +911,7 @@ Create ``tests.py`` in the ``movies`` package:
and put next into it: and put next into it:
.. code-block:: python .. code-block:: python
:emphasize-lines: 41,50 :emphasize-lines: 36,51
"""Tests module.""" """Tests module."""
@ -941,18 +941,13 @@ and put next into it:
return container return container
@pytest.fixture def test_movies_directed_by(container):
def finder_mock(container):
finder_mock = mock.Mock() finder_mock = mock.Mock()
finder_mock.find_all.return_value = [ finder_mock.find_all.return_value = [
container.movie("The 33", 2015, "Patricia Riggen"), container.movie("The 33", 2015, "Patricia Riggen"),
container.movie("The Jungle Book", 2016, "Jon Favreau"), container.movie("The Jungle Book", 2016, "Jon Favreau"),
] ]
return finder_mock
def test_movies_directed_by(container, finder_mock):
with container.finder.override(finder_mock): with container.finder.override(finder_mock):
lister = container.lister() lister = container.lister()
movies = lister.movies_directed_by("Jon Favreau") movies = lister.movies_directed_by("Jon Favreau")
@ -961,7 +956,13 @@ and put next into it:
assert movies[0].title == "The Jungle Book" assert movies[0].title == "The Jungle Book"
def test_movies_released_in(container, finder_mock): def test_movies_released_in(container):
finder_mock = mock.Mock()
finder_mock.find_all.return_value = [
container.movie("The 33", 2015, "Patricia Riggen"),
container.movie("The Jungle Book", 2016, "Jon Favreau"),
]
with container.finder.override(finder_mock): with container.finder.override(finder_mock):
lister = container.lister() lister = container.lister()
movies = lister.movies_released_in(2015) movies = lister.movies_released_in(2015)
@ -977,7 +978,7 @@ Run in the terminal:
You should see: You should see:
.. code-block:: text .. code-block::
platform darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0 platform darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
plugins: cov-3.0.0 plugins: cov-3.0.0
@ -994,9 +995,9 @@ You should see:
movies/entities.py 7 1 86% movies/entities.py 7 1 86%
movies/finders.py 26 13 50% movies/finders.py 26 13 50%
movies/listers.py 8 0 100% movies/listers.py 8 0 100%
movies/tests.py 24 0 100% movies/tests.py 23 0 100%
------------------------------------------ ------------------------------------------
TOTAL 90 30 67% TOTAL 89 30 66%
.. note:: .. note::

View File

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

View File

@ -64,7 +64,7 @@ FastAPI example:
@app.api_route("/") @app.api_route("/")
@inject @inject
async def index(service: Annotated[Service, Depends(Provide[Container.service])]): async def index(service: Service = Depends(Provide[Container.service])):
value = await service.process() value = await service.process()
return {"result": value} return {"result": value}
@ -127,7 +127,6 @@ To inject the provider itself use ``Provide[foo.provider]``:
def foo(bar_provider: Factory[Bar] = Provide[Container.bar.provider]): def foo(bar_provider: Factory[Bar] = Provide[Container.bar.provider]):
bar = bar_provider(argument="baz") bar = bar_provider(argument="baz")
... ...
You can also use ``Provider[foo]`` for injecting the provider itself: You can also use ``Provider[foo]`` for injecting the provider itself:
.. code-block:: python .. code-block:: python
@ -251,73 +250,17 @@ To inject a container use special identifier ``<container>``:
def foo(container: Container = Provide["<container>"]) -> None: 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 Making injections into modules and class attributes
--------------------------------------------------- ---------------------------------------------------
You can use wiring to make injections into modules and class attributes. Both the classic marker You can use wiring to make injections into modules and class attributes.
syntax and the ``Annotated`` form are supported.
Classic marker syntax:
.. code-block:: python
service: Service = Provide[Container.service]
class Main:
service: Service = Provide[Container.service]
Full example of the classic marker syntax:
.. literalinclude:: ../examples/wiring/example_attribute.py .. literalinclude:: ../examples/wiring/example_attribute.py
:language: python :language: python
:lines: 3- :lines: 3-
:emphasize-lines: 14,19 :emphasize-lines: 14,19
Annotated form (Python 3.9+):
.. code-block:: python
from typing import Annotated
service: Annotated[Service, Provide[Container.service]]
class Main:
service: Annotated[Service, Provide[Container.service]]
Full example of the annotated form:
.. literalinclude:: ../examples/wiring/example_attribute_annotated.py
:language: python
:lines: 3-
:emphasize-lines: 16,21
You could also use string identifiers to avoid a dependency on a container: You could also use string identifiers to avoid a dependency on a container:
.. code-block:: python .. code-block:: python
@ -658,36 +601,6 @@ or with a single container ``register_loader_containers(container)`` multiple ti
To unregister a container use ``unregister_loader_containers(container)``. To unregister a container use ``unregister_loader_containers(container)``.
Wiring module will uninstall the import hook when unregister last container. Wiring module will uninstall the import hook when unregister last container.
Few notes on performance
------------------------
``.wire()`` utilize caching to speed up the wiring process. At the end it clears the cache to avoid memory leaks.
But this may not always be desirable, when you want to keep the cache for the next wiring
(e.g. due to usage of multiple containers or during unit tests).
To keep the cache after wiring, you can set flag ``keep_cache=True`` (works with ``WiringConfiguration`` too):
.. code-block:: python
container1.wire(
modules=["yourapp.module1", "yourapp.module2"],
keep_cache=True,
)
container2.wire(
modules=["yourapp.module2", "yourapp.module3"],
keep_cache=True,
)
...
and then clear it manually when you need it:
.. code-block:: python
from dependency_injector.wiring import clear_cache
clear_cache()
Integration with other frameworks Integration with other frameworks
--------------------------------- ---------------------------------
@ -719,6 +632,5 @@ Take a look at other application examples:
- :ref:`fastapi-example` - :ref:`fastapi-example`
- :ref:`fastapi-redis-example` - :ref:`fastapi-redis-example`
- :ref:`fastapi-sqlalchemy-example` - :ref:`fastapi-sqlalchemy-example`
- :ref:`fastdepends-example`
.. disqus:: .. disqus::

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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,5 +1,5 @@
[build-system] [build-system]
requires = ["setuptools", "Cython>=3.1.4"] requires = ["setuptools", "Cython"]
build-backend = "setuptools.build_meta" build-backend = "setuptools.build_meta"
[project] [project]
@ -13,7 +13,7 @@ maintainers = [
description = "Dependency injection framework for Python" description = "Dependency injection framework for Python"
readme = {file = "README.rst", content-type = "text/x-rst"} readme = {file = "README.rst", content-type = "text/x-rst"}
license = {file = "LICENSE.rst", content-type = "text/x-rst"} license = {file = "LICENSE.rst", content-type = "text/x-rst"}
requires-python = ">=3.8" requires-python = ">=3.7"
keywords = [ keywords = [
"Dependency injection", "Dependency injection",
"DI", "DI",
@ -31,6 +31,7 @@ classifiers = [
"Operating System :: OS Independent", "Operating System :: OS Independent",
"Programming Language :: Python", "Programming Language :: Python",
"Programming Language :: Python :: 3", "Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.10",
@ -52,12 +53,6 @@ classifiers = [
"Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Software Development :: Libraries :: Python Modules",
] ]
dynamic = ["version"] 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] [project.optional-dependencies]
yaml = ["pyyaml"] yaml = ["pyyaml"]
@ -97,7 +92,6 @@ show_missing = true
[tool.isort] [tool.isort]
profile = "black" profile = "black"
combine_as_imports = true
[tool.pylint.main] [tool.pylint.main]
ignore = ["tests"] ignore = ["tests"]
@ -105,19 +99,3 @@ ignore = ["tests"]
[tool.pylint.design] [tool.pylint.design]
min-public-methods = 0 min-public-methods = 0
max-public-methods = 30 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,4 +1,4 @@
cython==3.1.4 cython==3.0.11
setuptools setuptools
pytest pytest
pytest-asyncio pytest-asyncio
@ -13,13 +13,10 @@ mypy
pyyaml pyyaml
httpx httpx
fastapi fastapi
pydantic pydantic==1.10.17
pydantic-settings
numpy numpy
scipy scipy
boto3 boto3
mypy_boto3_s3 mypy_boto3_s3
typing_extensions
fast-depends
-r requirements-ext.txt -r requirements-ext.txt

View File

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

View File

@ -1,22 +1,13 @@
"""`Dependency injector` setup script.""" """`Dependency injector` setup script."""
import os import os
import sys
import sysconfig
from Cython.Build import cythonize from Cython.Build import cythonize
from Cython.Compiler import Options from Cython.Compiler import Options
from setuptools import Extension, setup from setuptools import Extension, setup
debug = os.environ.get("DEPENDENCY_INJECTOR_DEBUG_MODE") == "1" 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 = [] defined_macros = []
options = {}
compiler_directives = { compiler_directives = {
"language_level": 3, "language_level": 3,
"profile": debug, "profile": debug,
@ -26,7 +17,6 @@ Options.annotate = debug
# Adding debug options: # Adding debug options:
if debug: if debug:
limited_api = False # line tracing is not part of the Limited API
defined_macros.extend( defined_macros.extend(
[ [
("CYTHON_TRACE", "1"), ("CYTHON_TRACE", "1"),
@ -35,20 +25,14 @@ if debug:
] ]
) )
if limited_api:
options.setdefault("bdist_wheel", {})
options["bdist_wheel"]["py_limited_api"] = "cp310"
defined_macros.append(("Py_LIMITED_API", "0x030A0000"))
setup( setup(
options=options,
ext_modules=cythonize( ext_modules=cythonize(
[ [
Extension( Extension(
"*", "*",
["src/**/*.pyx"], ["src/**/*.pyx"],
define_macros=defined_macros, define_macros=defined_macros,
py_limited_api=limited_api,
), ),
], ],
annotate=debug, annotate=debug,

View File

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

View File

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

View File

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

View File

@ -1,11 +1,17 @@
"""Containers module.""" """Containers module."""
import asyncio
import contextlib import contextlib
import copy as copy_module import copy as copy_module
import json import json
import sys
import importlib import importlib
import inspect import inspect
import warnings
try:
import asyncio
except ImportError:
asyncio = None
try: try:
import yaml import yaml
@ -14,40 +20,40 @@ except ImportError:
from . import providers, errors from . import providers, errors
from .providers cimport __is_future_or_coroutine from .providers cimport __is_future_or_coroutine
from .wiring import wire, unwire
if sys.version_info[:2] >= (3, 6):
from .wiring import wire, unwire
else:
def wire(*args, **kwargs):
raise NotImplementedError("Wiring requires Python 3.6 or above")
def unwire(*args, **kwargs):
raise NotImplementedError("Wiring requires Python 3.6 or above")
if sys.version_info[:2] == (3, 5):
warnings.warn(
"Dependency Injector will drop support of Python 3.5 after Jan 1st of 2022. "
"This does not mean that there will be any immediate breaking changes, "
"but tests will no longer be executed on Python 3.5, and bugs will not be addressed.",
category=DeprecationWarning,
)
class WiringConfiguration: class WiringConfiguration:
"""Container wiring configuration.""" """Container wiring configuration."""
def __init__( def __init__(self, modules=None, packages=None, from_package=None, auto_wire=True):
self,
modules=None,
packages=None,
from_package=None,
auto_wire=True,
keep_cache=False,
warn_unresolved=False,
):
self.modules = [*modules] if modules else [] self.modules = [*modules] if modules else []
self.packages = [*packages] if packages else [] self.packages = [*packages] if packages else []
self.from_package = from_package self.from_package = from_package
self.auto_wire = auto_wire self.auto_wire = auto_wire
self.keep_cache = keep_cache
self.warn_unresolved = warn_unresolved
def __deepcopy__(self, memo=None): def __deepcopy__(self, memo=None):
return self.__class__( return self.__class__(self.modules, self.packages, self.from_package, self.auto_wire)
self.modules,
self.packages,
self.from_package,
self.auto_wire,
self.keep_cache,
self.warn_unresolved,
)
class Container: class Container(object):
"""Abstract container.""" """Abstract container."""
@ -275,14 +281,7 @@ class DynamicContainer(Container):
"""Check if auto wiring is needed.""" """Check if auto wiring is needed."""
return self.wiring_config.auto_wire is True return self.wiring_config.auto_wire is True
def wire( def wire(self, modules=None, packages=None, from_package=None):
self,
modules=None,
packages=None,
from_package=None,
keep_cache=None,
warn_unresolved=False,
):
"""Wire container providers with provided packages and modules. """Wire container providers with provided packages and modules.
:rtype: None :rtype: None
@ -313,15 +312,10 @@ class DynamicContainer(Container):
if not modules and not packages: if not modules and not packages:
return return
if keep_cache is None:
keep_cache = self.wiring_config.keep_cache
wire( wire(
container=self, container=self,
modules=modules, modules=modules,
packages=packages, packages=packages,
keep_cache=keep_cache,
warn_unresolved=warn_unresolved,
) )
if modules: if modules:
@ -339,15 +333,11 @@ class DynamicContainer(Container):
self.wired_to_modules.clear() self.wired_to_modules.clear()
self.wired_to_packages.clear() self.wired_to_packages.clear()
def init_resources(self, resource_type=providers.Resource): def init_resources(self):
"""Initialize all container resources.""" """Initialize all container resources."""
if not issubclass(resource_type, providers.Resource):
raise TypeError("resource_type must be a subclass of Resource provider")
futures = [] futures = []
for provider in self.traverse(types=[resource_type]): for provider in self.traverse(types=[providers.Resource]):
resource = provider.init() resource = provider.init()
if __is_future_or_coroutine(resource): if __is_future_or_coroutine(resource):
@ -356,12 +346,8 @@ class DynamicContainer(Container):
if futures: if futures:
return asyncio.gather(*futures) return asyncio.gather(*futures)
def shutdown_resources(self, resource_type=providers.Resource): def shutdown_resources(self):
"""Shutdown all container resources.""" """Shutdown all container resources."""
if not issubclass(resource_type, providers.Resource):
raise TypeError("resource_type must be a subclass of Resource provider")
def _independent_resources(resources): def _independent_resources(resources):
for resource in resources: for resource in resources:
for other_resource in resources: for other_resource in resources:
@ -392,7 +378,7 @@ class DynamicContainer(Container):
for resource in resources_to_shutdown: for resource in resources_to_shutdown:
resource.shutdown() resource.shutdown()
resources = list(self.traverse(types=[resource_type])) resources = list(self.traverse(types=[providers.Resource]))
if any(resource.is_async_mode_enabled() for resource in resources): if any(resource.is_async_mode_enabled() for resource in resources):
return _async_ordered_shutdown(resources) return _async_ordered_shutdown(resources)
else: else:

View File

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

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 from dependency_injector import providers
T = TypeVar("T")
class Application(providers.Singleton[T]): ... class Application(providers.Singleton): ...
class Extension(providers.Singleton[T]): ...
class Middleware(providers.DelegatedCallable[T]): ...
class MiddlewareFactory(providers.Factory[T]): ...
class View(providers.Callable[T]):
def as_view(self) -> _Awaitable[T]: ...
class ClassBasedView(providers.Factory[T]): class Extension(providers.Singleton): ...
def as_view(self) -> _Awaitable[T]: ...
class Middleware(providers.DelegatedCallable): ...
class MiddlewareFactory(providers.Factory): ...
class View(providers.Callable):
def as_view(self) -> _Awaitable: ...
class ClassBasedView(providers.Factory):
def as_view(self) -> _Awaitable: ...

View File

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

View File

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

View File

@ -1,5 +1,5 @@
import sys import sys
from typing import Any, Type from typing import Any
if sys.version_info >= (3, 11): # pragma: no cover if sys.version_info >= (3, 11): # pragma: no cover
from typing import Self from typing import Self
@ -7,7 +7,6 @@ else: # pragma: no cover
from typing_extensions import Self from typing_extensions import Self
from dependency_injector.containers import Container from dependency_injector.containers import Container
from dependency_injector.providers import Resource
class Lifespan: class Lifespan:
@ -30,32 +29,24 @@ class Lifespan:
app = Factory(Starlette, lifespan=lifespan) app = Factory(Starlette, lifespan=lifespan)
:param container: container instance :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 container: Container
resource_type: Type[Resource[Any]]
def __init__( def __init__(self, container: Container) -> None:
self,
container: Container,
resource_type: Type[Resource[Any]] = Resource,
) -> None:
self.container = container self.container = container
self.resource_type = resource_type
def __call__(self, app: Any) -> Self: def __call__(self, app: Any) -> Self:
return self return self
async def __aenter__(self) -> None: async def __aenter__(self) -> None:
result = self.container.init_resources(self.resource_type) result = self.container.init_resources()
if result is not None: if result is not None:
await result await result
async def __aexit__(self, *exc_info: Any) -> None: async def __aexit__(self, *exc_info: Any) -> None:
result = self.container.shutdown_resources(self.resource_type) result = self.container.shutdown_resources()
if result is not None: if result is not None:
await result await result

View File

@ -1,6 +1,10 @@
"""Providers module.""" """Providers module."""
import asyncio try:
import asyncio
except ImportError:
asyncio = None
import functools import functools
cimport cython cimport cython
@ -15,7 +19,7 @@ cdef tuple __COROUTINE_TYPES
# Base providers # Base providers
cdef class Provider: cdef class Provider(object):
cdef tuple _overridden cdef tuple _overridden
cdef Provider _last_overriding cdef Provider _last_overriding
cdef tuple _overrides cdef tuple _overrides
@ -287,7 +291,7 @@ cdef class MethodCaller(Provider):
# Injections # Injections
cdef class Injection: cdef class Injection(object):
cdef object _value cdef object _value
cdef int _is_provider cdef int _is_provider
cdef int _is_delegated cdef int _is_delegated
@ -309,12 +313,12 @@ cpdef tuple parse_named_injections(dict kwargs)
# Utils # Utils
cdef class OverridingContext: cdef class OverridingContext(object):
cdef Provider _overridden cdef Provider _overridden
cdef Provider _overriding cdef Provider _overriding
cdef class BaseSingletonResetContext: cdef class BaseSingletonResetContext(object):
cdef object _singleton cdef object _singleton
@ -697,10 +701,3 @@ cdef inline object __future_result(object instance):
future_result = asyncio.Future() future_result = asyncio.Future()
future_result.set_result(instance) future_result.set_result(instance)
return future_result return future_result
cdef class NullAwaitable:
pass
cdef NullAwaitable NULL_AWAITABLE

View File

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

View File

@ -2,8 +2,6 @@
from __future__ import absolute_import from __future__ import absolute_import
import asyncio
import builtins
import copy import copy
import errno import errno
import functools import functools
@ -14,26 +12,36 @@ import os
import re import re
import sys import sys
import threading import threading
import types
import warnings import warnings
from asyncio import ensure_future
from configparser import ConfigParser as IniConfigParser
from contextlib import asynccontextmanager, contextmanager
from contextvars import ContextVar
from inspect import isasyncgenfunction, isgeneratorfunction
try: try:
from inspect import _is_coroutine_mark as _is_coroutine_marker import contextvars
except ImportError: except ImportError:
try: contextvars = None
# Python >=3.12.0,<3.12.5
from inspect import _is_coroutine_marker try:
except ImportError: import builtins
except ImportError:
# Python 2.7
import __builtin__ as builtins
try:
import asyncio
except ImportError:
asyncio = None
_is_coroutine_marker = None
else:
if sys.version_info >= (3, 5, 3):
import asyncio.coroutines
_is_coroutine_marker = asyncio.coroutines._is_coroutine
else:
_is_coroutine_marker = True _is_coroutine_marker = True
try: try:
from asyncio.coroutines import _is_coroutine import ConfigParser as iniconfigparser
except ImportError: except ImportError:
_is_coroutine = True import configparser as iniconfigparser
try: try:
import yaml import yaml
@ -69,11 +77,29 @@ from .errors import (
cimport cython cimport cython
if sys.version_info[0] == 3: # pragma: no cover
CLASS_TYPES = (type,)
else: # pragma: no cover
CLASS_TYPES = (type, types.ClassType)
copy._deepcopy_dispatch[types.MethodType] = \
lambda obj, memo: type(obj)(obj.im_func,
copy.deepcopy(obj.im_self, memo),
obj.im_class)
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,
)
config_env_marker_pattern = re.compile( config_env_marker_pattern = re.compile(
r"\${(?P<name>[^}^{:]+)(?P<separator>:?)(?P<default>.*?)}", r"\${(?P<name>[^}^{:]+)(?P<separator>:?)(?P<default>.*?)}",
) )
cdef str _resolve_config_env_markers(config_content: str, envs_required: bool): def _resolve_config_env_markers(config_content, envs_required=False):
"""Replace environment variable markers with their values.""" """Replace environment variable markers with their values."""
findings = list(config_env_marker_pattern.finditer(config_content)) findings = list(config_env_marker_pattern.finditer(config_content))
@ -92,19 +118,28 @@ cdef str _resolve_config_env_markers(config_content: str, envs_required: bool):
return config_content return config_content
cdef object _parse_ini_file(filepath, envs_required: bool | None): if sys.version_info[0] == 3:
parser = IniConfigParser() def _parse_ini_file(filepath, envs_required=False):
parser = iniconfigparser.ConfigParser()
with open(filepath) as config_file: with open(filepath) as config_file:
config_string = config_file.read()
if envs_required is not None:
config_string = _resolve_config_env_markers( config_string = _resolve_config_env_markers(
config_string, config_file.read(),
envs_required=envs_required, envs_required=envs_required,
) )
parser.read_string(config_string) parser.read_string(config_string)
return parser return parser
else:
import StringIO
def _parse_ini_file(filepath, envs_required=False):
parser = iniconfigparser.ConfigParser()
with open(filepath) as config_file:
config_string = _resolve_config_env_markers(
config_file.read(),
envs_required=envs_required,
)
parser.readfp(StringIO.StringIO(config_string))
return parser
if yaml: if yaml:
@ -128,7 +163,7 @@ cdef int ASYNC_MODE_ENABLED = 1
cdef int ASYNC_MODE_DISABLED = 2 cdef int ASYNC_MODE_DISABLED = 2
cdef set __iscoroutine_typecache = set() cdef set __iscoroutine_typecache = set()
cdef tuple __COROUTINE_TYPES = asyncio.coroutines._COROUTINE_TYPES cdef tuple __COROUTINE_TYPES = asyncio.coroutines._COROUTINE_TYPES if asyncio else tuple()
cdef dict pydantic_settings_to_dict(settings, dict kwargs): cdef dict pydantic_settings_to_dict(settings, dict kwargs):
if not has_pydantic_settings: if not has_pydantic_settings:
@ -138,7 +173,7 @@ cdef dict pydantic_settings_to_dict(settings, dict kwargs):
f"\"pip install dependency-injector[{pydantic_extra}]\"" f"\"pip install dependency-injector[{pydantic_extra}]\""
) )
if isinstance(settings, type) and issubclass(settings, PydanticSettings): if isinstance(settings, CLASS_TYPES) and issubclass(settings, PydanticSettings):
raise Error( raise Error(
"Got settings class, but expect instance: " "Got settings class, but expect instance: "
"instead \"{0}\" use \"{0}()\"".format(settings.__name__) "instead \"{0}\" use \"{0}()\"".format(settings.__name__)
@ -156,7 +191,7 @@ cdef dict pydantic_settings_to_dict(settings, dict kwargs):
return settings.model_dump(mode="python", **kwargs) return settings.model_dump(mode="python", **kwargs)
cdef class Provider: cdef class Provider(object):
"""Base provider class. """Base provider class.
:py:class:`Provider` is callable (implements ``__call__`` method). Every :py:class:`Provider` is callable (implements ``__call__`` method). Every
@ -878,9 +913,12 @@ cdef class Dependency(Provider):
def set_instance_of(self, instance_of): def set_instance_of(self, instance_of):
"""Set type.""" """Set type."""
if not isinstance(instance_of, type): if not isinstance(instance_of, CLASS_TYPES):
raise TypeError( raise TypeError(
f"\"instance_of\" is not a class (got {instance_of!r}))", "\"instance_of\" has incorrect type (expected {0}, got {1}))".format(
CLASS_TYPES,
instance_of,
),
) )
self._instance_of = instance_of self._instance_of = instance_of
return self return self
@ -1437,11 +1475,12 @@ cdef class Coroutine(Callable):
some_coroutine.add_kwargs(keyword_argument1=3, keyword_argument=4) some_coroutine.add_kwargs(keyword_argument1=3, keyword_argument=4)
""" """
_is_coroutine_marker = _is_coroutine_marker # Python >=3.12 _is_coroutine = _is_coroutine_marker
_is_coroutine = _is_coroutine # Python <3.16
def set_provides(self, provides): def set_provides(self, provides):
"""Set provider provides.""" """Set provider provides."""
if not asyncio:
raise Error("Package asyncio is not available")
provides = _resolve_string_import(provides) provides = _resolve_string_import(provides)
if provides and not asyncio.iscoroutinefunction(provides): if provides and not asyncio.iscoroutinefunction(provides):
raise Error(f"Provider {_class_qualname(self)} expected to get coroutine function, " raise Error(f"Provider {_class_qualname(self)} expected to get coroutine function, "
@ -1595,11 +1634,12 @@ cdef class ConfigurationOption(Provider):
segment() if is_provider(segment) else segment for segment in self._name segment() if is_provider(segment) else segment for segment in self._name
) )
def _get_root(self): @property
def root(self):
return self._root return self._root
def get_name(self): def get_name(self):
return f"{self._root.get_name()}.{self._get_self_name()}" return ".".join((self._root.get_name(), self._get_self_name()))
def get_name_segments(self): def get_name_segments(self):
return self._name return self._name
@ -1673,7 +1713,7 @@ cdef class ConfigurationOption(Provider):
try: try:
parser = _parse_ini_file( parser = _parse_ini_file(
filepath, filepath,
envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(), envs_required=envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
) )
except IOError as exception: except IOError as exception:
if required is not False \ if required is not False \
@ -1732,11 +1772,10 @@ cdef class ConfigurationOption(Provider):
raise raise
return return
if envs_required is not None: config_content = _resolve_config_env_markers(
config_content = _resolve_config_env_markers( config_content,
config_content, envs_required=envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(), )
)
config = yaml.load(config_content, loader) config = yaml.load(config_content, loader)
current_config = self.__call__() current_config = self.__call__()
@ -1771,11 +1810,10 @@ cdef class ConfigurationOption(Provider):
raise raise
return return
if envs_required is not None: config_content = _resolve_config_env_markers(
config_content = _resolve_config_env_markers( config_content,
config_content, envs_required=envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(), )
)
config = json.loads(config_content) config = json.loads(config_content)
current_config = self.__call__() current_config = self.__call__()
@ -2228,7 +2266,7 @@ cdef class Configuration(Object):
try: try:
parser = _parse_ini_file( parser = _parse_ini_file(
filepath, filepath,
envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(), envs_required=envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
) )
except IOError as exception: except IOError as exception:
if required is not False \ if required is not False \
@ -2287,11 +2325,10 @@ cdef class Configuration(Object):
raise raise
return return
if envs_required is not None: config_content = _resolve_config_env_markers(
config_content = _resolve_config_env_markers( config_content,
config_content, envs_required=envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(), )
)
config = yaml.load(config_content, loader) config = yaml.load(config_content, loader)
current_config = self.__call__() current_config = self.__call__()
@ -2326,11 +2363,10 @@ cdef class Configuration(Object):
raise raise
return return
if envs_required is not None: config_content = _resolve_config_env_markers(
config_content = _resolve_config_env_markers( config_content,
config_content, envs_required=envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(), )
)
config = json.loads(config_content) config = json.loads(config_content)
current_config = self.__call__() current_config = self.__call__()
@ -3226,10 +3262,15 @@ cdef class ContextLocalSingleton(BaseSingleton):
:param provides: Provided type. :param provides: Provided type.
:type provides: type :type provides: type
""" """
if not contextvars:
raise RuntimeError(
"Contextvars library not found. This provider "
"requires Python 3.7 or a backport of contextvars. "
"To install a backport run \"pip install contextvars\"."
)
super(ContextLocalSingleton, self).__init__(provides, *args, **kwargs) super(ContextLocalSingleton, self).__init__(provides, *args, **kwargs)
self._storage = ContextVar("_storage", default=self._none) self._storage = contextvars.ContextVar("_storage", default=self._none)
def reset(self): def reset(self):
"""Reset cached instance, if any. """Reset cached instance, if any.
@ -3601,17 +3642,6 @@ cdef class Dict(Provider):
return __provide_keyword_args(kwargs, self._kwargs, self._kwargs_len, self._async_mode) return __provide_keyword_args(kwargs, self._kwargs, self._kwargs_len, self._async_mode)
@cython.no_gc
cdef class NullAwaitable:
def __next__(self):
raise StopIteration from None
def __await__(self):
return self
cdef NullAwaitable NULL_AWAITABLE = NullAwaitable()
cdef class Resource(Provider): cdef class Resource(Provider):
"""Resource provider provides a component with initialization and shutdown.""" """Resource provider provides a component with initialization and shutdown."""
@ -3667,12 +3697,6 @@ cdef class Resource(Provider):
def set_provides(self, provides): def set_provides(self, provides):
"""Set provider provides.""" """Set provider provides."""
provides = _resolve_string_import(provides) provides = _resolve_string_import(provides)
if isasyncgenfunction(provides):
provides = asynccontextmanager(provides)
elif isgeneratorfunction(provides):
provides = contextmanager(provides)
self._provides = provides self._provides = provides
return self return self
@ -3773,21 +3797,28 @@ cdef class Resource(Provider):
"""Shutdown resource.""" """Shutdown resource."""
if not self._initialized: if not self._initialized:
if self._async_mode == ASYNC_MODE_ENABLED: if self._async_mode == ASYNC_MODE_ENABLED:
return NULL_AWAITABLE result = asyncio.Future()
result.set_result(None)
return result
return return
if self._shutdowner: if self._shutdowner:
future = self._shutdowner(None, None, None) try:
shutdown = self._shutdowner(self._resource)
if __is_future_or_coroutine(future): except StopIteration:
return ensure_future(self._shutdown_async(future)) pass
else:
if inspect.isawaitable(shutdown):
return self._create_shutdown_future(shutdown)
self._resource = None self._resource = None
self._initialized = False self._initialized = False
self._shutdowner = None self._shutdowner = None
if self._async_mode == ASYNC_MODE_ENABLED: if self._async_mode == ASYNC_MODE_ENABLED:
return NULL_AWAITABLE result = asyncio.Future()
result.set_result(None)
return result
@property @property
def related(self): def related(self):
@ -3797,75 +3828,169 @@ cdef class Resource(Provider):
yield from filter(is_provider, self.kwargs.values()) yield from filter(is_provider, self.kwargs.values())
yield from super().related yield from super().related
async def _shutdown_async(self, future) -> None:
try:
await future
finally:
self._resource = None
self._initialized = False
self._shutdowner = None
async def _handle_async_cm(self, obj) -> None:
try:
self._resource = resource = await obj.__aenter__()
self._shutdowner = obj.__aexit__
return resource
except:
self._initialized = False
raise
async def _provide_async(self, future) -> None:
try:
obj = await future
if hasattr(obj, '__aenter__') and hasattr(obj, '__aexit__'):
self._resource = await obj.__aenter__()
self._shutdowner = obj.__aexit__
elif hasattr(obj, '__enter__') and hasattr(obj, '__exit__'):
self._resource = obj.__enter__()
self._shutdowner = obj.__exit__
else:
self._resource = obj
self._shutdowner = None
return self._resource
except:
self._initialized = False
raise
cpdef object _provide(self, tuple args, dict kwargs): cpdef object _provide(self, tuple args, dict kwargs):
if self._initialized: if self._initialized:
return self._resource return self._resource
obj = __call( if self._is_resource_subclass(self._provides):
self._provides, initializer = self._provides()
args, self._resource = __call(
self._args, initializer.init,
self._args_len, args,
kwargs, self._args,
self._kwargs, self._args_len,
self._kwargs_len, kwargs,
self._async_mode, self._kwargs,
) self._kwargs_len,
self._async_mode,
if __is_future_or_coroutine(obj): )
self._shutdowner = initializer.shutdown
elif self._is_async_resource_subclass(self._provides):
initializer = self._provides()
async_init = __call(
initializer.init,
args,
self._args,
self._args_len,
kwargs,
self._kwargs,
self._kwargs_len,
self._async_mode,
)
self._initialized = True self._initialized = True
self._resource = resource = ensure_future(self._provide_async(obj)) return self._create_init_future(async_init, initializer.shutdown)
return resource elif inspect.isgeneratorfunction(self._provides):
elif hasattr(obj, '__enter__') and hasattr(obj, '__exit__'): initializer = __call(
self._resource = obj.__enter__() self._provides,
self._shutdowner = obj.__exit__ args,
elif hasattr(obj, '__aenter__') and hasattr(obj, '__aexit__'): self._args,
self._args_len,
kwargs,
self._kwargs,
self._kwargs_len,
self._async_mode,
)
self._resource = next(initializer)
self._shutdowner = initializer.send
elif iscoroutinefunction(self._provides):
initializer = __call(
self._provides,
args,
self._args,
self._args_len,
kwargs,
self._kwargs,
self._kwargs_len,
self._async_mode,
)
self._initialized = True self._initialized = True
self._resource = resource = ensure_future(self._handle_async_cm(obj)) return self._create_init_future(initializer)
return resource elif isasyncgenfunction(self._provides):
initializer = __call(
self._provides,
args,
self._args,
self._args_len,
kwargs,
self._kwargs,
self._kwargs_len,
self._async_mode,
)
self._initialized = True
return self._create_async_gen_init_future(initializer)
elif callable(self._provides):
self._resource = __call(
self._provides,
args,
self._args,
self._args_len,
kwargs,
self._kwargs,
self._kwargs_len,
self._async_mode,
)
else: else:
self._resource = obj raise Error("Unknown type of resource initializer")
self._shutdowner = None
self._initialized = True self._initialized = True
return self._resource return self._resource
def _create_init_future(self, future, shutdowner=None):
callback = self._async_init_callback
if shutdowner:
callback = functools.partial(callback, shutdowner=shutdowner)
future = asyncio.ensure_future(future)
future.add_done_callback(callback)
self._resource = future
return future
def _create_async_gen_init_future(self, initializer):
if inspect.isasyncgen(initializer):
return self._create_init_future(initializer.__anext__(), initializer.asend)
future = asyncio.Future()
create_initializer = asyncio.ensure_future(initializer)
create_initializer.add_done_callback(functools.partial(self._async_create_gen_callback, future))
self._resource = future
return future
def _async_init_callback(self, initializer, shutdowner=None):
try:
resource = initializer.result()
except Exception:
self._initialized = False
else:
self._resource = resource
self._shutdowner = shutdowner
def _async_create_gen_callback(self, future, initializer_future):
initializer = initializer_future.result()
init_future = self._create_init_future(initializer.__anext__(), initializer.asend)
init_future.add_done_callback(functools.partial(self._async_trigger_result, future))
def _async_trigger_result(self, future, future_result):
future.set_result(future_result.result())
def _create_shutdown_future(self, shutdown_future):
future = asyncio.Future()
shutdown_future = asyncio.ensure_future(shutdown_future)
shutdown_future.add_done_callback(functools.partial(self._async_shutdown_callback, future))
return future
def _async_shutdown_callback(self, future_result, shutdowner):
try:
shutdowner.result()
except StopAsyncIteration:
pass
self._resource = None
self._initialized = False
self._shutdowner = None
future_result.set_result(None)
@staticmethod
def _is_resource_subclass(instance):
if sys.version_info < (3, 5):
return False
if not isinstance(instance, CLASS_TYPES):
return
from . import resources
return issubclass(instance, resources.Resource)
@staticmethod
def _is_async_resource_subclass(instance):
if sys.version_info < (3, 5):
return False
if not isinstance(instance, CLASS_TYPES):
return
from . import resources
return issubclass(instance, resources.AsyncResource)
cdef class Container(Provider): cdef class Container(Provider):
"""Container provider provides an instance of declarative container. """Container provider provides an instance of declarative container.
@ -4519,7 +4644,7 @@ cdef class MethodCaller(Provider):
future_result.set_result(result) future_result.set_result(result)
cdef class Injection: cdef class Injection(object):
"""Abstract injection class.""" """Abstract injection class."""
@ -4646,7 +4771,7 @@ cpdef tuple parse_named_injections(dict kwargs):
return tuple(injections) return tuple(injections)
cdef class OverridingContext: cdef class OverridingContext(object):
"""Provider overriding context. """Provider overriding context.
:py:class:`OverridingContext` is used by :py:meth:`Provider.override` for :py:class:`OverridingContext` is used by :py:meth:`Provider.override` for
@ -4682,7 +4807,7 @@ cdef class OverridingContext:
self._overridden.reset_last_overriding() self._overridden.reset_last_overriding()
cdef class BaseSingletonResetContext: cdef class BaseSingletonResetContext(object):
def __init__(self, Provider provider): def __init__(self, Provider provider):
self._singleton = provider self._singleton = provider
@ -4718,7 +4843,7 @@ cpdef bint is_provider(object instance):
:rtype: bool :rtype: bool
""" """
return (not isinstance(instance, type) and return (not isinstance(instance, CLASS_TYPES) and
getattr(instance, "__IS_PROVIDER__", False) is True) getattr(instance, "__IS_PROVIDER__", False) is True)
@ -4746,7 +4871,7 @@ cpdef bint is_delegated(object instance):
:rtype: bool :rtype: bool
""" """
return (not isinstance(instance, type) and return (not isinstance(instance, CLASS_TYPES) and
getattr(instance, "__IS_DELEGATED__", False) is True) getattr(instance, "__IS_DELEGATED__", False) is True)
@ -4777,7 +4902,7 @@ cpdef bint is_container_instance(object instance):
:rtype: bool :rtype: bool
""" """
return (not isinstance(instance, type) and return (not isinstance(instance, CLASS_TYPES) and
getattr(instance, "__IS_CONTAINER__", False) is True) getattr(instance, "__IS_CONTAINER__", False) is True)
@ -4789,7 +4914,7 @@ cpdef bint is_container_class(object instance):
:rtype: bool :rtype: bool
""" """
return (isinstance(instance, type) and return (isinstance(instance, CLASS_TYPES) and
getattr(instance, "__IS_CONTAINER__", False) is True) getattr(instance, "__IS_CONTAINER__", False) is True)
@ -4916,6 +5041,14 @@ def iscoroutinefunction(obj):
return False return False
def isasyncgenfunction(obj):
"""Check if object is an asynchronous generator function."""
try:
return inspect.isasyncgenfunction(obj)
except AttributeError:
return False
def _resolve_string_import(provides): def _resolve_string_import(provides):
if provides is None: if provides is None:
return provides return provides

View File

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

View File

@ -27,9 +27,9 @@ class SchemaProcessorV1:
return self._container.providers return self._container.providers
def _create_providers( def _create_providers(
self, self,
provider_schema: ProviderSchema, provider_schema: ProviderSchema,
container: Optional[containers.Container] = None, container: Optional[containers.Container] = None,
) -> None: ) -> None:
if container is None: if container is None:
container = self._container container = self._container
@ -57,9 +57,9 @@ class SchemaProcessorV1:
self._create_providers(provider_schema=data, container=provider) self._create_providers(provider_schema=data, container=provider)
def _setup_injections( # noqa: C901 def _setup_injections( # noqa: C901
self, self,
provider_schema: ProviderSchema, provider_schema: ProviderSchema,
container: Optional[containers.Container] = None, container: Optional[containers.Container] = None,
) -> None: ) -> None:
if container is None: if container is None:
container = self._container container = self._container
@ -72,7 +72,7 @@ class SchemaProcessorV1:
provides = data.get("provides") provides = data.get("provides")
if provides: if provides:
if isinstance(provides, str) and provides.startswith("container."): if isinstance(provides, str) and provides.startswith("container."):
provides = self._resolve_provider(provides[len("container.") :]) provides = self._resolve_provider(provides[len("container."):])
else: else:
provides = _import_string(provides) provides = _import_string(provides)
provider.set_provides(provides) provider.set_provides(provides)
@ -83,7 +83,7 @@ class SchemaProcessorV1:
injection = None injection = None
if isinstance(arg, str) and arg.startswith("container."): if isinstance(arg, str) and arg.startswith("container."):
injection = self._resolve_provider(arg[len("container.") :]) injection = self._resolve_provider(arg[len("container."):])
# TODO: refactoring # TODO: refactoring
if isinstance(arg, dict): if isinstance(arg, dict):
@ -91,23 +91,16 @@ class SchemaProcessorV1:
provider_type = _get_provider_cls(arg.get("provider")) provider_type = _get_provider_cls(arg.get("provider"))
provides = arg.get("provides") provides = arg.get("provides")
if provides: if provides:
if isinstance(provides, str) and provides.startswith( if isinstance(provides, str) and provides.startswith("container."):
"container." provides = self._resolve_provider(provides[len("container."):])
):
provides = self._resolve_provider(
provides[len("container.") :]
)
else: else:
provides = _import_string(provides) provides = _import_string(provides)
provider_args.append(provides) provider_args.append(provides)
for provider_arg in arg.get("args", []): for provider_arg in arg.get("args", []):
if isinstance( if isinstance(provider_arg, str) \
provider_arg, str and provider_arg.startswith("container."):
) and provider_arg.startswith("container."):
provider_args.append( provider_args.append(
self._resolve_provider( self._resolve_provider(provider_arg[len("container."):]),
provider_arg[len("container.") :]
),
) )
injection = provider_type(*provider_args) injection = provider_type(*provider_args)
@ -124,7 +117,7 @@ class SchemaProcessorV1:
injection = None injection = None
if isinstance(arg, str) and arg.startswith("container."): if isinstance(arg, str) and arg.startswith("container."):
injection = self._resolve_provider(arg[len("container.") :]) injection = self._resolve_provider(arg[len("container."):])
# TODO: refactoring # TODO: refactoring
if isinstance(arg, dict): if isinstance(arg, dict):
@ -132,23 +125,16 @@ class SchemaProcessorV1:
provider_type = _get_provider_cls(arg.get("provider")) provider_type = _get_provider_cls(arg.get("provider"))
provides = arg.get("provides") provides = arg.get("provides")
if provides: if provides:
if isinstance(provides, str) and provides.startswith( if isinstance(provides, str) and provides.startswith("container."):
"container." provides = self._resolve_provider(provides[len("container."):])
):
provides = self._resolve_provider(
provides[len("container.") :]
)
else: else:
provides = _import_string(provides) provides = _import_string(provides)
provider_args.append(provides) provider_args.append(provides)
for provider_arg in arg.get("args", []): for provider_arg in arg.get("args", []):
if isinstance( if isinstance(provider_arg, str) \
provider_arg, str and provider_arg.startswith("container."):
) and provider_arg.startswith("container."):
provider_args.append( provider_args.append(
self._resolve_provider( self._resolve_provider(provider_arg[len("container."):]),
provider_arg[len("container.") :]
),
) )
injection = provider_type(*provider_args) injection = provider_type(*provider_args)
@ -172,7 +158,7 @@ class SchemaProcessorV1:
for segment in segments[1:]: for segment in segments[1:]:
parentheses = "" parentheses = ""
if "(" in segment and ")" in segment: if "(" in segment and ")" in segment:
parentheses = segment[segment.find("(") : segment.rfind(")") + 1] parentheses = segment[segment.find("("):segment.rfind(")")+1]
segment = segment.replace(parentheses, "") segment = segment.replace(parentheses, "")
try: try:
@ -204,12 +190,10 @@ def _get_provider_cls(provider_cls_name: str) -> Type[providers.Provider]:
if custom_provider_type: if custom_provider_type:
return custom_provider_type return custom_provider_type
raise SchemaError(f'Undefined provider class "{provider_cls_name}"') raise SchemaError(f"Undefined provider class \"{provider_cls_name}\"")
def _fetch_provider_cls_from_std( def _fetch_provider_cls_from_std(provider_cls_name: str) -> Optional[Type[providers.Provider]]:
provider_cls_name: str,
) -> Optional[Type[providers.Provider]]:
return getattr(providers, provider_cls_name, None) return getattr(providers, provider_cls_name, None)
@ -217,16 +201,12 @@ def _import_provider_cls(provider_cls_name: str) -> Optional[Type[providers.Prov
try: try:
cls = _import_string(provider_cls_name) cls = _import_string(provider_cls_name)
except (ImportError, ValueError) as exception: except (ImportError, ValueError) as exception:
raise SchemaError( raise SchemaError(f"Can not import provider \"{provider_cls_name}\"") from exception
f'Can not import provider "{provider_cls_name}"'
) from exception
except AttributeError: except AttributeError:
return None return None
else: else:
if isinstance(cls, type) and not issubclass(cls, providers.Provider): if isinstance(cls, type) and not issubclass(cls, providers.Provider):
raise SchemaError( raise SchemaError(f"Provider class \"{cls}\" is not a subclass of providers base class")
f'Provider class "{cls}" is not a subclass of providers base class'
)
return cls 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.*

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

@ -0,0 +1,13 @@
[pytest]
testpaths = tests/unit/
python_files = test_*_py3*.py
asyncio_mode = auto
markers =
pydantic: Tests with Pydantic as a dependency
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.*

View File

@ -1,43 +1,32 @@
from dependency_injector import providers from dependency_injector import providers
from typing_extensions import assert_type, Any
class Animal: ... class Animal:
...
class Cat(Animal): ... class Cat(Animal):
...
# Test 1: to check Aggregate provider # Test 1: to check Aggregate provider
provider1 = providers.Aggregate( provider1: providers.Aggregate[str] = providers.Aggregate(
a=providers.Object("str1"), a=providers.Object("str1"),
b=providers.Object("str2"), b=providers.Object("str2"),
) )
provider_a_1 = provider1.a provider_a_1: providers.Provider[str] = provider1.a
provider_b_1: providers.Provider[str] = provider1.b provider_b_1: providers.Provider[str] = provider1.b
val1 = provider1("a") val1: str = provider1("a")
assert_type(provider1, providers.Aggregate[str])
assert_type(provider_a_1, providers.Provider[str])
assert_type(provider_b_1, providers.Provider[str])
assert_type(val1, str)
provider1_set_non_string_keys = providers.Aggregate[str]() provider1_set_non_string_keys: providers.Aggregate[str] = providers.Aggregate()
provider1_set_non_string_keys.set_providers({Cat: providers.Object("str")}) provider1_set_non_string_keys.set_providers({Cat: providers.Object("str")})
provider_set_non_string_1 = provider1_set_non_string_keys.providers[Cat] provider_set_non_string_1: providers.Provider[str] = provider1_set_non_string_keys.providers[Cat]
assert_type(provider_set_non_string_1, providers.Provider[str])
provider1_new_non_string_keys: providers.Aggregate[str] = providers.Aggregate(
provider1_new_non_string_keys = providers.Aggregate(
{Cat: providers.Object("str")}, {Cat: providers.Object("str")},
) )
factory_new_non_string_1 = provider1_new_non_string_keys.providers[Cat] factory_new_non_string_1: providers.Provider[str] = provider1_new_non_string_keys.providers[Cat]
assert_type(provider1_new_non_string_keys, providers.Aggregate[str])
assert_type(factory_new_non_string_1, providers.Provider[str])
provider1_no_explicit_typing = providers.Aggregate(a=providers.Object("str")) provider1_no_explicit_typing = providers.Aggregate(a=providers.Object("str"))
provider1_no_explicit_typing_factory = provider1_no_explicit_typing.providers["a"] provider1_no_explicit_typing_factory: providers.Provider[str] = provider1_no_explicit_typing.providers["a"]
provider1_no_explicit_typing_object = provider1_no_explicit_typing("a") provider1_no_explicit_typing_object: str = provider1_no_explicit_typing("a")
assert_type(provider1_no_explicit_typing_factory, providers.Provider[str])
assert_type(provider1_no_explicit_typing_object, str)

View File

@ -1,13 +1,14 @@
from typing import Any, Callable, Dict, Optional, Tuple from typing import Callable, Optional, Tuple, Any, Dict, Type
from typing_extensions import assert_type
from dependency_injector import providers from dependency_injector import providers
class Animal: ... class Animal:
...
class Cat(Animal): class Cat(Animal):
@classmethod @classmethod
def create(cls) -> Animal: def create(cls) -> Animal:
return cls() return cls()
@ -15,13 +16,11 @@ class Cat(Animal):
# Test 1: to check the return type (class) # Test 1: to check the return type (class)
provider1 = providers.Callable(Cat) provider1 = providers.Callable(Cat)
cat1 = provider1(1, 2, 3, b="1", c=2, e=0.0) animal1: Animal = provider1(1, 2, 3, b="1", c=2, e=0.0)
assert_type(cat1, Cat)
# Test 2: to check the return type (class factory method) # Test 2: to check the return type (class factory method)
provider2 = providers.Callable(Cat.create) provider2 = providers.Callable(Cat.create)
animal2 = provider2() animal2: Animal = provider2()
assert_type(animal2, Animal)
# Test 3: to check the .override() method # Test 3: to check the .override() method
provider3 = providers.Callable(Animal) provider3 = providers.Callable(Animal)
@ -30,57 +29,44 @@ with provider3.override(providers.Callable(Cat)):
# Test 4: to check the .args & .kwargs attributes # Test 4: to check the .args & .kwargs attributes
provider4 = providers.Callable(Animal) provider4 = providers.Callable(Animal)
args4 = provider4.args args4: Tuple[Any] = provider4.args
kwargs4 = provider4.kwargs kwargs4: Dict[str, Any] = provider4.kwargs
assert_type(args4, Tuple[Any])
assert_type(kwargs4, Dict[str, Any])
# Test 5: to check the provided instance interface # Test 5: to check the provided instance interface
provider5 = providers.Callable(Animal) provider5 = providers.Callable(Animal)
provided_val5 = provider5.provided() provided5: providers.ProvidedInstance = provider5.provided
attr_getter5 = provider5.provided.attr attr_getter5: providers.AttributeGetter = provider5.provided.attr
item_getter5 = provider5.provided["item"] item_getter5: providers.ItemGetter = provider5.provided["item"]
method_caller5 = provider5.provided.method.call(123, arg=324) method_caller: providers.MethodCaller = provider5.provided.method.call(123, arg=324)
assert_type(provided_val5, Any)
assert_type(attr_getter5, providers.AttributeGetter)
assert_type(item_getter5, providers.ItemGetter)
assert_type(method_caller5, providers.MethodCaller)
# Test 6: to check the DelegatedCallable # Test 6: to check the DelegatedCallable
provider6 = providers.DelegatedCallable(Cat) provider6 = providers.DelegatedCallable(Cat)
cat6 = provider6(1, 2, 3, b="1", c=2, e=0.0) animal6: Animal = provider6(1, 2, 3, b="1", c=2, e=0.0)
assert_type(cat6, Cat)
# Test 7: to check the AbstractCallable # Test 7: to check the AbstractCallable
provider7 = providers.AbstractCallable(Animal) provider7 = providers.AbstractCallable(Animal)
provider7.override(providers.Callable(Cat)) provider7.override(providers.Callable(Cat))
animal7 = provider7(1, 2, 3, b="1", c=2, e=0.0) animal7: Animal = provider7(1, 2, 3, b="1", c=2, e=0.0)
assert_type(animal7, Animal)
# Test 8: to check the CallableDelegate __init__ # Test 8: to check the CallableDelegate __init__
provider8 = providers.CallableDelegate(providers.Callable(lambda: None)) provider8 = providers.CallableDelegate(providers.Callable(lambda: None))
# Test 9: to check the return type with await # Test 9: to check the return type with await
provider9 = providers.Callable(Cat) provider9 = providers.Callable(Cat)
async def _async9() -> None: async def _async9() -> None:
await provider9(1, 2, 3, b="1", c=2, e=0.0) # type: ignore[misc] animal1: Animal = await provider9(1, 2, 3, b="1", c=2, e=0.0) # type: ignore
cat9 = await provider9.async_(1, 2, 3, b="1", c=2, e=0.0) animal2: Animal = await provider9.async_(1, 2, 3, b="1", c=2, e=0.0)
assert_type(cat9, Cat)
# Test 10: to check the .provides # Test 10: to check the .provides
provider10 = providers.Callable(Cat) provider10 = providers.Callable(Cat)
provides10 = provider10.provides provides10: Optional[Callable[..., Cat]] = provider10.provides
assert_type(provides10, Optional[Callable[..., Cat]]) assert provides10 is Cat
# Test 11: to check the .provides for explicit typevar # Test 11: to check the .provides for explicit typevar
provider11 = providers.Callable[Animal](Cat) provider11 = providers.Callable[Animal](Cat)
provides11 = provider11.provides provides11: Optional[Callable[..., Animal]] = provider11.provides
assert_type(provides11, Optional[Callable[..., Animal]]) assert provides11 is Cat
# Test 12: to check string imports # Test 12: to check string imports
provider12 = providers.Callable("builtins.dict") provider12: providers.Callable[dict] = providers.Callable("builtins.dict")
provider12.set_provides("builtins.dict") provider12.set_provides("builtins.dict")

View File

@ -1,13 +1,13 @@
from pathlib import Path from pathlib import Path
from typing import Any, Dict from typing import Any
from pydantic_settings import BaseSettings as PydanticSettings
from dependency_injector import providers from dependency_injector import providers
from pydantic_settings import BaseSettings as PydanticSettings
# Test 1: to check the getattr # Test 1: to check the getattr
config1: providers.Configuration = providers.Configuration() config1: providers.Configuration = providers.Configuration()
provider1: providers.Provider[Dict[str, Any]] = providers.Factory(dict, a=config1.a) provider1: providers.Provider[dict] = providers.Factory(dict, a=config1.a)
# Test 2: to check the from_*() method # Test 2: to check the from_*() method
config2 = providers.Configuration() config2 = providers.Configuration()
@ -68,9 +68,7 @@ config5_pydantic = providers.Configuration(
pydantic_settings=[PydanticSettings()], pydantic_settings=[PydanticSettings()],
) )
config5_pydantic.set_pydantic_settings([PydanticSettings()]) config5_pydantic.set_pydantic_settings([PydanticSettings()])
config5_pydantic_settings: list[PydanticSettings] = ( config5_pydantic_settings: list[PydanticSettings] = config5_pydantic.get_pydantic_settings()
config5_pydantic.get_pydantic_settings()
)
# Test 6: to check init arguments # Test 6: to check init arguments
config6 = providers.Configuration( config6 = providers.Configuration(

View File

@ -1,9 +1,8 @@
from typing import Any
from dependency_injector import providers from dependency_injector import providers
class Container: ... class Container:
...
# Test 1: to check the return type # Test 1: to check the return type
@ -12,4 +11,4 @@ var1: Container = provider1()
# Test 2: to check the getattr # Test 2: to check the getattr
provider2 = providers.Container(Container) provider2 = providers.Container(Container)
attr: providers.Provider[Any] = provider2.attr attr: providers.Provider = provider2.attr

View File

@ -1,14 +1,14 @@
from typing import Awaitable, Coroutine from typing import Coroutine
from dependency_injector import providers from dependency_injector import providers
async def _coro() -> None: ... async def _coro() -> None:
...
# Test 1: to check the return type # Test 1: to check the return type
provider1 = providers.Coroutine(_coro) provider1 = providers.Coroutine(_coro)
var1: Awaitable[None] = provider1() var1: Coroutine = provider1()
# Test 2: to check string imports # Test 2: to check string imports
provider2: providers.Coroutine[None] = providers.Coroutine("_coro") provider2: providers.Coroutine[None] = providers.Coroutine("_coro")

View File

@ -1,4 +1,4 @@
from typing import Any, Dict from typing import Dict
from dependency_injector import containers, providers from dependency_injector import containers, providers
@ -10,7 +10,7 @@ class Container1(containers.DeclarativeContainer):
container1 = Container1() container1 = Container1()
container1_type: containers.Container = Container1() container1_type: containers.Container = Container1()
provider1: providers.Provider[int] = container1.provider provider1: providers.Provider = container1.provider
val1: int = container1.provider(3) val1: int = container1.provider(3)
@ -20,7 +20,8 @@ class Container21(containers.DeclarativeContainer):
@containers.override(Container21) @containers.override(Container21)
class Container22(containers.DeclarativeContainer): ... class Container22(containers.DeclarativeContainer):
...
# Test 3: to check @copy decorator # Test 3: to check @copy decorator
@ -29,14 +30,14 @@ class Container31(containers.DeclarativeContainer):
@containers.copy(Container31) @containers.copy(Container31)
class Container32(containers.DeclarativeContainer): ... class Container32(containers.DeclarativeContainer):
...
# Test 4: to override() # Test 4: to override()
class Container4(containers.DeclarativeContainer): class Container4(containers.DeclarativeContainer):
provider = providers.Factory(int) provider = providers.Factory(int)
container4 = Container4() container4 = Container4()
container4.override(Container4()) container4.override(Container4())
@ -46,7 +47,7 @@ class Container5(containers.DeclarativeContainer):
provider = providers.Factory(int) provider = providers.Factory(int)
dependencies: Dict[str, providers.Provider[Any]] = Container5.dependencies dependencies: Dict[str, providers.Provider] = Container5.dependencies
# Test 6: to check base class # Test 6: to check base class
@ -61,7 +62,6 @@ container6: containers.Container = Container6()
class Container7(containers.DeclarativeContainer): class Container7(containers.DeclarativeContainer):
provider = providers.Factory(str) provider = providers.Factory(str)
container7 = Container7() container7 = Container7()
container7.override_providers(provider="new_value") container7.override_providers(provider="new_value")
with container7.override_providers(a=providers.Provider()): with container7.override_providers(a=providers.Provider()):

View File

@ -1,20 +1,17 @@
from typing import Any, Optional from typing import Optional
from dependency_injector import providers from dependency_injector import providers
# Test 1: to check the return type # Test 1: to check the return type
provider1 = providers.Delegate(providers.Provider()) provider1 = providers.Delegate(providers.Provider())
var1: providers.Provider[Any] = provider1() var1: providers.Provider = provider1()
# Test 2: to check the return type with await # Test 2: to check the return type with await
provider2 = providers.Delegate(providers.Provider()) provider2 = providers.Delegate(providers.Provider())
async def _async2() -> None: async def _async2() -> None:
var1: providers.Provider[Any] = await provider2() # type: ignore var1: providers.Provider = await provider2() # type: ignore
var2: providers.Provider[Any] = await provider2.async_() var2: providers.Provider = await provider2.async_()
# Test 3: to check class type from provider # Test 3: to check class type from provider
provider3 = providers.Delegate(providers.Provider()) provider3 = providers.Delegate(providers.Provider())
provided_provides: Optional[providers.Provider[Any]] = provider3.provides provided_provides: Optional[providers.Provider] = provider3.provides

View File

@ -1,12 +1,11 @@
from typing import Any
from dependency_injector import providers from dependency_injector import providers
# Test 1: to check the getattr type # Test 1: to check the getattr type
provider1 = providers.DependenciesContainer( provider1 = providers.DependenciesContainer(
a=providers.Provider(), a=providers.Provider(),
b=providers.Provider(), b=providers.Provider(),
) )
a1: providers.Provider[Any] = provider1.a a1: providers.Provider = provider1.a
b1: providers.Provider[Any] = provider1.b b1: providers.Provider = provider1.b
c1: providers.ProvidedInstance = provider1.c.provided c1: providers.ProvidedInstance = provider1.c.provided

View File

@ -1,14 +1,15 @@
from typing import Any, Type from typing import Type
from dependency_injector import providers from dependency_injector import providers
class Animal: ... class Animal:
...
class Cat(Animal): class Cat(Animal):
def __init__(self, *a: Any, **kw: Any) -> None: ... def __init__(self, *_, **__): ...
# Test 1: to check the return type # Test 1: to check the return type
@ -22,8 +23,6 @@ var2: Type[Animal] = provider2.instance_of
# Test 3: to check the return type with await # Test 3: to check the return type with await
provider3 = providers.Dependency(instance_of=Animal) provider3 = providers.Dependency(instance_of=Animal)
async def _async3() -> None: async def _async3() -> None:
var1: Animal = await provider3() # type: ignore var1: Animal = await provider3() # type: ignore
var2: Animal = await provider3.async_() var2: Animal = await provider3.async_()

View File

@ -2,6 +2,7 @@ from typing import Any, Dict
from dependency_injector import providers from dependency_injector import providers
# Test 1: to check the return type (class) # Test 1: to check the return type (class)
provider1 = providers.Dict( provider1 = providers.Dict(
a1=providers.Factory(object), a1=providers.Factory(object),
@ -16,9 +17,7 @@ var2: Dict[Any, Any] = provider2()
# Test 3: to check init with non-string keys # Test 3: to check init with non-string keys
provider3 = providers.Dict( provider3 = providers.Dict({object(): providers.Factory(object)}, a2=providers.Factory(object))
{object(): providers.Factory(object)}, a2=providers.Factory(object)
)
var3: Dict[Any, Any] = provider3() var3: Dict[Any, Any] = provider3()
@ -35,7 +34,7 @@ provider5 = providers.Dict(
a1=providers.Factory(object), a1=providers.Factory(object),
a2=providers.Factory(object), a2=providers.Factory(object),
) )
provided5: dict[Any, Any] = provider5.provided() provided5: providers.ProvidedInstance = provider5.provided
# Test 6: to check the return type with await # Test 6: to check the return type with await
@ -43,8 +42,6 @@ provider6 = providers.Dict(
a1=providers.Factory(object), a1=providers.Factory(object),
a2=providers.Factory(object), a2=providers.Factory(object),
) )
async def _async3() -> None: async def _async3() -> None:
var1: Dict[Any, Any] = await provider6() # type: ignore var1: Dict[Any, Any] = await provider6() # type: ignore
var2: Dict[Any, Any] = await provider6.async_() var2: Dict[Any, Any] = await provider6.async_()

View File

@ -1,7 +1,8 @@
from typing import Any, Dict from typing import Dict
from dependency_injector import containers, providers from dependency_injector import containers, providers
# Test 1: to check setattr # Test 1: to check setattr
container1 = containers.DynamicContainer() container1 = containers.DynamicContainer()
container1.abc = providers.Provider() container1.abc = providers.Provider()
@ -22,7 +23,7 @@ container4.set_providers(a=providers.Provider())
# Test 5: to check .dependencies attribute # Test 5: to check .dependencies attribute
container5 = containers.DynamicContainer() container5 = containers.DynamicContainer()
dependencies: Dict[str, providers.Provider[Any]] = container5.dependencies dependencies: Dict[str, providers.Provider] = container5.dependencies
# Test 6: to check base class # Test 6: to check base class
container6: containers.Container = containers.DynamicContainer() container6: containers.Container = containers.DynamicContainer()

View File

@ -1,14 +1,15 @@
from typing import Any, Callable, Dict, Optional, Tuple, Type from typing import Callable, Optional, Tuple, Any, Dict, Type
from dependency_injector import providers from dependency_injector import providers
class Animal: ... class Animal:
...
class Cat(Animal): class Cat(Animal):
def __init__(self, *a: Any, **kw: Any) -> None: ... def __init__(self, *_, **__): ...
@classmethod @classmethod
def create(cls) -> Animal: def create(cls) -> Animal:
@ -36,7 +37,7 @@ attributes4: Dict[str, Any] = provider4.attributes
# Test 5: to check the provided instance interface # Test 5: to check the provided instance interface
provider5 = providers.Factory(Animal) provider5 = providers.Factory(Animal)
provided5: Animal = provider5.provided() provided5: providers.ProvidedInstance = provider5.provided
attr_getter5: providers.AttributeGetter = provider5.provided.attr attr_getter5: providers.AttributeGetter = provider5.provided.attr
item_getter5: providers.ItemGetter = provider5.provided["item"] item_getter5: providers.ItemGetter = provider5.provided["item"]
method_caller5: providers.MethodCaller = provider5.provided.method.call(123, arg=324) method_caller5: providers.MethodCaller = provider5.provided.method.call(123, arg=324)
@ -62,29 +63,17 @@ factory_a_9: providers.Factory[str] = provider9.a
factory_b_9: providers.Factory[str] = provider9.b factory_b_9: providers.Factory[str] = provider9.b
val9: str = provider9("a") val9: str = provider9("a")
provider9_set_non_string_keys: providers.FactoryAggregate[str] = ( provider9_set_non_string_keys: providers.FactoryAggregate[str] = providers.FactoryAggregate()
providers.FactoryAggregate()
)
provider9_set_non_string_keys.set_factories({Cat: providers.Factory(str, "str")}) provider9_set_non_string_keys.set_factories({Cat: providers.Factory(str, "str")})
factory_set_non_string_9: providers.Factory[str] = ( factory_set_non_string_9: providers.Factory[str] = provider9_set_non_string_keys.factories[Cat]
provider9_set_non_string_keys.factories[Cat]
)
provider9_new_non_string_keys: providers.FactoryAggregate[str] = ( provider9_new_non_string_keys: providers.FactoryAggregate[str] = providers.FactoryAggregate(
providers.FactoryAggregate( {Cat: providers.Factory(str, "str")},
{Cat: providers.Factory(str, "str")},
)
)
factory_new_non_string_9: providers.Factory[str] = (
provider9_new_non_string_keys.factories[Cat]
) )
factory_new_non_string_9: providers.Factory[str] = provider9_new_non_string_keys.factories[Cat]
provider9_no_explicit_typing = providers.FactoryAggregate( provider9_no_explicit_typing = providers.FactoryAggregate(a=providers.Factory(str, "str"))
a=providers.Factory(str, "str") provider9_no_explicit_typing_factory: providers.Factory[str] = provider9_no_explicit_typing.factories["a"]
)
provider9_no_explicit_typing_factory: providers.Factory[str] = (
provider9_no_explicit_typing.factories["a"]
)
provider9_no_explicit_typing_object: str = provider9_no_explicit_typing("a") provider9_no_explicit_typing_object: str = provider9_no_explicit_typing("a")
# Test 10: to check the explicit typing # Test 10: to check the explicit typing
@ -93,13 +82,10 @@ animal10: Animal = factory10()
# Test 11: to check the return type with await # Test 11: to check the return type with await
provider11 = providers.Factory(Cat) provider11 = providers.Factory(Cat)
async def _async11() -> None: async def _async11() -> None:
animal1: Animal = await provider11(1, 2, 3, b="1", c=2, e=0.0) # type: ignore animal1: Animal = await provider11(1, 2, 3, b="1", c=2, e=0.0) # type: ignore
animal2: Animal = await provider11.async_(1, 2, 3, b="1", c=2, e=0.0) animal2: Animal = await provider11.async_(1, 2, 3, b="1", c=2, e=0.0)
# Test 12: to check class type from .provides # Test 12: to check class type from .provides
provider12 = providers.Factory(Cat) provider12 = providers.Factory(Cat)
provided_cls12: Type[Animal] = provider12.cls provided_cls12: Type[Animal] = provider12.cls
@ -115,5 +101,5 @@ provided_provides13: Optional[Callable[..., Animal]] = provider13.provides
assert provided_provides13 is not None and provided_provides13() == Cat() assert provided_provides13 is not None and provided_provides13() == Cat()
# Test 14: to check string imports # Test 14: to check string imports
provider14: providers.Factory[Dict[Any, Any]] = providers.Factory("builtins.dict") provider14: providers.Factory[dict] = providers.Factory("builtins.dict")
provider14.set_provides("builtins.dict") provider14.set_provides("builtins.dict")

View File

@ -1,7 +1,8 @@
from typing import Any, List, Tuple from typing import Tuple, Any, List
from dependency_injector import providers from dependency_injector import providers
# Test 1: to check the return type (class) # Test 1: to check the return type (class)
provider1 = providers.List( provider1 = providers.List(
providers.Factory(object), providers.Factory(object),
@ -22,7 +23,7 @@ provider3 = providers.List(
providers.Factory(object), providers.Factory(object),
providers.Factory(object), providers.Factory(object),
) )
provided3: List[Any] = provider3.provided() provided3: providers.ProvidedInstance = provider3.provided
attr_getter3: providers.AttributeGetter = provider3.provided.attr attr_getter3: providers.AttributeGetter = provider3.provided.attr
item_getter3: providers.ItemGetter = provider3.provided["item"] item_getter3: providers.ItemGetter = provider3.provided["item"]
method_caller3: providers.MethodCaller = provider3.provided.method.call(123, arg=324) method_caller3: providers.MethodCaller = provider3.provided.method.call(123, arg=324)
@ -32,8 +33,6 @@ provider4 = providers.List(
providers.Factory(object), providers.Factory(object),
providers.Factory(object), providers.Factory(object),
) )
async def _async4() -> None: async def _async4() -> None:
var1: List[Any] = await provider4() # type: ignore var1: List[Any] = await provider4() # type: ignore
var2: List[Any] = await provider4.async_() var2: List[Any] = await provider4.async_()

View File

@ -1,27 +1,25 @@
from typing import Optional, Type from typing import Type, Optional
from dependency_injector import providers from dependency_injector import providers
# Test 1: to check the return type # Test 1: to check the return type
provider1 = providers.Object(int(3)) provider1 = providers.Object(int(3))
var1: int = provider1() var1: int = provider1()
# Test 2: to check the provided instance interface # Test 2: to check the provided instance interface
provider2 = providers.Object(int) provider2 = providers.Object(int)
provided2: Type[int] = provider2.provided() provided2: providers.ProvidedInstance = provider2.provided
attr_getter2: providers.AttributeGetter = provider2.provided.attr attr_getter2: providers.AttributeGetter = provider2.provided.attr
item_getter2: providers.ItemGetter = provider2.provided["item"] item_getter2: providers.ItemGetter = provider2.provided["item"]
method_caller2: providers.MethodCaller = provider2.provided.method.call(123, arg=324) method_caller2: providers.MethodCaller = provider2.provided.method.call(123, arg=324)
# Test 3: to check the return type with await # Test 3: to check the return type with await
provider3 = providers.Object(int(3)) provider3 = providers.Object(int(3))
async def _async3() -> None: async def _async3() -> None:
var1: int = await provider3() # type: ignore var1: int = await provider3() # type: ignore
var2: int = await provider3.async_() var2: int = await provider3.async_()
# Test 4: to check class type from provider # Test 4: to check class type from provider
provider4 = providers.Object(int("1")) provider4 = providers.Object(int("1"))
provided_provides: Optional[int] = provider4.provides provided_provides: Optional[int] = provider4.provides

View File

@ -1,14 +1,12 @@
from typing import Any
from dependency_injector import providers from dependency_injector import providers
# Test 1: to check .provided attribute # Test 1: to check .provided attribute
provider1: providers.Provider[int] = providers.Object(1) provider1: providers.Provider[int] = providers.Object(1)
provided: int = provider1.provided() provided: providers.ProvidedInstance = provider1.provided
provider1_delegate: providers.Provider[int] = provider1.provider
# Test 2: to check async mode API # Test 2: to check async mode API
provider2: providers.Provider[Any] = providers.Provider() provider2: providers.Provider = providers.Provider()
provider2.enable_async_mode() provider2.enable_async_mode()
provider2.disable_async_mode() provider2.disable_async_mode()
provider2.reset_async_mode() provider2.reset_async_mode()

View File

@ -1,15 +1,4 @@
from contextlib import contextmanager, asynccontextmanager from typing import List, Iterator, Generator, AsyncIterator, AsyncGenerator, Optional
from typing import (
Any,
AsyncGenerator,
AsyncIterator,
Dict,
Generator,
Iterator,
List,
Optional,
Self,
)
from dependency_injector import providers, resources from dependency_injector import providers, resources
@ -43,10 +32,11 @@ var3: List[int] = provider3()
# Test 4: to check the return type with resource subclass # Test 4: to check the return type with resource subclass
class MyResource4(resources.Resource[List[int]]): class MyResource4(resources.Resource[List[int]]):
def init(self, *args: Any, **kwargs: Any) -> List[int]: def init(self, *args, **kwargs) -> List[int]:
return [] return []
def shutdown(self, resource: Optional[List[int]]) -> None: ... def shutdown(self, resource: Optional[List[int]]) -> None:
...
provider4 = providers.Resource(MyResource4) provider4 = providers.Resource(MyResource4)
@ -94,10 +84,11 @@ async def _provide7() -> None:
# Test 8: to check the return type with async resource subclass # Test 8: to check the return type with async resource subclass
class MyResource8(resources.AsyncResource[List[int]]): class MyResource8(resources.AsyncResource[List[int]]):
async def init(self, *args: Any, **kwargs: Any) -> List[int]: async def init(self, *args, **kwargs) -> List[int]:
return [] return []
async def shutdown(self, resource: Optional[List[int]]) -> None: ... async def shutdown(self, resource: Optional[List[int]]) -> None:
...
provider8 = providers.Resource(MyResource8) provider8 = providers.Resource(MyResource8)
@ -109,64 +100,5 @@ async def _provide8() -> None:
# Test 9: to check string imports # Test 9: to check string imports
provider9: providers.Resource[Dict[Any, Any]] = providers.Resource("builtins.dict") provider9: providers.Resource[dict] = providers.Resource("builtins.dict")
provider9.set_provides("builtins.dict") provider9.set_provides("builtins.dict")
# Test 10: to check the return type with classes implementing AbstractContextManager protocol
class MyResource10:
def __init__(self) -> None:
pass
def __enter__(self) -> Self:
return self
def __exit__(self, *args: Any, **kwargs: Any) -> None:
return None
provider10 = providers.Resource(MyResource10)
var10: MyResource10 = provider10()
# Test 11: to check the return type with functions decorated with contextlib.contextmanager
@contextmanager
def init11() -> Iterator[int]:
yield 1
provider11 = providers.Resource(init11)
var11: int = provider11()
# Test 12: to check the return type with classes implementing AbstractAsyncContextManager protocol
class MyResource12:
def __init__(self) -> None:
pass
async def __aenter__(self) -> Self:
return self
async def __aexit__(self, *args: Any, **kwargs: Any) -> None:
return None
provider12 = providers.Resource(MyResource12)
async def _provide12() -> None:
var1: MyResource12 = await provider12() # type: ignore
var2: MyResource12 = await provider12.async_()
# Test 13: to check the return type with functions decorated with contextlib.asynccontextmanager
@asynccontextmanager
async def init13() -> AsyncIterator[int]:
yield 1
provider13 = providers.Resource(init13)
async def _provide13() -> None:
var1: int = await provider13() # type: ignore
var2: int = await provider13.async_()

View File

@ -1,7 +1,8 @@
from typing import Any, Callable, Optional, Dict from typing import Any
from dependency_injector import providers from dependency_injector import providers
# Test 1: to check the return type # Test 1: to check the return type
provider1 = providers.Selector( provider1 = providers.Selector(
lambda: "a", lambda: "a",
@ -27,7 +28,7 @@ provider3 = providers.Selector(
a=providers.Factory(object), a=providers.Factory(object),
b=providers.Factory(object), b=providers.Factory(object),
) )
attr3: providers.Provider[Any] = provider3.a attr3: providers.Provider = provider3.a
# Test 4: to check the return type with await # Test 4: to check the return type with await
provider4 = providers.Selector( provider4 = providers.Selector(
@ -35,41 +36,6 @@ provider4 = providers.Selector(
a=providers.Factory(object), a=providers.Factory(object),
b=providers.Factory(object), b=providers.Factory(object),
) )
async def _async4() -> None: async def _async4() -> None:
var1: Any = await provider4() var1: Any = await provider4() # type: ignore
var2: Any = await provider4.async_() var2: Any = await provider4.async_()
# Test 5: to check selector getter and setter
provider5 = providers.Selector(
lambda: "a",
a=providers.Factory(object),
b=providers.Factory(object),
)
selector5: Optional[Callable[..., Any]] = provider5.selector
provider5_after_set_selector: providers.Selector[Any] = provider5.set_selector(lambda: "a")
# Test 6: to check providers getter and setter
provider6 = providers.Selector(
lambda: "a",
a=providers.Factory(object),
b=providers.Factory(object),
)
providers6: Dict[str, providers.Provider[Any]] = provider6.providers
provider6_after_set_providers: providers.Selector[Any] = provider6.set_providers(c=providers.Factory(object))
# Test 7: to check explicit typing: return type, getattr, getter/setter of providers and selectors
provider7 = providers.Selector[bool](lambda: "a", a=providers.Factory(bool), b=providers.Factory(int))
var7: bool = provider7()
attr7: providers.Provider[bool] = provider7.a
selector7: Optional[Callable[..., Any]] = provider7.selector
provider7_after_set_selector: providers.Selector[bool] = provider7.set_selector(lambda: "a")
providers7: Dict[str, providers.Provider[bool]] = provider7.providers
provider7_after_set_providers: providers.Selector[bool] = provider7.set_providers(
c=providers.Factory(str)
) # We don't require Provider of subclass of bool yet since Provider is invariant

View File

@ -1,14 +1,15 @@
from typing import Any, Callable, Dict, Optional, Tuple, Type from typing import Callable, Optional, Tuple, Any, Dict, Type
from dependency_injector import providers from dependency_injector import providers
class Animal: ... class Animal:
...
class Cat(Animal): class Cat(Animal):
def __init__(self, *a: Any, **kw: Any) -> None: ... def __init__(self, *_, **__): ...
@classmethod @classmethod
def create(cls) -> Animal: def create(cls) -> Animal:
@ -36,7 +37,7 @@ attributes4: Dict[str, Any] = provider4.attributes
# Test 5: to check the provided instance interface # Test 5: to check the provided instance interface
provider5 = providers.Singleton(Animal) provider5 = providers.Singleton(Animal)
provided5: Animal = provider5.provided() provided5: providers.ProvidedInstance = provider5.provided
attr_getter5: providers.AttributeGetter = provider5.provided.attr attr_getter5: providers.AttributeGetter = provider5.provided.attr
item_getter5: providers.ItemGetter = provider5.provided["item"] item_getter5: providers.ItemGetter = provider5.provided["item"]
method_caller5: providers.MethodCaller = provider5.provided.method.call(123, arg=324) method_caller5: providers.MethodCaller = provider5.provided.method.call(123, arg=324)
@ -71,13 +72,10 @@ provider12 = providers.SingletonDelegate(providers.Singleton(object))
# Test 13: to check the return type with await # Test 13: to check the return type with await
provider13 = providers.Singleton(Cat) provider13 = providers.Singleton(Cat)
async def _async13() -> None: async def _async13() -> None:
animal1: Animal = await provider13(1, 2, 3, b="1", c=2, e=0.0) # type: ignore animal1: Animal = await provider13(1, 2, 3, b="1", c=2, e=0.0) # type: ignore
animal2: Animal = await provider13.async_(1, 2, 3, b="1", c=2, e=0.0) animal2: Animal = await provider13.async_(1, 2, 3, b="1", c=2, e=0.0)
# Test 14: to check class from .provides # Test 14: to check class from .provides
provider14 = providers.Singleton(Cat) provider14 = providers.Singleton(Cat)
provided_cls14: Type[Cat] = provider14.cls provided_cls14: Type[Cat] = provider14.cls
@ -93,5 +91,5 @@ provided_provides15: Optional[Callable[..., Animal]] = provider15.provides
assert provided_provides15 is not None and provided_provides15() == Cat() assert provided_provides15 is not None and provided_provides15() == Cat()
# Test 16: to check string imports # Test 16: to check string imports
provider16: providers.Singleton[Dict[Any, Any]] = providers.Singleton("builtins.dict") provider16: providers.Singleton[dict] = providers.Singleton("builtins.dict")
provider16.set_provides("builtins.dict") provider16.set_provides("builtins.dict")

View File

@ -1,36 +0,0 @@
from typing import Iterator
from typing_extensions import Annotated
from dependency_injector.containers import DeclarativeContainer
from dependency_injector.providers import Object, Resource
from dependency_injector.wiring import Closing, Provide, required
def _resource() -> Iterator[int]:
yield 1
class Container(DeclarativeContainer):
value = Object(1)
res = Resource(_resource)
def default_by_ref(value: int = Provide[Container.value]) -> None: ...
def default_by_string(value: int = Provide["value"]) -> None: ...
def default_by_string_with_modifier(
value: int = Provide["value", required().as_int()]
) -> None: ...
def default_container(container: Container = Provide[Container]) -> None: ...
def default_with_closing(value: int = Closing[Provide[Container.res]]) -> None: ...
def annotated_by_ref(value: Annotated[int, Provide[Container.value]]) -> None: ...
def annotated_by_string(value: Annotated[int, Provide["value"]]) -> None: ...
def annotated_by_string_with_modifier(
value: Annotated[int, Provide["value", required().as_int()]],
) -> None: ...
def annotated_container(
container: Annotated[Container, Provide[Container]],
) -> None: ...
def annotated_with_closing(
value: Annotated[int, Closing[Provide[Container.res]]],
) -> None: ...

View File

@ -145,121 +145,3 @@ async def test_shutdown_sync_and_async_ordering():
await container.shutdown_resources() await container.shutdown_resources()
assert initialized_resources == ["r1", "r2", "r3", "r1", "r2", "r3"] assert initialized_resources == ["r1", "r2", "r3", "r1", "r2", "r3"]
assert shutdown_resources == ["r3", "r2", "r1", "r3", "r2", "r1"] assert shutdown_resources == ["r3", "r2", "r1", "r3", "r2", "r1"]
@mark.asyncio
async def test_init_and_shutdown_scoped_resources():
initialized_resources = []
shutdown_resources = []
def _sync_resource(name, **_):
initialized_resources.append(name)
yield name
shutdown_resources.append(name)
async def _async_resource(name, **_):
initialized_resources.append(name)
yield name
shutdown_resources.append(name)
class ResourceA(providers.Resource):
pass
class ResourceB(providers.Resource):
pass
class Container(containers.DeclarativeContainer):
resource_a = ResourceA(
_sync_resource,
name="ra1",
)
resource_b1 = ResourceB(
_sync_resource,
name="rb1",
r1=resource_a,
)
resource_b2 = ResourceB(
_async_resource,
name="rb2",
r2=resource_b1,
)
container = Container()
container.init_resources(resource_type=ResourceA)
assert initialized_resources == ["ra1"]
assert shutdown_resources == []
container.shutdown_resources(resource_type=ResourceA)
assert initialized_resources == ["ra1"]
assert shutdown_resources == ["ra1"]
await container.init_resources(resource_type=ResourceB)
assert initialized_resources == ["ra1", "ra1", "rb1", "rb2"]
assert shutdown_resources == ["ra1"]
await container.shutdown_resources(resource_type=ResourceB)
assert initialized_resources == ["ra1", "ra1", "rb1", "rb2"]
assert shutdown_resources == ["ra1", "rb2", "rb1"]
@mark.asyncio
async def test_init_and_shutdown_all_scoped_resources_using_default_value():
initialized_resources = []
shutdown_resources = []
def _sync_resource(name, **_):
initialized_resources.append(name)
yield name
shutdown_resources.append(name)
async def _async_resource(name, **_):
initialized_resources.append(name)
yield name
shutdown_resources.append(name)
class ResourceA(providers.Resource):
pass
class ResourceB(providers.Resource):
pass
class Container(containers.DeclarativeContainer):
resource_a = ResourceA(
_sync_resource,
name="r1",
)
resource_b1 = ResourceB(
_sync_resource,
name="r2",
r1=resource_a,
)
resource_b2 = ResourceB(
_async_resource,
name="r3",
r2=resource_b1,
)
container = Container()
await container.init_resources()
assert initialized_resources == ["r1", "r2", "r3"]
assert shutdown_resources == []
await container.shutdown_resources()
assert initialized_resources == ["r1", "r2", "r3"]
assert shutdown_resources == ["r3", "r2", "r1"]
await container.init_resources()
assert initialized_resources == ["r1", "r2", "r3", "r1", "r2", "r3"]
assert shutdown_resources == ["r3", "r2", "r1"]
await container.shutdown_resources()
assert initialized_resources == ["r1", "r2", "r3", "r1", "r2", "r3"]
assert shutdown_resources == ["r3", "r2", "r1", "r3", "r2", "r1"]

View File

@ -325,19 +325,6 @@ def test_init_shutdown_nested_resources():
assert _init2.shutdown_counter == 2 assert _init2.shutdown_counter == 2
def test_init_shutdown_resources_wrong_type() -> None:
class Container(containers.DeclarativeContainer):
pass
c = Container()
with raises(TypeError, match=r"resource_type must be a subclass of Resource provider"):
c.init_resources(int) # type: ignore[arg-type]
with raises(TypeError, match=r"resource_type must be a subclass of Resource provider"):
c.shutdown_resources(int) # type: ignore[arg-type]
def test_reset_singletons(): def test_reset_singletons():
class SubSubContainer(containers.DeclarativeContainer): class SubSubContainer(containers.DeclarativeContainer):
singleton = providers.Singleton(object) singleton = providers.Singleton(object)

View File

@ -11,7 +11,7 @@ def index():
return "Hello World!" return "Hello World!"
def _test(): def test():
return "Test!" return "Test!"
@ -25,7 +25,7 @@ class ApplicationContainer(containers.DeclarativeContainer):
app = flask.Application(Flask, __name__) app = flask.Application(Flask, __name__)
index_view = flask.View(index) index_view = flask.View(index)
test_view = flask.View(_test) test_view = flask.View(test)
test_class_view = flask.ClassBasedView(Test) test_class_view = flask.ClassBasedView(Test)

View File

@ -1,4 +1,4 @@
from typing import AsyncIterator, Iterator, TypeVar from typing import AsyncIterator, Iterator
from unittest.mock import ANY from unittest.mock import ANY
from pytest import mark from pytest import mark
@ -7,12 +7,6 @@ from dependency_injector.containers import DeclarativeContainer
from dependency_injector.ext.starlette import Lifespan from dependency_injector.ext.starlette import Lifespan
from dependency_injector.providers import Resource from dependency_injector.providers import Resource
T = TypeVar("T")
class XResource(Resource[T]):
"""A test provider"""
class TestLifespan: class TestLifespan:
@mark.parametrize("sync", [False, True]) @mark.parametrize("sync", [False, True])
@ -34,15 +28,11 @@ class TestLifespan:
yield yield
shutdown = True shutdown = True
def nope():
assert False, "should not be called"
class Container(DeclarativeContainer): class Container(DeclarativeContainer):
x = XResource(sync_resource if sync else async_resource) x = Resource(sync_resource if sync else async_resource)
y = Resource(nope)
container = Container() container = Container()
lifespan = Lifespan(container, resource_type=XResource) lifespan = Lifespan(container)
async with lifespan(ANY) as scope: async with lifespan(ANY) as scope:
assert scope is None assert scope is None

View File

@ -5,23 +5,6 @@ import os
from pytest import mark, raises from pytest import mark, raises
def test_no_env_variable_interpolation(config, ini_config_file_3):
config.from_ini(ini_config_file_3, envs_required=None)
assert config() == {
"section1": {
"value1": "${CONFIG_TEST_ENV}",
"value2": "${CONFIG_TEST_PATH}/path",
},
}
assert config.section1() == {
"value1": "${CONFIG_TEST_ENV}",
"value2": "${CONFIG_TEST_PATH}/path",
}
assert config.section1.value1() == "${CONFIG_TEST_ENV}"
assert config.section1.value2() == "${CONFIG_TEST_PATH}/path"
def test_env_variable_interpolation(config, ini_config_file_3): def test_env_variable_interpolation(config, ini_config_file_3):
config.from_ini(ini_config_file_3) config.from_ini(ini_config_file_3)

View File

@ -6,23 +6,6 @@ import os
from pytest import mark, raises from pytest import mark, raises
def test_no_env_variable_interpolation(config, json_config_file_3):
config.from_json(json_config_file_3, envs_required=None)
assert config() == {
"section1": {
"value1": "${CONFIG_TEST_ENV}",
"value2": "${CONFIG_TEST_PATH}/path",
},
}
assert config.section1() == {
"value1": "${CONFIG_TEST_ENV}",
"value2": "${CONFIG_TEST_PATH}/path",
}
assert config.section1.value1() == "${CONFIG_TEST_ENV}"
assert config.section1.value2() == "${CONFIG_TEST_PATH}/path"
def test_env_variable_interpolation(config, json_config_file_3): def test_env_variable_interpolation(config, json_config_file_3):
config.from_json(json_config_file_3) config.from_json(json_config_file_3)

View File

@ -6,23 +6,6 @@ import yaml
from pytest import mark, raises from pytest import mark, raises
def test_no_env_variable_interpolation(config, yaml_config_file_3):
config.from_yaml(yaml_config_file_3, envs_required=None)
assert config() == {
"section1": {
"value1": "${CONFIG_TEST_ENV}",
"value2": "${CONFIG_TEST_PATH}/path",
},
}
assert config.section1() == {
"value1": "${CONFIG_TEST_ENV}",
"value2": "${CONFIG_TEST_PATH}/path",
}
assert config.section1.value1() == "${CONFIG_TEST_ENV}"
assert config.section1.value2() == "${CONFIG_TEST_PATH}/path"
def test_env_variable_interpolation(config, yaml_config_file_3): def test_env_variable_interpolation(config, yaml_config_file_3):
config.from_yaml(yaml_config_file_3) config.from_yaml(yaml_config_file_3)

View File

@ -1,5 +1,4 @@
"""Coroutine provider tests.""" """Coroutine provider tests."""
import sys
from dependency_injector import providers, errors from dependency_injector import providers, errors
from pytest import mark, raises from pytest import mark, raises
@ -209,17 +208,3 @@ def test_repr():
"<dependency_injector.providers." "<dependency_injector.providers."
"Coroutine({0}) at {1}>".format(repr(example), hex(id(provider))) "Coroutine({0}) at {1}>".format(repr(example), hex(id(provider)))
) )
@mark.skipif(sys.version_info > (3, 15), reason="requires Python<3.16")
def test_asyncio_iscoroutinefunction() -> None:
from asyncio.coroutines import iscoroutinefunction
assert iscoroutinefunction(providers.Coroutine(example))
@mark.skipif(sys.version_info < (3, 12), reason="requires Python>=3.12")
def test_inspect_iscoroutinefunction() -> None:
from inspect import iscoroutinefunction
assert iscoroutinefunction(providers.Coroutine(example))

View File

@ -2,12 +2,11 @@
import asyncio import asyncio
import inspect import inspect
from contextlib import asynccontextmanager import sys
from typing import Any from typing import Any
from pytest import mark, raises
from dependency_injector import containers, providers, resources from dependency_injector import containers, providers, resources
from pytest import mark, raises
@mark.asyncio @mark.asyncio
@ -35,6 +34,7 @@ async def test_init_async_function():
@mark.asyncio @mark.asyncio
@mark.skipif(sys.version_info < (3, 6), reason="requires Python 3.6+")
async def test_init_async_generator(): async def test_init_async_generator():
resource = object() resource = object()
@ -71,46 +71,6 @@ async def test_init_async_generator():
assert _init.shutdown_counter == 2 assert _init.shutdown_counter == 2
@mark.asyncio
async def test_init_async_context_manager() -> None:
resource = object()
init_counter = 0
shutdown_counter = 0
@asynccontextmanager
async def _init():
nonlocal init_counter, shutdown_counter
await asyncio.sleep(0.001)
init_counter += 1
yield resource
await asyncio.sleep(0.001)
shutdown_counter += 1
provider = providers.Resource(_init)
result1 = await provider()
assert result1 is resource
assert init_counter == 1
assert shutdown_counter == 0
await provider.shutdown()
assert init_counter == 1
assert shutdown_counter == 1
result2 = await provider()
assert result2 is resource
assert init_counter == 2
assert shutdown_counter == 1
await provider.shutdown()
assert init_counter == 2
assert shutdown_counter == 2
@mark.asyncio @mark.asyncio
async def test_init_async_class(): async def test_init_async_class():
resource = object() resource = object()

View File

@ -2,12 +2,10 @@
import decimal import decimal
import sys import sys
from contextlib import contextmanager
from typing import Any from typing import Any
from pytest import mark, raises from dependency_injector import containers, providers, resources, errors
from pytest import raises, mark
from dependency_injector import containers, errors, providers, resources
def init_fn(*args, **kwargs): def init_fn(*args, **kwargs):
@ -125,41 +123,6 @@ def test_init_generator():
assert _init.shutdown_counter == 2 assert _init.shutdown_counter == 2
def test_init_context_manager() -> None:
init_counter, shutdown_counter = 0, 0
@contextmanager
def _init():
nonlocal init_counter, shutdown_counter
init_counter += 1
yield
shutdown_counter += 1
init_counter = 0
shutdown_counter = 0
provider = providers.Resource(_init)
result1 = provider()
assert result1 is None
assert init_counter == 1
assert shutdown_counter == 0
provider.shutdown()
assert init_counter == 1
assert shutdown_counter == 1
result2 = provider()
assert result2 is None
assert init_counter == 2
assert shutdown_counter == 1
provider.shutdown()
assert init_counter == 2
assert shutdown_counter == 2
def test_init_class(): def test_init_class():
class TestResource(resources.Resource): class TestResource(resources.Resource):
init_counter = 0 init_counter = 0
@ -227,7 +190,7 @@ def test_init_class_abc_shutdown_definition_is_not_required():
def test_init_not_callable(): def test_init_not_callable():
provider = providers.Resource(1) provider = providers.Resource(1)
with raises(TypeError, match=r"object is not callable"): with raises(errors.Error):
provider.init() provider.init()

View File

@ -1,9 +1,7 @@
import asyncio import asyncio
from typing_extensions import Annotated
from dependency_injector import containers, providers from dependency_injector import containers, providers
from dependency_injector.wiring import Closing, Provide, inject from dependency_injector.wiring import inject, Provide, Closing
class TestResource: class TestResource:
@ -44,15 +42,6 @@ async def async_injection(
return resource1, resource2 return resource1, resource2
@inject
async def async_generator_injection(
resource1: object = Provide[Container.resource1],
resource2: object = Closing[Provide[Container.resource2]],
):
yield resource1
yield resource2
@inject @inject
async def async_injection_with_closing( async def async_injection_with_closing(
resource1: object = Closing[Provide[Container.resource1]], resource1: object = Closing[Provide[Container.resource1]],

View File

@ -1,133 +0,0 @@
"""Test module for wiring with Annotated."""
import sys
import pytest
if sys.version_info < (3, 9):
pytest.skip("Annotated is only available in Python 3.9+", allow_module_level=True)
from decimal import Decimal
from typing import Callable, Annotated
from dependency_injector import providers
from dependency_injector.wiring import inject, Provide, Provider
from .container import Container, SubContainer
from .service import Service
service: Annotated[Service, Provide[Container.service]]
service_provider: Annotated[Callable[..., Service], Provider[Container.service]]
undefined: Annotated[Callable, Provide[providers.Provider()]]
class TestClass:
service: Annotated[Service, Provide[Container.service]]
service_provider: Annotated[Callable[..., Service], Provider[Container.service]]
undefined: Annotated[Callable, Provide[providers.Provider()]]
@inject
def __init__(self, service: Annotated[Service, Provide[Container.service]]):
self.service = service
@inject
def method(self, service: Annotated[Service, Provide[Container.service]]):
return service
@classmethod
@inject
def class_method(cls, service: Annotated[Service, Provide[Container.service]]):
return service
@staticmethod
@inject
def static_method(service: Annotated[Service, Provide[Container.service]]):
return service
@inject
def test_function(service: Annotated[Service, Provide[Container.service]]):
return service
@inject
def test_function_provider(service_provider: Annotated[Callable[..., Service], Provider[Container.service]]):
service = service_provider()
return service
@inject
def test_config_value(
value_int: Annotated[int, Provide[Container.config.a.b.c.as_int()]],
value_float: Annotated[float, Provide[Container.config.a.b.c.as_float()]],
value_str: Annotated[str, Provide[Container.config.a.b.c.as_(str)]],
value_decimal: Annotated[Decimal, Provide[Container.config.a.b.c.as_(Decimal)]],
value_required: Annotated[str, Provide[Container.config.a.b.c.required()]],
value_required_int: Annotated[int, Provide[Container.config.a.b.c.required().as_int()]],
value_required_float: Annotated[float, Provide[Container.config.a.b.c.required().as_float()]],
value_required_str: Annotated[str, Provide[Container.config.a.b.c.required().as_(str)]],
value_required_decimal: Annotated[str, Provide[Container.config.a.b.c.required().as_(Decimal)]],
):
return (
value_int,
value_float,
value_str,
value_decimal,
value_required,
value_required_int,
value_required_float,
value_required_str,
value_required_decimal,
)
@inject
def test_config_value_required_undefined(
value_required: Annotated[int, Provide[Container.config.a.b.c.required()]],
):
return value_required
@inject
def test_provide_provider(service_provider: Annotated[Callable[..., Service], Provide[Container.service.provider]]):
service = service_provider()
return service
@inject
def test_provider_provider(service_provider: Annotated[Callable[..., Service], Provider[Container.service.provider]]):
service = service_provider()
return service
@inject
def test_provided_instance(some_value: Annotated[int, Provide[Container.service.provided.foo["bar"].call()]]):
return some_value
@inject
def test_subcontainer_provider(some_value: Annotated[int, Provide[Container.sub.int_object]]):
return some_value
@inject
def test_config_invariant(some_value: Annotated[int, Provide[Container.config.option[Container.config.switch]]]):
return some_value
@inject
def test_provide_from_different_containers(
service: Annotated[Service, Provide[Container.service]],
some_value: Annotated[int, Provide[SubContainer.int_object]],
):
return service, some_value
class ClassDecorator:
def __init__(self, fn):
self._fn = fn
def __call__(self, *args, **kwargs):
return self._fn(*args, **kwargs)
@ClassDecorator
@inject
def test_class_decorator(service: Annotated[Service, Provide[Container.service]]):
return service
def test_container(container: Annotated[Container, Provide[Container]]):
return container.service()
@inject
def test_annotated_with_non_di_metadata_first(
service: Annotated[Service, "some other annotated value", Provide[Container.service]],
):
return service

View File

@ -1,11 +1,7 @@
import sys import sys
from typing_extensions import Annotated
from fastapi import FastAPI, Depends from fastapi import FastAPI, Depends
from fastapi import ( from fastapi import Request # See: https://github.com/ets-labs/python-dependency-injector/issues/398
Request,
) # See: https://github.com/ets-labs/python-dependency-injector/issues/398
from fastapi.security import HTTPBasic, HTTPBasicCredentials from fastapi.security import HTTPBasic, HTTPBasicCredentials
from dependency_injector import containers, providers from dependency_injector import containers, providers
from dependency_injector.wiring import inject, Provide from dependency_injector.wiring import inject, Provide
@ -32,16 +28,11 @@ async def index(service: Service = Depends(Provide[Container.service])):
return {"result": result} return {"result": result}
@app.api_route("/annotated")
@inject
async def annotated(service: Annotated[Service, Depends(Provide[Container.service])]):
result = await service.process()
return {"result": result}
@app.get("/auth") @app.get("/auth")
@inject @inject
def read_current_user(credentials: HTTPBasicCredentials = Depends(security)): def read_current_user(
credentials: HTTPBasicCredentials = Depends(security)
):
return {"username": credentials.username, "password": credentials.password} return {"username": credentials.username, "password": credentials.password}

View File

@ -1,39 +0,0 @@
import sys
from fast_depends import Depends
from typing_extensions import Annotated
from dependency_injector import containers, providers
from dependency_injector.wiring import Provide, inject
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()
@inject
def apply_coefficient_annotated(
a: int,
coefficient_provider: Annotated[
CoefficientService, Depends(Provide[Container.service])
],
) -> float:
return a * coefficient_provider.get_coefficient()
container = Container()
container.wire(modules=[sys.modules[__name__]])

View File

@ -1,5 +1,3 @@
from typing_extensions import Annotated
from flask import Flask, jsonify, request, current_app, session, g from flask import Flask, jsonify, request, current_app, session, g
from dependency_injector import containers, providers from dependency_injector import containers, providers
from dependency_injector.wiring import inject, Provide from dependency_injector.wiring import inject, Provide
@ -28,12 +26,5 @@ def index(service: Service = Provide[Container.service]):
return jsonify({"result": result}) return jsonify({"result": result})
@app.route("/annotated")
@inject
def annotated(service: Annotated[Service, Provide[Container.service]]):
result = service.process()
return jsonify({"result": result})
container = Container() container = Container()
container.wire(modules=[__name__]) container.wire(modules=[__name__])

View File

@ -1,15 +0,0 @@
from dependency_injector.wiring import Provide, inject
missing_obj: object = Provide["missing"]
class TestMissingClass:
obj: object = Provide["missing"]
def method(self, obj: object = Provide["missing"]) -> object:
return obj
@inject
def test_missing_function(obj: object = Provide["missing"]):
return obj

View File

@ -1,81 +1,41 @@
from typing import Any, Dict, List, Optional
from dependency_injector import containers, providers from dependency_injector import containers, providers
from dependency_injector.wiring import Closing, Provide, inject from dependency_injector.wiring import inject, Provide, Closing
class Counter:
def __init__(self) -> None:
self._init = 0
self._shutdown = 0
def init(self) -> None:
self._init += 1
def shutdown(self) -> None:
self._shutdown += 1
def reset(self) -> None:
self._init = 0
self._shutdown = 0
class Service: class Service:
def __init__(self, counter: Optional[Counter] = None, **dependencies: Any) -> None: init_counter: int = 0
self.counter = counter or Counter() shutdown_counter: int = 0
self.dependencies = dependencies
def init(self) -> None: @classmethod
self.counter.init() def reset_counter(cls):
cls.init_counter = 0
cls.shutdown_counter = 0
def shutdown(self) -> None: @classmethod
self.counter.shutdown() def init(cls):
cls.init_counter += 1
@property @classmethod
def init_counter(self) -> int: def shutdown(cls):
return self.counter._init cls.shutdown_counter += 1
@property
def shutdown_counter(self) -> int:
return self.counter._shutdown
class FactoryService: class FactoryService:
def __init__(self, service: Service, service2: Service): def __init__(self, service: Service):
self.service = service self.service = service
self.service2 = service2
class NestedService: def init_service():
def __init__(self, factory_service: FactoryService): service = Service()
self.factory_service = factory_service
def init_service(counter: Counter, _list: List[int], _dict: Dict[str, int]):
service = Service(counter, _list=_list, _dict=_dict)
service.init() service.init()
yield service yield service
service.shutdown() service.shutdown()
class Container(containers.DeclarativeContainer): class Container(containers.DeclarativeContainer):
config = providers.Configuration(default={"a": 1, "b": 4})
counter = providers.Singleton(Counter) service = providers.Resource(init_service)
_list = providers.List( factory_service = providers.Factory(FactoryService, service)
providers.Callable(lambda a: a, a=config.a), providers.Callable(lambda b: b, 2)
)
_dict = providers.Dict(
a=providers.Callable(lambda a: a, a=3), b=providers.Callable(lambda b: b, config.b)
)
service = providers.Resource(init_service, counter, _list, _dict=_dict)
service2 = providers.Resource(init_service, counter, _list, _dict=_dict)
factory_service = providers.Factory(FactoryService, service, service2)
factory_service_kwargs = providers.Factory(
FactoryService,
service=service,
service2=service2,
)
nested_service = providers.Factory(NestedService, factory_service)
@inject @inject
@ -84,21 +44,5 @@ def test_function(service: Service = Closing[Provide["service"]]):
@inject @inject
def test_function_dependency( def test_function_dependency(factory: FactoryService = Closing[Provide["factory_service"]]):
factory: FactoryService = Closing[Provide["factory_service"]],
):
return factory return factory
@inject
def test_function_dependency_kwargs(
factory: FactoryService = Closing[Provide["factory_service_kwargs"]],
):
return factory
@inject
def test_function_nested_dependency(
nested: NestedService = Closing[Provide["nested_service"]],
):
return nested

View File

@ -32,23 +32,6 @@ async def test_async_injections():
assert asyncinjections.resource2.shutdown_counter == 0 assert asyncinjections.resource2.shutdown_counter == 0
@mark.asyncio
async def test_async_generator_injections() -> None:
resources = []
async for resource in asyncinjections.async_generator_injection():
resources.append(resource)
assert len(resources) == 2
assert resources[0] is asyncinjections.resource1
assert asyncinjections.resource1.init_counter == 1
assert asyncinjections.resource1.shutdown_counter == 0
assert resources[1] is asyncinjections.resource2
assert asyncinjections.resource2.init_counter == 1
assert asyncinjections.resource2.shutdown_counter == 1
@mark.asyncio @mark.asyncio
async def test_async_injections_with_closing(): async def test_async_injections_with_closing():
resource1, resource2 = await asyncinjections.async_injection_with_closing() resource1, resource2 = await asyncinjections.async_injection_with_closing()

View File

@ -1,187 +0,0 @@
"""Main wiring tests for Annotated attribute and argument injection."""
from decimal import Decimal
import typing
from dependency_injector import errors
from dependency_injector.wiring import Closing, Provide, Provider, wire
from pytest import fixture, mark, raises
from samples.wiring import module_annotated as module, package, resourceclosing
from samples.wiring.service import Service
from samples.wiring.container import Container, SubContainer
@fixture(autouse=True)
def container():
container = Container(config={"a": {"b": {"c": 10}}})
container.wire(
modules=[module],
packages=[package],
)
yield container
container.unwire()
@fixture
def subcontainer():
container = SubContainer()
container.wire(
modules=[module],
packages=[package],
)
yield container
container.unwire()
@fixture
def resourceclosing_container():
container = resourceclosing.Container()
container.wire(modules=[resourceclosing])
yield container
container.unwire()
def test_module_attributes_wiring():
assert isinstance(module.service, Service)
assert isinstance(module.service_provider(), Service)
assert isinstance(module.__annotations__['undefined'], typing._AnnotatedAlias)
def test_class_wiring():
test_class_object = module.TestClass()
assert isinstance(test_class_object.service, Service)
def test_class_wiring_context_arg(container: Container):
test_service = container.service()
test_class_object = module.TestClass(service=test_service)
assert test_class_object.service is test_service
def test_class_method_wiring():
test_class_object = module.TestClass()
service = test_class_object.method()
assert isinstance(service, Service)
def test_class_classmethod_wiring():
service = module.TestClass.class_method()
assert isinstance(service, Service)
def test_instance_classmethod_wiring():
instance = module.TestClass()
service = instance.class_method()
assert isinstance(service, Service)
def test_class_staticmethod_wiring():
service = module.TestClass.static_method()
assert isinstance(service, Service)
def test_instance_staticmethod_wiring():
instance = module.TestClass()
service = instance.static_method()
assert isinstance(service, Service)
def test_class_attribute_wiring():
assert isinstance(module.TestClass.service, Service)
assert isinstance(module.TestClass.service_provider(), Service)
assert isinstance(module.TestClass.__annotations__['undefined'], typing._AnnotatedAlias)
def test_function_wiring():
service = module.test_function()
assert isinstance(service, Service)
def test_function_wiring_context_arg(container: Container):
test_service = container.service()
service = module.test_function(service=test_service)
assert service is test_service
def test_function_wiring_provider():
service = module.test_function_provider()
assert isinstance(service, Service)
def test_function_wiring_provider_context_arg(container: Container):
test_service = container.service()
service = module.test_function_provider(service_provider=lambda: test_service)
assert service is test_service
def test_configuration_option():
(
value_int,
value_float,
value_str,
value_decimal,
value_required,
value_required_int,
value_required_float,
value_required_str,
value_required_decimal,
) = module.test_config_value()
assert value_int == 10
assert value_float == 10.0
assert value_str == "10"
assert value_decimal == Decimal(10)
assert value_required == 10
assert value_required_int == 10
assert value_required_float == 10.0
assert value_required_str == "10"
assert value_required_decimal == Decimal(10)
def test_configuration_option_required_undefined(container: Container):
container.config.reset_override()
with raises(errors.Error, match="Undefined configuration option \"config.a.b.c\""):
module.test_config_value_required_undefined()
def test_provide_provider():
service = module.test_provide_provider()
assert isinstance(service, Service)
def test_provider_provider():
service = module.test_provider_provider()
assert isinstance(service, Service)
def test_provided_instance(container: Container):
class TestService:
foo = {"bar": lambda: 10}
with container.service.override(TestService()):
some_value = module.test_provided_instance()
assert some_value == 10
def test_subcontainer():
some_value = module.test_subcontainer_provider()
assert some_value == 1
def test_config_invariant(container: Container):
config = {
"option": {
"a": 1,
"b": 2,
},
"switch": "a",
}
container.config.from_dict(config)
value_default = module.test_config_invariant()
assert value_default == 1
with container.config.switch.override("a"):
value_a = module.test_config_invariant()
assert value_a == 1
with container.config.switch.override("b"):
value_b = module.test_config_invariant()
assert value_b == 2
def test_class_decorator():
service = module.test_class_decorator()
assert isinstance(service, Service)
def test_container():
service = module.test_container()
assert isinstance(service, Service)
def test_annotated_with_non_di_metadata_first():
"""Test that Annotated works when DI marker is not the first metadata item.
This tests the case where Annotated has other metadata (like docstrings or
other annotations) before the Provide marker, e.g.:
Annotated[Service, "some doc", Provide[Container.service]]
"""
service = module.test_annotated_with_non_di_metadata_first()
assert isinstance(service, Service)

View File

@ -1,6 +1,5 @@
"""Main wiring tests.""" """Main wiring tests."""
import re
from decimal import Decimal from decimal import Decimal
from dependency_injector import errors from dependency_injector import errors
@ -68,7 +67,7 @@ def test_module_attributes_wiring():
def test_module_attribute_wiring_with_invalid_marker(container: Container): def test_module_attribute_wiring_with_invalid_marker(container: Container):
from samples.wiring import module_invalid_attr_injection from samples.wiring import module_invalid_attr_injection
with raises(Exception, match=re.escape("Unknown type of marker {0}".format(module_invalid_attr_injection.service))): with raises(Exception, match="Unknown type of marker {0}".format(module_invalid_attr_injection.service)):
container.wire(modules=[module_invalid_attr_injection]) container.wire(modules=[module_invalid_attr_injection])

View File

@ -1,21 +1,14 @@
"""Main wiring tests.""" """Main wiring tests."""
import re
from decimal import Decimal from decimal import Decimal
from pytest import fixture, mark, raises, warns
from samples.wiringstringids import module, package, resourceclosing
from samples.wiringstringids.container import Container, SubContainer
from samples.wiringstringids.service import Service
from dependency_injector import errors from dependency_injector import errors
from dependency_injector.wiring import ( from dependency_injector.wiring import Closing, Provide, Provider, wire
Closing, from pytest import fixture, mark, raises
Provide,
Provider, from samples.wiringstringids import module, package, resourceclosing
UnresolvedMarkerWarning, from samples.wiringstringids.service import Service
wire, from samples.wiringstringids.container import Container, SubContainer
)
@fixture(autouse=True) @fixture(autouse=True)
@ -41,11 +34,10 @@ def subcontainer():
@fixture @fixture
def resourceclosing_container(request): def resourceclosing_container():
container = resourceclosing.Container() container = resourceclosing.Container()
container.wire(modules=[resourceclosing]) container.wire(modules=[resourceclosing])
with container.reset_singletons(): yield container
yield container
container.unwire() container.unwire()
@ -75,20 +67,10 @@ def test_module_attributes_wiring():
def test_module_attribute_wiring_with_invalid_marker(container: Container): def test_module_attribute_wiring_with_invalid_marker(container: Container):
from samples.wiringstringids import module_invalid_attr_injection from samples.wiringstringids import module_invalid_attr_injection
with raises(Exception, match=re.escape("Unknown type of marker {0}".format(module_invalid_attr_injection.service))): with raises(Exception, match="Unknown type of marker {0}".format(module_invalid_attr_injection.service)):
container.wire(modules=[module_invalid_attr_injection]) container.wire(modules=[module_invalid_attr_injection])
def test_warn_unresolved_marker(container: Container):
from samples.wiringstringids import missing
with warns(
UnresolvedMarkerWarning,
match=r"^Unresolved marker .+ in .+$",
):
container.wire(modules=[missing], warn_unresolved=True)
def test_class_wiring(): def test_class_wiring():
test_class_object = module.TestClass() test_class_object = module.TestClass()
assert isinstance(test_class_object.service, Service) assert isinstance(test_class_object.service, Service)
@ -292,65 +274,42 @@ def test_wire_multiple_containers():
@mark.usefixtures("resourceclosing_container") @mark.usefixtures("resourceclosing_container")
def test_closing_resource(): def test_closing_resource():
resourceclosing.Service.reset_counter()
result_1 = resourceclosing.test_function() result_1 = resourceclosing.test_function()
assert isinstance(result_1, resourceclosing.Service) assert isinstance(result_1, resourceclosing.Service)
assert result_1.init_counter == 1 assert result_1.init_counter == 1
assert result_1.shutdown_counter == 1 assert result_1.shutdown_counter == 1
assert result_1.dependencies == {"_list": [1, 2], "_dict": {"a": 3, "b": 4}}
result_2 = resourceclosing.test_function() result_2 = resourceclosing.test_function()
assert isinstance(result_2, resourceclosing.Service) assert isinstance(result_2, resourceclosing.Service)
assert result_2.init_counter == 2 assert result_2.init_counter == 2
assert result_2.shutdown_counter == 2 assert result_2.shutdown_counter == 2
assert result_1.dependencies == {"_list": [1, 2], "_dict": {"a": 3, "b": 4}}
assert result_1 is not result_2 assert result_1 is not result_2
@mark.usefixtures("resourceclosing_container") @mark.usefixtures("resourceclosing_container")
def test_closing_dependency_resource(): def test_closing_dependency_resource():
resourceclosing.Service.reset_counter()
result_1 = resourceclosing.test_function_dependency() result_1 = resourceclosing.test_function_dependency()
assert isinstance(result_1, resourceclosing.FactoryService) assert isinstance(result_1, resourceclosing.FactoryService)
assert result_1.service.init_counter == 2 assert result_1.service.init_counter == 1
assert result_1.service.shutdown_counter == 2 assert result_1.service.shutdown_counter == 1
result_2 = resourceclosing.test_function_dependency() result_2 = resourceclosing.test_function_dependency()
assert isinstance(result_2, resourceclosing.FactoryService) assert isinstance(result_2, resourceclosing.FactoryService)
assert result_2.service.init_counter == 4 assert result_2.service.init_counter == 2
assert result_2.service.shutdown_counter == 4 assert result_2.service.shutdown_counter == 2
@mark.usefixtures("resourceclosing_container")
def test_closing_dependency_resource_kwargs():
result_1 = resourceclosing.test_function_dependency_kwargs()
assert isinstance(result_1, resourceclosing.FactoryService)
assert result_1.service.init_counter == 2
assert result_1.service.shutdown_counter == 2
result_2 = resourceclosing.test_function_dependency_kwargs()
assert isinstance(result_2, resourceclosing.FactoryService)
assert result_2.service.init_counter == 4
assert result_2.service.shutdown_counter == 4
@mark.usefixtures("resourceclosing_container")
def test_closing_nested_dependency_resource():
result_1 = resourceclosing.test_function_nested_dependency()
assert isinstance(result_1, resourceclosing.NestedService)
assert result_1.factory_service.service.init_counter == 2
assert result_1.factory_service.service.shutdown_counter == 2
result_2 = resourceclosing.test_function_nested_dependency()
assert isinstance(result_2, resourceclosing.NestedService)
assert result_2.factory_service.service.init_counter == 4
assert result_2.factory_service.service.shutdown_counter == 4
assert result_1 is not result_2 assert result_1 is not result_2
@mark.usefixtures("resourceclosing_container") @mark.usefixtures("resourceclosing_container")
def test_closing_resource_bypass_marker_injection(): def test_closing_resource_bypass_marker_injection():
resourceclosing.Service.reset_counter()
result_1 = resourceclosing.test_function(service=Closing[Provide["service"]]) result_1 = resourceclosing.test_function(service=Closing[Provide["service"]])
assert isinstance(result_1, resourceclosing.Service) assert isinstance(result_1, resourceclosing.Service)
assert result_1.init_counter == 1 assert result_1.init_counter == 1
@ -366,6 +325,7 @@ def test_closing_resource_bypass_marker_injection():
@mark.usefixtures("resourceclosing_container") @mark.usefixtures("resourceclosing_container")
def test_closing_resource_context(): def test_closing_resource_context():
resourceclosing.Service.reset_counter()
service = resourceclosing.Service() service = resourceclosing.Service()
result_1 = resourceclosing.test_function(service=service) result_1 = resourceclosing.test_function(service=service)

View File

@ -1,46 +0,0 @@
"""Tests for string module and package names."""
from typing import Iterator, Optional
from pytest import fixture, mark
from samples.wiring.container import Container
from dependency_injector.wiring import _fetch_reference_injections
@fixture
def container() -> Iterator[Container]:
container = Container()
yield container
container.unwire()
@mark.parametrize(
["arg_value", "wc_value", "empty_cache"],
[
(None, False, True),
(False, True, True),
(True, False, False),
(None, True, False),
],
)
def test_fetch_reference_injections_cache(
container: Container,
arg_value: Optional[bool],
wc_value: bool,
empty_cache: bool,
) -> None:
container.wiring_config.keep_cache = wc_value
container.wire(
modules=["samples.wiring.module"],
packages=["samples.wiring.package"],
keep_cache=arg_value,
)
cache_info = _fetch_reference_injections.cache_info()
if empty_cache:
assert cache_info == (0, 0, None, 0)
else:
assert cache_info.hits > 0
assert cache_info.misses > 0
assert cache_info.currsize > 0

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