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