Skip to content

Commit 3f4cfb9

Browse files
committed
Merge branch 'master' into improve-celery-task-error-messages-and-improve-taskid-name
2 parents 8da86df + 407f10e commit 3f4cfb9

File tree

200 files changed

+2539
-1863
lines changed

Some content is hidden

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

200 files changed

+2539
-1863
lines changed

.env-devel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ CLUSTERS_KEEPER_COMPUTATIONAL_BACKEND_DEFAULT_CLUSTER_AUTH='{"type":"tls","tls_c
5656
CLUSTERS_KEEPER_COMPUTATIONAL_BACKEND_DOCKER_IMAGE_TAG=master-github-latest
5757
CLUSTERS_KEEPER_DASK_NPROCS=1
5858
CLUSTERS_KEEPER_DASK_NTHREADS=0
59+
CLUSTERS_KEEPER_DASK_NTHREADS_MULTIPLIER=1
5960
CLUSTERS_KEEPER_DASK_WORKER_SATURATION=inf
6061
CLUSTERS_KEEPER_EC2_ACCESS=null
6162
CLUSTERS_KEEPER_SSM_ACCESS=null

.github/instructions/python.instructions.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ Ensure compatibility with the following library versions:
3535

3636
* Use `f-string` formatting for all string interpolation except for logging message strings.
3737
* Use **relative imports** within the same package/module.
38+
- For imports within the same repository/project, always use relative imports (e.g., `from ..constants import APP_SETTINGS_KEY` instead of `from simcore_service_webserver.constants import APP_SETTINGS_KEY`)
39+
- Use absolute imports only for external libraries and packages
3840
* Place **all imports at the top** of the file.
3941
* Document functions when the code is not self-explanatory or if asked explicitly.
4042

@@ -44,6 +46,18 @@ Ensure compatibility with the following library versions:
4446
* Prefer `json_dumps` / `json_loads` from `common_library.json_serialization` instead of the built-in `json.dumps` / `json.loads`.
4547
* When using Pydantic models, prefer methods like `model.model_dump_json()` for serialization.
4648

47-
### 7. **Running tests**
49+
### 7. **aiohttp Framework**
50+
51+
* **Application Keys**: Always use `web.AppKey` for type-safe application storage instead of string keys
52+
- Define keys with specific types: `APP_MY_KEY: Final = web.AppKey("APP_MY_KEY", MySpecificType)`
53+
- Use precise types instead of generic `object` when the actual type is known
54+
- Example: `APP_SETTINGS_KEY: Final = web.AppKey("APP_SETTINGS_KEY", ApplicationSettings)`
55+
- Store and retrieve: `app[APP_MY_KEY] = value` and `data = app[APP_MY_KEY]`
56+
* **Request Keys**: Use `web.AppKey` for request storage as well for consistency and type safety
57+
* **Middleware**: Follow the repository's middleware patterns for cross-cutting concerns
58+
* **Error Handling**: Use the established exception handling decorators and patterns
59+
* **Route Definitions**: Use `web.RouteTableDef()` and organize routes logically within modules
60+
61+
### 8. **Running tests**
4862
* Use `--keep-docker-up` flag when testing to keep docker containers up between sessions.
4963
* Always activate the python virtual environment before running pytest.

packages/celery-library/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ tests: ## runs unit tests
2727
--durations=10 \
2828
--exitfirst \
2929
--failed-first \
30+
--keep-docker-up \
3031
--pdb \
3132
-vv \
3233
$(CURDIR)/tests
@@ -41,6 +42,7 @@ tests-ci: ## runs unit tests
4142
--cov-report=term-missing \
4243
--cov-report=xml \
4344
--junitxml=junit.xml -o junit_family=legacy \
45+
--keep-docker-up \
4446
--cov=celery_library \
4547
--durations=10 \
4648
--log-date-format="%Y-%m-%d %H:%M:%S" \

packages/pytest-simcore/src/pytest_simcore/redis_service.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
import logging
66
from collections.abc import AsyncIterator
7-
from datetime import timedelta
87

98
import pytest
109
import tenacity
@@ -116,14 +115,6 @@ async def wait_till_redis_responsive(redis_url: URL | str) -> None:
116115
await client.aclose(close_connection_pool=True)
117116

118117

