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
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import datetime

from pydantic import field_validator


def _get_float_string_as_seconds(
v: datetime.timedelta | str | float,
) -> datetime.timedelta | float | str:
if isinstance(v, str):
try:
return float(v)
except ValueError:
# returns format like "1:00:00"
return v
return v


def validate_timedelta_in_legacy_mode(field: str):
"""Transforms a float/int number into a valid datetime as it used to work in the past"""
return field_validator(field, mode="before")(_get_float_string_as_seconds)
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
from typing import Final

from common_library.pydantic_networks_extension import AnyHttpUrlLegacy, AnyUrlLegacy
from pydantic import TypeAdapter
from pydantic import ByteSize, TypeAdapter

AnyUrlLegacyAdapter: Final[TypeAdapter[AnyUrlLegacy]] = TypeAdapter(AnyUrlLegacy)

AnyHttpUrlLegacyAdapter: Final[TypeAdapter[AnyHttpUrlLegacy]] = TypeAdapter(AnyHttpUrlLegacy)
AnyHttpUrlLegacyAdapter: Final[TypeAdapter[AnyHttpUrlLegacy]] = TypeAdapter(
AnyHttpUrlLegacy
)

ByteSizeAdapter = TypeAdapter(ByteSize)
42 changes: 42 additions & 0 deletions packages/common-library/tests/test_pydantic_settings_validators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from datetime import timedelta

import pytest
from common_library.pydantic_settings_validators import (
validate_timedelta_in_legacy_mode,
)
from faker import Faker
from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict
from pytest_simcore.helpers.monkeypatch_envs import setenvs_from_dict


def test_validate_timedelta_in_legacy_mode(
monkeypatch: pytest.MonkeyPatch, faker: Faker
):
class Settings(BaseSettings):
APP_NAME: str
REQUEST_TIMEOUT: timedelta = Field(default=timedelta(seconds=40))

_legacy_parsing_request_timeout = validate_timedelta_in_legacy_mode(
"REQUEST_TIMEOUT"
)

model_config = SettingsConfigDict()

app_name = faker.pystr()
env_vars: dict[str, str | bool] = {"APP_NAME": app_name}

# without timedelta
setenvs_from_dict(monkeypatch, env_vars)
settings = Settings()
print(settings.model_dump())
assert app_name == settings.APP_NAME
assert timedelta(seconds=40) == settings.REQUEST_TIMEOUT

# with timedelta in seconds
env_vars["REQUEST_TIMEOUT"] = "5555"
setenvs_from_dict(monkeypatch, env_vars)
settings = Settings()
print(settings.model_dump())
assert app_name == settings.APP_NAME
assert timedelta(seconds=5555) == settings.REQUEST_TIMEOUT
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
SimcoreS3FileID: TypeAlias = Annotated[
str, StringConstraints(pattern=SIMCORE_S3_FILE_ID_RE)
]
SimcoreS3FileIDTypeAdapter = TypeAdapter(SimcoreS3FileID)


class SimcoreS3DirectoryID(ConstrainedStr):
Expand Down
21 changes: 17 additions & 4 deletions packages/models-library/src/models_library/services_creation.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
from typing import Any

from pydantic import BaseModel, ConfigDict
from pydantic import BaseModel, ConfigDict, TypeAdapter

from .services import ServiceKey, ServiceVersion
from .services_resources import ServiceResourcesDict
from .services_types import (
ServiceKey,
ServiceKeyTypeAdapter,
ServiceVersion,
ServiceVersionTypeAdapter,
)
from .wallets import WalletID


Expand Down Expand Up @@ -36,10 +41,18 @@ class CreateServiceMetricsAdditionalParams(BaseModel):
"user_email": "[email protected]",
"project_name": "_!New Study",
"node_name": "the service of a lifetime _ *!",
"service_key": ServiceKey("simcore/services/dynamic/test"),
"service_version": ServiceVersion("0.0.1"),
"service_key": ServiceKeyTypeAdapter.validate_python(
"simcore/services/dynamic/test"
),
"service_version": ServiceVersionTypeAdapter.validate_python("0.0.1"),
"service_resources": {},
"service_additional_metadata": {},
"pricing_unit_cost_id": None,
}
}
)


CreateServiceMetricsAdditionalParamsTypeAdapter = TypeAdapter(
CreateServiceMetricsAdditionalParams
)
6 changes: 4 additions & 2 deletions packages/models-library/src/models_library/services_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
StrictFloat,
StrictInt,
StringConstraints,
TypeAdapter,
ValidationInfo,
field_validator,
)
Expand Down Expand Up @@ -149,7 +150,6 @@ class ServiceInput(BaseServiceIOModel):
)

