Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/prompts/pydantic-annotated-fields.prompt.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Follow these guidelines:
4. Add the import: `from common_library.basic_types import DEFAULT_FACTORY` if it's not already present.
5. If `Field()` has no parameters (empty), don't use Annotated at all. Just use: `field_name: field_type = default_value`.
6. Leave any model validations, `model_config` settings, and `field_validators` untouched.

7. Must keep the original Field descriptions and validation parameters intact (except for the `default` parameter).

## Examples

Expand Down
44 changes: 27 additions & 17 deletions packages/aws-library/src/aws_library/ec2/_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from typing import Annotated, Final, TypeAlias

import sh # type: ignore[import-untyped]
from common_library.basic_types import DEFAULT_FACTORY
from models_library.docker import DockerGenericTag
from pydantic import (
BaseModel,
Expand Down Expand Up @@ -143,23 +144,32 @@ class EC2InstanceConfig:

class EC2InstanceBootSpecific(BaseModel):
ami_id: AMIIdStr
custom_boot_scripts: list[CommandStr] = Field(
default_factory=list,
description="script(s) to run on EC2 instance startup (be careful!), "
"each entry is run one after the other using '&&' operator",
)
pre_pull_images: list[DockerGenericTag] = Field(
default_factory=list,
description="a list of docker image/tags to pull on instance cold start",
)
pre_pull_images_cron_interval: datetime.timedelta = Field(
default=datetime.timedelta(minutes=30),
description="time interval between pulls of images (minimum is 1 minute) "
"(default to seconds, or see https://pydantic-docs.helpmanual.io/usage/types/#datetime-types for string formating)",
)
buffer_count: NonNegativeInt = Field(
default=0, description="number of buffer EC2s to keep (defaults to 0)"
)
custom_boot_scripts: Annotated[
list[CommandStr],
Field(
default_factory=list,
description="script(s) to run on EC2 instance startup (be careful!), "
"each entry is run one after the other using '&&' operator",
),
] = DEFAULT_FACTORY
pre_pull_images: Annotated[
list[DockerGenericTag],
Field(
default_factory=list,
description="a list of docker image/tags to pull on instance cold start",
),
] = DEFAULT_FACTORY
pre_pull_images_cron_interval: Annotated[
datetime.timedelta,
Field(
description="time interval between pulls of images (minimum is 1 minute) "
"(default to seconds, or see https://pydantic-docs.helpmanual.io/usage/types/#datetime-types for string formating)",
),
] = datetime.timedelta(minutes=30)
buffer_count: Annotated[
NonNegativeInt,
Field(description="number of buffer EC2s to keep (defaults to 0)"),
] = 0

@field_validator("custom_boot_scripts")
@classmethod
Expand Down
9 changes: 5 additions & 4 deletions packages/aws-library/src/aws_library/s3/_models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import datetime
from pathlib import Path
from typing import TypeAlias, cast
from typing import Annotated, TypeAlias, cast

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

class S3DirectoryMetaData(BaseModel, frozen=True):
prefix: S3ObjectPrefix
size: ByteSize | None = Field(
..., description="Size of the directory if computed, None if unknown"
)
size: Annotated[
ByteSize | None,
Field(description="Size of the directory if computed, None if unknown"),
]

def as_path(self) -> Path:
return self.prefix
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
from typing import Any, Callable, Literal
from collections.abc import Callable
from typing import Any, Literal

import pytest
from common_library.pydantic_fields_extension import get_type, is_literal, is_nullable
from pydantic import BaseModel, Field
from pydantic import BaseModel


class MyModel(BaseModel):
a: int
b: float | None = Field(...)
b: float | None
c: str = "bla"
d: bool | None = None
e: Literal["bla"]
Expand Down
6 changes: 3 additions & 3 deletions packages/common-library/tests/test_pydantic_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
validate_numeric_string_as_timedelta,
)
from faker import Faker
from pydantic import BeforeValidator, Field
from pydantic import BeforeValidator
from pydantic_settings import BaseSettings, SettingsConfigDict
from pytest_simcore.helpers.monkeypatch_envs import setenvs_from_dict

Expand All @@ -17,7 +17,7 @@ class Settings(BaseSettings):
APP_NAME: str
REQUEST_TIMEOUT: Annotated[
timedelta, BeforeValidator(_validate_legacy_timedelta_str)
] = Field(default=timedelta(hours=1))
] = timedelta(hours=1)

model_config = SettingsConfigDict()

Expand Down Expand Up @@ -45,7 +45,7 @@ def test_validate_timedelta_in_legacy_mode(
):
class Settings(BaseSettings):
APP_NAME: str
REQUEST_TIMEOUT: timedelta = Field(default=timedelta(seconds=40))
REQUEST_TIMEOUT: timedelta = timedelta(seconds=40)

