mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2024-11-29 04:53:59 +03:00
369 Add required argument to config from_* methods (#376)
* Update typing stubs * Update from_yaml() method * Update from_ini() method * Update from_dict() method * Update from_env() method * Update documentation * Update changelog * Update changelog * Make doc block fix * Add extra test for from_ini()
This commit is contained in:
parent
2d49308c16
commit
4cc39fc6eb
|
@ -9,12 +9,18 @@ follows `Semantic versioning`_
|
||||||
|
|
||||||
Development version
|
Development version
|
||||||
-------------------
|
-------------------
|
||||||
- Add ``loader`` argument to the configuration provider ``Configuration.from_yaml(..., loader=...)`` to override the
|
- Add ``loader`` argument to the configuration provider ``Configuration.from_yaml(..., loader=...)``
|
||||||
default YAML loader.
|
to override the default YAML loader.
|
||||||
|
Many thanks to `Stefano Frazzetto <https://github.com/StefanoFrazzetto>`_ for suggesting an improvement.
|
||||||
- Make security improvement: change default YAML loader to the custom ``yaml.SafeLoader`` with a support
|
- Make security improvement: change default YAML loader to the custom ``yaml.SafeLoader`` with a support
|
||||||
of environment variables interpolation.
|
of environment variables interpolation.
|
||||||
|
Many thanks to `Stefano Frazzetto <https://github.com/StefanoFrazzetto>`_ for suggesting an improvement.
|
||||||
- Update configuration provider ``.from_*()`` methods to raise an exception in strict mode if
|
- Update configuration provider ``.from_*()`` methods to raise an exception in strict mode if
|
||||||
configuration file does not exist or configuration data is undefined.
|
configuration file does not exist or configuration data is undefined.
|
||||||
|
Many thanks to `Stefano Frazzetto <https://github.com/StefanoFrazzetto>`_ for suggesting an improvement.
|
||||||
|
- Add ``required`` argument to the configuration provider ``.from_*()`` methods to specify
|
||||||
|
mandatory configuration sources.
|
||||||
|
Many thanks to `Stefano Frazzetto <https://github.com/StefanoFrazzetto>`_ for suggesting an improvement.
|
||||||
- Fix a bug with asynchronous injections: async providers do not work with async dependencies.
|
- Fix a bug with asynchronous injections: async providers do not work with async dependencies.
|
||||||
See issue: `#368 <https://github.com/ets-labs/python-dependency-injector/issues/368>`_.
|
See issue: `#368 <https://github.com/ets-labs/python-dependency-injector/issues/368>`_.
|
||||||
Thanks `@kolypto <https://github.com/kolypto>`_ for the bug report.
|
Thanks `@kolypto <https://github.com/kolypto>`_ for the bug report.
|
||||||
|
|
|
@ -127,6 +127,43 @@ 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
|
||||||
|
|
||||||
|
Mandatory and optional sources
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
By default, methods ``.from_yaml()`` and ``.from_ini()`` ignore errors if configuration file does not exist.
|
||||||
|
You can use this to specify optional configuration files.
|
||||||
|
|
||||||
|
If configuration file is mandatory, use ``required`` argument. Configuration provider will raise an error
|
||||||
|
if required file does not exist.
|
||||||
|
|
||||||
|
You can also use ``required`` argument when loading configuration from dictionaries and environment variables.
|
||||||
|
|
||||||
|
Mandatory YAML file:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
container.config.from_yaml('config.yaml', required=True)
|
||||||
|
|
||||||
|
Mandatory INI file:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
container.config.from_ini('config.ini', required=True)
|
||||||
|
|
||||||
|
Mandatory dictionary:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
container.config.from_dict(config_dict, required=True)
|
||||||
|
|
||||||
|
Mandatory environment variable:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
container.config.api_key.from_env('API_KEY', required=True)
|
||||||
|
|
||||||
|
See also: :ref:`configuration-strict-mode`.
|
||||||
|
|
||||||
Specifying the value type
|
Specifying the value type
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
|
@ -157,6 +194,8 @@ With the ``.as_(callback, *args, **kwargs)`` you can specify a function that wil
|
||||||
before the injection. The value from the config will be passed as a first argument. The returned
|
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.
|
value will be injected. Parameters ``*args`` and ``**kwargs`` are handled as any other injections.
|
||||||
|
|
||||||
|
.. _configuration-strict-mode:
|
||||||
|
|
||||||
Strict mode and required options
|
Strict mode and required options
|
||||||
--------------------------------
|
--------------------------------
|
||||||
|
|
||||||
|
@ -183,12 +222,12 @@ configuration data is undefined:
|
||||||
container = Container()
|
container = Container()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
container.config.from_yaml('./does-not_exist.yml') # raise exception
|
container.config.from_yaml('does-not_exist.yml') # raise exception
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
...
|
...
|
||||||
|
|
||||||
try:
|
try:
|
||||||
container.config.from_ini('./does-not_exist.ini') # raise exception
|
container.config.from_ini('does-not_exist.ini') # raise exception
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
...
|
...
|
||||||
|
|
||||||
|
@ -202,6 +241,21 @@ configuration data is undefined:
|
||||||
except ValueError:
|
except ValueError:
|
||||||
...
|
...
|
||||||
|
|
||||||
|
You can override ``.from_*()`` methods behaviour in strict mode using ``required`` argument:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
class Container(containers.DeclarativeContainer):
|
||||||
|
|
||||||
|
config = providers.Configuration(strict=True)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
container = Container()
|
||||||
|
|
||||||
|
container.config.from_yaml('config.yml')
|
||||||
|
container.config.from_yaml('config.local.yml', required=False)
|
||||||
|
|
||||||
You can also use ``.required()`` option modifier when making an injection. It does not require to switch
|
You can also use ``.required()`` option modifier when making an injection. It does not require to switch
|
||||||
configuration provider to strict mode.
|
configuration provider to strict mode.
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -154,10 +154,10 @@ class ConfigurationOption(Provider[Any]):
|
||||||
def required(self) -> ConfigurationOption: ...
|
def required(self) -> ConfigurationOption: ...
|
||||||
def is_required(self) -> bool: ...
|
def is_required(self) -> bool: ...
|
||||||
def update(self, value: Any) -> None: ...
|
def update(self, value: Any) -> None: ...
|
||||||
def from_ini(self, filepath: Union[Path, str]) -> None: ...
|
def from_ini(self, filepath: Union[Path, str], required: bool = False) -> None: ...
|
||||||
def from_yaml(self, filepath: Union[Path, str], loader: Optional[Any]=None) -> None: ...
|
def from_yaml(self, filepath: Union[Path, str], required: bool = False, loader: Optional[Any]=None) -> None: ...
|
||||||
def from_dict(self, options: _Dict[str, Any]) -> None: ...
|
def from_dict(self, options: _Dict[str, Any], required: bool = False) -> None: ...
|
||||||
def from_env(self, name: str, default: Optional[Any] = None) -> None: ...
|
def from_env(self, name: str, default: Optional[Any] = None, required: bool = False) -> None: ...
|
||||||
|
|
||||||
|
|
||||||
class TypedConfigurationOption(Callable[T]):
|
class TypedConfigurationOption(Callable[T]):
|
||||||
|
@ -175,10 +175,10 @@ class Configuration(Object[Any]):
|
||||||
def set(self, selector: str, value: Any) -> OverridingContext: ...
|
def set(self, selector: str, value: Any) -> OverridingContext: ...
|
||||||
def reset_cache(self) -> None: ...
|
def reset_cache(self) -> None: ...
|
||||||
def update(self, value: Any) -> None: ...
|
def update(self, value: Any) -> None: ...
|
||||||
def from_ini(self, filepath: Union[Path, str]) -> None: ...
|
def from_ini(self, filepath: Union[Path, str], required: bool = False) -> None: ...
|
||||||
def from_yaml(self, filepath: Union[Path, str], loader: Optional[Any]=None) -> None: ...
|
def from_yaml(self, filepath: Union[Path, str], required: bool = False, loader: Optional[Any]=None) -> None: ...
|
||||||
def from_dict(self, options: _Dict[str, Any]) -> None: ...
|
def from_dict(self, options: _Dict[str, Any], required: bool = False) -> None: ...
|
||||||
def from_env(self, name: str, default: Optional[Any] = None) -> None: ...
|
def from_env(self, name: str, default: Optional[Any] = None, required: bool = False) -> None: ...
|
||||||
|
|
||||||
|
|
||||||
class Factory(Provider[T]):
|
class Factory(Provider[T]):
|
||||||
|
|
|
@ -1312,7 +1312,7 @@ cdef class ConfigurationOption(Provider):
|
||||||
"""
|
"""
|
||||||
self.override(value)
|
self.override(value)
|
||||||
|
|
||||||
def from_ini(self, filepath):
|
def from_ini(self, filepath, required=UNDEFINED):
|
||||||
"""Load configuration from the ini file.
|
"""Load configuration from the ini file.
|
||||||
|
|
||||||
Loaded configuration is merged recursively over existing configuration.
|
Loaded configuration is merged recursively over existing configuration.
|
||||||
|
@ -1320,12 +1320,17 @@ cdef class ConfigurationOption(Provider):
|
||||||
:param filepath: Path to the configuration file.
|
:param filepath: Path to the configuration file.
|
||||||
:type filepath: str
|
:type filepath: str
|
||||||
|
|
||||||
|
:param required: When required is True, raise an exception if file does not exist.
|
||||||
|
:type required: bool
|
||||||
|
|
||||||
:rtype: None
|
:rtype: None
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
parser = _parse_ini_file(filepath)
|
parser = _parse_ini_file(filepath)
|
||||||
except IOError as exception:
|
except IOError as exception:
|
||||||
if self._is_strict_mode_enabled() and exception.errno in (errno.ENOENT, errno.EISDIR):
|
if required is not False \
|
||||||
|
and (self._is_strict_mode_enabled() or required is True) \
|
||||||
|
and exception.errno in (errno.ENOENT, errno.EISDIR):
|
||||||
exception.strerror = 'Unable to load configuration file {0}'.format(exception.strerror)
|
exception.strerror = 'Unable to load configuration file {0}'.format(exception.strerror)
|
||||||
raise
|
raise
|
||||||
return
|
return
|
||||||
|
@ -1339,7 +1344,7 @@ cdef class ConfigurationOption(Provider):
|
||||||
current_config = {}
|
current_config = {}
|
||||||
self.override(merge_dicts(current_config, config))
|
self.override(merge_dicts(current_config, config))
|
||||||
|
|
||||||
def from_yaml(self, filepath, loader=None):
|
def from_yaml(self, filepath, required=UNDEFINED, loader=None):
|
||||||
"""Load configuration from the yaml file.
|
"""Load configuration from the yaml file.
|
||||||
|
|
||||||
Loaded configuration is merged recursively over existing configuration.
|
Loaded configuration is merged recursively over existing configuration.
|
||||||
|
@ -1347,6 +1352,9 @@ cdef class ConfigurationOption(Provider):
|
||||||
:param filepath: Path to the configuration file.
|
:param filepath: Path to the configuration file.
|
||||||
:type filepath: str
|
:type filepath: str
|
||||||
|
|
||||||
|
:param required: When required is True, raise an exception if file does not exist.
|
||||||
|
:type required: bool
|
||||||
|
|
||||||
:param loader: YAML loader, :py:class:`YamlLoader` is used if not specified.
|
:param loader: YAML loader, :py:class:`YamlLoader` is used if not specified.
|
||||||
:type loader: ``yaml.Loader``
|
:type loader: ``yaml.Loader``
|
||||||
|
|
||||||
|
@ -1367,7 +1375,9 @@ cdef class ConfigurationOption(Provider):
|
||||||
with open(filepath) as opened_file:
|
with open(filepath) as opened_file:
|
||||||
config = yaml.load(opened_file, loader)
|
config = yaml.load(opened_file, loader)
|
||||||
except IOError as exception:
|
except IOError as exception:
|
||||||
if self._is_strict_mode_enabled() and exception.errno in (errno.ENOENT, errno.EISDIR):
|
if required is not False \
|
||||||
|
and (self._is_strict_mode_enabled() or required is True) \
|
||||||
|
and exception.errno in (errno.ENOENT, errno.EISDIR):
|
||||||
exception.strerror = 'Unable to load configuration file {0}'.format(exception.strerror)
|
exception.strerror = 'Unable to load configuration file {0}'.format(exception.strerror)
|
||||||
raise
|
raise
|
||||||
return
|
return
|
||||||
|
@ -1377,7 +1387,7 @@ cdef class ConfigurationOption(Provider):
|
||||||
current_config = {}
|
current_config = {}
|
||||||
self.override(merge_dicts(current_config, config))
|
self.override(merge_dicts(current_config, config))
|
||||||
|
|
||||||
def from_dict(self, options):
|
def from_dict(self, options, required=UNDEFINED):
|
||||||
"""Load configuration from the dictionary.
|
"""Load configuration from the dictionary.
|
||||||
|
|
||||||
Loaded configuration is merged recursively over existing configuration.
|
Loaded configuration is merged recursively over existing configuration.
|
||||||
|
@ -1385,17 +1395,27 @@ cdef class ConfigurationOption(Provider):
|
||||||
:param options: Configuration options.
|
:param options: Configuration options.
|
||||||
:type options: dict
|
:type options: dict
|
||||||
|
|
||||||
|
:param required: When required is True, raise an exception if dictionary is empty.
|
||||||
|
:type required: bool
|
||||||
|
|
||||||
:rtype: None
|
:rtype: None
|
||||||
"""
|
"""
|
||||||
if self._is_strict_mode_enabled() and not options:
|
if required is not False \
|
||||||
|
and (self._is_strict_mode_enabled() or required is True) \
|
||||||
|
and not options:
|
||||||
raise ValueError('Can not use empty dictionary')
|
raise ValueError('Can not use empty dictionary')
|
||||||
|
|
||||||
|
try:
|
||||||
current_config = self.__call__()
|
current_config = self.__call__()
|
||||||
|
except Error:
|
||||||
|
current_config = {}
|
||||||
|
else:
|
||||||
if not current_config:
|
if not current_config:
|
||||||
current_config = {}
|
current_config = {}
|
||||||
|
|
||||||
self.override(merge_dicts(current_config, options))
|
self.override(merge_dicts(current_config, options))
|
||||||
|
|
||||||
def from_env(self, name, default=UNDEFINED):
|
def from_env(self, name, default=UNDEFINED, required=UNDEFINED):
|
||||||
"""Load configuration value from the environment variable.
|
"""Load configuration value from the environment variable.
|
||||||
|
|
||||||
:param name: Name of the environment variable.
|
:param name: Name of the environment variable.
|
||||||
|
@ -1404,12 +1424,16 @@ cdef class ConfigurationOption(Provider):
|
||||||
:param default: Default value that is used if environment variable does not exist.
|
:param default: Default value that is used if environment variable does not exist.
|
||||||
:type default: object
|
:type default: object
|
||||||
|
|
||||||
|
:param required: When required is True, raise an exception if environment variable is undefined.
|
||||||
|
:type required: bool
|
||||||
|
|
||||||
:rtype: None
|
:rtype: None
|
||||||
"""
|
"""
|
||||||
value = os.environ.get(name, default)
|
value = os.environ.get(name, default)
|
||||||
|
|
||||||
if value is UNDEFINED:
|
if value is UNDEFINED:
|
||||||
if self._is_strict_mode_enabled():
|
if required is not False \
|
||||||
|
and (self._is_strict_mode_enabled() or required is True):
|
||||||
raise ValueError('Environment variable "{0}" is undefined'.format(name))
|
raise ValueError('Environment variable "{0}" is undefined'.format(name))
|
||||||
value = None
|
value = None
|
||||||
|
|
||||||
|
@ -1621,7 +1645,7 @@ cdef class Configuration(Object):
|
||||||
"""
|
"""
|
||||||
self.override(value)
|
self.override(value)
|
||||||
|
|
||||||
def from_ini(self, filepath):
|
def from_ini(self, filepath, required=UNDEFINED):
|
||||||
"""Load configuration from the ini file.
|
"""Load configuration from the ini file.
|
||||||
|
|
||||||
Loaded configuration is merged recursively over existing configuration.
|
Loaded configuration is merged recursively over existing configuration.
|
||||||
|
@ -1629,12 +1653,17 @@ cdef class Configuration(Object):
|
||||||
:param filepath: Path to the configuration file.
|
:param filepath: Path to the configuration file.
|
||||||
:type filepath: str
|
:type filepath: str
|
||||||
|
|
||||||
|
:param required: When required is True, raise an exception if file does not exist.
|
||||||
|
:type required: bool
|
||||||
|
|
||||||
:rtype: None
|
:rtype: None
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
parser = _parse_ini_file(filepath)
|
parser = _parse_ini_file(filepath)
|
||||||
except IOError as exception:
|
except IOError as exception:
|
||||||
if self._is_strict_mode_enabled() and exception.errno in (errno.ENOENT, errno.EISDIR):
|
if required is not False \
|
||||||
|
and (self._is_strict_mode_enabled() or required is True) \
|
||||||
|
and exception.errno in (errno.ENOENT, errno.EISDIR):
|
||||||
exception.strerror = 'Unable to load configuration file {0}'.format(exception.strerror)
|
exception.strerror = 'Unable to load configuration file {0}'.format(exception.strerror)
|
||||||
raise
|
raise
|
||||||
return
|
return
|
||||||
|
@ -1648,7 +1677,7 @@ cdef class Configuration(Object):
|
||||||
current_config = {}
|
current_config = {}
|
||||||
self.override(merge_dicts(current_config, config))
|
self.override(merge_dicts(current_config, config))
|
||||||
|
|
||||||
def from_yaml(self, filepath, loader=None):
|
def from_yaml(self, filepath, required=UNDEFINED, loader=None):
|
||||||
"""Load configuration from the yaml file.
|
"""Load configuration from the yaml file.
|
||||||
|
|
||||||
Loaded configuration is merged recursively over existing configuration.
|
Loaded configuration is merged recursively over existing configuration.
|
||||||
|
@ -1656,6 +1685,9 @@ cdef class Configuration(Object):
|
||||||
:param filepath: Path to the configuration file.
|
:param filepath: Path to the configuration file.
|
||||||
:type filepath: str
|
:type filepath: str
|
||||||
|
|
||||||
|
:param required: When required is True, raise an exception if file does not exist.
|
||||||
|
:type required: bool
|
||||||
|
|
||||||
:param loader: YAML loader, :py:class:`YamlLoader` is used if not specified.
|
:param loader: YAML loader, :py:class:`YamlLoader` is used if not specified.
|
||||||
:type loader: ``yaml.Loader``
|
:type loader: ``yaml.Loader``
|
||||||
|
|
||||||
|
@ -1675,7 +1707,9 @@ cdef class Configuration(Object):
|
||||||
with open(filepath) as opened_file:
|
with open(filepath) as opened_file:
|
||||||
config = yaml.load(opened_file, loader)
|
config = yaml.load(opened_file, loader)
|
||||||
except IOError as exception:
|
except IOError as exception:
|
||||||
if self._is_strict_mode_enabled() and exception.errno in (errno.ENOENT, errno.EISDIR):
|
if required is not False \
|
||||||
|
and (self._is_strict_mode_enabled() or required is True) \
|
||||||
|
and exception.errno in (errno.ENOENT, errno.EISDIR):
|
||||||
exception.strerror = 'Unable to load configuration file {0}'.format(exception.strerror)
|
exception.strerror = 'Unable to load configuration file {0}'.format(exception.strerror)
|
||||||
raise
|
raise
|
||||||
return
|
return
|
||||||
|
@ -1685,7 +1719,7 @@ cdef class Configuration(Object):
|
||||||
current_config = {}
|
current_config = {}
|
||||||
self.override(merge_dicts(current_config, config))
|
self.override(merge_dicts(current_config, config))
|
||||||
|
|
||||||
def from_dict(self, options):
|
def from_dict(self, options, required=UNDEFINED):
|
||||||
"""Load configuration from the dictionary.
|
"""Load configuration from the dictionary.
|
||||||
|
|
||||||
Loaded configuration is merged recursively over existing configuration.
|
Loaded configuration is merged recursively over existing configuration.
|
||||||
|
@ -1693,9 +1727,14 @@ cdef class Configuration(Object):
|
||||||
:param options: Configuration options.
|
:param options: Configuration options.
|
||||||
:type options: dict
|
:type options: dict
|
||||||
|
|
||||||
|
:param required: When required is True, raise an exception if dictionary is empty.
|
||||||
|
:type required: bool
|
||||||
|
|
||||||
:rtype: None
|
:rtype: None
|
||||||
"""
|
"""
|
||||||
if self._is_strict_mode_enabled() and not options:
|
if required is not False \
|
||||||
|
and (self._is_strict_mode_enabled() or required is True) \
|
||||||
|
and not options:
|
||||||
raise ValueError('Can not use empty dictionary')
|
raise ValueError('Can not use empty dictionary')
|
||||||
|
|
||||||
current_config = self.__call__()
|
current_config = self.__call__()
|
||||||
|
@ -1703,7 +1742,7 @@ cdef class Configuration(Object):
|
||||||
current_config = {}
|
current_config = {}
|
||||||
self.override(merge_dicts(current_config, options))
|
self.override(merge_dicts(current_config, options))
|
||||||
|
|
||||||
def from_env(self, name, default=UNDEFINED):
|
def from_env(self, name, default=UNDEFINED, required=UNDEFINED):
|
||||||
"""Load configuration value from the environment variable.
|
"""Load configuration value from the environment variable.
|
||||||
|
|
||||||
:param name: Name of the environment variable.
|
:param name: Name of the environment variable.
|
||||||
|
@ -1712,12 +1751,16 @@ cdef class Configuration(Object):
|
||||||
:param default: Default value that is used if environment variable does not exist.
|
:param default: Default value that is used if environment variable does not exist.
|
||||||
:type default: object
|
:type default: object
|
||||||
|
|
||||||
|
:param required: When required is True, raise an exception if environment variable is undefined.
|
||||||
|
:type required: bool
|
||||||
|
|
||||||
:rtype: None
|
:rtype: None
|
||||||
"""
|
"""
|
||||||
value = os.environ.get(name, default)
|
value = os.environ.get(name, default)
|
||||||
|
|
||||||
if value is UNDEFINED:
|
if value is UNDEFINED:
|
||||||
if self._is_strict_mode_enabled():
|
if required is not False \
|
||||||
|
and (self._is_strict_mode_enabled() or required is True):
|
||||||
raise ValueError('Environment variable "{0}" is undefined'.format(name))
|
raise ValueError('Environment variable "{0}" is undefined'.format(name))
|
||||||
value = None
|
value = None
|
||||||
|
|
||||||
|
|
|
@ -399,6 +399,16 @@ class ConfigFromIniTests(unittest.TestCase):
|
||||||
self.assertEqual(self.config.section2(), {'value2': '2'})
|
self.assertEqual(self.config.section2(), {'value2': '2'})
|
||||||
self.assertEqual(self.config.section2.value2(), '2')
|
self.assertEqual(self.config.section2.value2(), '2')
|
||||||
|
|
||||||
|
def test_option(self):
|
||||||
|
self.config.option.from_ini(self.config_file_1)
|
||||||
|
|
||||||
|
self.assertEqual(self.config(), {'option': {'section1': {'value1': '1'}, 'section2': {'value2': '2'}}})
|
||||||
|
self.assertEqual(self.config.option(), {'section1': {'value1': '1'}, 'section2': {'value2': '2'}})
|
||||||
|
self.assertEqual(self.config.option.section1(), {'value1': '1'})
|
||||||
|
self.assertEqual(self.config.option.section1.value1(), '1')
|
||||||
|
self.assertEqual(self.config.option.section2(), {'value2': '2'})
|
||||||
|
self.assertEqual(self.config.option.section2.value2(), '2')
|
||||||
|
|
||||||
def test_merge(self):
|
def test_merge(self):
|
||||||
self.config.from_ini(self.config_file_1)
|
self.config.from_ini(self.config_file_1)
|
||||||
self.config.from_ini(self.config_file_2)
|
self.config.from_ini(self.config_file_2)
|
||||||
|
@ -444,6 +454,25 @@ class ConfigFromIniTests(unittest.TestCase):
|
||||||
with self.assertRaises(IOError):
|
with self.assertRaises(IOError):
|
||||||
self.config.option.from_ini('./does_not_exist.ini')
|
self.config.option.from_ini('./does_not_exist.ini')
|
||||||
|
|
||||||
|
def test_required_file_does_not_exist(self):
|
||||||
|
with self.assertRaises(IOError):
|
||||||
|
self.config.from_ini('./does_not_exist.ini', required=True)
|
||||||
|
|
||||||
|
def test_required_option_file_does_not_exist(self):
|
||||||
|
with self.assertRaises(IOError):
|
||||||
|
self.config.option.from_ini('./does_not_exist.ini', required=True)
|
||||||
|
|
||||||
|
def test_not_required_file_does_not_exist_strict_mode(self):
|
||||||
|
self.config = providers.Configuration(strict=True)
|
||||||
|
self.config.from_ini('./does_not_exist.ini', required=False)
|
||||||
|
self.assertEqual(self.config(), {})
|
||||||
|
|
||||||
|
def test_not_required_option_file_does_not_exist_strict_mode(self):
|
||||||
|
self.config = providers.Configuration(strict=True)
|
||||||
|
self.config.option.from_ini('./does_not_exist.ini', required=False)
|
||||||
|
with self.assertRaises(errors.Error):
|
||||||
|
self.config.option()
|
||||||
|
|
||||||
|
|
||||||
class ConfigFromIniWithEnvInterpolationTests(unittest.TestCase):
|
class ConfigFromIniWithEnvInterpolationTests(unittest.TestCase):
|
||||||
|
|
||||||
|
@ -565,6 +594,25 @@ class ConfigFromYamlTests(unittest.TestCase):
|
||||||
with self.assertRaises(IOError):
|
with self.assertRaises(IOError):
|
||||||
self.config.option.from_yaml('./does_not_exist.yml')
|
self.config.option.from_yaml('./does_not_exist.yml')
|
||||||
|
|
||||||
|
def test_required_file_does_not_exist(self):
|
||||||
|
with self.assertRaises(IOError):
|
||||||
|
self.config.from_yaml('./does_not_exist.yml', required=True)
|
||||||
|
|
||||||
|
def test_required_option_file_does_not_exist(self):
|
||||||
|
with self.assertRaises(IOError):
|
||||||
|
self.config.option.from_yaml('./does_not_exist.yml', required=True)
|
||||||
|
|
||||||
|
def test_not_required_file_does_not_exist_strict_mode(self):
|
||||||
|
self.config = providers.Configuration(strict=True)
|
||||||
|
self.config.from_yaml('./does_not_exist.yml', required=False)
|
||||||
|
self.assertEqual(self.config(), {})
|
||||||
|
|
||||||
|
def test_not_required_option_file_does_not_exist_strict_mode(self):
|
||||||
|
self.config = providers.Configuration(strict=True)
|
||||||
|
self.config.option.from_yaml('./does_not_exist.yml', required=False)
|
||||||
|
with self.assertRaises(errors.Error):
|
||||||
|
self.config.option()
|
||||||
|
|
||||||
def test_no_yaml_installed(self):
|
def test_no_yaml_installed(self):
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def no_yaml_module():
|
def no_yaml_module():
|
||||||
|
@ -699,24 +747,6 @@ class ConfigFromDict(unittest.TestCase):
|
||||||
self.assertEqual(self.config.section2(), {'value2': '2'})
|
self.assertEqual(self.config.section2(), {'value2': '2'})
|
||||||
self.assertEqual(self.config.section2.value2(), '2')
|
self.assertEqual(self.config.section2.value2(), '2')
|
||||||
|
|
||||||
def test_empty_dict(self):
|
|
||||||
self.config.from_dict({})
|
|
||||||
self.assertEqual(self.config(), {})
|
|
||||||
|
|
||||||
def test_option_empty_dict(self):
|
|
||||||
self.config.option.from_dict({})
|
|
||||||
self.assertEqual(self.config.option(), {})
|
|
||||||
|
|
||||||
def test_empty_dict_in_strict_mode(self):
|
|
||||||
self.config = providers.Configuration(strict=True)
|
|
||||||
with self.assertRaises(ValueError):
|
|
||||||
self.config.from_dict({})
|
|
||||||
|
|
||||||
def test_option_empty_dict_in_strict_mode(self):
|
|
||||||
self.config = providers.Configuration(strict=True)
|
|
||||||
with self.assertRaises(ValueError):
|
|
||||||
self.config.option.from_dict({})
|
|
||||||
|
|
||||||
def test_merge(self):
|
def test_merge(self):
|
||||||
self.config.from_dict(self.config_options_1)
|
self.config.from_dict(self.config_options_1)
|
||||||
self.config.from_dict(self.config_options_2)
|
self.config.from_dict(self.config_options_2)
|
||||||
|
@ -744,6 +774,43 @@ class ConfigFromDict(unittest.TestCase):
|
||||||
self.assertEqual(self.config.section3(), {'value3': '3'})
|
self.assertEqual(self.config.section3(), {'value3': '3'})
|
||||||
self.assertEqual(self.config.section3.value3(), '3')
|
self.assertEqual(self.config.section3.value3(), '3')
|
||||||
|
|
||||||
|
def test_empty_dict(self):
|
||||||
|
self.config.from_dict({})
|
||||||
|
self.assertEqual(self.config(), {})
|
||||||
|
|
||||||
|
def test_option_empty_dict(self):
|
||||||
|
self.config.option.from_dict({})
|
||||||
|
self.assertEqual(self.config.option(), {})
|
||||||
|
|
||||||
|
def test_empty_dict_in_strict_mode(self):
|
||||||
|
self.config = providers.Configuration(strict=True)
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
self.config.from_dict({})
|
||||||
|
|
||||||
|
def test_option_empty_dict_in_strict_mode(self):
|
||||||
|
self.config = providers.Configuration(strict=True)
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
self.config.option.from_dict({})
|
||||||
|
|
||||||
|
def test_required_empty_dict(self):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
self.config.from_dict({}, required=True)
|
||||||
|
|
||||||
|
def test_required_option_empty_dict(self):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
self.config.option.from_dict({}, required=True)
|
||||||
|
|
||||||
|
def test_not_required_empty_dict_strict_mode(self):
|
||||||
|
self.config = providers.Configuration(strict=True)
|
||||||
|
self.config.from_dict({}, required=False)
|
||||||
|
self.assertEqual(self.config(), {})
|
||||||
|
|
||||||
|
def test_not_required_option_empty_dict_strict_mode(self):
|
||||||
|
self.config = providers.Configuration(strict=True)
|
||||||
|
self.config.option.from_dict({}, required=False)
|
||||||
|
self.assertEqual(self.config.option(), {})
|
||||||
|
self.assertEqual(self.config(), {'option': {}})
|
||||||
|
|
||||||
|
|
||||||
class ConfigFromEnvTests(unittest.TestCase):
|
class ConfigFromEnvTests(unittest.TestCase):
|
||||||
|
|
||||||
|
@ -759,10 +826,25 @@ class ConfigFromEnvTests(unittest.TestCase):
|
||||||
self.config.from_env('CONFIG_TEST_ENV')
|
self.config.from_env('CONFIG_TEST_ENV')
|
||||||
self.assertEqual(self.config(), 'test-value')
|
self.assertEqual(self.config(), 'test-value')
|
||||||
|
|
||||||
|
def test_with_children(self):
|
||||||
|
self.config.section1.value1.from_env('CONFIG_TEST_ENV')
|
||||||
|
|
||||||
|
self.assertEqual(self.config(), {'section1': {'value1': 'test-value'}})
|
||||||
|
self.assertEqual(self.config.section1(), {'value1': 'test-value'})
|
||||||
|
self.assertEqual(self.config.section1.value1(), 'test-value')
|
||||||
|
|
||||||
def test_default(self):
|
def test_default(self):
|
||||||
self.config.from_env('UNDEFINED_ENV', 'default-value')
|
self.config.from_env('UNDEFINED_ENV', 'default-value')
|
||||||
self.assertEqual(self.config(), 'default-value')
|
self.assertEqual(self.config(), 'default-value')
|
||||||
|
|
||||||
|
def test_default_none(self):
|
||||||
|
self.config.from_env('UNDEFINED_ENV')
|
||||||
|
self.assertIsNone(self.config())
|
||||||
|
|
||||||
|
def test_option_default_none(self):
|
||||||
|
self.config.option.from_env('UNDEFINED_ENV')
|
||||||
|
self.assertIsNone(self.config.option())
|
||||||
|
|
||||||
def test_undefined_in_strict_mode(self):
|
def test_undefined_in_strict_mode(self):
|
||||||
self.config = providers.Configuration(strict=True)
|
self.config = providers.Configuration(strict=True)
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
|
@ -783,17 +865,38 @@ class ConfigFromEnvTests(unittest.TestCase):
|
||||||
self.config.option.from_env('UNDEFINED_ENV', 'default-value')
|
self.config.option.from_env('UNDEFINED_ENV', 'default-value')
|
||||||
self.assertEqual(self.config.option(), 'default-value')
|
self.assertEqual(self.config.option(), 'default-value')
|
||||||
|
|
||||||
def test_default_none(self):
|
def test_required_undefined(self):
|
||||||
self.config.from_env('UNDEFINED_ENV')
|
with self.assertRaises(ValueError):
|
||||||
|
self.config.from_env('UNDEFINED_ENV', required=True)
|
||||||
|
|
||||||
|
def test_required_undefined_with_default(self):
|
||||||
|
self.config.from_env('UNDEFINED_ENV', default='default-value', required=True)
|
||||||
|
self.assertEqual(self.config(), 'default-value')
|
||||||
|
|
||||||
|
def test_option_required_undefined(self):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
self.config.option.from_env('UNDEFINED_ENV', required=True)
|
||||||
|
|
||||||
|
def test_option_required_undefined_with_default(self):
|
||||||
|
self.config.option.from_env('UNDEFINED_ENV', default='default-value', required=True)
|
||||||
|
self.assertEqual(self.config.option(), 'default-value')
|
||||||
|
|
||||||
|
def test_not_required_undefined_in_strict_mode(self):
|
||||||
|
self.config = providers.Configuration(strict=True)
|
||||||
|
self.config.from_env('UNDEFINED_ENV', required=False)
|
||||||
self.assertIsNone(self.config())
|
self.assertIsNone(self.config())
|
||||||
|
|
||||||
def test_option_default_none(self):
|
def test_option_not_required_undefined_in_strict_mode(self):
|
||||||
self.config.option.from_env('UNDEFINED_ENV')
|
self.config = providers.Configuration(strict=True)
|
||||||
|
self.config.option.from_env('UNDEFINED_ENV', required=False)
|
||||||
self.assertIsNone(self.config.option())
|
self.assertIsNone(self.config.option())
|
||||||
|
|
||||||
def test_with_children(self):
|
def test_not_required_undefined_with_default_in_strict_mode(self):
|
||||||
self.config.section1.value1.from_env('CONFIG_TEST_ENV')
|
self.config = providers.Configuration(strict=True)
|
||||||
|
self.config.from_env('UNDEFINED_ENV', default='default-value', required=False)
|
||||||
|
self.assertEqual(self.config(), 'default-value')
|
||||||
|
|
||||||
self.assertEqual(self.config(), {'section1': {'value1': 'test-value'}})
|
def test_option_not_required_undefined_with_default_in_strict_mode(self):
|
||||||
self.assertEqual(self.config.section1(), {'value1': 'test-value'})
|
self.config = providers.Configuration(strict=True)
|
||||||
self.assertEqual(self.config.section1.value1(), 'test-value')
|
self.config.option.from_env('UNDEFINED_ENV', default='default-value', required=False)
|
||||||
|
self.assertEqual(self.config.option(), 'default-value')
|
||||||
|
|
Loading…
Reference in New Issue
Block a user