Merge branch 'release/3.32.0' into master

This commit is contained in:
Roman Mogylatov 2020-08-24 13:38:25 -04:00
commit 0f952b5915
9 changed files with 2912 additions and 2480 deletions

View File

@ -7,6 +7,12 @@ that were made in every particular version.
From version 0.7.6 *Dependency Injector* framework strictly
follows `Semantic versioning`_
3.32.0
------
- Add a feature that helps to explicitly specify the type of the configuration option value
before the injection.
- Add disqus comments to the docs page on injecting provided instance attributes, items, etc.
3.31.0
------
- Add a feature that helps to inject provided instance attribute, item, or method call result

View File

@ -96,4 +96,34 @@ where ``examples/providers/configuration/config.local.yml`` is:
.. literalinclude:: ../../examples/providers/configuration/config.local.yml
:language: ini
Specifying value type
~~~~~~~~~~~~~~~~~~~~~
You can specify the type of the injected configuration value explicitly.
This helps when you read the value from the ini file or the environment variable and need to
convert it into an ``int`` or a ``float``.
.. literalinclude:: ../../examples/providers/configuration/configuration_type.py
:language: python
:lines: 3-
:emphasize-lines: 17
:py:class:`Configuration` provider has next helper methods:
- ``.as_int()``
- ``.as_float()``
- ``.as_(callback, *args, **kwargs)``
The last method ``.as_(callback, *args, **kwargs)`` helps to implement a other conversions.
.. literalinclude:: ../../examples/providers/configuration/configuration_type_custom.py
:language: python
:lines: 3-
:emphasize-lines: 16
With the ``.as_(callback, *args, **kwargs)`` you can specify the function that will be called
before the injection. The value from the config will be passed as a first argument. The returned
value will be injected. Parameters ``*args`` and ``**kwargs`` are handled as any other injections.
.. disqus::

View File

@ -62,3 +62,5 @@ should use the :py:class:`ProvidedInstance` provider.
In all other cases you should not use :py:class:`ProvidedInstance`, :py:class:`AttributeGetter`,
:py:class:`ItemGetter`, or :py:class:`MethodCaller` providers directly. Use the ``.provided``
attribute of the injected provider instead.
.. disqus::

View File

@ -0,0 +1,34 @@
"""`Configuration` provider type specification example."""
import os
from dependency_injector import providers
class ApiClient:
def __init__(self, api_key: str, timeout: int):
self.api_key = api_key
self.timeout = timeout
config = providers.Configuration()
api_client_factory = providers.Factory(
ApiClient,
api_key=config.api.key,
timeout=config.api.timeout.as_int(),
)
if __name__ == '__main__':
# Emulate environment variables
os.environ['API_KEY'] = 'secret'
os.environ['API_TIMEOUT'] = '5'
config.api.key.from_env('API_KEY')
config.api.timeout.from_env('API_TIMEOUT')
api_client = api_client_factory()
assert api_client.api_key == 'secret'
assert api_client.timeout == 5

View File

@ -0,0 +1,30 @@
"""`Configuration` provider custom type specification example."""
import os
import decimal
from dependency_injector import providers
class Calculator:
def __init__(self, pi: decimal.Decimal):
self.pi = pi
config = providers.Configuration()
calculator_factory = providers.Factory(
Calculator,
pi=config.pi.as_(decimal.Decimal),
)
if __name__ == '__main__':
# Emulate environment variables
os.environ['PI'] = '3.1415926535897932384626433832'
config.pi.from_env('PI')
calculator = calculator_factory()
assert calculator.pi == decimal.Decimal('3.1415926535897932384626433832')

View File

@ -1,6 +1,6 @@
"""Dependency injector top-level package."""
__version__ = '3.31.0'
__version__ = '3.32.0'
"""Version number that follows semantic versioning.
:type: str

File diff suppressed because it is too large Load Diff

View File

@ -1140,6 +1140,15 @@ cdef class ConfigurationOption(Provider):
root = self.__root_ref()
return '.'.join((root.get_name(), self._get_self_name()))
def as_int(self):
return Callable(int, self)
def as_float(self):
return Callable(float, self)
def as_(self, callback, *args, **kwargs):
return Callable(callback, self, *args, **kwargs)
def override(self, value):
if isinstance(value, Provider):
raise Error('Configuration option can only be overridden by a value')

View File

@ -1,6 +1,7 @@
"""Dependency injector config providers unit tests."""
import contextlib
import decimal
import os
import sys
import tempfile
@ -69,6 +70,33 @@ class ConfigTests(unittest.TestCase):
self.assertEqual(abc(), 1)
self.assertEqual(abd(), 2)
def test_as_int(self):
value_provider = providers.Callable(lambda value: value, self.config.test.as_int())
self.config.from_dict({'test': '123'})
value = value_provider()
self.assertEqual(value, 123)
def test_as_float(self):
value_provider = providers.Callable(lambda value: value, self.config.test.as_float())
self.config.from_dict({'test': '123.123'})
value = value_provider()
self.assertEqual(value, 123.123)
def test_as_(self):
value_provider = providers.Callable(
lambda value: value,
self.config.test.as_(decimal.Decimal),
)
self.config.from_dict({'test': '123.123'})
value = value_provider()
self.assertEqual(value, decimal.Decimal('123.123'))
def test_providers_value_override(self):
a = self.config.a
ab = self.config.a.b
@ -358,7 +386,6 @@ class ConfigFromIniWithEnvInterpolationTests(unittest.TestCase):
self.assertEqual(self.config.section1.value1(), 'test-value')
class ConfigFromYamlTests(unittest.TestCase):
def setUp(self):