Add implementation and basic test

This commit is contained in:
Roman Mogylatov 2021-02-02 17:27:20 -05:00
parent 1fabbf314b
commit 1005c51bd0
3 changed files with 7558 additions and 6639 deletions

File diff suppressed because it is too large Load Diff

View File

@ -35,6 +35,11 @@ try:
except ImportError:
yaml = None
try:
import pydantic
except ImportError:
pydantic = None
from .errors import (
Error,
NoSuchProviderError,
@ -1413,7 +1418,6 @@ cdef class ConfigurationOption(Provider):
'"pip install dependency-injector[yaml]"'
)
if loader is None:
loader = YamlLoader
@ -1433,6 +1437,37 @@ cdef class ConfigurationOption(Provider):
current_config = {}
self.override(merge_dicts(current_config, config))
def from_pydantic(self, settings, required=UNDEFINED, **kwargs):
"""Load configuration from pydantic settings.
Loaded configuration is merged recursively over existing configuration.
:param settings: Pydantic settings instances.
:type settings: :py:class:`pydantic.BaseSettings`
:param required: When required is True, raise an exception if settings dict is empty.
:type required: bool
:param kwargs: Keyword arguments forwarded to ``pydantic.BaseSettings.dict()`` call.
:type kwargs: Dict[Any, Any]
:rtype: None
"""
if pydantic is None:
raise Error(
'Unable to load pydantic configuration - pydantic is not installed. '
'Install pydantic or install Dependency Injector with pydantic extras: '
'"pip install dependency-injector[pydantic]"'
)
if not isinstance(settings, pydantic.BaseSettings):
raise Error(
'Unable to recognize settings instance, expect "pydantic.BaseSettings", '
'got {0} instead'.format(settings)
)
self.from_dict(settings.dict(**kwargs), required=required)
def from_dict(self, options, required=UNDEFINED):
"""Load configuration from the dictionary.
@ -1766,6 +1801,37 @@ cdef class Configuration(Object):
current_config = {}
self.override(merge_dicts(current_config, config))
def from_pydantic(self, settings, required=UNDEFINED, **kwargs):
"""Load configuration from pydantic settings.
Loaded configuration is merged recursively over existing configuration.
:param settings: Pydantic settings instances.
:type settings: :py:class:`pydantic.BaseSettings`
:param required: When required is True, raise an exception if settings dict is empty.
:type required: bool
:param kwargs: Keyword arguments forwarded to ``pydantic.BaseSettings.dict()`` call.
:type kwargs: Dict[Any, Any]
:rtype: None
"""
if pydantic is None:
raise Error(
'Unable to load pydantic configuration - pydantic is not installed. '
'Install pydantic or install Dependency Injector with pydantic extras: '
'"pip install dependency-injector[pydantic]"'
)
if not isinstance(settings, pydantic.BaseSettings):
raise Error(
'Unable to recognize settings instance, expect "pydantic.BaseSettings", '
'got {0} instead'.format(settings)
)
self.from_dict(settings.dict(**kwargs), required=required)
def from_dict(self, options, required=UNDEFINED):
"""Load configuration from the dictionary.

View File

