Skip to content

Commit 8540a81

Browse files
upgrade web server
1 parent 4151198 commit 8540a81

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+383
-440
lines changed

services/web/server/src/simcore_service_webserver/announcements/_models.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
from datetime import datetime
2-
from typing import Any, ClassVar, Literal
2+
from typing import Literal
33

44
import arrow
5-
from pydantic import BaseModel, validator
5+
from pydantic import ConfigDict, BaseModel, field_validator
66

77

88
# NOTE: this model is used for BOTH
@@ -18,7 +18,7 @@ class Announcement(BaseModel):
1818
link: str
1919
widgets: list[Literal["login", "ribbon", "user-menu"]]
2020

21-
@validator("end")
21+
@field_validator("end")
2222
@classmethod
2323
def check_start_before_end(cls, v, values):
2424
if start := values.get("start"):
@@ -30,9 +30,8 @@ def check_start_before_end(cls, v, values):
3030

3131
def expired(self) -> bool:
3232
return self.end <= arrow.utcnow().datetime
33-
34-
class Config:
35-
schema_extra: ClassVar[dict[str, Any]] = {
33+
model_config = ConfigDict(
34+
json_schema_extra={
3635
"examples": [
3736
{
3837
"id": "Student_Competition_2023",
@@ -56,3 +55,4 @@ class Config:
5655
},
5756
]
5857
}
58+
)

services/web/server/src/simcore_service_webserver/application_settings.py

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
VersionTag,
1212
)
1313
from models_library.utils.change_case import snake_to_camel
14-
from pydantic import AnyHttpUrl, parse_obj_as, root_validator, validator
14+
from pydantic import AliasChoices, TypeAdapter, field_validator, model_validator, AnyHttpUrl
1515
from pydantic.fields import Field, ModelField
1616
from pydantic.types import PositiveInt
1717
from settings_library.base import BaseCustomSettings
@@ -53,7 +53,7 @@ class ApplicationSettings(BaseCustomSettings, MixinLoggingSettings):
5353
# CODE STATICS ---------------------------------------------------------
5454
API_VERSION: str = API_VERSION
5555
APP_NAME: str = APP_NAME
56-
API_VTAG: VersionTag = parse_obj_as(VersionTag, API_VTAG)
56+
API_VTAG: VersionTag = TypeAdapter(VersionTag).validate_python(API_VTAG)
5757

5858
# IMAGE BUILDTIME ------------------------------------------------------
5959
# @Makefile
@@ -119,52 +119,52 @@ class ApplicationSettings(BaseCustomSettings, MixinLoggingSettings):
119119
description="host name to serve within the container."
120120
"NOTE that this different from WEBSERVER_HOST env which is the host seen outside the container",
121121
)
122-
WEBSERVER_HOST: str | None = Field(None, env=["WEBSERVER_HOST", "HOST", "HOSTNAME"])
123-
WEBSERVER_PORT: PortInt = parse_obj_as(PortInt, DEFAULT_AIOHTTP_PORT)
122+
WEBSERVER_HOST: str | None = Field(None, validation_alias=AliasChoices("WEBSERVER_HOST", "HOST", "HOSTNAME"))
123+
WEBSERVER_PORT: PortInt = TypeAdapter(PortInt).validate_python(DEFAULT_AIOHTTP_PORT)
124124

125125
WEBSERVER_FRONTEND: FrontEndAppSettings | None = Field(
126-
auto_default_from_env=True, description="front-end static settings"
126+
json_schema_extra={"auto_default_from_env": True}, description="front-end static settings"
127127
)
128128

129129
# PLUGINS ----------------
130130

131131
WEBSERVER_ACTIVITY: PrometheusSettings | None = Field(
132-
auto_default_from_env=True,
132+
json_schema_extra={"auto_default_from_env": True},
133133
description="activity plugin",
134134
)
135135
WEBSERVER_CATALOG: CatalogSettings | None = Field(
136-
auto_default_from_env=True, description="catalog service client's plugin"
136+
json_schema_extra={"auto_default_from_env": True}, description="catalog service client's plugin"
137137
)
138138
# TODO: Shall be required
139139
WEBSERVER_DB: PostgresSettings | None = Field(
140-
auto_default_from_env=True, description="database plugin"
140+
json_schema_extra={"auto_default_from_env": True}, description="database plugin"
141141
)
142142
WEBSERVER_DIAGNOSTICS: DiagnosticsSettings | None = Field(
143-
auto_default_from_env=True, description="diagnostics plugin"
143+
json_schema_extra={"auto_default_from_env": True}, description="diagnostics plugin"
144144
)
145145
WEBSERVER_DIRECTOR_V2: DirectorV2Settings | None = Field(
146-
auto_default_from_env=True, description="director-v2 service client's plugin"
146+
json_schema_extra={"auto_default_from_env": True}, description="director-v2 service client's plugin"
147147
)
148148
WEBSERVER_EMAIL: SMTPSettings | None = Field(
149-
auto_default_from_env=True, description="email plugin"
149+
json_schema_extra={"auto_default_from_env": True}, description="email plugin"
150150
)
151151
WEBSERVER_EXPORTER: ExporterSettings | None = Field(
152-
auto_default_from_env=True, description="exporter plugin"
152+
json_schema_extra={"auto_default_from_env": True}, description="exporter plugin"
153153
)
154154
WEBSERVER_GARBAGE_COLLECTOR: GarbageCollectorSettings | None = Field(
155-
auto_default_from_env=True, description="garbage collector plugin"
155+
json_schema_extra={"auto_default_from_env": True}, description="garbage collector plugin"
156156
)
157157

