mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2025-04-07 18:54:25 +03:00
Compare commits
35 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
6e4794bab1 | ||
|
f3b3b1baa4 | ||
|
9b66d4bf16 | ||
|
7d4ebecd19 | ||
|
09efbffab1 | ||
|
8b625d81ad | ||
|
23acf01c15 | ||
|
0d6fdb5b78 | ||
|
2330122de6 | ||
|
29ae3e1337 | ||
|
50643e0dfb | ||
|
3893e1df81 | ||
|
0fd35baee6 | ||
|
3df95847d5 | ||
|
6d9d34c0f6 | ||
|
de50666a13 | ||
|
ccbd5bbb80 | ||
|
00326e9a22 | ||
|
46646b1acf | ||
|
9f38db6ef3 | ||
|
9f4e2839d2 | ||
|
41e18dfa90 | ||
|
f9db578c59 | ||
|
d82d9fb822 | ||
|
3ba4704bc1 | ||
|
aa56b70dc8 | ||
|
7f586246b4 | ||
|
87741edb53 | ||
|
be7abb3ec7 | ||
|
15400dea7d | ||
|
704e36a642 | ||
|
83d71acb70 | ||
|
c61fc16b8d | ||
|
cab75cb9c7 | ||
|
494c457643 |
10
.coveragerc
10
.coveragerc
|
@ -1,10 +0,0 @@
|
|||
[run]
|
||||
source = dependency_injector
|
||||
omit = tests/unit
|
||||
plugins = Cython.Coverage
|
||||
|
||||
[report]
|
||||
show_missing = true
|
||||
|
||||
[html]
|
||||
directory=reports/unittests/
|
48
.github/workflows/publishing.yml
vendored
48
.github/workflows/publishing.yml
vendored
|
@ -10,7 +10,7 @@ jobs:
|
|||
|
||||
tests:
|
||||
name: Run tests
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
|
@ -23,7 +23,7 @@ jobs:
|
|||
|
||||
linters:
|
||||
name: Run linters
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
matrix:
|
||||
toxenv: [flake8, pydocstyle, mypy, pylint]
|
||||
|
@ -40,17 +40,18 @@ jobs:
|
|||
build-sdist:
|
||||
name: Build source tarball
|
||||
needs: [tests, linters]
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.13
|
||||
- run: |
|
||||
python -m pip install --upgrade pip setuptools
|
||||
python setup.py sdist
|
||||
- uses: actions/upload-artifact@v3
|
||||
python -m pip install --upgrade build
|
||||
python -m build --sdist
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: cibw-sdist
|
||||
path: ./dist/*
|
||||
|
||||
build-wheels:
|
||||
|
@ -59,45 +60,28 @@ jobs:
|
|||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-22.04, windows-2019, macos-14]
|
||||
os: [ubuntu-24.04, ubuntu-24.04-arm, windows-2019, macos-14]
|
||||
env:
|
||||
CIBW_SKIP: cp27-*
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Build wheels
|
||||
uses: pypa/cibuildwheel@v2.20.0
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
path: ./wheelhouse/*.whl
|
||||
|
||||
build-wheels-linux-aarch64:
|
||||
name: Build wheels (ubuntu-22.04-aarch64)
|
||||
needs: [tests, linters]
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
CIBW_SKIP: cp27-*
|
||||
steps:
|
||||
- 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
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: cibw-wheels-x86-${{ matrix.os }}-${{ strategy.job-index }}
|
||||
path: ./wheelhouse/*.whl
|
||||
|
||||
publish:
|
||||
name: Publish on PyPI
|
||||
needs: [build-sdist, build-wheels, build-wheels-linux-aarch64]
|
||||
runs-on: ubuntu-22.04
|
||||
needs: [build-sdist, build-wheels]
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: artifact
|
||||
pattern: cibw-*
|
||||
path: dist
|
||||
merge-multiple: true
|
||||
- uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
user: __token__
|
||||
|
@ -109,7 +93,7 @@ jobs:
|
|||
publish-docs:
|
||||
name: Publish docs
|
||||
needs: [publish]
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
|
|
12
.github/workflows/tests-and-linters.yml
vendored
12
.github/workflows/tests-and-linters.yml
vendored
|
@ -36,6 +36,17 @@ jobs:
|
|||
env:
|
||||
TOXENV: ${{ matrix.python-version }}
|
||||
|
||||
test-different-pydantic-versions:
|
||||
name: Run tests with different pydantic versions
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.12"
|
||||
- run: pip install tox
|
||||
- run: tox -e pydantic-v1,pydantic-v2
|
||||
|
||||
test-coverage:
|
||||
name: Run tests with coverage
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -50,7 +61,6 @@ jobs:
|
|||
with:
|
||||
python-version: 3.12
|
||||
- run: pip install tox 'cython>=3,<4'
|
||||
- run: make cythonize
|
||||
- run: tox -vv
|
||||
env:
|
||||
TOXENV: coveralls
|
||||
|
|
12
.gitignore
vendored
12
.gitignore
vendored
|
@ -63,13 +63,11 @@ venv*/
|
|||
# Vim Rope
|
||||
.ropeproject/
|
||||
|
||||
# C extensions
|
||||
src/dependency_injector/*.h
|
||||
src/dependency_injector/*.so
|
||||
src/dependency_injector/containers/*.h
|
||||
src/dependency_injector/containers/*.so
|
||||
src/dependency_injector/providers/*.h
|
||||
src/dependency_injector/providers/*.so
|
||||
# Cython artifacts
|
||||
src/**/*.c
|
||||
src/**/*.h
|
||||
src/**/*.so
|
||||
src/**/*.html
|
||||
|
||||
# Workspace for samples
|
||||
.workspace/
|
||||
|
|
49
.pylintrc
49
.pylintrc
|
@ -1,49 +0,0 @@
|
|||
[MASTER]
|
||||
|
||||
# Add <file or directory> to the black list. It should be a base name, not a
|
||||
# path. You may set this option multiple times.
|
||||
ignore=utils,tests
|
||||
|
||||
[MESSAGES CONTROL]
|
||||
|
||||
# Disable the message(s) with the given id(s).
|
||||
# disable-msg=
|
||||
|
||||
[SIMILARITIES]
|
||||
|
||||
# Minimum lines number of a similarity.
|
||||
min-similarity-lines=5
|
||||
|
||||
[TYPECHECK]
|
||||
ignore-mixin-members=yes
|
||||
# ignored-classes=
|
||||
zope=no
|
||||
# generated-members=providedBy,implementedBy,rawDataReceived
|
||||
|
||||
[DESIGN]
|
||||
# Maximum number of arguments for function / method
|
||||
max-args=10
|
||||
|
||||
# Maximum number of locals for function / method body
|
||||
max-locals=20
|
||||
|
||||
# Maximum number of return / yield for function / method body
|
||||
max-returns=10
|
||||
|
||||
# Maximum number of branch for function / method body
|
||||
max-branchs=10
|
||||
|
||||
# Maximum number of statements in function / method body
|
||||
max-statements=60
|
||||
|
||||
# Maximum number of parents for a class (see R0901).
|
||||
max-parents=10
|
||||
|
||||
# Maximum number of attributes for a class (see R0902).
|
||||
max-attributes=30
|
||||
|
||||
# Minimum number of public methods for a class (see R0903).
|
||||
min-public-methods=0
|
||||
|
||||
# Maximum number of public methods for a class (see R0904).
|
||||
max-public-methods=30
|
32
Makefile
32
Makefile
|
@ -1,14 +1,6 @@
|
|||
VERSION := $(shell python setup.py --version)
|
||||
|
||||
CYTHON_SRC := $(shell find src/dependency_injector -name '*.pyx')
|
||||
|
||||
CYTHON_DIRECTIVES = -Xlanguage_level=3
|
||||
|
||||
ifdef DEPENDENCY_INJECTOR_DEBUG_MODE
|
||||
CYTHON_DIRECTIVES += -Xprofile=True
|
||||
CYTHON_DIRECTIVES += -Xlinetrace=True
|
||||
endif
|
||||
|
||||
export COVERAGE_RCFILE := pyproject.toml
|
||||
|
||||
clean:
|
||||
# Clean sources
|
||||
|
@ -25,21 +17,17 @@ clean:
|
|||
find examples -name '*.py[co]' -delete
|
||||
find examples -name '__pycache__' -delete
|
||||
|
||||
cythonize:
|
||||
# Compile Cython to C
|
||||
cython -a $(CYTHON_DIRECTIVES) $(CYTHON_SRC)
|
||||
build: clean
|
||||
# Compile C extensions
|
||||
python setup.py build_ext --inplace
|
||||
# Move all Cython html reports
|
||||
mkdir -p reports/cython/
|
||||
find src -name '*.html' -exec mv {} reports/cython/ \;
|
||||
|
||||
build: clean cythonize
|
||||
# Compile C extensions
|
||||
python setup.py build_ext --inplace
|
||||
|
||||
docs-live:
|
||||
sphinx-autobuild docs docs/_build/html
|
||||
|
||||
install: uninstall clean cythonize
|
||||
install: uninstall clean build
|
||||
pip install -ve .
|
||||
|
||||
uninstall:
|
||||
|
@ -48,9 +36,9 @@ uninstall:
|
|||
test:
|
||||
# Unit tests with coverage report
|
||||
coverage erase
|
||||
coverage run --rcfile=./.coveragerc -m pytest -c tests/.configs/pytest.ini
|
||||
coverage report --rcfile=./.coveragerc
|
||||
coverage html --rcfile=./.coveragerc
|
||||
coverage run -m pytest -c tests/.configs/pytest.ini
|
||||
coverage report
|
||||
coverage html
|
||||
|
||||
check:
|
||||
flake8 src/dependency_injector/
|
||||
|
@ -61,9 +49,9 @@ check:
|
|||
|
||||
mypy tests/typing
|
||||
|
||||
test-publish: cythonize
|
||||
test-publish: build
|
||||
# Create distributions
|
||||
python setup.py sdist
|
||||
python -m build --sdist
|
||||
# Upload distributions to PyPI
|
||||
twine upload --repository testpypi dist/dependency-injector-$(VERSION)*
|
||||
|
||||
|
|
|
@ -7,6 +7,48 @@ that were made in every particular version.
|
|||
From version 0.7.6 *Dependency Injector* framework strictly
|
||||
follows `Semantic versioning`_
|
||||
|
||||
4.46.0
|
||||
------
|
||||
|
||||
- Add option to disable env var interpolation in configs (`#861 <https://github.com/ets-labs/python-dependency-injector/pull/861>`_)
|
||||
- Fix ``Closing`` dependency resolution (`#852 <https://github.com/ets-labs/python-dependency-injector/pull/852>`_)
|
||||
- Add support for ``inspect.iscoroutinefunction()`` in ``Coroutine`` provider (`#830 <https://github.com/ets-labs/python-dependency-injector/pull/830>`_)
|
||||
- Fix broken wiring of sync inject-decorated methods (`#673 <https://github.com/ets-labs/python-dependency-injector/pull/673>`_)
|
||||
- Add support for ``typing.Annotated`` (`#721 <https://github.com/ets-labs/python-dependency-injector/pull/721>`_, `#853 <https://github.com/ets-labs/python-dependency-injector/pull/853>`_)
|
||||
- Documentation updates for movie-lister example (`#747 <https://github.com/ets-labs/python-dependency-injector/pull/747>`_)
|
||||
- Fix type propagation in ``Provider.provider`` (`#744 <https://github.com/ets-labs/python-dependency-injector/pull/744>`_)
|
||||
|
||||
Many thanks for the contributions to:
|
||||
- `ZipFile <https://github.com/ZipFile>`_
|
||||
- `Yegor Statkevich <https://github.com/jazzthief>`_
|
||||
- `Federico Tomasi <https://github.com/federinik>`_
|
||||
- `Martin Lafrance <https://github.com/martlaf>`_
|
||||
- `Philip Bjorge <https://github.com/philipbjorge>`_
|
||||
- `Ilya Kazakov <https://github.com/mrKazzila>`_
|
||||
|
||||
4.45.0
|
||||
--------
|
||||
- Add Starlette lifespan handler implementation (`#683 <https://github.com/ets-labs/python-dependency-injector/pull/683>`_).
|
||||
- Raise exception in ``ThreadLocalSingleton`` instead of hiding it in finally (`#845 <https://github.com/ets-labs/python-dependency-injector/pull/845>`_).
|
||||
- Improve debuggability of ``deepcopy`` errors (`#839 <https://github.com/ets-labs/python-dependency-injector/pull/839>`_).
|
||||
- Update examples (`#838 <https://github.com/ets-labs/python-dependency-injector/pull/838>`_).
|
||||
- Upgrade testing dependencies (`#837 <https://github.com/ets-labs/python-dependency-injector/pull/837>`_).
|
||||
- Add minor fixes to the documentation (`#709 <https://github.com/ets-labs/python-dependency-injector/pull/709>`_).
|
||||
- Remove ``six`` from the dependencies (`3ba4704 <https://github.com/ets-labs/python-dependency-injector/commit/3ba4704bc1cb00310749fd2eda0c8221167c313c>`_).
|
||||
|
||||
Many thanks for the contributions to:
|
||||
- `ZipFile <https://github.com/ZipFile>`_
|
||||
- `František Trebuňa <https://github.com/gortibaldik>`_
|
||||
- `JC (Jonathan Chen) <https://github.com/dijonkitchen>`_
|
||||
|
||||
4.44.0
|
||||
--------
|
||||
- Implement support for Pydantic 2. PR: `#832 <https://github.com/ets-labs/python-dependency-injector/pull/832>`_.
|
||||
- Implement `PEP-517 <https://peps.python.org/pep-0517/>`_, `PEP-518 <https://peps.python.org/pep-0518/>`_, and
|
||||
`PEP-621 <https://peps.python.org/pep-0621/>`_. PR: `#829 <https://github.com/ets-labs/python-dependency-injector/pull/829>`_.
|
||||
|
||||
Many thanks to `ZipFile <https://github.com/ZipFile>`_ for both contributions.
|
||||
|
||||
4.43.0
|
||||
--------
|
||||
- Add support for Python 3.13.
|
||||
|
|
|
@ -183,22 +183,22 @@ See also: :ref:`configuration-envs-interpolation`.
|
|||
Loading from a Pydantic settings
|
||||
--------------------------------
|
||||
|
||||
``Configuration`` provider can load configuration from a ``pydantic`` settings object using the
|
||||
``Configuration`` provider can load configuration from a ``pydantic_settings.BaseSettings`` object using the
|
||||
:py:meth:`Configuration.from_pydantic` method:
|
||||
|
||||
.. literalinclude:: ../../examples/providers/configuration/configuration_pydantic.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 31
|
||||
:emphasize-lines: 32
|
||||
|
||||
To get the data from pydantic settings ``Configuration`` provider calls ``Settings.dict()`` method.
|
||||
To get the data from pydantic settings ``Configuration`` provider calls its ``model_dump()`` method.
|
||||
If you need to pass an argument to this call, use ``.from_pydantic()`` keyword arguments.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
container.config.from_pydantic(Settings(), exclude={"optional"})
|
||||
|
||||
Alternatively, you can provide a ``pydantic`` settings object over the configuration provider argument. In that case,
|
||||
Alternatively, you can provide a ``pydantic_settings.BaseSettings`` object over the configuration provider argument. In that case,
|
||||
the container will call ``config.from_pydantic()`` automatically:
|
||||
|
||||
.. code-block:: python
|
||||
|
@ -215,18 +215,23 @@ the container will call ``config.from_pydantic()`` automatically:
|
|||
|
||||
.. note::
|
||||
|
||||
``Dependency Injector`` doesn't install ``pydantic`` by default.
|
||||
``Dependency Injector`` doesn't install ``pydantic-settings`` by default.
|
||||
|
||||
You can install the ``Dependency Injector`` with an extra dependency::
|
||||
|
||||
pip install dependency-injector[pydantic]
|
||||
pip install dependency-injector[pydantic2]
|
||||
|
||||
or install ``pydantic`` directly::
|
||||
or install ``pydantic-settings`` directly::
|
||||
|
||||
pip install pydantic
|
||||
pip install pydantic-settings
|
||||
|
||||
*Don't forget to mirror the changes in the requirements file.*
|
||||
|
||||
.. note::
|
||||
|
||||
For backward-compatibility, Pydantic v1 is still supported.
|
||||
Passing ``pydantic.BaseSettings`` instances will work just as fine as ``pydantic_settings.BaseSettings``.
|
||||
|
||||
Loading from a dictionary
|
||||
-------------------------
|
||||
|
||||
|
@ -361,6 +366,19 @@ See also: :ref:`configuration-strict-mode`.
|
|||
|
||||
assert container.config.section.option() is None
|
||||
|
||||
If you want to disable environment variables interpolation, pass ``envs_required=None``:
|
||||
|
||||
.. code-block:: yaml
|
||||
:caption: templates.yml
|
||||
|
||||
template_string: 'Hello, ${name}!'
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> container.config.from_yaml("templates.yml", envs_required=None)
|
||||
>>> container.config.template_string()
|
||||
'Hello, ${name}!'
|
||||
|
||||
Mandatory and optional sources
|
||||
------------------------------
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ returns it on the rest of the calls.
|
|||
|
||||
.. note::
|
||||
|
||||
``Singleton`` provider makes dependencies injection only when creates an object. When an object
|
||||
``Singleton`` provider makes dependencies injection only when it creates an object. When an object
|
||||
is created and memorized ``Singleton`` provider just returns it without applying injections.
|
||||
|
||||
Specialization of the provided type and abstract singletons work the same like like for the
|
||||
|
|
|
@ -18,7 +18,7 @@ In this tutorial we will use:
|
|||
|
||||
- Python 3
|
||||
- Docker
|
||||
- Docker-compose
|
||||
- Docker Compose
|
||||
|
||||
Start from the scratch or jump to the section:
|
||||
|
||||
|
@ -47,28 +47,27 @@ response it will log:
|
|||
Prerequisites
|
||||
-------------
|
||||
|
||||
We will use `Docker <https://www.docker.com/>`_ and
|
||||
`docker-compose <https://docs.docker.com/compose/>`_ in this tutorial. Let's check the versions:
|
||||
We will use `docker compose <https://docs.docker.com/compose/>`_ in this tutorial. Let's check the versions:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker --version
|
||||
docker-compose --version
|
||||
docker compose version
|
||||
|
||||
The output should look something like:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
Docker version 20.10.5, build 55c4c88
|
||||
docker-compose version 1.29.0, build 07737305
|
||||
Docker version 27.3.1, build ce12230
|
||||
Docker Compose version v2.29.7
|
||||
|
||||
.. note::
|
||||
|
||||
If you don't have ``Docker`` or ``docker-compose`` you need to install them before proceeding.
|
||||
If you don't have ``Docker`` or ``docker compose`` you need to install them before proceeding.
|
||||
Follow these installation guides:
|
||||
|
||||
- `Install Docker <https://docs.docker.com/get-docker/>`_
|
||||
- `Install docker-compose <https://docs.docker.com/compose/install/>`_
|
||||
- `Install docker compose <https://docs.docker.com/compose/install/>`_
|
||||
|
||||
The prerequisites are satisfied. Let's get started with the project layout.
|
||||
|
||||
|
@ -129,13 +128,13 @@ Put next lines into the ``requirements.txt`` file:
|
|||
pytest-cov
|
||||
|
||||
Second, we need to create the ``Dockerfile``. It will describe the daemon's build process and
|
||||
specify how to run it. We will use ``python:3.9-buster`` as a base image.
|
||||
specify how to run it. We will use ``python:3.13-bookworm`` as a base image.
|
||||
|
||||
Put next lines into the ``Dockerfile`` file:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
FROM python:3.10-buster
|
||||
FROM python:3.13-bookworm
|
||||
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
|
||||
|
@ -155,8 +154,6 @@ Put next lines into the ``docker-compose.yml`` file:
|
|||
|
||||
.. code-block:: yaml
|
||||
|
||||
version: "3.7"
|
||||
|
||||
services:
|
||||
|
||||
monitor:
|
||||
|
@ -171,7 +168,7 @@ Run in the terminal:
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
docker-compose build
|
||||
docker compose build
|
||||
|
||||
The build process may take a couple of minutes. You should see something like this in the end:
|
||||
|
||||
|
@ -184,7 +181,7 @@ After the build is done run the container:
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
docker-compose up
|
||||
docker compose up
|
||||
|
||||
The output should look like:
|
||||
|
||||
|
@ -461,7 +458,7 @@ Run in the terminal:
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
docker-compose up
|
||||
docker compose up
|
||||
|
||||
The output should look like:
|
||||
|
||||
|
@ -705,7 +702,7 @@ Run in the terminal:
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
docker-compose up
|
||||
docker compose up
|
||||
|
||||
You should see:
|
||||
|
||||
|
@ -813,7 +810,7 @@ Run in the terminal:
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
docker-compose up
|
||||
docker compose up
|
||||
|
||||
You should see:
|
||||
|
||||
|
@ -965,15 +962,16 @@ Run in the terminal:
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
docker-compose run --rm monitor py.test monitoringdaemon/tests.py --cov=monitoringdaemon
|
||||
docker compose run --rm monitor py.test monitoringdaemon/tests.py --cov=monitoringdaemon
|
||||
|
||||
You should see:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
platform linux -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
|
||||
platform linux -- Python 3.13.1, pytest-8.3.4, pluggy-1.5.0
|
||||
rootdir: /code
|
||||
plugins: asyncio-0.16.0, cov-3.0.0
|
||||
plugins: cov-6.0.0, asyncio-0.24.0
|
||||
asyncio: mode=Mode.STRICT, default_loop_scope=None
|
||||
collected 2 items
|
||||
|
||||
monitoringdaemon/tests.py .. [100%]
|
||||
|
|
|
@ -911,7 +911,7 @@ Create ``tests.py`` in the ``movies`` package:
|
|||
and put next into it:
|
||||
|
||||
.. code-block:: python
|
||||
:emphasize-lines: 36,51
|
||||
:emphasize-lines: 41,50
|
||||
|
||||
"""Tests module."""
|
||||
|
||||
|
@ -941,13 +941,18 @@ and put next into it:
|
|||
return container
|
||||
|
||||
|
||||
def test_movies_directed_by(container):
|
||||
@pytest.fixture
|
||||
def finder_mock(container):
|
||||
finder_mock = mock.Mock()
|
||||
finder_mock.find_all.return_value = [
|
||||
container.movie("The 33", 2015, "Patricia Riggen"),
|
||||
container.movie("The Jungle Book", 2016, "Jon Favreau"),
|
||||
]
|
||||
|
||||
return finder_mock
|
||||
|
||||
|
||||
def test_movies_directed_by(container, finder_mock):
|
||||
with container.finder.override(finder_mock):
|
||||
lister = container.lister()
|
||||
movies = lister.movies_directed_by("Jon Favreau")
|
||||
|
@ -956,13 +961,7 @@ and put next into it:
|
|||
assert movies[0].title == "The Jungle Book"
|
||||
|
||||
|
||||
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"),
|
||||
]
|
||||
|
||||
def test_movies_released_in(container, finder_mock):
|
||||
with container.finder.override(finder_mock):
|
||||
lister = container.lister()
|
||||
movies = lister.movies_released_in(2015)
|
||||
|
@ -995,9 +994,9 @@ You should see:
|
|||
movies/entities.py 7 1 86%
|
||||
movies/finders.py 26 13 50%
|
||||
movies/listers.py 8 0 100%
|
||||
movies/tests.py 23 0 100%
|
||||
movies/tests.py 24 0 100%
|
||||
------------------------------------------
|
||||
TOTAL 89 30 66%
|
||||
TOTAL 90 30 67%
|
||||
|
||||
.. note::
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ FastAPI example:
|
|||
|
||||
@app.api_route("/")
|
||||
@inject
|
||||
async def index(service: Service = Depends(Provide[Container.service])):
|
||||
async def index(service: Annotated[Service, Depends(Provide[Container.service])]):
|
||||
value = await service.process()
|
||||
return {"result": value}
|
||||
|
||||
|
|
|
@ -98,8 +98,9 @@ The output should be something like:
|
|||
|
||||
.. code-block::
|
||||
|
||||
platform darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
|
||||
plugins: asyncio-0.16.0, anyio-3.3.4, aiohttp-0.3.0, cov-3.0.0
|
||||
platform linux -- Python 3.12.3, pytest-8.3.2, pluggy-1.5.0
|
||||
plugins: cov-6.0.0, anyio-4.4.0, asyncio-0.24.0, aiohttp-1.0.5
|
||||
asyncio: mode=Mode.STRICT, default_loop_scope=None
|
||||
collected 3 items
|
||||
|
||||
giphynavigator/tests.py ... [100%]
|
||||
|
|
|
@ -3,11 +3,15 @@
|
|||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
|
||||
from giphynavigator.application import create_app
|
||||
from giphynavigator.giphy import GiphyClient
|
||||
|
||||
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def app():
|
||||
app = create_app()
|
||||
|
@ -15,9 +19,9 @@ def app():
|
|||
app.container.unwire()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client(app, aiohttp_client, loop):
|
||||
return loop.run_until_complete(aiohttp_client(app))
|
||||
@pytest_asyncio.fixture
|
||||
async def client(app, aiohttp_client):
|
||||
return await aiohttp_client(app)
|
||||
|
||||
|
||||
async def test_index(client, app):
|
||||
|
|
|
@ -2,4 +2,5 @@ dependency-injector
|
|||
aiohttp
|
||||
pyyaml
|
||||
pytest-aiohttp
|
||||
pytest-asyncio
|
||||
pytest-cov
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM python:3.10-buster
|
||||
FROM python:3.13-bookworm
|
||||
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
|
||||
|
|
|
@ -13,13 +13,13 @@ Build the Docker image:
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
docker-compose build
|
||||
docker compose build
|
||||
|
||||
Run the docker-compose environment:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker-compose up
|
||||
docker compose up
|
||||
|
||||
The output should be something like:
|
||||
|
||||
|
@ -59,15 +59,16 @@ To run the tests do:
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
docker-compose run --rm monitor py.test monitoringdaemon/tests.py --cov=monitoringdaemon
|
||||
docker compose run --rm monitor py.test monitoringdaemon/tests.py --cov=monitoringdaemon
|
||||
|
||||
The output should be something like:
|
||||
|
||||
.. code-block::
|
||||
|
||||
platform linux -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
|
||||
platform linux -- Python 3.13.1, pytest-8.3.4, pluggy-1.5.0
|
||||
rootdir: /code
|
||||
plugins: asyncio-0.16.0, cov-3.0.0
|
||||
plugins: cov-6.0.0, asyncio-0.24.0
|
||||
asyncio: mode=Mode.STRICT, default_loop_scope=None
|
||||
collected 2 items
|
||||
|
||||
monitoringdaemon/tests.py .. [100%]
|
||||
|
|
|
@ -61,7 +61,7 @@ async def test_example_monitor(container, caplog):
|
|||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_dispatcher(container, caplog, event_loop):
|
||||
async def test_dispatcher(container, caplog):
|
||||
caplog.set_level("INFO")
|
||||
|
||||
example_monitor_mock = mock.AsyncMock()
|
||||
|
@ -72,6 +72,7 @@ async def test_dispatcher(container, caplog, event_loop):
|
|||
httpbin_monitor=httpbin_monitor_mock,
|
||||
):
|
||||
dispatcher = container.dispatcher()
|
||||
event_loop = asyncio.get_running_loop()
|
||||
event_loop.create_task(dispatcher.start())
|
||||
await asyncio.sleep(0.1)
|
||||
dispatcher.stop()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM python:3.10-buster
|
||||
FROM python:3.13-bookworm
|
||||
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
|
||||
|
|
|
@ -12,13 +12,13 @@ Build the Docker image:
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
docker-compose build
|
||||
docker compose build
|
||||
|
||||
Run the docker-compose environment:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker-compose up
|
||||
docker compose up
|
||||
|
||||
The output should be something like:
|
||||
|
||||
|
@ -54,16 +54,16 @@ To run the tests do:
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
docker-compose run --rm example py.test fastapiredis/tests.py --cov=fastapiredis
|
||||
docker compose run --rm example py.test fastapiredis/tests.py --cov=fastapiredis
|
||||
|
||||
The output should be something like:
|
||||
|
||||
.. code-block::
|
||||
|
||||
platform linux -- Python 3.10.9, pytest-7.2.0, pluggy-1.0.0
|
||||
platform linux -- Python 3.13.1, pytest-8.3.4, pluggy-1.5.0
|
||||
rootdir: /code
|
||||
plugins: cov-4.0.0, asyncio-0.20.3
|
||||
collected 1 item
|
||||
plugins: cov-6.0.0, asyncio-0.24.0, anyio-4.7.0
|
||||
asyncio: mode=Mode.STRICT, default_loop_scope=None
|
||||
|
||||
fastapiredis/tests.py . [100%]
|
||||
|
||||
|
@ -77,4 +77,4 @@ The output should be something like:
|
|||
fastapiredis/services.py 7 3 57%
|
||||
fastapiredis/tests.py 18 0 100%
|
||||
-------------------------------------------------
|
||||
TOTAL 52 7 87%
|
||||
TOTAL 52 7 87%
|
||||
|
|
|
@ -1,18 +1,22 @@
|
|||
"""Application module."""
|
||||
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
from fastapi import FastAPI, Depends
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Depends, FastAPI
|
||||
|
||||
from dependency_injector.wiring import Provide, inject
|
||||
|
||||
from .containers import Container
|
||||
from .services import Service
|
||||
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.api_route("/")
|
||||
@inject
|
||||
async def index(service: Service = Depends(Provide[Container.service])):
|
||||
async def index(
|
||||
service: Annotated[Service, Depends(Provide[Container.service])]
|
||||
) -> dict[str, str]:
|
||||
value = await service.process()
|
||||
return {"result": value}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from typing import AsyncIterator
|
||||
|
||||
from aioredis import from_url, Redis
|
||||
from redis.asyncio import from_url, Redis
|
||||
|
||||
|
||||
async def init_redis_pool(host: str, password: str) -> AsyncIterator[Redis]:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
"""Services module."""
|
||||
|
||||
from aioredis import Redis
|
||||
from redis.asyncio import Redis
|
||||
|
||||
|
||||
class Service:
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
from httpx import AsyncClient
|
||||
from httpx import ASGITransport, AsyncClient
|
||||
|
||||
from .application import app, container
|
||||
from .services import Service
|
||||
|
@ -11,7 +11,10 @@ from .services import Service
|
|||
|
||||
@pytest.fixture
|
||||
def client(event_loop):
|
||||
client = AsyncClient(app=app, base_url="http://test")
|
||||
client = AsyncClient(
|
||||
transport=ASGITransport(app=app),
|
||||
base_url="http://test",
|
||||
)
|
||||
yield client
|
||||
event_loop.run_until_complete(client.aclose())
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
dependency-injector
|
||||
fastapi
|
||||
uvicorn
|
||||
aioredis
|
||||
redis>=4.2
|
||||
|
||||
# For testing:
|
||||
pytest
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
from fastapi import FastAPI, Depends
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Depends, FastAPI
|
||||
|
||||
from dependency_injector import containers, providers
|
||||
from dependency_injector.wiring import Provide, inject
|
||||
|
||||
|
@ -18,7 +21,9 @@ app = FastAPI()
|
|||
|
||||
@app.api_route("/")
|
||||
@inject
|
||||
async def index(service: Service = Depends(Provide[Container.service])):
|
||||
async def index(
|
||||
service: Annotated[Service, Depends(Provide[Container.service])]
|
||||
) -> dict[str, str]:
|
||||
result = await service.process()
|
||||
return {"result": result}
|
||||
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
from httpx import AsyncClient
|
||||
import pytest_asyncio
|
||||
from httpx import ASGITransport, AsyncClient
|
||||
|
||||
from fastapi_di_example import app, container, Service
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def client(event_loop):
|
||||
async with AsyncClient(app=app, base_url="http://test") as client:
|
||||
@pytest_asyncio.fixture
|
||||
async def client():
|
||||
async with AsyncClient(
|
||||
transport=ASGITransport(app=app),
|
||||
base_url="http://test",
|
||||
) as client:
|
||||
yield client
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM python:3.10-buster
|
||||
FROM python:3.13-bookworm
|
||||
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
ENV HOST=0.0.0.0
|
||||
|
|
|
@ -15,13 +15,13 @@ Build the Docker image:
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
docker-compose build
|
||||
docker compose build
|
||||
|
||||
Run the docker-compose environment:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker-compose up
|
||||
docker compose up
|
||||
|
||||
The output should be something like:
|
||||
|
||||
|
@ -67,15 +67,15 @@ To run the tests do:
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
docker-compose run --rm webapp py.test webapp/tests.py --cov=webapp
|
||||
docker compose run --rm webapp py.test webapp/tests.py --cov=webapp
|
||||
|
||||
The output should be something like:
|
||||
|
||||
.. code-block::
|
||||
|
||||
platform linux -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
|
||||
platform linux -- Python 3.13.1, pytest-8.3.4, pluggy-1.5.0
|
||||
rootdir: /code
|
||||
plugins: cov-3.0.0
|
||||
plugins: cov-6.0.0, anyio-4.7.0
|
||||
collected 7 items
|
||||
|
||||
webapp/tests.py ....... [100%]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
dependency-injector
|
||||
fastapi
|
||||
fastapi[standard]
|
||||
uvicorn
|
||||
pyyaml
|
||||
sqlalchemy
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
"""Endpoints module."""
|
||||
|
||||
from typing import Annotated
|
||||
|
||||
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 .services import UserService
|
||||
from .repositories import NotFoundError
|
||||
from .services import UserService
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
@ -13,7 +16,7 @@ router = APIRouter()
|
|||
@router.get("/users")
|
||||
@inject
|
||||
def get_list(
|
||||
user_service: UserService = Depends(Provide[Container.user_service]),
|
||||
user_service: Annotated[UserService, Depends(Provide[Container.user_service])],
|
||||
):
|
||||
return user_service.get_users()
|
||||
|
||||
|
@ -21,8 +24,8 @@ def get_list(
|
|||
@router.get("/users/{user_id}")
|
||||
@inject
|
||||
def get_by_id(
|
||||
user_id: int,
|
||||
user_service: UserService = Depends(Provide[Container.user_service]),
|
||||
user_id: int,
|
||||
user_service: Annotated[UserService, Depends(Provide[Container.user_service])],
|
||||
):
|
||||
try:
|
||||
return user_service.get_user_by_id(user_id)
|
||||
|
@ -33,7 +36,7 @@ def get_by_id(
|
|||
@router.post("/users", status_code=status.HTTP_201_CREATED)
|
||||
@inject
|
||||
def add(
|
||||
user_service: UserService = Depends(Provide[Container.user_service]),
|
||||
user_service: Annotated[UserService, Depends(Provide[Container.user_service])],
|
||||
):
|
||||
return user_service.create_user()
|
||||
|
||||
|
@ -41,9 +44,9 @@ def add(
|
|||
@router.delete("/users/{user_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||
@inject
|
||||
def remove(
|
||||
user_id: int,
|
||||
user_service: UserService = Depends(Provide[Container.user_service]),
|
||||
):
|
||||
user_id: int,
|
||||
user_service: Annotated[UserService, Depends(Provide[Container.user_service])],
|
||||
) -> Response:
|
||||
try:
|
||||
user_service.delete_user_by_id(user_id)
|
||||
except NotFoundError:
|
||||
|
|
|
@ -101,9 +101,9 @@ The output should be something like:
|
|||
|
||||
.. code-block::
|
||||
|
||||
platform darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
|
||||
plugins: asyncio-0.16.0, cov-3.0.0
|
||||
collected 3 items
|
||||
platform linux -- Python 3.12.3, pytest-8.3.2, pluggy-1.5.0
|
||||
plugins: cov-6.0.0, anyio-4.4.0, asyncio-0.24.0, aiohttp-1.0.5
|
||||
asyncio: mode=Mode.STRICT, default_loop_scope=None
|
||||
|
||||
giphynavigator/tests.py ... [100%]
|
||||
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
"""Endpoints module."""
|
||||
|
||||
from typing import Optional, List
|
||||
from typing import Annotated, List
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from pydantic import BaseModel
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
|
||||
from .services import SearchService
|
||||
from dependency_injector.wiring import Provide, inject
|
||||
|
||||
from .containers import Container
|
||||
from .services import SearchService
|
||||
|
||||
|
||||
class Gif(BaseModel):
|
||||
|
@ -26,11 +27,15 @@ router = APIRouter()
|
|||
@router.get("/", response_model=Response)
|
||||
@inject
|
||||
async def index(
|
||||
query: Optional[str] = None,
|
||||
limit: Optional[str] = None,
|
||||
default_query: str = Depends(Provide[Container.config.default.query]),
|
||||
default_limit: int = Depends(Provide[Container.config.default.limit.as_int()]),
|
||||
search_service: SearchService = Depends(Provide[Container.search_service]),
|
||||
default_query: Annotated[str, Depends(Provide[Container.config.default.query])],
|
||||
default_limit: Annotated[
|
||||
int, Depends(Provide[Container.config.default.limit.as_int()])
|
||||
],
|
||||
search_service: Annotated[
|
||||
SearchService, Depends(Provide[Container.search_service])
|
||||
],
|
||||
query: str | None = None,
|
||||
limit: int | None = None,
|
||||
):
|
||||
query = query or default_query
|
||||
limit = limit or default_limit
|
||||
|
|
|
@ -3,15 +3,19 @@
|
|||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
from httpx import AsyncClient
|
||||
import pytest_asyncio
|
||||
from httpx import ASGITransport, AsyncClient
|
||||
|
||||
from giphynavigator.application import app
|
||||
from giphynavigator.giphy import GiphyClient
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@pytest_asyncio.fixture
|
||||
async def client():
|
||||
async with AsyncClient(app=app, base_url="http://test") as client:
|
||||
async with AsyncClient(
|
||||
transport=ASGITransport(app=app),
|
||||
base_url="http://test",
|
||||
) as client:
|
||||
yield client
|
||||
|
||||
|
||||
|
|
|
@ -81,8 +81,9 @@ The output should be something like:
|
|||
|
||||
.. code-block::
|
||||
|
||||
platform darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
|
||||
plugins: cov-3.0.0, flask-1.2.0
|
||||
platform linux -- Python 3.12.3, pytest-8.3.2, pluggy-1.5.0
|
||||
plugins: cov-6.0.0, flask-1.3.0
|
||||
asyncio: mode=Mode.STRICT, default_loop_scope=None
|
||||
collected 2 items
|
||||
|
||||
githubnavigator/tests.py .. [100%]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
"""Application module."""
|
||||
|
||||
from flask import Flask
|
||||
from flask_bootstrap import Bootstrap
|
||||
from flask_bootstrap import Bootstrap4
|
||||
|
||||
from .containers import Container
|
||||
from .blueprints import example
|
||||
|
@ -15,7 +15,7 @@ def create_app() -> Flask:
|
|||
app.container = container
|
||||
app.register_blueprint(example.blueprint)
|
||||
|
||||
bootstrap = Bootstrap()
|
||||
bootstrap = Bootstrap4()
|
||||
bootstrap.init_app(app)
|
||||
|
||||
return app
|
||||
|
|
|
@ -81,8 +81,9 @@ The output should be something like:
|
|||
|
||||
.. code-block::
|
||||
|
||||
platform darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
|
||||
plugins: cov-3.0.0, flask-1.2.0
|
||||
platform linux -- Python 3.12.3, pytest-8.3.2, pluggy-1.5.0
|
||||
plugins: cov-6.0.0, flask-1.3.0
|
||||
asyncio: mode=Mode.STRICT, default_loop_scope=None
|
||||
collected 2 items
|
||||
|
||||
githubnavigator/tests.py .. [100%]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
"""Application module."""
|
||||
|
||||
from flask import Flask
|
||||
from flask_bootstrap import Bootstrap
|
||||
from flask_bootstrap import Bootstrap4
|
||||
|
||||
from .containers import Container
|
||||
from . import views
|
||||
|
@ -15,7 +15,7 @@ def create_app() -> Flask:
|
|||
app.container = container
|
||||
app.add_url_rule("/", "index", views.index)
|
||||
|
||||
bootstrap = Bootstrap()
|
||||
bootstrap = Bootstrap4()
|
||||
bootstrap.init_app(app)
|
||||
|
||||
return app
|
||||
|
|
|
@ -58,8 +58,8 @@ The output should be something like:
|
|||
|
||||
.. code-block::
|
||||
|
||||
platform darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
|
||||
plugins: cov-3.0.0
|
||||
platform linux -- Python 3.12.3, pytest-8.3.2, pluggy-1.5.0
|
||||
plugins: cov-6.0.0
|
||||
collected 2 items
|
||||
|
||||
movies/tests.py .. [100%]
|
||||
|
|
|
@ -26,13 +26,18 @@ def container():
|
|||
return container
|
||||
|
||||
|
||||
def test_movies_directed_by(container):
|
||||
@pytest.fixture
|
||||
def finder_mock(container):
|
||||
finder_mock = mock.Mock()
|
||||
finder_mock.find_all.return_value = [
|
||||
container.movie("The 33", 2015, "Patricia Riggen"),
|
||||
container.movie("The Jungle Book", 2016, "Jon Favreau"),
|
||||
]
|
||||
|
||||
return finder_mock
|
||||
|
||||
|
||||
def test_movies_directed_by(container, finder_mock):
|
||||
with container.finder.override(finder_mock):
|
||||
lister = container.lister()
|
||||
movies = lister.movies_directed_by("Jon Favreau")
|
||||
|
@ -41,13 +46,7 @@ def test_movies_directed_by(container):
|
|||
assert movies[0].title == "The Jungle Book"
|
||||
|
||||
|
||||
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"),
|
||||
]
|
||||
|
||||
def test_movies_released_in(container, finder_mock):
|
||||
with container.finder.override(finder_mock):
|
||||
lister = container.lister()
|
||||
movies = lister.movies_released_in(2015)
|
||||
|
|
|
@ -27,7 +27,7 @@ To run the application do:
|
|||
.. code-block:: bash
|
||||
|
||||
export GIPHY_API_KEY=wBJ2wZG7SRqfrU9nPgPiWvORmloDyuL0
|
||||
python -m giphynavigator
|
||||
sanic giphynavigator.application:create_app
|
||||
|
||||
The output should be something like:
|
||||
|
||||
|
@ -98,8 +98,9 @@ The output should be something like:
|
|||
|
||||
.. code-block::
|
||||
|
||||
platform darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
|
||||
plugins: sanic-1.9.1, anyio-3.3.4, cov-3.0.0
|
||||
platform linux -- Python 3.12.3, pytest-8.3.2, pluggy-1.5.0
|
||||
plugins: cov-6.0.0, anyio-4.4.0, asyncio-0.24.0
|
||||
asyncio: mode=Mode.STRICT, default_loop_scope=None
|
||||
collected 3 items
|
||||
|
||||
giphynavigator/tests.py ... [100%]
|
||||
|
|
|
@ -8,6 +8,8 @@ from sanic import Sanic
|
|||
from giphynavigator.application import create_app
|
||||
from giphynavigator.giphy import GiphyClient
|
||||
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def app():
|
||||
|
@ -17,12 +19,7 @@ def app():
|
|||
app.ctx.container.unwire()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def test_client(loop, app, sanic_client):
|
||||
return loop.run_until_complete(sanic_client(app))
|
||||
|
||||
|
||||
async def test_index(app, test_client):
|
||||
async def test_index(app):
|
||||
giphy_client_mock = mock.AsyncMock(spec=GiphyClient)
|
||||
giphy_client_mock.search.return_value = {
|
||||
"data": [
|
||||
|
@ -32,7 +29,7 @@ async def test_index(app, test_client):
|
|||
}
|
||||
|
||||
with app.ctx.container.giphy_client.override(giphy_client_mock):
|
||||
response = await test_client.get(
|
||||
_, response = await app.asgi_client.get(
|
||||
"/",
|
||||
params={
|
||||
"query": "test",
|
||||
|
@ -41,7 +38,7 @@ async def test_index(app, test_client):
|
|||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
data = response.json
|
||||
assert data == {
|
||||
"query": "test",
|
||||
"limit": 10,
|
||||
|
@ -52,30 +49,30 @@ async def test_index(app, test_client):
|
|||
}
|
||||
|
||||
|
||||
async def test_index_no_data(app, test_client):
|
||||
async def test_index_no_data(app):
|
||||
giphy_client_mock = mock.AsyncMock(spec=GiphyClient)
|
||||
giphy_client_mock.search.return_value = {
|
||||
"data": [],
|
||||
}
|
||||
|
||||
with app.ctx.container.giphy_client.override(giphy_client_mock):
|
||||
response = await test_client.get("/")
|
||||
_, response = await app.asgi_client.get("/")
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
data = response.json
|
||||
assert data["gifs"] == []
|
||||
|
||||
|
||||
async def test_index_default_params(app, test_client):
|
||||
async def test_index_default_params(app):
|
||||
giphy_client_mock = mock.AsyncMock(spec=GiphyClient)
|
||||
giphy_client_mock.search.return_value = {
|
||||
"data": [],
|
||||
}
|
||||
|
||||
with app.ctx.container.giphy_client.override(giphy_client_mock):
|
||||
response = await test_client.get("/")
|
||||
_, response = await app.asgi_client.get("/")
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
data = response.json
|
||||
assert data["query"] == app.ctx.container.config.default.query()
|
||||
assert data["limit"] == app.ctx.container.config.default.limit()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
dependency-injector
|
||||
sanic<=21.6
|
||||
sanic
|
||||
sanic-testing
|
||||
aiohttp
|
||||
pyyaml
|
||||
pytest-sanic
|
||||
pytest-cov
|
||||
|
|
39
examples/miniapps/starlette-lifespan/README.rst
Normal file
39
examples/miniapps/starlette-lifespan/README.rst
Normal file
|
@ -0,0 +1,39 @@
|
|||
Integration With Starlette-based Frameworks
|
||||
===========================================
|
||||
|
||||
This is a `Starlette <https://www.starlette.io/>`_ +
|
||||
`Dependency Injector <https://python-dependency-injector.ets-labs.org/>`_ example application
|
||||
utilizing `lifespan API <https://www.starlette.io/lifespan/>`_.
|
||||
|
||||
.. note::
|
||||
|
||||
Pretty much `any framework built on top of Starlette <https://www.starlette.io/third-party-packages/#frameworks>`_
|
||||
supports this feature (`FastAPI <https://fastapi.tiangolo.com/advanced/events/#lifespan>`_,
|
||||
`Xpresso <https://xpresso-api.dev/latest/tutorial/lifespan/>`_, etc...).
|
||||
|
||||
Run
|
||||
---
|
||||
|
||||
Create virtual environment:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python -m venv env
|
||||
. env/bin/activate
|
||||
|
||||
Install requirements:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pip install -r requirements.txt
|
||||
|
||||
To run the application do:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python example.py
|
||||
# or (logging won't be configured):
|
||||
uvicorn --factory example:container.app
|
||||
|
||||
After that visit http://127.0.0.1:8000/ in your browser or use CLI command (``curl``, ``httpie``,
|
||||
etc).
|
59
examples/miniapps/starlette-lifespan/example.py
Executable file
59
examples/miniapps/starlette-lifespan/example.py
Executable file
|
@ -0,0 +1,59 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
from logging import basicConfig, getLogger
|
||||
|
||||
from dependency_injector.containers import DeclarativeContainer
|
||||
from dependency_injector.ext.starlette import Lifespan
|
||||
from dependency_injector.providers import Factory, Resource, Self, Singleton
|
||||
from starlette.applications import Starlette
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import JSONResponse
|
||||
from starlette.routing import Route
|
||||
|
||||
count = 0
|
||||
|
||||
|
||||
def init():
|
||||
log = getLogger(__name__)
|
||||
log.info("Inittializing resources")
|
||||
yield
|
||||
log.info("Cleaning up resources")
|
||||
|
||||
|
||||
async def homepage(request: Request) -> JSONResponse:
|
||||
global count
|
||||
response = JSONResponse({"hello": "world", "count": count})
|
||||
count += 1
|
||||
return response
|
||||
|
||||
|
||||
class Container(DeclarativeContainer):
|
||||
__self__ = Self()
|
||||
lifespan = Singleton(Lifespan, __self__)
|
||||
logging = Resource(
|
||||
basicConfig,
|
||||
level="DEBUG",
|
||||
datefmt="%Y-%m-%d %H:%M",
|
||||
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
|
||||
)
|
||||
init = Resource(init)
|
||||
app = Factory(
|
||||
Starlette,
|
||||
debug=True,
|
||||
lifespan=lifespan,
|
||||
routes=[Route("/", homepage)],
|
||||
)
|
||||
|
||||
|
||||
container = Container()
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
|
||||
uvicorn.run(
|
||||
container.app,
|
||||
factory=True,
|
||||
# NOTE: `None` prevents uvicorn from configuring logging, which is
|
||||
# impossible via CLI
|
||||
log_config=None,
|
||||
)
|
3
examples/miniapps/starlette-lifespan/requirements.txt
Normal file
3
examples/miniapps/starlette-lifespan/requirements.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
dependency-injector
|
||||
starlette
|
||||
uvicorn
|
|
@ -3,7 +3,7 @@
|
|||
import os
|
||||
|
||||
from dependency_injector import containers, providers
|
||||
from pydantic import BaseSettings, Field
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
|
||||
# Emulate environment variables
|
||||
os.environ["AWS_ACCESS_KEY_ID"] = "KEY"
|
||||
|
@ -11,15 +11,16 @@ os.environ["AWS_SECRET_ACCESS_KEY"] = "SECRET"
|
|||
|
||||
|
||||
class AwsSettings(BaseSettings):
|
||||
model_config = SettingsConfigDict(env_prefix="aws_")
|
||||
|
||||
access_key_id: str = Field(env="aws_access_key_id")
|
||||
secret_access_key: str = Field(env="aws_secret_access_key")
|
||||
access_key_id: str
|
||||
secret_access_key: str
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
|
||||
aws: AwsSettings = AwsSettings()
|
||||
optional: str = Field(default="default_value")
|
||||
optional: str = "default_value"
|
||||
|
||||
|
||||
class Container(containers.DeclarativeContainer):
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import os
|
||||
|
||||
from dependency_injector import containers, providers
|
||||
from pydantic import BaseSettings, Field
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
|
||||
# Emulate environment variables
|
||||
os.environ["AWS_ACCESS_KEY_ID"] = "KEY"
|
||||
|
@ -11,15 +11,16 @@ os.environ["AWS_SECRET_ACCESS_KEY"] = "SECRET"
|
|||
|
||||
|
||||
class AwsSettings(BaseSettings):
|
||||
model_config = SettingsConfigDict(env_prefix="aws_")
|
||||
|
||||
access_key_id: str = Field(env="aws_access_key_id")
|
||||
secret_access_key: str = Field(env="aws_secret_access_key")
|
||||
access_key_id: str
|
||||
secret_access_key: str
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
|
||||
aws: AwsSettings = AwsSettings()
|
||||
optional: str = Field(default="default_value")
|
||||
optional: str = "default_value"
|
||||
|
||||
|
||||
class Container(containers.DeclarativeContainer):
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
from dependency_injector import containers, providers
|
||||
from dependency_injector.wiring import Provide, inject
|
||||
from typing import Annotated
|
||||
|
||||
|
||||
class Service:
|
||||
...
|
||||
class Service: ...
|
||||
|
||||
|
||||
class Container(containers.DeclarativeContainer):
|
||||
|
@ -13,9 +13,16 @@ class Container(containers.DeclarativeContainer):
|
|||
service = providers.Factory(Service)
|
||||
|
||||
|
||||
# You can place marker on parameter default value
|
||||
@inject
|
||||
def main(service: Service = Provide[Container.service]) -> None:
|
||||
...
|
||||
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__":
|
||||
|
|
101
pyproject.toml
Normal file
101
pyproject.toml
Normal file
|
@ -0,0 +1,101 @@
|
|||
[build-system]
|
||||
requires = ["setuptools", "Cython"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "dependency-injector"
|
||||
authors = [
|
||||
{name = "Roman Mogylatov", email = "rmogilatov@gmail.com"},
|
||||
]
|
||||
maintainers = [
|
||||
{name = "Roman Mogylatov", email = "rmogilatov@gmail.com"},
|
||||
]
|
||||
description = "Dependency injection framework for Python"
|
||||
readme = {file = "README.rst", content-type = "text/x-rst"}
|
||||
license = {file = "LICENSE.rst", content-type = "text/x-rst"}
|
||||
requires-python = ">=3.7"
|
||||
keywords = [
|
||||
"Dependency injection",
|
||||
"DI",
|
||||
"Inversion of Control",
|
||||
"IoC",
|
||||
"Factory",
|
||||
"Singleton",
|
||||
"Design patterns",
|
||||
"Flask",
|
||||
]
|
||||
classifiers = [
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
"Intended Audience :: Developers",
|
||||
"License :: OSI Approved :: BSD License",
|
||||
"Operating System :: OS Independent",
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.7",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Programming Language :: Python :: 3.13",
|
||||
"Programming Language :: Python :: Implementation :: CPython",
|
||||
"Programming Language :: Python :: Implementation :: PyPy",
|
||||
"Framework :: AsyncIO",
|
||||
"Framework :: Bottle",
|
||||
"Framework :: Django",
|
||||
"Framework :: Flask",
|
||||
"Framework :: Pylons",
|
||||
"Framework :: Pyramid",
|
||||
"Framework :: Pytest",
|
||||
"Framework :: TurboGears",
|
||||
"Topic :: Software Development",
|
||||
"Topic :: Software Development :: Libraries",
|
||||
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||
]
|
||||
dynamic = ["version"]
|
||||
|
||||
[project.optional-dependencies]
|
||||
yaml = ["pyyaml"]
|
||||
pydantic = ["pydantic"]
|
||||
pydantic2 = ["pydantic-settings"]
|
||||
flask = ["flask"]
|
||||
aiohttp = ["aiohttp"]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/ets-labs/python-dependency-injector"
|
||||
Documentation = "https://python-dependency-injector.ets-labs.org/"
|
||||
Download = "https://pypi.python.org/pypi/dependency_injector"
|
||||
|
||||
[tool.setuptools]
|
||||
package-dir = {"" = "src"}
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
where = ["src"]
|
||||
|
||||
[tool.setuptools.package-data]
|
||||
dependency_injector = ["*.pxd", "*.pyi", "py.typed"]
|
||||
|
||||
[tool.setuptools.dynamic]
|
||||
version = {attr = "dependency_injector.__version__"}
|
||||
|
||||
[tool.coverage.run]
|
||||
branch = false
|
||||
relative_files = true
|
||||
source_pkgs = ["dependency_injector"]
|
||||
plugins = ["Cython.Coverage"]
|
||||
|
||||
[tool.coverage.html]
|
||||
directory = "reports/unittests/"
|
||||
|
||||
[tool.coverage.report]
|
||||
show_missing = true
|
||||
|
||||
[tool.isort]
|
||||
profile = "black"
|
||||
|
||||
[tool.pylint.main]
|
||||
ignore = ["tests"]
|
||||
|
||||
[tool.pylint.design]
|
||||
min-public-methods = 0
|
||||
max-public-methods = 30
|
|
@ -5,6 +5,7 @@ pytest-asyncio
|
|||
tox
|
||||
coverage
|
||||
flake8
|
||||
flake8-pyproject
|
||||
pydocstyle
|
||||
sphinx_autobuild
|
||||
pip
|
||||
|
@ -17,5 +18,6 @@ numpy
|
|||
scipy
|
||||
boto3
|
||||
mypy_boto3_s3
|
||||
typing_extensions
|
||||
|
||||
-r requirements-ext.txt
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
flask==2.1.3
|
||||
werkzeug==2.2.2
|
||||
flask
|
||||
werkzeug
|
||||
aiohttp
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
six>=1.7.0,<=1.16.0
|
|
@ -2,6 +2,7 @@
|
|||
max_line_length = 120
|
||||
max_complexity = 10
|
||||
exclude = types.py
|
||||
extend-ignore = E203,E701
|
||||
per-file-ignores =
|
||||
examples/demo/*: F841
|
||||
examples/containers/traverse.py: E501
|
||||
|
|
152
setup.py
152
setup.py
|
@ -1,128 +1,42 @@
|
|||
"""`Dependency injector` setup script."""
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
from setuptools import setup, Extension
|
||||
from Cython.Build import cythonize
|
||||
from Cython.Compiler import Options
|
||||
from setuptools import Extension, setup
|
||||
|
||||
|
||||
def _open(filename):
|
||||
if sys.version_info[0] == 2:
|
||||
return open(filename)
|
||||
return open(filename, encoding="utf-8")
|
||||
|
||||
|
||||
# Defining setup variables:
|
||||
defined_macros = dict()
|
||||
defined_macros["CYTHON_CLINE_IN_TRACEBACK"] = 0
|
||||
|
||||
# Getting description:
|
||||
with _open("README.rst") as readme_file:
|
||||
description = readme_file.read()
|
||||
|
||||
# Getting requirements:
|
||||
with _open("requirements.txt") as requirements_file:
|
||||
requirements = requirements_file.readlines()
|
||||
|
||||
# Getting version:
|
||||
with _open("src/dependency_injector/__init__.py") as init_file:
|
||||
version = re.search("__version__ = \"(.*?)\"", init_file.read()).group(1)
|
||||
debug = os.environ.get("DEPENDENCY_INJECTOR_DEBUG_MODE") == "1"
|
||||
defined_macros = []
|
||||
compiler_directives = {
|
||||
"language_level": 3,
|
||||
"profile": debug,
|
||||
"linetrace": debug,
|
||||
}
|
||||
Options.annotate = debug
|
||||
|
||||
# Adding debug options:
|
||||
if os.environ.get("DEPENDENCY_INJECTOR_DEBUG_MODE") == "1":
|
||||
defined_macros["CYTHON_TRACE"] = 1
|
||||
defined_macros["CYTHON_TRACE_NOGIL"] = 1
|
||||
defined_macros["CYTHON_CLINE_IN_TRACEBACK"] = 1
|
||||
if debug:
|
||||
defined_macros.extend(
|
||||
[
|
||||
("CYTHON_TRACE", "1"),
|
||||
("CYTHON_TRACE_NOGIL", "1"),
|
||||
("CYTHON_CLINE_IN_TRACEBACK", "1"),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
setup(name="dependency-injector",
|
||||
version=version,
|
||||
description="Dependency injection framework for Python",
|
||||
long_description=description,
|
||||
author="Roman Mogylatov",
|
||||
author_email="rmogilatov@gmail.com",
|
||||
maintainer="Roman Mogylatov",
|
||||
maintainer_email="rmogilatov@gmail.com",
|
||||
url="https://github.com/ets-labs/python-dependency-injector",
|
||||
download_url="https://pypi.python.org/pypi/dependency_injector",
|
||||
packages=[
|
||||
"dependency_injector",
|
||||
"dependency_injector.ext",
|
||||
],
|
||||
package_dir={
|
||||
"": "src",
|
||||
},
|
||||
package_data={
|
||||
"dependency_injector": ["*.pxd", "*.pyi", "py.typed"],
|
||||
},
|
||||
ext_modules=[
|
||||
Extension("dependency_injector.containers",
|
||||
["src/dependency_injector/containers.c"],
|
||||
define_macros=list(defined_macros.items()),
|
||||
extra_compile_args=["-O2"]),
|
||||
Extension("dependency_injector.providers",
|
||||
["src/dependency_injector/providers.c"],
|
||||
define_macros=list(defined_macros.items()),
|
||||
extra_compile_args=["-O2"]),
|
||||
Extension("dependency_injector._cwiring",
|
||||
["src/dependency_injector/_cwiring.c"],
|
||||
define_macros=list(defined_macros.items()),
|
||||
extra_compile_args=["-O2"]),
|
||||
],
|
||||
install_requires=requirements,
|
||||
extras_require={
|
||||
"yaml": [
|
||||
"pyyaml",
|
||||
],
|
||||
"pydantic": [
|
||||
"pydantic",
|
||||
],
|
||||
"flask": [
|
||||
"flask",
|
||||
],
|
||||
"aiohttp": [
|
||||
"aiohttp",
|
||||
],
|
||||
},
|
||||
zip_safe=True,
|
||||
license="BSD New",
|
||||
platforms=["any"],
|
||||
keywords=[
|
||||
"Dependency injection",
|
||||
"DI",
|
||||
"Inversion of Control",
|
||||
"IoC",
|
||||
"Factory",
|
||||
"Singleton",
|
||||
"Design patterns",
|
||||
"Flask",
|
||||
],
|
||||
classifiers=[
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
"Intended Audience :: Developers",
|
||||
"License :: OSI Approved :: BSD License",
|
||||
"Operating System :: OS Independent",
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.7",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Programming Language :: Python :: 3.13",
|
||||
"Programming Language :: Python :: Implementation :: CPython",
|
||||
"Programming Language :: Python :: Implementation :: PyPy",
|
||||
"Framework :: AsyncIO",
|
||||
"Framework :: Bottle",
|
||||
"Framework :: Django",
|
||||
"Framework :: Flask",
|
||||
"Framework :: Pylons",
|
||||
"Framework :: Pyramid",
|
||||
"Framework :: Pytest",
|
||||
"Framework :: TurboGears",
|
||||
"Topic :: Software Development",
|
||||
"Topic :: Software Development :: Libraries",
|
||||
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||
])
|
||||
setup(
|
||||
ext_modules=cythonize(
|
||||
[
|
||||
Extension(
|
||||
"*",
|
||||
["src/**/*.pyx"],
|
||||
define_macros=defined_macros,
|
||||
),
|
||||
],
|
||||
annotate=debug,
|
||||
show_all_warnings=True,
|
||||
compiler_directives=compiler_directives,
|
||||
),
|
||||
)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
"""Top-level package."""
|
||||
|
||||
__version__ = "4.43.0"
|
||||
__version__ = "4.46.0"
|
||||
"""Version number.
|
||||
|
||||
:type: str
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2,44 +2,39 @@
|
|||
|
||||
import asyncio
|
||||
import collections.abc
|
||||
import functools
|
||||
import inspect
|
||||
import types
|
||||
|
||||
from . import providers
|
||||
from .wiring import _Marker, PatchedCallable
|
||||
from .wiring import _Marker
|
||||
|
||||
from .providers cimport Provider
|
||||
from .providers cimport Provider, Resource
|
||||
|
||||
|
||||
def _get_sync_patched(fn, patched: PatchedCallable):
|
||||
@functools.wraps(fn)
|
||||
def _patched(*args, **kwargs):
|
||||
cdef object result
|
||||
cdef dict to_inject
|
||||
cdef object arg_key
|
||||
cdef Provider provider
|
||||
def _sync_inject(object fn, tuple args, dict kwargs, dict injections, dict closings, /):
|
||||
cdef object result
|
||||
cdef dict to_inject
|
||||
cdef object arg_key
|
||||
cdef Provider provider
|
||||
|
||||
to_inject = kwargs.copy()
|
||||
for arg_key, provider in patched.injections.items():
|
||||
if arg_key not in kwargs or isinstance(kwargs[arg_key], _Marker):
|
||||
to_inject[arg_key] = provider()
|
||||
to_inject = kwargs.copy()
|
||||
for arg_key, provider in injections.items():
|
||||
if arg_key not in kwargs or isinstance(kwargs[arg_key], _Marker):
|
||||
to_inject[arg_key] = provider()
|
||||
|
||||
result = fn(*args, **to_inject)
|
||||
result = fn(*args, **to_inject)
|
||||
|
||||
if patched.closing:
|
||||
for arg_key, provider in patched.closing.items():
|
||||
if arg_key in kwargs and not isinstance(kwargs[arg_key], _Marker):
|
||||
continue
|
||||
if not isinstance(provider, providers.Resource):
|
||||
continue
|
||||
provider.shutdown()
|
||||
if closings:
|
||||
for arg_key, provider in closings.items():
|
||||
if arg_key in kwargs and not isinstance(kwargs[arg_key], _Marker):
|
||||
continue
|
||||
if not isinstance(provider, Resource):
|
||||
continue
|
||||
provider.shutdown()
|
||||
|
||||
return result
|
||||
return _patched
|
||||
return result
|
||||
|
||||
|
||||
async def _async_inject(object fn, tuple args, dict kwargs, dict injections, dict closings):
|
||||
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 = []
|
||||
|
@ -69,7 +64,7 @@ async def _async_inject(object fn, tuple args, dict kwargs, dict injections, dic
|
|||
for arg_key, provider in closings.items():
|
||||
if arg_key in kwargs and isinstance(kwargs[arg_key], _Marker):
|
||||
continue
|
||||
if not isinstance(provider, providers.Resource):
|
||||
if not isinstance(provider, Resource):
|
||||
continue
|
||||
shutdown = provider.shutdown()
|
||||
if _isawaitable(shutdown):
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -19,21 +19,24 @@ from typing import (
|
|||
|
||||
from .providers import Provider, Self, ProviderParent
|
||||
|
||||
|
||||
C_Base = TypeVar("C_Base", bound="Container")
|
||||
C = TypeVar("C", bound="DeclarativeContainer")
|
||||
C_Overriding = TypeVar("C_Overriding", bound="DeclarativeContainer")
|
||||
T = TypeVar("T")
|
||||
TT = TypeVar("TT")
|
||||
|
||||
|
||||
class WiringConfiguration:
|
||||
modules: List[Any]
|
||||
packages: List[Any]
|
||||
from_package: Optional[str]
|
||||
auto_wire: bool
|
||||
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,
|
||||
) -> None: ...
|
||||
|
||||
class Container:
|
||||
provider_type: Type[Provider] = Provider
|
||||
|
@ -51,11 +54,18 @@ class Container:
|
|||
def set_providers(self, **providers: Provider): ...
|
||||
def set_provider(self, name: str, provider: Provider) -> None: ...
|
||||
def override(self, overriding: Union[Container, Type[Container]]) -> None: ...
|
||||
def override_providers(self, **overriding_providers: Union[Provider, Any]) -> ProvidersOverridingContext[C_Base]: ...
|
||||
def override_providers(
|
||||
self, **overriding_providers: Union[Provider, Any]
|
||||
) -> ProvidersOverridingContext[C_Base]: ...
|
||||
def reset_last_overriding(self) -> None: ...
|
||||
def reset_override(self) -> None: ...
|
||||
def is_auto_wiring_enabled(self) -> bool: ...
|
||||
def wire(self, modules: Optional[Iterable[Any]] = None, packages: Optional[Iterable[Any]] = None, from_package: Optional[str] = None) -> None: ...
|
||||
def wire(
|
||||
self,
|
||||
modules: Optional[Iterable[Any]] = None,
|
||||
packages: Optional[Iterable[Any]] = None,
|
||||
from_package: Optional[str] = None,
|
||||
) -> None: ...
|
||||
def unwire(self) -> None: ...
|
||||
def init_resources(self) -> Optional[Awaitable]: ...
|
||||
def shutdown_resources(self) -> Optional[Awaitable]: ...
|
||||
|
@ -64,7 +74,9 @@ class Container:
|
|||
def reset_singletons(self) -> SingletonResetContext[C_Base]: ...
|
||||
def check_dependencies(self) -> None: ...
|
||||
def from_schema(self, schema: Dict[Any, Any]) -> None: ...
|
||||
def from_yaml_schema(self, filepath: Union[Path, str], loader: Optional[Any]=None) -> None: ...
|
||||
def from_yaml_schema(
|
||||
self, filepath: Union[Path, str], loader: Optional[Any] = None
|
||||
) -> None: ...
|
||||
def from_json_schema(self, filepath: Union[Path, str]) -> None: ...
|
||||
@overload
|
||||
def resolve_provider_name(self, provider: Provider) -> str: ...
|
||||
|
@ -82,10 +94,8 @@ class Container:
|
|||
@overload
|
||||
def traverse(cls, types: Optional[Iterable[Type[TT]]] = None) -> Iterator[TT]: ...
|
||||
|
||||
|
||||
class DynamicContainer(Container): ...
|
||||
|
||||
|
||||
class DeclarativeContainer(Container):
|
||||
cls_providers: ClassVar[Dict[str, Provider]]
|
||||
inherited_providers: ClassVar[Dict[str, Provider]]
|
||||
|
@ -93,29 +103,28 @@ class DeclarativeContainer(Container):
|
|||
@classmethod
|
||||
def override(cls, overriding: Union[Container, Type[Container]]) -> None: ...
|
||||
@classmethod
|
||||
def override_providers(cls, **overriding_providers: Union[Provider, Any]) -> ProvidersOverridingContext[C_Base]: ...
|
||||
def override_providers(
|
||||
cls, **overriding_providers: Union[Provider, Any]
|
||||
) -> ProvidersOverridingContext[C_Base]: ...
|
||||
@classmethod
|
||||
def reset_last_overriding(cls) -> None: ...
|
||||
@classmethod
|
||||
def reset_override(cls) -> None: ...
|
||||
|
||||
|
||||
class ProvidersOverridingContext(Generic[T]):
|
||||
def __init__(self, container: T, overridden_providers: Iterable[Union[Provider, Any]]) -> None: ...
|
||||
def __init__(
|
||||
self, container: T, overridden_providers: Iterable[Union[Provider, Any]]
|
||||
) -> None: ...
|
||||
def __enter__(self) -> T: ...
|
||||
def __exit__(self, *_: Any) -> None: ...
|
||||
|
||||
|
||||
class SingletonResetContext(Generic[T]):
|
||||
def __init__(self, container: T): ...
|
||||
def __enter__(self) -> T: ...
|
||||
def __exit__(self, *_: Any) -> None: ...
|
||||
|
||||
|
||||
def override(container: Type[C]) -> _Callable[[Type[C_Overriding]], Type[C_Overriding]]: ...
|
||||
|
||||
|
||||
def override(
|
||||
container: Type[C],
|
||||
) -> _Callable[[Type[C_Overriding]], Type[C_Overriding]]: ...
|
||||
def copy(container: Type[C]) -> _Callable[[Type[C_Overriding]], Type[C_Overriding]]: ...
|
||||
|
||||
|
||||
def is_container(instance: Any) -> bool: ...
|
||||
|
|
|
@ -1,45 +1,20 @@
|
|||
"""Containers module."""
|
||||
|
||||
import asyncio
|
||||
import contextlib
|
||||
import copy as copy_module
|
||||
import json
|
||||
import sys
|
||||
import importlib
|
||||
import inspect
|
||||
import warnings
|
||||
|
||||
try:
|
||||
import asyncio
|
||||
except ImportError:
|
||||
asyncio = None
|
||||
|
||||
try:
|
||||
import yaml
|
||||
except ImportError:
|
||||
yaml = None
|
||||
|
||||
import six
|
||||
|
||||
from . import providers, errors
|
||||
from .providers cimport __is_future_or_coroutine
|
||||
|
||||
|
||||
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,
|
||||
)
|
||||
from .wiring import wire, unwire
|
||||
|
||||
|
||||
class WiringConfiguration:
|
||||
|
@ -55,7 +30,7 @@ class WiringConfiguration:
|
|||
return self.__class__(self.modules, self.packages, self.from_package, self.auto_wire)
|
||||
|
||||
|
||||
class Container(object):
|
||||
class Container:
|
||||
"""Abstract container."""
|
||||
|
||||
|
||||
|
@ -201,7 +176,7 @@ class DynamicContainer(Container):
|
|||
|
||||
:rtype: None
|
||||
"""
|
||||
for name, provider in six.iteritems(providers):
|
||||
for name, provider in providers.items():
|
||||
setattr(self, name, provider)
|
||||
|
||||
def set_provider(self, name, provider):
|
||||
|
@ -234,7 +209,7 @@ class DynamicContainer(Container):
|
|||
|
||||
self.overridden += (overriding,)
|
||||
|
||||
for name, provider in six.iteritems(overriding.providers):
|
||||
for name, provider in overriding.providers.items():
|
||||
try:
|
||||
getattr(self, name).override(provider)
|
||||
except AttributeError:
|
||||
|
@ -250,7 +225,7 @@ class DynamicContainer(Container):
|
|||
:rtype: None
|
||||
"""
|
||||
overridden_providers = []
|
||||
for name, overriding_provider in six.iteritems(overriding_providers):
|
||||
for name, overriding_provider in overriding_providers.items():
|
||||
container_provider = getattr(self, name)
|
||||
container_provider.override(overriding_provider)
|
||||
overridden_providers.append(container_provider)
|
||||
|
@ -266,7 +241,7 @@ class DynamicContainer(Container):
|
|||
|
||||
self.overridden = self.overridden[:-1]
|
||||
|
||||
for provider in six.itervalues(self.providers):
|
||||
for provider in self.providers.values():
|
||||
provider.reset_last_overriding()
|
||||
|
||||
def reset_override(self):
|
||||
|
@ -276,7 +251,7 @@ class DynamicContainer(Container):
|
|||
"""
|
||||
self.overridden = tuple()
|
||||
|
||||
for provider in six.itervalues(self.providers):
|
||||
for provider in self.providers.values():
|
||||
provider.reset_override()
|
||||
|
||||
def is_auto_wiring_enabled(self):
|
||||
|
@ -495,13 +470,13 @@ class DeclarativeContainerMetaClass(type):
|
|||
|
||||
containers = {
|
||||
name: container
|
||||
for name, container in six.iteritems(attributes)
|
||||
for name, container in attributes.items()
|
||||
if is_container(container)
|
||||
}
|
||||
|
||||
cls_providers = {
|
||||
name: provider
|
||||
for name, provider in six.iteritems(attributes)
|
||||
for name, provider in attributes.items()
|
||||
if isinstance(provider, providers.Provider) and not isinstance(provider, providers.Self)
|
||||
}
|
||||
|
||||
|
@ -509,7 +484,7 @@ class DeclarativeContainerMetaClass(type):
|
|||
name: provider
|
||||
for base in bases
|
||||
if is_container(base) and base is not DynamicContainer
|
||||
for name, provider in six.iteritems(base.providers)
|
||||
for name, provider in base.providers.items()
|
||||
}
|
||||
|
||||
all_providers = {}
|
||||
|
@ -536,10 +511,10 @@ class DeclarativeContainerMetaClass(type):
|
|||
self.set_container(cls)
|
||||
cls.__self__ = self
|
||||
|
||||
for provider in six.itervalues(cls.providers):
|
||||
for provider in cls.providers.values():
|
||||
_check_provider_type(cls, provider)
|
||||
|
||||
for provider in six.itervalues(cls.cls_providers):
|
||||
for provider in cls.cls_providers.values():
|
||||
if isinstance(provider, providers.CHILD_PROVIDERS):
|
||||
provider.assign_parent(cls)
|
||||
|
||||
|
@ -641,8 +616,7 @@ class DeclarativeContainerMetaClass(type):
|
|||
return self
|
||||
|
||||
|
||||
@six.add_metaclass(DeclarativeContainerMetaClass)
|
||||
class DeclarativeContainer(Container):
|
||||
class DeclarativeContainer(Container, metaclass=DeclarativeContainerMetaClass):
|
||||
"""Declarative inversion of control container.
|
||||
|
||||
.. code-block:: python
|
||||
|
@ -767,7 +741,7 @@ class DeclarativeContainer(Container):
|
|||
|
||||
cls.overridden += (overriding,)
|
||||
|
||||
for name, provider in six.iteritems(overriding.cls_providers):
|
||||
for name, provider in overriding.cls_providers.items():
|
||||
try:
|
||||
getattr(cls, name).override(provider)
|
||||
except AttributeError:
|
||||
|
@ -784,7 +758,7 @@ class DeclarativeContainer(Container):
|
|||
|
||||
cls.overridden = cls.overridden[:-1]
|
||||
|
||||
for provider in six.itervalues(cls.providers):
|
||||
for provider in cls.providers.values():
|
||||
provider.reset_last_overriding()
|
||||
|
||||
@classmethod
|
||||
|
@ -795,7 +769,7 @@ class DeclarativeContainer(Container):
|
|||
"""
|
||||
cls.overridden = tuple()
|
||||
|
||||
for provider in six.itervalues(cls.providers):
|
||||
for provider in cls.providers.values():
|
||||
provider.reset_override()
|
||||
|
||||
|
||||
|
@ -858,7 +832,7 @@ def copy(object base_container):
|
|||
"""
|
||||
def _get_memo_for_matching_names(new_providers, base_providers):
|
||||
memo = {}
|
||||
for new_provider_name, new_provider in six.iteritems(new_providers):
|
||||
for new_provider_name, new_provider in new_providers.items():
|
||||
if new_provider_name not in base_providers:
|
||||
continue
|
||||
source_provider = base_providers[new_provider_name]
|
||||
|
@ -877,7 +851,7 @@ def copy(object base_container):
|
|||
new_providers.update(providers.deepcopy(base_container.providers, memo))
|
||||
new_providers.update(providers.deepcopy(new_container.cls_providers, memo))
|
||||
|
||||
for name, provider in six.iteritems(new_providers):
|
||||
for name, provider in new_providers.items():
|
||||
setattr(new_container, name, provider)
|
||||
return new_container
|
||||
|
||||
|
|
|
@ -10,3 +10,24 @@ class Error(Exception):
|
|||
|
||||
class NoSuchProviderError(Error, AttributeError):
|
||||
"""Error that is raised when provider lookup is failed."""
|
||||
|
||||
|
||||
class NonCopyableArgumentError(Error):
|
||||
"""Error that is raised when provider argument is not deep-copyable."""
|
||||
|
||||
index: int
|
||||
keyword: str
|
||||
provider: object
|
||||
|
||||
def __init__(self, provider: object, index: int = -1, keyword: str = "") -> None:
|
||||
self.provider = provider
|
||||
self.index = index
|
||||
self.keyword = keyword
|
||||
|
||||
def __str__(self) -> str:
|
||||
s = (
|
||||
f"keyword argument {self.keyword}"
|
||||
if self.keyword
|
||||
else f"argument at index {self.index}"
|
||||
)
|
||||
return f"Couldn't copy {s} for provider {self.provider!r}"
|
||||
|
|
|
@ -38,9 +38,11 @@ class View(providers.Callable):
|
|||
|
||||
def as_view(self):
|
||||
"""Return aiohttp view function."""
|
||||
|
||||
@functools.wraps(self.provides)
|
||||
async def _view(request, *args, **kwargs):
|
||||
return await self.__call__(request, *args, **kwargs)
|
||||
|
||||
return _view
|
||||
|
||||
|
||||
|
@ -49,6 +51,8 @@ class ClassBasedView(providers.Factory):
|
|||
|
||||
def as_view(self):
|
||||
"""Return aiohttp view function."""
|
||||
|
||||
async def _view(request, *args, **kwargs):
|
||||
return await self.__call__(request, *args, **kwargs)
|
||||
|
||||
return _view
|
||||
|
|
|
@ -2,22 +2,13 @@ from typing import Awaitable as _Awaitable
|
|||
|
||||
from dependency_injector import providers
|
||||
|
||||
|
||||
class Application(providers.Singleton): ...
|
||||
|
||||
|
||||
class Extension(providers.Singleton): ...
|
||||
|
||||
|
||||
class Middleware(providers.DelegatedCallable): ...
|
||||
|
||||
|
||||
class MiddlewareFactory(providers.Factory): ...
|
||||
|
||||
|
||||
class View(providers.Callable):
|
||||
def as_view(self) -> _Awaitable: ...
|
||||
|
||||
|
||||
class ClassBasedView(providers.Factory):
|
||||
def as_view(self) -> _Awaitable: ...
|
||||
|
|
|
@ -45,6 +45,7 @@ class ClassBasedView(providers.Factory):
|
|||
def as_view(provider, name=None):
|
||||
"""Transform class-based view provider to view function."""
|
||||
if isinstance(provider, providers.Factory):
|
||||
|
||||
def view(*args, **kwargs):
|
||||
self = provider()
|
||||
return self.dispatch_request(*args, **kwargs)
|
||||
|
@ -52,12 +53,13 @@ def as_view(provider, name=None):
|
|||
assert name, 'Argument "endpoint" is required for class-based views'
|
||||
view.__name__ = name
|
||||
elif isinstance(provider, providers.Callable):
|
||||
|
||||
def view(*args, **kwargs):
|
||||
return provider(*args, **kwargs)
|
||||
|
||||
view.__name__ = provider.provides.__name__
|
||||
else:
|
||||
raise errors.Error('Undefined provider type')
|
||||
raise errors.Error("Undefined provider type")
|
||||
|
||||
view.__doc__ = provider.provides.__doc__
|
||||
view.__module__ = provider.provides.__module__
|
||||
|
@ -65,14 +67,14 @@ def as_view(provider, name=None):
|
|||
if isinstance(provider.provides, type):
|
||||
view.view_class = provider.provides
|
||||
|
||||
if hasattr(provider.provides, 'decorators'):
|
||||
if hasattr(provider.provides, "decorators"):
|
||||
for decorator in provider.provides.decorators:
|
||||
view = decorator(view)
|
||||
|
||||
if hasattr(provider.provides, 'methods'):
|
||||
if hasattr(provider.provides, "methods"):
|
||||
view.methods = provider.provides.methods
|
||||
|
||||
if hasattr(provider.provides, 'provide_automatic_options'):
|
||||
if hasattr(provider.provides, "provide_automatic_options"):
|
||||
view.provide_automatic_options = provider.provides.provide_automatic_options
|
||||
|
||||
return view
|
||||
|
|
|
@ -3,22 +3,17 @@ from typing import Union, Optional, Callable as _Callable, Any
|
|||
from flask import request as flask_request
|
||||
from dependency_injector import providers
|
||||
|
||||
|
||||
request: providers.Object[flask_request]
|
||||
|
||||
|
||||
class Application(providers.Singleton): ...
|
||||
|
||||
|
||||
class Extension(providers.Singleton): ...
|
||||
|
||||
|
||||
class View(providers.Callable):
|
||||
def as_view(self) -> _Callable[..., Any]: ...
|
||||
|
||||
|
||||
class ClassBasedView(providers.Factory):
|
||||
def as_view(self, name: str) -> _Callable[..., Any]: ...
|
||||
|
||||
|
||||
def as_view(provider: Union[View, ClassBasedView], name: Optional[str] = None) -> _Callable[..., Any]: ...
|
||||
def as_view(
|
||||
provider: Union[View, ClassBasedView], name: Optional[str] = None
|
||||
) -> _Callable[..., Any]: ...
|
||||
|
|
52
src/dependency_injector/ext/starlette.py
Normal file
52
src/dependency_injector/ext/starlette.py
Normal file
|
@ -0,0 +1,52 @@
|
|||
import sys
|
||||
from typing import Any
|
||||
|
||||
if sys.version_info >= (3, 11): # pragma: no cover
|
||||
from typing import Self
|
||||
else: # pragma: no cover
|
||||
from typing_extensions import Self
|
||||
|
||||
from dependency_injector.containers import Container
|
||||
|
||||
|
||||
class Lifespan:
|
||||
"""A starlette lifespan handler performing container resource initialization and shutdown.
|
||||
|
||||
See https://www.starlette.io/lifespan/ for details.
|
||||
|
||||
Usage:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from dependency_injector.containers import DeclarativeContainer
|
||||
from dependency_injector.ext.starlette import Lifespan
|
||||
from dependency_injector.providers import Factory, Self, Singleton
|
||||
from starlette.applications import Starlette
|
||||
|
||||
class Container(DeclarativeContainer):
|
||||
__self__ = Self()
|
||||
lifespan = Singleton(Lifespan, __self__)
|
||||
app = Factory(Starlette, lifespan=lifespan)
|
||||
|
||||
:param container: container instance
|
||||
"""
|
||||
|
||||
container: Container
|
||||
|
||||
def __init__(self, container: Container) -> None:
|
||||
self.container = container
|
||||
|
||||
def __call__(self, app: Any) -> Self:
|
||||
return self
|
||||
|
||||
async def __aenter__(self) -> None:
|
||||
result = self.container.init_resources()
|
||||
|
||||
if result is not None:
|
||||
await result
|
||||
|
||||
async def __aexit__(self, *exc_info: Any) -> None:
|
||||
result = self.container.shutdown_resources()
|
||||
|
||||
if result is not None:
|
||||
await result
|
File diff suppressed because it is too large
Load Diff
|
@ -1,10 +1,6 @@
|
|||
"""Providers module."""
|
||||
|
||||
try:
|
||||
import asyncio
|
||||
except ImportError:
|
||||
asyncio = None
|
||||
|
||||
import asyncio
|
||||
import functools
|
||||
|
||||
cimport cython
|
||||
|
@ -19,7 +15,7 @@ cdef tuple __COROUTINE_TYPES
|
|||
|
||||
|
||||
# Base providers
|
||||
cdef class Provider(object):
|
||||
cdef class Provider:
|
||||
cdef tuple _overridden
|
||||
cdef Provider _last_overriding
|
||||
cdef tuple _overrides
|
||||
|
@ -291,7 +287,7 @@ cdef class MethodCaller(Provider):
|
|||
|
||||
|
||||
# Injections
|
||||
cdef class Injection(object):
|
||||
cdef class Injection:
|
||||
cdef object _value
|
||||
cdef int _is_provider
|
||||
cdef int _is_delegated
|
||||
|
@ -313,12 +309,12 @@ cpdef tuple parse_named_injections(dict kwargs)
|
|||
|
||||
|
||||
# Utils
|
||||
cdef class OverridingContext(object):
|
||||
cdef class OverridingContext:
|
||||
cdef Provider _overridden
|
||||
cdef Provider _overriding
|
||||
|
||||
|
||||
cdef class BaseSingletonResetContext(object):
|
||||
cdef class BaseSingletonResetContext:
|
||||
cdef object _singleton
|
||||
|
||||
|
||||
|
|
|
@ -33,7 +33,6 @@ except ImportError:
|
|||
|
||||
from . import resources
|
||||
|
||||
|
||||
Injection = Any
|
||||
ProviderParent = Union["Provider", Any]
|
||||
T = TypeVar("T")
|
||||
|
@ -41,16 +40,13 @@ TT = TypeVar("TT")
|
|||
P = TypeVar("P", bound="Provider")
|
||||
BS = TypeVar("BS", bound="BaseSingleton")
|
||||
|
||||
|
||||
class Provider(Generic[T]):
|
||||
def __init__(self) -> None: ...
|
||||
|
||||
@overload
|
||||
def __call__(self, *args: Injection, **kwargs: Injection) -> T: ...
|
||||
@overload
|
||||
def __call__(self, *args: Injection, **kwargs: Injection) -> Awaitable[T]: ...
|
||||
def async_(self, *args: Injection, **kwargs: Injection) -> Awaitable[T]: ...
|
||||
|
||||
def __deepcopy__(self, memo: Optional[_Dict[Any, Any]]) -> Provider: ...
|
||||
def __str__(self) -> str: ...
|
||||
def __repr__(self) -> str: ...
|
||||
|
@ -67,9 +63,9 @@ class Provider(Generic[T]):
|
|||
def unregister_overrides(self, provider: Union[Provider, Any]) -> None: ...
|
||||
def delegate(self) -> Provider: ...
|
||||
@property
|
||||
def provider(self) -> Provider: ...
|
||||
def provider(self) -> Provider[T]: ...
|
||||
@property
|
||||
def provided(self) -> ProvidedInstance: ...
|
||||
def provided(self) -> ProvidedInstance[T]: ...
|
||||
def enable_async_mode(self) -> None: ...
|
||||
def disable_async_mode(self) -> None: ...
|
||||
def reset_async_mode(self) -> None: ...
|
||||
|
@ -78,9 +74,12 @@ class Provider(Generic[T]):
|
|||
def is_async_mode_undefined(self) -> bool: ...
|
||||
@property
|
||||
def related(self) -> _Iterator[Provider]: ...
|
||||
def traverse(self, types: Optional[_Iterable[Type[TT]]] = None) -> _Iterator[TT]: ...
|
||||
def _copy_overridings(self, copied: Provider, memo: Optional[_Dict[Any, Any]]) -> None: ...
|
||||
|
||||
def traverse(
|
||||
self, types: Optional[_Iterable[Type[TT]]] = None
|
||||
) -> _Iterator[TT]: ...
|
||||
def _copy_overridings(
|
||||
self, copied: Provider, memo: Optional[_Dict[Any, Any]]
|
||||
) -> None: ...
|
||||
|
||||
class Object(Provider[T]):
|
||||
def __init__(self, provides: Optional[T] = None) -> None: ...
|
||||
|
@ -88,7 +87,6 @@ class Object(Provider[T]):
|
|||
def provides(self) -> Optional[T]: ...
|
||||
def set_provides(self, provides: Optional[T]) -> Object: ...
|
||||
|
||||
|
||||
class Self(Provider[T]):
|
||||
def __init__(self, container: Optional[T] = None) -> None: ...
|
||||
def set_container(self, container: T) -> None: ...
|
||||
|
@ -96,41 +94,51 @@ class Self(Provider[T]):
|
|||
@property
|
||||
def alt_names(self) -> Tuple[Any]: ...
|
||||
|
||||
|
||||
class Delegate(Provider[Provider]):
|
||||
def __init__(self, provides: Optional[Provider] = None) -> None: ...
|
||||
@property
|
||||
def provides(self) -> Optional[Provider]: ...
|
||||
def set_provides(self, provides: Optional[Provider]) -> Delegate: ...
|
||||
|
||||
|
||||
class Aggregate(Provider[T]):
|
||||
def __init__(self, provider_dict: Optional[_Dict[Any, Provider[T]]] = None, **provider_kwargs: Provider[T]): ...
|
||||
def __init__(
|
||||
self,
|
||||
provider_dict: Optional[_Dict[Any, Provider[T]]] = None,
|
||||
**provider_kwargs: Provider[T],
|
||||
): ...
|
||||
def __getattr__(self, provider_name: Any) -> Provider[T]: ...
|
||||
|
||||
@overload
|
||||
def __call__(self, provider_name: Optional[Any] = None, *args: Injection, **kwargs: Injection) -> T: ...
|
||||
def __call__(
|
||||
self, provider_name: Optional[Any] = None, *args: Injection, **kwargs: Injection
|
||||
) -> T: ...
|
||||
@overload
|
||||
def __call__(self, provider_name: Optional[Any] = None, *args: Injection, **kwargs: Injection) -> Awaitable[T]: ...
|
||||
def async_(self, provider_name: Optional[Any] = None, *args: Injection, **kwargs: Injection) -> Awaitable[T]: ...
|
||||
|
||||
def __call__(
|
||||
self, provider_name: Optional[Any] = None, *args: Injection, **kwargs: Injection
|
||||
) -> Awaitable[T]: ...
|
||||
def async_(
|
||||
self, provider_name: Optional[Any] = None, *args: Injection, **kwargs: Injection
|
||||
) -> Awaitable[T]: ...
|
||||
@property
|
||||
def providers(self) -> _Dict[Any, Provider[T]]: ...
|
||||
def set_providers(self, provider_dict: Optional[_Dict[Any, Provider[T]]] = None, **provider_kwargs: Provider[T]) -> Aggregate[T]: ...
|
||||
|
||||
def set_providers(
|
||||
self,
|
||||
provider_dict: Optional[_Dict[Any, Provider[T]]] = None,
|
||||
**provider_kwargs: Provider[T],
|
||||
) -> Aggregate[T]: ...
|
||||
|
||||
class Dependency(Provider[T]):
|
||||
def __init__(self, instance_of: Type[T] = object, default: Optional[Union[Provider, Any]] = None) -> None: ...
|
||||
def __init__(
|
||||
self,
|
||||
instance_of: Type[T] = object,
|
||||
default: Optional[Union[Provider, Any]] = None,
|
||||
) -> None: ...
|
||||
def __getattr__(self, name: str) -> Any: ...
|
||||
|
||||
@property
|
||||
def instance_of(self) -> Type[T]: ...
|
||||
def set_instance_of(self, instance_of: Type[T]) -> Dependency[T]: ...
|
||||
|
||||
@property
|
||||
def default(self) -> Provider[T]: ...
|
||||
def set_default(self, default: Optional[Union[Provider, Any]]) -> Dependency[T]: ...
|
||||
|
||||
@property
|
||||
def is_defined(self) -> bool: ...
|
||||
def provided_by(self, provider: Provider) -> OverridingContext[P]: ...
|
||||
|
@ -140,10 +148,8 @@ class Dependency(Provider[T]):
|
|||
def parent_name(self) -> Optional[str]: ...
|
||||
def assign_parent(self, parent: ProviderParent) -> None: ...
|
||||
|
||||
|
||||
class ExternalDependency(Dependency[T]): ...
|
||||
|
||||
|
||||
class DependenciesContainer(Object):
|
||||
def __init__(self, **dependencies: Provider) -> None: ...
|
||||
def __getattr__(self, name: str) -> Provider: ...
|
||||
|
@ -156,12 +162,18 @@ class DependenciesContainer(Object):
|
|||
def parent_name(self) -> Optional[str]: ...
|
||||
def assign_parent(self, parent: ProviderParent) -> None: ...
|
||||
|
||||
|
||||
class Callable(Provider[T]):
|
||||
def __init__(self, provides: Optional[Union[_Callable[..., T], str]] = None, *args: Injection, **kwargs: Injection) -> None: ...
|
||||
def __init__(
|
||||
self,
|
||||
provides: Optional[Union[_Callable[..., T], str]] = None,
|
||||
*args: Injection,
|
||||
**kwargs: Injection,
|
||||
) -> None: ...
|
||||
@property
|
||||
def provides(self) -> Optional[_Callable[..., T]]: ...
|
||||
def set_provides(self, provides: Optional[Union[_Callable[..., T], str]]) -> Callable[T]: ...
|
||||
def set_provides(
|
||||
self, provides: Optional[Union[_Callable[..., T], str]]
|
||||
) -> Callable[T]: ...
|
||||
@property
|
||||
def args(self) -> Tuple[Injection]: ...
|
||||
def add_args(self, *args: Injection) -> Callable[T]: ...
|
||||
|
@ -173,32 +185,23 @@ class Callable(Provider[T]):
|
|||
def set_kwargs(self, **kwargs: Injection) -> Callable[T]: ...
|
||||
def clear_kwargs(self) -> Callable[T]: ...
|
||||
|
||||
|
||||
class DelegatedCallable(Callable[T]): ...
|
||||
|
||||
|
||||
class AbstractCallable(Callable[T]):
|
||||
def override(self, provider: Callable) -> OverridingContext[P]: ...
|
||||
|
||||
|
||||
class CallableDelegate(Delegate):
|
||||
def __init__(self, callable: Callable) -> None: ...
|
||||
|
||||
|
||||
class Coroutine(Callable[T]): ...
|
||||
|
||||
|
||||
class DelegatedCoroutine(Coroutine[T]): ...
|
||||
|
||||
|
||||
class AbstractCoroutine(Coroutine[T]):
|
||||
def override(self, provider: Coroutine) -> OverridingContext[P]: ...
|
||||
|
||||
|
||||
class CoroutineDelegate(Delegate):
|
||||
def __init__(self, coroutine: Coroutine) -> None: ...
|
||||
|
||||
|
||||
class ConfigurationOption(Provider[Any]):
|
||||
UNDEFINED: object
|
||||
def __init__(self, name: Tuple[str], root: Configuration) -> None: ...
|
||||
|
@ -212,89 +215,137 @@ class ConfigurationOption(Provider[Any]):
|
|||
def get_name_segments(self) -> Tuple[Union[str, Provider]]: ...
|
||||
def as_int(self) -> TypedConfigurationOption[int]: ...
|
||||
def as_float(self) -> TypedConfigurationOption[float]: ...
|
||||
def as_(self, callback: _Callable[..., T], *args: Injection, **kwargs: Injection) -> TypedConfigurationOption[T]: ...
|
||||
def as_(
|
||||
self, callback: _Callable[..., T], *args: Injection, **kwargs: Injection
|
||||
) -> TypedConfigurationOption[T]: ...
|
||||
def required(self) -> ConfigurationOption: ...
|
||||
def is_required(self) -> bool: ...
|
||||
def update(self, value: Any) -> None: ...
|
||||
def from_ini(self, filepath: Union[Path, str], required: bool = False, envs_required: bool = False) -> None: ...
|
||||
def from_yaml(self, filepath: Union[Path, str], required: bool = False, loader: Optional[Any] = None, envs_required: bool = False) -> None: ...
|
||||
def from_json(self, filepath: Union[Path, str], required: bool = False, envs_required: bool = False) -> None: ...
|
||||
def from_pydantic(self, settings: PydanticSettings, required: bool = False, **kwargs: Any) -> None: ...
|
||||
def from_ini(
|
||||
self,
|
||||
filepath: Union[Path, str],
|
||||
required: bool = False,
|
||||
envs_required: 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_env(self, name: str, default: Optional[Any] = None, required: bool = False, as_: Optional[_Callable[..., Any]] = None) -> None: ...
|
||||
def from_env(
|
||||
self,
|
||||
name: str,
|
||||
default: Optional[Any] = None,
|
||||
required: bool = False,
|
||||
as_: Optional[_Callable[..., Any]] = None,
|
||||
) -> None: ...
|
||||
def from_value(self, value: Any) -> None: ...
|
||||
|
||||
|
||||
class TypedConfigurationOption(Callable[T]):
|
||||
@property
|
||||
def option(self) -> ConfigurationOption: ...
|
||||
|
||||
|
||||
class Configuration(Object[Any]):
|
||||
DEFAULT_NAME: str = "config"
|
||||
def __init__(
|
||||
self,
|
||||
name: str = DEFAULT_NAME,
|
||||
default: Optional[Any] = None,
|
||||
*,
|
||||
strict: bool = False,
|
||||
ini_files: Optional[_Iterable[Union[Path, str]]] = None,
|
||||
yaml_files: Optional[_Iterable[Union[Path, str]]] = None,
|
||||
json_files: Optional[_Iterable[Union[Path, str]]] = None,
|
||||
pydantic_settings: Optional[_Iterable[PydanticSettings]] = None,
|
||||
self,
|
||||
name: str = DEFAULT_NAME,
|
||||
default: Optional[Any] = None,
|
||||
*,
|
||||
strict: bool = False,
|
||||
ini_files: Optional[_Iterable[Union[Path, str]]] = None,
|
||||
yaml_files: Optional[_Iterable[Union[Path, str]]] = None,
|
||||
json_files: Optional[_Iterable[Union[Path, str]]] = None,
|
||||
pydantic_settings: Optional[_Iterable[PydanticSettings]] = None,
|
||||
) -> None: ...
|
||||
def __enter__(self) -> Configuration : ...
|
||||
def __enter__(self) -> Configuration: ...
|
||||
def __exit__(self, *exc_info: Any) -> None: ...
|
||||
def __getattr__(self, item: str) -> ConfigurationOption: ...
|
||||
def __getitem__(self, item: Union[str, Provider]) -> ConfigurationOption: ...
|
||||
|
||||
def get_name(self) -> str: ...
|
||||
def set_name(self, name: str) -> Configuration: ...
|
||||
|
||||
def get_default(self) -> _Dict[Any, Any]: ...
|
||||
def set_default(self, default: _Dict[Any, Any]): ...
|
||||
|
||||
def get_strict(self) -> bool: ...
|
||||
def set_strict(self, strict: bool) -> Configuration: ...
|
||||
|
||||
def get_children(self) -> _Dict[str, ConfigurationOption]: ...
|
||||
def set_children(self, children: _Dict[str, ConfigurationOption]) -> Configuration: ...
|
||||
|
||||
def set_children(
|
||||
self, children: _Dict[str, ConfigurationOption]
|
||||
) -> Configuration: ...
|
||||
def get_ini_files(self) -> _List[Union[Path, str]]: ...
|
||||
def set_ini_files(self, files: _Iterable[Union[Path, str]]) -> Configuration: ...
|
||||
|
||||
def get_yaml_files(self) -> _List[Union[Path, str]]: ...
|
||||
def set_yaml_files(self, files: _Iterable[Union[Path, str]]) -> Configuration: ...
|
||||
|
||||
def get_json_files(self) -> _List[Union[Path, str]]: ...
|
||||
def set_json_files(self, files: _Iterable[Union[Path, str]]) -> Configuration: ...
|
||||
|
||||
def get_pydantic_settings(self) -> _List[PydanticSettings]: ...
|
||||
def set_pydantic_settings(self, settings: _Iterable[PydanticSettings]) -> Configuration: ...
|
||||
|
||||
def set_pydantic_settings(
|
||||
self, settings: _Iterable[PydanticSettings]
|
||||
) -> Configuration: ...
|
||||
def load(self, required: bool = False, envs_required: bool = False) -> None: ...
|
||||
|
||||
def get(self, selector: str) -> Any: ...
|
||||
def set(self, selector: str, value: Any) -> OverridingContext[P]: ...
|
||||
def reset_cache(self) -> None: ...
|
||||
def update(self, value: Any) -> None: ...
|
||||
def from_ini(self, filepath: Union[Path, str], required: bool = False, envs_required: bool = False) -> None: ...
|
||||
def from_yaml(self, filepath: Union[Path, str], required: bool = False, loader: Optional[Any] = None, envs_required: bool = False) -> None: ...
|
||||
def from_json(self, filepath: Union[Path, str], required: bool = False, envs_required: bool = False) -> None: ...
|
||||
def from_pydantic(self, settings: PydanticSettings, required: bool = False, **kwargs: Any) -> None: ...
|
||||
def from_ini(
|
||||
self,
|
||||
filepath: Union[Path, str],
|
||||
required: bool = False,
|
||||
envs_required: bool = False,
|
||||
) -> None: ...
|
||||
def from_yaml(
|
||||
self,
|
||||
filepath: Union[Path, str],
|
||||
required: bool = False,
|
||||
loader: Optional[Any] = None,
|
||||
envs_required: bool = False,
|
||||
) -> None: ...
|
||||
def from_json(
|
||||
self,
|
||||
filepath: Union[Path, str],
|
||||
required: bool = False,
|
||||
envs_required: bool = False,
|
||||
) -> None: ...
|
||||
def from_pydantic(
|
||||
self, settings: PydanticSettings, required: bool = False, **kwargs: Any
|
||||
) -> None: ...
|
||||
def from_dict(self, options: _Dict[str, Any], required: bool = False) -> None: ...
|
||||
def from_env(self, name: str, default: Optional[Any] = None, required: bool = False, as_: Optional[_Callable[..., Any]] = None) -> None: ...
|
||||
def from_env(
|
||||
self,
|
||||
name: str,
|
||||
default: Optional[Any] = None,
|
||||
required: bool = False,
|
||||
as_: Optional[_Callable[..., Any]] = None,
|
||||
) -> None: ...
|
||||
def from_value(self, value: Any) -> None: ...
|
||||
|
||||
|
||||
class Factory(Provider[T]):
|
||||
provided_type: Optional[Type]
|
||||
def __init__(self, provides: Optional[Union[_Callable[..., T], str]] = None, *args: Injection, **kwargs: Injection) -> None: ...
|
||||
def __init__(
|
||||
self,
|
||||
provides: Optional[Union[_Callable[..., T], str]] = None,
|
||||
*args: Injection,
|
||||
**kwargs: Injection,
|
||||
) -> None: ...
|
||||
@property
|
||||
def cls(self) -> Type[T]: ...
|
||||
@property
|
||||
def provides(self) -> Optional[_Callable[..., T]]: ...
|
||||
def set_provides(self, provides: Optional[Union[_Callable[..., T], str]]) -> Factory[T]: ...
|
||||
def set_provides(
|
||||
self, provides: Optional[Union[_Callable[..., T], str]]
|
||||
) -> Factory[T]: ...
|
||||
@property
|
||||
def args(self) -> Tuple[Injection]: ...
|
||||
def add_args(self, *args: Injection) -> Factory[T]: ...
|
||||
|
@ -311,33 +362,39 @@ class Factory(Provider[T]):
|
|||
def set_attributes(self, **kwargs: Injection) -> Factory[T]: ...
|
||||
def clear_attributes(self) -> Factory[T]: ...
|
||||
|
||||
|
||||
class DelegatedFactory(Factory[T]): ...
|
||||
|
||||
|
||||
class AbstractFactory(Factory[T]):
|
||||
def override(self, provider: Factory) -> OverridingContext[P]: ...
|
||||
|
||||
|
||||
class FactoryDelegate(Delegate):
|
||||
def __init__(self, factory: Factory): ...
|
||||
|
||||
|
||||
class FactoryAggregate(Aggregate[T]):
|
||||
def __getattr__(self, provider_name: Any) -> Factory[T]: ...
|
||||
@property
|
||||
def factories(self) -> _Dict[Any, Factory[T]]: ...
|
||||
def set_factories(self, provider_dict: Optional[_Dict[Any, Factory[T]]] = None, **provider_kwargs: Factory[T]) -> FactoryAggregate[T]: ...
|
||||
|
||||
def set_factories(
|
||||
self,
|
||||
provider_dict: Optional[_Dict[Any, Factory[T]]] = None,
|
||||
**provider_kwargs: Factory[T],
|
||||
) -> FactoryAggregate[T]: ...
|
||||
|
||||
class BaseSingleton(Provider[T]):
|
||||
provided_type = Optional[Type]
|
||||
def __init__(self, provides: Optional[Union[_Callable[..., T], str]] = None, *args: Injection, **kwargs: Injection) -> None: ...
|
||||
def __init__(
|
||||
self,
|
||||
provides: Optional[Union[_Callable[..., T], str]] = None,
|
||||
*args: Injection,
|
||||
**kwargs: Injection,
|
||||
) -> None: ...
|
||||
@property
|
||||
def cls(self) -> Type[T]: ...
|
||||
@property
|
||||
def provides(self) -> Optional[_Callable[..., T]]: ...
|
||||
def set_provides(self, provides: Optional[Union[_Callable[..., T], str]]) -> BaseSingleton[T]: ...
|
||||
def set_provides(
|
||||
self, provides: Optional[Union[_Callable[..., T], str]]
|
||||
) -> BaseSingleton[T]: ...
|
||||
@property
|
||||
def args(self) -> Tuple[Injection]: ...
|
||||
def add_args(self, *args: Injection) -> BaseSingleton[T]: ...
|
||||
|
@ -356,36 +413,20 @@ class BaseSingleton(Provider[T]):
|
|||
def reset(self) -> SingletonResetContext[BS]: ...
|
||||
def full_reset(self) -> SingletonFullResetContext[BS]: ...
|
||||
|
||||
|
||||
class Singleton(BaseSingleton[T]): ...
|
||||
|
||||
|
||||
class DelegatedSingleton(Singleton[T]): ...
|
||||
|
||||
|
||||
class ThreadSafeSingleton(Singleton[T]): ...
|
||||
|
||||
|
||||
class DelegatedThreadSafeSingleton(ThreadSafeSingleton[T]): ...
|
||||
|
||||
|
||||
class ThreadLocalSingleton(BaseSingleton[T]): ...
|
||||
|
||||
|
||||
class ContextLocalSingleton(BaseSingleton[T]): ...
|
||||
|
||||
|
||||
class DelegatedThreadLocalSingleton(ThreadLocalSingleton[T]): ...
|
||||
|
||||
|
||||
class AbstractSingleton(BaseSingleton[T]):
|
||||
def override(self, provider: BaseSingleton) -> OverridingContext[P]: ...
|
||||
|
||||
|
||||
class SingletonDelegate(Delegate):
|
||||
def __init__(self, singleton: BaseSingleton): ...
|
||||
|
||||
|
||||
class List(Provider[_List]):
|
||||
def __init__(self, *args: Injection): ...
|
||||
@property
|
||||
|
@ -394,29 +435,63 @@ class List(Provider[_List]):
|
|||
def set_args(self, *args: Injection) -> List[T]: ...
|
||||
def clear_args(self) -> List[T]: ...
|
||||
|
||||
|
||||
class Dict(Provider[_Dict]):
|
||||
def __init__(self, dict_: Optional[_Dict[Any, Injection]] = None, **kwargs: Injection): ...
|
||||
def __init__(
|
||||
self, dict_: Optional[_Dict[Any, Injection]] = None, **kwargs: Injection
|
||||
): ...
|
||||
@property
|
||||
def kwargs(self) -> _Dict[Any, Injection]: ...
|
||||
def add_kwargs(self, dict_: Optional[_Dict[Any, Injection]] = None, **kwargs: Injection) -> Dict: ...
|
||||
def set_kwargs(self, dict_: Optional[_Dict[Any, Injection]] = None, **kwargs: Injection) -> Dict: ...
|
||||
def add_kwargs(
|
||||
self, dict_: Optional[_Dict[Any, Injection]] = None, **kwargs: Injection
|
||||
) -> Dict: ...
|
||||
def set_kwargs(
|
||||
self, dict_: Optional[_Dict[Any, Injection]] = None, **kwargs: Injection
|
||||
) -> Dict: ...
|
||||
def clear_kwargs(self) -> Dict: ...
|
||||
|
||||
|
||||
class Resource(Provider[T]):
|
||||
@overload
|
||||
def __init__(self, provides: Optional[Type[resources.Resource[T]]] = None, *args: Injection, **kwargs: Injection) -> None: ...
|
||||
def __init__(
|
||||
self,
|
||||
provides: Optional[Type[resources.Resource[T]]] = None,
|
||||
*args: Injection,
|
||||
**kwargs: Injection,
|
||||
) -> None: ...
|
||||
@overload
|
||||
def __init__(self, provides: Optional[Type[resources.AsyncResource[T]]] = None, *args: Injection, **kwargs: Injection) -> None: ...
|
||||
def __init__(
|
||||
self,
|
||||
provides: Optional[Type[resources.AsyncResource[T]]] = None,
|
||||
*args: Injection,
|
||||
**kwargs: Injection,
|
||||
) -> None: ...
|
||||
@overload
|
||||
def __init__(self, provides: Optional[_Callable[..., _Iterator[T]]] = None, *args: Injection, **kwargs: Injection) -> None: ...
|
||||
def __init__(
|
||||
self,
|
||||
provides: Optional[_Callable[..., _Iterator[T]]] = None,
|
||||
*args: Injection,
|
||||
**kwargs: Injection,
|
||||
) -> None: ...
|
||||
@overload
|
||||
def __init__(self, provides: Optional[_Callable[..., _AsyncIterator[T]]] = None, *args: Injection, **kwargs: Injection) -> None: ...
|
||||
def __init__(
|
||||
self,
|
||||
provides: Optional[_Callable[..., _AsyncIterator[T]]] = None,
|
||||
*args: Injection,
|
||||
**kwargs: Injection,
|
||||
) -> None: ...
|
||||
@overload
|
||||
def __init__(self, provides: Optional[_Callable[..., _Coroutine[Injection, Injection, T]]] = None, *args: Injection, **kwargs: Injection) -> None: ...
|
||||
def __init__(
|
||||
self,
|
||||
provides: Optional[_Callable[..., _Coroutine[Injection, Injection, T]]] = None,
|
||||
*args: Injection,
|
||||
**kwargs: Injection,
|
||||
) -> None: ...
|
||||
@overload
|
||||
def __init__(self, provides: Optional[Union[_Callable[..., T], str]] = None, *args: Injection, **kwargs: Injection) -> None: ...
|
||||
def __init__(
|
||||
self,
|
||||
provides: Optional[Union[_Callable[..., T], str]] = None,
|
||||
*args: Injection,
|
||||
**kwargs: Injection,
|
||||
) -> None: ...
|
||||
@property
|
||||
def provides(self) -> Optional[_Callable[..., Any]]: ...
|
||||
def set_provides(self, provides: Optional[Any]) -> Resource[T]: ...
|
||||
|
@ -435,9 +510,13 @@ class Resource(Provider[T]):
|
|||
def init(self) -> Optional[Awaitable[T]]: ...
|
||||
def shutdown(self) -> Optional[Awaitable]: ...
|
||||
|
||||
|
||||
class Container(Provider[T]):
|
||||
def __init__(self, container_cls: Type[T], container: Optional[T] = None, **overriding_providers: Union[Provider, Any]) -> None: ...
|
||||
def __init__(
|
||||
self,
|
||||
container_cls: Type[T],
|
||||
container: Optional[T] = None,
|
||||
**overriding_providers: Union[Provider, Any],
|
||||
) -> None: ...
|
||||
def __getattr__(self, name: str) -> Provider: ...
|
||||
@property
|
||||
def container(self) -> T: ...
|
||||
|
@ -448,50 +527,51 @@ class Container(Provider[T]):
|
|||
def parent_name(self) -> Optional[str]: ...
|
||||
def assign_parent(self, parent: ProviderParent) -> None: ...
|
||||
|
||||
|
||||
class Selector(Provider[Any]):
|
||||
def __init__(self, selector: Optional[_Callable[..., Any]] = None, **providers: Provider): ...
|
||||
def __init__(
|
||||
self, selector: Optional[_Callable[..., Any]] = None, **providers: Provider
|
||||
): ...
|
||||
def __getattr__(self, name: str) -> Provider: ...
|
||||
|
||||
@property
|
||||
def selector(self) -> Optional[_Callable[..., Any]]: ...
|
||||
def set_selector(self, selector: Optional[_Callable[..., Any]]) -> Selector: ...
|
||||
|
||||
@property
|
||||
def providers(self) -> _Dict[str, Provider]: ...
|
||||
def set_providers(self, **providers: Provider) -> Selector: ...
|
||||
|
||||
|
||||
class ProvidedInstanceFluentInterface:
|
||||
def __getattr__(self, item: Any) -> AttributeGetter: ...
|
||||
def __getitem__(self, item: Any) -> ItemGetter: ...
|
||||
def call(self, *args: Injection, **kwargs: Injection) -> MethodCaller: ...
|
||||
@property
|
||||
def provides(self) -> Optional[Provider]: ...
|
||||
def set_provides(self, provides: Optional[Provider]) -> ProvidedInstanceFluentInterface: ...
|
||||
|
||||
def set_provides(
|
||||
self, provides: Optional[Provider]
|
||||
) -> ProvidedInstanceFluentInterface: ...
|
||||
|
||||
class ProvidedInstance(Provider, ProvidedInstanceFluentInterface):
|
||||
def __init__(self, provides: Optional[Provider] = None) -> None: ...
|
||||
|
||||
|
||||
class AttributeGetter(Provider, ProvidedInstanceFluentInterface):
|
||||
def __init__(self, provides: Optional[Provider] = None, name: Optional[str] = None) -> None: ...
|
||||
def __init__(
|
||||
self, provides: Optional[Provider] = None, name: Optional[str] = None
|
||||
) -> None: ...
|
||||
@property
|
||||
def name(self) -> Optional[str]: ...
|
||||
def set_name(self, name: Optional[str]) -> ProvidedInstanceFluentInterface: ...
|
||||
|
||||
|
||||
class ItemGetter(Provider, ProvidedInstanceFluentInterface):
|
||||
def __init__(self, provides: Optional[Provider] = None, name: Optional[str] = None) -> None: ...
|
||||
def __init__(
|
||||
self, provides: Optional[Provider] = None, name: Optional[str] = None
|
||||
) -> None: ...
|
||||
@property
|
||||
def name(self) -> Optional[str]: ...
|
||||
def set_name(self, name: Optional[str]) -> ProvidedInstanceFluentInterface: ...
|
||||
|
||||
|
||||
class MethodCaller(Provider, ProvidedInstanceFluentInterface):
|
||||
def __init__(self, provides: Optional[Provider] = None, *args: Injection, **kwargs: Injection) -> None: ...
|
||||
|
||||
def __init__(
|
||||
self, provides: Optional[Provider] = None, *args: Injection, **kwargs: Injection
|
||||
) -> None: ...
|
||||
|
||||
class OverridingContext(Generic[T]):
|
||||
def __init__(self, overridden: Provider, overriding: Provider): ...
|
||||
|
@ -500,47 +580,39 @@ class OverridingContext(Generic[T]):
|
|||
pass
|
||||
...
|
||||
|
||||
|
||||
class BaseSingletonResetContext(Generic[T]):
|
||||
def __init__(self, provider: T): ...
|
||||
def __enter__(self) -> T: ...
|
||||
def __exit__(self, *_: Any) -> None: ...
|
||||
|
||||
|
||||
class SingletonResetContext(BaseSingletonResetContext):
|
||||
...
|
||||
|
||||
|
||||
class SingletonFullResetContext(BaseSingletonResetContext):
|
||||
...
|
||||
|
||||
class SingletonResetContext(BaseSingletonResetContext): ...
|
||||
class SingletonFullResetContext(BaseSingletonResetContext): ...
|
||||
|
||||
CHILD_PROVIDERS: Tuple[Provider]
|
||||
|
||||
|
||||
def is_provider(instance: Any) -> bool: ...
|
||||
|
||||
|
||||
def ensure_is_provider(instance: Any) -> Provider: ...
|
||||
|
||||
|
||||
def is_delegated(instance: Any) -> bool: ...
|
||||
|
||||
|
||||
def represent_provider(provider: Provider, provides: Any) -> str: ...
|
||||
|
||||
|
||||
def deepcopy(instance: Any, memo: Optional[_Dict[Any, Any]] = None): Any: ...
|
||||
|
||||
|
||||
def deepcopy(instance: Any, memo: Optional[_Dict[Any, Any]] = None) -> Any: ...
|
||||
def deepcopy_args(
|
||||
provider: Provider[Any],
|
||||
args: Tuple[Any, ...],
|
||||
memo: Optional[_Dict[int, Any]] = None,
|
||||
) -> Tuple[Any, ...]: ...
|
||||
def deepcopy_kwargs(
|
||||
provider: Provider[Any],
|
||||
kwargs: _Dict[str, Any],
|
||||
memo: Optional[_Dict[int, Any]] = None,
|
||||
) -> Dict[str, Any]: ...
|
||||
def merge_dicts(dict1: _Dict[Any, Any], dict2: _Dict[Any, Any]) -> _Dict[Any, Any]: ...
|
||||
|
||||
|
||||
def traverse(*providers: Provider, types: Optional[_Iterable[Type]]=None) -> _Iterator[Provider]: ...
|
||||
|
||||
def traverse(
|
||||
*providers: Provider, types: Optional[_Iterable[Type]] = None
|
||||
) -> _Iterator[Provider]: ...
|
||||
|
||||
if yaml:
|
||||
class YamlLoader(yaml.SafeLoader): ...
|
||||
|
||||
else:
|
||||
class YamlLoader: ...
|
||||
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import asyncio
|
||||
import builtins
|
||||
import contextvars
|
||||
import copy
|
||||
import errno
|
||||
import functools
|
||||
|
@ -12,78 +15,62 @@ import os
|
|||
import re
|
||||
import sys
|
||||
import threading
|
||||
import types
|
||||
import warnings
|
||||
from configparser import ConfigParser as IniConfigParser
|
||||
|
||||
try:
|
||||
import contextvars
|
||||
from inspect import _is_coroutine_mark as _is_coroutine_marker
|
||||
except ImportError:
|
||||
contextvars = None
|
||||
|
||||
try:
|
||||
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:
|
||||
try:
|
||||
# Python >=3.12.0,<3.12.5
|
||||
from inspect import _is_coroutine_marker
|
||||
except ImportError:
|
||||
_is_coroutine_marker = True
|
||||
|
||||
try:
|
||||
import ConfigParser as iniconfigparser
|
||||
from asyncio.coroutines import _is_coroutine
|
||||
except ImportError:
|
||||
import configparser as iniconfigparser
|
||||
_is_coroutine = True
|
||||
|
||||
try:
|
||||
import yaml
|
||||
except ImportError:
|
||||
yaml = None
|
||||
|
||||
has_pydantic_settings = True
|
||||
cdef bint pydantic_v1 = False
|
||||
cdef str pydantic_module = "pydantic_settings"
|
||||
cdef str pydantic_extra = "pydantic2"
|
||||
|
||||
try:
|
||||
import pydantic
|
||||
from pydantic_settings import BaseSettings as PydanticSettings
|
||||
except ImportError:
|
||||
pydantic = None
|
||||
try:
|
||||
# pydantic-settings requires pydantic v2,
|
||||
# so it is safe to assume that we're dealing with v1:
|
||||
from pydantic import BaseSettings as PydanticSettings
|
||||
pydantic_v1 = True
|
||||
pydantic_module = "pydantic"
|
||||
pydantic_extra = "pydantic"
|
||||
except ImportError:
|
||||
# if it is present, ofc
|
||||
has_pydantic_settings = False
|
||||
|
||||
|
||||
from .errors import (
|
||||
Error,
|
||||
NoSuchProviderError,
|
||||
NonCopyableArgumentError,
|
||||
)
|
||||
|
||||
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(
|
||||
r"\${(?P<name>[^}^{:]+)(?P<separator>:?)(?P<default>.*?)}",
|
||||
)
|
||||
|
||||
def _resolve_config_env_markers(config_content, envs_required=False):
|
||||
cdef str _resolve_config_env_markers(config_content: str, envs_required: bool):
|
||||
"""Replace environment variable markers with their values."""
|
||||
findings = list(config_env_marker_pattern.finditer(config_content))
|
||||
|
||||
|
@ -102,28 +89,19 @@ def _resolve_config_env_markers(config_content, envs_required=False):
|
|||
return config_content
|
||||
|
||||
|
||||
if sys.version_info[0] == 3:
|
||||
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.read_string(config_string)
|
||||
return parser
|
||||
else:
|
||||
import StringIO
|
||||
cdef object _parse_ini_file(filepath, envs_required: bool | None):
|
||||
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_file.read(),
|
||||
config_string,
|
||||
envs_required=envs_required,
|
||||
)
|
||||
parser.readfp(StringIO.StringIO(config_string))
|
||||
return parser
|
||||
parser.read_string(config_string)
|
||||
return parser
|
||||
|
||||
|
||||
if yaml:
|
||||
|
@ -147,10 +125,35 @@ cdef int ASYNC_MODE_ENABLED = 1
|
|||
cdef int ASYNC_MODE_DISABLED = 2
|
||||
|
||||
cdef set __iscoroutine_typecache = set()
|
||||
cdef tuple __COROUTINE_TYPES = asyncio.coroutines._COROUTINE_TYPES if asyncio else tuple()
|
||||
cdef tuple __COROUTINE_TYPES = asyncio.coroutines._COROUTINE_TYPES
|
||||
|
||||
cdef dict pydantic_settings_to_dict(settings, dict kwargs):
|
||||
if not has_pydantic_settings:
|
||||
raise Error(
|
||||
f"Unable to load pydantic configuration - {pydantic_module} is not installed. "
|
||||
"Install pydantic or install Dependency Injector with pydantic extras: "
|
||||
f"\"pip install dependency-injector[{pydantic_extra}]\""
|
||||
)
|
||||
|
||||
if isinstance(settings, type) and issubclass(settings, PydanticSettings):
|
||||
raise Error(
|
||||
"Got settings class, but expect instance: "
|
||||
"instead \"{0}\" use \"{0}()\"".format(settings.__name__)
|
||||
)
|
||||
|
||||
if not isinstance(settings, PydanticSettings):
|
||||
raise Error(
|
||||
f"Unable to recognize settings instance, expect \"{pydantic_module}.BaseSettings\", "
|
||||
f"got {settings} instead"
|
||||
)
|
||||
|
||||
if pydantic_v1:
|
||||
return settings.dict(**kwargs)
|
||||
|
||||
return settings.model_dump(mode="python", **kwargs)
|
||||
|
||||
|
||||
cdef class Provider(object):
|
||||
cdef class Provider:
|
||||
"""Base provider class.
|
||||
|
||||
:py:class:`Provider` is callable (implements ``__call__`` method). Every
|
||||
|
@ -872,12 +875,9 @@ cdef class Dependency(Provider):
|
|||
|
||||
def set_instance_of(self, instance_of):
|
||||
"""Set type."""
|
||||
if not isinstance(instance_of, CLASS_TYPES):
|
||||
if not isinstance(instance_of, type):
|
||||
raise TypeError(
|
||||
"\"instance_of\" has incorrect type (expected {0}, got {1}))".format(
|
||||
CLASS_TYPES,
|
||||
instance_of,
|
||||
),
|
||||
f"\"instance_of\" is not a class (got {instance_of!r}))",
|
||||
)
|
||||
self._instance_of = instance_of
|
||||
return self
|
||||
|
@ -1212,8 +1212,8 @@ cdef class Callable(Provider):
|
|||
|
||||
copied = _memorized_duplicate(self, memo)
|
||||
copied.set_provides(_copy_if_provider(self.provides, memo))
|
||||
copied.set_args(*deepcopy(self.args, memo))
|
||||
copied.set_kwargs(**deepcopy(self.kwargs, memo))
|
||||
copied.set_args(*deepcopy_args(self, self.args, memo))
|
||||
copied.set_kwargs(**deepcopy_kwargs(self, self.kwargs, memo))
|
||||
self._copy_overridings(copied, memo)
|
||||
return copied
|
||||
|
||||
|
@ -1434,12 +1434,11 @@ cdef class Coroutine(Callable):
|
|||
some_coroutine.add_kwargs(keyword_argument1=3, keyword_argument=4)
|
||||
"""
|
||||
|
||||
_is_coroutine = _is_coroutine_marker
|
||||
_is_coroutine_marker = _is_coroutine_marker # Python >=3.12
|
||||
_is_coroutine = _is_coroutine # Python <3.16
|
||||
|
||||
def set_provides(self, provides):
|
||||
"""Set provider provides."""
|
||||
if not asyncio:
|
||||
raise Error("Package asyncio is not available")
|
||||
provides = _resolve_string_import(provides)
|
||||
if provides and not asyncio.iscoroutinefunction(provides):
|
||||
raise Error(f"Provider {_class_qualname(self)} expected to get coroutine function, "
|
||||
|
@ -1672,7 +1671,7 @@ cdef class ConfigurationOption(Provider):
|
|||
try:
|
||||
parser = _parse_ini_file(
|
||||
filepath,
|
||||
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(),
|
||||
)
|
||||
except IOError as exception:
|
||||
if required is not False \
|
||||
|
@ -1731,10 +1730,11 @@ cdef class ConfigurationOption(Provider):
|
|||
raise
|
||||
return
|
||||
|
||||
config_content = _resolve_config_env_markers(
|
||||
config_content,
|
||||
envs_required=envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
|
||||
)
|
||||
if envs_required is not None:
|
||||
config_content = _resolve_config_env_markers(
|
||||
config_content,
|
||||
envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
|
||||
)
|
||||
config = yaml.load(config_content, loader)
|
||||
|
||||
current_config = self.__call__()
|
||||
|
@ -1769,10 +1769,11 @@ cdef class ConfigurationOption(Provider):
|
|||
raise
|
||||
return
|
||||
|
||||
config_content = _resolve_config_env_markers(
|
||||
config_content,
|
||||
envs_required=envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
|
||||
)
|
||||
if envs_required is not None:
|
||||
config_content = _resolve_config_env_markers(
|
||||
config_content,
|
||||
envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
|
||||
)
|
||||
config = json.loads(config_content)
|
||||
|
||||
current_config = self.__call__()
|
||||
|
@ -1786,36 +1787,20 @@ cdef class ConfigurationOption(Provider):
|
|||
Loaded configuration is merged recursively over existing configuration.
|
||||
|
||||
:param settings: Pydantic settings instances.
|
||||
:type settings: :py:class:`pydantic.BaseSettings`
|
||||
:type settings: :py:class:`pydantic.BaseSettings` (pydantic v1) or
|
||||
:py:class:`pydantic_settings.BaseSettings` (pydantic v2 and onwards)
|
||||
|
||||
:param required: When required is True, raise an exception if settings dict is empty.
|
||||
:type required: bool
|
||||
|
||||
:param kwargs: Keyword arguments forwarded to ``pydantic.BaseSettings.dict()`` call.
|
||||
:param kwargs: Keyword arguments forwarded to ``pydantic.BaseSettings.dict()`` or
|
||||
``pydantic_settings.BaseSettings.model_dump()`` call (based on pydantic version).
|
||||
:type kwargs: Dict[Any, Any]
|
||||
|
||||
:rtype: None
|
||||
"""
|
||||
if pydantic is None:
|
||||
raise Error(
|
||||
"Unable to load pydantic configuration - pydantic is not installed. "
|
||||
"Install pydantic or install Dependency Injector with pydantic extras: "
|
||||
"\"pip install dependency-injector[pydantic]\""
|
||||
)
|
||||
|
||||
if isinstance(settings, CLASS_TYPES) and issubclass(settings, pydantic.BaseSettings):
|
||||
raise Error(
|
||||
"Got settings class, but expect instance: "
|
||||
"instead \"{0}\" use \"{0}()\"".format(settings.__name__)
|
||||
)
|
||||
|
||||
if not isinstance(settings, pydantic.BaseSettings):
|
||||
raise Error(
|
||||
"Unable to recognize settings instance, expect \"pydantic.BaseSettings\", "
|
||||
"got {0} instead".format(settings)
|
||||
)
|
||||
|
||||
self.from_dict(settings.dict(**kwargs), required=required)
|
||||
self.from_dict(pydantic_settings_to_dict(settings, kwargs), required=required)
|
||||
|
||||
def from_dict(self, options, required=UNDEFINED):
|
||||
"""Load configuration from the dictionary.
|
||||
|
@ -2241,7 +2226,7 @@ cdef class Configuration(Object):
|
|||
try:
|
||||
parser = _parse_ini_file(
|
||||
filepath,
|
||||
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(),
|
||||
)
|
||||
except IOError as exception:
|
||||
if required is not False \
|
||||
|
@ -2300,10 +2285,11 @@ cdef class Configuration(Object):
|
|||
raise
|
||||
return
|
||||
|
||||
config_content = _resolve_config_env_markers(
|
||||
config_content,
|
||||
envs_required=envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
|
||||
)
|
||||
if envs_required is not None:
|
||||
config_content = _resolve_config_env_markers(
|
||||
config_content,
|
||||
envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
|
||||
)
|
||||
config = yaml.load(config_content, loader)
|
||||
|
||||
current_config = self.__call__()
|
||||
|
@ -2338,10 +2324,11 @@ cdef class Configuration(Object):
|
|||
raise
|
||||
return
|
||||
|
||||
config_content = _resolve_config_env_markers(
|
||||
config_content,
|
||||
envs_required=envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
|
||||
)
|
||||
if envs_required is not None:
|
||||
config_content = _resolve_config_env_markers(
|
||||
config_content,
|
||||
envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
|
||||
)
|
||||
config = json.loads(config_content)
|
||||
|
||||
current_config = self.__call__()
|
||||
|
@ -2355,7 +2342,8 @@ cdef class Configuration(Object):
|
|||
Loaded configuration is merged recursively over existing configuration.
|
||||
|
||||
:param settings: Pydantic settings instances.
|
||||
:type settings: :py:class:`pydantic.BaseSettings`
|
||||
:type settings: :py:class:`pydantic.BaseSettings` (pydantic v1) or
|
||||
:py:class:`pydantic_settings.BaseSettings` (pydantic v2 and onwards)
|
||||
|
||||
:param required: When required is True, raise an exception if settings dict is empty.
|
||||
:type required: bool
|
||||
|
@ -2365,26 +2353,8 @@ cdef class Configuration(Object):
|
|||
|
||||
:rtype: None
|
||||
"""
|
||||
if pydantic is None:
|
||||
raise Error(
|
||||
"Unable to load pydantic configuration - pydantic is not installed. "
|
||||
"Install pydantic or install Dependency Injector with pydantic extras: "
|
||||
"\"pip install dependency-injector[pydantic]\""
|
||||
)
|
||||
|
||||
if isinstance(settings, CLASS_TYPES) and issubclass(settings, pydantic.BaseSettings):
|
||||
raise Error(
|
||||
"Got settings class, but expect instance: "
|
||||
"instead \"{0}\" use \"{0}()\"".format(settings.__name__)
|
||||
)
|
||||
|
||||
if not isinstance(settings, pydantic.BaseSettings):
|
||||
raise Error(
|
||||
"Unable to recognize settings instance, expect \"pydantic.BaseSettings\", "
|
||||
"got {0} instead".format(settings)
|
||||
)
|
||||
|
||||
self.from_dict(settings.dict(**kwargs), required=required)
|
||||
self.from_dict(pydantic_settings_to_dict(settings, kwargs), required=required)
|
||||
|
||||
def from_dict(self, options, required=UNDEFINED):
|
||||
"""Load configuration from the dictionary.
|
||||
|
@ -2532,8 +2502,8 @@ cdef class Factory(Provider):
|
|||
|
||||
copied = _memorized_duplicate(self, memo)
|
||||
copied.set_provides(_copy_if_provider(self.provides, memo))
|
||||
copied.set_args(*deepcopy(self.args, memo))
|
||||
copied.set_kwargs(**deepcopy(self.kwargs, memo))
|
||||
copied.set_args(*deepcopy_args(self, self.args, memo))
|
||||
copied.set_kwargs(**deepcopy_kwargs(self, self.kwargs, memo))
|
||||
copied.set_attributes(**deepcopy(self.attributes, memo))
|
||||
self._copy_overridings(copied, memo)
|
||||
return copied
|
||||
|
@ -2831,8 +2801,8 @@ cdef class BaseSingleton(Provider):
|
|||
|
||||
copied = _memorized_duplicate(self, memo)
|
||||
copied.set_provides(_copy_if_provider(self.provides, memo))
|
||||
copied.set_args(*deepcopy(self.args, memo))
|
||||
copied.set_kwargs(**deepcopy(self.kwargs, memo))
|
||||
copied.set_args(*deepcopy_args(self, self.args, memo))
|
||||
copied.set_kwargs(**deepcopy_kwargs(self, self.kwargs, memo))
|
||||
copied.set_attributes(**deepcopy(self.attributes, memo))
|
||||
self._copy_overridings(copied, memo)
|
||||
return copied
|
||||
|
@ -3214,8 +3184,8 @@ cdef class ThreadLocalSingleton(BaseSingleton):
|
|||
return future_result
|
||||
|
||||
self._storage.instance = instance
|
||||
finally:
|
||||
return instance
|
||||
|
||||
return instance
|
||||
|
||||
def _async_init_instance(self, future_result, result):
|
||||
try:
|
||||
|
@ -3444,7 +3414,7 @@ cdef class List(Provider):
|
|||
return copied
|
||||
|
||||
copied = _memorized_duplicate(self, memo)
|
||||
copied.set_args(*deepcopy(self.args, memo))
|
||||
copied.set_args(*deepcopy_args(self, self.args, memo))
|
||||
self._copy_overridings(copied, memo)
|
||||
return copied
|
||||
|
||||
|
@ -3667,8 +3637,8 @@ cdef class Resource(Provider):
|
|||
|
||||
copied = _memorized_duplicate(self, memo)
|
||||
copied.set_provides(_copy_if_provider(self.provides, memo))
|
||||
copied.set_args(*deepcopy(self.args, memo))
|
||||
copied.set_kwargs(**deepcopy(self.kwargs, memo))
|
||||
copied.set_args(*deepcopy_args(self, self.args, memo))
|
||||
copied.set_kwargs(**deepcopy_kwargs(self, self.kwargs, memo))
|
||||
|
||||
self._copy_overridings(copied, memo)
|
||||
|
||||
|
@ -3967,18 +3937,14 @@ cdef class Resource(Provider):
|
|||
|
||||
@staticmethod
|
||||
def _is_resource_subclass(instance):
|
||||
if sys.version_info < (3, 5):
|
||||
return False
|
||||
if not isinstance(instance, CLASS_TYPES):
|
||||
if not isinstance(instance, type):
|
||||
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):
|
||||
if not isinstance(instance, type):
|
||||
return
|
||||
from . import resources
|
||||
return issubclass(instance, resources.AsyncResource)
|
||||
|
@ -4518,8 +4484,8 @@ cdef class MethodCaller(Provider):
|
|||
|
||||
copied = _memorized_duplicate(self, memo)
|
||||
copied.set_provides(_copy_if_provider(self.provides, memo))
|
||||
copied.set_args(*deepcopy(self.args, memo))
|
||||
copied.set_kwargs(**deepcopy(self.kwargs, memo))
|
||||
copied.set_args(*deepcopy_args(self, self.args, memo))
|
||||
copied.set_kwargs(**deepcopy_kwargs(self, self.kwargs, memo))
|
||||
self._copy_overridings(copied, memo)
|
||||
return copied
|
||||
|
||||
|
@ -4636,7 +4602,7 @@ cdef class MethodCaller(Provider):
|
|||
future_result.set_result(result)
|
||||
|
||||
|
||||
cdef class Injection(object):
|
||||
cdef class Injection:
|
||||
"""Abstract injection class."""
|
||||
|
||||
|
||||
|
@ -4763,7 +4729,7 @@ cpdef tuple parse_named_injections(dict kwargs):
|
|||
return tuple(injections)
|
||||
|
||||
|
||||
cdef class OverridingContext(object):
|
||||
cdef class OverridingContext:
|
||||
"""Provider overriding context.
|
||||
|
||||
:py:class:`OverridingContext` is used by :py:meth:`Provider.override` for
|
||||
|
@ -4799,7 +4765,7 @@ cdef class OverridingContext(object):
|
|||
self._overridden.reset_last_overriding()
|
||||
|
||||
|
||||
cdef class BaseSingletonResetContext(object):
|
||||
cdef class BaseSingletonResetContext:
|
||||
|
||||
def __init__(self, Provider provider):
|
||||
self._singleton = provider
|
||||
|
@ -4835,7 +4801,7 @@ cpdef bint is_provider(object instance):
|
|||
|
||||
:rtype: bool
|
||||
"""
|
||||
return (not isinstance(instance, CLASS_TYPES) and
|
||||
return (not isinstance(instance, type) and
|
||||
getattr(instance, "__IS_PROVIDER__", False) is True)
|
||||
|
||||
|
||||
|
@ -4863,7 +4829,7 @@ cpdef bint is_delegated(object instance):
|
|||
|
||||
:rtype: bool
|
||||
"""
|
||||
return (not isinstance(instance, CLASS_TYPES) and
|
||||
return (not isinstance(instance, type) and
|
||||
getattr(instance, "__IS_DELEGATED__", False) is True)
|
||||
|
||||
|
||||
|
@ -4894,7 +4860,7 @@ cpdef bint is_container_instance(object instance):
|
|||
|
||||
:rtype: bool
|
||||
"""
|
||||
return (not isinstance(instance, CLASS_TYPES) and
|
||||
return (not isinstance(instance, type) and
|
||||
getattr(instance, "__IS_CONTAINER__", False) is True)
|
||||
|
||||
|
||||
|
@ -4906,7 +4872,7 @@ cpdef bint is_container_class(object instance):
|
|||
|
||||
:rtype: bool
|
||||
"""
|
||||
return (isinstance(instance, CLASS_TYPES) and
|
||||
return (isinstance(instance, type) and
|
||||
getattr(instance, "__IS_CONTAINER__", False) is True)
|
||||
|
||||
|
||||
|
@ -4920,6 +4886,48 @@ cpdef object deepcopy(object instance, dict memo=None):
|
|||
return copy.deepcopy(instance, memo)
|
||||
|
||||
|
||||
cpdef tuple deepcopy_args(
|
||||
Provider provider,
|
||||
tuple args,
|
||||
dict[int, object] memo = None,
|
||||
):
|
||||
"""A wrapper for deepcopy for positional arguments.
|
||||
|
||||
Used to improve debugability of objects that cannot be deep-copied.
|
||||
"""
|
||||
|
||||
cdef list[object] out = []
|
||||
|
||||
for i, arg in enumerate(args):
|
||||
try:
|
||||
out.append(copy.deepcopy(arg, memo))
|
||||
except Exception as e:
|
||||
raise NonCopyableArgumentError(provider, index=i) from e
|
||||
|
||||
return tuple(out)
|
||||
|
||||
|
||||
cpdef dict[str, object] deepcopy_kwargs(
|
||||
Provider provider,
|
||||
dict[str, object] kwargs,
|
||||
dict[int, object] memo = None,
|
||||
):
|
||||
"""A wrapper for deepcopy for keyword arguments.
|
||||
|
||||
Used to improve debugability of objects that cannot be deep-copied.
|
||||
"""
|
||||
|
||||
cdef dict[str, object] out = {}
|
||||
|
||||
for name, arg in kwargs.items():
|
||||
try:
|
||||
out[name] = copy.deepcopy(arg, memo)
|
||||
except Exception as e:
|
||||
raise NonCopyableArgumentError(provider, keyword=name) from e
|
||||
|
||||
return out
|
||||
|
||||
|
||||
def __add_sys_streams(memo):
|
||||
"""Add system streams to memo dictionary.
|
||||
|
||||
|
|
|
@ -10,18 +10,14 @@ T = TypeVar("T")
|
|||
class Resource(Generic[T], metaclass=abc.ABCMeta):
|
||||
|
||||
@abc.abstractmethod
|
||||
def init(self, *args, **kwargs) -> Optional[T]:
|
||||
...
|
||||
def init(self, *args, **kwargs) -> Optional[T]: ...
|
||||
|
||||
def shutdown(self, resource: Optional[T]) -> None:
|
||||
...
|
||||
def shutdown(self, resource: Optional[T]) -> None: ...
|
||||
|
||||
|
||||
class AsyncResource(Generic[T], metaclass=abc.ABCMeta):
|
||||
|
||||
@abc.abstractmethod
|
||||
async def init(self, *args, **kwargs) -> Optional[T]:
|
||||
...
|
||||
async def init(self, *args, **kwargs) -> Optional[T]: ...
|
||||
|
||||
async def shutdown(self, resource: Optional[T]) -> None:
|
||||
...
|
||||
async def shutdown(self, resource: Optional[T]) -> None: ...
|
||||
|
|
|
@ -27,9 +27,9 @@ class SchemaProcessorV1:
|
|||
return self._container.providers
|
||||
|
||||
def _create_providers(
|
||||
self,
|
||||
provider_schema: ProviderSchema,
|
||||
container: Optional[containers.Container] = None,
|
||||
self,
|
||||
provider_schema: ProviderSchema,
|
||||
container: Optional[containers.Container] = None,
|
||||
) -> None:
|
||||
if container is None:
|
||||
container = self._container
|
||||
|
@ -57,9 +57,9 @@ class SchemaProcessorV1:
|
|||
self._create_providers(provider_schema=data, container=provider)
|
||||
|
||||
def _setup_injections( # noqa: C901
|
||||
self,
|
||||
provider_schema: ProviderSchema,
|
||||
container: Optional[containers.Container] = None,
|
||||
self,
|
||||
provider_schema: ProviderSchema,
|
||||
container: Optional[containers.Container] = None,
|
||||
) -> None:
|
||||
if container is None:
|
||||
container = self._container
|
||||
|
@ -72,7 +72,7 @@ class SchemaProcessorV1:
|
|||
provides = data.get("provides")
|
||||
if provides:
|
||||
if isinstance(provides, str) and provides.startswith("container."):
|
||||
provides = self._resolve_provider(provides[len("container."):])
|
||||
provides = self._resolve_provider(provides[len("container.") :])
|
||||
else:
|
||||
provides = _import_string(provides)
|
||||
provider.set_provides(provides)
|
||||
|
@ -83,7 +83,7 @@ class SchemaProcessorV1:
|
|||
injection = None
|
||||
|
||||
if isinstance(arg, str) and arg.startswith("container."):
|
||||
injection = self._resolve_provider(arg[len("container."):])
|
||||
injection = self._resolve_provider(arg[len("container.") :])
|
||||
|
||||
# TODO: refactoring
|
||||
if isinstance(arg, dict):
|
||||
|
@ -91,16 +91,23 @@ class SchemaProcessorV1:
|
|||
provider_type = _get_provider_cls(arg.get("provider"))
|
||||
provides = arg.get("provides")
|
||||
if provides:
|
||||
if isinstance(provides, str) and provides.startswith("container."):
|
||||
provides = self._resolve_provider(provides[len("container."):])
|
||||
if isinstance(provides, str) and provides.startswith(
|
||||
"container."
|
||||
):
|
||||
provides = self._resolve_provider(
|
||||
provides[len("container.") :]
|
||||
)
|
||||
else:
|
||||
provides = _import_string(provides)
|
||||
provider_args.append(provides)
|
||||
for provider_arg in arg.get("args", []):
|
||||
if isinstance(provider_arg, str) \
|
||||
and provider_arg.startswith("container."):
|
||||
if isinstance(
|
||||
provider_arg, str
|
||||
) and provider_arg.startswith("container."):
|
||||
provider_args.append(
|
||||
self._resolve_provider(provider_arg[len("container."):]),
|
||||
self._resolve_provider(
|
||||
provider_arg[len("container.") :]
|
||||
),
|
||||
)
|
||||
injection = provider_type(*provider_args)
|
||||
|
||||
|
@ -117,7 +124,7 @@ class SchemaProcessorV1:
|
|||
injection = None
|
||||
|
||||
if isinstance(arg, str) and arg.startswith("container."):
|
||||
injection = self._resolve_provider(arg[len("container."):])
|
||||
injection = self._resolve_provider(arg[len("container.") :])
|
||||
|
||||
# TODO: refactoring
|
||||
if isinstance(arg, dict):
|
||||
|
@ -125,16 +132,23 @@ class SchemaProcessorV1:
|
|||
provider_type = _get_provider_cls(arg.get("provider"))
|
||||
provides = arg.get("provides")
|
||||
if provides:
|
||||
if isinstance(provides, str) and provides.startswith("container."):
|
||||
provides = self._resolve_provider(provides[len("container."):])
|
||||
if isinstance(provides, str) and provides.startswith(
|
||||
"container."
|
||||
):
|
||||
provides = self._resolve_provider(
|
||||
provides[len("container.") :]
|
||||
)
|
||||
else:
|
||||
provides = _import_string(provides)
|
||||
provider_args.append(provides)
|
||||
for provider_arg in arg.get("args", []):
|
||||
if isinstance(provider_arg, str) \
|
||||
and provider_arg.startswith("container."):
|
||||
if isinstance(
|
||||
provider_arg, str
|
||||
) and provider_arg.startswith("container."):
|
||||
provider_args.append(
|
||||
self._resolve_provider(provider_arg[len("container."):]),
|
||||
self._resolve_provider(
|
||||
provider_arg[len("container.") :]
|
||||
),
|
||||
)
|
||||
injection = provider_type(*provider_args)
|
||||
|
||||
|
@ -158,7 +172,7 @@ class SchemaProcessorV1:
|
|||
for segment in segments[1:]:
|
||||
parentheses = ""
|
||||
if "(" in segment and ")" in segment:
|
||||
parentheses = segment[segment.find("("):segment.rfind(")")+1]
|
||||
parentheses = segment[segment.find("(") : segment.rfind(")") + 1]
|
||||
segment = segment.replace(parentheses, "")
|
||||
|
||||
try:
|
||||
|
@ -190,10 +204,12 @@ def _get_provider_cls(provider_cls_name: str) -> Type[providers.Provider]:
|
|||
if custom_provider_type:
|
||||
return custom_provider_type
|
||||
|
||||
raise SchemaError(f"Undefined provider class \"{provider_cls_name}\"")
|
||||
raise SchemaError(f'Undefined provider class "{provider_cls_name}"')
|
||||
|
||||
|
||||
def _fetch_provider_cls_from_std(provider_cls_name: str) -> Optional[Type[providers.Provider]]:
|
||||
def _fetch_provider_cls_from_std(
|
||||
provider_cls_name: str,
|
||||
) -> Optional[Type[providers.Provider]]:
|
||||
return getattr(providers, provider_cls_name, None)
|
||||
|
||||
|
||||
|
@ -201,12 +217,16 @@ def _import_provider_cls(provider_cls_name: str) -> Optional[Type[providers.Prov
|
|||
try:
|
||||
cls = _import_string(provider_cls_name)
|
||||
except (ImportError, ValueError) as exception:
|
||||
raise SchemaError(f"Can not import provider \"{provider_cls_name}\"") from exception
|
||||
raise SchemaError(
|
||||
f'Can not import provider "{provider_cls_name}"'
|
||||
) from exception
|
||||
except AttributeError:
|
||||
return None
|
||||
else:
|
||||
if isinstance(cls, type) and not issubclass(cls, providers.Provider):
|
||||
raise SchemaError(f"Provider class \"{cls}\" is not a subclass of providers base class")
|
||||
raise SchemaError(
|
||||
f'Provider class "{cls}" is not a subclass of providers base class'
|
||||
)
|
||||
return cls
|
||||
|
||||
|
||||
|
|
|
@ -1,34 +1,28 @@
|
|||
"""Wiring module."""
|
||||
|
||||
import functools
|
||||
import inspect
|
||||
import importlib
|
||||
import importlib.machinery
|
||||
import inspect
|
||||
import pkgutil
|
||||
import warnings
|
||||
import sys
|
||||
from types import ModuleType
|
||||
from typing import (
|
||||
Optional,
|
||||
Iterable,
|
||||
Iterator,
|
||||
Callable,
|
||||
Any,
|
||||
Tuple,
|
||||
Callable,
|
||||
Dict,
|
||||
Generic,
|
||||
TypeVar,
|
||||
Type,
|
||||
Union,
|
||||
Iterable,
|
||||
Iterator,
|
||||
Optional,
|
||||
Set,
|
||||
Tuple,
|
||||
Type,
|
||||
TypeVar,
|
||||
Union,
|
||||
cast,
|
||||
)
|
||||
|
||||
if sys.version_info < (3, 7):
|
||||
from typing import GenericMeta
|
||||
else:
|
||||
class GenericMeta(type):
|
||||
...
|
||||
|
||||
# Hotfix, see: https://github.com/ets-labs/python-dependency-injector/issues/362
|
||||
if sys.version_info >= (3, 9):
|
||||
|
@ -36,6 +30,21 @@ if sys.version_info >= (3, 9):
|
|||
else:
|
||||
GenericAlias = None
|
||||
|
||||
if sys.version_info >= (3, 9):
|
||||
from typing import Annotated, get_args, get_origin
|
||||
else:
|
||||
try:
|
||||
from typing_extensions import Annotated, get_args, get_origin
|
||||
except ImportError:
|
||||
Annotated = object()
|
||||
|
||||
# For preventing NameError. Never executes
|
||||
def get_args(hint):
|
||||
return ()
|
||||
|
||||
def get_origin(tp):
|
||||
return None
|
||||
|
||||
|
||||
try:
|
||||
import fastapi.params
|
||||
|
@ -57,13 +66,6 @@ except ImportError:
|
|||
|
||||
from . import providers
|
||||
|
||||
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,
|
||||
)
|
||||
|
||||
__all__ = (
|
||||
"wire",
|
||||
|
@ -99,7 +101,9 @@ class PatchedRegistry:
|
|||
def register_callable(self, patched: "PatchedCallable") -> None:
|
||||
self._callables[patched.patched] = patched
|
||||
|
||||
def get_callables_from_module(self, module: ModuleType) -> Iterator[Callable[..., Any]]:
|
||||
def get_callables_from_module(
|
||||
self, module: ModuleType
|
||||
) -> Iterator[Callable[..., Any]]:
|
||||
for patched_callable in self._callables.values():
|
||||
if not patched_callable.is_in_module(module):
|
||||
continue
|
||||
|
@ -114,7 +118,9 @@ class PatchedRegistry:
|
|||
def register_attribute(self, patched: "PatchedAttribute") -> None:
|
||||
self._attributes.add(patched)
|
||||
|
||||
def get_attributes_from_module(self, module: ModuleType) -> Iterator["PatchedAttribute"]:
|
||||
def get_attributes_from_module(
|
||||
self, module: ModuleType
|
||||
) -> Iterator["PatchedAttribute"]:
|
||||
for attribute in self._attributes:
|
||||
if not attribute.is_in_module(module):
|
||||
continue
|
||||
|
@ -139,11 +145,11 @@ class PatchedCallable:
|
|||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
patched: Optional[Callable[..., Any]] = None,
|
||||
original: Optional[Callable[..., Any]] = None,
|
||||
reference_injections: Optional[Dict[Any, Any]] = None,
|
||||
reference_closing: Optional[Dict[Any, Any]] = None,
|
||||
self,
|
||||
patched: Optional[Callable[..., Any]] = None,
|
||||
original: Optional[Callable[..., Any]] = None,
|
||||
reference_injections: Optional[Dict[Any, Any]] = None,
|
||||
reference_closing: Optional[Dict[Any, Any]] = None,
|
||||
) -> None:
|
||||
self.patched = patched
|
||||
self.original = original
|
||||
|
@ -214,18 +220,21 @@ class ProvidersMap:
|
|||
)
|
||||
|
||||
def resolve_provider(
|
||||
self,
|
||||
provider: Union[providers.Provider, str],
|
||||
modifier: Optional["Modifier"] = None,
|
||||
self,
|
||||
provider: Union[providers.Provider, str],
|
||||
modifier: Optional["Modifier"] = None,
|
||||
) -> Optional[providers.Provider]:
|
||||
if isinstance(provider, providers.Delegate):
|
||||
return self._resolve_delegate(provider)
|
||||
elif isinstance(provider, (
|
||||
providers.ProvidedInstance,
|
||||
providers.AttributeGetter,
|
||||
providers.ItemGetter,
|
||||
providers.MethodCaller,
|
||||
)):
|
||||
elif isinstance(
|
||||
provider,
|
||||
(
|
||||
providers.ProvidedInstance,
|
||||
providers.AttributeGetter,
|
||||
providers.ItemGetter,
|
||||
providers.MethodCaller,
|
||||
),
|
||||
):
|
||||
return self._resolve_provided_instance(provider)
|
||||
elif isinstance(provider, providers.ConfigurationOption):
|
||||
return self._resolve_config_option(provider)
|
||||
|
@ -237,9 +246,9 @@ class ProvidersMap:
|
|||
return self._resolve_provider(provider)
|
||||
|
||||
def _resolve_string_id(
|
||||
self,
|
||||
id: str,
|
||||
modifier: Optional["Modifier"] = None,
|
||||
self,
|
||||
id: str,
|
||||
modifier: Optional["Modifier"] = None,
|
||||
) -> Optional[providers.Provider]:
|
||||
if id == self.CONTAINER_STRING_ID:
|
||||
return self._container.__self__
|
||||
|
@ -256,16 +265,19 @@ class ProvidersMap:
|
|||
return provider
|
||||
|
||||
def _resolve_provided_instance(
|
||||
self,
|
||||
original: providers.Provider,
|
||||
self,
|
||||
original: providers.Provider,
|
||||
) -> Optional[providers.Provider]:
|
||||
modifiers = []
|
||||
while isinstance(original, (
|
||||
while isinstance(
|
||||
original,
|
||||
(
|
||||
providers.ProvidedInstance,
|
||||
providers.AttributeGetter,
|
||||
providers.ItemGetter,
|
||||
providers.MethodCaller,
|
||||
)):
|
||||
),
|
||||
):
|
||||
modifiers.insert(0, original)
|
||||
original = original.provides
|
||||
|
||||
|
@ -289,8 +301,8 @@ class ProvidersMap:
|
|||
return new
|
||||
|
||||
def _resolve_delegate(
|
||||
self,
|
||||
original: providers.Delegate,
|
||||
self,
|
||||
original: providers.Delegate,
|
||||
) -> Optional[providers.Provider]:
|
||||
provider = self._resolve_provider(original.provides)
|
||||
if provider:
|
||||
|
@ -298,9 +310,9 @@ class ProvidersMap:
|
|||
return provider
|
||||
|
||||
def _resolve_config_option(
|
||||
self,
|
||||
original: providers.ConfigurationOption,
|
||||
as_: Any = None,
|
||||
self,
|
||||
original: providers.ConfigurationOption,
|
||||
as_: Any = None,
|
||||
) -> Optional[providers.Provider]:
|
||||
original_root = original.root
|
||||
new = self._resolve_provider(original_root)
|
||||
|
@ -324,8 +336,8 @@ class ProvidersMap:
|
|||
return new
|
||||
|
||||
def _resolve_provider(
|
||||
self,
|
||||
original: providers.Provider,
|
||||
self,
|
||||
original: providers.Provider,
|
||||
) -> Optional[providers.Provider]:
|
||||
try:
|
||||
return self._map[original]
|
||||
|
@ -334,9 +346,9 @@ class ProvidersMap:
|
|||
|
||||
@classmethod
|
||||
def _create_providers_map(
|
||||
cls,
|
||||
current_container: Container,
|
||||
original_container: Container,
|
||||
cls,
|
||||
current_container: Container,
|
||||
original_container: Container,
|
||||
) -> Dict[providers.Provider, providers.Provider]:
|
||||
current_providers = current_container.providers
|
||||
current_providers["__self__"] = current_container.__self__
|
||||
|
@ -349,8 +361,9 @@ class ProvidersMap:
|
|||
original_provider = original_providers[provider_name]
|
||||
providers_map[original_provider] = current_provider
|
||||
|
||||
if isinstance(current_provider, providers.Container) \
|
||||
and isinstance(original_provider, providers.Container):
|
||||
if isinstance(current_provider, providers.Container) and isinstance(
|
||||
original_provider, providers.Container
|
||||
):
|
||||
subcontainer_map = cls._create_providers_map(
|
||||
current_container=current_provider.container,
|
||||
original_container=original_provider.container,
|
||||
|
@ -376,19 +389,21 @@ class InspectFilter:
|
|||
return werkzeug and isinstance(instance, werkzeug.local.LocalProxy)
|
||||
|
||||
def _is_starlette_request_cls(self, instance: object) -> bool:
|
||||
return starlette \
|
||||
and isinstance(instance, type) \
|
||||
and _safe_is_subclass(instance, starlette.requests.Request)
|
||||
return (
|
||||
starlette
|
||||
and isinstance(instance, type)
|
||||
and _safe_is_subclass(instance, starlette.requests.Request)
|
||||
)
|
||||
|
||||
def _is_builtin(self, instance: object) -> bool:
|
||||
return inspect.isbuiltin(instance)
|
||||
|
||||
|
||||
def wire( # noqa: C901
|
||||
container: Container,
|
||||
*,
|
||||
modules: Optional[Iterable[ModuleType]] = None,
|
||||
packages: Optional[Iterable[ModuleType]] = None,
|
||||
container: Container,
|
||||
*,
|
||||
modules: Optional[Iterable[ModuleType]] = None,
|
||||
packages: Optional[Iterable[ModuleType]] = None,
|
||||
) -> None:
|
||||
"""Wire container providers with provided packages and modules."""
|
||||
modules = [*modules] if modules else []
|
||||
|
@ -418,18 +433,22 @@ def wire( # noqa: C901
|
|||
else:
|
||||
for cls_member_name, cls_member in cls_members:
|
||||
if _is_marker(cls_member):
|
||||
_patch_attribute(cls, cls_member_name, cls_member, providers_map)
|
||||
_patch_attribute(
|
||||
cls, cls_member_name, cls_member, providers_map
|
||||
)
|
||||
elif _is_method(cls_member):
|
||||
_patch_method(cls, cls_member_name, cls_member, providers_map)
|
||||
_patch_method(
|
||||
cls, cls_member_name, cls_member, providers_map
|
||||
)
|
||||
|
||||
for patched in _patched_registry.get_callables_from_module(module):
|
||||
_bind_injections(patched, providers_map)
|
||||
|
||||
|
||||
def unwire( # noqa: C901
|
||||
*,
|
||||
modules: Optional[Iterable[ModuleType]] = None,
|
||||
packages: Optional[Iterable[ModuleType]] = None,
|
||||
*,
|
||||
modules: Optional[Iterable[ModuleType]] = None,
|
||||
packages: Optional[Iterable[ModuleType]] = None,
|
||||
) -> None:
|
||||
"""Wire provided packages and modules with previous wired providers."""
|
||||
modules = [*modules] if modules else []
|
||||
|
@ -443,7 +462,9 @@ def unwire( # noqa: C901
|
|||
if inspect.isfunction(member):
|
||||
_unpatch(module, name, member)
|
||||
elif inspect.isclass(member):
|
||||
for method_name, method in inspect.getmembers(member, inspect.isfunction):
|
||||
for method_name, method in inspect.getmembers(
|
||||
member, inspect.isfunction
|
||||
):
|
||||
_unpatch(member, method_name, method)
|
||||
|
||||
for patched in _patched_registry.get_callables_from_module(module):
|
||||
|
@ -462,10 +483,10 @@ def inject(fn: F) -> F:
|
|||
|
||||
|
||||
def _patch_fn(
|
||||
module: ModuleType,
|
||||
name: str,
|
||||
fn: Callable[..., Any],
|
||||
providers_map: ProvidersMap,
|
||||
module: ModuleType,
|
||||
name: str,
|
||||
fn: Callable[..., Any],
|
||||
providers_map: ProvidersMap,
|
||||
) -> None:
|
||||
if not _is_patched(fn):
|
||||
reference_injections, reference_closing = _fetch_reference_injections(fn)
|
||||
|
@ -479,14 +500,16 @@ def _patch_fn(
|
|||
|
||||
|
||||
def _patch_method(
|
||||
cls: Type,
|
||||
name: str,
|
||||
method: Callable[..., Any],
|
||||
providers_map: ProvidersMap,
|
||||
cls: Type,
|
||||
name: str,
|
||||
method: Callable[..., Any],
|
||||
providers_map: ProvidersMap,
|
||||
) -> None:
|
||||
if hasattr(cls, "__dict__") \
|
||||
and name in cls.__dict__ \
|
||||
and isinstance(cls.__dict__[name], (classmethod, staticmethod)):
|
||||
if (
|
||||
hasattr(cls, "__dict__")
|
||||
and name in cls.__dict__
|
||||
and isinstance(cls.__dict__[name], (classmethod, staticmethod))
|
||||
):
|
||||
method = cls.__dict__[name]
|
||||
fn = method.__func__
|
||||
else:
|
||||
|
@ -507,13 +530,15 @@ def _patch_method(
|
|||
|
||||
|
||||
def _unpatch(
|
||||
module: ModuleType,
|
||||
name: str,
|
||||
fn: Callable[..., Any],
|
||||
module: ModuleType,
|
||||
name: str,
|
||||
fn: Callable[..., Any],
|
||||
) -> None:
|
||||
if hasattr(module, "__dict__") \
|
||||
and name in module.__dict__ \
|
||||
and isinstance(module.__dict__[name], (classmethod, staticmethod)):
|
||||
if (
|
||||
hasattr(module, "__dict__")
|
||||
and name in module.__dict__
|
||||
and isinstance(module.__dict__[name], (classmethod, staticmethod))
|
||||
):
|
||||
method = module.__dict__[name]
|
||||
fn = method.__func__
|
||||
|
||||
|
@ -524,10 +549,10 @@ def _unpatch(
|
|||
|
||||
|
||||
def _patch_attribute(
|
||||
member: Any,
|
||||
name: str,
|
||||
marker: "_Marker",
|
||||
providers_map: ProvidersMap,
|
||||
member: Any,
|
||||
name: str,
|
||||
marker: "_Marker",
|
||||
providers_map: ProvidersMap,
|
||||
) -> None:
|
||||
provider = providers_map.resolve_provider(marker.provider, marker.modifier)
|
||||
if provider is None:
|
||||
|
@ -548,16 +573,33 @@ def _unpatch_attribute(patched: PatchedAttribute) -> None:
|
|||
setattr(patched.member, patched.name, patched.marker)
|
||||
|
||||
|
||||
def _extract_marker(parameter: inspect.Parameter) -> Optional["_Marker"]:
|
||||
if get_origin(parameter.annotation) is Annotated:
|
||||
marker = get_args(parameter.annotation)[1]
|
||||
else:
|
||||
marker = parameter.default
|
||||
|
||||
if not isinstance(marker, _Marker) and not _is_fastapi_depends(marker):
|
||||
return None
|
||||
|
||||
if _is_fastapi_depends(marker):
|
||||
marker = marker.dependency
|
||||
|
||||
if not isinstance(marker, _Marker):
|
||||
return None
|
||||
|
||||
return marker
|
||||
|
||||
|
||||
def _fetch_reference_injections( # noqa: C901
|
||||
fn: Callable[..., Any],
|
||||
fn: Callable[..., Any],
|
||||
) -> Tuple[Dict[str, Any], Dict[str, Any]]:
|
||||
# Hotfix, see:
|
||||
# - https://github.com/ets-labs/python-dependency-injector/issues/362
|
||||
# - https://github.com/ets-labs/python-dependency-injector/issues/398
|
||||
if GenericAlias and any((
|
||||
fn is GenericAlias,
|
||||
getattr(fn, "__func__", None) is GenericAlias
|
||||
)):
|
||||
if GenericAlias and any(
|
||||
(fn is GenericAlias, getattr(fn, "__func__", None) is GenericAlias)
|
||||
):
|
||||
fn = fn.__init__
|
||||
|
||||
try:
|
||||
|
@ -573,18 +615,11 @@ def _fetch_reference_injections( # noqa: C901
|
|||
injections = {}
|
||||
closing = {}
|
||||
for parameter_name, parameter in signature.parameters.items():
|
||||
if not isinstance(parameter.default, _Marker) \
|
||||
and not _is_fastapi_depends(parameter.default):
|
||||
marker = _extract_marker(parameter)
|
||||
|
||||
if marker is None:
|
||||
continue
|
||||
|
||||
marker = parameter.default
|
||||
|
||||
if _is_fastapi_depends(marker):
|
||||
marker = marker.dependency
|
||||
|
||||
if not isinstance(marker, _Marker):
|
||||
continue
|
||||
|
||||
if isinstance(marker, Closing):
|
||||
marker = marker.provider
|
||||
closing[parameter_name] = marker
|
||||
|
@ -593,20 +628,19 @@ def _fetch_reference_injections( # noqa: C901
|
|||
return injections, closing
|
||||
|
||||
|
||||
def _locate_dependent_closing_args(provider: providers.Provider) -> Dict[str, providers.Provider]:
|
||||
if not hasattr(provider, "args"):
|
||||
return {}
|
||||
|
||||
closing_deps = {}
|
||||
for arg in provider.args:
|
||||
if not isinstance(arg, providers.Provider) or not hasattr(arg, "args"):
|
||||
def _locate_dependent_closing_args(
|
||||
provider: providers.Provider, closing_deps: Dict[str, providers.Provider]
|
||||
) -> Dict[str, providers.Provider]:
|
||||
for arg in [
|
||||
*getattr(provider, "args", []),
|
||||
*getattr(provider, "kwargs", {}).values(),
|
||||
]:
|
||||
if not isinstance(arg, providers.Provider):
|
||||
continue
|
||||
if isinstance(arg, providers.Resource):
|
||||
closing_deps[str(id(arg))] = arg
|
||||
|
||||
if not arg.args and isinstance(arg, providers.Resource):
|
||||
return {str(id(arg)): arg}
|
||||
else:
|
||||
closing_deps += _locate_dependent_closing_args(arg)
|
||||
return closing_deps
|
||||
_locate_dependent_closing_args(arg, closing_deps)
|
||||
|
||||
|
||||
def _bind_injections(fn: Callable[..., Any], providers_map: ProvidersMap) -> None:
|
||||
|
@ -630,7 +664,8 @@ def _bind_injections(fn: Callable[..., Any], providers_map: ProvidersMap) -> Non
|
|||
|
||||
if injection in patched_callable.reference_closing:
|
||||
patched_callable.add_closing(injection, provider)
|
||||
deps = _locate_dependent_closing_args(provider)
|
||||
deps = {}
|
||||
_locate_dependent_closing_args(provider, deps)
|
||||
for key, dep in deps.items():
|
||||
patched_callable.add_closing(key, dep)
|
||||
|
||||
|
@ -647,8 +682,8 @@ def _fetch_modules(package):
|
|||
if not hasattr(package, "__path__") or not hasattr(package, "__name__"):
|
||||
return modules
|
||||
for module_info in pkgutil.walk_packages(
|
||||
path=package.__path__,
|
||||
prefix=package.__name__ + ".",
|
||||
path=package.__path__,
|
||||
prefix=package.__name__ + ".",
|
||||
):
|
||||
module = importlib.import_module(module_info.name)
|
||||
modules.append(module)
|
||||
|
@ -664,9 +699,9 @@ def _is_marker(member) -> bool:
|
|||
|
||||
|
||||
def _get_patched(
|
||||
fn: F,
|
||||
reference_injections: Dict[Any, Any],
|
||||
reference_closing: Dict[Any, Any],
|
||||
fn: F,
|
||||
reference_injections: Dict[Any, Any],
|
||||
reference_closing: Dict[Any, Any],
|
||||
) -> F:
|
||||
patched_object = PatchedCallable(
|
||||
original=fn,
|
||||
|
@ -694,9 +729,11 @@ def _is_patched(fn) -> bool:
|
|||
|
||||
|
||||
def _is_declarative_container(instance: Any) -> bool:
|
||||
return (isinstance(instance, type)
|
||||
and getattr(instance, "__IS_CONTAINER__", False) is True
|
||||
and getattr(instance, "declarative_parent", None) is None)
|
||||
return (
|
||||
isinstance(instance, type)
|
||||
and getattr(instance, "__IS_CONTAINER__", False) is True
|
||||
and getattr(instance, "declarative_parent", None) is None
|
||||
)
|
||||
|
||||
|
||||
def _safe_is_subclass(instance: Any, cls: Type) -> bool:
|
||||
|
@ -709,11 +746,10 @@ def _safe_is_subclass(instance: Any, cls: Type) -> bool:
|
|||
class Modifier:
|
||||
|
||||
def modify(
|
||||
self,
|
||||
provider: providers.ConfigurationOption,
|
||||
providers_map: ProvidersMap,
|
||||
) -> providers.Provider:
|
||||
...
|
||||
self,
|
||||
provider: providers.ConfigurationOption,
|
||||
providers_map: ProvidersMap,
|
||||
) -> providers.Provider: ...
|
||||
|
||||
|
||||
class TypeModifier(Modifier):
|
||||
|
@ -722,9 +758,9 @@ class TypeModifier(Modifier):
|
|||
self.type_ = type_
|
||||
|
||||
def modify(
|
||||
self,
|
||||
provider: providers.ConfigurationOption,
|
||||
providers_map: ProvidersMap,
|
||||
self,
|
||||
provider: providers.ConfigurationOption,
|
||||
providers_map: ProvidersMap,
|
||||
) -> providers.Provider:
|
||||
return provider.as_(self.type_)
|
||||
|
||||
|
@ -762,9 +798,9 @@ class RequiredModifier(Modifier):
|
|||
return self
|
||||
|
||||
def modify(
|
||||
self,
|
||||
provider: providers.ConfigurationOption,
|
||||
providers_map: ProvidersMap,
|
||||
self,
|
||||
provider: providers.ConfigurationOption,
|
||||
providers_map: ProvidersMap,
|
||||
) -> providers.Provider:
|
||||
provider = provider.required()
|
||||
if self.type_modifier:
|
||||
|
@ -783,9 +819,9 @@ class InvariantModifier(Modifier):
|
|||
self.id = id
|
||||
|
||||
def modify(
|
||||
self,
|
||||
provider: providers.ConfigurationOption,
|
||||
providers_map: ProvidersMap,
|
||||
self,
|
||||
provider: providers.ConfigurationOption,
|
||||
providers_map: ProvidersMap,
|
||||
) -> providers.Provider:
|
||||
invariant_segment = providers_map.resolve_provider(self.id)
|
||||
return provider[invariant_segment]
|
||||
|
@ -818,9 +854,9 @@ class ProvidedInstance(Modifier):
|
|||
return self
|
||||
|
||||
def modify(
|
||||
self,
|
||||
provider: providers.Provider,
|
||||
providers_map: ProvidersMap,
|
||||
self,
|
||||
provider: providers.Provider,
|
||||
providers_map: ProvidersMap,
|
||||
) -> providers.Provider:
|
||||
provider = provider.provided
|
||||
for type_, value in self.segments:
|
||||
|
@ -838,22 +874,14 @@ def provided() -> ProvidedInstance:
|
|||
return ProvidedInstance()
|
||||
|
||||
|
||||
class ClassGetItemMeta(GenericMeta):
|
||||
def __getitem__(cls, item):
|
||||
# Spike for Python 3.6
|
||||
if isinstance(item, tuple):
|
||||
return cls(*item)
|
||||
return cls(item)
|
||||
|
||||
|
||||
class _Marker(Generic[T], metaclass=ClassGetItemMeta):
|
||||
class _Marker(Generic[T]):
|
||||
|
||||
__IS_MARKER__ = True
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
provider: Union[providers.Provider, Container, str],
|
||||
modifier: Optional[Modifier] = None,
|
||||
self,
|
||||
provider: Union[providers.Provider, Container, str],
|
||||
modifier: Optional[Modifier] = None,
|
||||
) -> None:
|
||||
if _is_declarative_container(provider):
|
||||
provider = provider.__self__
|
||||
|
@ -869,16 +897,13 @@ class _Marker(Generic[T], metaclass=ClassGetItemMeta):
|
|||
return self
|
||||
|
||||
|
||||
class Provide(_Marker):
|
||||
...
|
||||
class Provide(_Marker): ...
|
||||
|
||||
|
||||
class Provider(_Marker):
|
||||
...
|
||||
class Provider(_Marker): ...
|
||||
|
||||
|
||||
class Closing(_Marker):
|
||||
...
|
||||
class Closing(_Marker): ...
|
||||
|
||||
|
||||
class AutoLoader:
|
||||
|
@ -928,8 +953,7 @@ class AutoLoader:
|
|||
super().exec_module(module)
|
||||
loader.wire_module(module)
|
||||
|
||||
class ExtensionFileLoader(importlib.machinery.ExtensionFileLoader):
|
||||
...
|
||||
class ExtensionFileLoader(importlib.machinery.ExtensionFileLoader): ...
|
||||
|
||||
loader_details = [
|
||||
(SourcelessFileLoader, importlib.machinery.BYTECODE_SUFFIXES),
|
||||
|
@ -982,7 +1006,7 @@ _inspect_filter = InspectFilter()
|
|||
_loader = AutoLoader()
|
||||
|
||||
# Optimizations
|
||||
from ._cwiring import _get_sync_patched # noqa
|
||||
from ._cwiring import _sync_inject # noqa
|
||||
from ._cwiring import _async_inject # noqa
|
||||
|
||||
|
||||
|
@ -998,4 +1022,18 @@ def _get_async_patched(fn: F, patched: PatchedCallable) -> F:
|
|||
patched.injections,
|
||||
patched.closing,
|
||||
)
|
||||
return _patched
|
||||
|
||||
return cast(F, _patched)
|
||||
|
||||
|
||||
def _get_sync_patched(fn: F, patched: PatchedCallable) -> F:
|
||||
@functools.wraps(fn)
|
||||
def _patched(*args, **kwargs):
|
||||
return _sync_inject(
|
||||
fn,
|
||||
args,
|
||||
kwargs,
|
||||
patched.injections,
|
||||
patched.closing,
|
||||
)
|
||||
return cast(F, _patched)
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
[pytest]
|
||||
testpaths = tests/unit/
|
||||
python_files = test_*_py2_py3.py
|
||||
asyncio_mode = auto
|
||||
filterwarnings =
|
||||
ignore:Module \"dependency_injector.ext.aiohttp\" is deprecated since version 4\.0\.0:DeprecationWarning
|
||||
ignore:Module \"dependency_injector.ext.flask\" is deprecated since version 4\.0\.0:DeprecationWarning
|
||||
ignore:Please use \`.*?\` from the \`scipy.*?\`(.*?)namespace is deprecated\.:DeprecationWarning
|
||||
ignore:The \`scipy(.*?)\` namespace is deprecated(.*):DeprecationWarning
|
||||
ignore:ssl\.PROTOCOL_TLS is deprecated:DeprecationWarning:botocore.*
|
|
@ -1,10 +0,0 @@
|
|||
[pytest]
|
||||
testpaths = tests/unit/
|
||||
python_files = test_*_py3.py
|
||||
asyncio_mode = auto
|
||||
filterwarnings =
|
||||
ignore:Module \"dependency_injector.ext.aiohttp\" is deprecated since version 4\.0\.0:DeprecationWarning
|
||||
ignore:Module \"dependency_injector.ext.flask\" is deprecated since version 4\.0\.0:DeprecationWarning
|
||||
ignore:Please use \`.*?\` from the \`scipy.*?\`(.*?)namespace is deprecated\.:DeprecationWarning
|
||||
ignore:The \`scipy(.*?)\` namespace is deprecated(.*):DeprecationWarning
|
||||
ignore:ssl\.PROTOCOL_TLS is deprecated:DeprecationWarning:botocore.*
|
|
@ -2,6 +2,8 @@
|
|||
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
|
||||
|
|
|
@ -34,7 +34,7 @@ kwargs4: Dict[str, Any] = provider4.kwargs
|
|||
|
||||
# Test 5: to check the provided instance interface
|
||||
provider5 = providers.Callable(Animal)
|
||||
provided5: providers.ProvidedInstance = provider5.provided
|
||||
provided5: Animal = provider5.provided()
|
||||
attr_getter5: providers.AttributeGetter = provider5.provided.attr
|
||||
item_getter5: providers.ItemGetter = provider5.provided["item"]
|
||||
method_caller: providers.MethodCaller = provider5.provided.method.call(123, arg=324)
|
||||
|
|
|
@ -2,7 +2,7 @@ from pathlib import Path
|
|||
from typing import Any
|
||||
|
||||
from dependency_injector import providers
|
||||
from pydantic import BaseSettings as PydanticSettings
|
||||
from pydantic_settings import BaseSettings as PydanticSettings
|
||||
|
||||
|
||||
# Test 1: to check the getattr
|
||||
|
|
|
@ -34,7 +34,7 @@ provider5 = providers.Dict(
|
|||
a1=providers.Factory(object),
|
||||
a2=providers.Factory(object),
|
||||
)
|
||||
provided5: providers.ProvidedInstance = provider5.provided
|
||||
provided5: dict[Any, Any] = provider5.provided()
|
||||
|
||||
|
||||
# Test 6: to check the return type with await
|
||||
|
|
|
@ -37,7 +37,7 @@ attributes4: Dict[str, Any] = provider4.attributes
|
|||
|
||||
# Test 5: to check the provided instance interface
|
||||
provider5 = providers.Factory(Animal)
|
||||
provided5: providers.ProvidedInstance = provider5.provided
|
||||
provided5: Animal = provider5.provided()
|
||||
attr_getter5: providers.AttributeGetter = provider5.provided.attr
|
||||
item_getter5: providers.ItemGetter = provider5.provided["item"]
|
||||
method_caller5: providers.MethodCaller = provider5.provided.method.call(123, arg=324)
|
||||
|
|
|
@ -23,7 +23,7 @@ provider3 = providers.List(
|
|||
providers.Factory(object),
|
||||
providers.Factory(object),
|
||||
)
|
||||
provided3: providers.ProvidedInstance = provider3.provided
|
||||
provided3: List[Any] = provider3.provided()
|
||||
attr_getter3: providers.AttributeGetter = provider3.provided.attr
|
||||
item_getter3: providers.ItemGetter = provider3.provided["item"]
|
||||
method_caller3: providers.MethodCaller = provider3.provided.method.call(123, arg=324)
|
||||
|
|
|
@ -9,7 +9,7 @@ var1: int = provider1()
|
|||
|
||||
# Test 2: to check the provided instance interface
|
||||
provider2 = providers.Object(int)
|
||||
provided2: providers.ProvidedInstance = provider2.provided
|
||||
provided2: Type[int] = provider2.provided()
|
||||
attr_getter2: providers.AttributeGetter = provider2.provided.attr
|
||||
item_getter2: providers.ItemGetter = provider2.provided["item"]
|
||||
method_caller2: providers.MethodCaller = provider2.provided.method.call(123, arg=324)
|
||||
|
|
|
@ -3,7 +3,8 @@ from dependency_injector import providers
|
|||
|
||||
# Test 1: to check .provided attribute
|
||||
provider1: providers.Provider[int] = providers.Object(1)
|
||||
provided: providers.ProvidedInstance = provider1.provided
|
||||
provided: int = provider1.provided()
|
||||
provider1_delegate: providers.Provider[int] = provider1.provider
|
||||
|
||||
# Test 2: to check async mode API
|
||||
provider2: providers.Provider = providers.Provider()
|
||||
|
|
|
@ -37,7 +37,7 @@ attributes4: Dict[str, Any] = provider4.attributes
|
|||
|
||||
# Test 5: to check the provided instance interface
|
||||
provider5 = providers.Singleton(Animal)
|
||||
provided5: providers.ProvidedInstance = provider5.provided
|
||||
provided5: Animal = provider5.provided()
|
||||
attr_getter5: providers.AttributeGetter = provider5.provided.attr
|
||||
item_getter5: providers.ItemGetter = provider5.provided["item"]
|
||||
method_caller5: providers.MethodCaller = provider5.provided.method.call(123, arg=324)
|
||||
|
|
41
tests/unit/ext/test_starlette.py
Normal file
41
tests/unit/ext/test_starlette.py
Normal file
|
@ -0,0 +1,41 @@
|
|||
from typing import AsyncIterator, Iterator
|
||||
from unittest.mock import ANY
|
||||
|
||||
from pytest import mark
|
||||
|
||||
from dependency_injector.containers import DeclarativeContainer
|
||||
from dependency_injector.ext.starlette import Lifespan
|
||||
from dependency_injector.providers import Resource
|
||||
|
||||
|
||||
class TestLifespan:
|
||||
@mark.parametrize("sync", [False, True])
|
||||
@mark.asyncio
|
||||
async def test_context_manager(self, sync: bool) -> None:
|
||||
init, shutdown = False, False
|
||||
|
||||
def sync_resource() -> Iterator[None]:
|
||||
nonlocal init, shutdown
|
||||
|
||||
init = True
|
||||
yield
|
||||
shutdown = True
|
||||
|
||||
async def async_resource() -> AsyncIterator[None]:
|
||||
nonlocal init, shutdown
|
||||
|
||||
init = True
|
||||
yield
|
||||
shutdown = True
|
||||
|
||||
class Container(DeclarativeContainer):
|
||||
x = Resource(sync_resource if sync else async_resource)
|
||||
|
||||
container = Container()
|
||||
lifespan = Lifespan(container)
|
||||
|
||||
async with lifespan(ANY) as scope:
|
||||
assert scope is None
|
||||
assert init
|
||||
|
||||
assert shutdown
|
|
@ -5,6 +5,23 @@ import os
|
|||
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):
|
||||
config.from_ini(ini_config_file_3)
|
||||
|
||||
|
|
|
@ -6,6 +6,23 @@ import os
|
|||
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):
|
||||
config.from_json(json_config_file_3)
|
||||
|
||||
|
|
|
@ -1,41 +1,60 @@
|
|||
"""Configuration.from_pydantic() tests."""
|
||||
|
||||
import pydantic
|
||||
from dependency_injector import providers, errors
|
||||
from pydantic import BaseModel
|
||||
|
||||
try:
|
||||
from pydantic_settings import (
|
||||
BaseSettings, # type: ignore[import-not-found,unused-ignore]
|
||||
)
|
||||
except ImportError:
|
||||
try:
|
||||
from pydantic import BaseSettings # type: ignore[no-redef,unused-ignore]
|
||||
except ImportError:
|
||||
|
||||
class BaseSettings: # type: ignore[no-redef]
|
||||
"""No-op fallback"""
|
||||
|
||||
|
||||
from pytest import fixture, mark, raises
|
||||
|
||||
from dependency_injector import errors, providers
|
||||
|
||||
class Section11(pydantic.BaseModel):
|
||||
value1 = 1
|
||||
pytestmark = mark.pydantic
|
||||
|
||||
|
||||
class Section12(pydantic.BaseModel):
|
||||
value2 = 2
|
||||
class Section11(BaseModel):
|
||||
value1: int = 1
|
||||
|
||||
|
||||
class Settings1(pydantic.BaseSettings):
|
||||
section1 = Section11()
|
||||
section2 = Section12()
|
||||
class Section12(BaseModel):
|
||||
value2: int = 2
|
||||
|
||||
|
||||
class Section21(pydantic.BaseModel):
|
||||
value1 = 11
|
||||
value11 = 11
|
||||
class Settings1(BaseSettings):
|
||||
section1: Section11 = Section11()
|
||||
section2: Section12 = Section12()
|
||||
|
||||
|
||||
class Section3(pydantic.BaseModel):
|
||||
value3 = 3
|
||||
class Section21(BaseModel):
|
||||
value1: int = 11
|
||||
value11: int = 11
|
||||
|
||||
|
||||
class Settings2(pydantic.BaseSettings):
|
||||
section1 = Section21()
|
||||
section3 = Section3()
|
||||
class Section3(BaseModel):
|
||||
value3: int = 3
|
||||
|
||||
|
||||
class Settings2(BaseSettings):
|
||||
section1: Section21 = Section21()
|
||||
section3: Section3 = Section3()
|
||||
|
||||
|
||||
@fixture
|
||||
def no_pydantic_module_installed():
|
||||
providers.pydantic = None
|
||||
has_pydantic_settings = providers.has_pydantic_settings
|
||||
providers.has_pydantic_settings = False
|
||||
yield
|
||||
providers.pydantic = pydantic
|
||||
providers.has_pydantic_settings = has_pydantic_settings
|
||||
|
||||
|
||||
def test(config):
|
||||
|
@ -82,66 +101,70 @@ def test_merge(config):
|
|||
|
||||
|
||||
def test_empty_settings(config):
|
||||
config.from_pydantic(pydantic.BaseSettings())
|
||||
config.from_pydantic(BaseSettings())
|
||||
assert config() == {}
|
||||
|
||||
|
||||
@mark.parametrize("config_type", ["strict"])
|
||||
def test_empty_settings_strict_mode(config):
|
||||
with raises(ValueError):
|
||||
config.from_pydantic(pydantic.BaseSettings())
|
||||
config.from_pydantic(BaseSettings())
|
||||
|
||||
|
||||
def test_option_empty_settings(config):
|
||||
config.option.from_pydantic(pydantic.BaseSettings())
|
||||
config.option.from_pydantic(BaseSettings())
|
||||
assert config.option() == {}
|
||||
|
||||
|
||||
@mark.parametrize("config_type", ["strict"])
|
||||
def test_option_empty_settings_strict_mode(config):
|
||||
with raises(ValueError):
|
||||
config.option.from_pydantic(pydantic.BaseSettings())
|
||||
config.option.from_pydantic(BaseSettings())
|
||||
|
||||
|
||||
def test_required_empty_settings(config):
|
||||
with raises(ValueError):
|
||||
config.from_pydantic(pydantic.BaseSettings(), required=True)
|
||||
config.from_pydantic(BaseSettings(), required=True)
|
||||
|
||||
|
||||
def test_required_option_empty_settings(config):
|
||||
with raises(ValueError):
|
||||
config.option.from_pydantic(pydantic.BaseSettings(), required=True)
|
||||
config.option.from_pydantic(BaseSettings(), required=True)
|
||||
|
||||
|
||||
@mark.parametrize("config_type", ["strict"])
|
||||
def test_not_required_empty_settings_strict_mode(config):
|
||||
config.from_pydantic(pydantic.BaseSettings(), required=False)
|
||||
config.from_pydantic(BaseSettings(), required=False)
|
||||
assert config() == {}
|
||||
|
||||
|
||||
@mark.parametrize("config_type", ["strict"])
|
||||
def test_not_required_option_empty_settings_strict_mode(config):
|
||||
config.option.from_pydantic(pydantic.BaseSettings(), required=False)
|
||||
config.option.from_pydantic(BaseSettings(), required=False)
|
||||
assert config.option() == {}
|
||||
assert config() == {"option": {}}
|
||||
|
||||
|
||||
def test_not_instance_of_settings(config):
|
||||
with raises(errors.Error) as error:
|
||||
with raises(
|
||||
errors.Error,
|
||||
match=(
|
||||
r"Unable to recognize settings instance, expect \"pydantic(?:_settings)?\.BaseSettings\", "
|
||||
r"got {0} instead".format({})
|
||||
),
|
||||
):
|
||||
config.from_pydantic({})
|
||||
assert error.value.args[0] == (
|
||||
"Unable to recognize settings instance, expect \"pydantic.BaseSettings\", "
|
||||
"got {0} instead".format({})
|
||||
)
|
||||
|
||||
|
||||
def test_option_not_instance_of_settings(config):
|
||||
with raises(errors.Error) as error:
|
||||
with raises(
|
||||
errors.Error,
|
||||
match=(
|
||||
r"Unable to recognize settings instance, expect \"pydantic(?:_settings)?\.BaseSettings\", "
|
||||
"got {0} instead".format({})
|
||||
),
|
||||
):
|
||||
config.option.from_pydantic({})
|
||||
assert error.value.args[0] == (
|
||||
"Unable to recognize settings instance, expect \"pydantic.BaseSettings\", "
|
||||
"got {0} instead".format({})
|
||||
)
|
||||
|
||||
|
||||
def test_subclass_instead_of_instance(config):
|
||||
|
@ -164,21 +187,25 @@ def test_option_subclass_instead_of_instance(config):
|
|||
|
||||
@mark.usefixtures("no_pydantic_module_installed")
|
||||
def test_no_pydantic_installed(config):
|
||||
with raises(errors.Error) as error:
|
||||
with raises(
|
||||
errors.Error,
|
||||
match=(
|
||||
r"Unable to load pydantic configuration - pydantic(?:_settings)? is not installed\. "
|
||||
r"Install pydantic or install Dependency Injector with pydantic extras: "
|
||||
r"\"pip install dependency-injector\[pydantic2?\]\""
|
||||
),
|
||||
):
|
||||
config.from_pydantic(Settings1())
|
||||
assert error.value.args[0] == (
|
||||
"Unable to load pydantic configuration - pydantic is not installed. "
|
||||
"Install pydantic or install Dependency Injector with pydantic extras: "
|
||||
"\"pip install dependency-injector[pydantic]\""
|
||||
)
|
||||
|
||||
|
||||
@mark.usefixtures("no_pydantic_module_installed")
|
||||
def test_option_no_pydantic_installed(config):
|
||||
with raises(errors.Error) as error:
|
||||
with raises(
|
||||
errors.Error,
|
||||
match=(
|
||||
r"Unable to load pydantic configuration - pydantic(?:_settings)? is not installed\. "
|
||||
r"Install pydantic or install Dependency Injector with pydantic extras: "
|
||||
r"\"pip install dependency-injector\[pydantic2?\]\""
|
||||
),
|
||||
):
|
||||
config.option.from_pydantic(Settings1())
|
||||
assert error.value.args[0] == (
|
||||
"Unable to load pydantic configuration - pydantic is not installed. "
|
||||
"Install pydantic or install Dependency Injector with pydantic extras: "
|
||||
"\"pip install dependency-injector[pydantic]\""
|
||||
)
|
||||
|
|
|
@ -6,6 +6,23 @@ import yaml
|
|||
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):
|
||||
config.from_yaml(yaml_config_file_3)
|
||||
|
||||
|
|
|
@ -1,35 +1,52 @@
|
|||
"""Configuration.from_pydantic() tests."""
|
||||
|
||||
import pydantic
|
||||
from dependency_injector import providers
|
||||
from pydantic import BaseModel
|
||||
|
||||
try:
|
||||
from pydantic_settings import (
|
||||
BaseSettings, # type: ignore[import-not-found,unused-ignore]
|
||||
)
|
||||
except ImportError:
|
||||
try:
|
||||
from pydantic import BaseSettings # type: ignore[no-redef,unused-ignore]
|
||||
except ImportError:
|
||||
|
||||
class BaseSettings: # type: ignore[no-redef]
|
||||
"""No-op fallback"""
|
||||
|
||||
|
||||
from pytest import fixture, mark, raises
|
||||
|
||||
from dependency_injector import providers
|
||||
|
||||
class Section11(pydantic.BaseModel):
|
||||
pytestmark = mark.pydantic
|
||||
|
||||
|
||||
class Section11(BaseModel):
|
||||
value1: int = 1
|
||||
|
||||
|
||||
class Section12(pydantic.BaseModel):
|
||||
class Section12(BaseModel):
|
||||
value2: int = 2
|
||||
|
||||
|
||||
class Settings1(pydantic.BaseSettings):
|
||||
class Settings1(BaseSettings):
|
||||
section1: Section11 = Section11()
|
||||
section2: Section12 = Section12()
|
||||
|
||||
|
||||
class Section21(pydantic.BaseModel):
|
||||
class Section21(BaseModel):
|
||||
value1: int = 11
|
||||
value11: int = 11
|
||||
|
||||
|
||||
class Section3(pydantic.BaseModel):
|
||||
class Section3(BaseModel):
|
||||
value3: int = 3
|
||||
|
||||
|
||||
class Settings2(pydantic.BaseSettings):
|
||||
class Settings2(BaseSettings):
|
||||
section1: Section21 = Section21()
|
||||
section3: Section3= Section3()
|
||||
section3: Section3 = Section3()
|
||||
|
||||
|
||||
@fixture
|
||||
|
@ -86,10 +103,10 @@ def test_copy(config, pydantic_settings_1, pydantic_settings_2):
|
|||
|
||||
|
||||
def test_set_pydantic_settings(config):
|
||||
class Settings3(pydantic.BaseSettings):
|
||||
class Settings3(BaseSettings):
|
||||
...
|
||||
|
||||
class Settings4(pydantic.BaseSettings):
|
||||
class Settings4(BaseSettings):
|
||||
...
|
||||
|
||||
settings_3 = Settings3()
|
||||
|
@ -100,27 +117,27 @@ def test_set_pydantic_settings(config):
|
|||
|
||||
|
||||
def test_file_does_not_exist(config):
|
||||
config.set_pydantic_settings([pydantic.BaseSettings()])
|
||||
config.set_pydantic_settings([BaseSettings()])
|
||||
config.load()
|
||||
assert config() == {}
|
||||
|
||||
|
||||
@mark.parametrize("config_type", ["strict"])
|
||||
def test_file_does_not_exist_strict_mode(config):
|
||||
config.set_pydantic_settings([pydantic.BaseSettings()])
|
||||
config.set_pydantic_settings([BaseSettings()])
|
||||
with raises(ValueError):
|
||||
config.load()
|
||||
assert config() == {}
|
||||
|
||||
|
||||
def test_required_file_does_not_exist(config):
|
||||
config.set_pydantic_settings([pydantic.BaseSettings()])
|
||||
config.set_pydantic_settings([BaseSettings()])
|
||||
with raises(ValueError):
|
||||
config.load(required=True)
|
||||
|
||||
|
||||
@mark.parametrize("config_type", ["strict"])
|
||||
def test_not_required_file_does_not_exist_strict_mode(config):
|
||||
config.set_pydantic_settings([pydantic.BaseSettings()])
|
||||
config.set_pydantic_settings([BaseSettings()])
|
||||
config.load(required=False)
|
||||
assert config() == {}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
"""Coroutine provider tests."""
|
||||
import sys
|
||||
|
||||
from dependency_injector import providers, errors
|
||||
from pytest import mark, raises
|
||||
|
@ -208,3 +209,17 @@ def test_repr():
|
|||
"<dependency_injector.providers."
|
||||
"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))
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
import pytest
|
||||
|
||||
from dependency_injector.containers import Container
|
||||
from dependency_injector.providers import ThreadLocalSingleton
|
||||
|
||||
|
||||
class FailingClass:
|
||||
def __init__(self):
|
||||
raise ValueError("FAILING CLASS")
|
||||
|
||||
|
||||
class TestContainer(Container):
|
||||
failing_class = ThreadLocalSingleton(FailingClass)
|
||||
|
||||
|
||||
def test_on_failure_value_error_is_raised():
|
||||
container = TestContainer()
|
||||
|
||||
with pytest.raises(ValueError, match="FAILING CLASS"):
|
||||
container.failing_class()
|
65
tests/unit/providers/utils/test_deepcopy_py3.py
Normal file
65
tests/unit/providers/utils/test_deepcopy_py3.py
Normal file
|
@ -0,0 +1,65 @@
|
|||
import sys
|
||||
from typing import Any, Dict, NoReturn
|
||||
|
||||
from pytest import raises
|
||||
|
||||
from dependency_injector.errors import NonCopyableArgumentError
|
||||
from dependency_injector.providers import (
|
||||
Provider,
|
||||
deepcopy,
|
||||
deepcopy_args,
|
||||
deepcopy_kwargs,
|
||||
)
|
||||
|
||||
|
||||
class NonCopiable:
|
||||
def __deepcopy__(self, memo: Dict[int, Any]) -> NoReturn:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def test_deepcopy_streams_not_copied() -> None:
|
||||
l = [sys.stdin, sys.stdout, sys.stderr]
|
||||
assert deepcopy(l) == l
|
||||
|
||||
|
||||
def test_deepcopy_args() -> None:
|
||||
provider = Provider[None]()
|
||||
copiable = NonCopiable()
|
||||
memo: Dict[int, Any] = {id(copiable): copiable}
|
||||
|
||||
assert deepcopy_args(provider, (1, copiable), memo) == (1, copiable)
|
||||
|
||||
|
||||
def test_deepcopy_args_non_copiable() -> None:
|
||||
provider = Provider[None]()
|
||||
copiable = NonCopiable()
|
||||
memo: Dict[int, Any] = {id(copiable): copiable}
|
||||
|
||||
with raises(
|
||||
NonCopyableArgumentError,
|
||||
match=r"^Couldn't copy argument at index 3 for provider ",
|
||||
):
|
||||
deepcopy_args(provider, (1, copiable, object(), NonCopiable()), memo)
|
||||
|
||||
|
||||
def test_deepcopy_kwargs() -> None:
|
||||
provider = Provider[None]()
|
||||
copiable = NonCopiable()
|
||||
memo: Dict[int, Any] = {id(copiable): copiable}
|
||||
|
||||
assert deepcopy_kwargs(provider, {"x": 1, "y": copiable}, memo) == {
|
||||
"x": 1,
|
||||
"y": copiable,
|
||||
}
|
||||
|
||||
|
||||
def test_deepcopy_kwargs_non_copiable() -> None:
|
||||
provider = Provider[None]()
|
||||
copiable = NonCopiable()
|
||||
memo: Dict[int, Any] = {id(copiable): copiable}
|
||||
|
||||
with raises(
|
||||
NonCopyableArgumentError,
|
||||
match=r"^Couldn't copy keyword argument z for provider ",
|
||||
):
|
||||
deepcopy_kwargs(provider, {"x": 1, "y": copiable, "z": NonCopiable()}, memo)
|
|
@ -1,7 +1,11 @@
|
|||
import sys
|
||||
|
||||
from typing_extensions import Annotated
|
||||
|
||||
from fastapi import FastAPI, Depends
|
||||
from fastapi import Request # See: https://github.com/ets-labs/python-dependency-injector/issues/398
|
||||
from fastapi import (
|
||||
Request,
|
||||
) # See: https://github.com/ets-labs/python-dependency-injector/issues/398
|
||||
from fastapi.security import HTTPBasic, HTTPBasicCredentials
|
||||
from dependency_injector import containers, providers
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
|
@ -28,11 +32,16 @@ async def index(service: Service = Depends(Provide[Container.service])):
|
|||
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")
|
||||
@inject
|
||||
def read_current_user(
|
||||
credentials: HTTPBasicCredentials = Depends(security)
|
||||
):
|
||||
def read_current_user(credentials: HTTPBasicCredentials = Depends(security)):
|
||||
return {"username": credentials.username, "password": credentials.password}
|
||||
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
from typing_extensions import Annotated
|
||||
|
||||
from flask import Flask, jsonify, request, current_app, session, g
|
||||
from flask import _request_ctx_stack, _app_ctx_stack
|
||||
from dependency_injector import containers, providers
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
|
||||
# This is here for testing wiring bypasses these objects without crashing
|
||||
request, current_app, session, g # noqa
|
||||
_request_ctx_stack, _app_ctx_stack # noqa
|
||||
|
||||
|
||||
class Service:
|
||||
|
@ -28,5 +28,12 @@ def index(service: Service = Provide[Container.service]):
|
|||
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.wire(modules=[__name__])
|
||||
|
|
|
@ -1,41 +1,80 @@
|
|||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from dependency_injector import containers, providers
|
||||
from dependency_injector.wiring import inject, Provide, Closing
|
||||
from dependency_injector.wiring import Closing, Provide, inject
|
||||
|
||||
|
||||
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:
|
||||
init_counter: int = 0
|
||||
shutdown_counter: int = 0
|
||||
def __init__(self, counter: Optional[Counter] = None, **dependencies: Any) -> None:
|
||||
self.counter = counter or Counter()
|
||||
self.dependencies = dependencies
|
||||
|
||||
@classmethod
|
||||
def reset_counter(cls):
|
||||
cls.init_counter = 0
|
||||
cls.shutdown_counter = 0
|
||||
def init(self) -> None:
|
||||
self.counter.init()
|
||||
|
||||
@classmethod
|
||||
def init(cls):
|
||||
cls.init_counter += 1
|
||||
def shutdown(self) -> None:
|
||||
self.counter.shutdown()
|
||||
|
||||
@classmethod
|
||||
def shutdown(cls):
|
||||
cls.shutdown_counter += 1
|
||||
@property
|
||||
def init_counter(self) -> int:
|
||||
return self.counter._init
|
||||
|
||||
@property
|
||||
def shutdown_counter(self) -> int:
|
||||
return self.counter._shutdown
|
||||
|
||||
|
||||
class FactoryService:
|
||||
def __init__(self, service: Service):
|
||||
def __init__(self, service: Service, service2: Service):
|
||||
self.service = service
|
||||
self.service2 = service2
|
||||
|
||||
|
||||
def init_service():
|
||||
service = Service()
|
||||
class NestedService:
|
||||
def __init__(self, factory_service: FactoryService):
|
||||
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()
|
||||
yield service
|
||||
service.shutdown()
|
||||
|
||||
|
||||
class Container(containers.DeclarativeContainer):
|
||||
|
||||
service = providers.Resource(init_service)
|
||||
factory_service = providers.Factory(FactoryService, service)
|
||||
counter = providers.Singleton(Counter)
|
||||
_list = providers.List(
|
||||
providers.Callable(lambda a: a, a=1), providers.Callable(lambda b: b, 2)
|
||||
)
|
||||
_dict = providers.Dict(
|
||||
a=providers.Callable(lambda a: a, a=3), b=providers.Callable(lambda b: b, 4)
|
||||
)
|
||||
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
|
||||
|
@ -44,5 +83,21 @@ def test_function(service: Service = Closing[Provide["service"]]):
|
|||
|
||||
|
||||
@inject
|
||||
def test_function_dependency(factory: FactoryService = Closing[Provide["factory_service"]]):
|
||||
def test_function_dependency(
|
||||
factory: FactoryService = Closing[Provide["factory_service"]],
|
||||
):
|
||||
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
|
||||
|
|
|
@ -2,13 +2,13 @@
|
|||
|
||||
from decimal import Decimal
|
||||
|
||||
from pytest import fixture, mark, raises
|
||||
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.wiring import Closing, Provide, Provider, wire
|
||||
from pytest import fixture, mark, raises
|
||||
|
||||
from samples.wiringstringids import module, package, resourceclosing
|
||||
from samples.wiringstringids.service import Service
|
||||
from samples.wiringstringids.container import Container, SubContainer
|
||||
|
||||
|
||||
@fixture(autouse=True)
|
||||
|
@ -34,10 +34,11 @@ def subcontainer():
|
|||
|
||||
|
||||
@fixture
|
||||
def resourceclosing_container():
|
||||
def resourceclosing_container(request):
|
||||
container = resourceclosing.Container()
|
||||
container.wire(modules=[resourceclosing])
|
||||
yield container
|
||||
with container.reset_singletons():
|
||||
yield container
|
||||
container.unwire()
|
||||
|
||||
|
||||
|
@ -274,42 +275,65 @@ def test_wire_multiple_containers():
|
|||
|
||||
@mark.usefixtures("resourceclosing_container")
|
||||
def test_closing_resource():
|
||||
resourceclosing.Service.reset_counter()
|
||||
|
||||
result_1 = resourceclosing.test_function()
|
||||
assert isinstance(result_1, resourceclosing.Service)
|
||||
assert result_1.init_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()
|
||||
assert isinstance(result_2, resourceclosing.Service)
|
||||
assert result_2.init_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
|
||||
|
||||
|
||||
@mark.usefixtures("resourceclosing_container")
|
||||
def test_closing_dependency_resource():
|
||||
resourceclosing.Service.reset_counter()
|
||||
|
||||
result_1 = resourceclosing.test_function_dependency()
|
||||
assert isinstance(result_1, resourceclosing.FactoryService)
|
||||
assert result_1.service.init_counter == 1
|
||||
assert result_1.service.shutdown_counter == 1
|
||||
assert result_1.service.init_counter == 2
|
||||
assert result_1.service.shutdown_counter == 2
|
||||
|
||||
result_2 = resourceclosing.test_function_dependency()
|
||||
|
||||
assert isinstance(result_2, resourceclosing.FactoryService)
|
||||
assert result_2.service.init_counter == 2
|
||||
assert result_2.service.shutdown_counter == 2
|
||||
assert result_2.service.init_counter == 4
|
||||
assert result_2.service.shutdown_counter == 4
|
||||
|
||||
|
||||
@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
|
||||
|
||||
|
||||
@mark.usefixtures("resourceclosing_container")
|
||||
def test_closing_resource_bypass_marker_injection():
|
||||
resourceclosing.Service.reset_counter()
|
||||
|
||||
result_1 = resourceclosing.test_function(service=Closing[Provide["service"]])
|
||||
assert isinstance(result_1, resourceclosing.Service)
|
||||
assert result_1.init_counter == 1
|
||||
|
@ -325,7 +349,6 @@ def test_closing_resource_bypass_marker_injection():
|
|||
|
||||
@mark.usefixtures("resourceclosing_container")
|
||||
def test_closing_resource_context():
|
||||
resourceclosing.Service.reset_counter()
|
||||
service = resourceclosing.Service()
|
||||
|
||||
result_1 = resourceclosing.test_function(service=service)
|
||||
|
|
|
@ -1,16 +1,20 @@
|
|||
from httpx import AsyncClient
|
||||
from httpx import ASGITransport, AsyncClient
|
||||
from pytest import fixture, mark
|
||||
from pytest_asyncio import fixture as aio_fixture
|
||||
|
||||
# Runtime import to avoid syntax errors in samples on Python < 3.5 and reach top-dir
|
||||
import os
|
||||
|
||||
_SAMPLES_DIR = os.path.abspath(
|
||||
os.path.sep.join((
|
||||
os.path.dirname(__file__),
|
||||
"../samples/",
|
||||
)),
|
||||
os.path.sep.join(
|
||||
(
|
||||
os.path.dirname(__file__),
|
||||
"../samples/",
|
||||
)
|
||||
),
|
||||
)
|
||||
import sys
|
||||
|
||||
sys.path.append(_SAMPLES_DIR)
|
||||
|
||||
|
||||
|
@ -19,7 +23,7 @@ from wiringfastapi import web
|
|||
|
||||
@aio_fixture
|
||||
async def async_client():
|
||||
client = AsyncClient(app=web.app, base_url="http://test")
|
||||
client = AsyncClient(transport=ASGITransport(app=web.app), base_url="http://test")
|
||||
yield client
|
||||
await client.aclose()
|
||||
|
||||
|
@ -37,6 +41,19 @@ async def test_depends_marker_injection(async_client: AsyncClient):
|
|||
assert response.json() == {"result": "Foo"}
|
||||
|
||||
|
||||
@mark.asyncio
|
||||
async def test_depends_with_annotated(async_client: AsyncClient):
|
||||
class ServiceMock:
|
||||
async def process(self):
|
||||
return "Foo"
|
||||
|
||||
with web.container.service.override(ServiceMock()):
|
||||
response = await async_client.get("/")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"result": "Foo"}
|
||||
|
||||
|
||||
@mark.asyncio
|
||||
async def test_depends_injection(async_client: AsyncClient):
|
||||
response = await async_client.get("/auth", auth=("john_smith", "secret"))
|
||||
|
|
|
@ -2,19 +2,25 @@ import json
|
|||
|
||||
# Runtime import to avoid syntax errors in samples on Python < 3.5 and reach top-dir
|
||||
import os
|
||||
|
||||
_TOP_DIR = os.path.abspath(
|
||||
os.path.sep.join((
|
||||
os.path.dirname(__file__),
|
||||
"../",
|
||||
)),
|
||||
os.path.sep.join(
|
||||
(
|
||||
os.path.dirname(__file__),
|
||||
"../",
|
||||
)
|
||||
),
|
||||
)
|
||||
_SAMPLES_DIR = os.path.abspath(
|
||||
os.path.sep.join((
|
||||
os.path.dirname(__file__),
|
||||
"../samples/",
|
||||
)),
|
||||
os.path.sep.join(
|
||||
(
|
||||
os.path.dirname(__file__),
|
||||
"../samples/",
|
||||
)
|
||||
),
|
||||
)
|
||||
import sys
|
||||
|
||||
sys.path.append(_TOP_DIR)
|
||||
sys.path.append(_SAMPLES_DIR)
|
||||
|
||||
|
@ -29,3 +35,13 @@ def test_wiring_with_flask():
|
|||
|
||||
assert response.status_code == 200
|
||||
assert json.loads(response.data) == {"result": "OK"}
|
||||
|
||||
|
||||
def test_wiring_with_annotated():
|
||||
client = web.app.test_client()
|
||||
|
||||
with web.app.app_context():
|
||||
response = client.get("/annotated")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert json.loads(response.data) == {"result": "OK"}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user