mirror of
				https://github.com/ets-labs/python-dependency-injector.git
				synced 2025-10-31 16:07:51 +03:00 
			
		
		
		
	Merge branch 'release/4.33.0' into master
This commit is contained in:
		
						commit
						9abf34cb88
					
				|  | @ -7,6 +7,17 @@ that were made in every particular version. | ||||||
| From version 0.7.6 *Dependency Injector* framework strictly  | From version 0.7.6 *Dependency Injector* framework strictly  | ||||||
| follows `Semantic versioning`_ | follows `Semantic versioning`_ | ||||||
| 
 | 
 | ||||||
|  | 4.33.0 | ||||||
|  | ------ | ||||||
|  | - Add support of default value for environment variable in INI and YAML | ||||||
|  |   configuration files with ``${ENV_NAME:default}`` format. | ||||||
|  |   See issue `#459 <https://github.com/ets-labs/python-dependency-injector/issues/459>`_. | ||||||
|  |   Thanks to `Maksym Shemet @hbmshemet <https://github.com/hbmshemet>`_ for suggesting the feature. | ||||||
|  | - Add method ``Configuration.from_value()``. | ||||||
|  |   See issue `#462 <https://github.com/ets-labs/python-dependency-injector/issues/462>`_. | ||||||
|  |   Thanks to Mr. `Slack Clone <https://disqus.com/by/slackclone/>`_  for bringing it up | ||||||
|  |   in the comments for configuration provider docs. | ||||||
|  | 
 | ||||||
| 4.32.3 | 4.32.3 | ||||||
| ------ | ------ | ||||||
| - This fix a typo in ``di_in_python.rst`` doc. | - This fix a typo in ``di_in_python.rst`` doc. | ||||||
|  |  | ||||||
|  | @ -5,7 +5,7 @@ Configuration provider | ||||||
| 
 | 
 | ||||||
| .. meta:: | .. meta:: | ||||||
|    :keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Configuration,Injection, |    :keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Configuration,Injection, | ||||||
|               Option,Ini,Json,Yaml,Pydantic,Dict,Environment Variable,Load,Read,Get |               Option,Ini,Json,Yaml,Pydantic,Dict,Environment Variable,Default,Load,Read,Get | ||||||
|    :description: Configuration provides configuration options to the other providers. This page |    :description: Configuration provides configuration options to the other providers. This page | ||||||
|                  demonstrates how to use Configuration provider to inject the dependencies, load |                  demonstrates how to use Configuration provider to inject the dependencies, load | ||||||
|                  a configuration from an ini or yaml file, a dictionary, an environment variable, |                  a configuration from an ini or yaml file, a dictionary, an environment variable, | ||||||
|  | @ -43,8 +43,18 @@ where ``examples/providers/configuration/config.ini`` is: | ||||||
|    :language: ini |    :language: ini | ||||||
| 
 | 
 | ||||||
| :py:meth:`Configuration.from_ini` method supports environment variables interpolation. Use | :py:meth:`Configuration.from_ini` method supports environment variables interpolation. Use | ||||||
| ``${ENV_NAME}`` format in the configuration file to substitute value of the environment | ``${ENV_NAME}`` format in the configuration file to substitute value from ``ENV_NAME`` environment | ||||||
| variable ``ENV_NAME``. | variable. | ||||||
|  | 
 | ||||||
|  | You can also specify a default value using ``${ENV_NAME:default}`` format. If environment | ||||||
|  | variable ``ENV_NAME`` is undefined, configuration provider will substitute value ``default``. | ||||||
|  | 
 | ||||||
|  | .. code-block:: ini | ||||||
|  | 
 | ||||||
|  |    [section] | ||||||
|  |    option1 = {$ENV_VAR} | ||||||
|  |    option2 = {$ENV_VAR}/path | ||||||
|  |    option3 = {$ENV_VAR:default} | ||||||
| 
 | 
 | ||||||
| Loading from a YAML file | Loading from a YAML file | ||||||
| ------------------------ | ------------------------ | ||||||
|  | @ -62,12 +72,22 @@ where ``examples/providers/configuration/config.yml`` is: | ||||||
| .. literalinclude:: ../../examples/providers/configuration/config.yml | .. literalinclude:: ../../examples/providers/configuration/config.yml | ||||||
|    :language: ini |    :language: ini | ||||||
| 
 | 
 | ||||||