119-
@pytest.fixture
120-
def mock_redis_socket_timeout(mocker: MockerFixture) -> None:
121-
# lowered to allow CI to properly shutdown RedisClientSDK instances
122-
mocker.patch(
123-
"servicelib.redis._client.DEFAULT_SOCKET_TIMEOUT", timedelta(seconds=0.25)
124-
)
125-
126-
127118
@pytest.fixture
128119
async def use_in_memory_redis(mocker: MockerFixture) -> RedisSettings:
129120
mocker.patch("redis.asyncio.from_url", FakeAsyncRedis)

packages/service-library/src/servicelib/aiohttp/aiopg_utils.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
1-
""" Holderplace for random helpers using aiopg
1+
"""Holderplace for random helpers using aiopg
22
3-
- Drop here functions/constants that at that time does
4-
not fit in any of the setups. Then, they can be moved and
5-
refactor when new abstractions are used in place.
3+
- Drop here functions/constants that at that time does
4+
not fit in any of the setups. Then, they can be moved and
5+
refactor when new abstractions are used in place.
66
7-
- aiopg is used as a client sdk to interact asynchronously with postgres service
7+
- aiopg is used as a client sdk to interact asynchronously with postgres service
88
9-
SEE for aiopg: https://aiopg.readthedocs.io/en/stable/sa.html
10-
SEE for underlying psycopg: http://initd.org/psycopg/docs/module.html
11-
SEE for extra keywords: https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS
9+
SEE for aiopg: https://aiopg.readthedocs.io/en/stable/sa.html
10+
SEE for underlying psycopg: http://initd.org/psycopg/docs/module.html
11+
SEE for extra keywords: https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS
1212
"""
1313

1414
# TODO: Towards implementing https://github.com/ITISFoundation/osparc-simcore/issues/1195
1515
# TODO: deprecate this module. Move utils into retry_policies, simcore_postgres_database.utils_aiopg
1616

1717
import logging
18+
from typing import Final
1819

1920
import sqlalchemy as sa
2021
from aiohttp import web
@@ -31,6 +32,8 @@
3132

3233
log = logging.getLogger(__name__)
3334

35+
APP_AIOPG_ENGINE_KEY: Final = web.AppKey("APP_AIOPG_ENGINE_KEY", Engine)
36+
3437

3538
async def raise_if_not_responsive(engine: Engine):
3639
async with engine.acquire() as conn:

packages/service-library/src/servicelib/aiohttp/application_keys.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
""" Namespace to keep all application storage keys
1+
"""Namespace to keep all application storage keys
22
33
Unique keys to identify stored data
44
Naming convention accounts for the storage scope: application, request, response, configuration and/or resources
@@ -8,22 +8,25 @@
88
99
See https://aiohttp.readthedocs.io/en/stable/web_advanced.html#data-sharing-aka-no-singletons-please
1010
"""
11+
1112
from typing import Final
1213

13-
# REQUIREMENTS:
14-
# - guarantees all keys are unique
15-
# - one place for all common keys
16-
# - hierarchical classification
14+
from aiohttp import ClientSession, web
15+
16+
# APPLICATION's CONTEXT KEYS
17+
18+
# NOTE: use these keys to store/retrieve data from aiohttp.web.Application
19+
# SEE https://docs.aiohttp.org/en/stable/web_quickstart.html#aiohttp-web-app-key
1720

1821
#
1922
# web.Application keys, i.e. app[APP_*_KEY]
2023
#
21-
APP_CONFIG_KEY: Final[str] = f"{__name__ }.config"
22-
APP_SETTINGS_KEY: Final[str] = f"{__name__ }.settings"
24+
APP_CONFIG_KEY = web.AppKey("APP_CONFIG_KEY", dict[str, object])
2325

2426
APP_AIOPG_ENGINE_KEY: Final[str] = f"{__name__ }.aiopg_engine"
2527

26-
APP_CLIENT_SESSION_KEY: Final[str] = f"{__name__ }.session"
28+
APP_CLIENT_SESSION_KEY: web.AppKey[ClientSession] = web.AppKey("APP_CLIENT_SESSION_KEY")
29+
2730

2831
APP_FIRE_AND_FORGET_TASKS_KEY: Final[str] = f"{__name__}.tasks"
2932

packages/service-library/src/servicelib/aiohttp/application_setup.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@
1616
TypedDict,
1717
)
1818

