Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 9d69943

Browse files
committed
Merge branch 'release/4.48.0'
2 parents 8460465 + dd84a1b commit 9d69943

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1051
-398
lines changed

‎.editorconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
root = true
2+
3+
[*]
4+
end_of_line = lf
5+
insert_final_newline = true
6+
trim_trailing_whitespace = true
7+
8+
[*.{py,pyi,pxd,pyx}]
9+
ij_visual_guides = 80,88

‎docs/api/asgi-lifespan.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
dependency_injector.ext.starlette
2+
=================================
3+
4+
.. automodule:: dependency_injector.ext.starlette
5+
:members:
6+
:inherited-members:
7+
:show-inheritance:
8+
9+
.. disqus::

‎docs/api/index.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ API Documentation
22
=================
33

44
.. toctree::
5-
:maxdepth: 2
5+
:maxdepth: 2
66

77
top-level
88
providers
99
containers
1010
wiring
1111
errors
12+
asgi-lifespan

‎docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@
7272
#
7373
# This is also used if you do content translation via gettext catalogs.
7474
# Usually you set "language" from the command line for these cases.
75-
language = None
75+
language = "en"
7676

7777
# There are two options for replacing |today|: either, you set today to some
7878
# non-false value, then it is used:

‎docs/examples/django.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ Container is wired to the ``views`` module in the app config ``web/apps.py``:
7878

7979
.. literalinclude:: ../../examples/miniapps/django/web/apps.py
8080
:language: python
81-
:emphasize-lines: 13
81+
:emphasize-lines: 12
8282

8383
Tests
8484
-----

‎docs/examples/fastdepends.rst

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
.. _fastdepends-example:
2+
3+
FastDepends example
4+
===================
5+
6+
.. meta::
7+
:keywords: Python,Dependency Injection,FastDepends,Example
8+
:description: This example demonstrates a usage of the FastDepends and Dependency Injector.
9+
10+
11+
This example demonstrates how to use ``Dependency Injector`` with `FastDepends <https://github.com/Lancetnik/FastDepends>`_, a lightweight dependency injection framework inspired by FastAPI's dependency system, but without the web framework components.
12+
13+
Basic Usage
14+
-----------
15+
16+
The integration between FastDepends and Dependency Injector is straightforward. Simply use Dependency Injector's ``Provide`` marker within FastDepends' ``Depends`` function:
17+
18+
.. code-block:: python
19+
20+
import sys
21+
22+
from dependency_injector import containers, providers
23+
from dependency_injector.wiring import inject, Provide
24+
from fast_depends import Depends
25+
26+
27+
class CoefficientService:
28+
@staticmethod
29+
def get_coefficient() -> float:
30+
return 1.2
31+
32+
33+
class Container(containers.DeclarativeContainer):
34+
service = providers.Factory(CoefficientService)
35+
36+
37+
@inject
38+
def apply_coefficient(
39+
a: int,
40+
coefficient_provider: CoefficientService = Depends(Provide[Container.service]),
41+
) -> float:
42+
return a * coefficient_provider.get_coefficient()
43+
44+
45+
container = Container()
46+
container.wire(modules=[sys.modules[__name__]])
47+
48+
apply_coefficient(100) == 120.0

‎docs/examples/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,6 @@ Explore the examples to see the ``Dependency Injector`` in action.
2222
fastapi
2323
fastapi-redis
2424
fastapi-sqlalchemy
25+
fastdepends
2526

2627
.. disqus::

‎docs/introduction/key_features.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ Key features of the ``Dependency Injector``:
3131

3232
The framework stands on the `PEP20 (The Zen of Python) <https://www.python.org/dev/peps/pep-0020/>`_ principle:
3333

34-
.. code-block:: plain
34+
.. code-block:: text
3535
3636
Explicit is better than implicit
3737

‎docs/main/changelog.rst

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,31 @@ that were made in every particular version.
77
From version 0.7.6 *Dependency Injector* framework strictly
88
follows `Semantic versioning`_
99

10+
4.48.0
11+
------
12+
13+
- Improve performance of wiring (`#897 <https://github.com/ets-labs/python-dependency-injector/pull/897>`_)
14+
- Add Context Manager support to Resource provider (`#899 <https://github.com/ets-labs/python-dependency-injector/pull/899>`_)
15+
- Add support for async generator injections (`#900 <https://github.com/ets-labs/python-dependency-injector/pull/900>`_)
16+
- Fix unintended dependency on ``typing_extensions`` (`#902 <https://github.com/ets-labs/python-dependency-injector/pull/902>`_)
17+
- Add support for Fast Depends (`#898 <https://github.com/ets-labs/python-dependency-injector/pull/898>`_)
18+
- Add ``resource_type`` parameter to init and shutdown resources using specialized providers (`#858 <https://github.com/ets-labs/python-dependency-injector/pull/858>`_)
19+
20+
4.47.1
21+
------
22+
23+
- Fix typing for wiring marker (`#892 <https://github.com/ets-labs/python-dependency-injector/pull/896>`_)
24+
- Strip debug symbols in wheels
25+
1026
4.47.0
11-
-------
27+
------
1228