|  | :py:meth:`Configuration.from_yaml` method supports environment variables interpolation. Use | ||||||
|  | ``${ENV_NAME}`` format in the configuration file to substitute value from ``ENV_NAME`` environment | ||||||
|  | variable. | ||||||
|  | 
 | ||||||
|  | You can also specify a default value using ``${ENV_NAME:default}`` format. If environment | ||||||
|  | variable ``ENV_NAME`` is undefined, configuration provider will substitute value ``default``. | ||||||
|  | 
 | ||||||
|  | .. code-block:: ini | ||||||
|  | 
 | ||||||
|  |    section: | ||||||
|  |      option1: {$ENV_VAR} | ||||||
|  |      option2: {$ENV_VAR}/path | ||||||
|  |      option3: {$ENV_VAR:default} | ||||||
|  | 
 | ||||||
| :py:meth:`Configuration.from_yaml` method uses custom version of ``yaml.SafeLoader``. | :py:meth:`Configuration.from_yaml` method uses custom version of ``yaml.SafeLoader``. | ||||||
| 
 | To use another loader use ``loader`` argument: | ||||||
| The loader supports environment variables interpolation. Use ``${ENV_NAME}`` format |  | ||||||
| in the configuration file to substitute value of the environment variable ``ENV_NAME``. |  | ||||||
| 
 |  | ||||||
| You can also specify a YAML loader as an argument: |  | ||||||
| 
 | 
 | ||||||
| .. code-block:: python | .. code-block:: python | ||||||
| 
 | 
 | ||||||
|  | @ -144,6 +164,17 @@ Loading from an environment variable | ||||||
|    :lines: 3- |    :lines: 3- | ||||||
|    :emphasize-lines: 18-20 |    :emphasize-lines: 18-20 | ||||||
| 
 | 
 | ||||||
|  | Loading a value | ||||||
|  | --------------- | ||||||
|  | 
 | ||||||
|  | ``Configuration`` provider can load configuration value using the | ||||||
|  | :py:meth:`Configuration.from_value` method: | ||||||
|  | 
 | ||||||
|  | .. literalinclude:: ../../examples/providers/configuration/configuration_value.py | ||||||
|  |    :language: python | ||||||
|  |    :lines: 3- | ||||||
|  |    :emphasize-lines: 14-15 | ||||||
|  | 
 | ||||||
| Loading from the multiple sources | Loading from the multiple sources | ||||||
| --------------------------------- | --------------------------------- | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										24
									
								
								examples/providers/configuration/configuration_value.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								examples/providers/configuration/configuration_value.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,24 @@ | ||||||
|  | """`Configuration` provider values loading example.""" | ||||||
|  | 
 | ||||||
|  | from datetime import date | ||||||
|  | 
 | ||||||
|  | from dependency_injector import containers, providers | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Container(containers.DeclarativeContainer): | ||||||
|  | 
 | ||||||
|  |     config = providers.Configuration() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | if __name__ == '__main__': | ||||||
|  |     container = Container() | ||||||
|  | 
 | ||||||
|  |     container.config.option1.from_value(date(2021, 6, 13)) | ||||||
|  |     container.config.option2.from_value(date(2021, 6, 14)) | ||||||
|  | 
 | ||||||
|  |     assert container.config() == { | ||||||
|  |         'option1': date(2021, 6, 13), | ||||||
|  |         'option2': date(2021, 6, 14), | ||||||
|  |     } | ||||||
|  |     assert container.config.option1() == date(2021, 6, 13) | ||||||
|  |     assert container.config.option2() == date(2021, 6, 14) | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| """Top-level package.""" | """Top-level package.""" | ||||||
| 
 | 
 | ||||||
| __version__ = '4.32.3' | __version__ = '4.33.0' | ||||||
| """Version number. | """Version number. | ||||||
| 
 | 
 | ||||||
