Skip to content

Commit 60a743a

Browse files
committed
annotations
1 parent bb66bb8 commit 60a743a

File tree

6 files changed

+117
-67
lines changed

6 files changed

+117
-67
lines changed

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from models_library.workspaces import WorkspaceID
1313
from pydantic import BaseModel, ConfigDict, Field, HttpUrl, field_validator
1414

15+
from ._compat import Undefined
1516
from .basic_regex import DATE_RE, UUID_RE_BASE
1617
from .emails import LowerCaseEmailStr
1718
from .projects_access import AccessRights, GroupIDStr
@@ -24,6 +25,9 @@
2425
none_to_empty_str_pre_validator,
2526
)
2627

28+
_Unset: Any = Undefined
29+
30+
2731
ProjectID: TypeAlias = UUID
2832
ClassifierID: TypeAlias = str
2933

@@ -151,7 +155,7 @@ class Project(BaseProjectModel):
151155
description="Contains the reference to the project classifiers",
152156
examples=["some:id:to:a:classifier"],
153157
),
154-
]
158+
] = _Unset
155159

156160
# Project state (SEE projects_state.py)
157161
state: ProjectState | None = None

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

Lines changed: 79 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -144,31 +144,39 @@ class Node(BaseModel):
144144
label: str = Field(
145145
..., description="The short name of the node", examples=["JupyterLab"]
146146
)
147-
progress: float | None = Field(
148-
default=None,
149-
ge=0,
150-
le=100,
151-
description="the node progress value (deprecated in DB, still used for API only)",
152-
deprecated=True,
153-
)
154-
thumbnail: Annotated[str, HttpUrl] | None = Field(
155-
default=None,
156-
description="url of the latest screenshot of the node",
157-
examples=["https://placeimg.com/171/96/tech/grayscale/?0.jpg"],
158-
)
147+
progress: Annotated[
148+
float | None,
149+
Field(
150+
ge=0,
151+
le=100,
152+
description="the node progress value (deprecated in DB, still used for API only)",
153+
deprecated=True,
154+
),
155+
] = None
156+
157+
thumbnail: Annotated[
158+
str | HttpUrl | None,
159+
Field(
160+
description="url of the latest screenshot of the node",
161+
examples=["https://placeimg.com/171/96/tech/grayscale/?0.jpg"],
162+
),
163+
] = None
159164

160165
# RUN HASH
161-
run_hash: str | None = Field(
162-
default=None,
163-
description="the hex digest of the resolved inputs +outputs hash at the time when the last outputs were generated",
164-
alias="runHash",
165-
)
166+
run_hash: Annotated[
167+
str | None,
168+
Field(
169+
description="the hex digest of the resolved inputs +outputs hash at the time when the last outputs were generated",
170+
alias="runHash",
171+
),
172+
] = None
166173