19-
from .application_keys import APP_CONFIG_KEY, APP_SETTINGS_KEY
19+
from .application_keys import APP_CONFIG_KEY
2020

2121
_logger = logging.getLogger(__name__)
2222

23-
APP_SETUP_COMPLETED_KEY: Final[str] = f"{__name__ }.setup"
23+
APP_SETUP_COMPLETED_KEY: Final[web.AppKey] = web.AppKey("setup_completed", list[str])
2424

2525

2626
class _SetupFunc(Protocol):
@@ -115,13 +115,14 @@ def _get_app_settings_and_field_name(
115115
arg_settings_name: str | None,
116116
setup_func_name: str,
117117
logger: logging.Logger,
118+
app_settings_key: web.AppKey,
118119
) -> tuple[_ApplicationSettings | None, str | None]:
119-
app_settings: _ApplicationSettings | None = app.get(APP_SETTINGS_KEY)
120+
app_settings: _ApplicationSettings | None = app.get(app_settings_key)
120121
settings_field_name = arg_settings_name
121122

122123
if app_settings:
123124
if not settings_field_name:
124-
# FIXME: hard-coded WEBSERVER_ temporary
125+
# NOTE: hard-coded WEBSERVER_ temporary
125126
settings_field_name = f"WEBSERVER_{arg_module_name.split('.')[-1].upper()}"
126127

127128
logger.debug("Checking addon's %s ", f"{settings_field_name=}")
@@ -246,6 +247,7 @@ def app_module_setup(
246247
module_name: str,
247248
category: ModuleCategory,
248249
*,
250+
app_settings_key: web.AppKey,
249251
settings_name: str | None = None,
250252
depends: list[str] | None = None,
251253
logger: logging.Logger = _logger,
@@ -336,6 +338,7 @@ def _wrapper(app: web.Application, *args, **kargs) -> bool:
336338
settings_name,
337339
setup_func.__name__,
338340
logger,
341+
app_settings_key,
339342
)
340343

341344
if (

packages/service-library/src/servicelib/aiohttp/client_session.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
from collections.abc import AsyncGenerator
2-
from typing import cast
32

43
from aiohttp import ClientSession, ClientTimeout, web
54
from common_library.json_serialization import json_dumps
@@ -41,10 +40,11 @@ async def persistent_client_session(app: web.Application) -> AsyncGenerator[None
4140
def get_client_session(app: web.Application) -> ClientSession:
4241
"""Refers to the one-and-only client in the app"""
4342
assert APP_CLIENT_SESSION_KEY in app # nosec
44-
return cast(ClientSession, app[APP_CLIENT_SESSION_KEY])
43+
return app[APP_CLIENT_SESSION_KEY]
4544

4645

4746
__all__: tuple[str, ...] = (
47+
"APP_CLIENT_SESSION_KEY",
4848
"get_client_session",
4949
"persistent_client_session",
5050
)

packages/service-library/src/servicelib/aiohttp/docker_utils.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import logging
2+
from typing import Final
23

4+
import aiodocker
35
import aiohttp
6+
from aiohttp import web
47
from models_library.docker import DockerGenericTag
58
from pydantic import TypeAdapter, ValidationError
69
from settings_library.docker_registry import RegistrySettings
@@ -18,6 +21,8 @@
1821

1922
_logger = logging.getLogger(__name__)
2023

24+
APP_DOCKER_ENGINE_KEY: Final = web.AppKey("APP_DOCKER_ENGINE_KEY", aiodocker.Docker)
25+
2126

2227
async def retrieve_image_layer_information(
2328
image: DockerGenericTag, registry_settings: RegistrySettings

packages/service-library/src/servicelib/aiohttp/long_running_tasks/server.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,17 @@
66
running task.
77
"""
88

9+
from typing import Final
10+
11+
from aiohttp import web
12+
913
from ._manager import get_long_running_manager
1014
from ._server import setup, start_long_running_task
1115

16+
APP_LONG_RUNNING_TASKS_KEY: Final = web.AppKey(
17+
"APP_LONG_RUNNING_TASKS_KEY", dict[str, object]
18+
)
19+
1220
__all__: tuple[str, ...] = (
1321
"get_long_running_manager",
1422
"setup",

0 commit comments

Comments
 (0)