Description
Hello altogether,
change #204 is a breaking change. It introduces a dict-cast that leads to nested copy-by-value for BaseSettings instances. In 2.1.0 it was copy-by-reference.
See this line: https://github.com/pydantic/pydantic-settings/pull/204/files#diff-b4b68ace3cb0cf2820d1d882735748b675a379c39463ccc89700a9618a80a2a2R124
This breaks any use-case with a base class/type and/or abc classes, where the type is not explicitly known beforehand.
E.g. for pluggable authentication:
from abc import ABC, abstractmethod
from pydantic import SecretStr, HttpUrl
from pydantic_settings import BaseSettings
from typing import Type
class BaseAuth(ABC, BaseSettings):
@property
@abstractmethod
def token(self) -> str:
"""returns authentication token for XYZ"""
pass
class CustomAuth(BaseAuth):
url: HttpUrl
username: str
password: SecretStr
_token: SecretStr = None
@property
def token(self):
... # (re)fetch token
return self._token.get_secret_value()
class Settings(BaseSettings):
auth: Type[BaseAuth]
s = Settings(
auth=CustomAuth(
url='https://127.0.0.1',
username='some-username',
password='some-password'
)
)
print(s.auth)
print(type(s.auth))
Upon execution you'll receive following ValidationException:
Traceback (most recent call last):
File "/tmp/test_pydantic_settings_nesting.py", line 32, in <module>
s = Settings(
^^^^^^^^^
File "/tmp/venv/lib/python3.11/site-packages/pydantic_settings/main.py", line 85, in __init__
super().__init__(
File "/tmp/venv/lib/python3.11/site-packages/pydantic/main.py", line 171, in __init__
self.__pydantic_validator__.validate_python(data, self_instance=self)
pydantic_core._pydantic_core.ValidationError: 1 validation error for Settings
auth
Input should be a subclass of BaseAuth [type=is_subclass_of, input_value={'url': Url('https://127....SecretStr('**********')}, input_type=dict]
For further information visit https://errors.pydantic.dev/2.6/v/is_subclass_of
From this POV 2.2.0 should be 3.0.0, as this is a Breaking Change. Moving from copy-by-reference to copy-by-value with casting is a change that can have a lot of impact in an interfacing-focused library.
As a side note: The amount of breaking changes in Minor-Releases across the pydantic project increased in the last year. We have more and more debugging sessions regarding it and are scratching our heads, if and how we are possibly "holding it wrong" or its something out of our control and pydantic is not as stable as it should be...