Add full test coverage + bugfix

This commit is contained in:
Roman Mogylatov 2021-02-02 18:02:14 -05:00
parent 1005c51bd0
commit 24ea8acc7c
4 changed files with 4416 additions and 4141 deletions

File diff suppressed because it is too large Load Diff

View File

@ -26,6 +26,11 @@ try:
except ImportError: except ImportError:
yaml = None yaml = None
try:
import pydantic
except ImportError:
pydantic = None
from . import resources from . import resources
@ -162,6 +167,7 @@ class ConfigurationOption(Provider[Any]):
def update(self, value: Any) -> None: ... def update(self, value: Any) -> None: ...
def from_ini(self, filepath: Union[Path, str], required: bool = False) -> None: ... def from_ini(self, filepath: Union[Path, str], required: bool = False) -> None: ...
def from_yaml(self, filepath: Union[Path, str], required: bool = False, loader: Optional[Any]=None) -> None: ... def from_yaml(self, filepath: Union[Path, str], required: bool = False, loader: Optional[Any]=None) -> 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: ...
@ -183,6 +189,7 @@ class Configuration(Object[Any]):
def update(self, value: Any) -> None: ... def update(self, value: Any) -> None: ...
def from_ini(self, filepath: Union[Path, str], required: bool = False) -> None: ... def from_ini(self, filepath: Union[Path, str], required: bool = False) -> None: ...
def from_yaml(self, filepath: Union[Path, str], required: bool = False, loader: Optional[Any]=None) -> None: ... def from_yaml(self, filepath: Union[Path, str], required: bool = False, loader: Optional[Any]=None) -> 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: ...
@ -397,3 +404,8 @@ if yaml:
class YamlLoader(yaml.SafeLoader): ... class YamlLoader(yaml.SafeLoader): ...
else: else:
class YamlLoader: ... class YamlLoader: ...
if pydantic:
PydanticSettings = pydantic.BaseSettings
else:
PydanticSettings = Any

View File

