-
-
Notifications
You must be signed in to change notification settings - Fork 115
Description
Description
I inherit from the generic Secret to override the behavior of the _display method. I get something like SecretMultiHostUrl[MongoDsn]. Example:
from pydantic import Secret
MultiHostUrlType = TypeVar("MultiHostUrlType", bound=MultiHostUrl)
class SecretMultiHostUrl(Secret[MultiHostUrlType]):
def __init__(self, url: MultiHostUrlType) -> None:
super().__init__(url)
self._display_value = ...
def _display(self) -> str:
return str(self._display_value)
class SettingsMongo(BaseSettings):
mongo_uri: SecretMultiHostUrl[MongoDsn]
I get mongo_uri from the environment variables, but eventually the app crashes with an error:
raise SettingsError(
f'error parsing value for field "{field_name}" from source "{self.__class__.__name__}"'
) from e
E pydantic_settings.sources.SettingsError: error parsing value for field "mongo_uri" from source "EnvSettingsSource"
This error occurs because the field is considered complex. In the method _annotation_is_complex(annotation: type[Any] | None, metadata: list[Any])
annotation = __main__.SecretMultiHostUrl[typing.Annotated[pydantic_core._pydantic_core.MultiHostUrl, UrlConstraints(max_length=None, allowed_schemes=['mongodb', 'mongodb+srv'], host_required=None, default_host=None, default_port=27017, default_path=None)]]
origin = <class '__main__.SecretMultiHostUrl'>. At the time of checking for the origin attribute __get_pydantic_core_schema__ I get True. Therefore, the field is considered complex, and at the moment of decoding this field, the code crashes with the error JSONDecodeError.
I would like to know if this is a bug or an expected behavior. Maybe there are standard ways to solve this problem?
I know that in pydantic-settings 2.7.0 there is a NoDecode that solves this problem. I also know that I can provide my own source class in Settings model. But these methods are not suitable in my case.
Traceback
self = EnvSettingsSource(env_nested_delimiter=None, env_prefix_len=0)
def __call__(self) -> dict[str, Any]:
data: dict[str, Any] = {}
for field_name, field in self.settings_cls.model_fields.items():
try:
field_value, field_key, value_is_complex = self.get_field_value(field, field_name)
except Exception as e:
raise SettingsError(
f'error getting value for field "{field_name}" from source "{self.__class__.__name__}"'
) from e
try:
> field_value = self.prepare_field_value(field_name, field, field_value, value_is_complex)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
..\venv\Lib\site-packages\pydantic_settings\sources.py:323:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
..\venv\Lib\site-packages\pydantic_settings\sources.py:513: in prepare_field_value
raise e
..\venv\Lib\site-packages\pydantic_settings\sources.py:510: in prepare_field_value
value = self.decode_complex_value(field_name, field, value)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
..\venv\Lib\site-packages\pydantic_settings\sources.py:147: in decode_complex_value
return json.loads(value)
^^^^^^^^^^^^^^^^^
..\..\..\.pyenv\pyenv-win\versions\3.12.0\Lib\json\__init__.py:346: in loads
return _default_decoder.decode(s)
^^^^^^^^^^^^^^^^^^^^^^^^^^
..\..\..\.pyenv\pyenv-win\versions\3.12.0\Lib\json\decoder.py:337: in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <json.decoder.JSONDecoder object at 0x0000026DB1D7AE40>
s = 'mongodb://ycrawford:password@email-66.hall.info:27030/situation?replicaSet=myRepl'
idx = 0
def raw_decode(self, s, idx=0):
"""Decode a JSON document from ``s`` (a ``str`` beginning with
a JSON document) and return a 2-tuple of the Python
representation and the index in ``s`` where the document ended.
This can be used to decode a JSON document from a string that may
have extraneous data at the end.
"""
try:
obj, end = self.scan_once(s, idx)
except StopIteration as err:
> raise JSONDecodeError("Expecting value", s, err.value) from None
E json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
..\..\..\.pyenv\pyenv-win\versions\3.12.0\Lib\json\decoder.py:355: JSONDecodeError
The above exception was the direct cause of the following exception:
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x0000026DB5BCDFA0>
settings_factory_mongo = <function settings_factory_mongo.<locals>._factory at 0x0000026DB5BDF4C0>
password = 'password'
mongo_uri_with_auth = 'mongodb://ycrawford:password@email-66.hall.info:27030/situation?replicaSet=myRepl'
def test_secret_mongo_dsn(monkeypatch, settings_factory_mongo, password, mongo_uri_with_auth):
monkeypatch.setenv('MONGO_URI', mongo_uri_with_auth)
> settings = settings_factory_mongo()
^^^^^^^^^^^^^^^^^^^^^^^^
test_mongo_uri.py:3:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
conftest.py:39: in _factory
return SettingsMongo()
^^^^^^^^^^^^^^^
..\venv\Lib\site-packages\pydantic_settings\main.py:85: in __init__
**__pydantic_self__._settings_build_values(
..\venv\Lib\site-packages\pydantic_settings\main.py:187: in _settings_build_values
return deep_update(*reversed([source() for source in sources]))
^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = EnvSettingsSource(env_nested_delimiter=None, env_prefix_len=0)
def __call__(self) -> dict[str, Any]:
data: dict[str, Any] = {}
for field_name, field in self.settings_cls.model_fields.items():
try:
field_value, field_key, value_is_complex = self.get_field_value(field, field_name)
except Exception as e:
raise SettingsError(
f'error getting value for field "{field_name}" from source "{self.__class__.__name__}"'
) from e
try:
field_value = self.prepare_field_value(field_name, field, field_value, value_is_complex)
except ValueError as e:
> raise SettingsError(
f'error parsing value for field "{field_name}" from source "{self.__class__.__name__}"'
) from e
E pydantic_settings.sources.SettingsError: error parsing value for field "mongo_uri" from source "EnvSettingsSource"
..\venv\Lib\site-packages\pydantic_settings\sources.py:325: SettingsError
Python, Pydantic & OS Version
pydantic version: 2.7.0
pydantic-core version: 2.18.1
pydantic-core build: profile=release pgo=true
install path: ..\venv\Lib\site-packages\pydantic
python version: 3.12.0 (tags/v3.12.0:0fb18b0, Oct 2 2023, 13:03:39) [MSC v.1935 64 bit (AMD64)]
platform: Windows-11-10.0.26200-SP0
related packages: mypy-1.18.2 pydantic-settings-2.2.1 typing_extensions-4.15.0
commit: unknown