Skip to content

Commit 4255546

Browse files
authored
♻️ Dask: maintenance, refactor, upgrade (#4051)
1 parent aa901cf commit 4255546

File tree

17 files changed

+359
-149
lines changed

17 files changed

+359
-149
lines changed

packages/models-library/src/models_library/services.py

Lines changed: 32 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import re
88
from datetime import datetime
99
from enum import Enum
10-
from typing import Any, Optional, Union
10+
from typing import Any
1111
from uuid import UUID
1212

1313
from pydantic import (
@@ -60,10 +60,16 @@
6060
class ServiceKey(ConstrainedStr):
6161
regex = re.compile(SERVICE_KEY_RE)
6262

63+
class Config:
64+
frozen = True
65+
6366

6467
class ServiceVersion(ConstrainedStr):
6568
regex = re.compile(VERSION_RE)
6669

70+
class Config:
71+
frozen = True
72+
6773

6874
RunID = UUID
6975

@@ -126,7 +132,7 @@ class Author(BaseModel):
126132
127133
description="Email address",
128134
)
129-
affiliation: Optional[str] = Field(
135+
affiliation: str | None = Field(
130136
None, examples=["Sense8", "Babylon 5"], description="Affiliation of the author"
131137
)
132138

@@ -142,7 +148,7 @@ class BaseServiceIOModel(BaseModel):
142148
## management
143149

144150
### human readable descriptors
145-
display_order: Optional[float] = Field(
151+
display_order: float | None = Field(
146152
None,
147153
alias="displayOrder",
148154
deprecated=True,
@@ -177,22 +183,22 @@ class BaseServiceIOModel(BaseModel):
177183
regex=PROPERTY_TYPE_RE,
178184
)
179185

180-
content_schema: Optional[dict[str, Any]] = Field(
186+
content_schema: dict[str, Any] | None = Field(
181187
None,
182188
description="jsonschema of this input/output. Required when type='ref_contentSchema'",
183189
alias="contentSchema",
184190
)
185191

186192
# value
187-
file_to_key_map: Optional[dict[FileName, ServicePortKey]] = Field(
193+
file_to_key_map: dict[FileName, ServicePortKey] | None = Field(
188194
None,
189195
alias="fileToKeyMap",
190196
description="Place the data associated with the named keys in files",
191197
examples=[{"dir/input1.txt": "key_1", "dir33/input2.txt": "key2"}],
192198
)
193199

194200
# TODO: should deprecate since content_schema include units
195-
unit: Optional[str] = Field(
201+
unit: str | None = Field(
196202
None,
197203
description="Units, when it refers to a physical quantity",
198204
)
@@ -249,11 +255,11 @@ class ServiceInput(BaseServiceIOModel):
249255
"""
250256

251257
# TODO: should deprecate since content_schema include defaults as well
252-
default_value: Optional[Union[StrictBool, StrictInt, StrictFloat, str]] = Field(
258+
default_value: StrictBool | StrictInt | StrictFloat | str | None = Field(
253259
None, alias="defaultValue", examples=["Dog", True]
254260
)
255261

256-
widget: Optional[Widget] = Field(
262+
widget: Widget | None = Field(
257263
None,
258264
description="custom widget to use instead of the default one determined from the data-type",
259265
)
@@ -323,7 +329,7 @@ def from_json_schema(cls, port_schema: dict[str, Any]) -> "ServiceInput":
323329

324330

325331
class ServiceOutput(BaseServiceIOModel):
326-
widget: Optional[Widget] = Field(
332+
widget: Widget | None = Field(
327333
None,
328334
description="custom widget to use instead of the default one determined from the data-type",
329335
deprecated=True,
@@ -370,30 +376,26 @@ def from_json_schema(cls, port_schema: dict[str, Any]) -> "ServiceOutput":
370376
class ServiceKeyVersion(BaseModel):
371377
"""This pair uniquely identifies a services"""
372378

373-
key: str = Field(
379+
key: ServiceKey = Field(
374380
...,
375381
description="distinctive name for the node based on the docker registry path",
376-
regex=KEY_RE,
377-
examples=[
378-
"simcore/services/comp/itis/sleeper",
379-
"simcore/services/dynamic/3dviewer",
380-
],
381382
)
382-
version: str = Field(
383+
version: ServiceVersion = Field(
383384
...,
384385
description="service version number",
385-
regex=VERSION_RE,
386-
examples=["1.0.0", "0.0.1"],
387386
)
388387

388+
class Config:
389+
frozen = True
390+
389391

390392
class _BaseServiceCommonDataModel(BaseModel):
391393
name: str = Field(
392394
...,
393395
description="short, human readable name for the node",
394396
example="Fast Counter",
395397
)
396-
thumbnail: Optional[HttpUrl] = Field(
398+
thumbnail: HttpUrl | None = Field(
397399
None,
398400
description="url to the thumbnail",
399401
examples=[
@@ -428,7 +430,7 @@ class ServiceDockerData(ServiceKeyVersion, _BaseServiceCommonDataModel):
428430
This is one to one with node-meta-v0.0.1.json
429431
"""
430432

431-
integration_version: Optional[str] = Field(
433+
integration_version: str | None = Field(
432434
None,
433435
alias="integration-version",
434436
description="integration version number",
@@ -442,22 +444,22 @@ class ServiceDockerData(ServiceKeyVersion, _BaseServiceCommonDataModel):
442444
examples=["computational"],
443445
)
444446

445-
badges: Optional[list[Badge]] = Field(None)
447+
badges: list[Badge] | None = Field(None)
446448

447449
authors: list[Author] = Field(..., min_items=1)
448450
contact: LowerCaseEmailStr = Field(
449451
...,
450452
description="email to correspond to the authors about the node",
451453
examples=["[email protected]"],
452454
)
453-
inputs: Optional[ServiceInputsDict] = Field(
455+
inputs: ServiceInputsDict | None = Field(
454456
..., description="definition of the inputs of this node"
455457
)
456-
outputs: Optional[ServiceOutputsDict] = Field(
458+
outputs: ServiceOutputsDict | None = Field(
457459
..., description="definition of the outputs of this node"
458460
)
459461

460-
boot_options: Optional[BootOptions] = Field(
462+
boot_options: BootOptions | None = Field(
461463
None,
462464
alias="boot-options",
463465
description="Service defined boot options. These get injected in the service as env variables.",
@@ -466,6 +468,7 @@ class ServiceDockerData(ServiceKeyVersion, _BaseServiceCommonDataModel):
466468
class Config:
467469
description = "Description of a simcore node 'class' with input and output"
468470
extra = Extra.forbid
471+
frozen = False # it inherits from ServiceKeyVersion.
469472

470473
schema_extra = {
471474
"examples": [
@@ -567,16 +570,16 @@ class ServiceMetaData(_BaseServiceCommonDataModel):
567570
# it should be implemented with a different model e.g. ServiceMetaDataUpdate
568571
#
569572

570-
name: Optional[str]
571-
thumbnail: Optional[HttpUrl]
572-
description: Optional[str]
573-
deprecated: Optional[datetime] = Field(
573+
name: str | None
574+
thumbnail: HttpUrl | None
575+
description: str | None
576+
deprecated: datetime | None = Field(
574577
default=None,
575578
description="If filled with a date, then the service is to be deprecated at that date (e.g. cannot start anymore)",
576579
)
577580

578581
# user-defined metatada
579-
classifiers: Optional[list[str]]
582+
classifiers: list[str] | None
580583
quality: dict[str, Any] = {}
581584

582585
class Config:

services/api-server/VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.4.3
1+
0.4.4

services/api-server/openapi.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"info": {
44
"title": "osparc.io web API",
55
"description": "osparc-simcore public web API specifications",
6-
"version": "0.4.2",
6+
"version": "0.4.4",
77
"x-logo": {
88
"url": "https://raw.githubusercontent.com/ITISFoundation/osparc-manual/b809d93619512eb60c827b7e769c6145758378d0/_media/osparc-logo.svg",
99
"altText": "osparc-simcore logo"
@@ -548,6 +548,7 @@
548548
"required": true,
549549
"schema": {
550550
"title": "Version",
551+
"pattern": "^(0|[1-9]\\d*)(\\.(0|[1-9]\\d*)){2}(-(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*)(\\.(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*))*)?(\\+[-\\da-zA-Z]+(\\.[-\\da-zA-Z-]+)*)?$",
551552
"type": "string"
552553
},
553554
"name": "version",
@@ -608,6 +609,7 @@
608609
"required": true,
609610
"schema": {
610611
"title": "Version",
612+
"pattern": "^(0|[1-9]\\d*)(\\.(0|[1-9]\\d*)){2}(-(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*)(\\.(0|[1-9]\\d*|\\d*[-a-zA-Z][-\\da-zA-Z]*))*)?(\\+[-\\da-zA-Z]+(\\.[-\\da-zA-Z-]+)*)?$",
611613
"type": "string"
612614
},
613615
"name": "version",

services/api-server/setup.cfg

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[bumpversion]
2-
current_version = 0.4.3
2+
current_version = 0.4.4
33
commit = True
44
message = services/api-server version: {current_version} → {new_version}
55
tag = False
@@ -10,7 +10,7 @@ commit_args = --no-verify
1010
[tool:pytest]
1111
asyncio_mode = auto
1212
addopts = --strict-markers
13-
markers =
13+
markers =
1414
slow: marks tests as slow (deselect with '-m "not slow"')
1515
acceptance_test: "marks tests as 'acceptance tests' i.e. does the system do what the user expects? Typically those are workflows."
1616
testit: "marks test to run during development"

services/api-server/src/simcore_service_api_server/api/routes/solvers_jobs.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import logging
55
from collections import deque
6-
from typing import Callable, Optional, Union
6+
from typing import Callable
77
from uuid import UUID
88

99
from fastapi import APIRouter, Depends, status
@@ -62,7 +62,7 @@ def _compose_job_resource_name(solver_key, solver_version, job_id) -> str:
6262
)
6363
async def list_jobs(
6464
solver_key: SolverKeyId,
65-
version: str,
65+
version: VersionStr,
6666
user_id: PositiveInt = Depends(get_current_user_id),
6767
catalog_client: CatalogApi = Depends(get_api_client(CatalogApi)),
6868
webserver_api: AuthSession = Depends(get_webserver_session),
@@ -97,7 +97,7 @@ async def list_jobs(
9797
)
9898
async def create_job(
9999
solver_key: SolverKeyId,
100-
version: str,
100+
version: VersionStr,
101101
inputs: JobInputs,
102102
user_id: PositiveInt = Depends(get_current_user_id),
103103
catalog_client: CatalogApi = Depends(get_api_client(CatalogApi)),
@@ -186,7 +186,7 @@ async def start_job(
186186
solver_key: SolverKeyId,
187187
version: VersionStr,
188188
job_id: UUID,
189-
cluster_id: Optional[ClusterID] = None,
189+
cluster_id: ClusterID | None = None,
190190
user_id: PositiveInt = Depends(get_current_user_id),
191191
director2_api: DirectorV2Api = Depends(get_api_client(DirectorV2Api)),
192192
product_name: str = Depends(get_product_name),
@@ -269,7 +269,7 @@ async def get_job_outputs(
269269
assert len(node_ids) == 1 # nosec
270270

271271
outputs: dict[
272-
str, Union[float, int, bool, BaseFileLink, str, None]
272+
str, float | int | bool | BaseFileLink | str | None
273273
] = await get_solver_output_results(
274274
user_id=user_id,
275275
project_uuid=job_id,
Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1+
import re
2+
13
from models_library.basic_regex import VERSION_RE
2-
from pydantic import constr
4+
from pydantic import ConstrainedStr
5+
36

4-
VersionStr = constr(strip_whitespace=True, regex=VERSION_RE) # as M.m.p
7+
class VersionStr(ConstrainedStr):
8+
strip_whitespace = True
9+
regex = re.compile(VERSION_RE)

services/api-server/src/simcore_service_api_server/models/schemas/solvers.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1+
import re
12
import urllib.parse
2-
from typing import Any, Literal, Optional, Union
3+
from typing import Any, Literal
34

45
import packaging.version
56
from models_library.basic_regex import PUBLIC_VARIABLE_NAME_RE
67
from models_library.services import COMPUTATIONAL_SERVICE_KEY_RE, ServiceDockerData
78
from packaging.version import LegacyVersion, Version
8-
from pydantic import BaseModel, Extra, Field, HttpUrl, constr
9+
from pydantic import BaseModel, ConstrainedStr, Extra, Field, HttpUrl
910

1011
from ..api_resources import compose_resource_name
1112
from ..basic_types import VersionStr
@@ -28,11 +29,10 @@
2829
#
2930
SOLVER_RESOURCE_NAME_RE = r"^solvers/([^\s/]+)/releases/([\d\.]+)$"
3031

31-
SolverKeyId = constr(
32-
strip_whitespace=True,
33-
regex=COMPUTATIONAL_SERVICE_KEY_RE,
34-
# TODO: should we use here a less restrictive regex that does not impose simcore/comp/?? this should be catalog responsibility
35-
)
32+
33+
class SolverKeyId(ConstrainedStr):
34+
strip_whitespace = True
35+
regex = re.compile(COMPUTATIONAL_SERVICE_KEY_RE)
3636

3737

3838
class Solver(BaseModel):
@@ -49,13 +49,13 @@ class Solver(BaseModel):
4949

5050
# Human readables Identifiers
5151
title: str = Field(..., description="Human readable name")
52-
description: Optional[str]
52+
description: str | None
5353
maintainer: str
5454
# TODO: consider released: Optional[datetime] required?
5555
# TODO: consider version_aliases: list[str] = [] # remaining tags
5656

5757
# Get links to other resources
58-
url: Optional[HttpUrl] = Field(..., description="Link to get this resource")
58+
url: HttpUrl | None = Field(..., description="Link to get this resource")
5959

6060
class Config:
6161
extra = Extra.ignore
@@ -86,7 +86,7 @@ def create_from_image(cls, image_meta: ServiceDockerData) -> "Solver":
8686
)
8787

8888
@property
89-
def pep404_version(self) -> Union[Version, LegacyVersion]:
89+
def pep404_version(self) -> Version | LegacyVersion:
9090
"""Rich version type that can be used e.g. to compare"""
9191
return packaging.version.parse(self.version)
9292

@@ -118,7 +118,7 @@ class SolverPort(BaseModel):
118118
title="Key name",
119119
)
120120
kind: PortKindStr
121-
content_schema: Optional[dict[str, Any]] = Field(
121+
content_schema: dict[str, Any] | None = Field(
122122
None,
123123
description="jsonschema for the port's value. SEE https://json-schema.org",
124124
)

services/catalog/tests/unit/with_dbs/test_services_access_rights.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
from typing import Callable
66

77
from fastapi import FastAPI
8-
from models_library.services import ServiceDockerData
8+
from models_library.services import ServiceDockerData, ServiceVersion
99
from models_library.services_db import ServiceAccessRightsAtDB
10+
from pydantic import parse_obj_as
1011
from simcore_service_catalog.db.repositories.services import ServicesRepository
1112
from simcore_service_catalog.models.domain.group import GroupAtDB
1213
from simcore_service_catalog.services.access_rights import (
@@ -112,7 +113,7 @@ async def test_auto_upgrade_policy(
112113
new_service_metadata = ServiceDockerData.parse_obj(
113114
ServiceDockerData.Config.schema_extra["examples"][MOST_UPDATED_EXAMPLE]
114115
)
115-
new_service_metadata.version = "1.0.11"
116+
new_service_metadata.version = parse_obj_as(ServiceVersion, "1.0.11")
116117

117118
# we have three versions of the service in the database for which the sorting matters: (1.0.11 should inherit from 1.0.10 not 1.0.9)
118119
await services_db_tables_injector(

0 commit comments

Comments
 (0)