Skip to content

Commit c787ac2

Browse files
committed
Merge branch 'release/4.27.0' into master
2 parents d3720bd + 48392be commit c787ac2

24 files changed

+280
-18
lines changed

.deepsource.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
version = 1
2+
3+
test_patterns = ["tests/**/test_*.py"]
4+
5+
exclude_patterns = ["docs/**"]
6+
7+
[[analyzers]]
8+
name = "python"
9+
enabled = true
10+
11+
[analyzers.meta]
12+
runtime_version = "3.x.x"

CONTRIBUTORS.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ Dependency Injector Contributors
1515
+ Rüdiger Busche (JarnoRFB)
1616
+ Dmitry Rassoshenko (rda-dev)
1717
+ Fotis Koutoupas (kootoopas)
18+
+ Shubhendra Singh Chauhan (withshubh)

README.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ Choose one of the following:
155155
- `Application example (single container) <https://python-dependency-injector.ets-labs.org/examples/application-single-container.html>`_
156156
- `Application example (multiple containers) <https://python-dependency-injector.ets-labs.org/examples/application-multiple-containers.html>`_
157157
- `Decoupled packages example (multiple containers) <https://python-dependency-injector.ets-labs.org/examples/decoupled-packages.html>`_
158+
- `Boto3 example <https://python-dependency-injector.ets-labs.org/examples/boto3.html>`_
158159
- `Django example <https://python-dependency-injector.ets-labs.org/examples/django.html>`_
159160
- `Flask example <https://python-dependency-injector.ets-labs.org/examples/flask.html>`_
160161
- `Aiohttp example <https://python-dependency-injector.ets-labs.org/examples/aiohttp.html>`_

docs/examples/boto3.rst

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
.. _boto3-example:
2+
3+
Boto3 example
4+
=============
5+
6+
.. meta::
7+
:keywords: Python,Dependency Injection,Boto3,AWS,Amazon Web Services,S3,SQS,Rout53,EC2,Lambda,Example
8+
:description: This example demonstrates a usage of Boto3 AWS client and Dependency Injector.
9+
10+
11+
This example shows how to use ``Dependency Injector`` with `Boto3 <https://www.djangoproject.com/>`_.
12+
13+
The source code is available on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/boto3-session>`_.
14+
15+
Listing of ``boto3_session_example.py``:
16+
17+
.. literalinclude:: ../../examples/miniapps/boto3-session/boto3_session_example.py
18+
:language: python
19+
20+
.. disqus::

docs/examples/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Explore the examples to see the ``Dependency Injector`` in action.
1313
application-single-container
1414
application-multiple-containers
1515
decoupled-packages
16+
boto3
1617
django
1718
flask
1819
flask-blueprints

docs/introduction/di_in_python.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@ Choose one of the following as a next step:
281281
- :ref:`application-single-container`
282282
- :ref:`application-multiple-containers`
283283
- :ref:`decoupled-packages`
284+
- :ref:`boto3`
284285
- :ref:`django-example`
285286
- :ref:`flask-example`
286287
- :ref:`flask-blueprints-example`

docs/main/changelog.rst

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

10+
4.27.0
11+
------
12+
- Introduce wiring inspect filter to filter out ``flask.request`` and other local proxy objects
13+
from the inspection.
14+
See issue: `#408 <https://github.com/ets-labs/python-dependency-injector/issues/408>`_.
15+
Many thanks to `@bvanfleet <https://github.com/bvanfleet>`_ for reporting the issue and
16+
help in finding the root cause.
17+
- Add ``boto3`` example.
18+
- Add tests for ``.as_float()`` modifier usage with wiring.
19+
- Make refactoring of wiring module and tests.
20+
See PR # `#406 <https://github.com/ets-labs/python-dependency-injector/issues/406>`_.
21+
Thanks to `@withshubh <https://github.com/withshubh>`_ for the contribution:
22+
- Remove unused imports in tests.
23+
- Use literal syntax to create data structure in tests.
24+
- Add integration with a static analysis tool `DeepSource <https://deepsource.io/>`_.
25+
1026
4.26.0
1127
------
1228
- Add wiring by string id.