158158
WEBSERVER_INVITATIONS: InvitationsSettings | None = Field(
159-
auto_default_from_env=True, description="invitations plugin"
159+
json_schema_extra={"auto_default_from_env": True}, description="invitations plugin"
160160
)
161161

162162
WEBSERVER_LOGIN: LoginSettings | None = Field(
163-
auto_default_from_env=True, description="login plugin"
163+
json_schema_extra={"auto_default_from_env": True}, description="login plugin"
164164
)
165165

166166
WEBSERVER_PAYMENTS: PaymentsSettings | None = Field(
167-
auto_default_from_env=True, description="payments plugin settings"
167+
json_schema_extra={"auto_default_from_env": True}, description="payments plugin settings"
168168
)
169169

170170
WEBSERVER_DYNAMIC_SCHEDULER: DynamicSchedulerSettings | None = Field(
@@ -241,7 +241,7 @@ class ApplicationSettings(BaseCustomSettings, MixinLoggingSettings):
241241
"Currently this is a system plugin and cannot be disabled",
242242
)
243243

244-
@root_validator()
244+
@model_validator(mode="after")
245245
@classmethod
246246
def build_vcs_release_url_if_unset(cls, values):
247247
release_url = values.get("SIMCORE_VCS_RELEASE_URL")
@@ -259,14 +259,13 @@ def build_vcs_release_url_if_unset(cls, values):
259259

260260
return values
261261

262-
@validator(
262+
@field_validator(
263263
# List of plugins under-development (keep up-to-date)
264264
# TODO: consider mark as dev-feature in field extras of Config attr.
265265
# Then they can be automtically advertised
266266
"WEBSERVER_META_MODELING",
267267
"WEBSERVER_VERSION_CONTROL",
268-
pre=True,
269-
always=True,
268+
mode="before"
270269
)
271270
@classmethod
272271
def enable_only_if_dev_features_allowed(cls, v, values, field: ModelField):
@@ -286,12 +285,12 @@ def log_level(self) -> int:
286285
level: int = getattr(logging, self.WEBSERVER_LOGLEVEL.upper())
287286
return level
288287

289-
@validator("WEBSERVER_LOGLEVEL")
288+
@field_validator("WEBSERVER_LOGLEVEL")
290289
@classmethod
291290
def valid_log_level(cls, value):
292291
return cls.validate_log_level(value)
293292

294-
@validator("SC_HEALTHCHECK_TIMEOUT", pre=True)
293+
@field_validator("SC_HEALTHCHECK_TIMEOUT", mode="before")
295294
@classmethod
296295
def get_healthcheck_timeout_in_seconds(cls, v):
297296
# Ex. HEALTHCHECK --interval=5m --timeout=3s

services/web/server/src/simcore_service_webserver/catalog/_api.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from models_library.users import UserID
2424
from models_library.utils.fastapi_encoders import jsonable_encoder
2525
from pint import UnitRegistry
26-
from pydantic import BaseModel
26+
from pydantic import ConfigDict, BaseModel
2727
from servicelib.aiohttp.requests_validation import handle_validation_as_http_error
2828
from servicelib.rabbitmq.rpc_interfaces.catalog import services as catalog_rpc
2929
from servicelib.rest_constants import RESPONSE_MODEL_POLICY
@@ -42,9 +42,7 @@ class CatalogRequestContext(BaseModel):
4242
user_id: UserID
4343
product_name: str
4444
unit_registry: UnitRegistry
45-
46-
class Config:
47-
arbitrary_types_allowed = True
45+
model_config = ConfigDict(arbitrary_types_allowed=True)
4846

4947
@classmethod
5048
def create(cls, request: Request) -> "CatalogRequestContext":

services/web/server/src/simcore_service_webserver/catalog/_handlers.py

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
ServiceResourcesDict,
2727
ServiceResourcesDictHelpers,
2828
)
29-
from pydantic import BaseModel, Extra, Field, parse_obj_as, validator
29+
from pydantic import TypeAdapter, field_validator, ConfigDict, BaseModel, Field
3030
from servicelib.aiohttp.requests_validation import (
3131
parse_request_body_as,
3232
parse_request_path_parameters_as,
@@ -54,12 +54,9 @@
5454
class ServicePathParams(BaseModel):
5555
service_key: ServiceKey
5656
service_version: ServiceVersion
57+
model_config = ConfigDict(populate_by_name=True, extra="forbid")
5758

58-
class Config:
59-
allow_population_by_field_name = True
60-
extra = Extra.forbid
61-
62-
@validator("service_key", pre=True)
59+
@field_validator("service_key", mode="before")
6360
@classmethod
6461
def ensure_unquoted(cls, v):
6562
# NOTE: this is needed as in pytest mode, the aiohttp server does not seem to unquote automatically
@@ -90,7 +87,7 @@ async def list_services_latest(request: Request):
9087
user_id=request_ctx.user_id,
9188
product_name=request_ctx.product_name,
9289
unit_registry=request_ctx.unit_registry,
93-
page_params=PageQueryParameters.construct(
90+
page_params=PageQueryParameters.model_construct(
9491
offset=query_params.offset, limit=query_params.limit
9592
),
9693
)
@@ -160,7 +157,7 @@ async def update_service(request: Request):
160157
product_name=request_ctx.product_name,
161158
service_key=path_params.service_key,
162159
service_version=path_params.service_version,
163-
update_data=update.dict(exclude_unset=True),
160+
update_data=update.model_dump(exclude_unset=True),
164161
unit_registry=request_ctx.unit_registry,
165162
)
166163

