mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2025-07-19 20:42:24 +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,
|
overload,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
import yaml
|
||||||
|
except ImportError:
|
||||||
|
yaml = None
|
||||||
|
|
||||||
from . import resources
|
from . import resources
|
||||||
|
|
||||||
|
|
||||||
|
@ -150,7 +155,7 @@ class ConfigurationOption(Provider[Any]):
|
||||||
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]) -> 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_dict(self, options: _Dict[str, Any]) -> None: ...
|
||||||
def from_env(self, name: str, default: Optional[Any] = None) -> 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 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]) -> 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_dict(self, options: _Dict[str, Any]) -> None: ...
|
||||||
def from_env(self, name: str, default: Optional[Any] = None) -> 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]: ...
|
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
|
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_UNDEFINED = 0
|
||||||
cdef int ASYNC_MODE_ENABLED = 1
|
cdef int ASYNC_MODE_ENABLED = 1
|
||||||
cdef int ASYNC_MODE_DISABLED = 2
|
cdef int ASYNC_MODE_DISABLED = 2
|
||||||
|
@ -1317,7 +1352,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):
|
def from_yaml(self, filepath, 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.
|
||||||
|
@ -1325,6 +1360,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 loader: YAML loader, :py:class:`YamlLoader` is used if not specified.
|
||||||
|
:type loader: ``yaml.Loader``
|
||||||
|
|
||||||
:rtype: None
|
:rtype: None
|
||||||
"""
|
"""
|
||||||
if yaml is None:
|
if yaml is None:
|
||||||
|
@ -1334,9 +1372,13 @@ cdef class ConfigurationOption(Provider):
|
||||||
'"pip install dependency-injector[yaml]"'
|
'"pip install dependency-injector[yaml]"'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if loader is None:
|
||||||
|
loader = YamlLoader
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(filepath) as opened_file:
|
with open(filepath) as opened_file:
|
||||||
config = yaml.load(opened_file, yaml.Loader)
|
config = yaml.load(opened_file, loader)
|
||||||
except IOError:
|
except IOError:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -1593,7 +1635,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):
|
def from_yaml(self, filepath, 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.
|
||||||
|
@ -1601,6 +1643,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 loader: YAML loader, :py:class:`YamlLoader` is used if not specified.
|
||||||
|
:type loader: ``yaml.Loader``
|
||||||
|
|
||||||
:rtype: None
|
:rtype: None
|
||||||
"""
|
"""
|
||||||
if yaml is None:
|
if yaml is None:
|
||||||
|
@ -1610,9 +1655,12 @@ cdef class Configuration(Object):
|
||||||
'"pip install dependency-injector[yaml]"'
|
'"pip install dependency-injector[yaml]"'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if loader is None:
|
||||||
|
loader = YamlLoader
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(filepath) as opened_file:
|
with open(filepath) as opened_file:
|
||||||
config = yaml.load(opened_file, yaml.Loader)
|
config = yaml.load(opened_file, loader)
|
||||||
except IOError:
|
except IOError:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,10 @@ import tempfile
|
||||||
import unittest2 as unittest
|
import unittest2 as unittest
|
||||||
|
|
||||||
from dependency_injector import containers, providers, errors
|
from dependency_injector import containers, providers, errors
|
||||||
|
try:
|
||||||
|
import yaml
|
||||||
|
except ImportError:
|
||||||
|
yaml = None
|
||||||
|
|
||||||
|
|
||||||
class ConfigTests(unittest.TestCase):
|
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'})
|
||||||
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):
|
class ConfigFromDict(unittest.TestCase):
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user