Skip to content

Commit fdf2f2c

Browse files
author
Andrei Neagu
committed
Merge remote-tracking branch 'upstream/master' into pr-osparc-speedup-lrt-result
2 parents e88090b + 62565ba commit fdf2f2c

File tree

70 files changed

+1117
-654
lines changed

Some content is hidden

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

70 files changed

+1117
-654
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
applyTo: '**'
3+
---
4+
Provide project context and coding guidelines that AI should follow when generating code, answering questions, or reviewing changes.
5+
6+
## General Guidelines
7+
8+
1. **Test-Driven Development**: Write unit tests for all new functions and features. Use `pytest` for Python and appropriate testing frameworks for Node.js.
9+
2. **Environment Variables**: Use [Environment Variables Guide](../../docs/env-vars.md) for configuration. Avoid hardcoding sensitive information.
10+
3. **Documentation**: Prefer self-explanatory code; add documentation only if explicitly requested by the developer. Be concise.
11+
4. **Code Reviews**: Participate in code reviews and provide constructive feedback.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
applyTo: '**/*.js'
3+
---
4+
Provide project context and coding guidelines that AI should follow when generating code, answering questions, or reviewing changes.
5+
6+
## 🛠️Coding Instructions for Node.js in This Repository
7+
8+
* Use ES6+ syntax and features.
9+
* Follow the `package.json` configuration for dependencies and scripts.
10+
* Use `eslint` for linting and `prettier` for code formatting.
11+
* Write modular and reusable code, adhering to the project's structure.

.github/copilot-instructions.md renamed to .github/instructions/python.instructions.md

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,7 @@
1-
# GitHub Copilot Instructions
2-
3-
This document provides guidelines and best practices for using GitHub Copilot in the `osparc-simcore` repository and other Python and Node.js projects.
4-
5-
## General Guidelines
6-
7-
1. **Test-Driven Development**: Write unit tests for all new functions and features. Use `pytest` for Python and appropriate testing frameworks for Node.js.
8-
2. **Environment Variables**: Use [Environment Variables Guide](../docs/env-vars.md) for configuration. Avoid hardcoding sensitive information.
9-
3. **Documentation**: Prefer self-explanatory code; add documentation only if explicitly requested by the developer.
10-
111
---
2+
applyTo: '**/*.py'
3+
---
4+
Provide project context and coding guidelines that AI should follow when generating code, answering questions, or reviewing changes.
125

136
## 🛠️Coding Instructions for Python in This Repository
147

@@ -25,8 +18,8 @@ Follow these rules **strictly** when generating Python code:
2518

2619
### 3. **Code Style & Formatting**
2720

28-
* Follow [Python Coding Conventions](../docs/coding-conventions.md) **strictly**.
29-
* Format code with `black`.
21+
* Follow [Python Coding Conventions](../../docs/coding-conventions.md) **strictly**.
22+
* Format code with `black` and `ruff`.
3023
* Lint code with `ruff` and `pylint`.
3124

3225
### 4. **Library Compatibility**
@@ -51,11 +44,6 @@ Ensure compatibility with the following library versions:
5144
* Prefer `json_dumps` / `json_loads` from `common_library.json_serialization` instead of the built-in `json.dumps` / `json.loads`.
5245
* When using Pydantic models, prefer methods like `model.model_dump_json()` for serialization.
5346

54-
---
55-
56-
## 🛠️Coding Instructions for Node.js in This Repository
57-
58-
* Use ES6+ syntax and features.
59-
* Follow the `package.json` configuration for dependencies and scripts.
60-
* Use `eslint` for linting and `prettier` for code formatting.
61-
* Write modular and reusable code, adhering to the project's structure.
47+
### 7. **Running tests**
48+
* Use `--keep-docker-up` flag when testing to keep docker containers up between sessions.
49+
* Always activate the python virtual environment before running pytest.

packages/aws-library/src/aws_library/s3/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@
2222
)
2323