@ -14,6 +14,11 @@ try:
except ImportError:
yaml = None
try:
import pydantic
except ImportError:
pydantic = None
class ConfigTests(unittest.TestCase):
@ -723,6 +728,140 @@ class ConfigFromYamlWithEnvInterpolationTests(unittest.TestCase):
self.assertEqual(self.config.option.section1.value1(), 'test-value')
class ConfigFromPydanticTests(unittest.TestCase):
def setUp(self):
self.config = providers.Configuration(name='config')
class Section11(pydantic.BaseModel):
value1 = 1
class Section12(pydantic.BaseModel):
value2 = 2
class Settings1(pydantic.BaseSettings):
section1 = Section11()
section2 = Section12()
self.Settings1 = Settings1
class Section21(pydantic.BaseModel):
value1 = 11
value11 = 11
class Section3(pydantic.BaseModel):
value3 = 3
class Settings2(pydantic.BaseSettings):
section1 = Section21()
section2 = Section3()
self.Settings2 = Settings2
def test(self):
self.config.from_pydantic(self.Settings1())
self.assertEqual(self.config(), {'section1': {'value1': 1}, 'section2': {'value2': 2}})
self.assertEqual(self.config.section1(), {'value1': 1})
self.assertEqual(self.config.section1.value1(), 1)
self.assertEqual(self.config.section2(), {'value2': 2})
self.assertEqual(self.config.section2.value2(), 2)
# @unittest.skipIf(sys.version_info[:2] == (3, 4), 'PyYAML does not support Python 3.4')
# def test_merge(self):
# self.config.from_yaml(self.config_file_1)
# self.config.from_yaml(self.config_file_2)
#
# self.assertEqual(
# self.config(),
# {
# 'section1': {
# 'value1': 11,
# 'value11': 11,
# },
# 'section2': {
# 'value2': 2,
# },
# 'section3': {
# 'value3': 3,
# },
# },
# )
# self.assertEqual(self.config.section1(), {'value1': 11, 'value11': 11})
# self.assertEqual(self.config.section1.value1(), 11)
# self.assertEqual(self.config.section1.value11(), 11)
# self.assertEqual(self.config.section2(), {'value2': 2})
# self.assertEqual(self.config.section2.value2(), 2)
# self.assertEqual(self.config.section3(), {'value3': 3})
# self.assertEqual(self.config.section3.value3(), 3)
#
# @unittest.skipIf(sys.version_info[:2] == (3, 4), 'PyYAML does not support Python 3.4')
# def test_file_does_not_exist(self):
# self.config.from_yaml('./does_not_exist.yml')
# self.assertEqual(self.config(), {})
#
# @unittest.skipIf(sys.version_info[:2] == (3, 4), 'PyYAML does not support Python 3.4')
# def test_file_does_not_exist_strict_mode(self):
# self.config = providers.Configuration(strict=True)
# with self.assertRaises(IOError):
# self.config.from_yaml('./does_not_exist.yml')
#
# @unittest.skipIf(sys.version_info[:2] == (3, 4), 'PyYAML does not support Python 3.4')
# def test_option_file_does_not_exist(self):
# self.config.option.from_yaml('./does_not_exist.yml')
# self.assertIsNone(self.config.option())
#
# @unittest.skipIf(sys.version_info[:2] == (3, 4), 'PyYAML does not support Python 3.4')
# def test_option_file_does_not_exist_strict_mode(self):
# self.config = providers.Configuration(strict=True)
# with self.assertRaises(IOError):
# self.config.option.from_yaml('./does_not_exist.yml')
#
# @unittest.skipIf(sys.version_info[:2] == (3, 4), 'PyYAML does not support Python 3.4')
# def test_required_file_does_not_exist(self):
# with self.assertRaises(IOError):
# self.config.from_yaml('./does_not_exist.yml', required=True)
#
# @unittest.skipIf(sys.version_info[:2] == (3, 4), 'PyYAML does not support Python 3.4')
# def test_required_option_file_does_not_exist(self):
# with self.assertRaises(IOError):
# self.config.option.from_yaml('./does_not_exist.yml', required=True)
#
# @unittest.skipIf(sys.version_info[:2] == (3, 4), 'PyYAML does not support Python 3.4')
# 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(), {})
#
# @unittest.skipIf(sys.version_info[:2] == (3, 4), 'PyYAML does not support Python 3.4')
# 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_pydantic_installed(self):
@contextlib.contextmanager
def no_pydantic_module():
pydantic = providers.pydantic
providers.pydantic = None
yield
providers.pydantic = pydantic
with no_pydantic_module():
with self.assertRaises(errors.Error) as error:
self.config.from_pydantic(self.Settings1())
self.assertEqual(
error.exception.args[0],
'Unable to load pydantic configuration - pydantic is not installed. '
'Install pydantic or install Dependency Injector with pydantic extras: '
'"pip install dependency-injector[pydantic]"',
)
class ConfigFromDict(unittest.TestCase):
def setUp(self):