Skip to content

Commit be3f423

Browse files
committed
1st trial
1 parent 726b3d6 commit be3f423

File tree

6 files changed

+210
-159
lines changed

6 files changed

+210
-159
lines changed

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

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import logging
22
from collections.abc import Callable, Coroutine
33
from contextlib import AsyncExitStack
4+
from pathlib import Path
45
from typing import Any
56

67
from aiofiles.tempfile import TemporaryDirectory as AioTemporaryDirectory
@@ -11,8 +12,7 @@
1112
from .._constants import RQ_PRODUCT_KEY
1213
from .._meta import API_VTAG
1314
from ..login.decorators import login_required
14-
from ..projects.lock import lock_project
15-
from ..projects.projects_api import retrieve_and_notify_project_locked_state
15+
from ..projects.projects_api import with_project_locked_notified_state
1616
from ..security.decorators import permission_required
1717
from ..users.api import get_user_fullname
1818
from ._formatter.archive import get_sds_archive_path
@@ -42,18 +42,18 @@ async def export_project(request: web.Request):
4242
user_id = request[RQT_USERID_KEY]
4343
project_uuid = request.match_info.get("project_id")
4444
assert project_uuid # nosec
45-
delete_tmp_dir: Callable[[], Coroutine[Any, Any, None]] | None = None
46-
try:
47-
async with AsyncExitStack() as tmp_dir_stack, lock_project(
48-
request.app,
49-
project_uuid,
50-
ProjectStatus.EXPORTING,
51-
user_id,
52-
await get_user_fullname(request.app, user_id=user_id),
53-
):
54-
await retrieve_and_notify_project_locked_state(
55-
user_id, project_uuid, request.app
56-
)
45+
46+
@with_project_locked_notified_state(
47+
request.app,
48+
project_uuid=project_uuid,
49+
status=ProjectStatus.EXPORTING,
50+
user_id=user_id,
51+
user_name=await get_user_fullname(request.app, user_id=user_id),
52+
notify_users=True,
53+
)
54+
async def _() -> tuple[Callable[[], Coroutine[Any, Any, None]], Path]:
55+
# @GitHK what is this supposed to be doing??
56+
async with AsyncExitStack() as tmp_dir_stack:
5757
tmp_dir = await tmp_dir_stack.enter_async_context(AioTemporaryDirectory())
5858
file_to_download = await get_sds_archive_path(
5959
app=request.app,
@@ -68,14 +68,14 @@ async def export_project(request: web.Request):
6868
msg = f"Must provide a file to download, not {file_to_download!s}"
6969
raise SDSException(msg)
7070
# this allows to transfer deletion of the tmp dir responsibility
71-
delete_tmp_dir = tmp_dir_stack.pop_all().aclose
72-
finally:
73-
await retrieve_and_notify_project_locked_state(
74-
user_id, project_uuid, request.app
75-
)
71+
return tmp_dir_stack.pop_all().aclose, file_to_download
72+
73+
delete_tmp_dir_callable, file_to_download = await _()
7674

7775
headers = {"Content-Disposition": f'attachment; filename="{file_to_download.name}"'}
78-
assert delete_tmp_dir # nosec
76+
assert delete_tmp_dir_callable # nosec
7977
return CleanupFileResponse(
80-
remove_tmp_dir_cb=delete_tmp_dir, path=file_to_download, headers=headers
78+
remove_tmp_dir_cb=delete_tmp_dir_callable,
79+
path=file_to_download,
80+
headers=headers,
8181
)

services/web/server/src/simcore_service_webserver/projects/_crud_api_create.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import asyncio
22
import logging
33
from collections.abc import Coroutine
4-
from contextlib import AsyncExitStack
54
from typing import Any, TypeAlias
65

76
from aiohttp import web
@@ -164,17 +163,7 @@ async def _copy_files_from_source_project(
164163
!= ProjectTypeDB.TEMPLATE
165164
)
166165

167-
async with AsyncExitStack() as stack:
168-
if needs_lock_source_project:
169-
await stack.enter_async_context(
170-
projects_api.lock_with_notification(
171-
app,
172-
source_project["uuid"],
173-
ProjectStatus.CLONING,
174-
user_id,
175-
await get_user_fullname(app, user_id=user_id),
176-
)
177-
)
166+
async def _copy() -> None:
178167
starting_value = task_progress.percent
179168
async for long_running_task in copy_data_folders_from_project(
180169
app, source_project, new_project, nodes_map, user_id
@@ -191,6 +180,18 @@ async def _copy_files_from_source_project(
191180
if long_running_task.done():
192181
await long_running_task.result()
193182

183+
if needs_lock_source_project:
184+
await projects_api.with_project_locked_notified_state(
185+
app,
186+
project_uuid=source_project["uuid"],
187+
status=ProjectStatus.CLONING,
188+
user_id=user_id,
189+
user_name=await get_user_fullname(app, user_id=user_id),
190+
notify_users=True,
191+
)(_copy)()
192+
else:
193+
await _copy()
194+
194195

195196
async def _compose_project_data(
196197
app: web.Application,

services/web/server/src/simcore_service_webserver/projects/lock.py

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,46 @@
1-
import logging
2-
from collections.abc import AsyncIterator
3-
from contextlib import asynccontextmanager
1+
from collections.abc import Callable, Coroutine
2+
from functools import wraps
3+
from typing import Any, ParamSpec, TypeVar
44

55
from aiohttp import web
66
from models_library.projects import ProjectID
77
from models_library.projects_access import Owner
88
from models_library.projects_state import ProjectLocked, ProjectStatus
9-
from servicelib.project_lock import PROJECT_REDIS_LOCK_KEY
10-
from servicelib.project_lock import lock_project as common_lock_project
9+
from servicelib.project_lock import PROJECT_REDIS_LOCK_KEY, with_locked_project
1110

1211
from ..redis import get_redis_lock_manager_client, get_redis_lock_manager_client_sdk
1312
from ..users.api import FullNameDict
1413

15-
_logger = logging.getLogger(__name__)
14+
P = ParamSpec("P")
15+
R = TypeVar("R")
1616

1717

18-
@asynccontextmanager
19-
async def lock_project(
18+
def with_locked_project_from_app(
2019
app: web.Application,
20+
*,
2121
project_uuid: str | ProjectID,
2222
status: ProjectStatus,
2323
user_id: int,
2424
user_fullname: FullNameDict,
25-
) -> AsyncIterator[None]:
26-
"""Context manager to lock and unlock a project by user_id
25+
) -> Callable[
26+
[Callable[P, Coroutine[Any, Any, R]]], Callable[P, Coroutine[Any, Any, R]]
27+
]:
28+
def _decorator(
29+
func: Callable[P, Coroutine[Any, Any, R]],
30+
) -> Callable[P, Coroutine[Any, Any, R]]:
31+
@with_locked_project(
32+
get_redis_lock_manager_client_sdk(app),
33+
project_uuid=project_uuid,
34+
status=status,
35+
owner=Owner(user_id=user_id, **user_fullname),
36+
)
37+
@wraps(func)
38+
async def _wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
39+
return await func(*args, **kwargs)
2740

28-
Raises:
29-
ProjectLockError: if project is already locked
30-
"""
31-
async with common_lock_project(
32-
get_redis_lock_manager_client_sdk(app),
33-
project_uuid=project_uuid,
34-
status=status,
35-
owner=Owner(user_id=user_id, **user_fullname),
36-
):
37-
yield
41+
return _wrapper
42+
43+
return _decorator
3844

3945

4046
async def is_project_locked(

0 commit comments

Comments
 (0)