mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2024-11-25 19:14:00 +03:00
Configuration option typed injections (#284)
* Add implementation and tests * Add docs page and examples * Revert the api_client miniapp accidental changes
This commit is contained in:
parent
69ebc19b5f
commit
f5b97ca92e
|
@ -96,4 +96,34 @@ where ``examples/providers/configuration/config.local.yml`` is:
|
||||||
.. literalinclude:: ../../examples/providers/configuration/config.local.yml
|
.. literalinclude:: ../../examples/providers/configuration/config.local.yml
|
||||||
:language: ini
|
: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::
|
.. disqus::
|
||||||
|
|
|
@ -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`,
|
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``
|
:py:class:`ItemGetter`, or :py:class:`MethodCaller` providers directly. Use the ``.provided``
|
||||||
attribute of the injected provider instead.
|
attribute of the injected provider instead.
|
||||||
|
|
||||||
|
.. disqus::
|
||||||
|
|
34
examples/providers/configuration/configuration_type.py
Normal file
34
examples/providers/configuration/configuration_type.py
Normal 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
|
|
@ -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')
|
File diff suppressed because it is too large
Load Diff
|
@ -1140,6 +1140,15 @@ cdef class ConfigurationOption(Provider):
|
||||||
root = self.__root_ref()
|
root = self.__root_ref()
|
||||||
return '.'.join((root.get_name(), self._get_self_name()))
|
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):
|
def override(self, value):
|
||||||
if isinstance(value, Provider):
|
if isinstance(value, Provider):
|
||||||
raise Error('Configuration option can only be overridden by a value')
|
raise Error('Configuration option can only be overridden by a value')
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
"""Dependency injector config providers unit tests."""
|
"""Dependency injector config providers unit tests."""
|
||||||
|
|
||||||
import contextlib
|
import contextlib
|
||||||
|
import decimal
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
|
@ -69,6 +70,33 @@ class ConfigTests(unittest.TestCase):
|
||||||
self.assertEqual(abc(), 1)
|
self.assertEqual(abc(), 1)
|
||||||
self.assertEqual(abd(), 2)
|
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):
|
def test_providers_value_override(self):
|
||||||
a = self.config.a
|
a = self.config.a
|
||||||
ab = self.config.a.b
|
ab = self.config.a.b
|
||||||
|
@ -358,7 +386,6 @@ class ConfigFromIniWithEnvInterpolationTests(unittest.TestCase):
|
||||||
self.assertEqual(self.config.section1.value1(), 'test-value')
|
self.assertEqual(self.config.section1.value1(), 'test-value')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigFromYamlTests(unittest.TestCase):
|
class ConfigFromYamlTests(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|
Loading…
Reference in New Issue
Block a user