docs/wiring.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,7 @@ Take a look at other application examples:
405405
- :ref:`application-single-container`
406406
- :ref:`application-multiple-containers`
407407
- :ref:`decoupled-packages`
408+
- :ref:`boto3`
408409
- :ref:`django-example`
409410
- :ref:`flask-example`
410411
- :ref:`flask-blueprints-example`
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Boto3 Session Example
2+
=====================
3+
4+
This is a `Boto3 <https://boto3.amazonaws.com/v1/documentation/api/latest/index.html>`_ session +
5+
`Dependency Injector <https://python-dependency-injector.ets-labs.org/>`_ example.
6+
7+
Run
8+
---
9+
10+
To run the application do:
11+
12+
.. code-block:: bash
13+
14+
python boto3_session_example.py
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
"""Boto3 session example."""
2+
3+
import boto3.session
4+
from dependency_injector import containers, providers
5+
6+
7+
class Service:
8+
def __init__(self, s3_client, sqs_client):
9+
self.s3_client = s3_client
10+
self.sqs_client = sqs_client
11+
12+
13+
class Container(containers.DeclarativeContainer):
14+
15+
config = providers.Configuration()
16+
17+
session = providers.Resource(
18+
boto3.session.Session,
19+
aws_access_key_id=config.aws_access_key_id,
20+
aws_secret_access_key=config.aws_secret_access_key,
21+
aws_session_token=config.aws_session_token,
22+
)
23+
24+
s3_client = providers.Resource(
25+
session.provided.client.call(),
26+
service_name='s3',
27+
)
28+
29+
sqs_client = providers.Resource(
30+
providers.MethodCaller(session.provided.client), # Alternative syntax
31+
service_name='sqs',
32+
)
33+
34+
service1 = providers.Factory(
35+
Service,
36+
s3_client=s3_client,
37+
sqs_client=sqs_client,
38+
)
39+
40+
service2 = providers.Factory(
41+
Service,
42+
s3_client=session.provided.client.call(service_name='s3'), # Alternative inline syntax
43+
sqs_client=session.provided.client.call(service_name='sqs'), # Alternative inline syntax
44+
)
45+
46+
47+
def main():
48+
container = Container()
49+
container.config.aws_access_key_id.from_env('AWS_ACCESS_KEY_ID')
50+
container.config.aws_secret_access_key.from_env('AWS_SECRET_ACCESS_KEY')
51+
container.config.aws_session_token.from_env('AWS_SESSION_TOKEN')
52+
container.init_resources()
53+
54+
s3_client = container.s3_client()
55+
print(s3_client)
56+
57+
sqs_client = container.sqs_client()
58+
print(sqs_client)
59+
60+
service1 = container.service1()
61+
print(service1, service1.s3_client, service1.sqs_client)
62+
assert service1.s3_client is s3_client
63+
assert service1.sqs_client is sqs_client
64+
65+
service2 = container.service1()
66+
print(service2, service2.s3_client, service2.sqs_client)
67+
assert service2.s3_client is s3_client
68+
assert service2.sqs_client is sqs_client
69+
70+
71+
if __name__ == '__main__':
72+
main()

src/dependency_injector/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Top-level package."""
22

3-
__version__ = '4.26.0'
3+
__version__ = '4.27.0'
44
"""Version number.
55
66
:type: str

src/dependency_injector/wiring.py

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,21 @@ class GenericMeta(type):
3737

3838

3939
try:
40-
from fastapi.params import Depends as FastAPIDepends
41-
fastapi_installed = True
40+
import fastapi.params
4241
except ImportError:
43-
fastapi_installed = False
42+
fastapi = None
43+
44+
45+
try:
46+
import starlette.requests
47+
except ImportError:
48+
starlette = None
49+
50+
51+
try:
52+
import werkzeug.local
53+
except ImportError:
54+
werkzeug = None
4455

4556