_validate_request_timeout = validate_numeric_string_as_timedelta(
"REQUEST_TIMEOUT"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,15 @@ def _update_json_schema_extra(schema: JsonDict) -> None:

class FileUrl(BaseModel):
url: AnyUrl
file_mapping: str | None = Field(
default=None,
description="Local file relpath name (if given), otherwise it takes the url filename",
)
file_mime_type: str | None = Field(
default=None, description="the file MIME type", pattern=MIME_TYPE_RE
)
file_mapping: Annotated[
str | None,
Field(
description="Local file relpath name (if given), otherwise it takes the url filename"
),
] = None
file_mime_type: Annotated[
str | None, Field(description="the file MIME type", pattern=MIME_TYPE_RE)
] = None

@staticmethod
def _update_json_schema_extra(schema: JsonDict) -> None:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
import http
from typing import Any
from typing import Annotated, Any

from pydantic import BaseModel, Field

from ..basic_types import IDStr


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

@classmethod
def from_status_code(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Annotated

from pydantic import BaseModel, ConfigDict, Field

from ..basic_types import VersionStr
Expand All @@ -6,9 +8,10 @@
class BaseMeta(BaseModel):
name: str
version: VersionStr
released: dict[str, VersionStr] | None = Field(
default=None, description="Maps every route's path tag with a released version"
)
released: Annotated[
dict[str, VersionStr] | None,
Field(description="Maps every route's path tag with a released version"),
] = None

model_config = ConfigDict(
json_schema_extra={
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Annotated

from pydantic import BaseModel, ConfigDict, Field, SecretStr


Expand All @@ -10,7 +12,7 @@ class ApiKeyInDB(BaseModel):
api_key: str
api_secret: str

id_: int = Field(0, alias="id")
id_: Annotated[int, Field(alias="id")] = 0
display_name: str
user_id: int
product_name: str
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
from typing import Annotated

from pydantic import BaseModel, Field

from ..generated_models.docker_rest_api import ServiceSpec as DockerServiceSpec


class ServiceSpecifications(BaseModel):
sidecar: DockerServiceSpec | None = Field(
default=None,
description="schedule-time specifications for the service sidecar (follows Docker Service creation API, see https://docs.docker.com/engine/api/v1.25/#operation/ServiceCreate)",
)
service: DockerServiceSpec | None = Field(
default=None,
description="schedule-time specifications specifications for the service (follows Docker Service creation API (specifically only the Resources part), see https://docs.docker.com/engine/api/v1.41/#tag/Service/operation/ServiceCreate",
)
sidecar: Annotated[
DockerServiceSpec | None,
Field(
description="schedule-time specifications for the service sidecar (follows Docker Service creation API, see https://docs.docker.com/engine/api/v1.25/#operation/ServiceCreate)",
),
] = None
service: Annotated[
DockerServiceSpec | None,
Field(
description="schedule-time specifications specifications for the service (follows Docker Service creation API (specifically only the Resources part), see https://docs.docker.com/engine/api/v1.41/#tag/Service/operation/ServiceCreate",
),
] = None


class ServiceSpecificationsGet(ServiceSpecifications):
...
class ServiceSpecificationsGet(ServiceSpecifications): ...
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import datetime
from enum import auto
from typing import Annotated

from pydantic import AnyUrl, BaseModel, Field

Expand All @@ -17,7 +18,7 @@ class ClusterState(StrAutoEnum):

class OnDemandCluster(BaseModel):
endpoint: AnyUrl
authentication: ClusterAuthentication = Field(discriminator="type")
authentication: Annotated[ClusterAuthentication, Field(discriminator="type")]
state: ClusterState
user_id: UserID
wallet_id: WalletID | None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ class TaskCounts(BaseModel):


class WorkerMetrics(BaseModel):
cpu: float = Field(..., description="consumed % of cpus")
memory: ByteSize = Field(..., description="consumed memory")
num_fds: int = Field(..., description="consumed file descriptors")
task_counts: TaskCounts = Field(..., description="task details")
cpu: Annotated[float, Field(description="consumed % of cpus")]
memory: Annotated[ByteSize, Field(description="consumed memory")]
num_fds: Annotated[int, Field(description="consumed file descriptors")]
task_counts: Annotated[TaskCounts, Field(description="task details")]


AvailableResources: TypeAlias = DictModel[str, PositiveFloat]
Expand Down Expand Up @@ -54,7 +54,7 @@ class Worker(BaseModel):


class Scheduler(BaseModel):
status: str = Field(..., description="The running status of the scheduler")
status: Annotated[str, Field(description="The running status of the scheduler")]
workers: Annotated[WorkersDict | None, Field(default_factory=dict)]

@field_validator("workers", mode="before")
Expand All @@ -66,10 +66,5 @@ def ensure_workers_is_empty_dict(cls, v):


class ClusterDetails(BaseModel):
scheduler: Scheduler = Field(
...,
description="This contains dask scheduler information given by the underlying dask library",
)
dashboard_link: AnyUrl = Field(
..., description="Link to this scheduler's dashboard"
)
scheduler: Annotated[Scheduler, Field(description="scheduler information")]
dashboard_link: Annotated[AnyUrl, Field(description="Link to the dask dashboard")]
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
from typing import Annotated

from pydantic import BaseModel, Field


class Error(BaseModel):
code: str | None = Field(None, description="Server Exception")
code: Annotated[str | None, Field(description="Server Exception")] = None


class ErrorType(BaseModel):
message: str = Field(..., description="Error message")
message: Annotated[str, Field(description="Error message")]
status: Annotated[int, Field(description="Error code")]
errors: list[Error] | None = None
status: int = Field(..., description="Error code")


class ErrorEnveloped(BaseModel):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ class TaskProgress(BaseModel):
defined as a float bound between 0.0 and 1.0
"""

task_id: TaskId | None = Field(default=None)
message: ProgressMessage = Field(default="")
percent: ProgressPercent = Field(default=0.0)
task_id: TaskId | None = None
message: ProgressMessage = ""
percent: ProgressPercent = 0.0

@validate_call
def update(
Expand Down
Loading