@@ -182,7 +179,7 @@ async def list_service_inputs(request: Request):
182179
path_params.service_key, path_params.service_version, ctx
183180
)
184181

185-
data = [m.dict(**RESPONSE_MODEL_POLICY) for m in response_model]
182+
data = [m.model_dump(**RESPONSE_MODEL_POLICY) for m in response_model]
186183
return await asyncio.get_event_loop().run_in_executor(
187184
None, envelope_json_response, data
188185
)
@@ -210,7 +207,7 @@ async def get_service_input(request: Request):
210207
ctx,
211208
)
212209

213-
data = response_model.dict(**RESPONSE_MODEL_POLICY)
210+
data = response_model.model_dump(**RESPONSE_MODEL_POLICY)
214211
return await asyncio.get_event_loop().run_in_executor(
215212
None, envelope_json_response, data
216213
)
@@ -265,7 +262,7 @@ async def list_service_outputs(request: Request):
265262
path_params.service_key, path_params.service_version, ctx
266263
)
267264

268-
data = [m.dict(**RESPONSE_MODEL_POLICY) for m in response_model]
265+
data = [m.model_dump(**RESPONSE_MODEL_POLICY) for m in response_model]
269266
return await asyncio.get_event_loop().run_in_executor(
270267
None, envelope_json_response, data
271268
)
@@ -293,7 +290,7 @@ async def get_service_output(request: Request):
293290
ctx,
294291
)
295292

296-
data = response_model.dict(**RESPONSE_MODEL_POLICY)
293+
data = response_model.model_dump(**RESPONSE_MODEL_POLICY)
297294
return await asyncio.get_event_loop().run_in_executor(
298295
None, envelope_json_response, data
299296
)
@@ -386,4 +383,4 @@ async def get_service_pricing_plan(request: Request):
386383
service_version=f"{path_params.service_version}",
387384
)
388385

389-
return envelope_json_response(parse_obj_as(PricingPlanGet, pricing_plan))
386+
return envelope_json_response(TypeAdapter(PricingPlanGet).validate_python(pricing_plan))

services/web/server/src/simcore_service_webserver/diagnostics/settings.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from aiohttp.web import Application
2-
from pydantic import Field, NonNegativeFloat, PositiveFloat, validator
2+
from pydantic import AliasChoices, Field, NonNegativeFloat, PositiveFloat, field_validator
33
from servicelib.aiohttp.application_keys import APP_SETTINGS_KEY
44
from settings_library.base import BaseCustomSettings
55

@@ -11,7 +11,7 @@ class DiagnosticsSettings(BaseCustomSettings):
1111
"Any task blocked more than slow_duration_secs is logged as WARNING"
1212
"Aims to identify possible blocking calls"
1313
),
14-
env=["DIAGNOSTICS_SLOW_DURATION_SECS", "AIODEBUG_SLOW_DURATION_SECS"],
14+
validation_alias=AliasChoices("DIAGNOSTICS_SLOW_DURATION_SECS", "AIODEBUG_SLOW_DURATION_SECS"),
1515
)
1616

