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