2424
__all__: tuple[str, ...] = (
25-
"CopiedBytesTransferredCallback",
26-
"MultiPartUploadLinks",
2725
"PRESIGNED_LINK_MAX_SIZE",
2826
"S3_MAX_FILE_SIZE",
27+
"CopiedBytesTransferredCallback",
28+
"MultiPartUploadLinks",
2929
"S3AccessError",
3030
"S3BucketInvalidError",
3131
"S3DestinationNotEmptyError",
@@ -37,8 +37,8 @@
3737
"S3RuntimeError",
3838
"S3UploadNotFoundError",
3939
"SimcoreS3API",
40-
"UploadedBytesTransferredCallback",
4140
"UploadID",
41+
"UploadedBytesTransferredCallback",
4242
)
4343

4444
# nopycln: file

packages/celery-library/tests/unit/test_tasks.py

Lines changed: 41 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ async def test_listing_task_uuids_contains_submitted_task(
210210

211211
async def test_filtering_listing_tasks(
212212
celery_task_manager: CeleryTaskManager,
213+
with_celery_worker: WorkController,
213214
):
214215
class MyFilter(TaskFilter):
215216
user_id: int
@@ -218,39 +219,46 @@ class MyFilter(TaskFilter):
218219

219220
user_id = 42
220221
expected_task_uuids: set[TaskUUID] = set()
222+
all_tasks: list[tuple[TaskUUID, MyFilter]] = []
223+
224+
try:
225+
for _ in range(5):
226+
task_filter = MyFilter(
227+
user_id=user_id,
228+
product_name=_faker.word(),
229+
client_app=_faker.word(),
230+
)
231+
task_uuid = await celery_task_manager.submit_task(
232+
TaskMetadata(
233+
name=dreamer_task.__name__,
234+
),
235+
task_filter=task_filter,
236+
)
237+
expected_task_uuids.add(task_uuid)
238+
all_tasks.append((task_uuid, task_filter))
239+
240+
for _ in range(3):
241+
task_filter = MyFilter(
242+
user_id=_faker.pyint(min_value=100, max_value=200),
243+
product_name=_faker.word(),
244+
client_app=_faker.word(),
245+
)
246+
task_uuid = await celery_task_manager.submit_task(
247+
TaskMetadata(
248+
name=dreamer_task.__name__,
249+
),
250+
task_filter=task_filter,
251+
)
252+
all_tasks.append((task_uuid, task_filter))
221253

222-
for _ in range(5):
223-
task_filter = MyFilter(
254+
search_filter = MyFilter(
224255
user_id=user_id,
225-
product_name=_faker.word(),
226-
client_app=_faker.word(),
227-
)
228-
task_uuid = await celery_task_manager.submit_task(
229-
TaskMetadata(
230-
name=dreamer_task.__name__,
231-
),
232-
task_filter=task_filter,
256+
product_name=Wildcard(),
257+
client_app=Wildcard(),
233258
)
234-
expected_task_uuids.add(task_uuid)
235-
236-
for _ in range(3):
237-
task_filter = MyFilter(
238-
user_id=_faker.pyint(min_value=100, max_value=200),
239-
product_name=_faker.word(),
240-
client_app=_faker.word(),
241-
)
242-
await celery_task_manager.submit_task(
243-
TaskMetadata(
244-
name=dreamer_task.__name__,
245-
),
246-
task_filter=task_filter,
247-
)
248-
249-
search_filter = MyFilter(
250-
user_id=user_id,
251-
product_name=Wildcard(),
252-
client_app=Wildcard(),
253-
)
254-
tasks = await celery_task_manager.list_tasks(search_filter)
255-
assert expected_task_uuids == {task.uuid for task in tasks}
256-
await asyncio.sleep(5 * 60)
259+
tasks = await celery_task_manager.list_tasks(search_filter)
260+
assert expected_task_uuids == {task.uuid for task in tasks}
261+
finally:
262+
# clean up all tasks. this should ideally be done in the fixture
263+
for task_uuid, task_filter in all_tasks:
264+
await celery_task_manager.cancel_task(task_filter, task_uuid)

packages/common-library/src/common_library/async_tools.py

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -89,32 +89,37 @@ async def cancel_wait_task(
8989
TimeoutError: raised if cannot cancel the task.
9090
CancelledError: raised ONLY if owner is being cancelled.
9191
"""
92+
if task.done():
93+
# nothing to do here
94+
return
9295

93-
cancelling = task.cancel()
94-
if not cancelling:
95-
return # task was alredy cancelled
96-
97-
assert task.cancelling() # nosec
98-
assert not task.cancelled() # nosec
99-
96+
# mark for cancellation
97+
task.cancel("cancel_wait_task was called to cancel this task")
10098
try:
101-
99+
_logger.debug("Cancelling task %s", task.get_name())
102100
await asyncio.shield(
103101
# NOTE shield ensures that cancellation of the caller function won't stop you
104102
# from observing the cancellation/finalization of task.
105103
asyncio.wait_for(task, timeout=max_delay)
106104
)
107-
108-
except asyncio.CancelledError:
109-
if not task.cancelled():
110-
# task owner function is being cancelled -> propagate cancellation
111-
raise
112-
113-
# else: task cancellation is complete, we can safely ignore it
114-
_logger.debug(
115-
"Task %s cancellation is complete",
105+
except TimeoutError:
106+
_logger.exception(
107+
"Timeout while cancelling task %s after %s seconds",
116108
task.get_name(),
109+
max_delay,
117110
)
111+
raise
112+
except asyncio.CancelledError:
113+
current_task = asyncio.current_task()
114+
assert current_task is not None # nosec
115+
if current_task.cancelling() > 0:
116+
# owner function is being cancelled -> propagate cancellation
117+
raise
118+
finally:
119+
if not task.done():
120+
_logger.error("Failed to cancel %s", task.get_name())
121+
else:
122+
_logger.debug("Task %s cancelled", task.get_name())
118123

119124

120125
def delayed_start(

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

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ class ProjectGet(OutputSchema):
126126

127127
# display
128128
name: str
129-
description: str
129+
description: Annotated[str, BeforeValidator(none_to_empty_str_pre_validator)]
130130
thumbnail: HttpUrl | Literal[""]
131131

132132
type: ProjectType
@@ -144,7 +144,7 @@ class ProjectGet(OutputSchema):
144144
trashed_at: datetime | None
145145
trashed_by: Annotated[
146146
GroupID | None, Field(description="The primary gid of the user who trashed")
147-
]
147+
] = None
148148

149149
# labeling
150150
tags: list[int]
@@ -166,10 +166,6 @@ class ProjectGet(OutputSchema):
166166
workspace_id: WorkspaceID | None
167167
folder_id: FolderID | None
168168

169-
_empty_description = field_validator("description", mode="before")(
170-
none_to_empty_str_pre_validator
171-
)
172-
173169
@staticmethod
174170
def _update_json_schema_extra(schema: JsonDict) -> None:
175171
schema.update(

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

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from common_library.basic_types import DEFAULT_FACTORY
1111
from pydantic import (
1212
BaseModel,
13+
BeforeValidator,
1314
ConfigDict,
1415
Field,
1516
HttpUrl,
@@ -85,13 +86,17 @@ class BaseProjectModel(BaseModel):
8586
]
8687
description: Annotated[
8788
str,
89+
BeforeValidator(none_to_empty_str_pre_validator),
8890
Field(
8991
description="longer one-line description about the project",
9092
examples=["Dabbling in temporal transitions ..."],
9193
),
9294
]
9395
thumbnail: Annotated[
9496
HttpUrl | None,
97+
BeforeValidator(
98+
empty_str_to_none_pre_validator,
99+
),
95100
Field(
96101
description="url of the project thumbnail",
97102
examples=["https://placeimg.com/171/96/tech/grayscale/?0.jpg"],
@@ -104,15 +109,6 @@ class BaseProjectModel(BaseModel):
104109
# Pipeline of nodes (SEE projects_nodes.py)
105110
workbench: Annotated[NodesDict, Field(description="Project's pipeline")]
106111

107-
# validators
108-
_empty_thumbnail_is_none = field_validator("thumbnail", mode="before")(
109-
empty_str_to_none_pre_validator
110-
)
111-
112-
_none_description_is_empty = field_validator("description", mode="before")(
113-
none_to_empty_str_pre_validator
114-
)
115-
116112

117113
class ProjectAtDB(BaseProjectModel):
118114
# Model used to READ from database

0 commit comments

Comments
 (0)