1717
DIAGNOSTICS_MAX_TASK_DELAY: PositiveFloat = Field(
@@ -25,7 +25,7 @@ class DiagnosticsSettings(BaseCustomSettings):
2525

2626
DIAGNOSTICS_START_SENSING_DELAY: NonNegativeFloat = 60.0
2727

28-
@validator("DIAGNOSTICS_MAX_TASK_DELAY", pre=True)
28+
@field_validator("DIAGNOSTICS_MAX_TASK_DELAY", mode="before")
2929
@classmethod
3030
def validate_max_task_delay(cls, v, values):
3131
# Sets an upper threshold for blocking functions, i.e.

services/web/server/src/simcore_service_webserver/director_v2/_models.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
ExternalClusterAuthentication,
1111
)
1212
from models_library.users import GroupID
13-
from pydantic import AnyHttpUrl, BaseModel, Field, validator
13+
from pydantic import AnyHttpUrl, BaseModel, ConfigDict, Field, field_validator
1414
from pydantic.networks import AnyUrl, HttpUrl
1515
from simcore_postgres_database.models.clusters import ClusterType
1616

@@ -33,7 +33,7 @@ class ClusterCreate(BaseCluster):
3333
alias="accessRights", default_factory=dict
3434
)
3535

36-
@validator("thumbnail", always=True, pre=True)
36+
@field_validator("thumbnail", mode="before")
3737
@classmethod
3838
def set_default_thumbnail_if_empty(cls, v, values):
3939
if v is None and (
@@ -42,8 +42,8 @@ def set_default_thumbnail_if_empty(cls, v, values):
4242
return _DEFAULT_THUMBNAILS[f"{cluster_type}"]
4343
return v
4444

45-
class Config(BaseCluster.Config):
46-
schema_extra: ClassVar[dict[str, Any]] = {
45+
model_config = ConfigDict(
46+
json_schema_extra={
4747
"examples": [
4848
{
4949
"name": "My awesome cluster",
@@ -74,6 +74,7 @@ class Config(BaseCluster.Config):
7474
},
7575
]
7676
}
77+
)
7778

7879

7980
class ClusterPatch(BaseCluster):

services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from models_library.users import UserID
1717
from models_library.utils.common_validators import null_or_none_str_to_none_validator
1818
from models_library.workspaces import WorkspaceID
19-
from pydantic import Extra, Field, Json, parse_obj_as, validator
19+
from pydantic import TypeAdapter, field_validator, ConfigDict, Field, Json
2020
from servicelib.aiohttp.requests_validation import (
2121
RequestParams,
2222
StrictRequestParams,
@@ -89,7 +89,7 @@ class FolderListWithJsonStrQueryParams(PageQueryParameters):
8989
order_by: Json[OrderBy] = Field(
9090
default=OrderBy(field=IDStr("modified"), direction=OrderDirection.DESC),
9191
description="Order by field (modified_at|name|description) and direction (asc|desc). The default sorting order is ascending.",
92-
example='{"field": "name", "direction": "desc"}',
92+
examples=['{"field": "name", "direction": "desc"}'],
9393
alias="order_by",
9494
)
9595
folder_id: FolderID | None = Field(
@@ -101,7 +101,7 @@ class FolderListWithJsonStrQueryParams(PageQueryParameters):
101101
description="List folders in specific workspace. By default, list in the user private workspace",
102102
)
103103

104-
@validator("order_by", check_fields=False)
104+
@field_validator("order_by", check_fields=False)
105105
@classmethod
106106
def validate_order_by_field(cls, v):
107107
if v.field not in {
@@ -114,17 +114,15 @@ def validate_order_by_field(cls, v):
114114
if v.field == "modified_at":
115115
v.field = "modified"
116116
return v
117-
118-
class Config:
119-
extra = Extra.forbid
117+
model_config = ConfigDict(extra="forbid")
120118

121119
# validators
122-
_null_or_none_str_to_none_validator = validator(
123-
"folder_id", allow_reuse=True, pre=True
120+
_null_or_none_str_to_none_validator = field_validator(
121+
"folder_id", mode="before"
124122
)(null_or_none_str_to_none_validator)
125123

126-
_null_or_none_str_to_none_validator2 = validator(
127-
"workspace_id", allow_reuse=True, pre=True
124+
_null_or_none_str_to_none_validator2 = field_validator(
125+
"workspace_id", mode="before"
128126
)(null_or_none_str_to_none_validator)
129127

130128

@@ -166,7 +164,7 @@ async def list_folders(request: web.Request):
166164
workspace_id=query_params.workspace_id,
167165
offset=query_params.offset,
168166
limit=query_params.limit,
169-
order_by=parse_obj_as(OrderBy, query_params.order_by),
167+
order_by=TypeAdapter(OrderBy).validate_python(query_params.order_by),
170168
)
171169

172170
page = Page[FolderGet].model_validate(

0 commit comments

Comments
 (0)