mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2024-11-22 01:26:51 +03:00
Added support for pydantic-settings in Configuration.from_pydantic() method
This commit is contained in:
parent
cc2304e46e
commit
18143a4e44
|
@ -12,6 +12,7 @@ pyyaml
|
|||
httpx
|
||||
fastapi
|
||||
pydantic
|
||||
pydantic-settings
|
||||
numpy
|
||||
scipy
|
||||
boto3
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
flask
|
||||
flask~=2.2.2
|
||||
Werkzeug~=2.2.2
|
||||
aiohttp
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -27,9 +27,12 @@ except ImportError:
|
|||
yaml = None
|
||||
|
||||
try:
|
||||
import pydantic
|
||||
import pydantic_settings
|
||||
except ImportError:
|
||||
pydantic = None
|
||||
try:
|
||||
import pydantic as pydantic_settings
|
||||
except ImportError:
|
||||
pydantic_settings = None
|
||||
|
||||
from . import resources
|
||||
|
||||
|
@ -544,7 +547,7 @@ if yaml:
|
|||
else:
|
||||
class YamlLoader: ...
|
||||
|
||||
if pydantic:
|
||||
PydanticSettings = pydantic.BaseSettings
|
||||
if pydantic_settings:
|
||||
PydanticSettings = pydantic_settings.BaseSettings
|
||||
else:
|
||||
PydanticSettings = Any
|
||||
|
|
|
@ -49,9 +49,12 @@ except ImportError:
|
|||
yaml = None
|
||||
|
||||
try:
|
||||
import pydantic
|
||||
import pydantic_settings
|
||||
except ImportError:
|
||||
pydantic = None
|
||||
try:
|
||||
import pydantic as pydantic_settings
|
||||
except ImportError:
|
||||
pydantic_settings = None
|
||||
|
||||
from .errors import (
|
||||
Error,
|
||||
|
@ -1786,36 +1789,38 @@ cdef class ConfigurationOption(Provider):
|
|||
Loaded configuration is merged recursively over existing configuration.
|
||||
|
||||
:param settings: Pydantic settings instances.
|
||||
:type settings: :py:class:`pydantic.BaseSettings`
|
||||
:type settings: :py:class:`pydantic.BaseSettings` or :py:class:`pydantic_settings.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.
|
||||
:param kwargs: Keyword arguments forwarded to ``pydantic.BaseSettings.dict()``
|
||||
or ``pydantic_settings.BaseSettings.model_dump()`` call, depending on the used package.
|
||||
:type kwargs: Dict[Any, Any]
|
||||
|
||||
:rtype: None
|
||||
"""
|
||||
if pydantic is None:
|
||||
if pydantic_settings is None:
|
||||
raise Error(
|
||||
"Unable to load pydantic configuration - pydantic is not installed. "
|
||||
"Install pydantic or install Dependency Injector with pydantic extras: "
|
||||
"Unable to load pydantic configuration - neither pydantic_settings nor pydantic v1 is not installed. "
|
||||
"Install pydantic_settings or pydantic v1 or install Dependency Injector with pydantic extras: "
|
||||
"\"pip install dependency-injector[pydantic]\""
|
||||
)
|
||||
|
||||
if isinstance(settings, CLASS_TYPES) and issubclass(settings, pydantic.BaseSettings):
|
||||
if isinstance(settings, CLASS_TYPES) and issubclass(settings, pydantic_settings.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_settings.BaseSettings):
|
||||
raise Error(
|
||||
"Unable to recognize settings instance, expect \"pydantic.BaseSettings\", "
|
||||
"got {0} instead".format(settings)
|
||||
)
|
||||
|
||||
self.from_dict(settings.dict(**kwargs), required=required)
|
||||
settings_dict = settings.model_dump(**kwargs) if hasattr(settings, "model_dump") else settings.dict(**kwargs)
|
||||
self.from_dict(settings_dict, required=required)
|
||||
|
||||
def from_dict(self, options, required=UNDEFINED):
|
||||
"""Load configuration from the dictionary.
|
||||
|
@ -2355,36 +2360,38 @@ cdef class Configuration(Object):
|
|||
Loaded configuration is merged recursively over existing configuration.
|
||||
|
||||
:param settings: Pydantic settings instances.
|
||||
:type settings: :py:class:`pydantic.BaseSettings`
|
||||
:type settings: :py:class:`pydantic.BaseSettings` or :py:class:`pydantic_settings.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.
|
||||
:param kwargs: Keyword arguments forwarded to ``pydantic.BaseSettings.dict()``
|
||||
or ``pydantic.BaseSettings.model_dump call()`` call, depending on the used package.
|
||||
:type kwargs: Dict[Any, Any]
|
||||
|
||||
:rtype: None
|
||||
"""
|
||||
if pydantic is None:
|
||||
if pydantic_settings is None:
|
||||
raise Error(
|
||||
"Unable to load pydantic configuration - pydantic is not installed. "
|
||||
"Install pydantic or install Dependency Injector with pydantic extras: "
|
||||
"Unable to load pydantic configuration - neither pydantic_settings nor pydantic v1 is not installed. "
|
||||
"Install pydantic_settings or pydantic v1 or install Dependency Injector with pydantic extras: "
|
||||
"\"pip install dependency-injector[pydantic]\""
|
||||
)
|
||||
|
||||
if isinstance(settings, CLASS_TYPES) and issubclass(settings, pydantic.BaseSettings):
|
||||
if isinstance(settings, CLASS_TYPES) and issubclass(settings, pydantic_settings.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_settings.BaseSettings):
|
||||
raise Error(
|
||||
"Unable to recognize settings instance, expect \"pydantic.BaseSettings\", "
|
||||
"got {0} instead".format(settings)
|
||||
)
|
||||
|
||||
self.from_dict(settings.dict(**kwargs), required=required)
|
||||
settings_dict = settings.model_dump(**kwargs) if hasattr(settings, "model_dump") else settings.dict(**kwargs)
|
||||
self.from_dict(settings_dict, required=required)
|
||||
|
||||
def from_dict(self, options, required=UNDEFINED):
|
||||
"""Load configuration from the dictionary.
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
from pathlib import Path
|
||||
|
||||
from dependency_injector import providers
|
||||
from pydantic import BaseSettings as PydanticSettings
|
||||
|
||||
try:
|
||||
from pydantic_settings import BaseSettings as PydanticSettings
|
||||
except ImportError:
|
||||
from pydantic import BaseSettings as PydanticSettings
|
||||
|
||||
|
||||
# Test 1: to check the getattr
|
||||
|
|
|
@ -4,38 +4,44 @@ import pydantic
|
|||
from dependency_injector import providers, errors
|
||||
from pytest import fixture, mark, raises
|
||||
|
||||
try:
|
||||
from pydantic_settings import BaseSettings
|
||||
except ImportError:
|
||||
BaseSettings = pydantic.BaseSettings
|
||||
|
||||
|
||||
class Section11(pydantic.BaseModel):
|
||||
value1 = 1
|
||||
value1: int = 1
|
||||
|
||||
|
||||
class Section12(pydantic.BaseModel):
|
||||
value2 = 2
|
||||
value2: int = 2
|
||||
|
||||
|
||||
class Settings1(pydantic.BaseSettings):
|
||||
section1 = Section11()
|
||||
section2 = Section12()
|
||||
class Settings1(BaseSettings):
|
||||
section1: Section11 = Section11()
|
||||
section2: Section12 = Section12()
|
||||
|
||||
|
||||
class Section21(pydantic.BaseModel):
|
||||
value1 = 11
|
||||
value11 = 11
|
||||
value1: int = 11
|
||||
value11: int = 11
|
||||
|
||||
|
||||
class Section3(pydantic.BaseModel):
|
||||
value3 = 3
|
||||
value3: int = 3
|
||||
|
||||
|
||||
class Settings2(pydantic.BaseSettings):
|
||||
section1 = Section21()
|
||||
section3 = Section3()
|
||||
class Settings2(BaseSettings):
|
||||
section1: Section21 = Section21()
|
||||
section3: Section3 = Section3()
|
||||
|
||||
@fixture
|
||||
def no_pydantic_module_installed():
|
||||
providers.pydantic = None
|
||||
pydantic_setting = providers.pydantic_settings
|
||||
providers.pydantic_settings = None
|
||||
yield
|
||||
providers.pydantic = pydantic
|
||||
providers.pydantic_settings = pydantic_setting
|
||||
|
||||
|
||||
def test(config):
|
||||
|
@ -82,46 +88,46 @@ def test_merge(config):
|
|||
|
||||
|
||||
def test_empty_settings(config):
|
||||
config.from_pydantic(pydantic.BaseSettings())
|
||||
config.from_pydantic(BaseSettings())
|
||||
assert config() == {}
|
||||
|
||||
|
||||
@mark.parametrize("config_type", ["strict"])
|
||||
def test_empty_settings_strict_mode(config):
|
||||
with raises(ValueError):
|
||||
config.from_pydantic(pydantic.BaseSettings())
|
||||
config.from_pydantic(BaseSettings())
|
||||
|
||||
|
||||
def test_option_empty_settings(config):
|
||||
config.option.from_pydantic(pydantic.BaseSettings())
|
||||
config.option.from_pydantic(BaseSettings())
|
||||
assert config.option() == {}
|
||||
|
||||
|
||||
@mark.parametrize("config_type", ["strict"])
|
||||
def test_option_empty_settings_strict_mode(config):
|
||||
with raises(ValueError):
|
||||
config.option.from_pydantic(pydantic.BaseSettings())
|
||||
config.option.from_pydantic(BaseSettings())
|
||||
|
||||
|
||||
def test_required_empty_settings(config):
|
||||
with raises(ValueError):
|
||||
config.from_pydantic(pydantic.BaseSettings(), required=True)
|
||||
config.from_pydantic(BaseSettings(), required=True)
|
||||
|
||||
|
||||
def test_required_option_empty_settings(config):
|
||||
with raises(ValueError):
|
||||
config.option.from_pydantic(pydantic.BaseSettings(), required=True)
|
||||
config.option.from_pydantic(BaseSettings(), required=True)
|
||||
|
||||
|
||||
@mark.parametrize("config_type", ["strict"])
|
||||
def test_not_required_empty_settings_strict_mode(config):
|
||||
config.from_pydantic(pydantic.BaseSettings(), required=False)
|
||||
config.from_pydantic(BaseSettings(), required=False)
|
||||
assert config() == {}
|
||||
|
||||
|
||||
@mark.parametrize("config_type", ["strict"])
|
||||
def test_not_required_option_empty_settings_strict_mode(config):
|
||||
config.option.from_pydantic(pydantic.BaseSettings(), required=False)
|
||||
config.option.from_pydantic(BaseSettings(), required=False)
|
||||
assert config.option() == {}
|
||||
assert config() == {"option": {}}
|
||||
|
||||
|
@ -167,8 +173,8 @@ def test_no_pydantic_installed(config):
|
|||
with raises(errors.Error) as error:
|
||||
config.from_pydantic(Settings1())
|
||||
assert error.value.args[0] == (
|
||||
"Unable to load pydantic configuration - pydantic is not installed. "
|
||||
"Install pydantic or install Dependency Injector with pydantic extras: "
|
||||
"Unable to load pydantic configuration - neither pydantic_settings nor pydantic v1 is not installed. "
|
||||
"Install pydantic_settings or pydantic v1 or install Dependency Injector with pydantic extras: "
|
||||
"\"pip install dependency-injector[pydantic]\""
|
||||
)
|
||||
|
||||
|
@ -178,7 +184,7 @@ def test_option_no_pydantic_installed(config):
|
|||
with raises(errors.Error) as error:
|
||||
config.option.from_pydantic(Settings1())
|
||||
assert error.value.args[0] == (
|
||||
"Unable to load pydantic configuration - pydantic is not installed. "
|
||||
"Install pydantic or install Dependency Injector with pydantic extras: "
|
||||
"Unable to load pydantic configuration - neither pydantic_settings nor pydantic v1 is not installed. "
|
||||
"Install pydantic_settings or pydantic v1 or install Dependency Injector with pydantic extras: "
|
||||
"\"pip install dependency-injector[pydantic]\""
|
||||
)
|
||||
|
|
|
@ -5,31 +5,37 @@ from dependency_injector import providers
|
|||
from pytest import fixture, mark, raises
|
||||
|
||||
|
||||
try:
|
||||
from pydantic_settings import BaseSettings
|
||||
except ImportError:
|
||||
BaseSettings = pydantic.BaseSettings
|
||||
|
||||
|
||||
class Section11(pydantic.BaseModel):
|
||||
value1 = 1
|
||||
value1: int = 1
|
||||
|
||||
|
||||
class Section12(pydantic.BaseModel):
|
||||
value2 = 2
|
||||
value2: int = 2
|
||||
|
||||
|
||||
class Settings1(pydantic.BaseSettings):
|
||||
section1 = Section11()
|
||||
section2 = Section12()
|
||||
class Settings1(BaseSettings):
|
||||
section1: Section11 = Section11()
|
||||
section2: Section12 = Section12()
|
||||
|
||||
|
||||
class Section21(pydantic.BaseModel):
|
||||
value1 = 11
|
||||
value11 = 11
|
||||
value1: int = 11
|
||||
value11: int = 11
|
||||
|
||||
|
||||
class Section3(pydantic.BaseModel):
|
||||
value3 = 3
|
||||
value3: int = 3
|
||||
|
||||
|
||||
class Settings2(pydantic.BaseSettings):
|
||||
section1 = Section21()
|
||||
section3 = Section3()
|
||||
class Settings2(BaseSettings):
|
||||
section1: Section21 = Section21()
|
||||
section3: Section3 = Section3()
|
||||
|
||||
|
||||
@fixture
|
||||
|
@ -86,10 +92,10 @@ def test_copy(config, pydantic_settings_1, pydantic_settings_2):
|
|||
|
||||
|
||||
def test_set_pydantic_settings(config):
|
||||
class Settings3(pydantic.BaseSettings):
|
||||
class Settings3(BaseSettings):
|
||||
...
|
||||
|
||||
class Settings4(pydantic.BaseSettings):
|
||||
class Settings4(BaseSettings):
|
||||
...
|
||||
|
||||
settings_3 = Settings3()
|
||||
|
@ -100,27 +106,27 @@ def test_set_pydantic_settings(config):
|
|||
|
||||
|
||||
def test_file_does_not_exist(config):
|
||||
config.set_pydantic_settings([pydantic.BaseSettings()])
|
||||
config.set_pydantic_settings([BaseSettings()])
|
||||
config.load()
|
||||
assert config() == {}
|
||||
|
||||
|
||||
@mark.parametrize("config_type", ["strict"])
|
||||
def test_file_does_not_exist_strict_mode(config):
|
||||
config.set_pydantic_settings([pydantic.BaseSettings()])
|
||||
config.set_pydantic_settings([BaseSettings()])
|
||||
with raises(ValueError):
|
||||
config.load()
|
||||
assert config() == {}
|
||||
|
||||
|
||||
def test_required_file_does_not_exist(config):
|
||||
config.set_pydantic_settings([pydantic.BaseSettings()])
|
||||
config.set_pydantic_settings([BaseSettings()])
|
||||
with raises(ValueError):
|
||||
config.load(required=True)
|
||||
|
||||
|
||||
@mark.parametrize("config_type", ["strict"])
|
||||
def test_not_required_file_does_not_exist_strict_mode(config):
|
||||
config.set_pydantic_settings([pydantic.BaseSettings()])
|
||||
config.set_pydantic_settings([BaseSettings()])
|
||||
config.load(required=False)
|
||||
assert config() == {}
|
||||
|
|
Loading…
Reference in New Issue
Block a user