167174
# INPUT PORTS ---
168175
inputs: Annotated[
169176
InputsDict | None,
170177
Field(default_factory=dict, description="values of input properties"),
171-
]
178+
] = _Unset
179+
172180
inputs_required: Annotated[
173181
list[InputID],
174182
Field(
@@ -177,15 +185,23 @@ class Node(BaseModel):
177185
alias="inputsRequired",
178186
),
179187
] = _Unset
180-
inputs_units: dict[InputID, UnitStr] | None = Field(
181-
description="Overrides default unit (if any) defined in the service for each port",
182-
alias="inputsUnits",
183-
)
184-
input_access: dict[InputID, AccessEnum] | None = Field(
185-
default=None,
186-
description="map with key - access level pairs",
187-
alias="inputAccess",
188-
)
188+
189+
inputs_units: Annotated[
190+
dict[InputID, UnitStr] | None,
191+
Field(
192+
description="Overrides default unit (if any) defined in the service for each port",
193+
alias="inputsUnits",
194+
),
195+
] = None
196+
197+
input_access: Annotated[
198+
dict[InputID, AccessEnum] | None,
199+
Field(
200+
description="map with key - access level pairs",
201+
alias="inputAccess",
202+
),
203+
] = None
204+
189205
input_nodes: Annotated[
190206
list[NodeID] | None,
191207
Field(
@@ -200,38 +216,50 @@ class Node(BaseModel):
200216
OutputsDict | None,
201217
Field(default_factory=dict, description="values of output properties"),
202218
] = _Unset
203-
output_node: bool | None = Field(default=None, deprecated=True, alias="outputNode")
204-
output_nodes: list[NodeID] | None = Field(
205-
default=None,
206-
description="Used in group-nodes. Node IDs of those connected to the output",
207-
alias="outputNodes",
208-
)
209219

210-
parent: NodeID | None = Field(
211-
default=None,
212-
description="Parent's (group-nodes') node ID s. Used to group",
213-
)
220+
output_node: Annotated[
221+
bool | None, Field(deprecated=True, alias="outputNode")
222+
] = None
214223

215-
position: Position | None = Field(
216-
default=None,
217-
deprecated=True,
218-
description="Use projects_ui.WorkbenchUI.position instead",
219-
)
224+
output_nodes: Annotated[
225+
list[NodeID] | None,
226+
Field(
227+
description="Used in group-nodes. Node IDs of those connected to the output",
228+
alias="outputNodes",
229+
),
230+
] = None
231+
232+
parent: Annotated[
233+
NodeID | None,
234+
Field(
235+
description="Parent's (group-nodes') node ID s. Used to group",
236+
),
237+
] = None
238+
239+
position: Annotated[
240+
Position | None,
241+
Field(
242+
deprecated=True,
243+
description="Use projects_ui.WorkbenchUI.position instead",
244+
),
245+
] = None
220246

221247
state: Annotated[
222248
NodeState | None,
223249
Field(default_factory=NodeState, description="The node's state object"),
224250
] = _Unset
225251

226-
boot_options: dict[EnvVarKey, str] | None = Field(
227-
default=None,
228-
alias="bootOptions",
229-
description=(
230-
"Some services provide alternative parameters to be injected at boot time. "
231-
"The user selection should be stored here, and it will overwrite the "
232-
"services's defaults."
252+
boot_options: Annotated[
253+
dict[EnvVarKey, str] | None,
254+
Field(
255+
alias="bootOptions",
256+
description=(
257+
"Some services provide alternative parameters to be injected at boot time. "
258+
"The user selection should be stored here, and it will overwrite the "
259+
"services's defaults."
260+
),
233261
),
234-
)
262+
] = None
235263

236264
@field_validator("thumbnail", mode="before")
237265
@classmethod
@@ -258,4 +286,5 @@ def _convert_from_enum(cls, v):
258286
model_config = ConfigDict(
259287
extra="forbid",
260288
json_schema_extra=_patch_json_schema_extra,
289+
populate_by_name=True,
261290
)

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

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
1-
from typing import Annotated
2-
31
from models_library.api_schemas__common.meta import BaseMeta
4-
from pydantic import AnyHttpUrl, ConfigDict, StringConstraints
2+
from pydantic import AnyHttpUrl, ConfigDict
53

64

75
class Meta(BaseMeta):
8-
docs_url: Annotated[AnyHttpUrl, StringConstraints(max_length=65536)]
9-
docs_dev_url: Annotated[AnyHttpUrl, StringConstraints(max_length=65536)]
6+
docs_url: AnyHttpUrl
7+
docs_dev_url: AnyHttpUrl
108
model_config = ConfigDict(
119
json_schema_extra={
1210
"example": {
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import json
2+
from itertools import chain
3+
from typing import Any
4+
5+
import pytest
6+
import simcore_service_api_server.models.schemas
7+
import simcore_service_api_server.models.schemas.meta
8+
from pydantic import BaseModel
9+
from pytest_simcore.pydantic_models import walk_model_examples_in_package
10+
11+
12+
@pytest.mark.parametrize(
13+
"model_cls, example_name, example_data",
14+
chain(
15+
walk_model_examples_in_package(simcore_service_api_server.models.schemas.meta)
16+
),
17+
)
18+
def test_all_models_library_models_config_examples(
19+
model_cls: type[BaseModel], example_name: int, example_data: Any
20+
):
21+
assert model_cls.model_validate(
22+
example_data
23+
), f"Failed {example_name} : {json.dumps(example_data)}"

services/web/server/src/simcore_service_webserver/meta_modeling/_function_nodes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def create_param_node_from_iterator_with_outputs(iterator_node: Node) -> Node:
3636
version=ServiceVersion("1.0.0"),
3737
label=iterator_node.label,
3838
inputs={},
39-
inputNodes=[],
39+
input_nodes=[],
4040
thumbnail="", # NOTE: hack due to issue in projects json-schema
4141
outputs=deepcopy(iterator_node.outputs),
4242
)

services/web/server/src/simcore_service_webserver/studies_dispatcher/_projects.py

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,9 @@ def _create_file_picker(download_link: str, output_label: str | None):
5959
data = {}
6060
data["downloadLink"] = url = TypeAdapter(AnyUrl).validate_python(download_link)
6161
if output_label:
62-
data["label"] = Path(output_label).name # type: ignore[assignment]
62+
data["label"] = Path(output_label).name # type: ignore[assignment]
6363
elif url.path:
64-
data["label"] = Path(url.path).name # type: ignore[assignment]
64+
data["label"] = Path(url.path).name # type: ignore[assignment]
6565
output = DownloadLink.model_validate(data)
6666

6767
output_id = "outFile"
@@ -70,7 +70,7 @@ def _create_file_picker(download_link: str, output_label: str | None):
7070
version=_FILE_PICKER_VERSION,
7171
label="File Picker",
7272
inputs={},
73-
inputNodes=[],
73+
input_nodes=[],
7474
outputs={output_id: output},
7575
progress=0,
7676
)
@@ -121,7 +121,7 @@ def _create_project_with_service(
121121
inputs=None,
122122
)
123123

124-
project = _create_project(
124+
return _create_project(
125125
project_id=project_id,
126126
name=f"Service {service_info.title}",
127127
description=f"Autogenerated study with service {service_info.footprint}",
@@ -135,8 +135,6 @@ def _create_project_with_service(
135135
},
136136
)
137137

138-
return project
139-
140138

141139
def _create_project_with_filepicker_and_service(
142140
project_id: ProjectID,
@@ -160,12 +158,12 @@ def _create_project_with_filepicker_and_service(
160158
output=file_picker_output_id,
161159
)
162160
},
163-
inputNodes=[
161+
input_nodes=[
164162
file_picker_id,
165163
],
166164
)
167165

168-
project = _create_project(
166+
return _create_project(
169167
project_id=project_id,
170168
name=f"Viewer {viewer_info.title}",
171169
description=f"Autogenerated study with file-picker and service {viewer_info.footprint}",
@@ -181,8 +179,6 @@ def _create_project_with_filepicker_and_service(
181179
},
182180
)
183181

184-
return project
185-
186182

187183
async def _add_new_project(
188184
app: web.Application, project: Project, user: UserInfo, *, product_name: str

0 commit comments

Comments
 (0)