mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2025-07-05 12:53:30 +03:00
Compare commits
36 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
2c0aede4aa | ||
|
84a14f2ca7 | ||
|
0c58064a36 | ||
|
eb74b1e9d0 | ||
|
be7d25518d | ||
|
04b5907f21 | ||
|
e6cc12762f | ||
|
bf2ddbce32 | ||
|
9d6994391f | ||
|
dd84a1b5d6 | ||
|
31a98f7731 | ||
|
b261251b34 | ||
|
4bfe64563e | ||
|
b411807572 | ||
|
f2da51e0d4 | ||
|
2293251986 | ||
|
1b4b3d349f | ||
|
d8e49f7dd5 | ||
|
c1f14a876a | ||
|
c97a0cc515 | ||
|
0ada62acbf | ||
|
67827a36d1 | ||
|
ceed6a8222 | ||
|
6766ef3eba | ||
|
a8914e54e0 | ||
|
cdd9ce5048 | ||
|
51c7db771d | ||
|
a322584308 | ||
|
4b3476cb48 | ||
|
8460465b5e | ||
|
16f444b230 | ||
|
1ae96e3eeb | ||
|
7fcf1ac7ad | ||
|
1271d0fa79 | ||
|
b9df88eea7 | ||
|
99489afa3f |
9
.editorconfig
Normal file
9
.editorconfig
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.{py,pyi,pxd,pyx}]
|
||||||
|
ij_visual_guides = 80,88
|
7
.github/workflows/publishing.yml
vendored
7
.github/workflows/publishing.yml
vendored
|
@ -60,19 +60,20 @@ jobs:
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-24.04, ubuntu-24.04-arm, windows-2019, macos-14]
|
os: [ubuntu-24.04, ubuntu-24.04-arm, windows-2022, macos-14]
|
||||||
env:
|
env:
|
||||||
CIBW_ENABLE: pypy
|
CIBW_ENABLE: pypy
|
||||||
CIBW_ENVIRONMENT: >-
|
CIBW_ENVIRONMENT: >-
|
||||||
PIP_CONFIG_SETTINGS="build_ext=-j4"
|
PIP_CONFIG_SETTINGS="build_ext=-j4"
|
||||||
DEPENDENCY_INJECTOR_LIMITED_API="1"
|
DEPENDENCY_INJECTOR_LIMITED_API="1"
|
||||||
|
CFLAGS="-g0"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Build wheels
|
- name: Build wheels
|
||||||
uses: pypa/cibuildwheel@v2.23.3
|
uses: pypa/cibuildwheel@v3.0.0
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: cibw-wheels-x86-${{ matrix.os }}-${{ strategy.job-index }}
|
name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
|
||||||
path: ./wheelhouse/*.whl
|
path: ./wheelhouse/*.whl
|
||||||
|
|
||||||
test-publish:
|
test-publish:
|
||||||
|
|
9
docs/api/asgi-lifespan.rst
Normal file
9
docs/api/asgi-lifespan.rst
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
dependency_injector.ext.starlette
|
||||||
|
=================================
|
||||||
|
|
||||||
|
.. automodule:: dependency_injector.ext.starlette
|
||||||
|
:members:
|
||||||
|
:inherited-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
.. disqus::
|
|
@ -9,3 +9,4 @@ API Documentation
|
||||||
containers
|
containers
|
||||||
wiring
|
wiring
|
||||||
errors
|
errors
|
||||||
|
asgi-lifespan
|
||||||
|
|
|
@ -72,7 +72,7 @@ release = version
|
||||||
#
|
#
|
||||||
# This is also used if you do content translation via gettext catalogs.
|
# This is also used if you do content translation via gettext catalogs.
|
||||||
# Usually you set "language" from the command line for these cases.
|
# Usually you set "language" from the command line for these cases.
|
||||||
language = None
|
language = "en"
|
||||||
|
|
||||||
# There are two options for replacing |today|: either, you set today to some
|
# There are two options for replacing |today|: either, you set today to some
|
||||||
# non-false value, then it is used:
|
# non-false value, then it is used:
|
||||||
|
|
|
@ -78,7 +78,7 @@ Container is wired to the ``views`` module in the app config ``web/apps.py``:
|
||||||
|
|
||||||
.. literalinclude:: ../../examples/miniapps/django/web/apps.py
|
.. literalinclude:: ../../examples/miniapps/django/web/apps.py
|
||||||
:language: python
|
:language: python
|
||||||
:emphasize-lines: 13
|
:emphasize-lines: 12
|
||||||
|
|
||||||
Tests
|
Tests
|
||||||
-----
|
-----
|
||||||
|
|
48
docs/examples/fastdepends.rst
Normal file
48
docs/examples/fastdepends.rst
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
.. _fastdepends-example:
|
||||||
|
|
||||||
|
FastDepends example
|
||||||
|
===================
|
||||||
|
|
||||||
|
.. meta::
|
||||||
|
:keywords: Python,Dependency Injection,FastDepends,Example
|
||||||
|
:description: This example demonstrates a usage of the FastDepends and Dependency Injector.
|
||||||
|
|
||||||
|
|
||||||
|
This example demonstrates how to use ``Dependency Injector`` with `FastDepends <https://github.com/Lancetnik/FastDepends>`_, a lightweight dependency injection framework inspired by FastAPI's dependency system, but without the web framework components.
|
||||||
|
|
||||||
|
Basic Usage
|
||||||
|
-----------
|
||||||
|
|
||||||
|
The integration between FastDepends and Dependency Injector is straightforward. Simply use Dependency Injector's ``Provide`` marker within FastDepends' ``Depends`` function:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from dependency_injector import containers, providers
|
||||||
|
from dependency_injector.wiring import inject, Provide
|
||||||
|
from fast_depends import Depends
|
||||||
|
|
||||||
|
|
||||||
|
class CoefficientService:
|
||||||
|
@staticmethod
|
||||||
|
def get_coefficient() -> float:
|
||||||
|
return 1.2
|
||||||
|
|
||||||
|
|
||||||
|
class Container(containers.DeclarativeContainer):
|
||||||
|
service = providers.Factory(CoefficientService)
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
|
def apply_coefficient(
|
||||||
|
a: int,
|
||||||
|
coefficient_provider: CoefficientService = Depends(Provide[Container.service]),
|
||||||
|
) -> float:
|
||||||
|
return a * coefficient_provider.get_coefficient()
|
||||||
|
|
||||||
|
|
||||||
|
container = Container()
|
||||||
|
container.wire(modules=[sys.modules[__name__]])
|
||||||
|
|
||||||
|
apply_coefficient(100) == 120.0
|
|
@ -22,5 +22,6 @@ Explore the examples to see the ``Dependency Injector`` in action.
|
||||||
fastapi
|
fastapi
|
||||||
fastapi-redis
|
fastapi-redis
|
||||||
fastapi-sqlalchemy
|
fastapi-sqlalchemy
|
||||||
|
fastdepends
|
||||||
|
|
||||||
.. disqus::
|
.. disqus::
|
||||||
|
|
|
@ -31,7 +31,7 @@ Key features of the ``Dependency Injector``:
|
||||||
|
|
||||||
The framework stands on the `PEP20 (The Zen of Python) <https://www.python.org/dev/peps/pep-0020/>`_ principle:
|
The framework stands on the `PEP20 (The Zen of Python) <https://www.python.org/dev/peps/pep-0020/>`_ principle:
|
||||||
|
|
||||||
.. code-block:: plain
|
.. code-block:: text
|
||||||
|
|
||||||
Explicit is better than implicit
|
Explicit is better than implicit
|
||||||
|
|
||||||
|
|
|
@ -7,15 +7,39 @@ that were made in every particular version.
|
||||||
From version 0.7.6 *Dependency Injector* framework strictly
|
From version 0.7.6 *Dependency Injector* framework strictly
|
||||||
follows `Semantic versioning`_
|
follows `Semantic versioning`_
|
||||||
|
|
||||||
|
4.48.1
|
||||||
|
------
|
||||||
|
|
||||||
|
* Improve performance of ``dependency_injector._cwiring.DependencyResolver``
|
||||||
|
* Add ``typing-extensions`` as a dependency for older Python versions (<3.11)
|
||||||
|
* Produce warning on ``@inject``s without ``Provide[...]`` marks
|
||||||
|
* Add support for `resource_type` in ``Lifespan``s
|
||||||
|
|
||||||
|
4.48.0
|
||||||
|
------
|
||||||
|
|
||||||
|
- Improve performance of wiring (`#897 <https://github.com/ets-labs/python-dependency-injector/pull/897>`_)
|
||||||
|
- Add Context Manager support to Resource provider (`#899 <https://github.com/ets-labs/python-dependency-injector/pull/899>`_)
|
||||||
|
- Add support for async generator injections (`#900 <https://github.com/ets-labs/python-dependency-injector/pull/900>`_)
|
||||||
|
- Fix unintended dependency on ``typing_extensions`` (`#902 <https://github.com/ets-labs/python-dependency-injector/pull/902>`_)
|
||||||
|
- Add support for Fast Depends (`#898 <https://github.com/ets-labs/python-dependency-injector/pull/898>`_)
|
||||||
|
- Add ``resource_type`` parameter to init and shutdown resources using specialized providers (`#858 <https://github.com/ets-labs/python-dependency-injector/pull/858>`_)
|
||||||
|
|
||||||
|
4.47.1
|
||||||
|
------
|
||||||
|
|
||||||
|
- Fix typing for wiring marker (`#892 <https://github.com/ets-labs/python-dependency-injector/pull/896>`_)
|
||||||
|
- Strip debug symbols in wheels
|
||||||
|
|
||||||
4.47.0
|
4.47.0
|
||||||
-------
|
------
|
||||||
|
|
||||||
- Add support for ``Annotated`` type for module and class attribute injection in wiring,
|
- Add support for ``Annotated`` type for module and class attribute injection in wiring,
|
||||||
with updated documentation and examples.
|
with updated documentation and examples.
|
||||||
See discussion:
|
See discussion:
|
||||||
https://github.com/ets-labs/python-dependency-injector/pull/721#issuecomment-2025263718
|
https://github.com/ets-labs/python-dependency-injector/pull/721#issuecomment-2025263718
|
||||||
- Fix ``root`` property shadowing in ``ConfigurationOption`` (`#875 https://github.com/ets-labs/python-dependency-injector/pull/875`_)
|
- Fix ``root`` property shadowing in ``ConfigurationOption`` (`#875 <https://github.com/ets-labs/python-dependency-injector/pull/875>`_)
|
||||||
- Fix incorrect monkeypatching during ``wire()`` that could violate MRO in some classes (`#886 https://github.com/ets-labs/python-dependency-injector/pull/886`_)
|
- Fix incorrect monkeypatching during ``wire()`` that could violate MRO in some classes (`#886 <https://github.com/ets-labs/python-dependency-injector/pull/886>`_)
|
||||||
- ABI3 wheels are now published for CPython.
|
- ABI3 wheels are now published for CPython.
|
||||||
- Drop support of Python 3.7.
|
- Drop support of Python 3.7.
|
||||||
|
|
||||||
|
|
|
@ -61,11 +61,12 @@ When you call ``.shutdown()`` method on a resource provider, it will remove the
|
||||||
if any, and switch to uninitialized state. Some of resource initializer types support specifying custom
|
if any, and switch to uninitialized state. Some of resource initializer types support specifying custom
|
||||||
resource shutdown.
|
resource shutdown.
|
||||||
|
|
||||||
Resource provider supports 3 types of initializers:
|
Resource provider supports 4 types of initializers:
|
||||||
|
|
||||||
- Function
|
- Function
|
||||||
- Generator
|
- Context Manager
|
||||||
- Subclass of ``resources.Resource``
|
- Generator (legacy)
|
||||||
|
- Subclass of ``resources.Resource`` (legacy)
|
||||||
|
|
||||||
Function initializer
|
Function initializer
|
||||||
--------------------
|
--------------------
|
||||||
|
@ -103,8 +104,44 @@ you configure global resource:
|
||||||
|
|
||||||
Function initializer does not provide a way to specify custom resource shutdown.
|
Function initializer does not provide a way to specify custom resource shutdown.
|
||||||
|
|
||||||
Generator initializer
|
Context Manager initializer
|
||||||
---------------------
|
---------------------------
|
||||||
|
|
||||||
|
This is an extension to the Function initializer. Resource provider automatically detects if the initializer returns a
|
||||||
|
context manager and uses it to manage the resource lifecycle.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from dependency_injector import containers, providers
|
||||||
|
|
||||||
|
class DatabaseConnection:
|
||||||
|
def __init__(self, host, port, user, password):
|
||||||
|
self.host = host
|
||||||
|
self.port = port
|
||||||
|
self.user = user
|
||||||
|
self.password = password
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
print(f"Connecting to {self.host}:{self.port} as {self.user}")
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
|
print("Closing connection")
|
||||||
|
|
||||||
|
|
||||||
|
class Container(containers.DeclarativeContainer):
|
||||||
|
|
||||||
|
config = providers.Configuration()
|
||||||
|
db = providers.Resource(
|
||||||
|
DatabaseConnection,
|
||||||
|
host=config.db.host,
|
||||||
|
port=config.db.port,
|
||||||
|
user=config.db.user,
|
||||||
|
password=config.db.password,
|
||||||
|
)
|
||||||
|
|
||||||
|
Generator initializer (legacy)
|
||||||
|
------------------------------
|
||||||
|
|
||||||
Resource provider can use 2-step generators:
|
Resource provider can use 2-step generators:
|
||||||
|
|
||||||
|
@ -154,8 +191,13 @@ object is not mandatory. You can leave ``yield`` statement empty:
|
||||||
argument2=...,
|
argument2=...,
|
||||||
)
|
)
|
||||||
|
|
||||||
Subclass initializer
|
.. note::
|
||||||
--------------------
|
|
||||||
|
Generator initializers are automatically wrapped with ``contextmanager`` or ``asynccontextmanager`` decorator when
|
||||||
|
provided to a ``Resource`` provider.
|
||||||
|
|
||||||
|
Subclass initializer (legacy)
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
You can create resource initializer by implementing a subclass of the ``resources.Resource``:
|
You can create resource initializer by implementing a subclass of the ``resources.Resource``:
|
||||||
|
|
||||||
|
@ -210,6 +252,72 @@ first argument.
|
||||||
|
|
||||||
.. _resource-provider-wiring-closing:
|
.. _resource-provider-wiring-closing:
|
||||||
|
|
||||||
|
Scoping Resources using specialized subclasses
|
||||||
|
----------------------------------------------
|
||||||
|
|
||||||
|
You can use specialized subclasses of ``Resource`` provider to initialize and shutdown resources by type.
|
||||||
|
Allowing for example to only initialize a subgroup of resources.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
class ScopedResource(resources.Resource):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def init_service(name) -> Service:
|
||||||
|
print(f"Init {name}")
|
||||||
|
yield Service()
|
||||||
|
print(f"Shutdown {name}")
|
||||||
|
|
||||||
|
class Container(containers.DeclarativeContainer):
|
||||||
|
|
||||||
|
scoped = ScopedResource(
|
||||||
|
init_service,
|
||||||
|
"scoped",
|
||||||
|
)
|
||||||
|
|
||||||
|
generic = providers.Resource(
|
||||||
|
init_service,
|
||||||
|
"generic",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
To initialize resources by type you can use ``init_resources(resource_type)`` and ``shutdown_resources(resource_type)``
|
||||||
|
methods adding the resource type as an argument:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
def main():
|
||||||
|
container = Container()
|
||||||
|
container.init_resources(ScopedResource)
|
||||||
|
# Generates:
|
||||||
|
# >>> Init scoped
|
||||||
|
|
||||||
|
container.shutdown_resources(ScopedResource)
|
||||||
|
# Generates:
|
||||||
|
# >>> Shutdown scoped
|
||||||
|
|
||||||
|
|
||||||
|
And to initialize all resources you can use ``init_resources()`` and ``shutdown_resources()`` without arguments:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
def main():
|
||||||
|
container = Container()
|
||||||
|
container.init_resources()
|
||||||
|
# Generates:
|
||||||
|
# >>> Init scoped
|
||||||
|
# >>> Init generic
|
||||||
|
|
||||||
|
container.shutdown_resources()
|
||||||
|
# Generates:
|
||||||
|
# >>> Shutdown scoped
|
||||||
|
# >>> Shutdown generic
|
||||||
|
|
||||||
|
|
||||||
|
It works using the ``traverse()`` method to find all resources of the specified type, selecting all resources
|
||||||
|
which are instances of the specified type.
|
||||||
|
|
||||||
|
|
||||||
Resources, wiring, and per-function execution scope
|
Resources, wiring, and per-function execution scope
|
||||||
---------------------------------------------------
|
---------------------------------------------------
|
||||||
|
|
||||||
|
@ -263,10 +371,11 @@ Asynchronous function initializer:
|
||||||
argument2=...,
|
argument2=...,
|
||||||
)
|
)
|
||||||
|
|
||||||
Asynchronous generator initializer:
|
Asynchronous Context Manager initializer:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
|
@asynccontextmanager
|
||||||
async def init_async_resource(argument1=..., argument2=...):
|
async def init_async_resource(argument1=..., argument2=...):
|
||||||
connection = await connect()
|
connection = await connect()
|
||||||
yield connection
|
yield connection
|
||||||
|
@ -358,5 +467,54 @@ See also:
|
||||||
- Wiring :ref:`async-injections-wiring`
|
- Wiring :ref:`async-injections-wiring`
|
||||||
- :ref:`fastapi-redis-example`
|
- :ref:`fastapi-redis-example`
|
||||||
|
|
||||||
|
ASGI Lifespan Protocol Support
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
The :mod:`dependency_injector.ext.starlette` module provides a :class:`~dependency_injector.ext.starlette.Lifespan`
|
||||||
|
class that integrates resource providers with ASGI applications using the `Lifespan Protocol`_. This allows resources to
|
||||||
|
be automatically initialized at application startup and properly shut down when the application stops.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from contextlib import asynccontextmanager
|
||||||
|
from dependency_injector import containers, providers
|
||||||
|
from dependency_injector.wiring import Provide, inject
|
||||||
|
from dependency_injector.ext.starlette import Lifespan
|
||||||
|
from fastapi import FastAPI, Request, Depends, APIRouter
|
||||||
|
|
||||||
|
class Connection: ...
|
||||||
|
|
||||||
|
@asynccontextmanager
|
||||||
|
async def init_database():
|
||||||
|
print("opening database connection")
|
||||||
|
yield Connection()
|
||||||
|
print("closing database connection")
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
@router.get("/")
|
||||||
|
@inject
|
||||||
|
async def index(request: Request, db: Connection = Depends(Provide["db"])):
|
||||||
|
# use the database connection here
|
||||||
|
return "OK!"
|
||||||
|
|
||||||
|
class Container(containers.DeclarativeContainer):
|
||||||
|
__self__ = providers.Self()
|
||||||
|
db = providers.Resource(init_database)
|
||||||
|
lifespan = providers.Singleton(Lifespan, __self__)
|
||||||
|
app = providers.Singleton(FastAPI, lifespan=lifespan)
|
||||||
|
_include_router = providers.Resource(
|
||||||
|
app.provided.include_router.call(),
|
||||||
|
router,
|
||||||
|
)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import uvicorn
|
||||||
|
|
||||||
|
container = Container()
|
||||||
|
app = container.app()
|
||||||
|
uvicorn.run(app, host="localhost", port=8000)
|
||||||
|
|
||||||
|
.. _Lifespan Protocol: https://asgi.readthedocs.io/en/latest/specs/lifespan.html
|
||||||
|
|
||||||
.. disqus::
|
.. disqus::
|
||||||
|
|
|
@ -257,7 +257,7 @@ Let's check that it works. Open another terminal session and use ``httpie``:
|
||||||
|
|
||||||
You should see:
|
You should see:
|
||||||
|
|
||||||
.. code-block:: json
|
.. code-block:: http
|
||||||
|
|
||||||
HTTP/1.1 200 OK
|
HTTP/1.1 200 OK
|
||||||
Content-Length: 844
|
Content-Length: 844
|
||||||
|
@ -596,7 +596,7 @@ and make a request to the API in the terminal:
|
||||||
|
|
||||||
You should see:
|
You should see:
|
||||||
|
|
||||||
.. code-block:: json
|
.. code-block:: http
|
||||||
|
|
||||||
HTTP/1.1 200 OK
|
HTTP/1.1 200 OK
|
||||||
Content-Length: 492
|
Content-Length: 492
|
||||||
|
|
|
@ -84,7 +84,7 @@ Create next structure in the project root directory. All files are empty. That's
|
||||||
|
|
||||||
Initial project layout:
|
Initial project layout:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: text
|
||||||
|
|
||||||
./
|
./
|
||||||
├── movies/
|
├── movies/
|
||||||
|
@ -109,7 +109,7 @@ Now it's time to install the project requirements. We will use next packages:
|
||||||
|
|
||||||
Put next lines into the ``requirements.txt`` file:
|
Put next lines into the ``requirements.txt`` file:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: text
|
||||||
|
|
||||||
dependency-injector
|
dependency-injector
|
||||||
pyyaml
|
pyyaml
|
||||||
|
@ -134,7 +134,7 @@ We will create a script that creates database files.
|
||||||
First add the folder ``data/`` in the root of the project and then add the file
|
First add the folder ``data/`` in the root of the project and then add the file
|
||||||
``fixtures.py`` inside of it:
|
``fixtures.py`` inside of it:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: text
|
||||||
:emphasize-lines: 2-3
|
:emphasize-lines: 2-3
|
||||||
|
|
||||||
./
|
./
|
||||||
|
@ -205,13 +205,13 @@ Now run in the terminal:
|
||||||
|
|
||||||
You should see:
|
You should see:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: text
|
||||||
|
|
||||||
OK
|
OK
|
||||||
|
|
||||||
Check that files ``movies.csv`` and ``movies.db`` have appeared in the ``data/`` folder:
|
Check that files ``movies.csv`` and ``movies.db`` have appeared in the ``data/`` folder:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: text
|
||||||
:emphasize-lines: 4-5
|
:emphasize-lines: 4-5
|
||||||
|
|
||||||
./
|
./
|
||||||
|
@ -289,7 +289,7 @@ After each step we will add the provider to the container.
|
||||||
|
|
||||||
Create the ``entities.py`` in the ``movies`` package:
|
Create the ``entities.py`` in the ``movies`` package:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: text
|
||||||
:emphasize-lines: 10
|
:emphasize-lines: 10
|
||||||
|
|
||||||
./
|
./
|
||||||
|
@ -356,7 +356,7 @@ Let's move on to the finders.
|
||||||
|
|
||||||
Create the ``finders.py`` in the ``movies`` package:
|
Create the ``finders.py`` in the ``movies`` package:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: text
|
||||||
:emphasize-lines: 11
|
:emphasize-lines: 11
|
||||||
|
|
||||||
./
|
./
|
||||||
|
@ -465,7 +465,7 @@ The configuration file is ready. Move on to the lister.
|
||||||
|
|
||||||
Create the ``listers.py`` in the ``movies`` package:
|
Create the ``listers.py`` in the ``movies`` package:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: text
|
||||||
:emphasize-lines: 12
|
:emphasize-lines: 12
|
||||||
|
|
||||||
./
|
./
|
||||||
|
@ -613,7 +613,7 @@ Run in the terminal:
|
||||||
|
|
||||||
You should see:
|
You should see:
|
||||||
|
|
||||||
.. code-block:: plain
|
.. code-block:: text
|
||||||
|
|
||||||
Francis Lawrence movies:
|
Francis Lawrence movies:
|
||||||
- Movie(title='The Hunger Games: Mockingjay - Part 2', year=2015, director='Francis Lawrence')
|
- Movie(title='The Hunger Games: Mockingjay - Part 2', year=2015, director='Francis Lawrence')
|
||||||
|
@ -752,7 +752,7 @@ Run in the terminal:
|
||||||
|
|
||||||
You should see:
|
You should see:
|
||||||
|
|
||||||
.. code-block:: plain
|
.. code-block:: text
|
||||||
|
|
||||||
Francis Lawrence movies:
|
Francis Lawrence movies:
|
||||||
- Movie(title='The Hunger Games: Mockingjay - Part 2', year=2015, director='Francis Lawrence')
|
- Movie(title='The Hunger Games: Mockingjay - Part 2', year=2015, director='Francis Lawrence')
|
||||||
|
@ -868,7 +868,7 @@ Run in the terminal line by line:
|
||||||
|
|
||||||
The output should be similar for each command:
|
The output should be similar for each command:
|
||||||
|
|
||||||
.. code-block:: plain
|
.. code-block:: text
|
||||||
|
|
||||||
Francis Lawrence movies:
|
Francis Lawrence movies:
|
||||||
- Movie(title='The Hunger Games: Mockingjay - Part 2', year=2015, director='Francis Lawrence')
|
- Movie(title='The Hunger Games: Mockingjay - Part 2', year=2015, director='Francis Lawrence')
|
||||||
|
@ -888,7 +888,7 @@ We will use `pytest <https://docs.pytest.org/en/stable/>`_ and
|
||||||
|
|
||||||
Create ``tests.py`` in the ``movies`` package:
|
Create ``tests.py`` in the ``movies`` package:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: text
|
||||||
:emphasize-lines: 13
|
:emphasize-lines: 13
|
||||||
|
|
||||||
./
|
./
|
||||||
|
@ -977,7 +977,7 @@ Run in the terminal:
|
||||||
|
|
||||||
You should see:
|
You should see:
|
||||||
|
|
||||||
.. code-block::
|
.. code-block:: text
|
||||||
|
|
||||||
platform darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
|
platform darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
|
||||||
plugins: cov-3.0.0
|
plugins: cov-3.0.0
|
||||||
|
|
|
@ -280,7 +280,7 @@ Now let's fill in the layout.
|
||||||
|
|
||||||
Put next into the ``base.html``:
|
Put next into the ``base.html``:
|
||||||
|
|
||||||
.. code-block:: html
|
.. code-block:: jinja
|
||||||
|
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
@ -313,7 +313,7 @@ And put something to the index page.
|
||||||
|
|
||||||
Put next into the ``index.html``:
|
Put next into the ``index.html``:
|
||||||
|
|
||||||
.. code-block:: html
|
.. code-block:: jinja
|
||||||
|
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
|
|
@ -127,6 +127,7 @@ To inject the provider itself use ``Provide[foo.provider]``:
|
||||||
def foo(bar_provider: Factory[Bar] = Provide[Container.bar.provider]):
|
def foo(bar_provider: Factory[Bar] = Provide[Container.bar.provider]):
|
||||||
bar = bar_provider(argument="baz")
|
bar = bar_provider(argument="baz")
|
||||||
...
|
...
|
||||||
|
|
||||||
You can also use ``Provider[foo]`` for injecting the provider itself:
|
You can also use ``Provider[foo]`` for injecting the provider itself:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
@ -631,6 +632,36 @@ or with a single container ``register_loader_containers(container)`` multiple ti
|
||||||
To unregister a container use ``unregister_loader_containers(container)``.
|
To unregister a container use ``unregister_loader_containers(container)``.
|
||||||
Wiring module will uninstall the import hook when unregister last container.
|
Wiring module will uninstall the import hook when unregister last container.
|
||||||
|
|
||||||
|
Few notes on performance
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
``.wire()`` utilize caching to speed up the wiring process. At the end it clears the cache to avoid memory leaks.
|
||||||
|
But this may not always be desirable, when you want to keep the cache for the next wiring
|
||||||
|
(e.g. due to usage of multiple containers or during unit tests).
|
||||||
|
|
||||||
|
To keep the cache after wiring, you can set flag ``keep_cache=True`` (works with ``WiringConfiguration`` too):
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
container1.wire(
|
||||||
|
modules=["yourapp.module1", "yourapp.module2"],
|
||||||
|
keep_cache=True,
|
||||||
|
)
|
||||||
|
container2.wire(
|
||||||
|
modules=["yourapp.module2", "yourapp.module3"],
|
||||||
|
keep_cache=True,
|
||||||
|
)
|
||||||
|
...
|
||||||
|
|
||||||
|
and then clear it manually when you need it:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from dependency_injector.wiring import clear_cache
|
||||||
|
|
||||||
|
clear_cache()
|
||||||
|
|
||||||
|
|
||||||
Integration with other frameworks
|
Integration with other frameworks
|
||||||
---------------------------------
|
---------------------------------
|
||||||
|
|
||||||
|
@ -662,5 +693,6 @@ Take a look at other application examples:
|
||||||
- :ref:`fastapi-example`
|
- :ref:`fastapi-example`
|
||||||
- :ref:`fastapi-redis-example`
|
- :ref:`fastapi-redis-example`
|
||||||
- :ref:`fastapi-sqlalchemy-example`
|
- :ref:`fastapi-sqlalchemy-example`
|
||||||
|
- :ref:`fastdepends-example`
|
||||||
|
|
||||||
.. disqus::
|
.. disqus::
|
||||||
|
|
|
@ -18,10 +18,9 @@ SQLITE_FILE = DIR / "movies.db"
|
||||||
|
|
||||||
|
|
||||||
def create_csv(movies_data, path):
|
def create_csv(movies_data, path):
|
||||||
with open(path, "w") as opened_file:
|
with open(path, "w", newline="") as opened_file:
|
||||||
writer = csv.writer(opened_file)
|
writer = csv.writer(opened_file)
|
||||||
for row in movies_data:
|
writer.writerows(movies_data)
|
||||||
writer.writerow(row)
|
|
||||||
|
|
||||||
|
|
||||||
def create_sqlite(movies_data, path):
|
def create_sqlite(movies_data, path):
|
||||||
|
|
|
@ -29,7 +29,7 @@ class CsvMovieFinder(MovieFinder):
|
||||||
super().__init__(movie_factory)
|
super().__init__(movie_factory)
|
||||||
|
|
||||||
def find_all(self) -> List[Movie]:
|
def find_all(self) -> List[Movie]:
|
||||||
with open(self._csv_file_path) as csv_file:
|
with open(self._csv_file_path, newline="") as csv_file:
|
||||||
csv_reader = csv.reader(csv_file, delimiter=self._delimiter)
|
csv_reader = csv.reader(csv_file, delimiter=self._delimiter)
|
||||||
return [self._movie_factory(*row) for row in csv_reader]
|
return [self._movie_factory(*row) for row in csv_reader]
|
||||||
|
|
||||||
|
|
|
@ -3,10 +3,12 @@
|
||||||
import sys
|
import sys
|
||||||
import logging
|
import logging
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
from contextlib import contextmanager
|
||||||
|
|
||||||
from dependency_injector import containers, providers
|
from dependency_injector import containers, providers
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
def init_thread_pool(max_workers: int):
|
def init_thread_pool(max_workers: int):
|
||||||
thread_pool = ThreadPoolExecutor(max_workers=max_workers)
|
thread_pool = ThreadPoolExecutor(max_workers=max_workers)
|
||||||
yield thread_pool
|
yield thread_pool
|
||||||
|
|
|
@ -52,6 +52,11 @@ classifiers = [
|
||||||
"Topic :: Software Development :: Libraries :: Python Modules",
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||||
]
|
]
|
||||||
dynamic = ["version"]
|
dynamic = ["version"]
|
||||||
|
dependencies = [
|
||||||
|
# typing.Annotated since v3.9
|
||||||
|
# typing.Self since v3.11
|
||||||
|
"typing-extensions; python_version<'3.11'",
|
||||||
|
]
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
yaml = ["pyyaml"]
|
yaml = ["pyyaml"]
|
||||||
|
@ -91,6 +96,7 @@ show_missing = true
|
||||||
|
|
||||||
[tool.isort]
|
[tool.isort]
|
||||||
profile = "black"
|
profile = "black"
|
||||||
|
combine_as_imports = true
|
||||||
|
|
||||||
[tool.pylint.main]
|
[tool.pylint.main]
|
||||||
ignore = ["tests"]
|
ignore = ["tests"]
|
||||||
|
@ -107,6 +113,7 @@ markers = [
|
||||||
"pydantic: Tests with Pydantic as a dependency",
|
"pydantic: Tests with Pydantic as a dependency",
|
||||||
]
|
]
|
||||||
filterwarnings = [
|
filterwarnings = [
|
||||||
|
"ignore::dependency_injector.wiring.DIWiringWarning",
|
||||||
"ignore:Module \"dependency_injector.ext.aiohttp\" is deprecated since version 4\\.0\\.0:DeprecationWarning",
|
"ignore:Module \"dependency_injector.ext.aiohttp\" is deprecated since version 4\\.0\\.0:DeprecationWarning",
|
||||||
"ignore:Module \"dependency_injector.ext.flask\" is deprecated since version 4\\.0\\.0:DeprecationWarning",
|
"ignore:Module \"dependency_injector.ext.flask\" is deprecated since version 4\\.0\\.0:DeprecationWarning",
|
||||||
"ignore:Please use \\`.*?\\` from the \\`scipy.*?\\`(.*?)namespace is deprecated\\.:DeprecationWarning",
|
"ignore:Please use \\`.*?\\` from the \\`scipy.*?\\`(.*?)namespace is deprecated\\.:DeprecationWarning",
|
||||||
|
|
|
@ -20,5 +20,6 @@ scipy
|
||||||
boto3
|
boto3
|
||||||
mypy_boto3_s3
|
mypy_boto3_s3
|
||||||
typing_extensions
|
typing_extensions
|
||||||
|
fast-depends
|
||||||
|
|
||||||
-r requirements-ext.txt
|
-r requirements-ext.txt
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
"""Top-level package."""
|
"""Top-level package."""
|
||||||
|
|
||||||
__version__ = "4.47.0"
|
__version__ = "4.48.1"
|
||||||
"""Version number.
|
"""Version number.
|
||||||
|
|
||||||
:type: str
|
:type: str
|
||||||
|
|
18
src/dependency_injector/_cwiring.pyi
Normal file
18
src/dependency_injector/_cwiring.pyi
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
from typing import Any, Dict
|
||||||
|
|
||||||
|
from .providers import Provider
|
||||||
|
|
||||||
|
class DependencyResolver:
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
kwargs: Dict[str, Any],
|
||||||
|
injections: Dict[str, Provider[Any]],
|
||||||
|
closings: Dict[str, Provider[Any]],
|
||||||
|
/,
|
||||||
|
) -> None: ...
|
||||||
|
def __enter__(self) -> Dict[str, Any]: ...
|
||||||
|
def __exit__(self, *exc_info: Any) -> None: ...
|
||||||
|
async def __aenter__(self) -> Dict[str, Any]: ...
|
||||||
|
async def __aexit__(self, *exc_info: Any) -> None: ...
|
||||||
|
|
||||||
|
def _isawaitable(instance: Any) -> bool: ...
|
|
@ -1,83 +1,93 @@
|
||||||
"""Wiring optimizations module."""
|
"""Wiring optimizations module."""
|
||||||
|
|
||||||
import asyncio
|
from asyncio import gather
|
||||||
import collections.abc
|
from collections.abc import Awaitable
|
||||||
import inspect
|
from inspect import CO_ITERABLE_COROUTINE
|
||||||
import types
|
from types import CoroutineType, GeneratorType
|
||||||
|
|
||||||
from .wiring import _Marker
|
|
||||||
|
|
||||||
from .providers cimport Provider, Resource
|
from .providers cimport Provider, Resource
|
||||||
|
from .wiring import _Marker
|
||||||
|
|
||||||
|
|
||||||
def _sync_inject(object fn, tuple args, dict kwargs, dict injections, dict closings, /):
|
cdef inline bint _is_injectable(dict kwargs, object name):
|
||||||
cdef object result
|
return name not in kwargs or isinstance(kwargs[name], _Marker)
|
||||||
|
|
||||||
|
|
||||||
|
cdef class DependencyResolver:
|
||||||
|
cdef dict kwargs
|
||||||
cdef dict to_inject
|
cdef dict to_inject
|
||||||
cdef object arg_key
|
cdef dict injections
|
||||||
|
cdef dict closings
|
||||||
|
|
||||||
|
def __init__(self, dict kwargs, dict injections, dict closings, /):
|
||||||
|
self.kwargs = kwargs
|
||||||
|
self.to_inject = kwargs.copy()
|
||||||
|
self.injections = injections
|
||||||
|
self.closings = closings
|
||||||
|
|
||||||
|
async def _await_injection(self, name: str, value: object, /) -> None:
|
||||||
|
self.to_inject[name] = await value
|
||||||
|
|
||||||
|
cdef void _handle_injections_sync(self):
|
||||||
cdef Provider provider
|
cdef Provider provider
|
||||||
|
|
||||||
to_inject = kwargs.copy()
|
for name, provider in self.injections.items():
|
||||||
for arg_key, provider in injections.items():
|
if _is_injectable(self.kwargs, name):
|
||||||
if arg_key not in kwargs or isinstance(kwargs[arg_key], _Marker):
|
self.to_inject[name] = provider()
|
||||||
to_inject[arg_key] = provider()
|
|
||||||
|
|
||||||
result = fn(*args, **to_inject)
|
cdef list _handle_injections_async(self):
|
||||||
|
cdef list to_await = []
|
||||||
|
cdef Provider provider
|
||||||
|
|
||||||
if closings:
|
for name, provider in self.injections.items():
|
||||||
for arg_key, provider in closings.items():
|
if _is_injectable(self.kwargs, name):
|
||||||
if arg_key in kwargs and not isinstance(kwargs[arg_key], _Marker):
|
provide = provider()
|
||||||
continue
|
|
||||||
if not isinstance(provider, Resource):
|
if provider.is_async_mode_enabled() or _isawaitable(provide):
|
||||||
continue
|
to_await.append(self._await_injection(name, provide))
|
||||||
|
else:
|
||||||
|
self.to_inject[name] = provide
|
||||||
|
|
||||||
|
return to_await
|
||||||
|
|
||||||
|
cdef void _handle_closings_sync(self):
|
||||||
|
cdef Provider provider
|
||||||
|
|
||||||
|
for name, provider in self.closings.items():
|
||||||
|
if _is_injectable(self.kwargs, name) and isinstance(provider, Resource):
|
||||||
provider.shutdown()
|
provider.shutdown()
|
||||||
|
|
||||||
return result
|
cdef list _handle_closings_async(self):
|
||||||
|
cdef list to_await = []
|
||||||
|
|
||||||
async def _async_inject(object fn, tuple args, dict kwargs, dict injections, dict closings, /):
|
|
||||||
cdef object result
|
|
||||||
cdef dict to_inject
|
|
||||||
cdef list to_inject_await = []
|
|
||||||
cdef list to_close_await = []
|
|
||||||
cdef object arg_key
|
|
||||||
cdef Provider provider
|
cdef Provider provider
|
||||||
|
|
||||||
to_inject = kwargs.copy()
|
for name, provider in self.closings.items():
|
||||||
for arg_key, provider in injections.items():
|
if _is_injectable(self.kwargs, name) and isinstance(provider, Resource):
|
||||||
if arg_key not in kwargs or isinstance(kwargs[arg_key], _Marker):
|
if _isawaitable(shutdown := provider.shutdown()):
|
||||||
provide = provider()
|
to_await.append(shutdown)
|
||||||
if provider.is_async_mode_enabled():
|
|
||||||
to_inject_await.append((arg_key, provide))
|
|
||||||
elif _isawaitable(provide):
|
|
||||||
to_inject_await.append((arg_key, provide))
|
|
||||||
else:
|
|
||||||
to_inject[arg_key] = provide
|
|
||||||
|
|
||||||
if to_inject_await:
|
return to_await
|
||||||
async_to_inject = await asyncio.gather(*(provide for _, provide in to_inject_await))
|
|
||||||
for provide, (injection, _) in zip(async_to_inject, to_inject_await):
|
|
||||||
to_inject[injection] = provide
|
|
||||||
|
|
||||||
result = await fn(*args, **to_inject)
|
def __enter__(self):
|
||||||
|
self._handle_injections_sync()
|
||||||
|
return self.to_inject
|
||||||
|
|
||||||
if closings:
|
def __exit__(self, *_):
|
||||||
for arg_key, provider in closings.items():
|
self._handle_closings_sync()
|
||||||
if arg_key in kwargs and isinstance(kwargs[arg_key], _Marker):
|
|
||||||
continue
|
|
||||||
if not isinstance(provider, Resource):
|
|
||||||
continue
|
|
||||||
shutdown = provider.shutdown()
|
|
||||||
if _isawaitable(shutdown):
|
|
||||||
to_close_await.append(shutdown)
|
|
||||||
|
|
||||||
await asyncio.gather(*to_close_await)
|
async def __aenter__(self):
|
||||||
|
if to_await := self._handle_injections_async():
|
||||||
|
await gather(*to_await)
|
||||||
|
return self.to_inject
|
||||||
|
|
||||||
return result
|
async def __aexit__(self, *_):
|
||||||
|
if to_await := self._handle_closings_async():
|
||||||
|
await gather(*to_await)
|
||||||
|
|
||||||
|
|
||||||
cdef bint _isawaitable(object instance):
|
cdef bint _isawaitable(object instance):
|
||||||
"""Return true if object can be passed to an ``await`` expression."""
|
"""Return true if object can be passed to an ``await`` expression."""
|
||||||
return (isinstance(instance, types.CoroutineType) or
|
return (isinstance(instance, CoroutineType) or
|
||||||
isinstance(instance, types.GeneratorType) and
|
isinstance(instance, GeneratorType) and
|
||||||
bool(instance.gi_code.co_flags & inspect.CO_ITERABLE_COROUTINE) or
|
bool(instance.gi_code.co_flags & CO_ITERABLE_COROUTINE) or
|
||||||
isinstance(instance, collections.abc.Awaitable))
|
isinstance(instance, Awaitable))
|
||||||
|
|
|
@ -1,23 +1,28 @@
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import (
|
from typing import (
|
||||||
Generic,
|
|
||||||
Type,
|
|
||||||
Dict,
|
|
||||||
List,
|
|
||||||
Tuple,
|
|
||||||
Optional,
|
|
||||||
Any,
|
Any,
|
||||||
Union,
|
Awaitable,
|
||||||
ClassVar,
|
|
||||||
Callable as _Callable,
|
Callable as _Callable,
|
||||||
|
ClassVar,
|
||||||
|
Dict,
|
||||||
|
Generic,
|
||||||
Iterable,
|
Iterable,
|
||||||
Iterator,
|
Iterator,
|
||||||
|
List,
|
||||||
|
Optional,
|
||||||
|
Tuple,
|
||||||
|
Type,
|
||||||
TypeVar,
|
TypeVar,
|
||||||
Awaitable,
|
Union,
|
||||||
overload,
|
overload,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .providers import Provider, Self, ProviderParent
|
try:
|
||||||
|
from typing import Self as _Self
|
||||||
|
except ImportError:
|
||||||
|
from typing_extensions import Self as _Self
|
||||||
|
|
||||||
|
from .providers import Provider, Resource, Self, ProviderParent
|
||||||
|
|
||||||
C_Base = TypeVar("C_Base", bound="Container")
|
C_Base = TypeVar("C_Base", bound="Container")
|
||||||
C = TypeVar("C", bound="DeclarativeContainer")
|
C = TypeVar("C", bound="DeclarativeContainer")
|
||||||
|
@ -30,32 +35,34 @@ class WiringConfiguration:
|
||||||
packages: List[Any]
|
packages: List[Any]
|
||||||
from_package: Optional[str]
|
from_package: Optional[str]
|
||||||
auto_wire: bool
|
auto_wire: bool
|
||||||
|
keep_cache: bool
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
modules: Optional[Iterable[Any]] = None,
|
modules: Optional[Iterable[Any]] = None,
|
||||||
packages: Optional[Iterable[Any]] = None,
|
packages: Optional[Iterable[Any]] = None,
|
||||||
from_package: Optional[str] = None,
|
from_package: Optional[str] = None,
|
||||||
auto_wire: bool = True,
|
auto_wire: bool = True,
|
||||||
|
keep_cache: bool = False,
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
|
|
||||||
class Container:
|
class Container:
|
||||||
provider_type: Type[Provider] = Provider
|
provider_type: Type[Provider[Any]] = Provider
|
||||||
providers: Dict[str, Provider]
|
providers: Dict[str, Provider[Any]]
|
||||||
dependencies: Dict[str, Provider]
|
dependencies: Dict[str, Provider[Any]]
|
||||||
overridden: Tuple[Provider]
|
overridden: Tuple[Provider[Any], ...]
|
||||||
wiring_config: WiringConfiguration
|
wiring_config: WiringConfiguration
|
||||||
auto_load_config: bool = True
|
auto_load_config: bool = True
|
||||||
__self__: Self
|
__self__: Self
|
||||||
def __init__(self) -> None: ...
|
def __init__(self) -> None: ...
|
||||||
def __deepcopy__(self, memo: Optional[Dict[str, Any]]) -> Provider: ...
|
def __deepcopy__(self, memo: Optional[Dict[str, Any]]) -> _Self: ...
|
||||||
def __setattr__(self, name: str, value: Union[Provider, Any]) -> None: ...
|
def __setattr__(self, name: str, value: Union[Provider[Any], Any]) -> None: ...
|
||||||
def __getattr__(self, name: str) -> Provider: ...
|
def __getattr__(self, name: str) -> Provider[Any]: ...
|
||||||
def __delattr__(self, name: str) -> None: ...
|
def __delattr__(self, name: str) -> None: ...
|
||||||
def set_providers(self, **providers: Provider): ...
|
def set_providers(self, **providers: Provider[Any]) -> None: ...
|
||||||
def set_provider(self, name: str, provider: Provider) -> None: ...
|
def set_provider(self, name: str, provider: Provider[Any]) -> None: ...
|
||||||
def override(self, overriding: Union[Container, Type[Container]]) -> None: ...
|
def override(self, overriding: Union[Container, Type[Container]]) -> None: ...
|
||||||
def override_providers(
|
def override_providers(
|
||||||
self, **overriding_providers: Union[Provider, Any]
|
self, **overriding_providers: Union[Provider[Any], Any]
|
||||||
) -> ProvidersOverridingContext[C_Base]: ...
|
) -> ProvidersOverridingContext[C_Base]: ...
|
||||||
def reset_last_overriding(self) -> None: ...
|
def reset_last_overriding(self) -> None: ...
|
||||||
def reset_override(self) -> None: ...
|
def reset_override(self) -> None: ...
|
||||||
|
@ -67,8 +74,8 @@ class Container:
|
||||||
from_package: Optional[str] = None,
|
from_package: Optional[str] = None,
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def unwire(self) -> None: ...
|
def unwire(self) -> None: ...
|
||||||
def init_resources(self) -> Optional[Awaitable]: ...
|
def init_resources(self, resource_type: Type[Resource[Any]] = Resource) -> Optional[Awaitable[None]]: ...
|
||||||
def shutdown_resources(self) -> Optional[Awaitable]: ...
|
def shutdown_resources(self, resource_type: Type[Resource[Any]] = Resource) -> Optional[Awaitable[None]]: ...
|
||||||
def load_config(self) -> None: ...
|
def load_config(self) -> None: ...
|
||||||
def apply_container_providers_overridings(self) -> None: ...
|
def apply_container_providers_overridings(self) -> None: ...
|
||||||
def reset_singletons(self) -> SingletonResetContext[C_Base]: ...
|
def reset_singletons(self) -> SingletonResetContext[C_Base]: ...
|
||||||
|
@ -79,10 +86,10 @@ class Container:
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def from_json_schema(self, filepath: Union[Path, str]) -> None: ...
|
def from_json_schema(self, filepath: Union[Path, str]) -> None: ...
|
||||||
@overload
|
@overload
|
||||||
def resolve_provider_name(self, provider: Provider) -> str: ...
|
def resolve_provider_name(self, provider: Provider[Any]) -> str: ...
|
||||||
@classmethod
|
@classmethod
|
||||||
@overload
|
@overload
|
||||||
def resolve_provider_name(cls, provider: Provider) -> str: ...
|
def resolve_provider_name(cls, provider: Provider[Any]) -> str: ...
|
||||||
@property
|
@property
|
||||||
def parent(self) -> Optional[ProviderParent]: ...
|
def parent(self) -> Optional[ProviderParent]: ...
|
||||||
@property
|
@property
|
||||||
|
@ -97,14 +104,14 @@ class Container:
|
||||||
class DynamicContainer(Container): ...
|
class DynamicContainer(Container): ...
|
||||||
|
|
||||||
class DeclarativeContainer(Container):
|
class DeclarativeContainer(Container):
|
||||||
cls_providers: ClassVar[Dict[str, Provider]]
|
cls_providers: ClassVar[Dict[str, Provider[Any]]]
|
||||||
inherited_providers: ClassVar[Dict[str, Provider]]
|
inherited_providers: ClassVar[Dict[str, Provider[Any]]]
|
||||||
def __init__(self, **overriding_providers: Union[Provider, Any]) -> None: ...
|
def __init__(self, **overriding_providers: Union[Provider[Any], Any]) -> None: ...
|
||||||
@classmethod
|
@classmethod
|
||||||
def override(cls, overriding: Union[Container, Type[Container]]) -> None: ...
|
def override(cls, overriding: Union[Container, Type[Container]]) -> None: ...
|
||||||
@classmethod
|
@classmethod
|
||||||
def override_providers(
|
def override_providers(
|
||||||
cls, **overriding_providers: Union[Provider, Any]
|
cls, **overriding_providers: Union[Provider[Any], Any]
|
||||||
) -> ProvidersOverridingContext[C_Base]: ...
|
) -> ProvidersOverridingContext[C_Base]: ...
|
||||||
@classmethod
|
@classmethod
|
||||||
def reset_last_overriding(cls) -> None: ...
|
def reset_last_overriding(cls) -> None: ...
|
||||||
|
@ -113,7 +120,7 @@ class DeclarativeContainer(Container):
|
||||||
|
|
||||||
class ProvidersOverridingContext(Generic[T]):
|
class ProvidersOverridingContext(Generic[T]):
|
||||||
def __init__(
|
def __init__(
|
||||||
self, container: T, overridden_providers: Iterable[Union[Provider, Any]]
|
self, container: T, overridden_providers: Iterable[Union[Provider[Any], Any]]
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def __enter__(self) -> T: ...
|
def __enter__(self) -> T: ...
|
||||||
def __exit__(self, *_: Any) -> None: ...
|
def __exit__(self, *_: Any) -> None: ...
|
||||||
|
|
|
@ -20,14 +20,15 @@ from .wiring import wire, unwire
|
||||||
class WiringConfiguration:
|
class WiringConfiguration:
|
||||||
"""Container wiring configuration."""
|
"""Container wiring configuration."""
|
||||||
|
|
||||||
def __init__(self, modules=None, packages=None, from_package=None, auto_wire=True):
|
def __init__(self, modules=None, packages=None, from_package=None, auto_wire=True, keep_cache=False):
|
||||||
self.modules = [*modules] if modules else []
|
self.modules = [*modules] if modules else []
|
||||||
self.packages = [*packages] if packages else []
|
self.packages = [*packages] if packages else []
|
||||||
self.from_package = from_package
|
self.from_package = from_package
|
||||||
self.auto_wire = auto_wire
|
self.auto_wire = auto_wire
|
||||||
|
self.keep_cache = keep_cache
|
||||||
|
|
||||||
def __deepcopy__(self, memo=None):
|
def __deepcopy__(self, memo=None):
|
||||||
return self.__class__(self.modules, self.packages, self.from_package, self.auto_wire)
|
return self.__class__(self.modules, self.packages, self.from_package, self.auto_wire, self.keep_cache)
|
||||||
|
|
||||||
|
|
||||||
class Container:
|
class Container:
|
||||||
|
@ -258,7 +259,7 @@ class DynamicContainer(Container):
|
||||||
"""Check if auto wiring is needed."""
|
"""Check if auto wiring is needed."""
|
||||||
return self.wiring_config.auto_wire is True
|
return self.wiring_config.auto_wire is True
|
||||||
|
|
||||||
def wire(self, modules=None, packages=None, from_package=None):
|
def wire(self, modules=None, packages=None, from_package=None, keep_cache=None):
|
||||||
"""Wire container providers with provided packages and modules.
|
"""Wire container providers with provided packages and modules.
|
||||||
|
|
||||||
:rtype: None
|
:rtype: None
|
||||||
|
@ -289,10 +290,14 @@ class DynamicContainer(Container):
|
||||||
if not modules and not packages:
|
if not modules and not packages:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if keep_cache is None:
|
||||||
|
keep_cache = self.wiring_config.keep_cache
|
||||||
|
|
||||||
wire(
|
wire(
|
||||||
container=self,
|
container=self,
|
||||||
modules=modules,
|
modules=modules,
|
||||||
packages=packages,
|
packages=packages,
|
||||||
|
keep_cache=keep_cache,
|
||||||
)
|
)
|
||||||
|
|
||||||
if modules:
|
if modules:
|
||||||
|
@ -310,11 +315,15 @@ class DynamicContainer(Container):
|
||||||
self.wired_to_modules.clear()
|
self.wired_to_modules.clear()
|
||||||
self.wired_to_packages.clear()
|
self.wired_to_packages.clear()
|
||||||
|
|
||||||
def init_resources(self):
|
def init_resources(self, resource_type=providers.Resource):
|
||||||
"""Initialize all container resources."""
|
"""Initialize all container resources."""
|
||||||
|
|
||||||
|
if not issubclass(resource_type, providers.Resource):
|
||||||
|
raise TypeError("resource_type must be a subclass of Resource provider")
|
||||||
|
|
||||||
futures = []
|
futures = []
|
||||||
|
|
||||||
for provider in self.traverse(types=[providers.Resource]):
|
for provider in self.traverse(types=[resource_type]):
|
||||||
resource = provider.init()
|
resource = provider.init()
|
||||||
|
|
||||||
if __is_future_or_coroutine(resource):
|
if __is_future_or_coroutine(resource):
|
||||||
|
@ -323,8 +332,12 @@ class DynamicContainer(Container):
|
||||||
if futures:
|
if futures:
|
||||||
return asyncio.gather(*futures)
|
return asyncio.gather(*futures)
|
||||||
|
|
||||||
def shutdown_resources(self):
|
def shutdown_resources(self, resource_type=providers.Resource):
|
||||||
"""Shutdown all container resources."""
|
"""Shutdown all container resources."""
|
||||||
|
|
||||||
|
if not issubclass(resource_type, providers.Resource):
|
||||||
|
raise TypeError("resource_type must be a subclass of Resource provider")
|
||||||
|
|
||||||
def _independent_resources(resources):
|
def _independent_resources(resources):
|
||||||
for resource in resources:
|
for resource in resources:
|
||||||
for other_resource in resources:
|
for other_resource in resources:
|
||||||
|
@ -355,7 +368,7 @@ class DynamicContainer(Container):
|
||||||
for resource in resources_to_shutdown:
|
for resource in resources_to_shutdown:
|
||||||
resource.shutdown()
|
resource.shutdown()
|
||||||
|
|
||||||
resources = list(self.traverse(types=[providers.Resource]))
|
resources = list(self.traverse(types=[resource_type]))
|
||||||
if any(resource.is_async_mode_enabled() for resource in resources):
|
if any(resource.is_async_mode_enabled() for resource in resources):
|
||||||
return _async_ordered_shutdown(resources)
|
return _async_ordered_shutdown(resources)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -7,7 +7,6 @@ import warnings
|
||||||
|
|
||||||
from dependency_injector import providers
|
from dependency_injector import providers
|
||||||
|
|
||||||
|
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
'Module "dependency_injector.ext.aiohttp" is deprecated since '
|
'Module "dependency_injector.ext.aiohttp" is deprecated since '
|
||||||
'version 4.0.0. Use "dependency_injector.wiring" module instead.',
|
'version 4.0.0. Use "dependency_injector.wiring" module instead.',
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
from typing import Awaitable as _Awaitable
|
from typing import Any, Awaitable as _Awaitable, TypeVar
|
||||||
|
|
||||||
from dependency_injector import providers
|
from dependency_injector import providers
|
||||||
|
|
||||||
class Application(providers.Singleton): ...
|
T = TypeVar("T")
|
||||||
class Extension(providers.Singleton): ...
|
|
||||||
class Middleware(providers.DelegatedCallable): ...
|
|
||||||
class MiddlewareFactory(providers.Factory): ...
|
|
||||||
|
|
||||||
class View(providers.Callable):
|
class Application(providers.Singleton[T]): ...
|
||||||
def as_view(self) -> _Awaitable: ...
|
class Extension(providers.Singleton[T]): ...
|
||||||
|
class Middleware(providers.DelegatedCallable[T]): ...
|
||||||
|
class MiddlewareFactory(providers.Factory[T]): ...
|
||||||
|
|
||||||
class ClassBasedView(providers.Factory):
|
class View(providers.Callable[T]):
|
||||||
def as_view(self) -> _Awaitable: ...
|
def as_view(self) -> _Awaitable[T]: ...
|
||||||
|
|
||||||
|
class ClassBasedView(providers.Factory[T]):
|
||||||
|
def as_view(self) -> _Awaitable[T]: ...
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
"""Flask extension module."""
|
"""Flask extension module."""
|
||||||
|
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from flask import request as flask_request
|
from flask import request as flask_request
|
||||||
|
|
||||||
from dependency_injector import providers, errors
|
from dependency_injector import errors, providers
|
||||||
|
|
||||||
|
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
'Module "dependency_injector.ext.flask" is deprecated since '
|
'Module "dependency_injector.ext.flask" is deprecated since '
|
||||||
|
|
|
@ -1,19 +1,21 @@
|
||||||
from typing import Union, Optional, Callable as _Callable, Any
|
from typing import Any, Callable as _Callable, Optional, TypeVar, Union
|
||||||
|
|
||||||
|
from flask.wrappers import Request
|
||||||
|
|
||||||
from flask import request as flask_request
|
|
||||||
from dependency_injector import providers
|
from dependency_injector import providers
|
||||||
|
|
||||||
request: providers.Object[flask_request]
|
request: providers.Object[Request]
|
||||||
|
T = TypeVar("T")
|
||||||
|
|
||||||
class Application(providers.Singleton): ...
|
class Application(providers.Singleton[T]): ...
|
||||||
class Extension(providers.Singleton): ...
|
class Extension(providers.Singleton[T]): ...
|
||||||
|
|
||||||
class View(providers.Callable):
|
class View(providers.Callable[T]):
|
||||||
def as_view(self) -> _Callable[..., Any]: ...
|
def as_view(self) -> _Callable[..., T]: ...
|
||||||
|
|
||||||
class ClassBasedView(providers.Factory):
|
class ClassBasedView(providers.Factory[T]):
|
||||||
def as_view(self, name: str) -> _Callable[..., Any]: ...
|
def as_view(self, name: str) -> _Callable[..., T]: ...
|
||||||
|
|
||||||
def as_view(
|
def as_view(
|
||||||
provider: Union[View, ClassBasedView], name: Optional[str] = None
|
provider: Union[View[T], ClassBasedView[T]], name: Optional[str] = None
|
||||||
) -> _Callable[..., Any]: ...
|
) -> _Callable[..., T]: ...
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import sys
|
import sys
|
||||||
from typing import Any
|
from typing import Any, Type
|
||||||
|
|
||||||
if sys.version_info >= (3, 11): # pragma: no cover
|
if sys.version_info >= (3, 11): # pragma: no cover
|
||||||
from typing import Self
|
from typing import Self
|
||||||
|
@ -7,6 +7,7 @@ else: # pragma: no cover
|
||||||
from typing_extensions import Self
|
from typing_extensions import Self
|
||||||
|
|
||||||
from dependency_injector.containers import Container
|
from dependency_injector.containers import Container
|
||||||
|
from dependency_injector.providers import Resource
|
||||||
|
|
||||||
|
|
||||||
class Lifespan:
|
class Lifespan:
|
||||||
|
@ -29,24 +30,32 @@ class Lifespan:
|
||||||
app = Factory(Starlette, lifespan=lifespan)
|
app = Factory(Starlette, lifespan=lifespan)
|
||||||
|
|
||||||
:param container: container instance
|
:param container: container instance
|
||||||
|
:param resource_type: A :py:class:`~dependency_injector.resources.Resource`
|
||||||
|
subclass. Limits the resources to be initialized and shutdown.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
container: Container
|
container: Container
|
||||||
|
resource_type: Type[Resource[Any]]
|
||||||
|
|
||||||
def __init__(self, container: Container) -> None:
|
def __init__(
|
||||||
|
self,
|
||||||
|
container: Container,
|
||||||
|
resource_type: Type[Resource[Any]] = Resource,
|
||||||
|
) -> None:
|
||||||
self.container = container
|
self.container = container
|
||||||
|
self.resource_type = resource_type
|
||||||
|
|
||||||
def __call__(self, app: Any) -> Self:
|
def __call__(self, app: Any) -> Self:
|
||||||
return self
|
return self
|
||||||
|
|
||||||
async def __aenter__(self) -> None:
|
async def __aenter__(self) -> None:
|
||||||
result = self.container.init_resources()
|
result = self.container.init_resources(self.resource_type)
|
||||||
|
|
||||||
if result is not None:
|
if result is not None:
|
||||||
await result
|
await result
|
||||||
|
|
||||||
async def __aexit__(self, *exc_info: Any) -> None:
|
async def __aexit__(self, *exc_info: Any) -> None:
|
||||||
result = self.container.shutdown_resources()
|
result = self.container.shutdown_resources(self.resource_type)
|
||||||
|
|
||||||
if result is not None:
|
if result is not None:
|
||||||
await result
|
await result
|
||||||
|
|
|
@ -697,3 +697,10 @@ cdef inline object __future_result(object instance):
|
||||||
future_result = asyncio.Future()
|
future_result = asyncio.Future()
|
||||||
future_result.set_result(instance)
|
future_result.set_result(instance)
|
||||||
return future_result
|
return future_result
|
||||||
|
|
||||||
|
|
||||||
|
cdef class NullAwaitable:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
cdef NullAwaitable NULL_AWAITABLE
|
||||||
|
|
|
@ -15,8 +15,11 @@ import re
|
||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
import warnings
|
import warnings
|
||||||
|
from asyncio import ensure_future
|
||||||
from configparser import ConfigParser as IniConfigParser
|
from configparser import ConfigParser as IniConfigParser
|
||||||
|
from contextlib import asynccontextmanager, contextmanager
|
||||||
from contextvars import ContextVar
|
from contextvars import ContextVar
|
||||||
|
from inspect import isasyncgenfunction, isgeneratorfunction
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from inspect import _is_coroutine_mark as _is_coroutine_marker
|
from inspect import _is_coroutine_mark as _is_coroutine_marker
|
||||||
|
@ -3598,6 +3601,17 @@ cdef class Dict(Provider):
|
||||||
return __provide_keyword_args(kwargs, self._kwargs, self._kwargs_len, self._async_mode)
|
return __provide_keyword_args(kwargs, self._kwargs, self._kwargs_len, self._async_mode)
|
||||||
|
|
||||||
|
|
||||||
|
@cython.no_gc
|
||||||
|
cdef class NullAwaitable:
|
||||||
|
def __next__(self):
|
||||||
|
raise StopIteration from None
|
||||||
|
|
||||||
|
def __await__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
cdef NullAwaitable NULL_AWAITABLE = NullAwaitable()
|
||||||
|
|
||||||
|
|
||||||
cdef class Resource(Provider):
|
cdef class Resource(Provider):
|
||||||
"""Resource provider provides a component with initialization and shutdown."""
|
"""Resource provider provides a component with initialization and shutdown."""
|
||||||
|
@ -3653,6 +3667,12 @@ cdef class Resource(Provider):
|
||||||
def set_provides(self, provides):
|
def set_provides(self, provides):
|
||||||
"""Set provider provides."""
|
"""Set provider provides."""
|
||||||
provides = _resolve_string_import(provides)
|
provides = _resolve_string_import(provides)
|
||||||
|
|
||||||
|
if isasyncgenfunction(provides):
|
||||||
|
provides = asynccontextmanager(provides)
|
||||||
|
elif isgeneratorfunction(provides):
|
||||||
|
provides = contextmanager(provides)
|
||||||
|
|
||||||
self._provides = provides
|
self._provides = provides
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
@ -3753,28 +3773,21 @@ cdef class Resource(Provider):
|
||||||
"""Shutdown resource."""
|
"""Shutdown resource."""
|
||||||
if not self._initialized:
|
if not self._initialized:
|
||||||
if self._async_mode == ASYNC_MODE_ENABLED:
|
if self._async_mode == ASYNC_MODE_ENABLED:
|
||||||
result = asyncio.Future()
|
return NULL_AWAITABLE
|
||||||
result.set_result(None)
|
|
||||||
return result
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if self._shutdowner:
|
if self._shutdowner:
|
||||||
try:
|
future = self._shutdowner(None, None, None)
|
||||||
shutdown = self._shutdowner(self._resource)
|
|
||||||
except StopIteration:
|
if __is_future_or_coroutine(future):
|
||||||
pass
|
return ensure_future(self._shutdown_async(future))
|
||||||
else:
|
|
||||||
if inspect.isawaitable(shutdown):
|
|
||||||
return self._create_shutdown_future(shutdown)
|
|
||||||
|
|
||||||
self._resource = None
|
self._resource = None
|
||||||
self._initialized = False
|
self._initialized = False
|
||||||
self._shutdowner = None
|
self._shutdowner = None
|
||||||
|
|
||||||
if self._async_mode == ASYNC_MODE_ENABLED:
|
if self._async_mode == ASYNC_MODE_ENABLED:
|
||||||
result = asyncio.Future()
|
return NULL_AWAITABLE
|
||||||
result.set_result(None)
|
|
||||||
return result
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def related(self):
|
def related(self):
|
||||||
|
@ -3784,164 +3797,74 @@ cdef class Resource(Provider):
|
||||||
yield from filter(is_provider, self.kwargs.values())
|
yield from filter(is_provider, self.kwargs.values())
|
||||||
yield from super().related
|
yield from super().related
|
||||||
|
|
||||||
cpdef object _provide(self, tuple args, dict kwargs):
|
async def _shutdown_async(self, future) -> None:
|
||||||
if self._initialized:
|
|
||||||
return self._resource
|
|
||||||
|
|
||||||
if self._is_resource_subclass(self._provides):
|
|
||||||
initializer = self._provides()
|
|
||||||
self._resource = __call(
|
|
||||||
initializer.init,
|
|
||||||
args,
|
|
||||||
self._args,
|
|
||||||
self._args_len,
|
|
||||||
kwargs,
|
|
||||||
self._kwargs,
|
|
||||||
self._kwargs_len,
|
|
||||||
self._async_mode,
|
|
||||||
)
|
|
||||||
self._shutdowner = initializer.shutdown
|
|
||||||
elif self._is_async_resource_subclass(self._provides):
|
|
||||||
initializer = self._provides()
|
|
||||||
async_init = __call(
|
|
||||||
initializer.init,
|
|
||||||
args,
|
|
||||||
self._args,
|
|
||||||
self._args_len,
|
|
||||||
kwargs,
|
|
||||||
self._kwargs,
|
|
||||||
self._kwargs_len,
|
|
||||||
self._async_mode,
|
|
||||||
)
|
|
||||||
self._initialized = True
|
|
||||||
return self._create_init_future(async_init, initializer.shutdown)
|
|
||||||
elif inspect.isgeneratorfunction(self._provides):
|
|
||||||
initializer = __call(
|
|
||||||
self._provides,
|
|
||||||
args,
|
|
||||||
self._args,
|
|
||||||
self._args_len,
|
|
||||||
kwargs,
|
|
||||||
self._kwargs,
|
|
||||||
self._kwargs_len,
|
|
||||||
self._async_mode,
|
|
||||||
)
|
|
||||||
self._resource = next(initializer)
|
|
||||||
self._shutdowner = initializer.send
|
|
||||||
elif iscoroutinefunction(self._provides):
|
|
||||||
initializer = __call(
|
|
||||||
self._provides,
|
|
||||||
args,
|
|
||||||
self._args,
|
|
||||||
self._args_len,
|
|
||||||
kwargs,
|
|
||||||
self._kwargs,
|
|
||||||
self._kwargs_len,
|
|
||||||
self._async_mode,
|
|
||||||
)
|
|
||||||
self._initialized = True
|
|
||||||
return self._create_init_future(initializer)
|
|
||||||
elif isasyncgenfunction(self._provides):
|
|
||||||
initializer = __call(
|
|
||||||
self._provides,
|
|
||||||
args,
|
|
||||||
self._args,
|
|
||||||
self._args_len,
|
|
||||||
kwargs,
|
|
||||||
self._kwargs,
|
|
||||||
self._kwargs_len,
|
|
||||||
self._async_mode,
|
|
||||||
)
|
|
||||||
self._initialized = True
|
|
||||||
return self._create_async_gen_init_future(initializer)
|
|
||||||
elif callable(self._provides):
|
|
||||||
self._resource = __call(
|
|
||||||
self._provides,
|
|
||||||
args,
|
|
||||||
self._args,
|
|
||||||
self._args_len,
|
|
||||||
kwargs,
|
|
||||||
self._kwargs,
|
|
||||||
self._kwargs_len,
|
|
||||||
self._async_mode,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
raise Error("Unknown type of resource initializer")
|
|
||||||
|
|
||||||
self._initialized = True
|
|
||||||
return self._resource
|
|
||||||
|
|
||||||
def _create_init_future(self, future, shutdowner=None):
|
|
||||||
callback = self._async_init_callback
|
|
||||||
if shutdowner:
|
|
||||||
callback = functools.partial(callback, shutdowner=shutdowner)
|
|
||||||
|
|
||||||
future = asyncio.ensure_future(future)
|
|
||||||
future.add_done_callback(callback)
|
|
||||||
self._resource = future
|
|
||||||
|
|
||||||
return future
|
|
||||||
|
|
||||||
def _create_async_gen_init_future(self, initializer):
|
|
||||||
if inspect.isasyncgen(initializer):
|
|
||||||
return self._create_init_future(initializer.__anext__(), initializer.asend)
|
|
||||||
|
|
||||||
future = asyncio.Future()
|
|
||||||
|
|
||||||
create_initializer = asyncio.ensure_future(initializer)
|
|
||||||
create_initializer.add_done_callback(functools.partial(self._async_create_gen_callback, future))
|
|
||||||
self._resource = future
|
|
||||||
|
|
||||||
return future
|
|
||||||
|
|
||||||
def _async_init_callback(self, initializer, shutdowner=None):
|
|
||||||
try:
|
try:
|
||||||
resource = initializer.result()
|
await future
|
||||||
except Exception:
|
finally:
|
||||||
self._initialized = False
|
|
||||||
else:
|
|
||||||
self._resource = resource
|
|
||||||
self._shutdowner = shutdowner
|
|
||||||
|
|
||||||
def _async_create_gen_callback(self, future, initializer_future):
|
|
||||||
initializer = initializer_future.result()
|
|
||||||
init_future = self._create_init_future(initializer.__anext__(), initializer.asend)
|
|
||||||
init_future.add_done_callback(functools.partial(self._async_trigger_result, future))
|
|
||||||
|
|
||||||
def _async_trigger_result(self, future, future_result):
|
|
||||||
future.set_result(future_result.result())
|
|
||||||
|
|
||||||
def _create_shutdown_future(self, shutdown_future):
|
|
||||||
future = asyncio.Future()
|
|
||||||
shutdown_future = asyncio.ensure_future(shutdown_future)
|
|
||||||
shutdown_future.add_done_callback(functools.partial(self._async_shutdown_callback, future))
|
|
||||||
return future
|
|
||||||
|
|
||||||
def _async_shutdown_callback(self, future_result, shutdowner):
|
|
||||||
try:
|
|
||||||
shutdowner.result()
|
|
||||||
except StopAsyncIteration:
|
|
||||||
pass
|
|
||||||
|
|
||||||
self._resource = None
|
self._resource = None
|
||||||
self._initialized = False
|
self._initialized = False
|
||||||
self._shutdowner = None
|
self._shutdowner = None
|
||||||
|
|
||||||
future_result.set_result(None)
|
async def _handle_async_cm(self, obj) -> None:
|
||||||
|
try:
|
||||||
|
self._resource = resource = await obj.__aenter__()
|
||||||
|
self._shutdowner = obj.__aexit__
|
||||||
|
return resource
|
||||||
|
except:
|
||||||
|
self._initialized = False
|
||||||
|
raise
|
||||||
|
|
||||||
@staticmethod
|
async def _provide_async(self, future) -> None:
|
||||||
def _is_resource_subclass(instance):
|
try:
|
||||||
if not isinstance(instance, type):
|
obj = await future
|
||||||
return
|
|
||||||
from . import resources
|
|
||||||
return issubclass(instance, resources.Resource)
|
|
||||||
|
|
||||||
@staticmethod
|
if hasattr(obj, '__aenter__') and hasattr(obj, '__aexit__'):
|
||||||
def _is_async_resource_subclass(instance):
|
self._resource = await obj.__aenter__()
|
||||||
if not isinstance(instance, type):
|
self._shutdowner = obj.__aexit__
|
||||||
return
|
elif hasattr(obj, '__enter__') and hasattr(obj, '__exit__'):
|
||||||
from . import resources
|
self._resource = obj.__enter__()
|
||||||
return issubclass(instance, resources.AsyncResource)
|
self._shutdowner = obj.__exit__
|
||||||
|
else:
|
||||||
|
self._resource = obj
|
||||||
|
self._shutdowner = None
|
||||||
|
|
||||||
|
return self._resource
|
||||||
|
except:
|
||||||
|
self._initialized = False
|
||||||
|
raise
|
||||||
|
|
||||||
|
cpdef object _provide(self, tuple args, dict kwargs):
|
||||||
|
if self._initialized:
|
||||||
|
return self._resource
|
||||||
|
|
||||||
|
obj = __call(
|
||||||
|
self._provides,
|
||||||
|
args,
|
||||||
|
self._args,
|
||||||
|
self._args_len,
|
||||||
|
kwargs,
|
||||||
|
self._kwargs,
|
||||||
|
self._kwargs_len,
|
||||||
|
self._async_mode,
|
||||||
|
)
|
||||||
|
|
||||||
|
if __is_future_or_coroutine(obj):
|
||||||
|
self._initialized = True
|
||||||
|
self._resource = resource = ensure_future(self._provide_async(obj))
|
||||||
|
return resource
|
||||||
|
elif hasattr(obj, '__enter__') and hasattr(obj, '__exit__'):
|
||||||
|
self._resource = obj.__enter__()
|
||||||
|
self._shutdowner = obj.__exit__
|
||||||
|
elif hasattr(obj, '__aenter__') and hasattr(obj, '__aexit__'):
|
||||||
|
self._initialized = True
|
||||||
|
self._resource = resource = ensure_future(self._handle_async_cm(obj))
|
||||||
|
return resource
|
||||||
|
else:
|
||||||
|
self._resource = obj
|
||||||
|
self._shutdowner = None
|
||||||
|
|
||||||
|
self._initialized = True
|
||||||
|
return self._resource
|
||||||
|
|
||||||
|
|
||||||
cdef class Container(Provider):
|
cdef class Container(Provider):
|
||||||
|
@ -4993,14 +4916,6 @@ def iscoroutinefunction(obj):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def isasyncgenfunction(obj):
|
|
||||||
"""Check if object is an asynchronous generator function."""
|
|
||||||
try:
|
|
||||||
return inspect.isasyncgenfunction(obj)
|
|
||||||
except AttributeError:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def _resolve_string_import(provides):
|
def _resolve_string_import(provides):
|
||||||
if provides is None:
|
if provides is None:
|
||||||
return provides
|
return provides
|
||||||
|
|
|
@ -1,23 +1,54 @@
|
||||||
"""Resources module."""
|
"""Resources module."""
|
||||||
|
|
||||||
import abc
|
from abc import ABCMeta, abstractmethod
|
||||||
from typing import TypeVar, Generic, Optional
|
from typing import Any, ClassVar, Generic, Optional, Tuple, TypeVar
|
||||||
|
|
||||||
|
|
||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
|
||||||
class Resource(Generic[T], metaclass=abc.ABCMeta):
|
class Resource(Generic[T], metaclass=ABCMeta):
|
||||||
|
__slots__: ClassVar[Tuple[str, ...]] = ("args", "kwargs", "obj")
|
||||||
|
|
||||||
@abc.abstractmethod
|
obj: Optional[T]
|
||||||
def init(self, *args, **kwargs) -> Optional[T]: ...
|
|
||||||
|
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||||
|
self.args = args
|
||||||
|
self.kwargs = kwargs
|
||||||
|
self.obj = None
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def init(self, *args: Any, **kwargs: Any) -> Optional[T]: ...
|
||||||
|
|
||||||
def shutdown(self, resource: Optional[T]) -> None: ...
|
def shutdown(self, resource: Optional[T]) -> None: ...
|
||||||
|
|
||||||
|
def __enter__(self) -> Optional[T]:
|
||||||
|
self.obj = obj = self.init(*self.args, **self.kwargs)
|
||||||
|
return obj
|
||||||
|
|
||||||
class AsyncResource(Generic[T], metaclass=abc.ABCMeta):
|
def __exit__(self, *exc_info: Any) -> None:
|
||||||
|
self.shutdown(self.obj)
|
||||||
|
self.obj = None
|
||||||
|
|
||||||
@abc.abstractmethod
|
|
||||||
async def init(self, *args, **kwargs) -> Optional[T]: ...
|
class AsyncResource(Generic[T], metaclass=ABCMeta):
|
||||||
|
__slots__: ClassVar[Tuple[str, ...]] = ("args", "kwargs", "obj")
|
||||||
|
|
||||||
|
obj: Optional[T]
|
||||||
|
|
||||||
|
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||||
|
self.args = args
|
||||||
|
self.kwargs = kwargs
|
||||||
|
self.obj = None
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
async def init(self, *args: Any, **kwargs: Any) -> Optional[T]: ...
|
||||||
|
|
||||||
async def shutdown(self, resource: Optional[T]) -> None: ...
|
async def shutdown(self, resource: Optional[T]) -> None: ...
|
||||||
|
|
||||||
|
async def __aenter__(self) -> Optional[T]:
|
||||||
|
self.obj = obj = await self.init(*self.args, **self.kwargs)
|
||||||
|
return obj
|
||||||
|
|
||||||
|
async def __aexit__(self, *exc_info: Any) -> None:
|
||||||
|
await self.shutdown(self.obj)
|
||||||
|
self.obj = None
|
||||||
|
|
|
@ -6,15 +6,20 @@ import importlib.machinery
|
||||||
import inspect
|
import inspect
|
||||||
import pkgutil
|
import pkgutil
|
||||||
import sys
|
import sys
|
||||||
|
from contextlib import suppress
|
||||||
|
from inspect import isbuiltin, isclass
|
||||||
from types import ModuleType
|
from types import ModuleType
|
||||||
from typing import (
|
from typing import (
|
||||||
|
TYPE_CHECKING,
|
||||||
Any,
|
Any,
|
||||||
|
AsyncIterator,
|
||||||
Callable,
|
Callable,
|
||||||
Dict,
|
Dict,
|
||||||
Generic,
|
|
||||||
Iterable,
|
Iterable,
|
||||||
Iterator,
|
Iterator,
|
||||||
|
List,
|
||||||
Optional,
|
Optional,
|
||||||
|
Protocol,
|
||||||
Set,
|
Set,
|
||||||
Tuple,
|
Tuple,
|
||||||
Type,
|
Type,
|
||||||
|
@ -22,7 +27,19 @@ from typing import (
|
||||||
Union,
|
Union,
|
||||||
cast,
|
cast,
|
||||||
)
|
)
|
||||||
|
from warnings import warn
|
||||||
|
|
||||||
|
try:
|
||||||
|
from typing import Self
|
||||||
|
except ImportError:
|
||||||
|
from typing_extensions import Self
|
||||||
|
|
||||||
|
try:
|
||||||
|
from functools import cache
|
||||||
|
except ImportError:
|
||||||
|
from functools import lru_cache
|
||||||
|
|
||||||
|
cache = lru_cache(maxsize=None)
|
||||||
|
|
||||||
# Hotfix, see: https://github.com/ets-labs/python-dependency-injector/issues/362
|
# Hotfix, see: https://github.com/ets-labs/python-dependency-injector/issues/362
|
||||||
if sys.version_info >= (3, 9):
|
if sys.version_info >= (3, 9):
|
||||||
|
@ -46,26 +63,48 @@ else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
try:
|
MARKER_EXTRACTORS: List[Callable[[Any], Any]] = []
|
||||||
import fastapi.params
|
INSPECT_EXCLUSION_FILTERS: List[Callable[[Any], bool]] = [isbuiltin]
|
||||||
except ImportError:
|
|
||||||
fastapi = None
|
with suppress(ImportError):
|
||||||
|
from fastapi.params import Depends as FastAPIDepends
|
||||||
|
|
||||||
|
def extract_marker_from_fastapi(param: Any) -> Any:
|
||||||
|
if isinstance(param, FastAPIDepends):
|
||||||
|
return param.dependency
|
||||||
|
return None
|
||||||
|
|
||||||
|
MARKER_EXTRACTORS.append(extract_marker_from_fastapi)
|
||||||
|
|
||||||
|
with suppress(ImportError):
|
||||||
|
from fast_depends.dependencies import Depends as FastDepends
|
||||||
|
|
||||||
|
def extract_marker_from_fast_depends(param: Any) -> Any:
|
||||||
|
if isinstance(param, FastDepends):
|
||||||
|
return param.dependency
|
||||||
|
return None
|
||||||
|
|
||||||
|
MARKER_EXTRACTORS.append(extract_marker_from_fast_depends)
|
||||||
|
|
||||||
|
|
||||||
try:
|
with suppress(ImportError):
|
||||||
import starlette.requests
|
from starlette.requests import Request as StarletteRequest
|
||||||
except ImportError:
|
|
||||||
starlette = None
|
def is_starlette_request_cls(obj: Any) -> bool:
|
||||||
|
return isclass(obj) and _safe_is_subclass(obj, StarletteRequest)
|
||||||
|
|
||||||
|
INSPECT_EXCLUSION_FILTERS.append(is_starlette_request_cls)
|
||||||
|
|
||||||
|
|
||||||
try:
|
with suppress(ImportError):
|
||||||
import werkzeug.local
|
from werkzeug.local import LocalProxy as WerkzeugLocalProxy
|
||||||
except ImportError:
|
|
||||||
werkzeug = None
|
|
||||||
|
|
||||||
|
def is_werkzeug_local_proxy(obj: Any) -> bool:
|
||||||
|
return isinstance(obj, WerkzeugLocalProxy)
|
||||||
|
|
||||||
from . import providers
|
INSPECT_EXCLUSION_FILTERS.append(is_werkzeug_local_proxy)
|
||||||
|
|
||||||
|
from . import providers # noqa: E402
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
"wire",
|
"wire",
|
||||||
|
@ -89,9 +128,17 @@ __all__ = (
|
||||||
|
|
||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
F = TypeVar("F", bound=Callable[..., Any])
|
F = TypeVar("F", bound=Callable[..., Any])
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from .containers import Container
|
||||||
|
else:
|
||||||
Container = Any
|
Container = Any
|
||||||
|
|
||||||
|
|
||||||
|
class DIWiringWarning(RuntimeWarning):
|
||||||
|
"""Base class for all warnings raised by the wiring module."""
|
||||||
|
|
||||||
|
|
||||||
class PatchedRegistry:
|
class PatchedRegistry:
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
|
@ -373,37 +420,19 @@ class ProvidersMap:
|
||||||
return providers_map
|
return providers_map
|
||||||
|
|
||||||
|
|
||||||
class InspectFilter:
|
def is_excluded_from_inspect(obj: Any) -> bool:
|
||||||
|
for is_excluded in INSPECT_EXCLUSION_FILTERS:
|
||||||
def is_excluded(self, instance: object) -> bool:
|
if is_excluded(obj):
|
||||||
if self._is_werkzeug_local_proxy(instance):
|
|
||||||
return True
|
return True
|
||||||
elif self._is_starlette_request_cls(instance):
|
|
||||||
return True
|
|
||||||
elif self._is_builtin(instance):
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _is_werkzeug_local_proxy(self, instance: object) -> bool:
|
|
||||||
return werkzeug and isinstance(instance, werkzeug.local.LocalProxy)
|
|
||||||
|
|
||||||
def _is_starlette_request_cls(self, instance: object) -> bool:
|
|
||||||
return (
|
|
||||||
starlette
|
|
||||||
and isinstance(instance, type)
|
|
||||||
and _safe_is_subclass(instance, starlette.requests.Request)
|
|
||||||
)
|
|
||||||
|
|
||||||
def _is_builtin(self, instance: object) -> bool:
|
|
||||||
return inspect.isbuiltin(instance)
|
|
||||||
|
|
||||||
|
|
||||||
def wire( # noqa: C901
|
def wire( # noqa: C901
|
||||||
container: Container,
|
container: Container,
|
||||||
*,
|
*,
|
||||||
modules: Optional[Iterable[ModuleType]] = None,
|
modules: Optional[Iterable[ModuleType]] = None,
|
||||||
packages: Optional[Iterable[ModuleType]] = None,
|
packages: Optional[Iterable[ModuleType]] = None,
|
||||||
|
keep_cache: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Wire container providers with provided packages and modules."""
|
"""Wire container providers with provided packages and modules."""
|
||||||
modules = [*modules] if modules else []
|
modules = [*modules] if modules else []
|
||||||
|
@ -416,7 +445,7 @@ def wire( # noqa: C901
|
||||||
|
|
||||||
for module in modules:
|
for module in modules:
|
||||||
for member_name, member in _get_members_and_annotated(module):
|
for member_name, member in _get_members_and_annotated(module):
|
||||||
if _inspect_filter.is_excluded(member):
|
if is_excluded_from_inspect(member):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if _is_marker(member):
|
if _is_marker(member):
|
||||||
|
@ -444,6 +473,9 @@ def wire( # noqa: C901
|
||||||
for patched in _patched_registry.get_callables_from_module(module):
|
for patched in _patched_registry.get_callables_from_module(module):
|
||||||
_bind_injections(patched, providers_map)
|
_bind_injections(patched, providers_map)
|
||||||
|
|
||||||
|
if not keep_cache:
|
||||||
|
clear_cache()
|
||||||
|
|
||||||
|
|
||||||
def unwire( # noqa: C901
|
def unwire( # noqa: C901
|
||||||
*,
|
*,
|
||||||
|
@ -478,6 +510,11 @@ def unwire( # noqa: C901
|
||||||
def inject(fn: F) -> F:
|
def inject(fn: F) -> F:
|
||||||
"""Decorate callable with injecting decorator."""
|
"""Decorate callable with injecting decorator."""
|
||||||
reference_injections, reference_closing = _fetch_reference_injections(fn)
|
reference_injections, reference_closing = _fetch_reference_injections(fn)
|
||||||
|
|
||||||
|
if not reference_injections:
|
||||||
|
warn("@inject is not required here", DIWiringWarning, stacklevel=2)
|
||||||
|
return fn
|
||||||
|
|
||||||
patched = _get_patched(fn, reference_injections, reference_closing)
|
patched = _get_patched(fn, reference_injections, reference_closing)
|
||||||
return cast(F, patched)
|
return cast(F, patched)
|
||||||
|
|
||||||
|
@ -587,11 +624,10 @@ def _extract_marker(parameter: inspect.Parameter) -> Optional["_Marker"]:
|
||||||
else:
|
else:
|
||||||
marker = parameter.default
|
marker = parameter.default
|
||||||
|
|
||||||
if not isinstance(marker, _Marker) and not _is_fastapi_depends(marker):
|
for marker_extractor in MARKER_EXTRACTORS:
|
||||||
return None
|
if _marker := marker_extractor(marker):
|
||||||
|
marker = _marker
|
||||||
if _is_fastapi_depends(marker):
|
break
|
||||||
marker = marker.dependency
|
|
||||||
|
|
||||||
if not isinstance(marker, _Marker):
|
if not isinstance(marker, _Marker):
|
||||||
return None
|
return None
|
||||||
|
@ -599,6 +635,7 @@ def _extract_marker(parameter: inspect.Parameter) -> Optional["_Marker"]:
|
||||||
return marker
|
return marker
|
||||||
|
|
||||||
|
|
||||||
|
@cache
|
||||||
def _fetch_reference_injections( # noqa: C901
|
def _fetch_reference_injections( # noqa: C901
|
||||||
fn: Callable[..., Any],
|
fn: Callable[..., Any],
|
||||||
) -> Tuple[Dict[str, Any], Dict[str, Any]]:
|
) -> Tuple[Dict[str, Any], Dict[str, Any]]:
|
||||||
|
@ -703,6 +740,8 @@ def _get_patched(
|
||||||
|
|
||||||
if inspect.iscoroutinefunction(fn):
|
if inspect.iscoroutinefunction(fn):
|
||||||
patched = _get_async_patched(fn, patched_object)
|
patched = _get_async_patched(fn, patched_object)
|
||||||
|
elif inspect.isasyncgenfunction(fn):
|
||||||
|
patched = _get_async_gen_patched(fn, patched_object)
|
||||||
else:
|
else:
|
||||||
patched = _get_sync_patched(fn, patched_object)
|
patched = _get_sync_patched(fn, patched_object)
|
||||||
|
|
||||||
|
@ -712,10 +751,6 @@ def _get_patched(
|
||||||
return patched
|
return patched
|
||||||
|
|
||||||
|
|
||||||
def _is_fastapi_depends(param: Any) -> bool:
|
|
||||||
return fastapi and isinstance(param, fastapi.params.Depends)
|
|
||||||
|
|
||||||
|
|
||||||
def _is_patched(fn) -> bool:
|
def _is_patched(fn) -> bool:
|
||||||
return _patched_registry.has_callable(fn)
|
return _patched_registry.has_callable(fn)
|
||||||
|
|
||||||
|
@ -777,15 +812,15 @@ class RequiredModifier(Modifier):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.type_modifier = None
|
self.type_modifier = None
|
||||||
|
|
||||||
def as_int(self) -> "RequiredModifier":
|
def as_int(self) -> Self:
|
||||||
self.type_modifier = TypeModifier(int)
|
self.type_modifier = TypeModifier(int)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def as_float(self) -> "RequiredModifier":
|
def as_float(self) -> Self:
|
||||||
self.type_modifier = TypeModifier(float)
|
self.type_modifier = TypeModifier(float)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def as_(self, type_: Type) -> "RequiredModifier":
|
def as_(self, type_: Type) -> Self:
|
||||||
self.type_modifier = TypeModifier(type_)
|
self.type_modifier = TypeModifier(type_)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
@ -833,15 +868,15 @@ class ProvidedInstance(Modifier):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.segments = []
|
self.segments = []
|
||||||
|
|
||||||
def __getattr__(self, item):
|
def __getattr__(self, item: str) -> Self:
|
||||||
self.segments.append((self.TYPE_ATTRIBUTE, item))
|
self.segments.append((self.TYPE_ATTRIBUTE, item))
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __getitem__(self, item):
|
def __getitem__(self, item) -> Self:
|
||||||
self.segments.append((self.TYPE_ITEM, item))
|
self.segments.append((self.TYPE_ITEM, item))
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def call(self):
|
def call(self) -> Self:
|
||||||
self.segments.append((self.TYPE_CALL, None))
|
self.segments.append((self.TYPE_CALL, None))
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
@ -866,7 +901,30 @@ def provided() -> ProvidedInstance:
|
||||||
return ProvidedInstance()
|
return ProvidedInstance()
|
||||||
|
|
||||||
|
|
||||||
class _Marker(Generic[T]):
|
MarkerItem = Union[
|
||||||
|
str,
|
||||||
|
providers.Provider[Any],
|
||||||
|
Tuple[str, TypeModifier],
|
||||||
|
Type[Container],
|
||||||
|
"_Marker",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
|
||||||
|
class _Marker(Protocol):
|
||||||
|
__IS_MARKER__: bool
|
||||||
|
|
||||||
|
def __call__(self) -> Self: ...
|
||||||
|
def __getattr__(self, item: str) -> Self: ...
|
||||||
|
def __getitem__(self, item: Any) -> Any: ...
|
||||||
|
|
||||||
|
Provide: _Marker
|
||||||
|
Provider: _Marker
|
||||||
|
Closing: _Marker
|
||||||
|
else:
|
||||||
|
|
||||||
|
class _Marker:
|
||||||
|
|
||||||
__IS_MARKER__ = True
|
__IS_MARKER__ = True
|
||||||
|
|
||||||
|
@ -880,21 +938,18 @@ class _Marker(Generic[T]):
|
||||||
self.provider = provider
|
self.provider = provider
|
||||||
self.modifier = modifier
|
self.modifier = modifier
|
||||||
|
|
||||||
def __class_getitem__(cls, item) -> T:
|
def __class_getitem__(cls, item: MarkerItem) -> Self:
|
||||||
if isinstance(item, tuple):
|
if isinstance(item, tuple):
|
||||||
return cls(*item)
|
return cls(*item)
|
||||||
return cls(item)
|
return cls(item)
|
||||||
|
|
||||||
def __call__(self) -> T:
|
def __call__(self) -> Self:
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
class Provide(_Marker): ...
|
class Provide(_Marker): ...
|
||||||
|
|
||||||
|
|
||||||
class Provider(_Marker): ...
|
class Provider(_Marker): ...
|
||||||
|
|
||||||
|
|
||||||
class Closing(_Marker): ...
|
class Closing(_Marker): ...
|
||||||
|
|
||||||
|
|
||||||
|
@ -994,47 +1049,55 @@ def is_loader_installed() -> bool:
|
||||||
|
|
||||||
|
|
||||||
_patched_registry = PatchedRegistry()
|
_patched_registry = PatchedRegistry()
|
||||||
_inspect_filter = InspectFilter()
|
|
||||||
_loader = AutoLoader()
|
_loader = AutoLoader()
|
||||||
|
|
||||||
# Optimizations
|
# Optimizations
|
||||||
from ._cwiring import _sync_inject # noqa
|
from ._cwiring import DependencyResolver # noqa: E402
|
||||||
from ._cwiring import _async_inject # noqa
|
|
||||||
|
|
||||||
|
|
||||||
# Wiring uses the following Python wrapper because there is
|
# Wiring uses the following Python wrapper because there is
|
||||||
# no possibility to compile a first-type citizen coroutine in Cython.
|
# no possibility to compile a first-type citizen coroutine in Cython.
|
||||||
def _get_async_patched(fn: F, patched: PatchedCallable) -> F:
|
def _get_async_patched(fn: F, patched: PatchedCallable) -> F:
|
||||||
@functools.wraps(fn)
|
@functools.wraps(fn)
|
||||||
async def _patched(*args, **kwargs):
|
async def _patched(*args: Any, **raw_kwargs: Any) -> Any:
|
||||||
return await _async_inject(
|
resolver = DependencyResolver(raw_kwargs, patched.injections, patched.closing)
|
||||||
fn,
|
|
||||||
args,
|
async with resolver as kwargs:
|
||||||
kwargs,
|
return await fn(*args, **kwargs)
|
||||||
patched.injections,
|
|
||||||
patched.closing,
|
return cast(F, _patched)
|
||||||
)
|
|
||||||
|
|
||||||
|
def _get_async_gen_patched(fn: F, patched: PatchedCallable) -> F:
|
||||||
|
@functools.wraps(fn)
|
||||||
|
async def _patched(*args: Any, **raw_kwargs: Any) -> AsyncIterator[Any]:
|
||||||
|
resolver = DependencyResolver(raw_kwargs, patched.injections, patched.closing)
|
||||||
|
|
||||||
|
async with resolver as kwargs:
|
||||||
|
async for obj in fn(*args, **kwargs):
|
||||||
|
yield obj
|
||||||
|
|
||||||
return cast(F, _patched)
|
return cast(F, _patched)
|
||||||
|
|
||||||
|
|
||||||
def _get_sync_patched(fn: F, patched: PatchedCallable) -> F:
|
def _get_sync_patched(fn: F, patched: PatchedCallable) -> F:
|
||||||
@functools.wraps(fn)
|
@functools.wraps(fn)
|
||||||
def _patched(*args, **kwargs):
|
def _patched(*args: Any, **raw_kwargs: Any) -> Any:
|
||||||
return _sync_inject(
|
resolver = DependencyResolver(raw_kwargs, patched.injections, patched.closing)
|
||||||
fn,
|
|
||||||
args,
|
with resolver as kwargs:
|
||||||
kwargs,
|
return fn(*args, **kwargs)
|
||||||
patched.injections,
|
|
||||||
patched.closing,
|
|
||||||
)
|
|
||||||
return cast(F, _patched)
|
return cast(F, _patched)
|
||||||
|
|
||||||
|
|
||||||
if sys.version_info >= (3, 10):
|
if sys.version_info >= (3, 10):
|
||||||
|
|
||||||
def _get_annotations(obj: Any) -> Dict[str, Any]:
|
def _get_annotations(obj: Any) -> Dict[str, Any]:
|
||||||
return inspect.get_annotations(obj)
|
return inspect.get_annotations(obj)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
||||||
def _get_annotations(obj: Any) -> Dict[str, Any]:
|
def _get_annotations(obj: Any) -> Dict[str, Any]:
|
||||||
return getattr(obj, "__annotations__", {})
|
return getattr(obj, "__annotations__", {})
|
||||||
|
|
||||||
|
@ -1049,3 +1112,8 @@ def _get_members_and_annotated(obj: Any) -> Iterable[Tuple[str, Any]]:
|
||||||
member = args[1]
|
member = args[1]
|
||||||
members.append((annotation_name, member))
|
members.append((annotation_name, member))
|
||||||
return members
|
return members
|
||||||
|
|
||||||
|
|
||||||
|
def clear_cache() -> None:
|
||||||
|
"""Clear all caches used by :func:`wire`."""
|
||||||
|
_fetch_reference_injections.cache_clear()
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
from dependency_injector import providers
|
from dependency_injector import providers
|
||||||
|
|
||||||
|
|
||||||
class Animal:
|
class Animal: ...
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
class Cat(Animal):
|
class Cat(Animal): ...
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
# Test 1: to check Aggregate provider
|
# Test 1: to check Aggregate provider
|
||||||
|
@ -20,13 +18,19 @@ val1: str = provider1("a")
|
||||||
|
|
||||||
provider1_set_non_string_keys: providers.Aggregate[str] = providers.Aggregate()
|
provider1_set_non_string_keys: providers.Aggregate[str] = providers.Aggregate()
|
||||||
provider1_set_non_string_keys.set_providers({Cat: providers.Object("str")})
|
provider1_set_non_string_keys.set_providers({Cat: providers.Object("str")})
|
||||||
provider_set_non_string_1: providers.Provider[str] = provider1_set_non_string_keys.providers[Cat]
|
provider_set_non_string_1: providers.Provider[str] = (
|
||||||
|
provider1_set_non_string_keys.providers[Cat]
|
||||||
|
)
|
||||||
|
|
||||||
provider1_new_non_string_keys: providers.Aggregate[str] = providers.Aggregate(
|
provider1_new_non_string_keys: providers.Aggregate[str] = providers.Aggregate(
|
||||||
{Cat: providers.Object("str")},
|
{Cat: providers.Object("str")},
|
||||||
)
|
)
|
||||||
factory_new_non_string_1: providers.Provider[str] = provider1_new_non_string_keys.providers[Cat]
|
factory_new_non_string_1: providers.Provider[str] = (
|
||||||
|
provider1_new_non_string_keys.providers[Cat]
|
||||||
|
)
|
||||||
|
|
||||||
provider1_no_explicit_typing = providers.Aggregate(a=providers.Object("str"))
|
provider1_no_explicit_typing = providers.Aggregate(a=providers.Object("str"))
|
||||||
provider1_no_explicit_typing_factory: providers.Provider[str] = provider1_no_explicit_typing.providers["a"]
|
provider1_no_explicit_typing_factory: providers.Provider[str] = (
|
||||||
|
provider1_no_explicit_typing.providers["a"]
|
||||||
|
)
|
||||||
provider1_no_explicit_typing_object: str = provider1_no_explicit_typing("a")
|
provider1_no_explicit_typing_object: str = provider1_no_explicit_typing("a")
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
from typing import Callable, Optional, Tuple, Any, Dict, Type
|
from typing import Any, Callable, Dict, Optional, Tuple, Type
|
||||||
|
|
||||||
from dependency_injector import providers
|
from dependency_injector import providers
|
||||||
|
|
||||||
|
|
||||||
class Animal:
|
class Animal: ...
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
class Cat(Animal):
|
class Cat(Animal):
|
||||||
|
@ -53,10 +52,13 @@ provider8 = providers.CallableDelegate(providers.Callable(lambda: None))
|
||||||
|
|
||||||
# Test 9: to check the return type with await
|
# Test 9: to check the return type with await
|
||||||
provider9 = providers.Callable(Cat)
|
provider9 = providers.Callable(Cat)
|
||||||
|
|
||||||
|
|
||||||
async def _async9() -> None:
|
async def _async9() -> None:
|
||||||
animal1: Animal = await provider9(1, 2, 3, b="1", c=2, e=0.0) # type: ignore
|
animal1: Animal = await provider9(1, 2, 3, b="1", c=2, e=0.0) # type: ignore
|
||||||
animal2: Animal = await provider9.async_(1, 2, 3, b="1", c=2, e=0.0)
|
animal2: Animal = await provider9.async_(1, 2, 3, b="1", c=2, e=0.0)
|
||||||
|
|
||||||
|
|
||||||
# Test 10: to check the .provides
|
# Test 10: to check the .provides
|
||||||
provider10 = providers.Callable(Cat)
|
provider10 = providers.Callable(Cat)
|
||||||
provides10: Optional[Callable[..., Cat]] = provider10.provides
|
provides10: Optional[Callable[..., Cat]] = provider10.provides
|
||||||
|
@ -68,5 +70,5 @@ provides11: Optional[Callable[..., Animal]] = provider11.provides
|
||||||
assert provides11 is Cat
|
assert provides11 is Cat
|
||||||
|
|
||||||
# Test 12: to check string imports
|
# Test 12: to check string imports
|
||||||
provider12: providers.Callable[dict] = providers.Callable("builtins.dict")
|
provider12: providers.Callable[Dict[Any, Any]] = providers.Callable("builtins.dict")
|
||||||
provider12.set_provides("builtins.dict")
|
provider12.set_provides("builtins.dict")
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any, Dict
|
||||||
|
|
||||||
from dependency_injector import providers
|
|
||||||
from pydantic_settings import BaseSettings as PydanticSettings
|
from pydantic_settings import BaseSettings as PydanticSettings
|
||||||
|
|
||||||
|
from dependency_injector import providers
|
||||||
|
|
||||||
# Test 1: to check the getattr
|
# Test 1: to check the getattr
|
||||||
config1: providers.Configuration = providers.Configuration()
|
config1: providers.Configuration = providers.Configuration()
|
||||||
provider1: providers.Provider[dict] = providers.Factory(dict, a=config1.a)
|
provider1: providers.Provider[Dict[str, Any]] = providers.Factory(dict, a=config1.a)
|
||||||
|
|
||||||
# Test 2: to check the from_*() method
|
# Test 2: to check the from_*() method
|
||||||
config2 = providers.Configuration()
|
config2 = providers.Configuration()
|
||||||
|
@ -68,7 +68,9 @@ config5_pydantic = providers.Configuration(
|
||||||
pydantic_settings=[PydanticSettings()],
|
pydantic_settings=[PydanticSettings()],
|
||||||
)
|
)
|
||||||
config5_pydantic.set_pydantic_settings([PydanticSettings()])
|
config5_pydantic.set_pydantic_settings([PydanticSettings()])
|
||||||
config5_pydantic_settings: list[PydanticSettings] = config5_pydantic.get_pydantic_settings()
|
config5_pydantic_settings: list[PydanticSettings] = (
|
||||||
|
config5_pydantic.get_pydantic_settings()
|
||||||
|
)
|
||||||
|
|
||||||
# Test 6: to check init arguments
|
# Test 6: to check init arguments
|
||||||
config6 = providers.Configuration(
|
config6 = providers.Configuration(
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from dependency_injector import providers
|
from dependency_injector import providers
|
||||||
|
|
||||||
|
|
||||||
class Container:
|
class Container: ...
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
# Test 1: to check the return type
|
# Test 1: to check the return type
|
||||||
|
@ -11,4 +12,4 @@ var1: Container = provider1()
|
||||||
|
|
||||||
# Test 2: to check the getattr
|
# Test 2: to check the getattr
|
||||||
provider2 = providers.Container(Container)
|
provider2 = providers.Container(Container)
|
||||||
attr: providers.Provider = provider2.attr
|
attr: providers.Provider[Any] = provider2.attr
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
from typing import Coroutine
|
from typing import Awaitable, Coroutine
|
||||||
|
|
||||||
from dependency_injector import providers
|
from dependency_injector import providers
|
||||||
|
|
||||||
|
|
||||||
async def _coro() -> None:
|
async def _coro() -> None: ...
|
||||||
...
|
|
||||||
|
|
||||||
# Test 1: to check the return type
|
# Test 1: to check the return type
|
||||||
provider1 = providers.Coroutine(_coro)
|
provider1 = providers.Coroutine(_coro)
|
||||||
var1: Coroutine = provider1()
|
var1: Awaitable[None] = provider1()
|
||||||
|
|
||||||
# Test 2: to check string imports
|
# Test 2: to check string imports
|
||||||
provider2: providers.Coroutine[None] = providers.Coroutine("_coro")
|
provider2: providers.Coroutine[None] = providers.Coroutine("_coro")
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from typing import Dict
|
from typing import Any, Dict
|
||||||
|
|
||||||
from dependency_injector import containers, providers
|
from dependency_injector import containers, providers
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ class Container1(containers.DeclarativeContainer):
|
||||||
|
|
||||||
container1 = Container1()
|
container1 = Container1()
|
||||||
container1_type: containers.Container = Container1()
|
container1_type: containers.Container = Container1()
|
||||||
provider1: providers.Provider = container1.provider
|
provider1: providers.Provider[int] = container1.provider
|
||||||
val1: int = container1.provider(3)
|
val1: int = container1.provider(3)
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,8 +20,7 @@ class Container21(containers.DeclarativeContainer):
|
||||||
|
|
||||||
|
|
||||||
@containers.override(Container21)
|
@containers.override(Container21)
|
||||||
class Container22(containers.DeclarativeContainer):
|
class Container22(containers.DeclarativeContainer): ...
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
# Test 3: to check @copy decorator
|
# Test 3: to check @copy decorator
|
||||||
|
@ -30,14 +29,14 @@ class Container31(containers.DeclarativeContainer):
|
||||||
|
|
||||||
|
|
||||||
@containers.copy(Container31)
|
@containers.copy(Container31)
|
||||||
class Container32(containers.DeclarativeContainer):
|
class Container32(containers.DeclarativeContainer): ...
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
# Test 4: to override()
|
# Test 4: to override()
|
||||||
class Container4(containers.DeclarativeContainer):
|
class Container4(containers.DeclarativeContainer):
|
||||||
provider = providers.Factory(int)
|
provider = providers.Factory(int)
|
||||||
|
|
||||||
|
|
||||||
container4 = Container4()
|
container4 = Container4()
|
||||||
container4.override(Container4())
|
container4.override(Container4())
|
||||||
|
|
||||||
|
@ -47,7 +46,7 @@ class Container5(containers.DeclarativeContainer):
|
||||||
provider = providers.Factory(int)
|
provider = providers.Factory(int)
|
||||||
|
|
||||||
|
|
||||||
dependencies: Dict[str, providers.Provider] = Container5.dependencies
|
dependencies: Dict[str, providers.Provider[Any]] = Container5.dependencies
|
||||||
|
|
||||||
|
|
||||||
# Test 6: to check base class
|
# Test 6: to check base class
|
||||||
|
@ -62,6 +61,7 @@ container6: containers.Container = Container6()
|
||||||
class Container7(containers.DeclarativeContainer):
|
class Container7(containers.DeclarativeContainer):
|
||||||
provider = providers.Factory(str)
|
provider = providers.Factory(str)
|
||||||
|
|
||||||
|
|
||||||
container7 = Container7()
|
container7 = Container7()
|
||||||
container7.override_providers(provider="new_value")
|
container7.override_providers(provider="new_value")
|
||||||
with container7.override_providers(a=providers.Provider()):
|
with container7.override_providers(a=providers.Provider()):
|
||||||
|
|
|
@ -1,17 +1,20 @@
|
||||||
from typing import Optional
|
from typing import Any, Optional
|
||||||
|
|
||||||
from dependency_injector import providers
|
from dependency_injector import providers
|
||||||
|
|
||||||
# Test 1: to check the return type
|
# Test 1: to check the return type
|
||||||
provider1 = providers.Delegate(providers.Provider())
|
provider1 = providers.Delegate(providers.Provider())
|
||||||
var1: providers.Provider = provider1()
|
var1: providers.Provider[Any] = provider1()
|
||||||
|
|
||||||
# Test 2: to check the return type with await
|
# Test 2: to check the return type with await
|
||||||
provider2 = providers.Delegate(providers.Provider())
|
provider2 = providers.Delegate(providers.Provider())
|
||||||
|
|
||||||
|
|
||||||
async def _async2() -> None:
|
async def _async2() -> None:
|
||||||
var1: providers.Provider = await provider2() # type: ignore
|
var1: providers.Provider[Any] = await provider2() # type: ignore
|
||||||
var2: providers.Provider = await provider2.async_()
|
var2: providers.Provider[Any] = await provider2.async_()
|
||||||
|
|
||||||
|
|
||||||
# Test 3: to check class type from provider
|
# Test 3: to check class type from provider
|
||||||
provider3 = providers.Delegate(providers.Provider())
|
provider3 = providers.Delegate(providers.Provider())
|
||||||
provided_provides: Optional[providers.Provider] = provider3.provides
|
provided_provides: Optional[providers.Provider[Any]] = provider3.provides
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
from dependency_injector import providers
|
from typing import Any
|
||||||
|
|
||||||
|
from dependency_injector import providers
|
||||||
|
|
||||||
# Test 1: to check the getattr type
|
# Test 1: to check the getattr type
|
||||||
provider1 = providers.DependenciesContainer(
|
provider1 = providers.DependenciesContainer(
|
||||||
a=providers.Provider(),
|
a=providers.Provider(),
|
||||||
b=providers.Provider(),
|
b=providers.Provider(),
|
||||||
)
|
)
|
||||||
a1: providers.Provider = provider1.a
|
a1: providers.Provider[Any] = provider1.a
|
||||||
b1: providers.Provider = provider1.b
|
b1: providers.Provider[Any] = provider1.b
|
||||||
c1: providers.ProvidedInstance = provider1.c.provided
|
c1: providers.ProvidedInstance = provider1.c.provided
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
from typing import Type
|
from typing import Any, Type
|
||||||
|
|
||||||
from dependency_injector import providers
|
from dependency_injector import providers
|
||||||
|
|
||||||
|
|
||||||
class Animal:
|
class Animal: ...
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
class Cat(Animal):
|
class Cat(Animal):
|
||||||
|
|
||||||
def __init__(self, *_, **__): ...
|
def __init__(self, *a: Any, **kw: Any) -> None: ...
|
||||||
|
|
||||||
|
|
||||||
# Test 1: to check the return type
|
# Test 1: to check the return type
|
||||||
|
@ -23,6 +22,8 @@ var2: Type[Animal] = provider2.instance_of
|
||||||
|
|
||||||
# Test 3: to check the return type with await
|
# Test 3: to check the return type with await
|
||||||
provider3 = providers.Dependency(instance_of=Animal)
|
provider3 = providers.Dependency(instance_of=Animal)
|
||||||
|
|
||||||
|
|
||||||
async def _async3() -> None:
|
async def _async3() -> None:
|
||||||
var1: Animal = await provider3() # type: ignore
|
var1: Animal = await provider3() # type: ignore
|
||||||
var2: Animal = await provider3.async_()
|
var2: Animal = await provider3.async_()
|
||||||
|
|
|
@ -2,7 +2,6 @@ from typing import Any, Dict
|
||||||
|
|
||||||
from dependency_injector import providers
|
from dependency_injector import providers
|
||||||
|
|
||||||
|
|
||||||
# Test 1: to check the return type (class)
|
# Test 1: to check the return type (class)
|
||||||
provider1 = providers.Dict(
|
provider1 = providers.Dict(
|
||||||
a1=providers.Factory(object),
|
a1=providers.Factory(object),
|
||||||
|
@ -17,7 +16,9 @@ var2: Dict[Any, Any] = provider2()
|
||||||
|
|
||||||
|
|
||||||
# Test 3: to check init with non-string keys
|
# Test 3: to check init with non-string keys
|
||||||
provider3 = providers.Dict({object(): providers.Factory(object)}, a2=providers.Factory(object))
|
provider3 = providers.Dict(
|
||||||
|
{object(): providers.Factory(object)}, a2=providers.Factory(object)
|
||||||
|
)
|
||||||
var3: Dict[Any, Any] = provider3()
|
var3: Dict[Any, Any] = provider3()
|
||||||
|
|
||||||
|
|
||||||
|
@ -42,6 +43,8 @@ provider6 = providers.Dict(
|
||||||
a1=providers.Factory(object),
|
a1=providers.Factory(object),
|
||||||
a2=providers.Factory(object),
|
a2=providers.Factory(object),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def _async3() -> None:
|
async def _async3() -> None:
|
||||||
var1: Dict[Any, Any] = await provider6() # type: ignore
|
var1: Dict[Any, Any] = await provider6() # type: ignore
|
||||||
var2: Dict[Any, Any] = await provider6.async_()
|
var2: Dict[Any, Any] = await provider6.async_()
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
from typing import Dict
|
from typing import Any, Dict
|
||||||
|
|
||||||
from dependency_injector import containers, providers
|
from dependency_injector import containers, providers
|
||||||
|
|
||||||
|
|
||||||
# Test 1: to check setattr
|
# Test 1: to check setattr
|
||||||
container1 = containers.DynamicContainer()
|
container1 = containers.DynamicContainer()
|
||||||
container1.abc = providers.Provider()
|
container1.abc = providers.Provider()
|
||||||
|
@ -23,7 +22,7 @@ container4.set_providers(a=providers.Provider())
|
||||||
|
|
||||||
# Test 5: to check .dependencies attribute
|
# Test 5: to check .dependencies attribute
|
||||||
container5 = containers.DynamicContainer()
|
container5 = containers.DynamicContainer()
|
||||||
dependencies: Dict[str, providers.Provider] = container5.dependencies
|
dependencies: Dict[str, providers.Provider[Any]] = container5.dependencies
|
||||||
|
|
||||||
# Test 6: to check base class
|
# Test 6: to check base class
|
||||||
container6: containers.Container = containers.DynamicContainer()
|
container6: containers.Container = containers.DynamicContainer()
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
from typing import Callable, Optional, Tuple, Any, Dict, Type
|
from typing import Any, Callable, Dict, Optional, Tuple, Type
|
||||||
|
|
||||||
from dependency_injector import providers
|
from dependency_injector import providers
|
||||||
|
|
||||||
|
|
||||||
class Animal:
|
class Animal: ...
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
class Cat(Animal):
|
class Cat(Animal):
|
||||||
|
|
||||||
def __init__(self, *_, **__): ...
|
def __init__(self, *a: Any, **kw: Any) -> None: ...
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls) -> Animal:
|
def create(cls) -> Animal:
|
||||||
|
@ -63,17 +62,29 @@ factory_a_9: providers.Factory[str] = provider9.a
|
||||||
factory_b_9: providers.Factory[str] = provider9.b
|
factory_b_9: providers.Factory[str] = provider9.b
|
||||||
val9: str = provider9("a")
|
val9: str = provider9("a")
|
||||||
|
|
||||||
provider9_set_non_string_keys: providers.FactoryAggregate[str] = providers.FactoryAggregate()
|
provider9_set_non_string_keys: providers.FactoryAggregate[str] = (
|
||||||
|
providers.FactoryAggregate()
|
||||||
|
)
|
||||||
provider9_set_non_string_keys.set_factories({Cat: providers.Factory(str, "str")})
|
provider9_set_non_string_keys.set_factories({Cat: providers.Factory(str, "str")})
|
||||||
factory_set_non_string_9: providers.Factory[str] = provider9_set_non_string_keys.factories[Cat]
|
factory_set_non_string_9: providers.Factory[str] = (
|
||||||
|
provider9_set_non_string_keys.factories[Cat]
|
||||||
|
)
|
||||||
|
|
||||||
provider9_new_non_string_keys: providers.FactoryAggregate[str] = providers.FactoryAggregate(
|
provider9_new_non_string_keys: providers.FactoryAggregate[str] = (
|
||||||
|
providers.FactoryAggregate(
|
||||||
{Cat: providers.Factory(str, "str")},
|
{Cat: providers.Factory(str, "str")},
|
||||||
)
|
)
|
||||||
factory_new_non_string_9: providers.Factory[str] = provider9_new_non_string_keys.factories[Cat]
|
)
|
||||||
|
factory_new_non_string_9: providers.Factory[str] = (
|
||||||
|
provider9_new_non_string_keys.factories[Cat]
|
||||||
|
)
|
||||||
|
|
||||||
provider9_no_explicit_typing = providers.FactoryAggregate(a=providers.Factory(str, "str"))
|
provider9_no_explicit_typing = providers.FactoryAggregate(
|
||||||
provider9_no_explicit_typing_factory: providers.Factory[str] = provider9_no_explicit_typing.factories["a"]
|
a=providers.Factory(str, "str")
|
||||||
|
)
|
||||||
|
provider9_no_explicit_typing_factory: providers.Factory[str] = (
|
||||||
|
provider9_no_explicit_typing.factories["a"]
|
||||||
|
)
|
||||||
provider9_no_explicit_typing_object: str = provider9_no_explicit_typing("a")
|
provider9_no_explicit_typing_object: str = provider9_no_explicit_typing("a")
|
||||||
|
|
||||||
# Test 10: to check the explicit typing
|
# Test 10: to check the explicit typing
|
||||||
|
@ -82,10 +93,13 @@ animal10: Animal = factory10()
|
||||||
|
|
||||||
# Test 11: to check the return type with await
|
# Test 11: to check the return type with await
|
||||||
provider11 = providers.Factory(Cat)
|
provider11 = providers.Factory(Cat)
|
||||||
|
|
||||||
|
|
||||||
async def _async11() -> None:
|
async def _async11() -> None:
|
||||||
animal1: Animal = await provider11(1, 2, 3, b="1", c=2, e=0.0) # type: ignore
|
animal1: Animal = await provider11(1, 2, 3, b="1", c=2, e=0.0) # type: ignore
|
||||||
animal2: Animal = await provider11.async_(1, 2, 3, b="1", c=2, e=0.0)
|
animal2: Animal = await provider11.async_(1, 2, 3, b="1", c=2, e=0.0)
|
||||||
|
|
||||||
|
|
||||||
# Test 12: to check class type from .provides
|
# Test 12: to check class type from .provides
|
||||||
provider12 = providers.Factory(Cat)
|
provider12 = providers.Factory(Cat)
|
||||||
provided_cls12: Type[Animal] = provider12.cls
|
provided_cls12: Type[Animal] = provider12.cls
|
||||||
|
@ -101,5 +115,5 @@ provided_provides13: Optional[Callable[..., Animal]] = provider13.provides
|
||||||
assert provided_provides13 is not None and provided_provides13() == Cat()
|
assert provided_provides13 is not None and provided_provides13() == Cat()
|
||||||
|
|
||||||
# Test 14: to check string imports
|
# Test 14: to check string imports
|
||||||
provider14: providers.Factory[dict] = providers.Factory("builtins.dict")
|
provider14: providers.Factory[Dict[Any, Any]] = providers.Factory("builtins.dict")
|
||||||
provider14.set_provides("builtins.dict")
|
provider14.set_provides("builtins.dict")
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
from typing import Tuple, Any, List
|
from typing import Any, List, Tuple
|
||||||
|
|
||||||
from dependency_injector import providers
|
from dependency_injector import providers
|
||||||
|
|
||||||
|
|
||||||
# Test 1: to check the return type (class)
|
# Test 1: to check the return type (class)
|
||||||
provider1 = providers.List(
|
provider1 = providers.List(
|
||||||
providers.Factory(object),
|
providers.Factory(object),
|
||||||
|
@ -33,6 +32,8 @@ provider4 = providers.List(
|
||||||
providers.Factory(object),
|
providers.Factory(object),
|
||||||
providers.Factory(object),
|
providers.Factory(object),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def _async4() -> None:
|
async def _async4() -> None:
|
||||||
var1: List[Any] = await provider4() # type: ignore
|
var1: List[Any] = await provider4() # type: ignore
|
||||||
var2: List[Any] = await provider4.async_()
|
var2: List[Any] = await provider4.async_()
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
from typing import Type, Optional
|
from typing import Optional, Type
|
||||||
|
|
||||||
from dependency_injector import providers
|
from dependency_injector import providers
|
||||||
|
|
||||||
|
|
||||||
# Test 1: to check the return type
|
# Test 1: to check the return type
|
||||||
provider1 = providers.Object(int(3))
|
provider1 = providers.Object(int(3))
|
||||||
var1: int = provider1()
|
var1: int = provider1()
|
||||||
|
@ -16,10 +15,13 @@ method_caller2: providers.MethodCaller = provider2.provided.method.call(123, arg
|
||||||
|
|
||||||
# Test 3: to check the return type with await
|
# Test 3: to check the return type with await
|
||||||
provider3 = providers.Object(int(3))
|
provider3 = providers.Object(int(3))
|
||||||
|
|
||||||
|
|
||||||
async def _async3() -> None:
|
async def _async3() -> None:
|
||||||
var1: int = await provider3() # type: ignore
|
var1: int = await provider3() # type: ignore
|
||||||
var2: int = await provider3.async_()
|
var2: int = await provider3.async_()
|
||||||
|
|
||||||
|
|
||||||
# Test 4: to check class type from provider
|
# Test 4: to check class type from provider
|
||||||
provider4 = providers.Object(int("1"))
|
provider4 = providers.Object(int("1"))
|
||||||
provided_provides: Optional[int] = provider4.provides
|
provided_provides: Optional[int] = provider4.provides
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from dependency_injector import providers
|
from typing import Any
|
||||||
|
|
||||||
|
from dependency_injector import providers
|
||||||
|
|
||||||
# Test 1: to check .provided attribute
|
# Test 1: to check .provided attribute
|
||||||
provider1: providers.Provider[int] = providers.Object(1)
|
provider1: providers.Provider[int] = providers.Object(1)
|
||||||
|
@ -7,7 +8,7 @@ provided: int = provider1.provided()
|
||||||
provider1_delegate: providers.Provider[int] = provider1.provider
|
provider1_delegate: providers.Provider[int] = provider1.provider
|
||||||
|
|
||||||
# Test 2: to check async mode API
|
# Test 2: to check async mode API
|
||||||
provider2: providers.Provider = providers.Provider()
|
provider2: providers.Provider[Any] = providers.Provider()
|
||||||
provider2.enable_async_mode()
|
provider2.enable_async_mode()
|
||||||
provider2.disable_async_mode()
|
provider2.disable_async_mode()
|
||||||
provider2.reset_async_mode()
|
provider2.reset_async_mode()
|
||||||
|
|
|
@ -1,4 +1,13 @@
|
||||||
from typing import List, Iterator, Generator, AsyncIterator, AsyncGenerator, Optional
|
from typing import (
|
||||||
|
Any,
|
||||||
|
AsyncGenerator,
|
||||||
|
AsyncIterator,
|
||||||
|
Dict,
|
||||||
|
Generator,
|
||||||
|
Iterator,
|
||||||
|
List,
|
||||||
|
Optional,
|
||||||
|
)
|
||||||
|
|
||||||
from dependency_injector import providers, resources
|
from dependency_injector import providers, resources
|
||||||
|
|
||||||
|
@ -32,11 +41,10 @@ var3: List[int] = provider3()
|
||||||
|
|
||||||
# Test 4: to check the return type with resource subclass
|
# Test 4: to check the return type with resource subclass
|
||||||
class MyResource4(resources.Resource[List[int]]):
|
class MyResource4(resources.Resource[List[int]]):
|
||||||
def init(self, *args, **kwargs) -> List[int]:
|
def init(self, *args: Any, **kwargs: Any) -> List[int]:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def shutdown(self, resource: Optional[List[int]]) -> None:
|
def shutdown(self, resource: Optional[List[int]]) -> None: ...
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
provider4 = providers.Resource(MyResource4)
|
provider4 = providers.Resource(MyResource4)
|
||||||
|
@ -84,11 +92,10 @@ async def _provide7() -> None:
|
||||||
|
|
||||||
# Test 8: to check the return type with async resource subclass
|
# Test 8: to check the return type with async resource subclass
|
||||||
class MyResource8(resources.AsyncResource[List[int]]):
|
class MyResource8(resources.AsyncResource[List[int]]):
|
||||||
async def init(self, *args, **kwargs) -> List[int]:
|
async def init(self, *args: Any, **kwargs: Any) -> List[int]:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
async def shutdown(self, resource: Optional[List[int]]) -> None:
|
async def shutdown(self, resource: Optional[List[int]]) -> None: ...
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
provider8 = providers.Resource(MyResource8)
|
provider8 = providers.Resource(MyResource8)
|
||||||
|
@ -100,5 +107,5 @@ async def _provide8() -> None:
|
||||||
|
|
||||||
|
|
||||||
# Test 9: to check string imports
|
# Test 9: to check string imports
|
||||||
provider9: providers.Resource[dict] = providers.Resource("builtins.dict")
|
provider9: providers.Resource[Dict[Any, Any]] = providers.Resource("builtins.dict")
|
||||||
provider9.set_provides("builtins.dict")
|
provider9.set_provides("builtins.dict")
|
||||||
|
|
|
@ -2,7 +2,6 @@ from typing import Any
|
||||||
|
|
||||||
from dependency_injector import providers
|
from dependency_injector import providers
|
||||||
|
|
||||||
|
|
||||||
# Test 1: to check the return type
|
# Test 1: to check the return type
|
||||||
provider1 = providers.Selector(
|
provider1 = providers.Selector(
|
||||||
lambda: "a",
|
lambda: "a",
|
||||||
|
@ -28,7 +27,7 @@ provider3 = providers.Selector(
|
||||||
a=providers.Factory(object),
|
a=providers.Factory(object),
|
||||||
b=providers.Factory(object),
|
b=providers.Factory(object),
|
||||||
)
|
)
|
||||||
attr3: providers.Provider = provider3.a
|
attr3: providers.Provider[Any] = provider3.a
|
||||||
|
|
||||||
# Test 4: to check the return type with await
|
# Test 4: to check the return type with await
|
||||||
provider4 = providers.Selector(
|
provider4 = providers.Selector(
|
||||||
|
@ -36,6 +35,8 @@ provider4 = providers.Selector(
|
||||||
a=providers.Factory(object),
|
a=providers.Factory(object),
|
||||||
b=providers.Factory(object),
|
b=providers.Factory(object),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def _async4() -> None:
|
async def _async4() -> None:
|
||||||
var1: Any = await provider4() # type: ignore
|
var1: Any = await provider4()
|
||||||
var2: Any = await provider4.async_()
|
var2: Any = await provider4.async_()
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
from typing import Callable, Optional, Tuple, Any, Dict, Type
|
from typing import Any, Callable, Dict, Optional, Tuple, Type
|
||||||
|
|
||||||
from dependency_injector import providers
|
from dependency_injector import providers
|
||||||
|
|
||||||
|
|
||||||
class Animal:
|
class Animal: ...
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
class Cat(Animal):
|
class Cat(Animal):
|
||||||
|
|
||||||
def __init__(self, *_, **__): ...
|
def __init__(self, *a: Any, **kw: Any) -> None: ...
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls) -> Animal:
|
def create(cls) -> Animal:
|
||||||
|
@ -72,10 +71,13 @@ provider12 = providers.SingletonDelegate(providers.Singleton(object))
|
||||||
|
|
||||||
# Test 13: to check the return type with await
|
# Test 13: to check the return type with await
|
||||||
provider13 = providers.Singleton(Cat)
|
provider13 = providers.Singleton(Cat)
|
||||||
|
|
||||||
|
|
||||||
async def _async13() -> None:
|
async def _async13() -> None:
|
||||||
animal1: Animal = await provider13(1, 2, 3, b="1", c=2, e=0.0) # type: ignore
|
animal1: Animal = await provider13(1, 2, 3, b="1", c=2, e=0.0) # type: ignore
|
||||||
animal2: Animal = await provider13.async_(1, 2, 3, b="1", c=2, e=0.0)
|
animal2: Animal = await provider13.async_(1, 2, 3, b="1", c=2, e=0.0)
|
||||||
|
|
||||||
|
|
||||||
# Test 14: to check class from .provides
|
# Test 14: to check class from .provides
|
||||||
provider14 = providers.Singleton(Cat)
|
provider14 = providers.Singleton(Cat)
|
||||||
provided_cls14: Type[Cat] = provider14.cls
|
provided_cls14: Type[Cat] = provider14.cls
|
||||||
|
@ -91,5 +93,5 @@ provided_provides15: Optional[Callable[..., Animal]] = provider15.provides
|
||||||
assert provided_provides15 is not None and provided_provides15() == Cat()
|
assert provided_provides15 is not None and provided_provides15() == Cat()
|
||||||
|
|
||||||
# Test 16: to check string imports
|
# Test 16: to check string imports
|
||||||
provider16: providers.Singleton[dict] = providers.Singleton("builtins.dict")
|
provider16: providers.Singleton[Dict[Any, Any]] = providers.Singleton("builtins.dict")
|
||||||
provider16.set_provides("builtins.dict")
|
provider16.set_provides("builtins.dict")
|
||||||
|
|
36
tests/typing/wiring.py
Normal file
36
tests/typing/wiring.py
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
from typing import Iterator
|
||||||
|
|
||||||
|
from typing_extensions import Annotated
|
||||||
|
|
||||||
|
from dependency_injector.containers import DeclarativeContainer
|
||||||
|
from dependency_injector.providers import Object, Resource
|
||||||
|
from dependency_injector.wiring import Closing, Provide, required
|
||||||
|
|
||||||
|
|
||||||
|
def _resource() -> Iterator[int]:
|
||||||
|
yield 1
|
||||||
|
|
||||||
|
|
||||||
|
class Container(DeclarativeContainer):
|
||||||
|
value = Object(1)
|
||||||
|
res = Resource(_resource)
|
||||||
|
|
||||||
|
|
||||||
|
def default_by_ref(value: int = Provide[Container.value]) -> None: ...
|
||||||
|
def default_by_string(value: int = Provide["value"]) -> None: ...
|
||||||
|
def default_by_string_with_modifier(
|
||||||
|
value: int = Provide["value", required().as_int()]
|
||||||
|
) -> None: ...
|
||||||
|
def default_container(container: Container = Provide[Container]) -> None: ...
|
||||||
|
def default_with_closing(value: int = Closing[Provide[Container.res]]) -> None: ...
|
||||||
|
def annotated_by_ref(value: Annotated[int, Provide[Container.value]]) -> None: ...
|
||||||
|
def annotated_by_string(value: Annotated[int, Provide["value"]]) -> None: ...
|
||||||
|
def annotated_by_string_with_modifier(
|
||||||
|
value: Annotated[int, Provide["value", required().as_int()]],
|
||||||
|
) -> None: ...
|
||||||
|
def annotated_container(
|
||||||
|
container: Annotated[Container, Provide[Container]],
|
||||||
|
) -> None: ...
|
||||||
|
def annotated_with_closing(
|
||||||
|
value: Annotated[int, Closing[Provide[Container.res]]],
|
||||||
|
) -> None: ...
|
|
@ -145,3 +145,121 @@ async def test_shutdown_sync_and_async_ordering():
|
||||||
await container.shutdown_resources()
|
await container.shutdown_resources()
|
||||||
assert initialized_resources == ["r1", "r2", "r3", "r1", "r2", "r3"]
|
assert initialized_resources == ["r1", "r2", "r3", "r1", "r2", "r3"]
|
||||||
assert shutdown_resources == ["r3", "r2", "r1", "r3", "r2", "r1"]
|
assert shutdown_resources == ["r3", "r2", "r1", "r3", "r2", "r1"]
|
||||||
|
|
||||||
|
|
||||||
|
@mark.asyncio
|
||||||
|
async def test_init_and_shutdown_scoped_resources():
|
||||||
|
initialized_resources = []
|
||||||
|
shutdown_resources = []
|
||||||
|
|
||||||
|
def _sync_resource(name, **_):
|
||||||
|
initialized_resources.append(name)
|
||||||
|
yield name
|
||||||
|
shutdown_resources.append(name)
|
||||||
|
|
||||||
|
async def _async_resource(name, **_):
|
||||||
|
initialized_resources.append(name)
|
||||||
|
yield name
|
||||||
|
shutdown_resources.append(name)
|
||||||
|
|
||||||
|
|
||||||
|
class ResourceA(providers.Resource):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ResourceB(providers.Resource):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Container(containers.DeclarativeContainer):
|
||||||
|
resource_a = ResourceA(
|
||||||
|
_sync_resource,
|
||||||
|
name="ra1",
|
||||||
|
)
|
||||||
|
resource_b1 = ResourceB(
|
||||||
|
_sync_resource,
|
||||||
|
name="rb1",
|
||||||
|
r1=resource_a,
|
||||||
|
)
|
||||||
|
resource_b2 = ResourceB(
|
||||||
|
_async_resource,
|
||||||
|
name="rb2",
|
||||||
|
r2=resource_b1,
|
||||||
|
)
|
||||||
|
|
||||||
|
container = Container()
|
||||||
|
|
||||||
|
container.init_resources(resource_type=ResourceA)
|
||||||
|
assert initialized_resources == ["ra1"]
|
||||||
|
assert shutdown_resources == []
|
||||||
|
|
||||||
|
container.shutdown_resources(resource_type=ResourceA)
|
||||||
|
assert initialized_resources == ["ra1"]
|
||||||
|
assert shutdown_resources == ["ra1"]
|
||||||
|
|
||||||
|
await container.init_resources(resource_type=ResourceB)
|
||||||
|
assert initialized_resources == ["ra1", "ra1", "rb1", "rb2"]
|
||||||
|
assert shutdown_resources == ["ra1"]
|
||||||
|
|
||||||
|
await container.shutdown_resources(resource_type=ResourceB)
|
||||||
|
assert initialized_resources == ["ra1", "ra1", "rb1", "rb2"]
|
||||||
|
assert shutdown_resources == ["ra1", "rb2", "rb1"]
|
||||||
|
|
||||||
|
|
||||||
|
@mark.asyncio
|
||||||
|
async def test_init_and_shutdown_all_scoped_resources_using_default_value():
|
||||||
|
initialized_resources = []
|
||||||
|
shutdown_resources = []
|
||||||
|
|
||||||
|
def _sync_resource(name, **_):
|
||||||
|
initialized_resources.append(name)
|
||||||
|
yield name
|
||||||
|
shutdown_resources.append(name)
|
||||||
|
|
||||||
|
async def _async_resource(name, **_):
|
||||||
|
initialized_resources.append(name)
|
||||||
|
yield name
|
||||||
|
shutdown_resources.append(name)
|
||||||
|
|
||||||
|
|
||||||
|
class ResourceA(providers.Resource):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ResourceB(providers.Resource):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Container(containers.DeclarativeContainer):
|
||||||
|
resource_a = ResourceA(
|
||||||
|
_sync_resource,
|
||||||
|
name="r1",
|
||||||
|
)
|
||||||
|
resource_b1 = ResourceB(
|
||||||
|
_sync_resource,
|
||||||
|
name="r2",
|
||||||
|
r1=resource_a,
|
||||||
|
)
|
||||||
|
resource_b2 = ResourceB(
|
||||||
|
_async_resource,
|
||||||
|
name="r3",
|
||||||
|
r2=resource_b1,
|
||||||
|
)
|
||||||
|
|
||||||
|
container = Container()
|
||||||
|
|
||||||
|
await container.init_resources()
|
||||||
|
assert initialized_resources == ["r1", "r2", "r3"]
|
||||||
|
assert shutdown_resources == []
|
||||||
|
|
||||||
|
await container.shutdown_resources()
|
||||||
|
assert initialized_resources == ["r1", "r2", "r3"]
|
||||||
|
assert shutdown_resources == ["r3", "r2", "r1"]
|
||||||
|
|
||||||
|
await container.init_resources()
|
||||||
|
assert initialized_resources == ["r1", "r2", "r3", "r1", "r2", "r3"]
|
||||||
|
assert shutdown_resources == ["r3", "r2", "r1"]
|
||||||
|
|
||||||
|
await container.shutdown_resources()
|
||||||
|
assert initialized_resources == ["r1", "r2", "r3", "r1", "r2", "r3"]
|
||||||
|
assert shutdown_resources == ["r3", "r2", "r1", "r3", "r2", "r1"]
|
||||||
|
|
|
@ -325,6 +325,19 @@ def test_init_shutdown_nested_resources():
|
||||||
assert _init2.shutdown_counter == 2
|
assert _init2.shutdown_counter == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_init_shutdown_resources_wrong_type() -> None:
|
||||||
|
class Container(containers.DeclarativeContainer):
|
||||||
|
pass
|
||||||
|
|
||||||
|
c = Container()
|
||||||
|
|
||||||
|
with raises(TypeError, match=r"resource_type must be a subclass of Resource provider"):
|
||||||
|
c.init_resources(int) # type: ignore[arg-type]
|
||||||
|
|
||||||
|
with raises(TypeError, match=r"resource_type must be a subclass of Resource provider"):
|
||||||
|
c.shutdown_resources(int) # type: ignore[arg-type]
|
||||||
|
|
||||||
|
|
||||||
def test_reset_singletons():
|
def test_reset_singletons():
|
||||||
class SubSubContainer(containers.DeclarativeContainer):
|
class SubSubContainer(containers.DeclarativeContainer):
|
||||||
singleton = providers.Singleton(object)
|
singleton = providers.Singleton(object)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from typing import AsyncIterator, Iterator
|
from typing import AsyncIterator, Iterator, TypeVar
|
||||||
from unittest.mock import ANY
|
from unittest.mock import ANY
|
||||||
|
|
||||||
from pytest import mark
|
from pytest import mark
|
||||||
|
@ -7,6 +7,12 @@ from dependency_injector.containers import DeclarativeContainer
|
||||||
from dependency_injector.ext.starlette import Lifespan
|
from dependency_injector.ext.starlette import Lifespan
|
||||||
from dependency_injector.providers import Resource
|
from dependency_injector.providers import Resource
|
||||||
|
|
||||||
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
|
||||||
|
class XResource(Resource[T]):
|
||||||
|
"""A test provider"""
|
||||||
|
|
||||||
|
|
||||||
class TestLifespan:
|
class TestLifespan:
|
||||||
@mark.parametrize("sync", [False, True])
|
@mark.parametrize("sync", [False, True])
|
||||||
|
@ -28,11 +34,15 @@ class TestLifespan:
|
||||||
yield
|
yield
|
||||||
shutdown = True
|
shutdown = True
|
||||||
|
|
||||||
|
def nope():
|
||||||
|
assert False, "should not be called"
|
||||||
|
|
||||||
class Container(DeclarativeContainer):
|
class Container(DeclarativeContainer):
|
||||||
x = Resource(sync_resource if sync else async_resource)
|
x = XResource(sync_resource if sync else async_resource)
|
||||||
|
y = Resource(nope)
|
||||||
|
|
||||||
container = Container()
|
container = Container()
|
||||||
lifespan = Lifespan(container)
|
lifespan = Lifespan(container, resource_type=XResource)
|
||||||
|
|
||||||
async with lifespan(ANY) as scope:
|
async with lifespan(ANY) as scope:
|
||||||
assert scope is None
|
assert scope is None
|
||||||
|
|
|
@ -2,12 +2,13 @@
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import inspect
|
import inspect
|
||||||
import sys
|
from contextlib import asynccontextmanager
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from dependency_injector import containers, providers, resources
|
|
||||||
from pytest import mark, raises
|
from pytest import mark, raises
|
||||||
|
|
||||||
|
from dependency_injector import containers, providers, resources
|
||||||
|
|
||||||
|
|
||||||
@mark.asyncio
|
@mark.asyncio
|
||||||
async def test_init_async_function():
|
async def test_init_async_function():
|
||||||
|
@ -70,6 +71,46 @@ async def test_init_async_generator():
|
||||||
assert _init.shutdown_counter == 2
|
assert _init.shutdown_counter == 2
|
||||||
|
|
||||||
|
|
||||||
|
@mark.asyncio
|
||||||
|
async def test_init_async_context_manager() -> None:
|
||||||
|
resource = object()
|
||||||
|
|
||||||
|
init_counter = 0
|
||||||
|
shutdown_counter = 0
|
||||||
|
|
||||||
|
@asynccontextmanager
|
||||||
|
async def _init():
|
||||||
|
nonlocal init_counter, shutdown_counter
|
||||||
|
|
||||||
|
await asyncio.sleep(0.001)
|
||||||
|
init_counter += 1
|
||||||
|
|
||||||
|
yield resource
|
||||||
|
|
||||||
|
await asyncio.sleep(0.001)
|
||||||
|
shutdown_counter += 1
|
||||||
|
|
||||||
|
provider = providers.Resource(_init)
|
||||||
|
|
||||||
|
result1 = await provider()
|
||||||
|
assert result1 is resource
|
||||||
|
assert init_counter == 1
|
||||||
|
assert shutdown_counter == 0
|
||||||
|
|
||||||
|
await provider.shutdown()
|
||||||
|
assert init_counter == 1
|
||||||
|
assert shutdown_counter == 1
|
||||||
|
|
||||||
|
result2 = await provider()
|
||||||
|
assert result2 is resource
|
||||||
|
assert init_counter == 2
|
||||||
|
assert shutdown_counter == 1
|
||||||
|
|
||||||
|
await provider.shutdown()
|
||||||
|
assert init_counter == 2
|
||||||
|
assert shutdown_counter == 2
|
||||||
|
|
||||||
|
|
||||||
@mark.asyncio
|
@mark.asyncio
|
||||||
async def test_init_async_class():
|
async def test_init_async_class():
|
||||||
resource = object()
|
resource = object()
|
||||||
|
|
|
@ -2,10 +2,12 @@
|
||||||
|
|
||||||
import decimal
|
import decimal
|
||||||
import sys
|
import sys
|
||||||
|
from contextlib import contextmanager
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from dependency_injector import containers, providers, resources, errors
|
from pytest import mark, raises
|
||||||
from pytest import raises, mark
|
|
||||||
|
from dependency_injector import containers, errors, providers, resources
|
||||||
|
|
||||||
|
|
||||||
def init_fn(*args, **kwargs):
|
def init_fn(*args, **kwargs):
|
||||||
|
@ -123,6 +125,41 @@ def test_init_generator():
|
||||||
assert _init.shutdown_counter == 2
|
assert _init.shutdown_counter == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_init_context_manager() -> None:
|
||||||
|
init_counter, shutdown_counter = 0, 0
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def _init():
|
||||||
|
nonlocal init_counter, shutdown_counter
|
||||||
|
|
||||||
|
init_counter += 1
|
||||||
|
yield
|
||||||
|
shutdown_counter += 1
|
||||||
|
|
||||||
|
init_counter = 0
|
||||||
|
shutdown_counter = 0
|
||||||
|
|
||||||
|
provider = providers.Resource(_init)
|
||||||
|
|
||||||
|
result1 = provider()
|
||||||
|
assert result1 is None
|
||||||
|
assert init_counter == 1
|
||||||
|
assert shutdown_counter == 0
|
||||||
|
|
||||||
|
provider.shutdown()
|
||||||
|
assert init_counter == 1
|
||||||
|
assert shutdown_counter == 1
|
||||||
|
|
||||||
|
result2 = provider()
|
||||||
|
assert result2 is None
|
||||||
|
assert init_counter == 2
|
||||||
|
assert shutdown_counter == 1
|
||||||
|
|
||||||
|
provider.shutdown()
|
||||||
|
assert init_counter == 2
|
||||||
|
assert shutdown_counter == 2
|
||||||
|
|
||||||
|
|
||||||
def test_init_class():
|
def test_init_class():
|
||||||
class TestResource(resources.Resource):
|
class TestResource(resources.Resource):
|
||||||
init_counter = 0
|
init_counter = 0
|
||||||
|
@ -190,7 +227,7 @@ def test_init_class_abc_shutdown_definition_is_not_required():
|
||||||
|
|
||||||
def test_init_not_callable():
|
def test_init_not_callable():
|
||||||
provider = providers.Resource(1)
|
provider = providers.Resource(1)
|
||||||
with raises(errors.Error):
|
with raises(TypeError, match=r"object is not callable"):
|
||||||
provider.init()
|
provider.init()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
|
from typing_extensions import Annotated
|
||||||
|
|
||||||
from dependency_injector import containers, providers
|
from dependency_injector import containers, providers
|
||||||
from dependency_injector.wiring import inject, Provide, Closing
|
from dependency_injector.wiring import Closing, Provide, inject
|
||||||
|
|
||||||
|
|
||||||
class TestResource:
|
class TestResource:
|
||||||
|
@ -42,6 +44,15 @@ async def async_injection(
|
||||||
return resource1, resource2
|
return resource1, resource2
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
|
async def async_generator_injection(
|
||||||
|
resource1: object = Provide[Container.resource1],
|
||||||
|
resource2: object = Closing[Provide[Container.resource2]],
|
||||||
|
):
|
||||||
|
yield resource1
|
||||||
|
yield resource2
|
||||||
|
|
||||||
|
|
||||||
@inject
|
@inject
|
||||||
async def async_injection_with_closing(
|
async def async_injection_with_closing(
|
||||||
resource1: object = Closing[Provide[Container.resource1]],
|
resource1: object = Closing[Provide[Container.resource1]],
|
||||||
|
|
39
tests/unit/samples/wiringfastdepends/sample.py
Normal file
39
tests/unit/samples/wiringfastdepends/sample.py
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from fast_depends import Depends
|
||||||
|
from typing_extensions import Annotated
|
||||||
|
|
||||||
|
from dependency_injector import containers, providers
|
||||||
|
from dependency_injector.wiring import Provide, inject
|
||||||
|
|
||||||
|
|
||||||
|
class CoefficientService:
|
||||||
|
@staticmethod
|
||||||
|
def get_coefficient() -> float:
|
||||||
|
return 1.2
|
||||||
|
|
||||||
|
|
||||||
|
class Container(containers.DeclarativeContainer):
|
||||||
|
service = providers.Factory(CoefficientService)
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
|
def apply_coefficient(
|
||||||
|
a: int,
|
||||||
|
coefficient_provider: CoefficientService = Depends(Provide[Container.service]),
|
||||||
|
) -> float:
|
||||||
|
return a * coefficient_provider.get_coefficient()
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
|
def apply_coefficient_annotated(
|
||||||
|
a: int,
|
||||||
|
coefficient_provider: Annotated[
|
||||||
|
CoefficientService, Depends(Provide[Container.service])
|
||||||
|
],
|
||||||
|
) -> float:
|
||||||
|
return a * coefficient_provider.get_coefficient()
|
||||||
|
|
||||||
|
|
||||||
|
container = Container()
|
||||||
|
container.wire(modules=[sys.modules[__name__]])
|
|
@ -32,6 +32,23 @@ async def test_async_injections():
|
||||||
assert asyncinjections.resource2.shutdown_counter == 0
|
assert asyncinjections.resource2.shutdown_counter == 0
|
||||||
|
|
||||||
|
|
||||||
|
@mark.asyncio
|
||||||
|
async def test_async_generator_injections() -> None:
|
||||||
|
resources = []
|
||||||
|
|
||||||
|
async for resource in asyncinjections.async_generator_injection():
|
||||||
|
resources.append(resource)
|
||||||
|
|
||||||
|
assert len(resources) == 2
|
||||||
|
assert resources[0] is asyncinjections.resource1
|
||||||
|
assert asyncinjections.resource1.init_counter == 1
|
||||||
|
assert asyncinjections.resource1.shutdown_counter == 0
|
||||||
|
|
||||||
|
assert resources[1] is asyncinjections.resource2
|
||||||
|
assert asyncinjections.resource2.init_counter == 1
|
||||||
|
assert asyncinjections.resource2.shutdown_counter == 1
|
||||||
|
|
||||||
|
|
||||||
@mark.asyncio
|
@mark.asyncio
|
||||||
async def test_async_injections_with_closing():
|
async def test_async_injections_with_closing():
|
||||||
resource1, resource2 = await asyncinjections.async_injection_with_closing()
|
resource1, resource2 = await asyncinjections.async_injection_with_closing()
|
||||||
|
|
46
tests/unit/wiring/test_cache.py
Normal file
46
tests/unit/wiring/test_cache.py
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
"""Tests for string module and package names."""
|
||||||
|
|
||||||
|
from typing import Iterator, Optional
|
||||||
|
|
||||||
|
from pytest import fixture, mark
|
||||||
|
from samples.wiring.container import Container
|
||||||
|
|
||||||
|
from dependency_injector.wiring import _fetch_reference_injections
|
||||||
|
|
||||||
|
|
||||||
|
@fixture
|
||||||
|
def container() -> Iterator[Container]:
|
||||||
|
container = Container()
|
||||||
|
yield container
|
||||||
|
container.unwire()
|
||||||
|
|
||||||
|
|
||||||
|
@mark.parametrize(
|
||||||
|
["arg_value", "wc_value", "empty_cache"],
|
||||||
|
[
|
||||||
|
(None, False, True),
|
||||||
|
(False, True, True),
|
||||||
|
(True, False, False),
|
||||||
|
(None, True, False),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_fetch_reference_injections_cache(
|
||||||
|
container: Container,
|
||||||
|
arg_value: Optional[bool],
|
||||||
|
wc_value: bool,
|
||||||
|
empty_cache: bool,
|
||||||
|
) -> None:
|
||||||
|
container.wiring_config.keep_cache = wc_value
|
||||||
|
container.wire(
|
||||||
|
modules=["samples.wiring.module"],
|
||||||
|
packages=["samples.wiring.package"],
|
||||||
|
keep_cache=arg_value,
|
||||||
|
)
|
||||||
|
cache_info = _fetch_reference_injections.cache_info()
|
||||||
|
|
||||||
|
if empty_cache:
|
||||||
|
assert cache_info == (0, 0, None, 0)
|
||||||
|
else:
|
||||||
|
assert cache_info.hits > 0
|
||||||
|
assert cache_info.misses > 0
|
||||||
|
assert cache_info.currsize > 0
|
9
tests/unit/wiring/test_fastdepends.py
Normal file
9
tests/unit/wiring/test_fastdepends.py
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
from wiringfastdepends import sample
|
||||||
|
|
||||||
|
|
||||||
|
def test_apply_coefficient() -> None:
|
||||||
|
assert sample.apply_coefficient(100) == 120.0
|
||||||
|
|
||||||
|
|
||||||
|
def test_apply_coefficient_annotated() -> None:
|
||||||
|
assert sample.apply_coefficient_annotated(100) == 120.0
|
4
tox.ini
4
tox.ini
|
@ -17,6 +17,7 @@ deps=
|
||||||
mypy_boto3_s3
|
mypy_boto3_s3
|
||||||
pydantic-settings
|
pydantic-settings
|
||||||
werkzeug
|
werkzeug
|
||||||
|
fast-depends
|
||||||
extras=
|
extras=
|
||||||
yaml
|
yaml
|
||||||
commands = pytest
|
commands = pytest
|
||||||
|
@ -44,6 +45,7 @@ deps =
|
||||||
boto3
|
boto3
|
||||||
mypy_boto3_s3
|
mypy_boto3_s3
|
||||||
werkzeug
|
werkzeug
|
||||||
|
fast-depends
|
||||||
commands = pytest -m pydantic
|
commands = pytest -m pydantic
|
||||||
|
|
||||||
[testenv:coveralls]
|
[testenv:coveralls]
|
||||||
|
@ -88,4 +90,4 @@ deps=
|
||||||
pydantic-settings
|
pydantic-settings
|
||||||
mypy
|
mypy
|
||||||
commands=
|
commands=
|
||||||
mypy tests/typing
|
mypy --strict tests/typing
|
||||||
|
|
Loading…
Reference in New Issue
Block a user