-
-
Notifications
You must be signed in to change notification settings - Fork 102
Adopt pydantic-settings with Pydantic V2 #17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adopt pydantic-settings with Pydantic V2 #17
Conversation
Codecov ReportPatch coverage:
📣 This organization is not using Codecov’s GitHub App Integration. We recommend you install it so Codecov can continue to function properly for your repositories. Learn more Additional details and impacted files@@ Coverage Diff @@
## main #17 +/- ##
==========================================
- Coverage 96.31% 95.51% -0.81%
==========================================
Files 4 5 +1
Lines 244 245 +1
Branches 60 59 -1
==========================================
- Hits 235 234 -1
- Misses 9 10 +1
- Partials 0 1 +1
Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here. ☔ View full report in Codecov by Sentry. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
I did try to break some stuff related to the implementation of field_is_complex
and the only thing I could get to not work was generic dataclasses, which had an easy fix.
I didn't look at the changes to the tests super closely, but from what I could tell they all seemed reasonable. (If you want specific feedback on any of that let me know.)
from pydantic.config import BaseConfig, Extra | ||
from pydantic.fields import ModelField | ||
from pydantic import ConfigDict, DirectoryPath, FilePath | ||
from pydantic._internal._utils import deep_update |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we okay using pydantic internals here? I think it's fine since it's still our package, just want to make sure.
(If we do this, and I guess even if we don't, it might be a good idea to run the tests for pydantic_settings in the CI for pydantic.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we okay using pydantic internals here? I think it's fine since it's still our package, just want to make sure.
Yes, as we maintain Pydantic I think it's fine
(If we do this, and I guess even if we don't, it might be a good idea to run the tests for pydantic_settings in the CI for pydantic.)
Agree
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agreed, like we do for fastapi.
pydantic_settings/main.py
Outdated
@@ -18,7 +15,7 @@ | |||
SecretsSettingsSource, | |||
) | |||
|
|||
env_file_sentinel = str(object()) | |||
env_file_sentinel: DotenvType = FilePath('') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What motivated this change? Just curious
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make mypy happy
pydantic_settings/main.py
Outdated
|
||
# populated by the metaclass using the Config class defined above, annotated here to help IDEs only | ||
__config__: ClassVar[Type[Config]] | ||
model_config = ConfigDict( # type: ignore[typeddict-item] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we make a subclass of ConfigDict that adds more items? I guess that probably doesn't work well.
Probably we'll want to make this work better with the pydantic type hints at some point (annoying that TypedDicts are generally treated as invariant..). But I guess that's outside the scope of a pydantic-settings
issue and this approach seems good to me.
Maybe just ignore this comment lol.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes we should have a custom subclass, in theory it shouldn't break type hinting since we're not changing types, just adding to them, and pydantic will never know the dict has other keys.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SettingsConfigDict
added
pydantic_settings/utils.py
Outdated
} | ||
|
||
|
||
def path_type(p: Path) -> str: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor thing, I'd suggest renaming this to path_type_label
though, to indicate that the returned value is purely meant for display purposes, not for equality checks or anything like that.
5bdbff9
to
1eed3ad
Compare
1eed3ad
to
437093f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Otherwise LGTM.
Sorry for the slow response.
from pydantic.config import BaseConfig, Extra | ||
from pydantic.fields import ModelField | ||
from pydantic import ConfigDict, DirectoryPath, FilePath | ||
from pydantic._internal._utils import deep_update |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agreed, like we do for fastapi.
pydantic_settings/main.py
Outdated
|
||
# populated by the metaclass using the Config class defined above, annotated here to help IDEs only | ||
__config__: ClassVar[Type[Config]] | ||
model_config = ConfigDict( # type: ignore[typeddict-item] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes we should have a custom subclass, in theory it shouldn't break type hinting since we're not changing types, just adding to them, and pydantic will never know the dict has other keys.
env_settings: PydanticBaseSettingsSource, | ||
dotenv_settings: PydanticBaseSettingsSource, | ||
file_secret_settings: PydanticBaseSettingsSource, | ||
) -> Tuple[PydanticBaseSettingsSource, ...]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not now, but we need to:
- add docstrings to this and other methods
- rename these methods to all use a consistent prefix, maybe
settings_
- Implement Protected namespaces pydantic#4915 and use that to make
settings_
a protected namespace.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I created an issue for this #19
pydantic_settings/main.py
Outdated
|
||
from pydantic.config import BaseConfig, Extra | ||
from pydantic.fields import ModelField | ||
from pydantic import ConfigDict, DirectoryPath, FilePath |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure about useing DirectoryPath
and FilePath
here - I see it's useful semantically to communicate meaning, but it could also be confusing, since they're not enforced at runtime.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think on balance, it's better not to use these types, probably better to use Path
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DirectoryPath
and FilePath
replaced by Path
pydantic_settings/main.py
Outdated
file_secret_settings: PydanticBaseSettingsSource, | ||
) -> Tuple[PydanticBaseSettingsSource, ...]: | ||
return init_settings, env_settings, dotenv_settings, file_secret_settings | ||
|
||
def _build_values( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rename, I guess to _settings_build_values
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renamed
Thanks @samuelcolvin for the review. I've addressed all of them. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
otherwise LGTM.
Here is the list of changes:
class Config
is not valid anymore and has to be replaced withmodel_config
. Example:has to be replaced with:
Model.dict()
is not available anymore andModel.model_dump()
has to be used.env
for a field underclass Config
is not valid andvalidation_alias
has to be used. Example:has to be replaced with:
validation_alias
is defined does not work anymore. Example:env
inField
is not valid anymore andvalidation_alias
has to be used. Example:has to be replaced with:
customise_sources
is not part ofclass Config
anymore and moved toBaseSettings
class.