4657
from . import providers
@@ -248,6 +259,28 @@ def _create_providers_map(
248259
return providers_map
249260

250261

262+
class InspectFilter:
263+
264+
def is_excluded(self, instance: object) -> bool:
265+
if self._is_werkzeug_local_proxy(instance):
266+
return True
267+
elif self._is_starlette_request_cls(instance):
268+
return True
269+
else:
270+
return False
271+
272+
def _is_werkzeug_local_proxy(self, instance: object) -> bool:
273+
return werkzeug and isinstance(instance, werkzeug.local.LocalProxy)
274+
275+
def _is_starlette_request_cls(self, instance: object) -> bool:
276+
return starlette \
277+
and isinstance(instance, type) \
278+
and issubclass(instance, starlette.requests.Request)
279+
280+
281+
inspect_filter = InspectFilter()
282+
283+
251284
def wire( # noqa: C901
252285
container: Container,
253286
*,
@@ -269,6 +302,8 @@ def wire( # noqa: C901
269302

270303
for module in modules:
271304
for name, member in inspect.getmembers(module):
305+
if inspect_filter.is_excluded(member):
306+
continue
272307
if inspect.isfunction(member):
273308
_patch_fn(module, name, member, providers_map)
274309
elif inspect.isclass(member):
@@ -531,7 +566,7 @@ def _is_fastapi_default_arg_injection(injection, kwargs):
531566

532567

533568
def _is_fastapi_depends(param: Any) -> bool:
534-
return fastapi_installed and isinstance(param, FastAPIDepends)
569+
return fastapi and isinstance(param, fastapi.params.Depends)
535570

536571

537572
def _is_patched(fn):

tests/unit/providers/test_callables_py2_py3.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def test_set_args(self):
6969
provider = providers.Callable(_example) \
7070
.add_args(1, 2) \
7171
.set_args(3, 4)
72-
self.assertEqual(provider.args, tuple([3, 4]))
72+
self.assertEqual(provider.args, (3, 4))
7373

7474
def test_set_kwargs(self):
7575
provider = providers.Callable(_example) \

tests/unit/providers/test_coroutines_py35.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ def test_set_args(self):
8787
provider = providers.Coroutine(_example) \
8888
.add_args(1, 2) \
8989
.set_args(3, 4)
90-
self.assertEqual(provider.args, tuple([3, 4]))
90+
self.assertEqual(provider.args, (3, 4))
9191

9292
def test_set_kwargs(self):
9393
provider = providers.Coroutine(_example) \

tests/unit/providers/test_factories_py2_py3.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ def test_set_args(self):
228228
provider = providers.Factory(Example) \
229229
.add_args(1, 2) \
230230
.set_args(3, 4)
231-
self.assertEqual(provider.args, tuple([3, 4]))
231+
self.assertEqual(provider.args, (3, 4))
232232

233233
def test_set_kwargs(self):
234234
provider = providers.Factory(Example) \

tests/unit/providers/test_list_py2_py3.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def test_set_args(self):
4242
provider = providers.List() \
4343
.add_args(1, 2) \
4444
.set_args(3, 4)
45-
self.assertEqual(provider.args, tuple([3, 4]))
45+
self.assertEqual(provider.args, (3, 4))
4646

4747
def test_clear_args(self):
4848
provider = providers.List() \

tests/unit/providers/test_resource_py35.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ def test_set_args(self):
203203
provider = providers.Resource(init_fn) \
204204
.add_args(1, 2) \
205205
.set_args(3, 4)
206-
self.assertEqual(provider.args, tuple([3, 4]))
206+
self.assertEqual(provider.args, (3, 4))
207207

208208
def test_clear_args(self):
209209
provider = providers.Resource(init_fn) \

tests/unit/providers/test_singletons_py2_py3.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ def test_set_args(self):
190190
provider = self.singleton_cls(Example) \
191191
.add_args(1, 2) \
192192
.set_args(3, 4)
193-
self.assertEqual(provider.args, tuple([3, 4]))
193+
self.assertEqual(provider.args, (3, 4))
194194

195195
def test_set_kwargs(self):
196196
provider = self.singleton_cls(Example) \

tests/unit/samples/wiringflask/web.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import sys
2+
3+
from flask import Flask, jsonify, request, current_app, session, g
4+
from flask import _request_ctx_stack, _app_ctx_stack
5+
from dependency_injector import containers, providers
6+
from dependency_injector.wiring import inject, Provide
7+
8+
# This is here for testing wiring bypasses these objects without crashing
9+
request, current_app, session, g # noqa
10+
_request_ctx_stack, _app_ctx_stack # noqa
11+
12+
13+
class Service:
14+
def process(self) -> str:
15+
return 'Ok'
16+
17+
18+
class Container(containers.DeclarativeContainer):
19+
20+
service = providers.Factory(Service)
21+
22+
23+
app = Flask(__name__)
24+
25+
26+
@app.route('/')
27+
@inject
28+
def index(service: Service = Provide[Container.service]):
29+
result = service.process()
30+
return jsonify({'result': result})
31+
32+
33+
container = Container()
34+
container.wire(modules=[sys.modules[__name__]])

tests/unit/samples/wiringsamples/module.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,19 +44,23 @@ def test_function_provider(service_provider: Callable[..., Service] = Provider[C
4444
@inject
4545
def test_config_value(
4646
value_int: int = Provide[Container.config.a.b.c.as_int()],
47+
value_float: float = Provide[Container.config.a.b.c.as_float()],
4748
value_str: str = Provide[Container.config.a.b.c.as_(str)],
4849
value_decimal: Decimal = Provide[Container.config.a.b.c.as_(Decimal)],
4950
value_required: str = Provide[Container.config.a.b.c.required()],
5051
value_required_int: int = Provide[Container.config.a.b.c.required().as_int()],
52+
value_required_float: float = Provide[Container.config.a.b.c.required().as_float()],
5153
value_required_str: str = Provide[Container.config.a.b.c.required().as_(str)],
5254
value_required_decimal: str = Provide[Container.config.a.b.c.required().as_(Decimal)],
5355
):
5456
return (
5557
value_int,
58+
value_float,
5659
value_str,
5760
value_decimal,
5861
value_required,
5962
value_required_int,
63+
value_required_float,
6064
value_required_str,
6165
value_required_decimal,
6266
)

0 commit comments

Comments
 (0)