1329
- Add support for ``Annotated`` type for module and class attribute injection in wiring,
1430
with updated documentation and examples.
1531
See discussion:
1632
https://github.com/ets-labs/python-dependency-injector/pull/721#issuecomment-2025263718
17-
- Fix ``root`` property shadowing in ``ConfigurationOption`` (`#875 https://github.com/ets-labs/python-dependency-injector/pull/875`_)
18-
- Fix incorrect monkeypatching during ``wire()`` that could violate MRO in some classes (`#886 https://github.com/ets-labs/python-dependency-injector/pull/886`_)
33+
- Fix ``root`` property shadowing in ``ConfigurationOption`` (`#875 <https://github.com/ets-labs/python-dependency-injector/pull/875>`_)
34+
- Fix incorrect monkeypatching during ``wire()`` that could violate MRO in some classes (`#886 <https://github.com/ets-labs/python-dependency-injector/pull/886>`_)
1935
- ABI3 wheels are now published for CPython.
2036
- Drop support of Python 3.7.
2137

@@ -371,8 +387,8 @@ Many thanks to `ZipFile <https://github.com/ZipFile>`_ for both contributions.
371387
- Make refactoring of wiring module and tests.
372388
See PR # `#406 <https://github.com/ets-labs/python-dependency-injector/issues/406>`_.
373389
Thanks to `@withshubh <https://github.com/withshubh>`_ for the contribution:
374-
- Remove unused imports in tests.
375-
- Use literal syntax to create data structure in tests.
390+
- Remove unused imports in tests.
391+
- Use literal syntax to create data structure in tests.
376392
- Add integration with a static analysis tool `DeepSource <https://deepsource.io/>`_.
377393

378394
4.26.0

‎docs/providers/resource.rst

Lines changed: 166 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,12 @@ When you call ``.shutdown()`` method on a resource provider, it will remove the
6161
if any, and switch to uninitialized state. Some of resource initializer types support specifying custom
6262
resource shutdown.
6363

64-
Resource provider supports 3 types of initializers:
64+
Resource provider supports 4 types of initializers:
6565

6666
- Function
67-
- Generator
68-
- Subclass of ``resources.Resource``
67+
- Context Manager
68+
- Generator (legacy)
69+
- Subclass of ``resources.Resource`` (legacy)
6970

7071
Function initializer
7172
--------------------
@@ -103,8 +104,44 @@ you configure global resource:
103104
104105
Function initializer does not provide a way to specify custom resource shutdown.
105106

106-
Generator initializer
107-
---------------------
107+
Context Manager initializer
108+
---------------------------
109+
110+
This is an extension to the Function initializer. Resource provider automatically detects if the initializer returns a
111+
context manager and uses it to manage the resource lifecycle.
112+
113+
.. code-block:: python
114+
115+
from dependency_injector import containers, providers
116+
117+
class DatabaseConnection:
118+
def __init__(self, host, port, user, password):
119+
self.host = host
120+
self.port = port
121+
self.user = user
122+
self.password = password
123+
124+
def __enter__(self):
125+
print(f"Connecting to {self.host}:{self.port} as {self.user}")
126+
return self
127+
128+
def __exit__(self, exc_type, exc_val, exc_tb):
129+
print("Closing connection")
130+
131+
132+
class Container(containers.DeclarativeContainer):
133+
134+
config = providers.Configuration()
135+
db = providers.Resource(
136+
DatabaseConnection,
137+
host=config.db.host,
138+
port=config.db.port,
139+
user=config.db.user,
140+
password=config.db.password,
141+
)
142+
143+
Generator initializer (legacy)
144+
------------------------------
108145

109146
Resource provider can use 2-step generators:
110147

@@ -154,8 +191,13 @@ object is not mandatory. You can leave ``yield`` statement empty:
154191
argument2=...,
155192
)
156193
157-
Subclass initializer
158-
--------------------
194+
.. note::
195+
196+
Generator initializers are automatically wrapped with ``contextmanager`` or ``asynccontextmanager`` decorator when
197+
provided to a ``Resource`` provider.
198+
199+
Subclass initializer (legacy)
200+
-----------------------------
159201