| :type: str | :type: str | ||||||
|  |  | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -205,6 +205,7 @@ class ConfigurationOption(Provider[Any]): | ||||||
|     def from_pydantic(self, settings: PydanticSettings, required: bool = False, **kwargs: Any) -> None: ... |     def from_pydantic(self, settings: PydanticSettings, required: bool = False, **kwargs: Any) -> None: ... | ||||||
|     def from_dict(self, options: _Dict[str, Any], required: bool = False) -> None: ... |     def from_dict(self, options: _Dict[str, Any], required: bool = False) -> None: ... | ||||||
|     def from_env(self, name: str, default: Optional[Any] = None, required: bool = False) -> None: ... |     def from_env(self, name: str, default: Optional[Any] = None, required: bool = False) -> None: ... | ||||||
|  |     def from_value(self, value: Any) -> None: ... | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class TypedConfigurationOption(Callable[T]): | class TypedConfigurationOption(Callable[T]): | ||||||
|  | @ -241,6 +242,7 @@ class Configuration(Object[Any]): | ||||||
|     def from_pydantic(self, settings: PydanticSettings, required: bool = False, **kwargs: Any) -> None: ... |     def from_pydantic(self, settings: PydanticSettings, required: bool = False, **kwargs: Any) -> None: ... | ||||||
|     def from_dict(self, options: _Dict[str, Any], required: bool = False) -> None: ... |     def from_dict(self, options: _Dict[str, Any], required: bool = False) -> None: ... | ||||||
|     def from_env(self, name: str, default: Optional[Any] = None, required: bool = False) -> None: ... |     def from_env(self, name: str, default: Optional[Any] = None, required: bool = False) -> None: ... | ||||||
|  |     def from_value(self, value: Any) -> None: ... | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Factory(Provider[T]): | class Factory(Provider[T]): | ||||||
|  |  | ||||||
|  | @ -64,6 +64,25 @@ else:  # pragma: no cover | ||||||
|                                     copy.deepcopy(obj.im_self, memo), |                                     copy.deepcopy(obj.im_self, memo), | ||||||
|                                     obj.im_class) |                                     obj.im_class) | ||||||
| 
 | 
 | ||||||
