From d853bb669929b421b0903b52523437903679b20a Mon Sep 17 00:00:00 2001 From: Jan Tojnar Date: Sun, 22 Sep 2024 22:22:50 +0200 Subject: [PATCH 1/4] Switch to poetry-core This is the current recommendation: https://python-poetry.org/docs/pyproject/#poetry-and-pep-517 --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index d2fd04e..f5b0da3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,5 +44,5 @@ pytype = "^2020.8.17" waterloo = "waterloo.cli:main" [build-system] -requires = ["poetry>=0.12"] -build-backend = "poetry.masonry.api" +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" From 7fef0830dec9f5d76a2444558f63d8f1149d424b Mon Sep 17 00:00:00 2001 From: Jan Tojnar Date: Sun, 22 Sep 2024 22:36:02 +0200 Subject: [PATCH 2/4] Upgrade to Pydantic 2 - Used `bump-pydantic` - `BaseModel.__fields__` and `copy()` were deprecated in favour of `model_fields` and `model_copy()` respectively. - `BaseSettings` has been moved to the `pydantic-settings` package: https://docs.pydantic.dev/2.7/migration/#basesettings-has-moved-to-pydantic-settings - `FieldInfo.type_` is no longer available, must use `FieldInfo.annotation`: https://www.github.com/pydantic/pydantic/issues/7220 --- pyproject.toml | 3 ++- tests/refactor/test_annotations_pbt.py | 2 +- tests/utils.py | 2 +- waterloo/conf/types.py | 19 ++++++++++--------- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index f5b0da3..03faa83 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,8 @@ toml = "^0.10.0" megaparsy = "^0.1.4" typesystem = "^0.2.4" prompt-toolkit = "^3.0.3" -pydantic = "^1.6.1" +pydantic = "^2.0.0" +pydantic_settings = "^2.0.0" parso = "^0.7.0,<0.8.0" inject = "^4.3.1" structlog = "^20.1.0" diff --git a/tests/refactor/test_annotations_pbt.py b/tests/refactor/test_annotations_pbt.py index 6708e94..9698e23 100644 --- a/tests/refactor/test_annotations_pbt.py +++ b/tests/refactor/test_annotations_pbt.py @@ -217,7 +217,7 @@ def identity(arg1): with open(f.name, "w") as fw: fw.write(content) - test_settings = settings.copy(deep=True) + test_settings = settings.model_copy(deep=True) test_settings.ALLOW_UNTYPED_ARGS = False test_settings.REQUIRE_RETURN_TYPE = False test_settings.IMPORT_COLLISION_POLICY = import_collision_policy diff --git a/tests/utils.py b/tests/utils.py index 7239dd7..97ce0ea 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -3,7 +3,7 @@ @inject.params(settings="settings") def override_settings(settings, **kwargs): - test_settings = settings.copy(deep=True) + test_settings = settings.model_copy(deep=True) for key, val in kwargs.items(): setattr(test_settings, key, val) return test_settings diff --git a/waterloo/conf/types.py b/waterloo/conf/types.py index 9409293..0861e98 100644 --- a/waterloo/conf/types.py +++ b/waterloo/conf/types.py @@ -1,7 +1,8 @@ from enum import Enum from typing import Dict, Optional, Union, no_type_check -from pydantic import BaseSettings, validator +from pydantic import field_validator +from pydantic_settings import SettingsConfigDict, BaseSettings from waterloo.types import ( LOG_LEVEL_LABELS, @@ -19,16 +20,14 @@ class CoerceEnumSettings(BaseSettings): @no_type_check def __setattr__(self, name, value): - field = self.__fields__[name] - if issubclass(field.type_, Enum) and not isinstance(value, Enum): - value = field.type_[value] + field = self.model_fields[name] + if issubclass(field.annotation, Enum) and not isinstance(value, Enum): + value = field.annotation[value] return super().__setattr__(name, value) class Settings(CoerceEnumSettings): - class Config: - validate_assignment = True - env_prefix = "WATERLOO_" + model_config = SettingsConfigDict(validate_assignment=True, env_prefix="WATERLOO_") PYTHON_VERSION: str = "2.7" @@ -43,7 +42,8 @@ class Config: VERBOSE_ECHO: bool = True LOG_LEVEL: LogLevel = LogLevel.INFO - @validator("IMPORT_COLLISION_POLICY") + @field_validator("IMPORT_COLLISION_POLICY") + @classmethod def key_to_member( cls, value: Union[ImportCollisionPolicy, str] ) -> ImportCollisionPolicy: @@ -51,7 +51,8 @@ def key_to_member( return value return ImportCollisionPolicy[value] - @validator("ECHO_STYLES") + @field_validator("ECHO_STYLES") + @classmethod def echo_styles_required_fields( cls, value: Optional[Dict[str, str]] ) -> Optional[Dict[str, str]]: From 72f66a07a8054836dc46461ba5030825ebf45c2e Mon Sep 17 00:00:00 2001 From: Jan Tojnar Date: Sun, 22 Sep 2024 22:39:41 +0200 Subject: [PATCH 3/4] Fix typing error For some reason `@_repr_type_arg.register` annotation for `_` would fail: NameError: name 'ImportStrategy' is not defined --- waterloo/types.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/waterloo/types.py b/waterloo/types.py index a265ad0..c7d692f 100644 --- a/waterloo/types.py +++ b/waterloo/types.py @@ -25,6 +25,19 @@ class Types(str, Enum): NONE = "None" +class ImportStrategy(Enum): + USE_EXISTING = auto() # don't add any import, strip dotted path from docstring type + USE_EXISTING_DOTTED = auto() # don't add any import + ADD_FROM = auto() # from import + ADD_DOTTED = auto() # import + + +DOTTED_PATH_STRATEGIES: Final = { + ImportStrategy.ADD_DOTTED, + ImportStrategy.USE_EXISTING_DOTTED, +} + + # https://sphinxcontrib-napoleon.readthedocs.io/en/latest/#docstring-sections VALID_ARGS_SECTION_NAMES: Final = { "Args", @@ -42,7 +55,7 @@ class Types(str, Enum): "Yields": (r"Yields", ReturnsSection.YIELDS), } -NameToStrategy_T = Dict[str, "ImportStrategy"] +NameToStrategy_T = Dict[str, ImportStrategy] class TypeAtom(NamedTuple): @@ -324,19 +337,6 @@ def __len__(self): return len(self.all_names) -class ImportStrategy(Enum): - USE_EXISTING = auto() # don't add any import, strip dotted path from docstring type - USE_EXISTING_DOTTED = auto() # don't add any import - ADD_FROM = auto() # from import - ADD_DOTTED = auto() # import - - -DOTTED_PATH_STRATEGIES: Final = { - ImportStrategy.ADD_DOTTED, - ImportStrategy.USE_EXISTING_DOTTED, -} - - class AmbiguousTypeError(Exception): settings = inject.attr("settings") From 482250788f08e0b41e973d6a8bfc4cf3b5df10a7 Mon Sep 17 00:00:00 2001 From: Jan Tojnar Date: Mon, 23 Sep 2024 00:07:59 +0200 Subject: [PATCH 4/4] squash! Upgrade to Pydantic 2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Numbers are no longer coerced to strings by default, let’s revert that in config for now. https://docs.pydantic.dev/2.7/migration/#validator-behavior-changes --- waterloo/conf/types.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/waterloo/conf/types.py b/waterloo/conf/types.py index 0861e98..dcdbde1 100644 --- a/waterloo/conf/types.py +++ b/waterloo/conf/types.py @@ -27,7 +27,12 @@ def __setattr__(self, name, value): class Settings(CoerceEnumSettings): - model_config = SettingsConfigDict(validate_assignment=True, env_prefix="WATERLOO_") + model_config = SettingsConfigDict( + validate_assignment=True, + env_prefix="WATERLOO_", + # TODO: replace all str field values strictly with str + coerce_numbers_to_str=True, + ) PYTHON_VERSION: str = "2.7"