Skip to content

Commit 64695c3

Browse files
authored
♻️ Maintenance: Migrate more Pydantic models to Annotated types (#7965)
1 parent b109758 commit 64695c3

File tree

22 files changed

+199
-146
lines changed

22 files changed

+199
-146
lines changed

.github/prompts/pydantic-annotated-fields.prompt.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Follow these guidelines:
1313
4. Add the import: `from common_library.basic_types import DEFAULT_FACTORY` if it's not already present.
1414
5. If `Field()` has no parameters (empty), don't use Annotated at all. Just use: `field_name: field_type = default_value`.
1515
6. Leave any model validations, `model_config` settings, and `field_validators` untouched.
16-
16+
7. Must keep the original Field descriptions and validation parameters intact (except for the `default` parameter).
1717

1818
## Examples
1919

api/specs/web-server/_auth.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# pylint: disable=too-many-arguments
55

66

7-
from typing import Any
7+
from typing import Annotated, Any
88

99
from fastapi import APIRouter, status
1010
from models_library.api_schemas_webserver.auth import (
@@ -211,13 +211,16 @@ async def change_email(_body: ChangeEmailBody):
211211

212212

213213
class PasswordCheckSchema(BaseModel):
214-
strength: confloat(ge=0.0, le=1.0) = Field( # type: ignore
215-
...,
216-
description="The strength of the password ranges from 0 (extremely weak) and 1 (extremely strong)",
217-
)
218-
rating: str | None = Field(
219-
None, description="Human readable rating from infinitely weak to very strong"
220-
)
214+
strength: Annotated[
215+
confloat(ge=0.0, le=1.0),
216+
Field(
217+
description="The strength of the password ranges from 0 (extremely weak) and 1 (extremely strong)",
218+
),
219+
]
220+
rating: Annotated[
221+
str | None,
222+
Field(description="Human readable rating from infinitely weak to very strong"),
223+
] = None
221224
improvements: Any | None = None
222225

223226

packages/aws-library/src/aws_library/ec2/_models.py

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from typing import Annotated, Final, TypeAlias
66

77
import sh # type: ignore[import-untyped]
8+
from common_library.basic_types import DEFAULT_FACTORY
89
from models_library.docker import DockerGenericTag
910
from pydantic import (
1011
BaseModel,
@@ -143,23 +144,32 @@ class EC2InstanceConfig:
143144

144145
class EC2InstanceBootSpecific(BaseModel):
145146
ami_id: AMIIdStr
146-
custom_boot_scripts: list[CommandStr] = Field(
147-
default_factory=list,
148-
description="script(s) to run on EC2 instance startup (be careful!), "
149-
"each entry is run one after the other using '&&' operator",
150-
)
151-
pre_pull_images: list[DockerGenericTag] = Field(
152-
default_factory=list,
153-
description="a list of docker image/tags to pull on instance cold start",
154-
)
155-
pre_pull_images_cron_interval: datetime.timedelta = Field(
156-
default=datetime.timedelta(minutes=30),
157-
description="time interval between pulls of images (minimum is 1 minute) "
158-
"(default to seconds, or see https://pydantic-docs.helpmanual.io/usage/types/#datetime-types for string formating)",
159-
)
160-
buffer_count: NonNegativeInt = Field(
161-
default=0, description="number of buffer EC2s to keep (defaults to 0)"
162-
)
147+
custom_boot_scripts: Annotated[
148+
list[CommandStr],
149+
Field(
150+
default_factory=list,
151+
description="script(s) to run on EC2 instance startup (be careful!), "
152+
"each entry is run one after the other using '&&' operator",
153+
),
154+
] = DEFAULT_FACTORY
155+
pre_pull_images: Annotated[
156+
list[DockerGenericTag],
157+
Field(
158+
default_factory=list,
159+
description="a list of docker image/tags to pull on instance cold start",
160+
),
161+
] = DEFAULT_FACTORY
162+
pre_pull_images_cron_interval: Annotated[
163+
datetime.timedelta,
164+
Field(
165+
description="time interval between pulls of images (minimum is 1 minute) "
166+
"(default to seconds, or see https://pydantic-docs.helpmanual.io/usage/types/#datetime-types for string formating)",
167+
),
168+
] = datetime.timedelta(minutes=30)
169+
buffer_count: Annotated[
170+
NonNegativeInt,
171+
Field(description="number of buffer EC2s to keep (defaults to 0)"),
172+
] = 0
163173

164174
@field_validator("custom_boot_scripts")
165175
@classmethod

packages/aws-library/src/aws_library/s3/_models.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import datetime
22
from pathlib import Path
3-
from typing import TypeAlias, cast
3+
from typing import Annotated, TypeAlias, cast
44

55
from models_library.api_schemas_storage.storage_schemas import ETag
66
from models_library.basic_types import SHA256Str
@@ -54,9 +54,10 @@ def as_path(self) -> Path:
5454

5555
class S3DirectoryMetaData(BaseModel, frozen=True):
5656
prefix: S3ObjectPrefix
57-
size: ByteSize | None = Field(
58-
..., description="Size of the directory if computed, None if unknown"
59-
)
57+
size: Annotated[
58+
ByteSize | None,
59+
Field(description="Size of the directory if computed, None if unknown"),
60+
]
6061

6162
def as_path(self) -> Path:
6263
return self.prefix

packages/common-library/tests/test_pydantic_fields_extension.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
from typing import Any, Callable, Literal
1+
from collections.abc import Callable
2+
from typing import Any, Literal
23

34
import pytest
45
from common_library.pydantic_fields_extension import get_type, is_literal, is_nullable
5-
from pydantic import BaseModel, Field
6+
from pydantic import BaseModel
67

78

89
class MyModel(BaseModel):
910
a: int
10-
b: float | None = Field(...)
11+
b: float | None
1112
c: str = "bla"
1213
d: bool | None = None
1314
e: Literal["bla"]

packages/common-library/tests/test_pydantic_validators.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
validate_numeric_string_as_timedelta,
88
)
99
from faker import Faker
10-
from pydantic import BeforeValidator, Field
10+
from pydantic import BeforeValidator
1111
from pydantic_settings import BaseSettings, SettingsConfigDict
1212
from pytest_simcore.helpers.monkeypatch_envs import setenvs_from_dict
1313

@@ -17,7 +17,7 @@ class Settings(BaseSettings):
1717
APP_NAME: str
1818
REQUEST_TIMEOUT: Annotated[
1919
timedelta, BeforeValidator(_validate_legacy_timedelta_str)
20-
] = Field(default=timedelta(hours=1))
20+
] = timedelta(hours=1)
2121

2222
model_config = SettingsConfigDict()
2323

@@ -45,7 +45,7 @@ def test_validate_timedelta_in_legacy_mode(
4545
):
4646
class Settings(BaseSettings):
4747
APP_NAME: str
48-
REQUEST_TIMEOUT: timedelta = Field(default=timedelta(seconds=40))
48+
REQUEST_TIMEOUT: timedelta = timedelta(seconds=40)
4949

5050
_validate_request_timeout = validate_numeric_string_as_timedelta(
5151
"REQUEST_TIMEOUT"

packages/dask-task-models-library/src/dask_task_models_library/container_tasks/io.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,15 @@ def _update_json_schema_extra(schema: JsonDict) -> None:
7575

7676
class FileUrl(BaseModel):
7777
url: AnyUrl
78-
file_mapping: str | None = Field(
79-
default=None,
80-
description="Local file relpath name (if given), otherwise it takes the url filename",
81-
)
82-
file_mime_type: str | None = Field(
83-
default=None, description="the file MIME type", pattern=MIME_TYPE_RE
84-
)
78+
file_mapping: Annotated[
79+
str | None,
80+
Field(
81+
description="Local file relpath name (if given), otherwise it takes the url filename"
82+
),
83+
] = None
84+
file_mime_type: Annotated[
85+
str | None, Field(description="the file MIME type", pattern=MIME_TYPE_RE)
86+
] = None
8587

8688
@staticmethod
8789
def _update_json_schema_extra(schema: JsonDict) -> None:

packages/models-library/src/models_library/api_schemas__common/errors.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
import http
2-
from typing import Any
2+
from typing import Annotated, Any
33

44
from pydantic import BaseModel, Field
55

66
from ..basic_types import IDStr
77

88

99
class DefaultApiError(BaseModel):
10-
name: IDStr = Field(
11-
...,
12-
description="Error identifier as a code or a name. "
13-
"Mainly for machine-machine communication purposes.",
10+
name: Annotated[IDStr, Field(description="Exception's class name")]
11+
detail: Annotated[Any | None, Field(description="Human readable error message")] = (
12+
None
1413
)
15-
detail: Any | None = Field(default=None, description="Human readable error message")
1614

1715
@classmethod
1816
def from_status_code(

packages/models-library/src/models_library/api_schemas__common/meta.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import Annotated
2+
13
from pydantic import BaseModel, ConfigDict, Field
24

35
from ..basic_types import VersionStr
@@ -6,9 +8,10 @@
68
class BaseMeta(BaseModel):
79
name: str
810
version: VersionStr
9-
released: dict[str, VersionStr] | None = Field(
10-
default=None, description="Maps every route's path tag with a released version"
11-
)
11+
released: Annotated[
12+
dict[str, VersionStr] | None,
13+
Field(description="Maps every route's path tag with a released version"),
14+
] = None
1215

1316
model_config = ConfigDict(
1417
json_schema_extra={

packages/models-library/src/models_library/api_schemas_api_server/api_keys.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import Annotated
2+
13
from pydantic import BaseModel, ConfigDict, Field, SecretStr
24

35

@@ -10,7 +12,7 @@ class ApiKeyInDB(BaseModel):
1012
api_key: str
1113
api_secret: str
1214

13-
id_: int = Field(0, alias="id")
15+
id_: Annotated[int, Field(alias="id")] = 0
1416
display_name: str
1517
user_id: int
1618
product_name: str

0 commit comments

Comments
 (0)