|  | config_env_marker_pattern = re.compile( | ||||||
|  |     r'\${(?P<name>[^}^{:]+)(?P<separator>:?)(?P<default>.*?)}', | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | def _resolve_config_env_markers(config_value): | ||||||
|  |     """"Replace environment variable markers with their values.""" | ||||||
|  |     for match in reversed(list(config_env_marker_pattern.finditer(config_value))): | ||||||
|  |         has_default = match.group('separator') == ':' | ||||||
|  | 
 | ||||||
|  |         value = os.getenv(match.group('name')) | ||||||
|  |         if value is None: | ||||||
|  |             if not has_default: | ||||||
|  |                 continue | ||||||
|  |             value = match.group('default') | ||||||
|  | 
 | ||||||
|  |         span_min, span_max = match.span() | ||||||
|  |         config_value = f'{config_value[:span_min]}{value}{config_value[span_max:]}' | ||||||
|  |     return config_value | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| if sys.version_info[0] == 3: | if sys.version_info[0] == 3: | ||||||
|     class EnvInterpolation(iniconfigparser.BasicInterpolation): |     class EnvInterpolation(iniconfigparser.BasicInterpolation): | ||||||
|  | @ -71,7 +90,7 @@ if sys.version_info[0] == 3: | ||||||
| 
 | 
 | ||||||
|         def before_get(self, parser, section, option, value, defaults): |         def before_get(self, parser, section, option, value, defaults): | ||||||
|             value = super().before_get(parser, section, option, value, defaults) |             value = super().before_get(parser, section, option, value, defaults) | ||||||
|             return os.path.expandvars(value) |             return _resolve_config_env_markers(value) | ||||||
| 
 | 
 | ||||||
|     def _parse_ini_file(filepath): |     def _parse_ini_file(filepath): | ||||||
|         parser = iniconfigparser.ConfigParser(interpolation=EnvInterpolation()) |         parser = iniconfigparser.ConfigParser(interpolation=EnvInterpolation()) | ||||||
|  | @ -84,19 +103,18 @@ else: | ||||||
|     def _parse_ini_file(filepath): |     def _parse_ini_file(filepath): | ||||||
|         parser = iniconfigparser.ConfigParser() |         parser = iniconfigparser.ConfigParser() | ||||||
|         with open(filepath) as config_file: |         with open(filepath) as config_file: | ||||||
|             config_string = os.path.expandvars(config_file.read()) |             config_string = _resolve_config_env_markers(config_file.read()) | ||||||
|         parser.readfp(StringIO.StringIO(config_string)) |         parser.readfp(StringIO.StringIO(config_string)) | ||||||
|         return parser |         return parser | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| if yaml: | if yaml: | ||||||
|     # TODO: use SafeLoader without env interpolation by default in version 5.* |     # TODO: use SafeLoader without env interpolation by default in version 5.* | ||||||
|     yaml_env_marker_pattern = re.compile(r'\$\{([^}^{]+)\}') |  | ||||||
|     def yaml_env_marker_constructor(_, node): |     def yaml_env_marker_constructor(_, node): | ||||||
|         """"Replace environment variable marker with its value.""" |         """"Replace environment variable marker with its value.""" | ||||||
|         return os.path.expandvars(node.value) |         return _resolve_config_env_markers(node.value) | ||||||
| 
 | 
 | ||||||
|     yaml.add_implicit_resolver('!path', yaml_env_marker_pattern) |     yaml.add_implicit_resolver('!path', config_env_marker_pattern) | ||||||
|     yaml.add_constructor('!path', yaml_env_marker_constructor) |     yaml.add_constructor('!path', yaml_env_marker_constructor) | ||||||
| 
 | 
 | ||||||
|     class YamlLoader(yaml.SafeLoader): |     class YamlLoader(yaml.SafeLoader): | ||||||
|  | @ -105,7 +123,7 @@ if yaml: | ||||||
|         Inherits ``yaml.SafeLoader`` and add environment variables interpolation. |         Inherits ``yaml.SafeLoader`` and add environment variables interpolation. | ||||||
|         """ |         """ | ||||||
| 
 | 
 | ||||||
|     YamlLoader.add_implicit_resolver('!path', yaml_env_marker_pattern, None) |     YamlLoader.add_implicit_resolver('!path', config_env_marker_pattern, None) | ||||||
|     YamlLoader.add_constructor('!path', yaml_env_marker_constructor) |     YamlLoader.add_constructor('!path', yaml_env_marker_constructor) | ||||||
| else: | else: | ||||||
|     class YamlLoader: |     class YamlLoader: | ||||||
|  | @ -1680,6 +1698,16 @@ cdef class ConfigurationOption(Provider): | ||||||
| 
 | 
 | ||||||
|         self.override(value) |         self.override(value) | ||||||
| 
 | 
 | ||||||
|  |     def from_value(self, value): | ||||||
|  |         """Load configuration value. | ||||||
|  | 
 | ||||||
|  |         :param value: Configuration value | ||||||
|  |         :type value: object | ||||||
|  | 
 | ||||||
|  |         :rtype: None | ||||||
|  |         """ | ||||||
|  |         self.override(value) | ||||||
|  | 
 | ||||||
|     @property |     @property | ||||||
|     def related(self): |     def related(self): | ||||||
|         """Return related providers generator.""" |         """Return related providers generator.""" | ||||||
|  | @ -2086,6 +2114,16 @@ cdef class Configuration(Object): | ||||||
| 
 | 
 | ||||||
|         self.override(value) |         self.override(value) | ||||||
| 
 | 
 | ||||||
|  |     def from_value(self, value): | ||||||
|  |         """Load configuration value. | ||||||
|  | 
 | ||||||
|  |         :param value: Configuration value | ||||||
|  |         :type value: object | ||||||
|  | 
 | ||||||
|  |         :rtype: None | ||||||
|  |         """ | ||||||
|  |         self.override(value) | ||||||
|  | 
 | ||||||
|     @property |     @property | ||||||
|     def related(self): |     def related(self): | ||||||
|         """Return related providers generator.""" |         """Return related providers generator.""" | ||||||
|  |  | ||||||
|  | @ -591,17 +591,20 @@ class ConfigFromIniWithEnvInterpolationTests(unittest.TestCase): | ||||||
|         self.config = providers.Configuration(name='config') |         self.config = providers.Configuration(name='config') | ||||||
| 
 | 
 | ||||||
|         os.environ['CONFIG_TEST_ENV'] = 'test-value' |         os.environ['CONFIG_TEST_ENV'] = 'test-value' | ||||||
|  |         os.environ['CONFIG_TEST_PATH'] = 'test-path' | ||||||
| 
 | 
 | ||||||
|         _, self.config_file = tempfile.mkstemp() |         _, self.config_file = tempfile.mkstemp() | ||||||
|         with open(self.config_file, 'w') as config_file: |         with open(self.config_file, 'w') as config_file: | ||||||
|             config_file.write( |             config_file.write( | ||||||
|                 '[section1]\n' |                 '[section1]\n' | ||||||
|                 'value1=${CONFIG_TEST_ENV}\n' |                 'value1=${CONFIG_TEST_ENV}\n' | ||||||
|  |                 'value2=${CONFIG_TEST_PATH}/path\n' | ||||||
|             ) |             ) | ||||||
| 
 | 
 | ||||||
|     def tearDown(self): |     def tearDown(self): | ||||||
|         del self.config |         del self.config | ||||||
|         del os.environ['CONFIG_TEST_ENV'] |         os.environ.pop('CONFIG_TEST_ENV', None) | ||||||
|  |         os.environ.pop('CONFIG_TEST_PATH', None) | ||||||
|         os.unlink(self.config_file) |         os.unlink(self.config_file) | ||||||
| 
 | 
 | ||||||
|     def test_env_variable_interpolation(self): |     def test_env_variable_interpolation(self): | ||||||
|  | @ -612,11 +615,67 @@ class ConfigFromIniWithEnvInterpolationTests(unittest.TestCase): | ||||||
|             { |             { | ||||||
|                 'section1': { |                 'section1': { | ||||||
|                     'value1': 'test-value', |                     'value1': 'test-value', | ||||||
|  |                     'value2': 'test-path/path', | ||||||
|                 }, |                 }, | ||||||
|             }, |             }, | ||||||
|         ) |         ) | ||||||
|         self.assertEqual(self.config.section1(), {'value1': 'test-value'}) |         self.assertEqual( | ||||||
|  |             self.config.section1(), | ||||||
|  |             { | ||||||
|  |                 'value1': 'test-value', | ||||||
|  |                 'value2': 'test-path/path', | ||||||
|  |             }, | ||||||
|  |         ) | ||||||
|         self.assertEqual(self.config.section1.value1(), 'test-value') |         self.assertEqual(self.config.section1.value1(), 'test-value') | ||||||
|  |         self.assertEqual(self.config.section1.value2(), 'test-path/path') | ||||||
|  | 
 | ||||||
