Skip to content

Commit 47c79b2

Browse files
authored
Resources (#312)
* Add prototype * Add example * Remove typing erros in Python 2.7 and 3.4 * Move resources example * Draft resources docs * Update resources docs * Fix repr * Rename dict provider test * Add more tests * Add tests + refactoring * Add more tests * Update tests to run only on 3.5+ * Update setup.py * Add typing tests * Update changelog * Fix generator iteration * Remove contextlib * Hotfix aiohttp issue * Move aiohttp fix to tox.ini * Move aiohttp fix to a different place in tox
1 parent b54bcb7 commit 47c79b2

File tree

21 files changed

+13280
-7690
lines changed

21 files changed

+13280
-7690
lines changed

README.rst

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,17 +57,20 @@ It helps implementing the dependency injection principle.
5757
Key features of the ``Dependency Injector``:
5858

5959
- **Providers**. Provides ``Factory``, ``Singleton``, ``Callable``, ``Coroutine``, ``Object``,
60-
``List``, ``Dict``, ``Configuration``, ``Dependency`` and ``Selector`` providers that help
61-
assembling your objects.
60+
``List``, ``Dict``, ``Configuration``, ``Resource``, ``Dependency`` and ``Selector`` providers
61+
that help assembling your objects.
6262
See `Providers <https://python-dependency-injector.ets-labs.org/providers/index.html>`_.
6363
- **Overriding**. Can override any provider by another provider on the fly. This helps in testing
6464
and configuring dev / stage environment to replace API clients with stubs etc. See
6565
`Provider overriding <https://python-dependency-injector.ets-labs.org/providers/overriding.html>`_.
66-
- **Configuration**. Read configuration from ``yaml`` & ``ini`` files, environment variables
66+
- **Configuration**. Reads configuration from ``yaml`` & ``ini`` files, environment variables
6767
and dictionaries.
6868
See `Configuration provider <https://python-dependency-injector.ets-labs.org/providers/configuration.html>`_.
6969
- **Containers**. Provides declarative and dynamic containers.
7070
See `Containers <https://python-dependency-injector.ets-labs.org/containers/index.html>`_.
71+
- **Resources**. Helps with initialization and configuring of logging, event loop, thread
72+
or process pool, etc.
73+
See `Resource provider <https://python-dependency-injector.ets-labs.org/providers/resource.html>`_.
7174
- **Wiring**. Injects dependencies into functions and methods. Helps integrating with
7275
other frameworks: Django, Flask, Aiohttp, etc.
7376
See `Wiring <https://python-dependency-injector.ets-labs.org/wiring.html>`_.

docs/index.rst

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,15 @@ It helps implementing the dependency injection principle.
6969
Key features of the ``Dependency Injector``:
7070

7171
- **Providers**. Provides ``Factory``, ``Singleton``, ``Callable``, ``Coroutine``, ``Object``,
72-
``List``, ``Dict``, ``Configuration``, ``Dependency`` and ``Selector`` providers that help
73-
assembling your objects. See :ref:`providers`.
72+
``List``, ``Dict``, ``Configuration``, ``Resource``, ``Dependency`` and ``Selector`` providers
73+
that help assembling your objects. See :ref:`providers`.
7474
- **Overriding**. Can override any provider by another provider on the fly. This helps in testing
7575
and configuring dev / stage environment to replace API clients with stubs etc. See
7676
:ref:`provider-overriding`.
77-
- **Configuration**. Read configuration from ``yaml`` & ``ini`` files, environment variables
77+
- **Configuration**. Reads configuration from ``yaml`` & ``ini`` files, environment variables
7878
and dictionaries. See :ref:`configuration-provider`.
79+
- **Resources**. Helps with initialization and configuring of logging, event loop, thread
80+
or process pool, etc. See :ref:`resource-provider`.
7981
- **Containers**. Provides declarative and dynamic containers. See :ref:`containers`.
8082
- **Wiring**. Injects dependencies into functions and methods. Helps integrating with
8183
other frameworks: Django, Flask, Aiohttp, etc. See :ref:`wiring`.

docs/introduction/key_features.rst

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@ Key features
1111
Key features of the ``Dependency Injector``:
1212

1313
- **Providers**. Provides ``Factory``, ``Singleton``, ``Callable``, ``Coroutine``, ``Object``,
14-
``List``, ``Dict``, ``Configuration``, ``Dependency`` and ``Selector`` providers that help
15-
assembling your objects. See :ref:`providers`.
14+
``List``, ``Dict``, ``Configuration``, ``Resource``, ``Dependency`` and ``Selector`` providers
15+
that help assembling your objects. See :ref:`providers`.
1616
- **Overriding**. Can override any provider by another provider on the fly. This helps in testing
1717
and configuring dev / stage environment to replace API clients with stubs etc. See
1818
:ref:`provider-overriding`.
19-
- **Configuration**. Read configuration from ``yaml`` & ``ini`` files, environment variables
19+
- **Configuration**. Reads configuration from ``yaml`` & ``ini`` files, environment variables
2020
and dictionaries. See :ref:`configuration-provider`.
21+
- **Resources**. Helps with initialization and configuring of logging, event loop, thread
22+
or process pool, etc. See :ref:`resource-provider`.
2123
- **Containers**. Provides declarative and dynamic containers. See :ref:`containers`.
2224
- **Wiring**. Injects dependencies into functions and methods. Helps integrating with
2325
other frameworks: Django, Flask, Aiohttp, etc. See :ref:`wiring`.

docs/main/changelog.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@ follows `Semantic versioning`_
99

1010
Develop
1111
-------
12+
- Add ``Resource`` provider.
1213
- Add ``Dict`` provider.
1314
- "Un-deprecate" ``@containers.override()`` and ``@containers.copy()`` decorators (
1415
see `Issue 301 <https://github.com/ets-labs/python-dependency-injector/issues/301>`_
1516
for more information).
1617
- Add favicon.
1718
- Remove redirects that occur while getting badge images to optimize docs load speed.
1819
- Update license year.
20+
- Update short description on PyPI.
1921

2022
4.0.6
2123
-----

docs/providers/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ Providers module API docs - :py:mod:`dependency_injector.providers`
4545
list
4646
dict
4747
configuration
48+
resource
4849
selector
4950
dependency
5051
overriding

docs/providers/resource.rst

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
.. _resource-provider:
2+
3+
Resource provider
4+
=================
5+
6+
.. meta::
7+
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Resource,Injection,
8+
Logging,Event Loop,Thread Pool
9+
:description: Resource provider provides a component with initialization and shutdown. It works
10+
well for configuring logging, event loop, thread or process pool, etc.
11+
This page demonstrates how to use resource provider.
12+
13+
.. currentmodule:: dependency_injector.providers
14+
15+
:py:class:`Resource` provider provides a component with initialization and shutdown.
16+
17+
.. literalinclude:: ../../examples/providers/resource.py
18+
:language: python
19+
:lines: 3-
20+
21+
Resource providers help to initialize and configure logging, event loop, thread or process pool, etc.
22+
23+
Resource provider is similar to ``Singleton``. Resource initialization happens only once.
24+
You can do injections and use provided instance the same way like you do with any other provider.
25+
26+
.. code-block:: python
27+
:emphasize-lines: 12
28+
29+
class Container(containers.DeclarativeContainer):
30+
31+
config = providers.Configuration()
32+
33+
thread_pool = providers.Resource(
34+
init_threat_pool,
35+
max_workers=config.max_workers,
36+
)
37+
38+
dispatcher = providers.Factory(
39+
TaskDispatcher,
40+
executor=thread_pool,
41+
)
42+
43+
Container has an interface to initialize and shutdown all resources:
44+
45+
.. code-block:: python
46+
47+
container = Container()
48+
container.init_resources()
49+
container.shutdown_resources()
50+
51+
You also can initialize and shutdown resources one-by-one using ``init()`` and
52+
``shutdown()`` methods of the provider:
53+
54+
.. code-block:: python
55+
56+
container = Container()
57+
container.thread_pool.init()
58+
container.thread_pool.shutdown()
59+
60+
Resource provider supports 3 types of initializers:
61+
62+
- Function
63+
- Generator
64+
- Subclass of ``resources.Resource``
65+
66+
Function initializer
67+
--------------------
68+
69+
Function is the most common way to specify resource initialization:
70+
71+
.. code-block:: python
72+
73+
def init_resource(argument1=..., argument2=...):
74+
return SomeResource()
75+
76+
77+
class Container(containers.DeclarativeContainer):
78+
79+
resource = providers.Resource(
80+
init_resource,
81+
argument1=...,
82+
argument2=...,
83+
)
84+
85+
Function initializer may not return a value. This often happens when
86+
you configure global resource:
87+
88+
.. code-block:: python
89+
90+
import logging.config
91+
92+
93+
class Container(containers.DeclarativeContainer):
94+
95+
configure_logging = providers.Resource(
96+
logging.config.fileConfig,
97+
fname='logging.ini',
98+
)
99+
100+
Function initializer does not support shutdown.
101+
102+
Generator initializer
103+
---------------------
104+
105+
Resource provider can use 2-step generators:
106+
107+
- First step of generator is an initialization phase
108+
- The second is step is a shutdown phase
109+
110+
.. code-block:: python
111+
112+
def init_resource(argument1=..., argument2=...):
113+
resource = SomeResource() # initialization
114+
115+
yield resource
116+
117+
# shutdown
118+
...
119+
120+
121+
class Container(containers.DeclarativeContainer):
122+
123+
resource = providers.Resource(
124+
init_resource,
125+
argument1=...,
126+
argument2=...,
127+
)
128+
129+
Generator initialization phase ends on the first ``yield`` statement. You can return a
130+
resource object using ``yield resource`` like in the example above. Returning of the
131+
object is not mandatory. You can leave ``yield`` statement empty:
132+
133+
.. code-block:: python
134+
135+
def init_resource(argument1=..., argument2=...):
136+
# initialization
137+
...
138+
139+
yield
140+
141+
# shutdown
142+
...
143+
144+
145+
class Container(containers.DeclarativeContainer):
146+
147+
resource = providers.Resource(
148+
init_resource,
149+
argument1=...,
150+
argument2=...,
151+
)
152+
153+
Subclass initializer
154+
--------------------
155+
156+
You can create resource initializer by implementing a subclass of the ``resources.Resource``:
157+
158+
.. code-block:: python
159+
160+
from dependency_injector import resources
161+
162+
163+
class MyResource(resources.Resource):
164+
165+
def init(self, argument1=..., argument2=...) -> SomeResource:
166+
return SomeResource()
167+
168+
def shutdown(self, resource: SomeResource) -> None:
169+
# shutdown
170+
...
171+
172+
173+
class Container(containers.DeclarativeContainer):
174+
175+
resource = providers.Resource(
176+
MyResource,
177+
argument1=...,
178+
argument2=...,
179+
)
180+
181+
Subclass must implement two methods: ``init()`` and ``shutdown()``.
182+
183+
Method ``init()`` receives arguments specified in resource provider.
184+
It performs initialization and returns resource object. Returning of the object
185+
is not mandatory.
186+
187+
Method ``shutdown()`` receives resource object returned from ``init()``. If ``init()``
188+
didn't return an object ``shutdown()`` method will be called anyway with ``None`` as a
189+
first argument.
190+
191+
.. code-block:: python
192+
193+
from dependency_injector import resources
194+
195+
196+
class MyResource(resources.Resource):
197+
198+
def init(self, argument1=..., argument2=...) -> None:
199+
# initialization
200+
...
201+
202+
def shutdown(self, _: None) -> None:
203+
# shutdown
204+
...
205+
206+
.. disqus::

examples/providers/resource.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
"""`Resource` provider example."""
2+
3+
import sys
4+
import logging
5+
from concurrent.futures import ThreadPoolExecutor
6+
7+
from dependency_injector import containers, providers
8+
9+
10+
def init_threat_pool(max_workers: int):
11+
thread_pool = ThreadPoolExecutor(max_workers=max_workers)
12+
yield thread_pool
13+
thread_pool.shutdown(wait=True)
14+
15+
16+
class Container(containers.DeclarativeContainer):
17+
18+
config = providers.Configuration()
19+
20+
thread_pool = providers.Resource(
21+
init_threat_pool,
22+
max_workers=config.max_workers,
23+
)
24+
25+
logging = providers.Resource(
26+
logging.basicConfig,
27+
level=logging.INFO,
28+
stream=sys.stdout,
29+
)
30+
31+
32+
if __name__ == '__main__':
33+
container = Container(config={'max_workers': 4})
34+
35+
container.init_resources()
36+
37+
logging.info('Resources are initialized')
38+
thread_pool = container.thread_pool()
39+
thread_pool.map(print, range(10))
40+
41+
container.shutdown_resources()

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@
3131

3232
setup(name='dependency-injector',
3333
version=version,
34-
description='Dependency injection microframework for Python',
34+
description='Dependency injection framework for Python',
3535
long_description=description,
3636
author='ETS Labs',
3737
author_email='rmogilatov@gmail.com',
38-
maintainer='Roman Mogilatov',
38+
maintainer='Roman Mogylatov',
3939
maintainer_email='rmogilatov@gmail.com',
4040
url='https://github.com/ets-labs/python-dependency-injector',
4141
download_url='https://pypi.python.org/pypi/dependency_injector',

0 commit comments

Comments
 (0)