model_config = ConfigDict(
**BaseServiceIOModel.model_config,
json_schema_extra={
"examples": [
# file-wo-widget:
Expand Down Expand Up @@ -222,7 +222,6 @@ class ServiceOutput(BaseServiceIOModel):
)

model_config = ConfigDict(
**BaseServiceIOModel.model_config,
json_schema_extra={
"examples": [
{
Expand Down Expand Up @@ -259,3 +258,6 @@ def from_json_schema(cls, port_schema: dict[str, Any]) -> "ServiceOutput":
"""Creates output port model from a json-schema"""
data = cls._from_json_schema_base_implementation(port_schema)
return cls.model_validate(data)


ServiceOutputTypeAdapter = TypeAdapter(ServiceOutput)
12 changes: 9 additions & 3 deletions packages/models-library/src/models_library/services_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from uuid import uuid4

import arrow
from pydantic import StringConstraints
from pydantic import StringConstraints, TypeAdapter

from .basic_regex import PROPERTY_KEY_RE, SIMPLE_VERSION_RE
from .services_regex import (
Expand All @@ -18,16 +18,22 @@
FileName: TypeAlias = Annotated[str, StringConstraints(pattern=FILENAME_RE)]

ServiceKey: TypeAlias = Annotated[str, StringConstraints(pattern=SERVICE_KEY_RE)]
ServiceKeyTypeAdapter = TypeAdapter(ServiceKey)

ServiceKeyEncoded: TypeAlias = Annotated[str, StringConstraints(pattern=SERVICE_ENCODED_KEY_RE)]
ServiceKeyEncoded: TypeAlias = Annotated[
str, StringConstraints(pattern=SERVICE_ENCODED_KEY_RE)
]

DynamicServiceKey: TypeAlias = Annotated[str, StringConstraints(pattern=DYNAMIC_SERVICE_KEY_RE)]
DynamicServiceKey: TypeAlias = Annotated[
str, StringConstraints(pattern=DYNAMIC_SERVICE_KEY_RE)
]

ComputationalServiceKey: TypeAlias = Annotated[
str, StringConstraints(pattern=COMPUTATIONAL_SERVICE_KEY_RE)
]

ServiceVersion: TypeAlias = Annotated[str, StringConstraints(pattern=SIMPLE_VERSION_RE)]
ServiceVersionTypeAdapter = TypeAdapter(ServiceVersion)


class RunID(str):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ def create_base_app(settings: AppSettings | None = None) -> FastAPI:
config_all_loggers(
log_format_local_dev_enabled=settings.DIRECTOR_V2_LOG_FORMAT_LOCAL_DEV_ENABLED
)
_logger.debug(settings.json(indent=2))
_logger.debug(settings.model_dump_json(indent=2))

# keep mostly quiet noisy loggers
quiet_level: int = max(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
from faker import Faker
from fastapi import FastAPI, status
from httpx import Response
from models_library.services_creation import CreateServiceMetricsAdditionalParams
from models_library.services_creation import (
CreateServiceMetricsAdditionalParams,
CreateServiceMetricsAdditionalParamsTypeAdapter,
)
from models_library.sidecar_volumes import VolumeCategory, VolumeStatus
from pydantic import AnyHttpUrl, parse_obj_as
from pytest_simcore.helpers.typing_env import EnvVarsDict
Expand Down Expand Up @@ -282,9 +285,10 @@ async def test_put_volumes(
"post_containers_tasks",
"/containers",
{
"metrics_params": parse_obj_as(
CreateServiceMetricsAdditionalParams,
CreateServiceMetricsAdditionalParams.Config.schema_extra["example"],
"metrics_params": CreateServiceMetricsAdditionalParamsTypeAdapter.validate_python(
CreateServiceMetricsAdditionalParams.model_config[
"json_schema_extra"
]["example"],
)
},
id="post_containers_tasks",
Expand Down
2 changes: 1 addition & 1 deletion services/dynamic-sidecar/.env-devel
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ DY_SIDECAR_USER_SERVICES_HAVE_INTERNET_ACCESS=false
# DOCKER REGISTRY
DY_DEPLOYMENT_REGISTRY_SETTINGS='{"REGISTRY_AUTH":"false","REGISTRY_USER":"test","REGISTRY_PW":"test","REGISTRY_SSL":"false"}'

S3_ENDPOINT=http://145.456.25.54:12345
S3_ENDPOINT=http://111.111.111.111:12345
S3_ACCESS_KEY=mocked
S3_REGION=mocked
S3_SECRET_KEY=mocked
Expand Down
Loading
Loading