|  |     def test_missing_envs(self): | ||||||
|  |         del os.environ['CONFIG_TEST_ENV'] | ||||||
|  |         del os.environ['CONFIG_TEST_PATH'] | ||||||
|  | 
 | ||||||
|  |         self.config.from_ini(self.config_file) | ||||||
|  | 
 | ||||||
|  |         self.assertEqual( | ||||||
|  |             self.config(), | ||||||
|  |             { | ||||||
|  |                 'section1': { | ||||||
|  |                     'value1': '${CONFIG_TEST_ENV}', | ||||||
|  |                     'value2': '${CONFIG_TEST_PATH}/path', | ||||||
|  |                 }, | ||||||
|  |             }, | ||||||
|  |         ) | ||||||
|  |         self.assertEqual( | ||||||
|  |             self.config.section1(), | ||||||
|  |             { | ||||||
|  |                 'value1': '${CONFIG_TEST_ENV}', | ||||||
|  |                 'value2': '${CONFIG_TEST_PATH}/path', | ||||||
|  |             }, | ||||||
|  |         ) | ||||||
|  |         self.assertEqual(self.config.section1.value1(), '${CONFIG_TEST_ENV}') | ||||||
|  |         self.assertEqual(self.config.section1.value2(), '${CONFIG_TEST_PATH}/path') | ||||||
|  | 
 | ||||||
|  |     def test_default_values(self): | ||||||
|  |         os.environ['DEFINED'] = 'defined' | ||||||
|  |         self.addCleanup(os.environ.pop, 'DEFINED') | ||||||
|  | 
 | ||||||