160202
You can create resource initializer by implementing a subclass of the ``resources.Resource``:
161203

@@ -210,6 +252,72 @@ first argument.
210252
211253
.. _resource-provider-wiring-closing:
212254

255+
Scoping Resources using specialized subclasses
256+
----------------------------------------------
257+
258+
You can use specialized subclasses of ``Resource`` provider to initialize and shutdown resources by type.
259+
Allowing for example to only initialize a subgroup of resources.
260+
261+
.. code-block:: python
262+
263+
class ScopedResource(resources.Resource):
264+
pass
265+
266+
def init_service(name) -> Service:
267+
print(f"Init {name}")
268+
yield Service()
269+
print(f"Shutdown {name}")
270+
271+
class Container(containers.DeclarativeContainer):
272+
273+
scoped = ScopedResource(
274+
init_service,
275+
"scoped",
276+
)
277+
278+
generic = providers.Resource(
279+
init_service,
280+
"generic",
281+
)
282+
283+
284+
To initialize resources by type you can use ``init_resources(resource_type)`` and ``shutdown_resources(resource_type)``
285+
methods adding the resource type as an argument:
286+
287+
.. code-block:: python
288+
289+
def main():
290+
container = Container()
291+
container.init_resources(ScopedResource)
292+
# Generates:
293+
# >>> Init scoped
294+
295+
container.shutdown_resources(ScopedResource)
296+
# Generates:
297+
# >>> Shutdown scoped
298+
299+
300+
And to initialize all resources you can use ``init_resources()`` and ``shutdown_resources()`` without arguments:
301+
302+
.. code-block:: python
303+
304+
def main():
305+
container = Container()
306+
container.init_resources()
307+
# Generates:
308+
# >>> Init scoped
309+
# >>> Init generic
310+
311+
container.shutdown_resources()
312+
# Generates:
313+
# >>> Shutdown scoped
314+
# >>> Shutdown generic
315+
316+
317+
It works using the ``traverse()`` method to find all resources of the specified type, selecting all resources
318+
which are instances of the specified type.
319+
320+
213321
Resources, wiring, and per-function execution scope
214322
---------------------------------------------------
215323

@@ -263,10 +371,11 @@ Asynchronous function initializer:
263371
argument2=...,
264372
)
265373
266-
Asynchronous generator initializer:
374+
Asynchronous Context Manager initializer:
267375

268376
.. code-block:: python
269377
378+
@asynccontextmanager
270379
async def init_async_resource(argument1=..., argument2=...):
271380
connection = await connect()
272381
yield connection
@@ -358,5 +467,54 @@ See also:
358467
- Wiring :ref:`async-injections-wiring`
359468
- :ref:`fastapi-redis-example`
360469

470+
ASGI Lifespan Protocol Support
471+
------------------------------
472+
473+
The :mod:`dependency_injector.ext.starlette` module provides a :class:`~dependency_injector.ext.starlette.Lifespan`
474+
class that integrates resource providers with ASGI applications using the `Lifespan Protocol`_. This allows resources to
475+
be automatically initialized at application startup and properly shut down when the application stops.
476+
477+
.. code-block:: python
478+
479+
from contextlib import asynccontextmanager
480+
from dependency_injector import containers, providers
481+
from dependency_injector.wiring import Provide, inject
482+
from dependency_injector.ext.starlette import Lifespan
483+
from fastapi import FastAPI, Request, Depends, APIRouter
484+
485+
class Connection: ...
486+
487+
@asynccontextmanager
488+
async def init_database():
489+
print("opening database connection")
490+
yield Connection()
491+
print("closing database connection")
492+
493+
router = APIRouter()
494+
495+
@router.get("/")
496+
@inject
497+
async def index(request: Request, db: Connection = Depends(Provide["db"])):
498+
# use the database connection here
499+
return "OK!"
500+
501+
class Container(containers.DeclarativeContainer):
502+
__self__ = providers.Self()
503+
db = providers.Resource(init_database)
504+
lifespan = providers.Singleton(Lifespan, __self__)
505+
app = providers.Singleton(FastAPI, lifespan=lifespan)
506+
_include_router = providers.Resource(
507+
app.provided.include_router.call(),
508+
router,
509+
)
510+
511+
if __name__ == "__main__":
512+
import uvicorn
513+
514+
container = Container()
515+
app = container.app()
516+
uvicorn.run(app, host="localhost", port=8000)
517+
518+
.. _Lifespan Protocol: https://asgi.readthedocs.io/en/latest/specs/lifespan.html
361519

362520
.. disqus::

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /