From 982648a6800d3a913a9cca2124fcb66d25560609 Mon Sep 17 00:00:00 2001 From: Carmen Alvarez Date: Sat, 3 Feb 2024 12:43:44 +0100 Subject: [PATCH] Upgrade to pydantic 2. * Update dependencies * Run `bump-pydantic` to migrate some code: https://docs.pydantic.dev/latest/migration/ * Adapt the toml parsing per https://github.com/pydantic/pydantic/issues/2335 --- requirements/prod.txt | 3 ++- wspp/settings.py | 52 ++++++++++++++++++++++++++----------------- 2 files changed, 34 insertions(+), 21 deletions(-) diff --git a/requirements/prod.txt b/requirements/prod.txt index 9db9018..d8873d7 100644 --- a/requirements/prod.txt +++ b/requirements/prod.txt @@ -1,5 +1,6 @@ astral==3.2 pillow==10.2.0 -pydantic==1.10.14 +pydantic==2.6.0 +pydantic-settings==2.1.0 python-dotenv==1.0.1 requests==2.31.0 diff --git a/wspp/settings.py b/wspp/settings.py index b8eb68a..5346ca6 100644 --- a/wspp/settings.py +++ b/wspp/settings.py @@ -1,38 +1,50 @@ import tomllib from pathlib import Path +from typing import Any -from pydantic import BaseModel, BaseSettings, DirectoryPath, PositiveInt, confloat -from pydantic.env_settings import EnvSettingsSource -from pydantic.utils import deep_update +from pydantic import BaseModel, DirectoryPath, Field, PositiveInt +from pydantic.fields import FieldInfo +from pydantic_settings import ( + BaseSettings, + EnvSettingsSource, + PydanticBaseSettingsSource, +) +from typing_extensions import Annotated from wspp import slack, weatherstack class WsppSettings(BaseModel): - latitude: confloat(ge=-90.0, le=90.0) - longitude: confloat(ge=-180.0, le=180.0) + latitude: Annotated[float, Field(ge=-90.0, le=90.0)] + longitude: Annotated[float, Field(ge=-180.0, le=180.0)] profile_photos_dir: DirectoryPath = Path(__file__).parent.parent / "profile_photos" polling_interval_s: PositiveInt = 7200 +# https://github.com/pydantic/pydantic/issues/2335 +class TOMLConfigSettingsSource(PydanticBaseSettingsSource): + def get_field_value( + self, field: FieldInfo, field_name: str + ) -> tuple[Any, str, bool]: + return super().get_field_value(field, field_name) + + def __call__(self) -> dict[str, Any]: + return tomllib.loads(Path("config.toml").read_text()) + + class Settings(BaseSettings): wspp: WsppSettings weatherstack: weatherstack.WeatherstackSettings slack: list[slack.SlackSettings] @classmethod - def read(cls): - # Pydantic doesn't have built-in support for toml files, so we - # have to load the toml file (and override with any env vars) - # ourselves. - # https://github.com/pydantic/pydantic/issues/2335 - toml_settings = Settings(**tomllib.loads(Path("config.toml").read_text())) - env_data = EnvSettingsSource( - env_file=None, - env_file_encoding=None, - env_nested_delimiter="__", - )(toml_settings) - return Settings.parse_obj(obj=deep_update(toml_settings.dict(), env_data)) - - -settings = Settings.read() + def settings_customise_sources( + cls, *_args, **_kwargs + ) -> tuple[PydanticBaseSettingsSource, ...]: + return ( + EnvSettingsSource(Settings, env_nested_delimiter="__"), + TOMLConfigSettingsSource(Settings), + ) + + +settings = Settings()