|  |         with open(self.config_file, 'w') as config_file: | ||||||
|  |             config_file.write( | ||||||
|  |                 '[section]\n' | ||||||
|  |                 'defined_with_default=${DEFINED:default}\n' | ||||||
|  |                 'undefined_with_default=${UNDEFINED:default}\n' | ||||||
|  |                 'complex=${DEFINED}/path/${DEFINED:default}/${UNDEFINED}/${UNDEFINED:default}\n' | ||||||
|  |             ) | ||||||
|  | 
 | ||||||
|  |         self.config.from_ini(self.config_file) | ||||||
|  | 
 | ||||||
|  |         self.assertEqual( | ||||||
|  |             self.config.section(), | ||||||
|  |             { | ||||||
|  |                 'defined_with_default': 'defined', | ||||||
|  |                 'undefined_with_default': 'default', | ||||||
|  |                 'complex': 'defined/path/defined/${UNDEFINED}/default', | ||||||
|  |             }, | ||||||
|  |         ) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class ConfigFromYamlTests(unittest.TestCase): | class ConfigFromYamlTests(unittest.TestCase): | ||||||
|  | @ -781,17 +840,20 @@ class ConfigFromYamlWithEnvInterpolationTests(unittest.TestCase): | ||||||
|         self.config = providers.Configuration(name='config') |         self.config = providers.Configuration(name='config') | ||||||
| 
 | 
 | ||||||
|         os.environ['CONFIG_TEST_ENV'] = 'test-value' |         os.environ['CONFIG_TEST_ENV'] = 'test-value' | ||||||
|  |         os.environ['CONFIG_TEST_PATH'] = 'test-path' | ||||||
| 
 | 
 | ||||||
|         _, self.config_file = tempfile.mkstemp() |         _, self.config_file = tempfile.mkstemp() | ||||||
|         with open(self.config_file, 'w') as config_file: |         with open(self.config_file, 'w') as config_file: | ||||||
|             config_file.write( |             config_file.write( | ||||||
|                 'section1:\n' |                 'section1:\n' | ||||||
|                 '  value1: ${CONFIG_TEST_ENV}\n' |                 '  value1: ${CONFIG_TEST_ENV}\n' | ||||||
|  |                 '  value2: ${CONFIG_TEST_PATH}/path\n' | ||||||
|             ) |             ) | ||||||
| 
 | 
 | ||||||
|     def tearDown(self): |     def tearDown(self): | ||||||
|         del self.config |         del self.config | ||||||
|         del os.environ['CONFIG_TEST_ENV'] |         os.environ.pop('CONFIG_TEST_ENV', None) | ||||||
|  |         os.environ.pop('CONFIG_TEST_PATH', None) | ||||||
|         os.unlink(self.config_file) |         os.unlink(self.config_file) | ||||||
| 
 | 
 | ||||||
|     @unittest.skipIf(sys.version_info[:2] == (3, 4), 'PyYAML does not support Python 3.4') |     @unittest.skipIf(sys.version_info[:2] == (3, 4), 'PyYAML does not support Python 3.4') | ||||||
|  | @ -803,11 +865,69 @@ class ConfigFromYamlWithEnvInterpolationTests(unittest.TestCase): | ||||||
|             { |             { | ||||||
|                 'section1': { |                 'section1': { | ||||||
|                     'value1': 'test-value', |                     'value1': 'test-value', | ||||||
|  |                     'value2': 'test-path/path', | ||||||
|                 }, |                 }, | ||||||
|             }, |             }, | ||||||
|         ) |         ) | ||||||
|         self.assertEqual(self.config.section1(), {'value1': 'test-value'}) |         self.assertEqual( | ||||||
|  |             self.config.section1(), | ||||||
|  |             { | ||||||
|  |                 'value1': 'test-value', | ||||||
|  |                 'value2': 'test-path/path', | ||||||
|  |             }, | ||||||
|  |         ) | ||||||
|         self.assertEqual(self.config.section1.value1(), 'test-value') |         self.assertEqual(self.config.section1.value1(), 'test-value') | ||||||
|  |         self.assertEqual(self.config.section1.value2(), 'test-path/path') | ||||||
|  | 
 | ||||||
