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