Compare commits

..

18 Commits

Author SHA1 Message Date
ZipFile
6e4794bab1
Remove code for EOL Python versions (#864) 2025-03-02 14:33:31 +02:00
ZipFile
f3b3b1baa4 Merge branch 'release/4.46.0' into master 2025-02-25 15:14:15 +00:00
ZipFile
9b66d4bf16 Bump version to v4.46.0 2025-02-23 17:22:13 +00:00
ZipFile
7d4ebecd19
Add option to disable env var interpolation in configs (#861) 2025-02-23 19:01:01 +02:00
ZipFile
09efbffab1
Fix Closing dependency resolution (#852)
Co-authored-by: federinik <federico.tomasi@outlook.com>
Co-authored-by: jazzthief <mynameisyegor@gmail.com>
2025-02-23 18:31:34 +02:00
ZipFile
8b625d81ad
Use Annotated for DI in FastAPI examples (#853) 2025-02-23 18:21:31 +02:00
ZipFile
23acf01c15
Add support for inspect.iscoroutinefunction() in Coroutine provider (#830) 2025-02-23 18:20:38 +02:00
Martin Lafrance
0d6fdb5b78
Fix broken wiring of sync inject-decorated methods (#673)
Co-authored-by: Martin Lafrance <mlafrance@cae.com>
Co-authored-by: ZipFile <zipfile.d@protonmail.com>
2025-02-23 18:17:45 +02:00
Taein Min
2330122de6
Add support for typing.Annotated (#721) 2025-01-20 17:37:28 +02:00
ZipFile
29ae3e1337 Make flake8 config black-compatible 2025-01-18 17:39:07 +00:00
ZipFile
50643e0dfb Run black 2025-01-18 17:02:55 +00:00
ZipFile
3893e1df81 Use native GHA ubuntu-24.04-arm image for building wheels 2025-01-16 19:15:49 +00:00
ZipFile
0fd35baee6 Use ubuntu-24.04 GHA image 2025-01-16 19:13:11 +00:00
Ilya Kazakov
3df95847d5
[movie-lister] Added test fixture and updated documentation (#747)
* test: add fixture for finder mock

* docs: update tests code example, emphasize-lines & test coverage report results
2025-01-12 15:46:29 +02:00
ZipFile
6d9d34c0f6 Add test case for Provider.provider type propagation 2025-01-12 12:18:21 +00:00
Philip Bjorge
de50666a13
fix: type provider (#744) 2025-01-12 14:14:12 +02:00
ZipFile
ccbd5bbb80 Migrate CI pipeline to actions/upload-artifact@v4 2025-01-08 13:07:04 +00:00
Philip Bjorge
00326e9a22
fix: type propogation through provided (#733)
Co-authored-by: Gonzalo Martinez <gonzarmv@gmail.com>
2025-01-08 13:31:00 +02:00
47 changed files with 977 additions and 715 deletions

View File

@ -10,7 +10,7 @@ jobs:
tests: tests:
name: Run tests name: Run tests
runs-on: ubuntu-22.04 runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/setup-python@v4 - uses: actions/setup-python@v4
@ -23,7 +23,7 @@ jobs:
linters: linters:
name: Run linters name: Run linters
runs-on: ubuntu-22.04 runs-on: ubuntu-24.04
strategy: strategy:
matrix: matrix:
toxenv: [flake8, pydocstyle, mypy, pylint] toxenv: [flake8, pydocstyle, mypy, pylint]
@ -40,7 +40,7 @@ jobs:
build-sdist: build-sdist:
name: Build source tarball name: Build source tarball
needs: [tests, linters] needs: [tests, linters]
runs-on: ubuntu-22.04 runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/setup-python@v4 - uses: actions/setup-python@v4
@ -49,8 +49,9 @@ jobs:
- run: | - run: |
python -m pip install --upgrade build python -m pip install --upgrade build
python -m build --sdist python -m build --sdist
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v4
with: with:
name: cibw-sdist
path: ./dist/* path: ./dist/*
build-wheels: build-wheels:
@ -59,45 +60,28 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
matrix: matrix:
os: [ubuntu-22.04, windows-2019, macos-14] os: [ubuntu-24.04, ubuntu-24.04-arm, windows-2019, macos-14]
env: env:
CIBW_SKIP: cp27-* CIBW_SKIP: cp27-*
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Build wheels - name: Build wheels
uses: pypa/cibuildwheel@v2.20.0 uses: pypa/cibuildwheel@v2.20.0
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v4
with:
path: ./wheelhouse/*.whl
build-wheels-linux-aarch64:
name: Build wheels (ubuntu-22.04-aarch64)
needs: [tests, linters]
runs-on: ubuntu-22.04
env:
CIBW_SKIP: cp27-*
steps:
- uses: actions/checkout@v3
- name: Set up QEMU
if: runner.os == 'Linux'
uses: docker/setup-qemu-action@v2
- name: Build wheels
uses: pypa/cibuildwheel@v2.20.0
env:
CIBW_ARCHS_LINUX: aarch64
- uses: actions/upload-artifact@v3
with: with:
name: cibw-wheels-x86-${{ matrix.os }}-${{ strategy.job-index }}
path: ./wheelhouse/*.whl path: ./wheelhouse/*.whl
publish: publish:
name: Publish on PyPI name: Publish on PyPI
needs: [build-sdist, build-wheels, build-wheels-linux-aarch64] needs: [build-sdist, build-wheels]
runs-on: ubuntu-22.04 runs-on: ubuntu-24.04
steps: steps:
- uses: actions/download-artifact@v3 - uses: actions/download-artifact@v4
with: with:
name: artifact pattern: cibw-*
path: dist path: dist
merge-multiple: true
- uses: pypa/gh-action-pypi-publish@release/v1 - uses: pypa/gh-action-pypi-publish@release/v1
with: with:
user: __token__ user: __token__
@ -109,7 +93,7 @@ jobs:
publish-docs: publish-docs:
name: Publish docs name: Publish docs
needs: [publish] needs: [publish]
runs-on: ubuntu-22.04 runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/setup-python@v4 - uses: actions/setup-python@v4

View File

@ -7,6 +7,25 @@ that were made in every particular version.
From version 0.7.6 *Dependency Injector* framework strictly From version 0.7.6 *Dependency Injector* framework strictly
follows `Semantic versioning`_ follows `Semantic versioning`_
4.46.0
------
- Add option to disable env var interpolation in configs (`#861 <https://github.com/ets-labs/python-dependency-injector/pull/861>`_)
- Fix ``Closing`` dependency resolution (`#852 <https://github.com/ets-labs/python-dependency-injector/pull/852>`_)
- Add support for ``inspect.iscoroutinefunction()`` in ``Coroutine`` provider (`#830 <https://github.com/ets-labs/python-dependency-injector/pull/830>`_)
- Fix broken wiring of sync inject-decorated methods (`#673 <https://github.com/ets-labs/python-dependency-injector/pull/673>`_)
- Add support for ``typing.Annotated`` (`#721 <https://github.com/ets-labs/python-dependency-injector/pull/721>`_, `#853 <https://github.com/ets-labs/python-dependency-injector/pull/853>`_)
- Documentation updates for movie-lister example (`#747 <https://github.com/ets-labs/python-dependency-injector/pull/747>`_)
- Fix type propagation in ``Provider.provider`` (`#744 <https://github.com/ets-labs/python-dependency-injector/pull/744>`_)
Many thanks for the contributions to:
- `ZipFile <https://github.com/ZipFile>`_
- `Yegor Statkevich <https://github.com/jazzthief>`_
- `Federico Tomasi <https://github.com/federinik>`_
- `Martin Lafrance <https://github.com/martlaf>`_
- `Philip Bjorge <https://github.com/philipbjorge>`_
- `Ilya Kazakov <https://github.com/mrKazzila>`_
4.45.0 4.45.0
-------- --------
- Add Starlette lifespan handler implementation (`#683 <https://github.com/ets-labs/python-dependency-injector/pull/683>`_). - Add Starlette lifespan handler implementation (`#683 <https://github.com/ets-labs/python-dependency-injector/pull/683>`_).

View File

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

View File

@ -911,7 +911,7 @@ Create ``tests.py`` in the ``movies`` package:
and put next into it: and put next into it:
.. code-block:: python .. code-block:: python
:emphasize-lines: 36,51 :emphasize-lines: 41,50
"""Tests module.""" """Tests module."""
@ -941,13 +941,18 @@ and put next into it:
return container return container
def test_movies_directed_by(container): @pytest.fixture
def finder_mock(container):
finder_mock = mock.Mock() finder_mock = mock.Mock()
finder_mock.find_all.return_value = [ finder_mock.find_all.return_value = [
container.movie("The 33", 2015, "Patricia Riggen"), container.movie("The 33", 2015, "Patricia Riggen"),
container.movie("The Jungle Book", 2016, "Jon Favreau"), container.movie("The Jungle Book", 2016, "Jon Favreau"),
] ]
return finder_mock
def test_movies_directed_by(container, finder_mock):
with container.finder.override(finder_mock): with container.finder.override(finder_mock):
lister = container.lister() lister = container.lister()
movies = lister.movies_directed_by("Jon Favreau") movies = lister.movies_directed_by("Jon Favreau")
@ -956,13 +961,7 @@ and put next into it:
assert movies[0].title == "The Jungle Book" assert movies[0].title == "The Jungle Book"
def test_movies_released_in(container): def test_movies_released_in(container, finder_mock):
finder_mock = mock.Mock()
finder_mock.find_all.return_value = [
container.movie("The 33", 2015, "Patricia Riggen"),
container.movie("The Jungle Book", 2016, "Jon Favreau"),
]
with container.finder.override(finder_mock): with container.finder.override(finder_mock):
lister = container.lister() lister = container.lister()
movies = lister.movies_released_in(2015) movies = lister.movies_released_in(2015)
@ -995,9 +994,9 @@ You should see:
movies/entities.py 7 1 86% movies/entities.py 7 1 86%
movies/finders.py 26 13 50% movies/finders.py 26 13 50%
movies/listers.py 8 0 100% movies/listers.py 8 0 100%
movies/tests.py 23 0 100% movies/tests.py 24 0 100%
------------------------------------------ ------------------------------------------
TOTAL 89 30 66% TOTAL 90 30 67%
.. note:: .. note::

View File

@ -64,7 +64,7 @@ FastAPI example:
@app.api_route("/") @app.api_route("/")
@inject @inject
async def index(service: Service = Depends(Provide[Container.service])): async def index(service: Annotated[Service, Depends(Provide[Container.service])]):
value = await service.process() value = await service.process()
return {"result": value} return {"result": value}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -18,5 +18,6 @@ numpy
scipy scipy
boto3 boto3
mypy_boto3_s3 mypy_boto3_s3
typing_extensions
-r requirements-ext.txt -r requirements-ext.txt

View File

@ -2,6 +2,7 @@
max_line_length = 120 max_line_length = 120
max_complexity = 10 max_complexity = 10
exclude = types.py exclude = types.py
extend-ignore = E203,E701
per-file-ignores = per-file-ignores =
examples/demo/*: F841 examples/demo/*: F841
examples/containers/traverse.py: E501 examples/containers/traverse.py: E501

View File

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

View File

@ -2,44 +2,39 @@
import asyncio import asyncio
import collections.abc import collections.abc
import functools
import inspect import inspect
import types import types
from . import providers from .wiring import _Marker
from .wiring import _Marker, PatchedCallable
from .providers cimport Provider from .providers cimport Provider, Resource
def _get_sync_patched(fn, patched: PatchedCallable): def _sync_inject(object fn, tuple args, dict kwargs, dict injections, dict closings, /):
@functools.wraps(fn) cdef object result
def _patched(*args, **kwargs): cdef dict to_inject
cdef object result cdef object arg_key
cdef dict to_inject cdef Provider provider
cdef object arg_key
cdef Provider provider
to_inject = kwargs.copy() to_inject = kwargs.copy()
for arg_key, provider in patched.injections.items(): for arg_key, provider in injections.items():
if arg_key not in kwargs or isinstance(kwargs[arg_key], _Marker): if arg_key not in kwargs or isinstance(kwargs[arg_key], _Marker):
to_inject[arg_key] = provider() to_inject[arg_key] = provider()
result = fn(*args, **to_inject) result = fn(*args, **to_inject)
if patched.closing: if closings:
for arg_key, provider in patched.closing.items(): for arg_key, provider in closings.items():
if arg_key in kwargs and not isinstance(kwargs[arg_key], _Marker): if arg_key in kwargs and not isinstance(kwargs[arg_key], _Marker):
continue continue
if not isinstance(provider, providers.Resource): if not isinstance(provider, Resource):
continue continue
provider.shutdown() provider.shutdown()
return result return result
return _patched
async def _async_inject(object fn, tuple args, dict kwargs, dict injections, dict closings): async def _async_inject(object fn, tuple args, dict kwargs, dict injections, dict closings, /):
cdef object result cdef object result
cdef dict to_inject cdef dict to_inject
cdef list to_inject_await = [] cdef list to_inject_await = []
@ -69,7 +64,7 @@ async def _async_inject(object fn, tuple args, dict kwargs, dict injections, dic
for arg_key, provider in closings.items(): for arg_key, provider in closings.items():
if arg_key in kwargs and isinstance(kwargs[arg_key], _Marker): if arg_key in kwargs and isinstance(kwargs[arg_key], _Marker):
continue continue
if not isinstance(provider, providers.Resource): if not isinstance(provider, Resource):
continue continue
shutdown = provider.shutdown() shutdown = provider.shutdown()
if _isawaitable(shutdown): if _isawaitable(shutdown):

View File

@ -19,21 +19,24 @@ from typing import (
from .providers import Provider, Self, ProviderParent from .providers import Provider, Self, ProviderParent
C_Base = TypeVar("C_Base", bound="Container") C_Base = TypeVar("C_Base", bound="Container")
C = TypeVar("C", bound="DeclarativeContainer") C = TypeVar("C", bound="DeclarativeContainer")
C_Overriding = TypeVar("C_Overriding", bound="DeclarativeContainer") C_Overriding = TypeVar("C_Overriding", bound="DeclarativeContainer")
T = TypeVar("T") T = TypeVar("T")
TT = TypeVar("TT") TT = TypeVar("TT")
class WiringConfiguration: class WiringConfiguration:
modules: List[Any] modules: List[Any]
packages: List[Any] packages: List[Any]
from_package: Optional[str] from_package: Optional[str]
auto_wire: bool auto_wire: bool
def __init__(self, modules: Optional[Iterable[Any]] = None, packages: Optional[Iterable[Any]] = None, from_package: Optional[str] = None, auto_wire: bool = True) -> None: ... def __init__(
self,
modules: Optional[Iterable[Any]] = None,
packages: Optional[Iterable[Any]] = None,
from_package: Optional[str] = None,
auto_wire: bool = True,
) -> None: ...
class Container: class Container:
provider_type: Type[Provider] = Provider provider_type: Type[Provider] = Provider
@ -51,11 +54,18 @@ class Container:
def set_providers(self, **providers: Provider): ... def set_providers(self, **providers: Provider): ...
def set_provider(self, name: str, provider: Provider) -> None: ... def set_provider(self, name: str, provider: Provider) -> None: ...
def override(self, overriding: Union[Container, Type[Container]]) -> None: ... def override(self, overriding: Union[Container, Type[Container]]) -> None: ...
def override_providers(self, **overriding_providers: Union[Provider, Any]) -> ProvidersOverridingContext[C_Base]: ... def override_providers(
self, **overriding_providers: Union[Provider, Any]
) -> ProvidersOverridingContext[C_Base]: ...
def reset_last_overriding(self) -> None: ... def reset_last_overriding(self) -> None: ...
def reset_override(self) -> None: ... def reset_override(self) -> None: ...
def is_auto_wiring_enabled(self) -> bool: ... def is_auto_wiring_enabled(self) -> bool: ...
def wire(self, modules: Optional[Iterable[Any]] = None, packages: Optional[Iterable[Any]] = None, from_package: Optional[str] = None) -> None: ... def wire(
self,
modules: Optional[Iterable[Any]] = None,
packages: Optional[Iterable[Any]] = None,
from_package: Optional[str] = None,
) -> None: ...
def unwire(self) -> None: ... def unwire(self) -> None: ...
def init_resources(self) -> Optional[Awaitable]: ... def init_resources(self) -> Optional[Awaitable]: ...
def shutdown_resources(self) -> Optional[Awaitable]: ... def shutdown_resources(self) -> Optional[Awaitable]: ...
@ -64,7 +74,9 @@ class Container:
def reset_singletons(self) -> SingletonResetContext[C_Base]: ... def reset_singletons(self) -> SingletonResetContext[C_Base]: ...
def check_dependencies(self) -> None: ... def check_dependencies(self) -> None: ...
def from_schema(self, schema: Dict[Any, Any]) -> None: ... def from_schema(self, schema: Dict[Any, Any]) -> None: ...
def from_yaml_schema(self, filepath: Union[Path, str], loader: Optional[Any]=None) -> None: ... def from_yaml_schema(
self, filepath: Union[Path, str], loader: Optional[Any] = None
) -> None: ...
def from_json_schema(self, filepath: Union[Path, str]) -> None: ... def from_json_schema(self, filepath: Union[Path, str]) -> None: ...
@overload @overload
def resolve_provider_name(self, provider: Provider) -> str: ... def resolve_provider_name(self, provider: Provider) -> str: ...
@ -82,10 +94,8 @@ class Container:
@overload @overload
def traverse(cls, types: Optional[Iterable[Type[TT]]] = None) -> Iterator[TT]: ... def traverse(cls, types: Optional[Iterable[Type[TT]]] = None) -> Iterator[TT]: ...
class DynamicContainer(Container): ... class DynamicContainer(Container): ...
class DeclarativeContainer(Container): class DeclarativeContainer(Container):
cls_providers: ClassVar[Dict[str, Provider]] cls_providers: ClassVar[Dict[str, Provider]]
inherited_providers: ClassVar[Dict[str, Provider]] inherited_providers: ClassVar[Dict[str, Provider]]
@ -93,29 +103,28 @@ class DeclarativeContainer(Container):
@classmethod @classmethod
def override(cls, overriding: Union[Container, Type[Container]]) -> None: ... def override(cls, overriding: Union[Container, Type[Container]]) -> None: ...
@classmethod @classmethod
def override_providers(cls, **overriding_providers: Union[Provider, Any]) -> ProvidersOverridingContext[C_Base]: ... def override_providers(
cls, **overriding_providers: Union[Provider, Any]
) -> ProvidersOverridingContext[C_Base]: ...
@classmethod @classmethod
def reset_last_overriding(cls) -> None: ... def reset_last_overriding(cls) -> None: ...
@classmethod @classmethod
def reset_override(cls) -> None: ... def reset_override(cls) -> None: ...
class ProvidersOverridingContext(Generic[T]): class ProvidersOverridingContext(Generic[T]):
def __init__(self, container: T, overridden_providers: Iterable[Union[Provider, Any]]) -> None: ... def __init__(
self, container: T, overridden_providers: Iterable[Union[Provider, Any]]
) -> None: ...
def __enter__(self) -> T: ... def __enter__(self) -> T: ...
def __exit__(self, *_: Any) -> None: ... def __exit__(self, *_: Any) -> None: ...
class SingletonResetContext(Generic[T]): class SingletonResetContext(Generic[T]):
def __init__(self, container: T): ... def __init__(self, container: T): ...
def __enter__(self) -> T: ... def __enter__(self) -> T: ...
def __exit__(self, *_: Any) -> None: ... def __exit__(self, *_: Any) -> None: ...
def override(
def override(container: Type[C]) -> _Callable[[Type[C_Overriding]], Type[C_Overriding]]: ... container: Type[C],
) -> _Callable[[Type[C_Overriding]], Type[C_Overriding]]: ...
def copy(container: Type[C]) -> _Callable[[Type[C_Overriding]], Type[C_Overriding]]: ... def copy(container: Type[C]) -> _Callable[[Type[C_Overriding]], Type[C_Overriding]]: ...
def is_container(instance: Any) -> bool: ... def is_container(instance: Any) -> bool: ...

View File

@ -1,17 +1,11 @@
"""Containers module.""" """Containers module."""
import asyncio
import contextlib import contextlib
import copy as copy_module import copy as copy_module
import json import json
import sys
import importlib import importlib
import inspect import inspect
import warnings
try:
import asyncio
except ImportError:
asyncio = None
try: try:
import yaml import yaml
@ -20,24 +14,7 @@ except ImportError:
from . import providers, errors from . import providers, errors
from .providers cimport __is_future_or_coroutine from .providers cimport __is_future_or_coroutine
from .wiring import wire, unwire
if sys.version_info[:2] >= (3, 6):
from .wiring import wire, unwire
else:
def wire(*args, **kwargs):
raise NotImplementedError("Wiring requires Python 3.6 or above")
def unwire(*args, **kwargs):
raise NotImplementedError("Wiring requires Python 3.6 or above")
if sys.version_info[:2] == (3, 5):
warnings.warn(
"Dependency Injector will drop support of Python 3.5 after Jan 1st of 2022. "
"This does not mean that there will be any immediate breaking changes, "
"but tests will no longer be executed on Python 3.5, and bugs will not be addressed.",
category=DeprecationWarning,
)
class WiringConfiguration: class WiringConfiguration:
@ -53,7 +30,7 @@ class WiringConfiguration:
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)
class Container(object): class Container:
"""Abstract container.""" """Abstract container."""

View File

@ -38,9 +38,11 @@ class View(providers.Callable):
def as_view(self): def as_view(self):
"""Return aiohttp view function.""" """Return aiohttp view function."""
@functools.wraps(self.provides) @functools.wraps(self.provides)
async def _view(request, *args, **kwargs): async def _view(request, *args, **kwargs):
return await self.__call__(request, *args, **kwargs) return await self.__call__(request, *args, **kwargs)
return _view return _view
@ -49,6 +51,8 @@ class ClassBasedView(providers.Factory):
def as_view(self): def as_view(self):
"""Return aiohttp view function.""" """Return aiohttp view function."""
async def _view(request, *args, **kwargs): async def _view(request, *args, **kwargs):
return await self.__call__(request, *args, **kwargs) return await self.__call__(request, *args, **kwargs)
return _view return _view

View File

@ -2,22 +2,13 @@ from typing import Awaitable as _Awaitable
from dependency_injector import providers from dependency_injector import providers
class Application(providers.Singleton): ... class Application(providers.Singleton): ...
class Extension(providers.Singleton): ... class Extension(providers.Singleton): ...
class Middleware(providers.DelegatedCallable): ... class Middleware(providers.DelegatedCallable): ...
class MiddlewareFactory(providers.Factory): ... class MiddlewareFactory(providers.Factory): ...
class View(providers.Callable): class View(providers.Callable):
def as_view(self) -> _Awaitable: ... def as_view(self) -> _Awaitable: ...
class ClassBasedView(providers.Factory): class ClassBasedView(providers.Factory):
def as_view(self) -> _Awaitable: ... def as_view(self) -> _Awaitable: ...

View File

@ -45,6 +45,7 @@ class ClassBasedView(providers.Factory):
def as_view(provider, name=None): def as_view(provider, name=None):
"""Transform class-based view provider to view function.""" """Transform class-based view provider to view function."""
if isinstance(provider, providers.Factory): if isinstance(provider, providers.Factory):
def view(*args, **kwargs): def view(*args, **kwargs):
self = provider() self = provider()
return self.dispatch_request(*args, **kwargs) return self.dispatch_request(*args, **kwargs)
@ -52,12 +53,13 @@ def as_view(provider, name=None):
assert name, 'Argument "endpoint" is required for class-based views' assert name, 'Argument "endpoint" is required for class-based views'
view.__name__ = name view.__name__ = name
elif isinstance(provider, providers.Callable): elif isinstance(provider, providers.Callable):
def view(*args, **kwargs): def view(*args, **kwargs):
return provider(*args, **kwargs) return provider(*args, **kwargs)
view.__name__ = provider.provides.__name__ view.__name__ = provider.provides.__name__
else: else:
raise errors.Error('Undefined provider type') raise errors.Error("Undefined provider type")
view.__doc__ = provider.provides.__doc__ view.__doc__ = provider.provides.__doc__
view.__module__ = provider.provides.__module__ view.__module__ = provider.provides.__module__
@ -65,14 +67,14 @@ def as_view(provider, name=None):
if isinstance(provider.provides, type): if isinstance(provider.provides, type):
view.view_class = provider.provides view.view_class = provider.provides
if hasattr(provider.provides, 'decorators'): if hasattr(provider.provides, "decorators"):
for decorator in provider.provides.decorators: for decorator in provider.provides.decorators:
view = decorator(view) view = decorator(view)
if hasattr(provider.provides, 'methods'): if hasattr(provider.provides, "methods"):
view.methods = provider.provides.methods view.methods = provider.provides.methods
if hasattr(provider.provides, 'provide_automatic_options'): if hasattr(provider.provides, "provide_automatic_options"):
view.provide_automatic_options = provider.provides.provide_automatic_options view.provide_automatic_options = provider.provides.provide_automatic_options
return view return view

View File

@ -3,22 +3,17 @@ from typing import Union, Optional, Callable as _Callable, Any
from flask import request as flask_request from flask import request as flask_request
from dependency_injector import providers from dependency_injector import providers
request: providers.Object[flask_request] request: providers.Object[flask_request]
class Application(providers.Singleton): ... class Application(providers.Singleton): ...
class Extension(providers.Singleton): ... class Extension(providers.Singleton): ...
class View(providers.Callable): class View(providers.Callable):
def as_view(self) -> _Callable[..., Any]: ... def as_view(self) -> _Callable[..., Any]: ...
class ClassBasedView(providers.Factory): class ClassBasedView(providers.Factory):
def as_view(self, name: str) -> _Callable[..., Any]: ... def as_view(self, name: str) -> _Callable[..., Any]: ...
def as_view(
def as_view(provider: Union[View, ClassBasedView], name: Optional[str] = None) -> _Callable[..., Any]: ... provider: Union[View, ClassBasedView], name: Optional[str] = None
) -> _Callable[..., Any]: ...

View File

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

View File

@ -33,7 +33,6 @@ except ImportError:
from . import resources from . import resources
Injection = Any Injection = Any
ProviderParent = Union["Provider", Any] ProviderParent = Union["Provider", Any]
T = TypeVar("T") T = TypeVar("T")
@ -41,16 +40,13 @@ TT = TypeVar("TT")
P = TypeVar("P", bound="Provider") P = TypeVar("P", bound="Provider")
BS = TypeVar("BS", bound="BaseSingleton") BS = TypeVar("BS", bound="BaseSingleton")
class Provider(Generic[T]): class Provider(Generic[T]):
def __init__(self) -> None: ... def __init__(self) -> None: ...
@overload @overload
def __call__(self, *args: Injection, **kwargs: Injection) -> T: ... def __call__(self, *args: Injection, **kwargs: Injection) -> T: ...
@overload @overload
def __call__(self, *args: Injection, **kwargs: Injection) -> Awaitable[T]: ... def __call__(self, *args: Injection, **kwargs: Injection) -> Awaitable[T]: ...
def async_(self, *args: Injection, **kwargs: Injection) -> Awaitable[T]: ... def async_(self, *args: Injection, **kwargs: Injection) -> Awaitable[T]: ...
def __deepcopy__(self, memo: Optional[_Dict[Any, Any]]) -> Provider: ... def __deepcopy__(self, memo: Optional[_Dict[Any, Any]]) -> Provider: ...
def __str__(self) -> str: ... def __str__(self) -> str: ...
def __repr__(self) -> str: ... def __repr__(self) -> str: ...
@ -67,9 +63,9 @@ class Provider(Generic[T]):
def unregister_overrides(self, provider: Union[Provider, Any]) -> None: ... def unregister_overrides(self, provider: Union[Provider, Any]) -> None: ...
def delegate(self) -> Provider: ... def delegate(self) -> Provider: ...
@property @property
def provider(self) -> Provider: ... def provider(self) -> Provider[T]: ...
@property @property
def provided(self) -> ProvidedInstance: ... def provided(self) -> ProvidedInstance[T]: ...
def enable_async_mode(self) -> None: ... def enable_async_mode(self) -> None: ...
def disable_async_mode(self) -> None: ... def disable_async_mode(self) -> None: ...
def reset_async_mode(self) -> None: ... def reset_async_mode(self) -> None: ...
@ -78,9 +74,12 @@ class Provider(Generic[T]):
def is_async_mode_undefined(self) -> bool: ... def is_async_mode_undefined(self) -> bool: ...
@property @property
def related(self) -> _Iterator[Provider]: ... def related(self) -> _Iterator[Provider]: ...
def traverse(self, types: Optional[_Iterable[Type[TT]]] = None) -> _Iterator[TT]: ... def traverse(
def _copy_overridings(self, copied: Provider, memo: Optional[_Dict[Any, Any]]) -> None: ... self, types: Optional[_Iterable[Type[TT]]] = None
) -> _Iterator[TT]: ...
def _copy_overridings(
self, copied: Provider, memo: Optional[_Dict[Any, Any]]
) -> None: ...
class Object(Provider[T]): class Object(Provider[T]):
def __init__(self, provides: Optional[T] = None) -> None: ... def __init__(self, provides: Optional[T] = None) -> None: ...
@ -88,7 +87,6 @@ class Object(Provider[T]):
def provides(self) -> Optional[T]: ... def provides(self) -> Optional[T]: ...
def set_provides(self, provides: Optional[T]) -> Object: ... def set_provides(self, provides: Optional[T]) -> Object: ...
class Self(Provider[T]): class Self(Provider[T]):
def __init__(self, container: Optional[T] = None) -> None: ... def __init__(self, container: Optional[T] = None) -> None: ...
def set_container(self, container: T) -> None: ... def set_container(self, container: T) -> None: ...
@ -96,41 +94,51 @@ class Self(Provider[T]):
@property @property
def alt_names(self) -> Tuple[Any]: ... def alt_names(self) -> Tuple[Any]: ...
class Delegate(Provider[Provider]): class Delegate(Provider[Provider]):
def __init__(self, provides: Optional[Provider] = None) -> None: ... def __init__(self, provides: Optional[Provider] = None) -> None: ...
@property @property
def provides(self) -> Optional[Provider]: ... def provides(self) -> Optional[Provider]: ...
def set_provides(self, provides: Optional[Provider]) -> Delegate: ... def set_provides(self, provides: Optional[Provider]) -> Delegate: ...
class Aggregate(Provider[T]): class Aggregate(Provider[T]):
def __init__(self, provider_dict: Optional[_Dict[Any, Provider[T]]] = None, **provider_kwargs: Provider[T]): ... def __init__(
self,
provider_dict: Optional[_Dict[Any, Provider[T]]] = None,
**provider_kwargs: Provider[T],
): ...
def __getattr__(self, provider_name: Any) -> Provider[T]: ... def __getattr__(self, provider_name: Any) -> Provider[T]: ...
@overload @overload
def __call__(self, provider_name: Optional[Any] = None, *args: Injection, **kwargs: Injection) -> T: ... def __call__(
self, provider_name: Optional[Any] = None, *args: Injection, **kwargs: Injection
) -> T: ...
@overload @overload
def __call__(self, provider_name: Optional[Any] = None, *args: Injection, **kwargs: Injection) -> Awaitable[T]: ... def __call__(
def async_(self, provider_name: Optional[Any] = None, *args: Injection, **kwargs: Injection) -> Awaitable[T]: ... self, provider_name: Optional[Any] = None, *args: Injection, **kwargs: Injection
) -> Awaitable[T]: ...
def async_(
self, provider_name: Optional[Any] = None, *args: Injection, **kwargs: Injection
) -> Awaitable[T]: ...
@property @property
def providers(self) -> _Dict[Any, Provider[T]]: ... def providers(self) -> _Dict[Any, Provider[T]]: ...
def set_providers(self, provider_dict: Optional[_Dict[Any, Provider[T]]] = None, **provider_kwargs: Provider[T]) -> Aggregate[T]: ... def set_providers(
self,
provider_dict: Optional[_Dict[Any, Provider[T]]] = None,
**provider_kwargs: Provider[T],
) -> Aggregate[T]: ...
class Dependency(Provider[T]): class Dependency(Provider[T]):
def __init__(self, instance_of: Type[T] = object, default: Optional[Union[Provider, Any]] = None) -> None: ... def __init__(
self,
instance_of: Type[T] = object,
default: Optional[Union[Provider, Any]] = None,
) -> None: ...
def __getattr__(self, name: str) -> Any: ... def __getattr__(self, name: str) -> Any: ...
@property @property
def instance_of(self) -> Type[T]: ... def instance_of(self) -> Type[T]: ...
def set_instance_of(self, instance_of: Type[T]) -> Dependency[T]: ... def set_instance_of(self, instance_of: Type[T]) -> Dependency[T]: ...
@property @property
def default(self) -> Provider[T]: ... def default(self) -> Provider[T]: ...
def set_default(self, default: Optional[Union[Provider, Any]]) -> Dependency[T]: ... def set_default(self, default: Optional[Union[Provider, Any]]) -> Dependency[T]: ...
@property @property
def is_defined(self) -> bool: ... def is_defined(self) -> bool: ...
def provided_by(self, provider: Provider) -> OverridingContext[P]: ... def provided_by(self, provider: Provider) -> OverridingContext[P]: ...
@ -140,10 +148,8 @@ class Dependency(Provider[T]):
def parent_name(self) -> Optional[str]: ... def parent_name(self) -> Optional[str]: ...
def assign_parent(self, parent: ProviderParent) -> None: ... def assign_parent(self, parent: ProviderParent) -> None: ...
class ExternalDependency(Dependency[T]): ... class ExternalDependency(Dependency[T]): ...
class DependenciesContainer(Object): class DependenciesContainer(Object):
def __init__(self, **dependencies: Provider) -> None: ... def __init__(self, **dependencies: Provider) -> None: ...
def __getattr__(self, name: str) -> Provider: ... def __getattr__(self, name: str) -> Provider: ...
@ -156,12 +162,18 @@ class DependenciesContainer(Object):
def parent_name(self) -> Optional[str]: ... def parent_name(self) -> Optional[str]: ...
def assign_parent(self, parent: ProviderParent) -> None: ... def assign_parent(self, parent: ProviderParent) -> None: ...
class Callable(Provider[T]): class Callable(Provider[T]):
def __init__(self, provides: Optional[Union[_Callable[..., T], str]] = None, *args: Injection, **kwargs: Injection) -> None: ... def __init__(
self,
provides: Optional[Union[_Callable[..., T], str]] = None,
*args: Injection,
**kwargs: Injection,
) -> None: ...
@property @property
def provides(self) -> Optional[_Callable[..., T]]: ... def provides(self) -> Optional[_Callable[..., T]]: ...
def set_provides(self, provides: Optional[Union[_Callable[..., T], str]]) -> Callable[T]: ... def set_provides(
self, provides: Optional[Union[_Callable[..., T], str]]
) -> Callable[T]: ...
@property @property
def args(self) -> Tuple[Injection]: ... def args(self) -> Tuple[Injection]: ...
def add_args(self, *args: Injection) -> Callable[T]: ... def add_args(self, *args: Injection) -> Callable[T]: ...
@ -173,32 +185,23 @@ class Callable(Provider[T]):
def set_kwargs(self, **kwargs: Injection) -> Callable[T]: ... def set_kwargs(self, **kwargs: Injection) -> Callable[T]: ...
def clear_kwargs(self) -> Callable[T]: ... def clear_kwargs(self) -> Callable[T]: ...
class DelegatedCallable(Callable[T]): ... class DelegatedCallable(Callable[T]): ...
class AbstractCallable(Callable[T]): class AbstractCallable(Callable[T]):
def override(self, provider: Callable) -> OverridingContext[P]: ... def override(self, provider: Callable) -> OverridingContext[P]: ...
class CallableDelegate(Delegate): class CallableDelegate(Delegate):
def __init__(self, callable: Callable) -> None: ... def __init__(self, callable: Callable) -> None: ...
class Coroutine(Callable[T]): ... class Coroutine(Callable[T]): ...
class DelegatedCoroutine(Coroutine[T]): ... class DelegatedCoroutine(Coroutine[T]): ...
class AbstractCoroutine(Coroutine[T]): class AbstractCoroutine(Coroutine[T]):
def override(self, provider: Coroutine) -> OverridingContext[P]: ... def override(self, provider: Coroutine) -> OverridingContext[P]: ...
class CoroutineDelegate(Delegate): class CoroutineDelegate(Delegate):
def __init__(self, coroutine: Coroutine) -> None: ... def __init__(self, coroutine: Coroutine) -> None: ...
class ConfigurationOption(Provider[Any]): class ConfigurationOption(Provider[Any]):
UNDEFINED: object UNDEFINED: object
def __init__(self, name: Tuple[str], root: Configuration) -> None: ... def __init__(self, name: Tuple[str], root: Configuration) -> None: ...
@ -212,89 +215,137 @@ class ConfigurationOption(Provider[Any]):
def get_name_segments(self) -> Tuple[Union[str, Provider]]: ... def get_name_segments(self) -> Tuple[Union[str, Provider]]: ...
def as_int(self) -> TypedConfigurationOption[int]: ... def as_int(self) -> TypedConfigurationOption[int]: ...
def as_float(self) -> TypedConfigurationOption[float]: ... def as_float(self) -> TypedConfigurationOption[float]: ...
def as_(self, callback: _Callable[..., T], *args: Injection, **kwargs: Injection) -> TypedConfigurationOption[T]: ... def as_(
self, callback: _Callable[..., T], *args: Injection, **kwargs: Injection
) -> TypedConfigurationOption[T]: ...
def required(self) -> ConfigurationOption: ... def required(self) -> ConfigurationOption: ...
def is_required(self) -> bool: ... def is_required(self) -> bool: ...
def update(self, value: Any) -> None: ... def update(self, value: Any) -> None: ...
def from_ini(self, filepath: Union[Path, str], required: bool = False, envs_required: bool = False) -> None: ... def from_ini(
def from_yaml(self, filepath: Union[Path, str], required: bool = False, loader: Optional[Any] = None, envs_required: bool = False) -> None: ... self,
def from_json(self, filepath: Union[Path, str], required: bool = False, envs_required: bool = False) -> None: ... filepath: Union[Path, str],
def from_pydantic(self, settings: PydanticSettings, required: bool = False, **kwargs: Any) -> None: ... required: bool = False,
envs_required: Optional[bool] = False,
) -> None: ...
def from_yaml(
self,
filepath: Union[Path, str],
required: bool = False,
loader: Optional[Any] = None,
envs_required: Optional[bool] = False,
) -> None: ...
def from_json(
self,
filepath: Union[Path, str],
required: bool = False,
envs_required: Optional[bool] = False,
) -> None: ...
def from_pydantic(
self, settings: PydanticSettings, required: bool = False, **kwargs: Any
) -> None: ...
def from_dict(self, options: _Dict[str, Any], required: bool = False) -> None: ... def from_dict(self, options: _Dict[str, Any], required: bool = False) -> None: ...
def from_env(self, name: str, default: Optional[Any] = None, required: bool = False, as_: Optional[_Callable[..., Any]] = None) -> None: ... def from_env(
self,
name: str,
default: Optional[Any] = None,
required: bool = False,
as_: Optional[_Callable[..., Any]] = None,
) -> None: ...
def from_value(self, value: Any) -> None: ... def from_value(self, value: Any) -> None: ...
class TypedConfigurationOption(Callable[T]): class TypedConfigurationOption(Callable[T]):
@property @property
def option(self) -> ConfigurationOption: ... def option(self) -> ConfigurationOption: ...
class Configuration(Object[Any]): class Configuration(Object[Any]):
DEFAULT_NAME: str = "config" DEFAULT_NAME: str = "config"
def __init__( def __init__(
self, self,
name: str = DEFAULT_NAME, name: str = DEFAULT_NAME,
default: Optional[Any] = None, default: Optional[Any] = None,
*, *,
strict: bool = False, strict: bool = False,
ini_files: Optional[_Iterable[Union[Path, str]]] = None, ini_files: Optional[_Iterable[Union[Path, str]]] = None,
yaml_files: Optional[_Iterable[Union[Path, str]]] = None, yaml_files: Optional[_Iterable[Union[Path, str]]] = None,
json_files: Optional[_Iterable[Union[Path, str]]] = None, json_files: Optional[_Iterable[Union[Path, str]]] = None,
pydantic_settings: Optional[_Iterable[PydanticSettings]] = None, pydantic_settings: Optional[_Iterable[PydanticSettings]] = None,
) -> None: ... ) -> None: ...
def __enter__(self) -> Configuration : ... def __enter__(self) -> Configuration: ...
def __exit__(self, *exc_info: Any) -> None: ... def __exit__(self, *exc_info: Any) -> None: ...
def __getattr__(self, item: str) -> ConfigurationOption: ... def __getattr__(self, item: str) -> ConfigurationOption: ...
def __getitem__(self, item: Union[str, Provider]) -> ConfigurationOption: ... def __getitem__(self, item: Union[str, Provider]) -> ConfigurationOption: ...
def get_name(self) -> str: ... def get_name(self) -> str: ...
def set_name(self, name: str) -> Configuration: ... def set_name(self, name: str) -> Configuration: ...
def get_default(self) -> _Dict[Any, Any]: ... def get_default(self) -> _Dict[Any, Any]: ...
def set_default(self, default: _Dict[Any, Any]): ... def set_default(self, default: _Dict[Any, Any]): ...
def get_strict(self) -> bool: ... def get_strict(self) -> bool: ...
def set_strict(self, strict: bool) -> Configuration: ... def set_strict(self, strict: bool) -> Configuration: ...
def get_children(self) -> _Dict[str, ConfigurationOption]: ... def get_children(self) -> _Dict[str, ConfigurationOption]: ...
def set_children(self, children: _Dict[str, ConfigurationOption]) -> Configuration: ... def set_children(
self, children: _Dict[str, ConfigurationOption]
) -> Configuration: ...
def get_ini_files(self) -> _List[Union[Path, str]]: ... def get_ini_files(self) -> _List[Union[Path, str]]: ...
def set_ini_files(self, files: _Iterable[Union[Path, str]]) -> Configuration: ... def set_ini_files(self, files: _Iterable[Union[Path, str]]) -> Configuration: ...
def get_yaml_files(self) -> _List[Union[Path, str]]: ... def get_yaml_files(self) -> _List[Union[Path, str]]: ...
def set_yaml_files(self, files: _Iterable[Union[Path, str]]) -> Configuration: ... def set_yaml_files(self, files: _Iterable[Union[Path, str]]) -> Configuration: ...
def get_json_files(self) -> _List[Union[Path, str]]: ... def get_json_files(self) -> _List[Union[Path, str]]: ...
def set_json_files(self, files: _Iterable[Union[Path, str]]) -> Configuration: ... def set_json_files(self, files: _Iterable[Union[Path, str]]) -> Configuration: ...
def get_pydantic_settings(self) -> _List[PydanticSettings]: ... def get_pydantic_settings(self) -> _List[PydanticSettings]: ...
def set_pydantic_settings(self, settings: _Iterable[PydanticSettings]) -> Configuration: ... def set_pydantic_settings(
self, settings: _Iterable[PydanticSettings]
) -> Configuration: ...
def load(self, required: bool = False, envs_required: bool = False) -> None: ... def load(self, required: bool = False, envs_required: bool = False) -> None: ...
def get(self, selector: str) -> Any: ... def get(self, selector: str) -> Any: ...
def set(self, selector: str, value: Any) -> OverridingContext[P]: ... def set(self, selector: str, value: Any) -> OverridingContext[P]: ...
def reset_cache(self) -> None: ... def reset_cache(self) -> None: ...
def update(self, value: Any) -> None: ... def update(self, value: Any) -> None: ...
def from_ini(self, filepath: Union[Path, str], required: bool = False, envs_required: bool = False) -> None: ... def from_ini(
def from_yaml(self, filepath: Union[Path, str], required: bool = False, loader: Optional[Any] = None, envs_required: bool = False) -> None: ... self,
def from_json(self, filepath: Union[Path, str], required: bool = False, envs_required: bool = False) -> None: ... filepath: Union[Path, str],
def from_pydantic(self, settings: PydanticSettings, required: bool = False, **kwargs: Any) -> None: ... required: bool = False,
envs_required: bool = False,
) -> None: ...
def from_yaml(
self,
filepath: Union[Path, str],
required: bool = False,
loader: Optional[Any] = None,
envs_required: bool = False,
) -> None: ...
def from_json(
self,
filepath: Union[Path, str],
required: bool = False,
envs_required: bool = False,
) -> None: ...
def from_pydantic(
self, settings: PydanticSettings, required: bool = False, **kwargs: Any
) -> None: ...
def from_dict(self, options: _Dict[str, Any], required: bool = False) -> None: ... def from_dict(self, options: _Dict[str, Any], required: bool = False) -> None: ...
def from_env(self, name: str, default: Optional[Any] = None, required: bool = False, as_: Optional[_Callable[..., Any]] = None) -> None: ... def from_env(
self,
name: str,
default: Optional[Any] = None,
required: bool = False,
as_: Optional[_Callable[..., Any]] = None,
) -> None: ...
def from_value(self, value: Any) -> None: ... def from_value(self, value: Any) -> None: ...
class Factory(Provider[T]): class Factory(Provider[T]):
provided_type: Optional[Type] provided_type: Optional[Type]
def __init__(self, provides: Optional[Union[_Callable[..., T], str]] = None, *args: Injection, **kwargs: Injection) -> None: ... def __init__(
self,
provides: Optional[Union[_Callable[..., T], str]] = None,
*args: Injection,
**kwargs: Injection,
) -> None: ...
@property @property
def cls(self) -> Type[T]: ... def cls(self) -> Type[T]: ...
@property @property
def provides(self) -> Optional[_Callable[..., T]]: ... def provides(self) -> Optional[_Callable[..., T]]: ...
def set_provides(self, provides: Optional[Union[_Callable[..., T], str]]) -> Factory[T]: ... def set_provides(
self, provides: Optional[Union[_Callable[..., T], str]]
) -> Factory[T]: ...
@property @property
def args(self) -> Tuple[Injection]: ... def args(self) -> Tuple[Injection]: ...
def add_args(self, *args: Injection) -> Factory[T]: ... def add_args(self, *args: Injection) -> Factory[T]: ...
@ -311,33 +362,39 @@ class Factory(Provider[T]):
def set_attributes(self, **kwargs: Injection) -> Factory[T]: ... def set_attributes(self, **kwargs: Injection) -> Factory[T]: ...
def clear_attributes(self) -> Factory[T]: ... def clear_attributes(self) -> Factory[T]: ...
class DelegatedFactory(Factory[T]): ... class DelegatedFactory(Factory[T]): ...
class AbstractFactory(Factory[T]): class AbstractFactory(Factory[T]):
def override(self, provider: Factory) -> OverridingContext[P]: ... def override(self, provider: Factory) -> OverridingContext[P]: ...
class FactoryDelegate(Delegate): class FactoryDelegate(Delegate):
def __init__(self, factory: Factory): ... def __init__(self, factory: Factory): ...
class FactoryAggregate(Aggregate[T]): class FactoryAggregate(Aggregate[T]):
def __getattr__(self, provider_name: Any) -> Factory[T]: ... def __getattr__(self, provider_name: Any) -> Factory[T]: ...
@property @property
def factories(self) -> _Dict[Any, Factory[T]]: ... def factories(self) -> _Dict[Any, Factory[T]]: ...
def set_factories(self, provider_dict: Optional[_Dict[Any, Factory[T]]] = None, **provider_kwargs: Factory[T]) -> FactoryAggregate[T]: ... def set_factories(
self,
provider_dict: Optional[_Dict[Any, Factory[T]]] = None,
**provider_kwargs: Factory[T],
) -> FactoryAggregate[T]: ...
class BaseSingleton(Provider[T]): class BaseSingleton(Provider[T]):
provided_type = Optional[Type] provided_type = Optional[Type]
def __init__(self, provides: Optional[Union[_Callable[..., T], str]] = None, *args: Injection, **kwargs: Injection) -> None: ... def __init__(
self,
provides: Optional[Union[_Callable[..., T], str]] = None,
*args: Injection,
**kwargs: Injection,
) -> None: ...
@property @property
def cls(self) -> Type[T]: ... def cls(self) -> Type[T]: ...
@property @property
def provides(self) -> Optional[_Callable[..., T]]: ... def provides(self) -> Optional[_Callable[..., T]]: ...
def set_provides(self, provides: Optional[Union[_Callable[..., T], str]]) -> BaseSingleton[T]: ... def set_provides(
self, provides: Optional[Union[_Callable[..., T], str]]
) -> BaseSingleton[T]: ...
@property @property
def args(self) -> Tuple[Injection]: ... def args(self) -> Tuple[Injection]: ...
def add_args(self, *args: Injection) -> BaseSingleton[T]: ... def add_args(self, *args: Injection) -> BaseSingleton[T]: ...
@ -356,36 +413,20 @@ class BaseSingleton(Provider[T]):
def reset(self) -> SingletonResetContext[BS]: ... def reset(self) -> SingletonResetContext[BS]: ...
def full_reset(self) -> SingletonFullResetContext[BS]: ... def full_reset(self) -> SingletonFullResetContext[BS]: ...
class Singleton(BaseSingleton[T]): ... class Singleton(BaseSingleton[T]): ...
class DelegatedSingleton(Singleton[T]): ... class DelegatedSingleton(Singleton[T]): ...
class ThreadSafeSingleton(Singleton[T]): ... class ThreadSafeSingleton(Singleton[T]): ...
class DelegatedThreadSafeSingleton(ThreadSafeSingleton[T]): ... class DelegatedThreadSafeSingleton(ThreadSafeSingleton[T]): ...
class ThreadLocalSingleton(BaseSingleton[T]): ... class ThreadLocalSingleton(BaseSingleton[T]): ...
class ContextLocalSingleton(BaseSingleton[T]): ... class ContextLocalSingleton(BaseSingleton[T]): ...
class DelegatedThreadLocalSingleton(ThreadLocalSingleton[T]): ... class DelegatedThreadLocalSingleton(ThreadLocalSingleton[T]): ...
class AbstractSingleton(BaseSingleton[T]): class AbstractSingleton(BaseSingleton[T]):
def override(self, provider: BaseSingleton) -> OverridingContext[P]: ... def override(self, provider: BaseSingleton) -> OverridingContext[P]: ...
class SingletonDelegate(Delegate): class SingletonDelegate(Delegate):
def __init__(self, singleton: BaseSingleton): ... def __init__(self, singleton: BaseSingleton): ...
class List(Provider[_List]): class List(Provider[_List]):
def __init__(self, *args: Injection): ... def __init__(self, *args: Injection): ...
@property @property
@ -394,29 +435,63 @@ class List(Provider[_List]):
def set_args(self, *args: Injection) -> List[T]: ... def set_args(self, *args: Injection) -> List[T]: ...
def clear_args(self) -> List[T]: ... def clear_args(self) -> List[T]: ...
class Dict(Provider[_Dict]): class Dict(Provider[_Dict]):
def __init__(self, dict_: Optional[_Dict[Any, Injection]] = None, **kwargs: Injection): ... def __init__(
self, dict_: Optional[_Dict[Any, Injection]] = None, **kwargs: Injection
): ...
@property @property
def kwargs(self) -> _Dict[Any, Injection]: ... def kwargs(self) -> _Dict[Any, Injection]: ...
def add_kwargs(self, dict_: Optional[_Dict[Any, Injection]] = None, **kwargs: Injection) -> Dict: ... def add_kwargs(
def set_kwargs(self, dict_: Optional[_Dict[Any, Injection]] = None, **kwargs: Injection) -> Dict: ... self, dict_: Optional[_Dict[Any, Injection]] = None, **kwargs: Injection
) -> Dict: ...
def set_kwargs(
self, dict_: Optional[_Dict[Any, Injection]] = None, **kwargs: Injection
) -> Dict: ...
def clear_kwargs(self) -> Dict: ... def clear_kwargs(self) -> Dict: ...
class Resource(Provider[T]): class Resource(Provider[T]):
@overload @overload
def __init__(self, provides: Optional[Type[resources.Resource[T]]] = None, *args: Injection, **kwargs: Injection) -> None: ... def __init__(
self,
provides: Optional[Type[resources.Resource[T]]] = None,
*args: Injection,
**kwargs: Injection,
) -> None: ...
@overload @overload
def __init__(self, provides: Optional[Type[resources.AsyncResource[T]]] = None, *args: Injection, **kwargs: Injection) -> None: ... def __init__(
self,
provides: Optional[Type[resources.AsyncResource[T]]] = None,
*args: Injection,
**kwargs: Injection,
) -> None: ...
@overload @overload
def __init__(self, provides: Optional[_Callable[..., _Iterator[T]]] = None, *args: Injection, **kwargs: Injection) -> None: ... def __init__(
self,
provides: Optional[_Callable[..., _Iterator[T]]] = None,
*args: Injection,
**kwargs: Injection,
) -> None: ...
@overload @overload
def __init__(self, provides: Optional[_Callable[..., _AsyncIterator[T]]] = None, *args: Injection, **kwargs: Injection) -> None: ... def __init__(
self,
provides: Optional[_Callable[..., _AsyncIterator[T]]] = None,
*args: Injection,
**kwargs: Injection,
) -> None: ...
@overload @overload
def __init__(self, provides: Optional[_Callable[..., _Coroutine[Injection, Injection, T]]] = None, *args: Injection, **kwargs: Injection) -> None: ... def __init__(
self,
provides: Optional[_Callable[..., _Coroutine[Injection, Injection, T]]] = None,
*args: Injection,
**kwargs: Injection,
) -> None: ...
@overload @overload
def __init__(self, provides: Optional[Union[_Callable[..., T], str]] = None, *args: Injection, **kwargs: Injection) -> None: ... def __init__(
self,
provides: Optional[Union[_Callable[..., T], str]] = None,
*args: Injection,
**kwargs: Injection,
) -> None: ...
@property @property
def provides(self) -> Optional[_Callable[..., Any]]: ... def provides(self) -> Optional[_Callable[..., Any]]: ...
def set_provides(self, provides: Optional[Any]) -> Resource[T]: ... def set_provides(self, provides: Optional[Any]) -> Resource[T]: ...
@ -435,9 +510,13 @@ class Resource(Provider[T]):
def init(self) -> Optional[Awaitable[T]]: ... def init(self) -> Optional[Awaitable[T]]: ...
def shutdown(self) -> Optional[Awaitable]: ... def shutdown(self) -> Optional[Awaitable]: ...
class Container(Provider[T]): class Container(Provider[T]):
def __init__(self, container_cls: Type[T], container: Optional[T] = None, **overriding_providers: Union[Provider, Any]) -> None: ... def __init__(
self,
container_cls: Type[T],
container: Optional[T] = None,
**overriding_providers: Union[Provider, Any],
) -> None: ...
def __getattr__(self, name: str) -> Provider: ... def __getattr__(self, name: str) -> Provider: ...
@property @property
def container(self) -> T: ... def container(self) -> T: ...
@ -448,50 +527,51 @@ class Container(Provider[T]):
def parent_name(self) -> Optional[str]: ... def parent_name(self) -> Optional[str]: ...
def assign_parent(self, parent: ProviderParent) -> None: ... def assign_parent(self, parent: ProviderParent) -> None: ...
class Selector(Provider[Any]): class Selector(Provider[Any]):
def __init__(self, selector: Optional[_Callable[..., Any]] = None, **providers: Provider): ... def __init__(
self, selector: Optional[_Callable[..., Any]] = None, **providers: Provider
): ...
def __getattr__(self, name: str) -> Provider: ... def __getattr__(self, name: str) -> Provider: ...
@property @property
def selector(self) -> Optional[_Callable[..., Any]]: ... def selector(self) -> Optional[_Callable[..., Any]]: ...
def set_selector(self, selector: Optional[_Callable[..., Any]]) -> Selector: ... def set_selector(self, selector: Optional[_Callable[..., Any]]) -> Selector: ...
@property @property
def providers(self) -> _Dict[str, Provider]: ... def providers(self) -> _Dict[str, Provider]: ...
def set_providers(self, **providers: Provider) -> Selector: ... def set_providers(self, **providers: Provider) -> Selector: ...
class ProvidedInstanceFluentInterface: class ProvidedInstanceFluentInterface:
def __getattr__(self, item: Any) -> AttributeGetter: ... def __getattr__(self, item: Any) -> AttributeGetter: ...
def __getitem__(self, item: Any) -> ItemGetter: ... def __getitem__(self, item: Any) -> ItemGetter: ...
def call(self, *args: Injection, **kwargs: Injection) -> MethodCaller: ... def call(self, *args: Injection, **kwargs: Injection) -> MethodCaller: ...
@property @property
def provides(self) -> Optional[Provider]: ... def provides(self) -> Optional[Provider]: ...
def set_provides(self, provides: Optional[Provider]) -> ProvidedInstanceFluentInterface: ... def set_provides(
self, provides: Optional[Provider]
) -> ProvidedInstanceFluentInterface: ...
class ProvidedInstance(Provider, ProvidedInstanceFluentInterface): class ProvidedInstance(Provider, ProvidedInstanceFluentInterface):
def __init__(self, provides: Optional[Provider] = None) -> None: ... def __init__(self, provides: Optional[Provider] = None) -> None: ...
class AttributeGetter(Provider, ProvidedInstanceFluentInterface): class AttributeGetter(Provider, ProvidedInstanceFluentInterface):
def __init__(self, provides: Optional[Provider] = None, name: Optional[str] = None) -> None: ... def __init__(
self, provides: Optional[Provider] = None, name: Optional[str] = None
) -> None: ...
@property @property
def name(self) -> Optional[str]: ... def name(self) -> Optional[str]: ...
def set_name(self, name: Optional[str]) -> ProvidedInstanceFluentInterface: ... def set_name(self, name: Optional[str]) -> ProvidedInstanceFluentInterface: ...
class ItemGetter(Provider, ProvidedInstanceFluentInterface): class ItemGetter(Provider, ProvidedInstanceFluentInterface):
def __init__(self, provides: Optional[Provider] = None, name: Optional[str] = None) -> None: ... def __init__(
self, provides: Optional[Provider] = None, name: Optional[str] = None
) -> None: ...
@property @property
def name(self) -> Optional[str]: ... def name(self) -> Optional[str]: ...
def set_name(self, name: Optional[str]) -> ProvidedInstanceFluentInterface: ... def set_name(self, name: Optional[str]) -> ProvidedInstanceFluentInterface: ...
class MethodCaller(Provider, ProvidedInstanceFluentInterface): class MethodCaller(Provider, ProvidedInstanceFluentInterface):
def __init__(self, provides: Optional[Provider] = None, *args: Injection, **kwargs: Injection) -> None: ... def __init__(
self, provides: Optional[Provider] = None, *args: Injection, **kwargs: Injection
) -> None: ...
class OverridingContext(Generic[T]): class OverridingContext(Generic[T]):
def __init__(self, overridden: Provider, overriding: Provider): ... def __init__(self, overridden: Provider, overriding: Provider): ...
@ -500,61 +580,39 @@ class OverridingContext(Generic[T]):
pass pass
... ...
class BaseSingletonResetContext(Generic[T]): class BaseSingletonResetContext(Generic[T]):
def __init__(self, provider: T): ... def __init__(self, provider: T): ...
def __enter__(self) -> T: ... def __enter__(self) -> T: ...
def __exit__(self, *_: Any) -> None: ... def __exit__(self, *_: Any) -> None: ...
class SingletonResetContext(BaseSingletonResetContext): ...
class SingletonResetContext(BaseSingletonResetContext): class SingletonFullResetContext(BaseSingletonResetContext): ...
...
class SingletonFullResetContext(BaseSingletonResetContext):
...
CHILD_PROVIDERS: Tuple[Provider] CHILD_PROVIDERS: Tuple[Provider]
def is_provider(instance: Any) -> bool: ... def is_provider(instance: Any) -> bool: ...
def ensure_is_provider(instance: Any) -> Provider: ... def ensure_is_provider(instance: Any) -> Provider: ...
def is_delegated(instance: Any) -> bool: ... def is_delegated(instance: Any) -> bool: ...
def represent_provider(provider: Provider, provides: Any) -> str: ... def represent_provider(provider: Provider, provides: Any) -> str: ...
def deepcopy(instance: Any, memo: Optional[_Dict[Any, Any]] = None) -> Any: ... def deepcopy(instance: Any, memo: Optional[_Dict[Any, Any]] = None) -> Any: ...
def deepcopy_args( def deepcopy_args(
provider: Provider[Any], provider: Provider[Any],
args: Tuple[Any, ...], args: Tuple[Any, ...],
memo: Optional[_Dict[int, Any]] = None, memo: Optional[_Dict[int, Any]] = None,
) -> Tuple[Any, ...]: ... ) -> Tuple[Any, ...]: ...
def deepcopy_kwargs( def deepcopy_kwargs(
provider: Provider[Any], provider: Provider[Any],
kwargs: _Dict[str, Any], kwargs: _Dict[str, Any],
memo: Optional[_Dict[int, Any]] = None, memo: Optional[_Dict[int, Any]] = None,
) -> Dict[str, Any]: ... ) -> Dict[str, Any]: ...
def merge_dicts(dict1: _Dict[Any, Any], dict2: _Dict[Any, Any]) -> _Dict[Any, Any]: ... def merge_dicts(dict1: _Dict[Any, Any], dict2: _Dict[Any, Any]) -> _Dict[Any, Any]: ...
def traverse(
*providers: Provider, types: Optional[_Iterable[Type]] = None
def traverse(*providers: Provider, types: Optional[_Iterable[Type]]=None) -> _Iterator[Provider]: ... ) -> _Iterator[Provider]: ...
if yaml: if yaml:
class YamlLoader(yaml.SafeLoader): ... class YamlLoader(yaml.SafeLoader): ...
else: else:
class YamlLoader: ... class YamlLoader: ...

View File

@ -2,6 +2,9 @@
from __future__ import absolute_import from __future__ import absolute_import
import asyncio
import builtins
import contextvars
import copy import copy
import errno import errno
import functools import functools
@ -12,36 +15,22 @@ import os
import re import re
import sys import sys
import threading import threading
import types
import warnings import warnings
from configparser import ConfigParser as IniConfigParser
try: try:
import contextvars from inspect import _is_coroutine_mark as _is_coroutine_marker
except ImportError: except ImportError:
contextvars = None try:
# Python >=3.12.0,<3.12.5
try: from inspect import _is_coroutine_marker
import builtins except ImportError:
except ImportError:
# Python 2.7
import __builtin__ as builtins
try:
import asyncio
except ImportError:
asyncio = None
_is_coroutine_marker = None
else:
if sys.version_info >= (3, 5, 3):
import asyncio.coroutines
_is_coroutine_marker = asyncio.coroutines._is_coroutine
else:
_is_coroutine_marker = True _is_coroutine_marker = True
try: try:
import ConfigParser as iniconfigparser from asyncio.coroutines import _is_coroutine
except ImportError: except ImportError:
import configparser as iniconfigparser _is_coroutine = True
try: try:
import yaml import yaml
@ -77,29 +66,11 @@ from .errors import (
cimport cython cimport cython
if sys.version_info[0] == 3: # pragma: no cover
CLASS_TYPES = (type,)
else: # pragma: no cover
CLASS_TYPES = (type, types.ClassType)
copy._deepcopy_dispatch[types.MethodType] = \
lambda obj, memo: type(obj)(obj.im_func,
copy.deepcopy(obj.im_self, memo),
obj.im_class)
if sys.version_info[:2] == (3, 5):
warnings.warn(
"Dependency Injector will drop support of Python 3.5 after Jan 1st of 2022. "
"This does not mean that there will be any immediate breaking changes, "
"but tests will no longer be executed on Python 3.5, and bugs will not be addressed.",
category=DeprecationWarning,
)
config_env_marker_pattern = re.compile( config_env_marker_pattern = re.compile(
r"\${(?P<name>[^}^{:]+)(?P<separator>:?)(?P<default>.*?)}", r"\${(?P<name>[^}^{:]+)(?P<separator>:?)(?P<default>.*?)}",
) )
def _resolve_config_env_markers(config_content, envs_required=False): cdef str _resolve_config_env_markers(config_content: str, envs_required: bool):
"""Replace environment variable markers with their values.""" """Replace environment variable markers with their values."""
findings = list(config_env_marker_pattern.finditer(config_content)) findings = list(config_env_marker_pattern.finditer(config_content))
@ -118,28 +89,19 @@ def _resolve_config_env_markers(config_content, envs_required=False):
return config_content return config_content
if sys.version_info[0] == 3: cdef object _parse_ini_file(filepath, envs_required: bool | None):
def _parse_ini_file(filepath, envs_required=False): parser = IniConfigParser()
parser = iniconfigparser.ConfigParser()
with open(filepath) as config_file:
config_string = _resolve_config_env_markers(
config_file.read(),
envs_required=envs_required,
)
parser.read_string(config_string)
return parser
else:
import StringIO
def _parse_ini_file(filepath, envs_required=False): with open(filepath) as config_file:
parser = iniconfigparser.ConfigParser() config_string = config_file.read()
with open(filepath) as config_file:
if envs_required is not None:
config_string = _resolve_config_env_markers( config_string = _resolve_config_env_markers(
config_file.read(), config_string,
envs_required=envs_required, envs_required=envs_required,
) )
parser.readfp(StringIO.StringIO(config_string)) parser.read_string(config_string)
return parser return parser
if yaml: if yaml:
@ -163,7 +125,7 @@ cdef int ASYNC_MODE_ENABLED = 1
cdef int ASYNC_MODE_DISABLED = 2 cdef int ASYNC_MODE_DISABLED = 2
cdef set __iscoroutine_typecache = set() cdef set __iscoroutine_typecache = set()
cdef tuple __COROUTINE_TYPES = asyncio.coroutines._COROUTINE_TYPES if asyncio else tuple() cdef tuple __COROUTINE_TYPES = asyncio.coroutines._COROUTINE_TYPES
cdef dict pydantic_settings_to_dict(settings, dict kwargs): cdef dict pydantic_settings_to_dict(settings, dict kwargs):
if not has_pydantic_settings: if not has_pydantic_settings:
@ -173,7 +135,7 @@ cdef dict pydantic_settings_to_dict(settings, dict kwargs):
f"\"pip install dependency-injector[{pydantic_extra}]\"" f"\"pip install dependency-injector[{pydantic_extra}]\""
) )
if isinstance(settings, CLASS_TYPES) and issubclass(settings, PydanticSettings): if isinstance(settings, type) and issubclass(settings, PydanticSettings):
raise Error( raise Error(
"Got settings class, but expect instance: " "Got settings class, but expect instance: "
"instead \"{0}\" use \"{0}()\"".format(settings.__name__) "instead \"{0}\" use \"{0}()\"".format(settings.__name__)
@ -191,7 +153,7 @@ cdef dict pydantic_settings_to_dict(settings, dict kwargs):
return settings.model_dump(mode="python", **kwargs) return settings.model_dump(mode="python", **kwargs)
cdef class Provider(object): cdef class Provider:
"""Base provider class. """Base provider class.
:py:class:`Provider` is callable (implements ``__call__`` method). Every :py:class:`Provider` is callable (implements ``__call__`` method). Every
@ -913,12 +875,9 @@ cdef class Dependency(Provider):
def set_instance_of(self, instance_of): def set_instance_of(self, instance_of):
"""Set type.""" """Set type."""
if not isinstance(instance_of, CLASS_TYPES): if not isinstance(instance_of, type):
raise TypeError( raise TypeError(
"\"instance_of\" has incorrect type (expected {0}, got {1}))".format( f"\"instance_of\" is not a class (got {instance_of!r}))",
CLASS_TYPES,
instance_of,
),
) )
self._instance_of = instance_of self._instance_of = instance_of
return self return self
@ -1475,12 +1434,11 @@ cdef class Coroutine(Callable):
some_coroutine.add_kwargs(keyword_argument1=3, keyword_argument=4) some_coroutine.add_kwargs(keyword_argument1=3, keyword_argument=4)
""" """
_is_coroutine = _is_coroutine_marker _is_coroutine_marker = _is_coroutine_marker # Python >=3.12
_is_coroutine = _is_coroutine # Python <3.16
def set_provides(self, provides): def set_provides(self, provides):
"""Set provider provides.""" """Set provider provides."""
if not asyncio:
raise Error("Package asyncio is not available")
provides = _resolve_string_import(provides) provides = _resolve_string_import(provides)
if provides and not asyncio.iscoroutinefunction(provides): if provides and not asyncio.iscoroutinefunction(provides):
raise Error(f"Provider {_class_qualname(self)} expected to get coroutine function, " raise Error(f"Provider {_class_qualname(self)} expected to get coroutine function, "
@ -1713,7 +1671,7 @@ cdef class ConfigurationOption(Provider):
try: try:
parser = _parse_ini_file( parser = _parse_ini_file(
filepath, filepath,
envs_required=envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(), envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
) )
except IOError as exception: except IOError as exception:
if required is not False \ if required is not False \
@ -1772,10 +1730,11 @@ cdef class ConfigurationOption(Provider):
raise raise
return return
config_content = _resolve_config_env_markers( if envs_required is not None:
config_content, config_content = _resolve_config_env_markers(
envs_required=envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(), config_content,
) envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
)
config = yaml.load(config_content, loader) config = yaml.load(config_content, loader)
current_config = self.__call__() current_config = self.__call__()
@ -1810,10 +1769,11 @@ cdef class ConfigurationOption(Provider):
raise raise
return return
config_content = _resolve_config_env_markers( if envs_required is not None:
config_content, config_content = _resolve_config_env_markers(
envs_required=envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(), config_content,
) envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
)
config = json.loads(config_content) config = json.loads(config_content)
current_config = self.__call__() current_config = self.__call__()
@ -2266,7 +2226,7 @@ cdef class Configuration(Object):
try: try:
parser = _parse_ini_file( parser = _parse_ini_file(
filepath, filepath,
envs_required=envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(), envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
) )
except IOError as exception: except IOError as exception:
if required is not False \ if required is not False \
@ -2325,10 +2285,11 @@ cdef class Configuration(Object):
raise raise
return return
config_content = _resolve_config_env_markers( if envs_required is not None:
config_content, config_content = _resolve_config_env_markers(
envs_required=envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(), config_content,
) envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
)
config = yaml.load(config_content, loader) config = yaml.load(config_content, loader)
current_config = self.__call__() current_config = self.__call__()
@ -2363,10 +2324,11 @@ cdef class Configuration(Object):
raise raise
return return
config_content = _resolve_config_env_markers( if envs_required is not None:
config_content, config_content = _resolve_config_env_markers(
envs_required=envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(), config_content,
) envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
)
config = json.loads(config_content) config = json.loads(config_content)
current_config = self.__call__() current_config = self.__call__()
@ -3975,18 +3937,14 @@ cdef class Resource(Provider):
@staticmethod @staticmethod
def _is_resource_subclass(instance): def _is_resource_subclass(instance):
if sys.version_info < (3, 5): if not isinstance(instance, type):
return False
if not isinstance(instance, CLASS_TYPES):
return return
from . import resources from . import resources
return issubclass(instance, resources.Resource) return issubclass(instance, resources.Resource)
@staticmethod @staticmethod
def _is_async_resource_subclass(instance): def _is_async_resource_subclass(instance):
if sys.version_info < (3, 5): if not isinstance(instance, type):
return False
if not isinstance(instance, CLASS_TYPES):
return return
from . import resources from . import resources
return issubclass(instance, resources.AsyncResource) return issubclass(instance, resources.AsyncResource)
@ -4644,7 +4602,7 @@ cdef class MethodCaller(Provider):
future_result.set_result(result) future_result.set_result(result)
cdef class Injection(object): cdef class Injection:
"""Abstract injection class.""" """Abstract injection class."""
@ -4771,7 +4729,7 @@ cpdef tuple parse_named_injections(dict kwargs):
return tuple(injections) return tuple(injections)
cdef class OverridingContext(object): cdef class OverridingContext:
"""Provider overriding context. """Provider overriding context.
:py:class:`OverridingContext` is used by :py:meth:`Provider.override` for :py:class:`OverridingContext` is used by :py:meth:`Provider.override` for
@ -4807,7 +4765,7 @@ cdef class OverridingContext(object):
self._overridden.reset_last_overriding() self._overridden.reset_last_overriding()
cdef class BaseSingletonResetContext(object): cdef class BaseSingletonResetContext:
def __init__(self, Provider provider): def __init__(self, Provider provider):
self._singleton = provider self._singleton = provider
@ -4843,7 +4801,7 @@ cpdef bint is_provider(object instance):
:rtype: bool :rtype: bool
""" """
return (not isinstance(instance, CLASS_TYPES) and return (not isinstance(instance, type) and
getattr(instance, "__IS_PROVIDER__", False) is True) getattr(instance, "__IS_PROVIDER__", False) is True)
@ -4871,7 +4829,7 @@ cpdef bint is_delegated(object instance):
:rtype: bool :rtype: bool
""" """
return (not isinstance(instance, CLASS_TYPES) and return (not isinstance(instance, type) and
getattr(instance, "__IS_DELEGATED__", False) is True) getattr(instance, "__IS_DELEGATED__", False) is True)
@ -4902,7 +4860,7 @@ cpdef bint is_container_instance(object instance):
:rtype: bool :rtype: bool
""" """
return (not isinstance(instance, CLASS_TYPES) and return (not isinstance(instance, type) and
getattr(instance, "__IS_CONTAINER__", False) is True) getattr(instance, "__IS_CONTAINER__", False) is True)
@ -4914,7 +4872,7 @@ cpdef bint is_container_class(object instance):
:rtype: bool :rtype: bool
""" """
return (isinstance(instance, CLASS_TYPES) and return (isinstance(instance, type) and
getattr(instance, "__IS_CONTAINER__", False) is True) getattr(instance, "__IS_CONTAINER__", False) is True)

View File

@ -10,18 +10,14 @@ T = TypeVar("T")
class Resource(Generic[T], metaclass=abc.ABCMeta): class Resource(Generic[T], metaclass=abc.ABCMeta):
@abc.abstractmethod @abc.abstractmethod
def init(self, *args, **kwargs) -> Optional[T]: def init(self, *args, **kwargs) -> Optional[T]: ...
...
def shutdown(self, resource: Optional[T]) -> None: def shutdown(self, resource: Optional[T]) -> None: ...
...
class AsyncResource(Generic[T], metaclass=abc.ABCMeta): class AsyncResource(Generic[T], metaclass=abc.ABCMeta):
@abc.abstractmethod @abc.abstractmethod
async def init(self, *args, **kwargs) -> Optional[T]: async def init(self, *args, **kwargs) -> Optional[T]: ...
...
async def shutdown(self, resource: Optional[T]) -> None: async def shutdown(self, resource: Optional[T]) -> None: ...
...

View File

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

View File

@ -1,34 +1,28 @@
"""Wiring module.""" """Wiring module."""
import functools import functools
import inspect
import importlib import importlib
import importlib.machinery import importlib.machinery
import inspect
import pkgutil import pkgutil
import warnings
import sys import sys
from types import ModuleType from types import ModuleType
from typing import ( from typing import (
Optional,
Iterable,
Iterator,
Callable,
Any, Any,
Tuple, Callable,
Dict, Dict,
Generic, Generic,
TypeVar, Iterable,
Type, Iterator,
Union, Optional,
Set, Set,
Tuple,
Type,
TypeVar,
Union,
cast, cast,
) )
if sys.version_info < (3, 7):
from typing import GenericMeta
else:
class GenericMeta(type):
...
# Hotfix, see: https://github.com/ets-labs/python-dependency-injector/issues/362 # Hotfix, see: https://github.com/ets-labs/python-dependency-injector/issues/362
if sys.version_info >= (3, 9): if sys.version_info >= (3, 9):
@ -36,6 +30,21 @@ if sys.version_info >= (3, 9):
else: else:
GenericAlias = None GenericAlias = None
if sys.version_info >= (3, 9):
from typing import Annotated, get_args, get_origin
else:
try:
from typing_extensions import Annotated, get_args, get_origin
except ImportError:
Annotated = object()
# For preventing NameError. Never executes
def get_args(hint):
return ()
def get_origin(tp):
return None
try: try:
import fastapi.params import fastapi.params
@ -57,13 +66,6 @@ except ImportError:
from . import providers from . import providers
if sys.version_info[:2] == (3, 5):
warnings.warn(
"Dependency Injector will drop support of Python 3.5 after Jan 1st of 2022. "
"This does not mean that there will be any immediate breaking changes, "
"but tests will no longer be executed on Python 3.5, and bugs will not be addressed.",
category=DeprecationWarning,
)
__all__ = ( __all__ = (
"wire", "wire",
@ -99,7 +101,9 @@ class PatchedRegistry:
def register_callable(self, patched: "PatchedCallable") -> None: def register_callable(self, patched: "PatchedCallable") -> None:
self._callables[patched.patched] = patched self._callables[patched.patched] = patched
def get_callables_from_module(self, module: ModuleType) -> Iterator[Callable[..., Any]]: def get_callables_from_module(
self, module: ModuleType
) -> Iterator[Callable[..., Any]]:
for patched_callable in self._callables.values(): for patched_callable in self._callables.values():
if not patched_callable.is_in_module(module): if not patched_callable.is_in_module(module):
continue continue
@ -114,7 +118,9 @@ class PatchedRegistry:
def register_attribute(self, patched: "PatchedAttribute") -> None: def register_attribute(self, patched: "PatchedAttribute") -> None:
self._attributes.add(patched) self._attributes.add(patched)
def get_attributes_from_module(self, module: ModuleType) -> Iterator["PatchedAttribute"]: def get_attributes_from_module(
self, module: ModuleType
) -> Iterator["PatchedAttribute"]:
for attribute in self._attributes: for attribute in self._attributes:
if not attribute.is_in_module(module): if not attribute.is_in_module(module):
continue continue
@ -139,11 +145,11 @@ class PatchedCallable:
) )
def __init__( def __init__(
self, self,
patched: Optional[Callable[..., Any]] = None, patched: Optional[Callable[..., Any]] = None,
original: Optional[Callable[..., Any]] = None, original: Optional[Callable[..., Any]] = None,
reference_injections: Optional[Dict[Any, Any]] = None, reference_injections: Optional[Dict[Any, Any]] = None,
reference_closing: Optional[Dict[Any, Any]] = None, reference_closing: Optional[Dict[Any, Any]] = None,
) -> None: ) -> None:
self.patched = patched self.patched = patched
self.original = original self.original = original
@ -214,18 +220,21 @@ class ProvidersMap:
) )
def resolve_provider( def resolve_provider(
self, self,
provider: Union[providers.Provider, str], provider: Union[providers.Provider, str],
modifier: Optional["Modifier"] = None, modifier: Optional["Modifier"] = None,
) -> Optional[providers.Provider]: ) -> Optional[providers.Provider]:
if isinstance(provider, providers.Delegate): if isinstance(provider, providers.Delegate):
return self._resolve_delegate(provider) return self._resolve_delegate(provider)
elif isinstance(provider, ( elif isinstance(
providers.ProvidedInstance, provider,
providers.AttributeGetter, (
providers.ItemGetter, providers.ProvidedInstance,
providers.MethodCaller, providers.AttributeGetter,
)): providers.ItemGetter,
providers.MethodCaller,
),
):
return self._resolve_provided_instance(provider) return self._resolve_provided_instance(provider)
elif isinstance(provider, providers.ConfigurationOption): elif isinstance(provider, providers.ConfigurationOption):
return self._resolve_config_option(provider) return self._resolve_config_option(provider)
@ -237,9 +246,9 @@ class ProvidersMap:
return self._resolve_provider(provider) return self._resolve_provider(provider)
def _resolve_string_id( def _resolve_string_id(
self, self,
id: str, id: str,
modifier: Optional["Modifier"] = None, modifier: Optional["Modifier"] = None,
) -> Optional[providers.Provider]: ) -> Optional[providers.Provider]:
if id == self.CONTAINER_STRING_ID: if id == self.CONTAINER_STRING_ID:
return self._container.__self__ return self._container.__self__
@ -256,16 +265,19 @@ class ProvidersMap:
return provider return provider
def _resolve_provided_instance( def _resolve_provided_instance(
self, self,
original: providers.Provider, original: providers.Provider,
) -> Optional[providers.Provider]: ) -> Optional[providers.Provider]:
modifiers = [] modifiers = []
while isinstance(original, ( while isinstance(
original,
(
providers.ProvidedInstance, providers.ProvidedInstance,
providers.AttributeGetter, providers.AttributeGetter,
providers.ItemGetter, providers.ItemGetter,
providers.MethodCaller, providers.MethodCaller,
)): ),
):
modifiers.insert(0, original) modifiers.insert(0, original)
original = original.provides original = original.provides
@ -289,8 +301,8 @@ class ProvidersMap:
return new return new
def _resolve_delegate( def _resolve_delegate(
self, self,
original: providers.Delegate, original: providers.Delegate,
) -> Optional[providers.Provider]: ) -> Optional[providers.Provider]:
provider = self._resolve_provider(original.provides) provider = self._resolve_provider(original.provides)
if provider: if provider:
@ -298,9 +310,9 @@ class ProvidersMap:
return provider return provider
def _resolve_config_option( def _resolve_config_option(
self, self,
original: providers.ConfigurationOption, original: providers.ConfigurationOption,
as_: Any = None, as_: Any = None,
) -> Optional[providers.Provider]: ) -> Optional[providers.Provider]:
original_root = original.root original_root = original.root
new = self._resolve_provider(original_root) new = self._resolve_provider(original_root)
@ -324,8 +336,8 @@ class ProvidersMap:
return new return new
def _resolve_provider( def _resolve_provider(
self, self,
original: providers.Provider, original: providers.Provider,
) -> Optional[providers.Provider]: ) -> Optional[providers.Provider]:
try: try:
return self._map[original] return self._map[original]
@ -334,9 +346,9 @@ class ProvidersMap:
@classmethod @classmethod
def _create_providers_map( def _create_providers_map(
cls, cls,
current_container: Container, current_container: Container,
original_container: Container, original_container: Container,
) -> Dict[providers.Provider, providers.Provider]: ) -> Dict[providers.Provider, providers.Provider]:
current_providers = current_container.providers current_providers = current_container.providers
current_providers["__self__"] = current_container.__self__ current_providers["__self__"] = current_container.__self__
@ -349,8 +361,9 @@ class ProvidersMap:
original_provider = original_providers[provider_name] original_provider = original_providers[provider_name]
providers_map[original_provider] = current_provider providers_map[original_provider] = current_provider
if isinstance(current_provider, providers.Container) \ if isinstance(current_provider, providers.Container) and isinstance(
and isinstance(original_provider, providers.Container): original_provider, providers.Container
):
subcontainer_map = cls._create_providers_map( subcontainer_map = cls._create_providers_map(
current_container=current_provider.container, current_container=current_provider.container,
original_container=original_provider.container, original_container=original_provider.container,
@ -376,19 +389,21 @@ class InspectFilter:
return werkzeug and isinstance(instance, werkzeug.local.LocalProxy) return werkzeug and isinstance(instance, werkzeug.local.LocalProxy)
def _is_starlette_request_cls(self, instance: object) -> bool: def _is_starlette_request_cls(self, instance: object) -> bool:
return starlette \ return (
and isinstance(instance, type) \ starlette
and _safe_is_subclass(instance, starlette.requests.Request) and isinstance(instance, type)
and _safe_is_subclass(instance, starlette.requests.Request)
)
def _is_builtin(self, instance: object) -> bool: def _is_builtin(self, instance: object) -> bool:
return inspect.isbuiltin(instance) return inspect.isbuiltin(instance)
def wire( # noqa: C901 def wire( # noqa: C901
container: Container, container: Container,
*, *,
modules: Optional[Iterable[ModuleType]] = None, modules: Optional[Iterable[ModuleType]] = None,
packages: Optional[Iterable[ModuleType]] = None, packages: Optional[Iterable[ModuleType]] = None,
) -> None: ) -> None:
"""Wire container providers with provided packages and modules.""" """Wire container providers with provided packages and modules."""
modules = [*modules] if modules else [] modules = [*modules] if modules else []
@ -418,18 +433,22 @@ def wire( # noqa: C901
else: else:
for cls_member_name, cls_member in cls_members: for cls_member_name, cls_member in cls_members:
if _is_marker(cls_member): if _is_marker(cls_member):
_patch_attribute(cls, cls_member_name, cls_member, providers_map) _patch_attribute(
cls, cls_member_name, cls_member, providers_map
)
elif _is_method(cls_member): elif _is_method(cls_member):
_patch_method(cls, cls_member_name, cls_member, providers_map) _patch_method(
cls, cls_member_name, cls_member, providers_map
)
for patched in _patched_registry.get_callables_from_module(module): for patched in _patched_registry.get_callables_from_module(module):
_bind_injections(patched, providers_map) _bind_injections(patched, providers_map)
def unwire( # noqa: C901 def unwire( # noqa: C901
*, *,
modules: Optional[Iterable[ModuleType]] = None, modules: Optional[Iterable[ModuleType]] = None,
packages: Optional[Iterable[ModuleType]] = None, packages: Optional[Iterable[ModuleType]] = None,
) -> None: ) -> None:
"""Wire provided packages and modules with previous wired providers.""" """Wire provided packages and modules with previous wired providers."""
modules = [*modules] if modules else [] modules = [*modules] if modules else []
@ -443,7 +462,9 @@ def unwire( # noqa: C901
if inspect.isfunction(member): if inspect.isfunction(member):
_unpatch(module, name, member) _unpatch(module, name, member)
elif inspect.isclass(member): elif inspect.isclass(member):
for method_name, method in inspect.getmembers(member, inspect.isfunction): for method_name, method in inspect.getmembers(
member, inspect.isfunction
):
_unpatch(member, method_name, method) _unpatch(member, method_name, method)
for patched in _patched_registry.get_callables_from_module(module): for patched in _patched_registry.get_callables_from_module(module):
@ -462,10 +483,10 @@ def inject(fn: F) -> F:
def _patch_fn( def _patch_fn(
module: ModuleType, module: ModuleType,
name: str, name: str,
fn: Callable[..., Any], fn: Callable[..., Any],
providers_map: ProvidersMap, providers_map: ProvidersMap,
) -> None: ) -> None:
if not _is_patched(fn): if not _is_patched(fn):
reference_injections, reference_closing = _fetch_reference_injections(fn) reference_injections, reference_closing = _fetch_reference_injections(fn)
@ -479,14 +500,16 @@ def _patch_fn(
def _patch_method( def _patch_method(
cls: Type, cls: Type,
name: str, name: str,
method: Callable[..., Any], method: Callable[..., Any],
providers_map: ProvidersMap, providers_map: ProvidersMap,
) -> None: ) -> None:
if hasattr(cls, "__dict__") \ if (
and name in cls.__dict__ \ hasattr(cls, "__dict__")
and isinstance(cls.__dict__[name], (classmethod, staticmethod)): and name in cls.__dict__
and isinstance(cls.__dict__[name], (classmethod, staticmethod))
):
method = cls.__dict__[name] method = cls.__dict__[name]
fn = method.__func__ fn = method.__func__
else: else:
@ -507,13 +530,15 @@ def _patch_method(
def _unpatch( def _unpatch(
module: ModuleType, module: ModuleType,
name: str, name: str,
fn: Callable[..., Any], fn: Callable[..., Any],
) -> None: ) -> None:
if hasattr(module, "__dict__") \ if (
and name in module.__dict__ \ hasattr(module, "__dict__")
and isinstance(module.__dict__[name], (classmethod, staticmethod)): and name in module.__dict__
and isinstance(module.__dict__[name], (classmethod, staticmethod))
):
method = module.__dict__[name] method = module.__dict__[name]
fn = method.__func__ fn = method.__func__
@ -524,10 +549,10 @@ def _unpatch(
def _patch_attribute( def _patch_attribute(
member: Any, member: Any,
name: str, name: str,
marker: "_Marker", marker: "_Marker",
providers_map: ProvidersMap, providers_map: ProvidersMap,
) -> None: ) -> None:
provider = providers_map.resolve_provider(marker.provider, marker.modifier) provider = providers_map.resolve_provider(marker.provider, marker.modifier)
if provider is None: if provider is None:
@ -548,16 +573,33 @@ def _unpatch_attribute(patched: PatchedAttribute) -> None:
setattr(patched.member, patched.name, patched.marker) setattr(patched.member, patched.name, patched.marker)
def _extract_marker(parameter: inspect.Parameter) -> Optional["_Marker"]:
if get_origin(parameter.annotation) is Annotated:
marker = get_args(parameter.annotation)[1]
else:
marker = parameter.default
if not isinstance(marker, _Marker) and not _is_fastapi_depends(marker):
return None
if _is_fastapi_depends(marker):
marker = marker.dependency
if not isinstance(marker, _Marker):
return None
return marker
def _fetch_reference_injections( # noqa: C901 def _fetch_reference_injections( # noqa: C901
fn: Callable[..., Any], fn: Callable[..., Any],
) -> Tuple[Dict[str, Any], Dict[str, Any]]: ) -> Tuple[Dict[str, Any], Dict[str, Any]]:
# Hotfix, see: # Hotfix, see:
# - https://github.com/ets-labs/python-dependency-injector/issues/362 # - https://github.com/ets-labs/python-dependency-injector/issues/362
# - https://github.com/ets-labs/python-dependency-injector/issues/398 # - https://github.com/ets-labs/python-dependency-injector/issues/398
if GenericAlias and any(( if GenericAlias and any(
fn is GenericAlias, (fn is GenericAlias, getattr(fn, "__func__", None) is GenericAlias)
getattr(fn, "__func__", None) is GenericAlias ):
)):
fn = fn.__init__ fn = fn.__init__
try: try:
@ -573,18 +615,11 @@ def _fetch_reference_injections( # noqa: C901
injections = {} injections = {}
closing = {} closing = {}
for parameter_name, parameter in signature.parameters.items(): for parameter_name, parameter in signature.parameters.items():
if not isinstance(parameter.default, _Marker) \ marker = _extract_marker(parameter)
and not _is_fastapi_depends(parameter.default):
if marker is None:
continue continue
marker = parameter.default
if _is_fastapi_depends(marker):
marker = marker.dependency
if not isinstance(marker, _Marker):
continue
if isinstance(marker, Closing): if isinstance(marker, Closing):
marker = marker.provider marker = marker.provider
closing[parameter_name] = marker closing[parameter_name] = marker
@ -593,20 +628,19 @@ def _fetch_reference_injections( # noqa: C901
return injections, closing return injections, closing
def _locate_dependent_closing_args(provider: providers.Provider) -> Dict[str, providers.Provider]: def _locate_dependent_closing_args(
if not hasattr(provider, "args"): provider: providers.Provider, closing_deps: Dict[str, providers.Provider]
return {} ) -> Dict[str, providers.Provider]:
for arg in [
closing_deps = {} *getattr(provider, "args", []),
for arg in provider.args: *getattr(provider, "kwargs", {}).values(),
if not isinstance(arg, providers.Provider) or not hasattr(arg, "args"): ]:
if not isinstance(arg, providers.Provider):
continue continue
if isinstance(arg, providers.Resource):
closing_deps[str(id(arg))] = arg
if not arg.args and isinstance(arg, providers.Resource): _locate_dependent_closing_args(arg, closing_deps)
return {str(id(arg)): arg}
else:
closing_deps += _locate_dependent_closing_args(arg)
return closing_deps
def _bind_injections(fn: Callable[..., Any], providers_map: ProvidersMap) -> None: def _bind_injections(fn: Callable[..., Any], providers_map: ProvidersMap) -> None:
@ -630,7 +664,8 @@ def _bind_injections(fn: Callable[..., Any], providers_map: ProvidersMap) -> Non
if injection in patched_callable.reference_closing: if injection in patched_callable.reference_closing:
patched_callable.add_closing(injection, provider) patched_callable.add_closing(injection, provider)
deps = _locate_dependent_closing_args(provider) deps = {}
_locate_dependent_closing_args(provider, deps)
for key, dep in deps.items(): for key, dep in deps.items():
patched_callable.add_closing(key, dep) patched_callable.add_closing(key, dep)
@ -647,8 +682,8 @@ def _fetch_modules(package):
if not hasattr(package, "__path__") or not hasattr(package, "__name__"): if not hasattr(package, "__path__") or not hasattr(package, "__name__"):
return modules return modules
for module_info in pkgutil.walk_packages( for module_info in pkgutil.walk_packages(
path=package.__path__, path=package.__path__,
prefix=package.__name__ + ".", prefix=package.__name__ + ".",
): ):
module = importlib.import_module(module_info.name) module = importlib.import_module(module_info.name)
modules.append(module) modules.append(module)
@ -664,9 +699,9 @@ def _is_marker(member) -> bool:
def _get_patched( def _get_patched(
fn: F, fn: F,
reference_injections: Dict[Any, Any], reference_injections: Dict[Any, Any],
reference_closing: Dict[Any, Any], reference_closing: Dict[Any, Any],
) -> F: ) -> F:
patched_object = PatchedCallable( patched_object = PatchedCallable(
original=fn, original=fn,
@ -694,9 +729,11 @@ def _is_patched(fn) -> bool:
def _is_declarative_container(instance: Any) -> bool: def _is_declarative_container(instance: Any) -> bool:
return (isinstance(instance, type) return (
and getattr(instance, "__IS_CONTAINER__", False) is True isinstance(instance, type)
and getattr(instance, "declarative_parent", None) is None) and getattr(instance, "__IS_CONTAINER__", False) is True
and getattr(instance, "declarative_parent", None) is None
)
def _safe_is_subclass(instance: Any, cls: Type) -> bool: def _safe_is_subclass(instance: Any, cls: Type) -> bool:
@ -709,11 +746,10 @@ def _safe_is_subclass(instance: Any, cls: Type) -> bool:
class Modifier: class Modifier:
def modify( def modify(
self, self,
provider: providers.ConfigurationOption, provider: providers.ConfigurationOption,
providers_map: ProvidersMap, providers_map: ProvidersMap,
) -> providers.Provider: ) -> providers.Provider: ...
...
class TypeModifier(Modifier): class TypeModifier(Modifier):
@ -722,9 +758,9 @@ class TypeModifier(Modifier):
self.type_ = type_ self.type_ = type_
def modify( def modify(
self, self,
provider: providers.ConfigurationOption, provider: providers.ConfigurationOption,
providers_map: ProvidersMap, providers_map: ProvidersMap,
) -> providers.Provider: ) -> providers.Provider:
return provider.as_(self.type_) return provider.as_(self.type_)
@ -762,9 +798,9 @@ class RequiredModifier(Modifier):
return self return self
def modify( def modify(
self, self,
provider: providers.ConfigurationOption, provider: providers.ConfigurationOption,
providers_map: ProvidersMap, providers_map: ProvidersMap,
) -> providers.Provider: ) -> providers.Provider:
provider = provider.required() provider = provider.required()
if self.type_modifier: if self.type_modifier:
@ -783,9 +819,9 @@ class InvariantModifier(Modifier):
self.id = id self.id = id
def modify( def modify(
self, self,
provider: providers.ConfigurationOption, provider: providers.ConfigurationOption,
providers_map: ProvidersMap, providers_map: ProvidersMap,
) -> providers.Provider: ) -> providers.Provider:
invariant_segment = providers_map.resolve_provider(self.id) invariant_segment = providers_map.resolve_provider(self.id)
return provider[invariant_segment] return provider[invariant_segment]
@ -818,9 +854,9 @@ class ProvidedInstance(Modifier):
return self return self
def modify( def modify(
self, self,
provider: providers.Provider, provider: providers.Provider,
providers_map: ProvidersMap, providers_map: ProvidersMap,
) -> providers.Provider: ) -> providers.Provider:
provider = provider.provided provider = provider.provided
for type_, value in self.segments: for type_, value in self.segments:
@ -838,22 +874,14 @@ def provided() -> ProvidedInstance:
return ProvidedInstance() return ProvidedInstance()
class ClassGetItemMeta(GenericMeta): class _Marker(Generic[T]):
def __getitem__(cls, item):
# Spike for Python 3.6
if isinstance(item, tuple):
return cls(*item)
return cls(item)
class _Marker(Generic[T], metaclass=ClassGetItemMeta):
__IS_MARKER__ = True __IS_MARKER__ = True
def __init__( def __init__(
self, self,
provider: Union[providers.Provider, Container, str], provider: Union[providers.Provider, Container, str],
modifier: Optional[Modifier] = None, modifier: Optional[Modifier] = None,
) -> None: ) -> None:
if _is_declarative_container(provider): if _is_declarative_container(provider):
provider = provider.__self__ provider = provider.__self__
@ -869,16 +897,13 @@ class _Marker(Generic[T], metaclass=ClassGetItemMeta):
return self return self
class Provide(_Marker): class Provide(_Marker): ...
...
class Provider(_Marker): class Provider(_Marker): ...
...
class Closing(_Marker): class Closing(_Marker): ...
...
class AutoLoader: class AutoLoader:
@ -928,8 +953,7 @@ class AutoLoader:
super().exec_module(module) super().exec_module(module)
loader.wire_module(module) loader.wire_module(module)
class ExtensionFileLoader(importlib.machinery.ExtensionFileLoader): class ExtensionFileLoader(importlib.machinery.ExtensionFileLoader): ...
...
loader_details = [ loader_details = [
(SourcelessFileLoader, importlib.machinery.BYTECODE_SUFFIXES), (SourcelessFileLoader, importlib.machinery.BYTECODE_SUFFIXES),
@ -982,7 +1006,7 @@ _inspect_filter = InspectFilter()
_loader = AutoLoader() _loader = AutoLoader()
# Optimizations # Optimizations
from ._cwiring import _get_sync_patched # noqa from ._cwiring import _sync_inject # noqa
from ._cwiring import _async_inject # noqa from ._cwiring import _async_inject # noqa
@ -998,4 +1022,18 @@ def _get_async_patched(fn: F, patched: PatchedCallable) -> F:
patched.injections, patched.injections,
patched.closing, patched.closing,
) )
return _patched
return cast(F, _patched)
def _get_sync_patched(fn: F, patched: PatchedCallable) -> F:
@functools.wraps(fn)
def _patched(*args, **kwargs):
return _sync_inject(
fn,
args,
kwargs,
patched.injections,
patched.closing,
)
return cast(F, _patched)

View File

@ -1,10 +0,0 @@
[pytest]
testpaths = tests/unit/
python_files = test_*_py2_py3.py
asyncio_mode = auto
filterwarnings =
ignore:Module \"dependency_injector.ext.aiohttp\" is deprecated since version 4\.0\.0:DeprecationWarning
ignore:Module \"dependency_injector.ext.flask\" is deprecated since version 4\.0\.0:DeprecationWarning
ignore:Please use \`.*?\` from the \`scipy.*?\`(.*?)namespace is deprecated\.:DeprecationWarning
ignore:The \`scipy(.*?)\` namespace is deprecated(.*):DeprecationWarning
ignore:ssl\.PROTOCOL_TLS is deprecated:DeprecationWarning:botocore.*

View File

@ -1,10 +0,0 @@
[pytest]
testpaths = tests/unit/
python_files = test_*_py3.py
asyncio_mode = auto
filterwarnings =
ignore:Module \"dependency_injector.ext.aiohttp\" is deprecated since version 4\.0\.0:DeprecationWarning
ignore:Module \"dependency_injector.ext.flask\" is deprecated since version 4\.0\.0:DeprecationWarning
ignore:Please use \`.*?\` from the \`scipy.*?\`(.*?)namespace is deprecated\.:DeprecationWarning
ignore:The \`scipy(.*?)\` namespace is deprecated(.*):DeprecationWarning
ignore:ssl\.PROTOCOL_TLS is deprecated:DeprecationWarning:botocore.*

View File

@ -34,7 +34,7 @@ kwargs4: Dict[str, Any] = provider4.kwargs
# Test 5: to check the provided instance interface # Test 5: to check the provided instance interface
provider5 = providers.Callable(Animal) provider5 = providers.Callable(Animal)
provided5: providers.ProvidedInstance = provider5.provided provided5: Animal = provider5.provided()
attr_getter5: providers.AttributeGetter = provider5.provided.attr attr_getter5: providers.AttributeGetter = provider5.provided.attr
item_getter5: providers.ItemGetter = provider5.provided["item"] item_getter5: providers.ItemGetter = provider5.provided["item"]
method_caller: providers.MethodCaller = provider5.provided.method.call(123, arg=324) method_caller: providers.MethodCaller = provider5.provided.method.call(123, arg=324)

View File

@ -34,7 +34,7 @@ provider5 = providers.Dict(
a1=providers.Factory(object), a1=providers.Factory(object),
a2=providers.Factory(object), a2=providers.Factory(object),
) )
provided5: providers.ProvidedInstance = provider5.provided provided5: dict[Any, Any] = provider5.provided()
# Test 6: to check the return type with await # Test 6: to check the return type with await

View File

@ -37,7 +37,7 @@ attributes4: Dict[str, Any] = provider4.attributes
# Test 5: to check the provided instance interface # Test 5: to check the provided instance interface
provider5 = providers.Factory(Animal) provider5 = providers.Factory(Animal)
provided5: providers.ProvidedInstance = provider5.provided provided5: Animal = provider5.provided()
attr_getter5: providers.AttributeGetter = provider5.provided.attr attr_getter5: providers.AttributeGetter = provider5.provided.attr
item_getter5: providers.ItemGetter = provider5.provided["item"] item_getter5: providers.ItemGetter = provider5.provided["item"]
method_caller5: providers.MethodCaller = provider5.provided.method.call(123, arg=324) method_caller5: providers.MethodCaller = provider5.provided.method.call(123, arg=324)

View File

@ -23,7 +23,7 @@ provider3 = providers.List(
providers.Factory(object), providers.Factory(object),
providers.Factory(object), providers.Factory(object),
) )
provided3: providers.ProvidedInstance = provider3.provided provided3: List[Any] = provider3.provided()
attr_getter3: providers.AttributeGetter = provider3.provided.attr attr_getter3: providers.AttributeGetter = provider3.provided.attr
item_getter3: providers.ItemGetter = provider3.provided["item"] item_getter3: providers.ItemGetter = provider3.provided["item"]
method_caller3: providers.MethodCaller = provider3.provided.method.call(123, arg=324) method_caller3: providers.MethodCaller = provider3.provided.method.call(123, arg=324)

View File

@ -9,7 +9,7 @@ var1: int = provider1()
# Test 2: to check the provided instance interface # Test 2: to check the provided instance interface
provider2 = providers.Object(int) provider2 = providers.Object(int)
provided2: providers.ProvidedInstance = provider2.provided provided2: Type[int] = provider2.provided()
attr_getter2: providers.AttributeGetter = provider2.provided.attr attr_getter2: providers.AttributeGetter = provider2.provided.attr
item_getter2: providers.ItemGetter = provider2.provided["item"] item_getter2: providers.ItemGetter = provider2.provided["item"]
method_caller2: providers.MethodCaller = provider2.provided.method.call(123, arg=324) method_caller2: providers.MethodCaller = provider2.provided.method.call(123, arg=324)

View File

@ -3,7 +3,8 @@ from dependency_injector import providers
# Test 1: to check .provided attribute # Test 1: to check .provided attribute
provider1: providers.Provider[int] = providers.Object(1) provider1: providers.Provider[int] = providers.Object(1)
provided: providers.ProvidedInstance = provider1.provided provided: int = provider1.provided()
provider1_delegate: providers.Provider[int] = provider1.provider
# Test 2: to check async mode API # Test 2: to check async mode API
provider2: providers.Provider = providers.Provider() provider2: providers.Provider = providers.Provider()

View File

@ -37,7 +37,7 @@ attributes4: Dict[str, Any] = provider4.attributes
# Test 5: to check the provided instance interface # Test 5: to check the provided instance interface
provider5 = providers.Singleton(Animal) provider5 = providers.Singleton(Animal)
provided5: providers.ProvidedInstance = provider5.provided provided5: Animal = provider5.provided()
attr_getter5: providers.AttributeGetter = provider5.provided.attr attr_getter5: providers.AttributeGetter = provider5.provided.attr
item_getter5: providers.ItemGetter = provider5.provided["item"] item_getter5: providers.ItemGetter = provider5.provided["item"]
method_caller5: providers.MethodCaller = provider5.provided.method.call(123, arg=324) method_caller5: providers.MethodCaller = provider5.provided.method.call(123, arg=324)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,13 +2,13 @@
from decimal import Decimal from decimal import Decimal
from pytest import fixture, mark, raises
from samples.wiringstringids import module, package, resourceclosing
from samples.wiringstringids.container import Container, SubContainer
from samples.wiringstringids.service import Service
from dependency_injector import errors from dependency_injector import errors
from dependency_injector.wiring import Closing, Provide, Provider, wire from dependency_injector.wiring import Closing, Provide, Provider, wire
from pytest import fixture, mark, raises
from samples.wiringstringids import module, package, resourceclosing
from samples.wiringstringids.service import Service
from samples.wiringstringids.container import Container, SubContainer
@fixture(autouse=True) @fixture(autouse=True)
@ -34,10 +34,11 @@ def subcontainer():
@fixture @fixture
def resourceclosing_container(): def resourceclosing_container(request):
container = resourceclosing.Container() container = resourceclosing.Container()
container.wire(modules=[resourceclosing]) container.wire(modules=[resourceclosing])
yield container with container.reset_singletons():
yield container
container.unwire() container.unwire()
@ -274,42 +275,65 @@ def test_wire_multiple_containers():
@mark.usefixtures("resourceclosing_container") @mark.usefixtures("resourceclosing_container")
def test_closing_resource(): def test_closing_resource():
resourceclosing.Service.reset_counter()
result_1 = resourceclosing.test_function() result_1 = resourceclosing.test_function()
assert isinstance(result_1, resourceclosing.Service) assert isinstance(result_1, resourceclosing.Service)
assert result_1.init_counter == 1 assert result_1.init_counter == 1
assert result_1.shutdown_counter == 1 assert result_1.shutdown_counter == 1
assert result_1.dependencies == {"_list": [1, 2], "_dict": {"a": 3, "b": 4}}
result_2 = resourceclosing.test_function() result_2 = resourceclosing.test_function()
assert isinstance(result_2, resourceclosing.Service) assert isinstance(result_2, resourceclosing.Service)
assert result_2.init_counter == 2 assert result_2.init_counter == 2
assert result_2.shutdown_counter == 2 assert result_2.shutdown_counter == 2
assert result_1.dependencies == {"_list": [1, 2], "_dict": {"a": 3, "b": 4}}
assert result_1 is not result_2 assert result_1 is not result_2
@mark.usefixtures("resourceclosing_container") @mark.usefixtures("resourceclosing_container")
def test_closing_dependency_resource(): def test_closing_dependency_resource():
resourceclosing.Service.reset_counter()
result_1 = resourceclosing.test_function_dependency() result_1 = resourceclosing.test_function_dependency()
assert isinstance(result_1, resourceclosing.FactoryService) assert isinstance(result_1, resourceclosing.FactoryService)
assert result_1.service.init_counter == 1 assert result_1.service.init_counter == 2
assert result_1.service.shutdown_counter == 1 assert result_1.service.shutdown_counter == 2
result_2 = resourceclosing.test_function_dependency() result_2 = resourceclosing.test_function_dependency()
assert isinstance(result_2, resourceclosing.FactoryService) assert isinstance(result_2, resourceclosing.FactoryService)
assert result_2.service.init_counter == 2 assert result_2.service.init_counter == 4
assert result_2.service.shutdown_counter == 2 assert result_2.service.shutdown_counter == 4
@mark.usefixtures("resourceclosing_container")
def test_closing_dependency_resource_kwargs():
result_1 = resourceclosing.test_function_dependency_kwargs()
assert isinstance(result_1, resourceclosing.FactoryService)
assert result_1.service.init_counter == 2
assert result_1.service.shutdown_counter == 2
result_2 = resourceclosing.test_function_dependency_kwargs()
assert isinstance(result_2, resourceclosing.FactoryService)
assert result_2.service.init_counter == 4
assert result_2.service.shutdown_counter == 4
@mark.usefixtures("resourceclosing_container")
def test_closing_nested_dependency_resource():
result_1 = resourceclosing.test_function_nested_dependency()
assert isinstance(result_1, resourceclosing.NestedService)
assert result_1.factory_service.service.init_counter == 2
assert result_1.factory_service.service.shutdown_counter == 2
result_2 = resourceclosing.test_function_nested_dependency()
assert isinstance(result_2, resourceclosing.NestedService)
assert result_2.factory_service.service.init_counter == 4
assert result_2.factory_service.service.shutdown_counter == 4
assert result_1 is not result_2 assert result_1 is not result_2
@mark.usefixtures("resourceclosing_container") @mark.usefixtures("resourceclosing_container")
def test_closing_resource_bypass_marker_injection(): def test_closing_resource_bypass_marker_injection():
resourceclosing.Service.reset_counter()
result_1 = resourceclosing.test_function(service=Closing[Provide["service"]]) result_1 = resourceclosing.test_function(service=Closing[Provide["service"]])
assert isinstance(result_1, resourceclosing.Service) assert isinstance(result_1, resourceclosing.Service)
assert result_1.init_counter == 1 assert result_1.init_counter == 1
@ -325,7 +349,6 @@ def test_closing_resource_bypass_marker_injection():
@mark.usefixtures("resourceclosing_container") @mark.usefixtures("resourceclosing_container")
def test_closing_resource_context(): def test_closing_resource_context():
resourceclosing.Service.reset_counter()
service = resourceclosing.Service() service = resourceclosing.Service()
result_1 = resourceclosing.test_function(service=service) result_1 = resourceclosing.test_function(service=service)

View File

@ -4,13 +4,17 @@ from pytest_asyncio import fixture as aio_fixture
# Runtime import to avoid syntax errors in samples on Python < 3.5 and reach top-dir # Runtime import to avoid syntax errors in samples on Python < 3.5 and reach top-dir
import os import os
_SAMPLES_DIR = os.path.abspath( _SAMPLES_DIR = os.path.abspath(
os.path.sep.join(( os.path.sep.join(
os.path.dirname(__file__), (
"../samples/", os.path.dirname(__file__),
)), "../samples/",
)
),
) )
import sys import sys
sys.path.append(_SAMPLES_DIR) sys.path.append(_SAMPLES_DIR)
@ -37,6 +41,19 @@ async def test_depends_marker_injection(async_client: AsyncClient):
assert response.json() == {"result": "Foo"} assert response.json() == {"result": "Foo"}
@mark.asyncio
async def test_depends_with_annotated(async_client: AsyncClient):
class ServiceMock:
async def process(self):
return "Foo"
with web.container.service.override(ServiceMock()):
response = await async_client.get("/")
assert response.status_code == 200
assert response.json() == {"result": "Foo"}
@mark.asyncio @mark.asyncio
async def test_depends_injection(async_client: AsyncClient): async def test_depends_injection(async_client: AsyncClient):
response = await async_client.get("/auth", auth=("john_smith", "secret")) response = await async_client.get("/auth", auth=("john_smith", "secret"))

View File

@ -2,19 +2,25 @@ import json
# Runtime import to avoid syntax errors in samples on Python < 3.5 and reach top-dir # Runtime import to avoid syntax errors in samples on Python < 3.5 and reach top-dir
import os import os
_TOP_DIR = os.path.abspath( _TOP_DIR = os.path.abspath(
os.path.sep.join(( os.path.sep.join(
os.path.dirname(__file__), (
"../", os.path.dirname(__file__),
)), "../",
)
),
) )
_SAMPLES_DIR = os.path.abspath( _SAMPLES_DIR = os.path.abspath(
os.path.sep.join(( os.path.sep.join(
os.path.dirname(__file__), (
"../samples/", os.path.dirname(__file__),
)), "../samples/",
)
),
) )
import sys import sys
sys.path.append(_TOP_DIR) sys.path.append(_TOP_DIR)
sys.path.append(_SAMPLES_DIR) sys.path.append(_SAMPLES_DIR)
@ -29,3 +35,13 @@ def test_wiring_with_flask():
assert response.status_code == 200 assert response.status_code == 200
assert json.loads(response.data) == {"result": "OK"} assert json.loads(response.data) == {"result": "OK"}
def test_wiring_with_annotated():
client = web.app.test_client()
with web.app.app_context():
response = client.get("/annotated")
assert response.status_code == 200
assert json.loads(response.data) == {"result": "OK"}

View File

@ -6,6 +6,13 @@ import inspect
from dependency_injector.wiring import inject from dependency_injector.wiring import inject
def test_isfunction():
@inject
def foo(): ...
assert inspect.isfunction(foo)
def test_asyncio_iscoroutinefunction(): def test_asyncio_iscoroutinefunction():
@inject @inject
async def foo(): async def foo():