|  |     @unittest.skipIf(sys.version_info[:2] == (3, 4), 'PyYAML does not support Python 3.4') | ||||||
|  |     def test_missing_envs(self): | ||||||
|  |         del os.environ['CONFIG_TEST_ENV'] | ||||||
|  |         del os.environ['CONFIG_TEST_PATH'] | ||||||
|  | 
 | ||||||
|  |         self.config.from_yaml(self.config_file) | ||||||
|  | 
 | ||||||
|  |         self.assertEqual( | ||||||
|  |             self.config(), | ||||||
|  |             { | ||||||
|  |                 'section1': { | ||||||
|  |                     'value1': '${CONFIG_TEST_ENV}', | ||||||
|  |                     'value2': '${CONFIG_TEST_PATH}/path', | ||||||
|  |                 }, | ||||||
|  |             }, | ||||||
|  |         ) | ||||||
|  |         self.assertEqual( | ||||||
|  |             self.config.section1(), | ||||||
|  |             { | ||||||
|  |                 'value1': '${CONFIG_TEST_ENV}', | ||||||
|  |                 'value2': '${CONFIG_TEST_PATH}/path', | ||||||
|  |             }, | ||||||
|  |         ) | ||||||
|  |         self.assertEqual(self.config.section1.value1(), '${CONFIG_TEST_ENV}') | ||||||
|  |         self.assertEqual(self.config.section1.value2(), '${CONFIG_TEST_PATH}/path') | ||||||
|  | 
 | ||||||
|  |     @unittest.skipIf(sys.version_info[:2] == (3, 4), 'PyYAML does not support Python 3.4') | ||||||
|  |     def test_default_values(self): | ||||||
|  |         os.environ['DEFINED'] = 'defined' | ||||||
|  |         self.addCleanup(os.environ.pop, 'DEFINED') | ||||||
|  | 
 | ||||||
|  |         with open(self.config_file, 'w') as config_file: | ||||||
|  |             config_file.write( | ||||||
|  |                 'section:\n' | ||||||
|  |                 '  defined_with_default: ${DEFINED:default}\n' | ||||||
|  |                 '  undefined_with_default: ${UNDEFINED:default}\n' | ||||||
|  |                 '  complex: ${DEFINED}/path/${DEFINED:default}/${UNDEFINED}/${UNDEFINED:default}\n' | ||||||
|  |             ) | ||||||
|  | 
 | ||||||
|  |         self.config.from_yaml(self.config_file) | ||||||
|  | 
 | ||||||
|  |         self.assertEqual( | ||||||
|  |             self.config.section(), | ||||||
|  |             { | ||||||
|  |                 'defined_with_default': 'defined', | ||||||
|  |                 'undefined_with_default': 'default', | ||||||
|  |                 'complex': 'defined/path/defined/${UNDEFINED}/default', | ||||||
|  |             }, | ||||||
|  |         ) | ||||||
| 
 | 
 | ||||||
|     @unittest.skipIf(sys.version_info[:2] == (3, 4), 'PyYAML does not support Python 3.4') |     @unittest.skipIf(sys.version_info[:2] == (3, 4), 'PyYAML does not support Python 3.4') | ||||||
|     def test_option_env_variable_interpolation(self): |     def test_option_env_variable_interpolation(self): | ||||||
|  | @ -818,41 +938,47 @@ class ConfigFromYamlWithEnvInterpolationTests(unittest.TestCase): | ||||||
|             { |             { | ||||||
|                 'section1': { |                 'section1': { | ||||||
|                     'value1': 'test-value', |                     'value1': 'test-value', | ||||||
|  |                     'value2': 'test-path/path', | ||||||
|                 }, |                 }, | ||||||
|             }, |             }, | ||||||
|         ) |         ) | ||||||
|         self.assertEqual(self.config.option.section1(), {'value1': 'test-value'}) |         self.assertEqual( | ||||||
|  |             self.config.option.section1(), | ||||||
|  |             { | ||||||
|  |                 'value1': 'test-value', | ||||||
|  |                 'value2': 'test-path/path', | ||||||
|  |             }, | ||||||
|  |         ) | ||||||
|         self.assertEqual(self.config.option.section1.value1(), 'test-value') |         self.assertEqual(self.config.option.section1.value1(), 'test-value') | ||||||
|  |         self.assertEqual(self.config.option.section1.value2(), 'test-path/path') | ||||||
| 
 | 
 | ||||||
