mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2025-07-17 11:32:21 +03:00
Add safe loader with env interpolation and an arg to provide custom loader
This commit is contained in:
parent
582c232790
commit
0e55a59240
File diff suppressed because it is too large
Load Diff
|
@ -20,6 +20,11 @@ from typing import (
|
|||
overload,
|
||||
)
|
||||
|
||||
try:
|
||||
import yaml
|
||||
except ImportError:
|
||||
yaml = None
|
||||
|
||||
from . import resources
|
||||
|
||||
|
||||
|
@ -150,7 +155,7 @@ class ConfigurationOption(Provider[Any]):
|
|||
def is_required(self) -> bool: ...
|
||||
def update(self, value: Any) -> None: ...
|
||||
def from_ini(self, filepath: Union[Path, str]) -> None: ...
|
||||
def from_yaml(self, filepath: Union[Path, str]) -> None: ...
|
||||
def from_yaml(self, filepath: Union[Path, str], loader: Optional[Any]=None) -> None: ...
|
||||
def from_dict(self, options: _Dict[str, Any]) -> None: ...
|
||||
def from_env(self, name: str, default: Optional[Any] = None) -> None: ...
|
||||
|
||||
|
@ -171,7 +176,7 @@ class Configuration(Object[Any]):
|
|||
def reset_cache(self) -> None: ...
|
||||
def update(self, value: Any) -> None: ...
|
||||
def from_ini(self, filepath: Union[Path, str]) -> None: ...
|
||||
def from_yaml(self, filepath: Union[Path, str]) -> None: ...
|
||||
def from_yaml(self, filepath: Union[Path, str], loader: Optional[Any]=None) -> None: ...
|
||||
def from_dict(self, options: _Dict[str, Any]) -> None: ...
|
||||
def from_env(self, name: str, default: Optional[Any] = None) -> None: ...
|
||||
|
||||
|
@ -373,3 +378,9 @@ def deepcopy(instance: Any, memo: Optional[_Dict[Any, Any]] = None): Any: ...
|
|||
|
||||
|
||||
def merge_dicts(dict1: _Dict[Any, Any], dict2: _Dict[Any, Any]) -> _Dict[Any, Any]: ...
|
||||
|
||||
|
||||
if yaml:
|
||||
class YamlLoader(yaml.SafeLoader): ...
|
||||
else:
|
||||
class YamlLoader: ...
|
||||
|
|
|
@ -89,6 +89,41 @@ else:
|
|||
return parser
|
||||
|
||||
|
||||
if yaml:
|
||||
class YamlLoader(yaml.SafeLoader):
|
||||
"""Custom YAML loader.
|
||||
|
||||
Inherits ``yaml.SafeLoader`` and add environment variables interpolation.
|
||||
"""
|
||||
|
||||
tag = '!!str'
|
||||
pattern = re.compile('.*?\${(\w+)}.*?')
|
||||
|
||||
@classmethod
|
||||
def constructor_env_variables(cls, loader, node):
|
||||
value = loader.construct_scalar(node)
|
||||
match = cls.pattern.findall(value)
|
||||
if match:
|
||||
full_value = value
|
||||
for group in match:
|
||||
full_value = full_value.replace(
|
||||
f'${{{group}}}', os.environ.get(group, group)
|
||||
)
|
||||
return full_value
|
||||
return value
|
||||
|
||||
# TODO: use SafeLoader without env interpolation by default in version 5.*
|
||||
YamlLoader.add_implicit_resolver(YamlLoader.tag, YamlLoader.pattern, None)
|
||||
YamlLoader.add_constructor(YamlLoader.tag, YamlLoader.constructor_env_variables)
|
||||
else:
|
||||
class YamlLoader:
|
||||
"""Custom YAML loader.
|
||||
|
||||
Inherits ``yaml.SafeLoader`` and add environment variables interpolation.
|
||||
"""
|
||||
|
||||
|
||||
|
||||
cdef int ASYNC_MODE_UNDEFINED = 0
|
||||
cdef int ASYNC_MODE_ENABLED = 1
|
||||
cdef int ASYNC_MODE_DISABLED = 2
|
||||
|
@ -1317,7 +1352,7 @@ cdef class ConfigurationOption(Provider):
|
|||
current_config = {}
|
||||
self.override(merge_dicts(current_config, config))
|
||||
|
||||
def from_yaml(self, filepath):
|
||||
def from_yaml(self, filepath, loader=None):
|
||||
"""Load configuration from the yaml file.
|
||||
|
||||
Loaded configuration is merged recursively over existing configuration.
|
||||
|
@ -1325,6 +1360,9 @@ cdef class ConfigurationOption(Provider):
|
|||
:param filepath: Path to the configuration file.
|
||||
:type filepath: str
|
||||
|
||||
:param loader: YAML loader, :py:class:`YamlLoader` is used if not specified.
|
||||
:type loader: ``yaml.Loader``
|
||||
|
||||
:rtype: None
|
||||
"""
|
||||
if yaml is None:
|
||||
|
@ -1334,9 +1372,13 @@ cdef class ConfigurationOption(Provider):
|
|||
'"pip install dependency-injector[yaml]"'
|
||||
)
|
||||
|
||||
|
||||
if loader is None:
|
||||
loader = YamlLoader
|
||||
|
||||
try:
|
||||
with open(filepath) as opened_file:
|
||||
config = yaml.load(opened_file, yaml.Loader)
|
||||
config = yaml.load(opened_file, loader)
|
||||
except IOError:
|
||||
return
|
||||
|
||||
|
@ -1593,7 +1635,7 @@ cdef class Configuration(Object):
|
|||
current_config = {}
|
||||
self.override(merge_dicts(current_config, config))
|
||||
|
||||
def from_yaml(self, filepath):
|
||||
def from_yaml(self, filepath, loader=None):
|
||||
"""Load configuration from the yaml file.
|
||||
|
||||
Loaded configuration is merged recursively over existing configuration.
|
||||
|
@ -1601,6 +1643,9 @@ cdef class Configuration(Object):
|
|||
:param filepath: Path to the configuration file.
|
||||
:type filepath: str
|
||||
|
||||
:param loader: YAML loader, :py:class:`YamlLoader` is used if not specified.
|
||||
:type loader: ``yaml.Loader``
|
||||
|
||||
:rtype: None
|
||||
"""
|
||||
if yaml is None:
|
||||
|
@ -1610,9 +1655,12 @@ cdef class Configuration(Object):
|
|||
'"pip install dependency-injector[yaml]"'
|
||||
)
|
||||
|
||||
if loader is None:
|
||||
loader = YamlLoader
|
||||
|
||||
try:
|
||||
with open(filepath) as opened_file:
|
||||
config = yaml.load(opened_file, yaml.Loader)
|
||||
config = yaml.load(opened_file, loader)
|
||||
except IOError:
|
||||
return
|
||||
|
||||
|
|
|
@ -9,6 +9,10 @@ import tempfile
|
|||
import unittest2 as unittest
|
||||
|
||||
from dependency_injector import containers, providers, errors
|
||||
try:
|
||||
import yaml
|
||||
except ImportError:
|
||||
yaml = None
|
||||
|
||||
|
||||
class ConfigTests(unittest.TestCase):
|
||||
|
@ -581,6 +585,51 @@ class ConfigFromYamlWithEnvInterpolationTests(unittest.TestCase):
|
|||
self.assertEqual(self.config.section1(), {'value1': 'test-value'})
|
||||
self.assertEqual(self.config.section1.value1(), 'test-value')
|
||||
|
||||
@unittest.skipIf(sys.version_info[:2] == (3, 4), 'PyYAML does not support Python 3.4')
|
||||
def test_option_env_variable_interpolation(self):
|
||||
self.config.option.from_yaml(self.config_file)
|
||||
|
||||
self.assertEqual(
|
||||
self.config.option(),
|
||||
{
|
||||
'section1': {
|
||||
'value1': 'test-value',
|
||||
},
|
||||
},
|
||||
)
|
||||
self.assertEqual(self.config.option.section1(), {'value1': 'test-value'})
|
||||
self.assertEqual(self.config.option.section1.value1(), 'test-value')
|
||||
|
||||
@unittest.skipIf(sys.version_info[:2] == (3, 4), 'PyYAML does not support Python 3.4')
|
||||
def test_env_variable_interpolation_custom_loader(self):
|
||||
self.config.from_yaml(self.config_file, loader=yaml.UnsafeLoader)
|
||||
|
||||
self.assertEqual(
|
||||
self.config(),
|
||||
{
|
||||
'section1': {
|
||||
'value1': 'test-value',
|
||||
},
|
||||
},
|
||||
)
|
||||
self.assertEqual(self.config.section1(), {'value1': 'test-value'})
|
||||
self.assertEqual(self.config.section1.value1(), 'test-value')
|
||||
|
||||
@unittest.skipIf(sys.version_info[:2] == (3, 4), 'PyYAML does not support Python 3.4')
|
||||
def test_option_env_variable_interpolation_custom_loader(self):
|
||||
self.config.option.from_yaml(self.config_file, loader=yaml.UnsafeLoader)
|
||||
|
||||
self.assertEqual(
|
||||
self.config.option(),
|
||||
{
|
||||
'section1': {
|
||||
'value1': 'test-value',
|
||||
},
|
||||
},
|
||||
)
|
||||
self.assertEqual(self.config.option.section1(), {'value1': 'test-value'})
|
||||
self.assertEqual(self.config.option.section1.value1(), 'test-value')
|
||||
|
||||
|
||||
class ConfigFromDict(unittest.TestCase):
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user