From 779a2fbfcffdcc1dd8b5e4e79835edf46a84399a Mon Sep 17 00:00:00 2001 From: warmagedon007 Date: Mon, 11 May 2026 17:20:16 +0300 Subject: [PATCH 1/3] added a support to get pydantic class type instead of instance --- src/dependency_injector/providers.pyx | 5 +--- .../configuration/test_from_pydantic_py36.py | 26 ++++++++++--------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/dependency_injector/providers.pyx b/src/dependency_injector/providers.pyx index 4d4d5c39..edde7b1b 100644 --- a/src/dependency_injector/providers.pyx +++ b/src/dependency_injector/providers.pyx @@ -157,10 +157,7 @@ cdef dict pydantic_settings_to_dict(settings, dict kwargs): ) if isinstance(settings, type) and issubclass(settings, PydanticSettings): - raise Error( - "Got settings class, but expect instance: " - "instead \"{0}\" use \"{0}()\"".format(settings.__name__) - ) + settings = settings() if not isinstance(settings, PydanticSettings): raise Error( diff --git a/tests/unit/providers/configuration/test_from_pydantic_py36.py b/tests/unit/providers/configuration/test_from_pydantic_py36.py index ee2a1176..f47e458c 100644 --- a/tests/unit/providers/configuration/test_from_pydantic_py36.py +++ b/tests/unit/providers/configuration/test_from_pydantic_py36.py @@ -168,21 +168,23 @@ def test_option_not_instance_of_settings(config): def test_subclass_instead_of_instance(config): - with raises(errors.Error) as error: - config.from_pydantic(Settings1) - assert error.value.args[0] == ( - "Got settings class, but expect instance: " - "instead \"Settings1\" use \"Settings1()\"" - ) + config.from_pydantic(Settings1) + + assert config() == {"section1": {"value1": 1}, "section2": {"value2": 2}} + assert config.section1() == {"value1": 1} + assert config.section1.value1() == 1 + assert config.section2() == {"value2": 2} + assert config.section2.value2() == 2 def test_option_subclass_instead_of_instance(config): - with raises(errors.Error) as error: - config.option.from_pydantic(Settings1) - assert error.value.args[0] == ( - "Got settings class, but expect instance: " - "instead \"Settings1\" use \"Settings1()\"" - ) + config.option.from_pydantic(Settings1) + + assert config.option() == {"section1": {"value1": 1}, "section2": {"value2": 2}} + assert config.option.section1() == {"value1": 1} + assert config.option.section1.value1() == 1 + assert config.option.section2() == {"value2": 2} + assert config.option.section2.value2() == 2 @mark.usefixtures("no_pydantic_module_installed") From e71a2b34a6734120e9d70dc29cbf0f170e02dec0 Mon Sep 17 00:00:00 2001 From: warmagedon007 Date: Tue, 19 May 2026 10:47:53 +0300 Subject: [PATCH 2/3] updated configuration provider interface and doc --- docs/providers/configuration.rst | 13 +++++++++++++ src/dependency_injector/providers.pyi | 8 ++++---- tests/typing/configuration.py | 7 +++++++ 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/docs/providers/configuration.rst b/docs/providers/configuration.rst index 66c299f3..651bf772 100644 --- a/docs/providers/configuration.rst +++ b/docs/providers/configuration.rst @@ -212,6 +212,19 @@ the container will call ``config.from_pydantic()`` automatically: if __name__ == "__main__": container = Container() # Config is loaded from Settings() +In addition, if you need the pydantic instance to be initialized on use, you can provide ``pydantic_settings.BaseSettings`` type instead. +The container will initialize a pydantic instance on load without kwargs. + +.. code-block:: python + :emphasize-lines: 3 + + class Container(containers.DeclarativeContainer): + + config = providers.Configuration(pydantic_settings=[Settings]) + + + if __name__ == "__main__": + container = Container() # Config is loaded from Settings instance that is initialized .. note:: diff --git a/src/dependency_injector/providers.pyi b/src/dependency_injector/providers.pyi index e4211e00..ecfdc93b 100644 --- a/src/dependency_injector/providers.pyi +++ b/src/dependency_injector/providers.pyi @@ -272,7 +272,7 @@ class Configuration(Object[Any]): ini_files: Optional[_Iterable[Union[Path, str]]] = None, yaml_files: Optional[_Iterable[Union[Path, str]]] = None, json_files: Optional[_Iterable[Union[Path, str]]] = None, - pydantic_settings: Optional[_Iterable[PydanticSettings]] = None, + pydantic_settings: Optional[_Iterable[Union[PydanticSettings, Type[PydanticSettings]]]] = None, ) -> None: ... def __enter__(self) -> _Self: ... def __exit__(self, *exc_info: Any) -> None: ... @@ -292,8 +292,8 @@ class Configuration(Object[Any]): def set_yaml_files(self, files: _Iterable[Union[Path, str]]) -> _Self: ... def get_json_files(self) -> _List[Union[Path, str]]: ... def set_json_files(self, files: _Iterable[Union[Path, str]]) -> _Self: ... - def get_pydantic_settings(self) -> _List[PydanticSettings]: ... - def set_pydantic_settings(self, settings: _Iterable[PydanticSettings]) -> _Self: ... + def get_pydantic_settings(self) -> _List[Union[PydanticSettings, Type[PydanticSettings]]]: ... + def set_pydantic_settings(self, settings: _Iterable[Union[PydanticSettings, Type[PydanticSettings]]]) -> _Self: ... def load(self, required: bool = False, envs_required: bool = False) -> None: ... def get(self, selector: str) -> Any: ... def set(self, selector: str, value: Any) -> OverridingContext[P]: ... @@ -319,7 +319,7 @@ class Configuration(Object[Any]): envs_required: bool = False, ) -> None: ... def from_pydantic( - self, settings: PydanticSettings, required: bool = False, **kwargs: Any + self, settings: Union[PydanticSettings, Type[PydanticSettings]], required: bool = False, **kwargs: Any ) -> None: ... def from_dict(self, options: _Dict[str, Any], required: bool = False) -> None: ... def from_env( diff --git a/tests/typing/configuration.py b/tests/typing/configuration.py index 9c1dfea0..07ec5c38 100644 --- a/tests/typing/configuration.py +++ b/tests/typing/configuration.py @@ -33,6 +33,8 @@ config2.from_pydantic(PydanticSettings()) +config2.from_pydantic(PydanticSettings) + # Test 3: to check as_*() methods config3 = providers.Configuration() int3 = config3.option.as_int() @@ -91,3 +93,8 @@ strict=True, default={}, ) + +# Test 7: pydantic class +config7_pydantic_class = providers.Configuration( + pydantic_settings=[PydanticSettings] +) \ No newline at end of file From db6b5f582fed5d441008c35d5b5d23ee59925018 Mon Sep 17 00:00:00 2001 From: ZipFile Date: Tue, 19 May 2026 14:09:49 +0000 Subject: [PATCH 3/3] Fix mymy warnings --- src/dependency_injector/providers.pyi | 12 +++++------- tests/typing/configuration.py | 11 +++++------ 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/dependency_injector/providers.pyi b/src/dependency_injector/providers.pyi index ecfdc93b..e8542eeb 100644 --- a/src/dependency_injector/providers.pyi +++ b/src/dependency_injector/providers.pyi @@ -30,9 +30,12 @@ except ImportError: yaml = None try: - import pydantic + from pydantic_settings import BaseSettings as PydanticSettings except ImportError: - pydantic = None + try: + from pydantic import BaseSettings as PydanticSettings + except ImportError: + PydanticSettings = Any from . import resources @@ -630,8 +633,3 @@ if yaml: else: class YamlLoader: ... - -if pydantic: - PydanticSettings = pydantic.BaseSettings -else: - PydanticSettings = Any diff --git a/tests/typing/configuration.py b/tests/typing/configuration.py index 07ec5c38..cea97f76 100644 --- a/tests/typing/configuration.py +++ b/tests/typing/configuration.py @@ -1,5 +1,5 @@ from pathlib import Path -from typing import Any, Dict +from typing import Any, Dict, Type from typing_extensions import assert_type from pydantic_settings import BaseSettings as PydanticSettings @@ -82,10 +82,9 @@ ) config5_pydantic.set_pydantic_settings([PydanticSettings()]) -# NOTE: Using assignment since PydanticSettings is context-sensitive: conditional on whether pydantic is installed -config5_pydantic_settings: list[PydanticSettings] = ( - config5_pydantic.get_pydantic_settings() -) +config5_pydantic_settings = config5_pydantic.get_pydantic_settings() + +assert_type(config5_pydantic_settings, list[PydanticSettings | Type[PydanticSettings]]) # Test 6: to check init arguments config6 = providers.Configuration( @@ -97,4 +96,4 @@ # Test 7: pydantic class config7_pydantic_class = providers.Configuration( pydantic_settings=[PydanticSettings] -) \ No newline at end of file +)