@ -1460,6 +1460,12 @@ cdef class ConfigurationOption(Provider):
'"pip install dependency-injector[pydantic]"' '"pip install dependency-injector[pydantic]"'
) )
if isinstance(settings, CLASS_TYPES) and issubclass(settings, pydantic.BaseSettings):
raise Error(
'Got settings class, but expect instance: '
'instead "{0}" use "{0}()"'.format(settings.__name__)
)
if not isinstance(settings, pydantic.BaseSettings): if not isinstance(settings, pydantic.BaseSettings):
raise Error( raise Error(
'Unable to recognize settings instance, expect "pydantic.BaseSettings", ' 'Unable to recognize settings instance, expect "pydantic.BaseSettings", '
@ -1824,6 +1830,12 @@ cdef class Configuration(Object):
'"pip install dependency-injector[pydantic]"' '"pip install dependency-injector[pydantic]"'
) )
if isinstance(settings, CLASS_TYPES) and issubclass(settings, pydantic.BaseSettings):
raise Error(
'Got settings class, but expect instance: '
'instead "{0}" use "{0}()"'.format(settings.__name__)
)
if not isinstance(settings, pydantic.BaseSettings): if not isinstance(settings, pydantic.BaseSettings):
raise Error( raise Error(
'Unable to recognize settings instance, expect "pydantic.BaseSettings", ' 'Unable to recognize settings instance, expect "pydantic.BaseSettings", '

View File

@ -754,7 +754,7 @@ class ConfigFromPydanticTests(unittest.TestCase):
class Settings2(pydantic.BaseSettings): class Settings2(pydantic.BaseSettings):
section1 = Section21() section1 = Section21()
section2 = Section3() section3 = Section3()
self.Settings2 = Settings2 self.Settings2 = Settings2
@ -767,78 +767,109 @@ class ConfigFromPydanticTests(unittest.TestCase):
self.assertEqual(self.config.section2(), {'value2': 2}) self.assertEqual(self.config.section2(), {'value2': 2})
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):
# def test_merge(self): self.config.from_pydantic(self.Settings1())
# self.config.from_yaml(self.config_file_1) self.config.from_pydantic(self.Settings2())
# self.config.from_yaml(self.config_file_2)
# self.assertEqual(
# self.assertEqual( self.config(),
# self.config(), {
# { 'section1': {
# 'section1': { 'value1': 11,
# 'value1': 11, 'value11': 11,
# 'value11': 11, },
# }, 'section2': {
# 'section2': { 'value2': 2,
# 'value2': 2, },
# }, 'section3': {
# 'section3': { 'value3': 3,
# 'value3': 3, },
# }, },
# }, )
# ) self.assertEqual(self.config.section1(), {'value1': 11, 'value11': 11})
# self.assertEqual(self.config.section1(), {'value1': 11, 'value11': 11}) self.assertEqual(self.config.section1.value1(), 11)
# self.assertEqual(self.config.section1.value1(), 11) self.assertEqual(self.config.section1.value11(), 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.section2.value2(), 2)
# self.assertEqual(self.config.section2.value2(), 2) self.assertEqual(self.config.section3(), {'value3': 3})
# self.assertEqual(self.config.section3(), {'value3': 3}) self.assertEqual(self.config.section3.value3(), 3)
# self.assertEqual(self.config.section3.value3(), 3)
# def test_empty_settings(self):
# @unittest.skipIf(sys.version_info[:2] == (3, 4), 'PyYAML does not support Python 3.4') self.config.from_pydantic(pydantic.BaseSettings())
# def test_file_does_not_exist(self): self.assertEqual(self.config(), {})
# self.config.from_yaml('./does_not_exist.yml')
# self.assertEqual(self.config(), {}) def test_empty_settings_strict_mode(self):
# self.config = providers.Configuration(strict=True)
# @unittest.skipIf(sys.version_info[:2] == (3, 4), 'PyYAML does not support Python 3.4') with self.assertRaises(ValueError):
# def test_file_does_not_exist_strict_mode(self): self.config.from_pydantic(pydantic.BaseSettings())
# self.config = providers.Configuration(strict=True)
# with self.assertRaises(IOError): def test_option_empty_settings(self):
# self.config.from_yaml('./does_not_exist.yml') self.config.option.from_pydantic(pydantic.BaseSettings())
# self.assertEqual(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(self): def test_option_empty_settings_strict_mode(self):
# self.config.option.from_yaml('./does_not_exist.yml') self.config = providers.Configuration(strict=True)
# self.assertIsNone(self.config.option()) with self.assertRaises(ValueError):
# self.config.option.from_pydantic(pydantic.BaseSettings())
# @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): def test_required_empty_settings(self):
# self.config = providers.Configuration(strict=True) with self.assertRaises(ValueError):
# with self.assertRaises(IOError): self.config.from_pydantic(pydantic.BaseSettings(), required=True)
# self.config.option.from_yaml('./does_not_exist.yml')
# def test_required_option_empty_settings(self):
# @unittest.skipIf(sys.version_info[:2] == (3, 4), 'PyYAML does not support Python 3.4') with self.assertRaises(ValueError):
# def test_required_file_does_not_exist(self): self.config.option.from_pydantic(pydantic.BaseSettings(), required=True)
# with self.assertRaises(IOError):
# self.config.from_yaml('./does_not_exist.yml', required=True) def test_not_required_empty_settings_strict_mode(self):
# self.config = providers.Configuration(strict=True)
# @unittest.skipIf(sys.version_info[:2] == (3, 4), 'PyYAML does not support Python 3.4') self.config.from_pydantic(pydantic.BaseSettings(), required=False)
# def test_required_option_file_does_not_exist(self): self.assertEqual(self.config(), {})
# with self.assertRaises(IOError):
# self.config.option.from_yaml('./does_not_exist.yml', required=True) def test_not_required_option_empty_settings_strict_mode(self):
# self.config = providers.Configuration(strict=True)
# @unittest.skipIf(sys.version_info[:2] == (3, 4), 'PyYAML does not support Python 3.4') self.config.option.from_pydantic(pydantic.BaseSettings(), required=False)
# def test_not_required_file_does_not_exist_strict_mode(self): self.assertEqual(self.config.option(), {})
# self.config = providers.Configuration(strict=True) self.assertEqual(self.config(), {'option': {}})
# self.config.from_yaml('./does_not_exist.yml', required=False)
# self.assertEqual(self.config(), {}) def test_not_instance_of_settings(self):
# with self.assertRaises(errors.Error) as error:
# @unittest.skipIf(sys.version_info[:2] == (3, 4), 'PyYAML does not support Python 3.4') self.config.from_pydantic({})
# def test_not_required_option_file_does_not_exist_strict_mode(self):
# self.config = providers.Configuration(strict=True) self.assertEqual(
# self.config.option.from_yaml('./does_not_exist.yml', required=False) error.exception.args[0],
# with self.assertRaises(errors.Error): 'Unable to recognize settings instance, expect "pydantic.BaseSettings", '
# self.config.option() 'got {0} instead'.format({})
)
def test_option_not_instance_of_settings(self):
with self.assertRaises(errors.Error) as error:
self.config.option.from_pydantic({})
self.assertEqual(
error.exception.args[0],
'Unable to recognize settings instance, expect "pydantic.BaseSettings", '
'got {0} instead'.format({})
)
def test_subclass_instead_of_instance(self):
with self.assertRaises(errors.Error) as error:
self.config.from_pydantic(self.Settings1)
self.assertEqual(
error.exception.args[0],
'Got settings class, but expect instance: '
'instead "Settings1" use "Settings1()"'
)
def test_option_subclass_instead_of_instance(self):
with self.assertRaises(errors.Error) as error:
self.config.option.from_pydantic(self.Settings1)
self.assertEqual(
error.exception.args[0],
'Got settings class, but expect instance: '
'instead "Settings1" use "Settings1()"'
)
def test_no_pydantic_installed(self): def test_no_pydantic_installed(self):
@contextlib.contextmanager @contextlib.contextmanager
@ -861,6 +892,27 @@ class ConfigFromPydanticTests(unittest.TestCase):
'"pip install dependency-injector[pydantic]"', '"pip install dependency-injector[pydantic]"',
) )
def test_option_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.option.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): class ConfigFromDict(unittest.TestCase):