|     @unittest.skipIf(sys.version_info[:2] == (3, 4), 'PyYAML does not support Python 3.4') |     @unittest.skipIf(sys.version_info[:2] == (3, 4), 'PyYAML does not support Python 3.4') | ||||||
|     def test_env_variable_interpolation_custom_loader(self): |     def test_env_variable_interpolation_custom_loader(self): | ||||||
|         self.config.from_yaml(self.config_file, loader=yaml.UnsafeLoader) |         self.config.from_yaml(self.config_file, loader=yaml.UnsafeLoader) | ||||||
| 
 | 
 | ||||||
|         self.assertEqual( |         self.assertEqual( | ||||||
|             self.config(), |             self.config.section1(), | ||||||
|             { |             { | ||||||
|                 'section1': { |  | ||||||
|                 'value1': 'test-value', |                 'value1': 'test-value', | ||||||
|                 }, |                 'value2': 'test-path/path', | ||||||
|             }, |             }, | ||||||
|         ) |         ) | ||||||
|         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.value2(), 'test-path/path') | ||||||
| 
 | 
 | ||||||
|     @unittest.skipIf(sys.version_info[:2] == (3, 4), 'PyYAML does not support Python 3.4') |     @unittest.skipIf(sys.version_info[:2] == (3, 4), 'PyYAML does not support Python 3.4') | ||||||
|     def test_option_env_variable_interpolation_custom_loader(self): |     def test_option_env_variable_interpolation_custom_loader(self): | ||||||
|         self.config.option.from_yaml(self.config_file, loader=yaml.UnsafeLoader) |         self.config.option.from_yaml(self.config_file, loader=yaml.UnsafeLoader) | ||||||
| 
 | 
 | ||||||
|         self.assertEqual( |         self.assertEqual( | ||||||
|             self.config.option(), |             self.config.option.section1(), | ||||||
|             { |             { | ||||||
|                 'section1': { |  | ||||||
|                 'value1': 'test-value', |                 'value1': 'test-value', | ||||||
|                 }, |                 'value2': 'test-path/path', | ||||||
|             }, |             }, | ||||||
|         ) |         ) | ||||||
|         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') | ||||||
|  |         self.assertEqual(self.config.option.section1.value2(), 'test-path/path') | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class ConfigFromPydanticTests(unittest.TestCase): | class ConfigFromPydanticTests(unittest.TestCase): | ||||||
|  | @ -1250,3 +1376,25 @@ class ConfigFromEnvTests(unittest.TestCase): | ||||||
|         self.config = providers.Configuration(strict=True) |         self.config = providers.Configuration(strict=True) | ||||||
|         self.config.option.from_env('UNDEFINED_ENV', default='default-value', required=False) |         self.config.option.from_env('UNDEFINED_ENV', default='default-value', required=False) | ||||||
|         self.assertEqual(self.config.option(), 'default-value') |         self.assertEqual(self.config.option(), 'default-value') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ConfigFromValueTests(unittest.TestCase): | ||||||
|  | 
 | ||||||
|  |     def setUp(self): | ||||||
|  |         self.config = providers.Configuration(name='config') | ||||||
|  | 
 | ||||||
|  |     def test_from_value(self): | ||||||
|  |         test_value = 123321 | ||||||
|  |         self.config.from_value(test_value) | ||||||
|  |         self.assertEqual(self.config(), test_value) | ||||||
|  | 
 | ||||||
|  |     def test_option_from_value(self): | ||||||
|  |         test_value_1 = 123 | ||||||
|  |         test_value_2 = 321 | ||||||
|  | 
 | ||||||
|  |         self.config.option1.from_value(test_value_1) | ||||||
|  |         self.config.option2.from_value(test_value_2) | ||||||
|  | 
 | ||||||
|  |         self.assertEqual(self.config(), {'option1': test_value_1, 'option2': test_value_2}) | ||||||
|  |         self.assertEqual(self.config.option1(), test_value_1) | ||||||
|  |         self.assertEqual(self.config.option2(), test_value_2) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user