mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2025-02-27 09:00:38 +03:00
Add option to disable env var interpolation in configs (#861)
This commit is contained in:
parent
09efbffab1
commit
7d4ebecd19
|
@ -366,6 +366,19 @@ See also: :ref:`configuration-strict-mode`.
|
||||||
|
|
||||||
assert container.config.section.option() is None
|
assert container.config.section.option() is None
|
||||||
|
|
||||||
|
If you want to disable environment variables interpolation, pass ``envs_required=None``:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
:caption: templates.yml
|
||||||
|
|
||||||
|
template_string: 'Hello, ${name}!'
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
>>> container.config.from_yaml("templates.yml", envs_required=None)
|
||||||
|
>>> container.config.template_string()
|
||||||
|
'Hello, ${name}!'
|
||||||
|
|
||||||
Mandatory and optional sources
|
Mandatory and optional sources
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
|
|
|
@ -225,20 +225,20 @@ class ConfigurationOption(Provider[Any]):
|
||||||
self,
|
self,
|
||||||
filepath: Union[Path, str],
|
filepath: Union[Path, str],
|
||||||
required: bool = False,
|
required: bool = False,
|
||||||
envs_required: bool = False,
|
envs_required: Optional[bool] = False,
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def from_yaml(
|
def from_yaml(
|
||||||
self,
|
self,
|
||||||
filepath: Union[Path, str],
|
filepath: Union[Path, str],
|
||||||
required: bool = False,
|
required: bool = False,
|
||||||
loader: Optional[Any] = None,
|
loader: Optional[Any] = None,
|
||||||
envs_required: bool = False,
|
envs_required: Optional[bool] = False,
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def from_json(
|
def from_json(
|
||||||
self,
|
self,
|
||||||
filepath: Union[Path, str],
|
filepath: Union[Path, str],
|
||||||
required: bool = False,
|
required: bool = False,
|
||||||
envs_required: bool = False,
|
envs_required: Optional[bool] = False,
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def from_pydantic(
|
def from_pydantic(
|
||||||
self, settings: PydanticSettings, required: bool = False, **kwargs: Any
|
self, settings: PydanticSettings, required: bool = False, **kwargs: Any
|
||||||
|
|
|
@ -15,6 +15,7 @@ import sys
|
||||||
import threading
|
import threading
|
||||||
import types
|
import types
|
||||||
import warnings
|
import warnings
|
||||||
|
from configparser import ConfigParser as IniConfigParser
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import contextvars
|
import contextvars
|
||||||
|
@ -41,11 +42,6 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
_is_coroutine = True
|
_is_coroutine = True
|
||||||
|
|
||||||
try:
|
|
||||||
import ConfigParser as iniconfigparser
|
|
||||||
except ImportError:
|
|
||||||
import configparser as iniconfigparser
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import yaml
|
import yaml
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -102,7 +98,7 @@ config_env_marker_pattern = re.compile(
|
||||||
r"\${(?P<name>[^}^{:]+)(?P<separator>:?)(?P<default>.*?)}",
|
r"\${(?P<name>[^}^{:]+)(?P<separator>:?)(?P<default>.*?)}",
|
||||||
)
|
)
|
||||||
|
|
||||||
def _resolve_config_env_markers(config_content, envs_required=False):
|
cdef str _resolve_config_env_markers(config_content: str, envs_required: bool):
|
||||||
"""Replace environment variable markers with their values."""
|
"""Replace environment variable markers with their values."""
|
||||||
findings = list(config_env_marker_pattern.finditer(config_content))
|
findings = list(config_env_marker_pattern.finditer(config_content))
|
||||||
|
|
||||||
|
@ -121,28 +117,19 @@ def _resolve_config_env_markers(config_content, envs_required=False):
|
||||||
return config_content
|
return config_content
|
||||||
|
|
||||||
|
|
||||||
if sys.version_info[0] == 3:
|
cdef object _parse_ini_file(filepath, envs_required: bool | None):
|
||||||
def _parse_ini_file(filepath, envs_required=False):
|
parser = IniConfigParser()
|
||||||
parser = iniconfigparser.ConfigParser()
|
|
||||||
with open(filepath) as config_file:
|
|
||||||
config_string = _resolve_config_env_markers(
|
|
||||||
config_file.read(),
|
|
||||||
envs_required=envs_required,
|
|
||||||
)
|
|
||||||
parser.read_string(config_string)
|
|
||||||
return parser
|
|
||||||
else:
|
|
||||||
import StringIO
|
|
||||||
|
|
||||||
def _parse_ini_file(filepath, envs_required=False):
|
with open(filepath) as config_file:
|
||||||
parser = iniconfigparser.ConfigParser()
|
config_string = config_file.read()
|
||||||
with open(filepath) as config_file:
|
|
||||||
|
if envs_required is not None:
|
||||||
config_string = _resolve_config_env_markers(
|
config_string = _resolve_config_env_markers(
|
||||||
config_file.read(),
|
config_string,
|
||||||
envs_required=envs_required,
|
envs_required=envs_required,
|
||||||
)
|
)
|
||||||
parser.readfp(StringIO.StringIO(config_string))
|
parser.read_string(config_string)
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
|
||||||
if yaml:
|
if yaml:
|
||||||
|
@ -1717,7 +1704,7 @@ cdef class ConfigurationOption(Provider):
|
||||||
try:
|
try:
|
||||||
parser = _parse_ini_file(
|
parser = _parse_ini_file(
|
||||||
filepath,
|
filepath,
|
||||||
envs_required=envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
|
envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
|
||||||
)
|
)
|
||||||
except IOError as exception:
|
except IOError as exception:
|
||||||
if required is not False \
|
if required is not False \
|
||||||
|
@ -1776,10 +1763,11 @@ cdef class ConfigurationOption(Provider):
|
||||||
raise
|
raise
|
||||||
return
|
return
|
||||||
|
|
||||||
config_content = _resolve_config_env_markers(
|
if envs_required is not None:
|
||||||
config_content,
|
config_content = _resolve_config_env_markers(
|
||||||
envs_required=envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
|
config_content,
|
||||||
)
|
envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
|
||||||
|
)
|
||||||
config = yaml.load(config_content, loader)
|
config = yaml.load(config_content, loader)
|
||||||
|
|
||||||
current_config = self.__call__()
|
current_config = self.__call__()
|
||||||
|
@ -1814,10 +1802,11 @@ cdef class ConfigurationOption(Provider):
|
||||||
raise
|
raise
|
||||||
return
|
return
|
||||||
|
|
||||||
config_content = _resolve_config_env_markers(
|
if envs_required is not None:
|
||||||
config_content,
|
config_content = _resolve_config_env_markers(
|
||||||
envs_required=envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
|
config_content,
|
||||||
)
|
envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
|
||||||
|
)
|
||||||
config = json.loads(config_content)
|
config = json.loads(config_content)
|
||||||
|
|
||||||
current_config = self.__call__()
|
current_config = self.__call__()
|
||||||
|
@ -2270,7 +2259,7 @@ cdef class Configuration(Object):
|
||||||
try:
|
try:
|
||||||
parser = _parse_ini_file(
|
parser = _parse_ini_file(
|
||||||
filepath,
|
filepath,
|
||||||
envs_required=envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
|
envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
|
||||||
)
|
)
|
||||||
except IOError as exception:
|
except IOError as exception:
|
||||||
if required is not False \
|
if required is not False \
|
||||||
|
@ -2329,10 +2318,11 @@ cdef class Configuration(Object):
|
||||||
raise
|
raise
|
||||||
return
|
return
|
||||||
|
|
||||||
config_content = _resolve_config_env_markers(
|
if envs_required is not None:
|
||||||
config_content,
|
config_content = _resolve_config_env_markers(
|
||||||
envs_required=envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
|
config_content,
|
||||||
)
|
envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
|
||||||
|
)
|
||||||
config = yaml.load(config_content, loader)
|
config = yaml.load(config_content, loader)
|
||||||
|
|
||||||
current_config = self.__call__()
|
current_config = self.__call__()
|
||||||
|
@ -2367,10 +2357,11 @@ cdef class Configuration(Object):
|
||||||
raise
|
raise
|
||||||
return
|
return
|
||||||
|
|
||||||
config_content = _resolve_config_env_markers(
|
if envs_required is not None:
|
||||||
config_content,
|
config_content = _resolve_config_env_markers(
|
||||||
envs_required=envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
|
config_content,
|
||||||
)
|
envs_required if envs_required is not UNDEFINED else self._is_strict_mode_enabled(),
|
||||||
|
)
|
||||||
config = json.loads(config_content)
|
config = json.loads(config_content)
|
||||||
|
|
||||||
current_config = self.__call__()
|
current_config = self.__call__()
|
||||||
|
|
|
@ -5,6 +5,23 @@ import os
|
||||||
from pytest import mark, raises
|
from pytest import mark, raises
|
||||||
|
|
||||||
|
|
||||||
|
def test_no_env_variable_interpolation(config, ini_config_file_3):
|
||||||
|
config.from_ini(ini_config_file_3, envs_required=None)
|
||||||
|
|
||||||
|
assert config() == {
|
||||||
|
"section1": {
|
||||||
|
"value1": "${CONFIG_TEST_ENV}",
|
||||||
|
"value2": "${CONFIG_TEST_PATH}/path",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
assert config.section1() == {
|
||||||
|
"value1": "${CONFIG_TEST_ENV}",
|
||||||
|
"value2": "${CONFIG_TEST_PATH}/path",
|
||||||
|
}
|
||||||
|
assert config.section1.value1() == "${CONFIG_TEST_ENV}"
|
||||||
|
assert config.section1.value2() == "${CONFIG_TEST_PATH}/path"
|
||||||
|
|
||||||
|
|
||||||
def test_env_variable_interpolation(config, ini_config_file_3):
|
def test_env_variable_interpolation(config, ini_config_file_3):
|
||||||
config.from_ini(ini_config_file_3)
|
config.from_ini(ini_config_file_3)
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,23 @@ import os
|
||||||
from pytest import mark, raises
|
from pytest import mark, raises
|
||||||
|
|
||||||
|
|
||||||
|
def test_no_env_variable_interpolation(config, json_config_file_3):
|
||||||
|
config.from_json(json_config_file_3, envs_required=None)
|
||||||
|
|
||||||
|
assert config() == {
|
||||||
|
"section1": {
|
||||||
|
"value1": "${CONFIG_TEST_ENV}",
|
||||||
|
"value2": "${CONFIG_TEST_PATH}/path",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
assert config.section1() == {
|
||||||
|
"value1": "${CONFIG_TEST_ENV}",
|
||||||
|
"value2": "${CONFIG_TEST_PATH}/path",
|
||||||
|
}
|
||||||
|
assert config.section1.value1() == "${CONFIG_TEST_ENV}"
|
||||||
|
assert config.section1.value2() == "${CONFIG_TEST_PATH}/path"
|
||||||
|
|
||||||
|
|
||||||
def test_env_variable_interpolation(config, json_config_file_3):
|
def test_env_variable_interpolation(config, json_config_file_3):
|
||||||
config.from_json(json_config_file_3)
|
config.from_json(json_config_file_3)
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,23 @@ import yaml
|
||||||
from pytest import mark, raises
|
from pytest import mark, raises
|
||||||
|
|
||||||
|
|
||||||
|
def test_no_env_variable_interpolation(config, yaml_config_file_3):
|
||||||
|
config.from_yaml(yaml_config_file_3, envs_required=None)
|
||||||
|
|
||||||
|
assert config() == {
|
||||||
|
"section1": {
|
||||||
|
"value1": "${CONFIG_TEST_ENV}",
|
||||||
|
"value2": "${CONFIG_TEST_PATH}/path",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
assert config.section1() == {
|
||||||
|
"value1": "${CONFIG_TEST_ENV}",
|
||||||
|
"value2": "${CONFIG_TEST_PATH}/path",
|
||||||
|
}
|
||||||
|
assert config.section1.value1() == "${CONFIG_TEST_ENV}"
|
||||||
|
assert config.section1.value2() == "${CONFIG_TEST_PATH}/path"
|
||||||
|
|
||||||
|
|
||||||
def test_env_variable_interpolation(config, yaml_config_file_3):
|
def test_env_variable_interpolation(config, yaml_config_file_3):
|
||||||
config.from_yaml(yaml_config_file_3)
|
config.from_yaml(yaml_config_file_3)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user