8000 Merge branch 'release/3.32.0' into master · bimec/python-dependency-injector@0f952b5 · GitHub
[go: up one dir, main page]

Skip to content

Commit 0f952b5

Browse files
committed
Merge branch 'release/3.32.0' into master
2 parents 69ebc19 + fcbab97 commit 0f952b5

File tree

9 files changed

+2908
-2476
lines changed

9 files changed

+2908
-2476
lines changed

docs/main/changelog.rst

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

10+
3.32.0
11+
------
12+
- Add a feature that helps to explicitly specify the type of the configuration option value
13+
before the injection.
14+
- Add disqus comments to the docs page on injecting provided instance attributes, items, etc.
15+
1016
3.31.0
1117
------
1218
- Add a feature that helps to inject provided instance attribute, item, or method call result

docs/providers/configuration.rst

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,4 +96,34 @@ where ``examples/providers/configuration/config.local.yml`` is:
9696
.. literalinclude:: ../../examples/providers/configuration/config.local.yml
9797
:language: ini
9898

99+
Specifying value type
100+
~~~~~~~~~~~~~~~~~~~~~
101+
102+
You can specify the type of the injected configuration value explicitly.
103+
104+
This helps when you read the value from the ini file or the environment variable and need to
105+
convert it into an ``int`` or a ``float``.
106+
107+
.. literalinclude:: ../../examples/providers/configuration/configuration_type.py
108+
:language: python
109+
:lines: 3-
110+
:emphasize-lines: 17
111+
112+
:py:class:`Configuration` provider has next helper methods:
113+
114+
- ``.as_int()``
115+
- ``.as_float()``
116+
- ``.as_(callback, *args, **kwargs)``
117+
118+
The last method ``.as_(callback, *args, **kwargs)`` helps to implement a other conversions.
119+
120+
.. literalinclude:: ../../examples/providers/configuration/configuration_type_custom.py
121+
:language: python
122+
:lines: 3-
123+
:emphasize-lines: 16
124+
125+
With the ``.as_(callback, *args, **kwargs)`` you can specify the function that will be called
126+
before the injection. The value from the config will be passed as a first argument. The returned
127+
value will be injected. Parameters ``*args`` and ``**kwargs`` are handled as any other injections.
128+
99129
.. disqus::

docs/providers/provided_instance.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,5 @@ should use the :py:class:`ProvidedInstance` provider.
6262
In all other cases you should not use :py:class:`ProvidedInstance`, :py:class:`AttributeGetter`,
6363
:py:class:`ItemGetter`, or :py:class:`MethodCaller` providers directly. Use the ``.provided``
6464
attribute of the injected provider instead.
65+
66+
.. disqus::
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
"""`Configuration` provider type specification example."""
2+
3+
import os
4+
5+
from dependency_injector import providers
6+
7+
8+
class ApiClient:
9+
def __init__(self, api_key: str, timeout: int):
10+
self.api_key = api_key
11+
self.timeout = timeout
12+
13+
14+
config = providers.Configuration()
15+
16+
api_client_factory = providers.Factory(
17+
ApiClient,
18+
api_key=config.api.key,
19+
timeout=config.api.timeout.as_int(),
20+
)
21+
22+
23+
if __name__ == '__main__':
24+
# Emulate environment variables
25+
os.environ['API_KEY'] = 'secret'
26+
os.environ['API_TIMEOUT'] = '5'
27+
28+
config.api.key.from_env('API_KEY')
29+
config.api.timeout.from_env('API_TIMEOUT')
30+
31+
api_client = api_client_factory()
32+
33+
assert api_client.api_key == 'secret'
34+
assert api_client.timeout == 5
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"""`Configuration` provider custom type specification example."""
2+
3+
import os
4+
import decimal
5+
6+
from dependency_injector import providers
7+
8+
9+
class Calculator:
10+
def __init__(self, pi: decimal.Decimal):
11+
self.pi = pi
12+
13+
14+
config = providers.Configuration()
15+
16+
calculator_factory = providers.Factory(
17+
Calculator,
18+
pi=config.pi.as_(decimal.Decimal),
19+
)
20+
21+
22+
if __name__ == '__main__':
23+
# Emulate environment variables
24+
os.environ['PI'] = '3.1415926535897932384626433832'
25+
26+
config.pi.from_env('PI')
27+
28+
calculator = calculator_factory()
29+
30+
assert calculator.pi == decimal.Decimal('3.1415926535897932384626433832')

src/dependency_injector/__init__.py

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

3-
__version__ = '3.31.0'
3+
__version__ = '3.32.0'
44
"""Version number that follows semantic versioning.
55
66
:type: str

src/dependency_injector/providers.c

Lines changed: 2768 additions & 2474 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/dependency_injector/providers.pyx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,6 +1140,15 @@ cdef class ConfigurationOption(Provider):
11401140
root = self.__root_ref()
11411141
return '.'.join((root.get_name(), self._get_self_name()))
11421142

1143+
def as_int(self):
1144+
return Callable(int, self)
1145+
1146+
def as_float(self):
1147+
return Callable(float, self)
1148+
1149+
def as_(self, callback, *args, **kwargs):
1150+
return Callable(callback, self, *args, **kwargs)
1151+
11431152
def override(self, value):
11441153
if isinstance(value, Provider):
11451154
raise Error('Configuration option can only be overridden by a value')

tests/unit/providers/test_configuration_py2_py3.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Dependency injector config providers unit tests."""
22

33
import contextlib
4+
import decimal
45
import os
56
import sys
67
import tempfile
@@ -69,6 +70,33 @@ def test_providers_with_already_set_value(self):
6970
self.assertEqual(abc(), 1)
7071
self.assertEqual(abd(), 2)
7172

73+
def test_as_int(self):
74+
value_provider = providers.Callable(lambda value: value, self.config.test.as_int())
75+
self.config.from_dict({'test': '123'})
76+
77+
value = value_provider()
78+
79+
self.assertEqual(value, 123)
80+
81+
def test_as_float(self):
82+
value_provider = providers.Callable(lambda value: value, self.config.test.as_float())
83+
self.config.from_dict({'test': '123.123'})
84+
85+
value = value_provider()
86+
87+
self.assertEqual(value, 123.123)
88+
89+
def test_as_(self):
90+
value_provider = providers.Callable(
91+
lambda value: value,
92+
self.config.test.as_(decimal.Decimal),
93+
)
94+
self.config.from_dict({'test': '123.123'})
95+
96+
value = value_provider()
97+
98+
self.assertEqual(value, decimal.Decimal('123.123'))
99+
72100
def test_providers_value_override(self):
73101
a = self.config.a
74102
ab = self.config.a.b
@@ -358,7 +386,6 @@ def test_env_variable_interpolation(self):
358386
self.assertEqual(self.config.section1.value1(), 'test-value')
359387

360388

361-
362389
class ConfigFromYamlTests(unittest.TestCase):
363390

364391
def setUp(self):

0 commit comments

Comments
 (0)
0