Skip to content

Commit dc9dc44

Browse files
authored
♻️Mypy: webserver2 (#6200)
1 parent 7a98157 commit dc9dc44

Some content is hidden

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

54 files changed

+346
-198
lines changed

.github/workflows/ci-testing-deploy.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,6 @@ jobs:
332332
run: ./ci/github/unit-testing/webserver.bash install
333333
- name: typecheck
334334
run: ./ci/github/unit-testing/webserver.bash typecheck
335-
continue-on-error: true
336335
- name: test isolated
337336
if: always()
338337
run: ./ci/github/unit-testing/webserver.bash test_isolated

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
class WalletGet(OutputSchema):
1515
wallet_id: WalletID
16-
name: str
16+
name: IDStr
1717
description: str | None
1818
owner: GroupID
1919
thumbnail: str | None

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

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import re
22
from enum import Enum
3-
from typing import TypeAlias
3+
from typing import Final, TypeAlias
44

55
from pydantic import (
66
ConstrainedDecimal,
@@ -83,11 +83,32 @@ class UUIDStr(ConstrainedStr):
8383

8484
# non-empty bounded string used as identifier
8585
# e.g. "123" or "name_123" or "fa327c73-52d8-462a-9267-84eeaf0f90e3" but NOT ""
86+
_ELLIPSIS_CHAR: Final[str] = "..."
87+
88+
8689
class IDStr(ConstrainedStr):
8790
strip_whitespace = True
8891
min_length = 1
8992
max_length = 100
9093

94+
@staticmethod
95+
def concatenate(*args: "IDStr", link_char: str = " ") -> "IDStr":
96+
result = link_char.join(args).strip()
97+
assert IDStr.min_length # nosec
98+
assert IDStr.max_length # nosec
99+
if len(result) > IDStr.max_length:
100+
if IDStr.max_length > len(_ELLIPSIS_CHAR):
101+
result = (
102+
result[: IDStr.max_length - len(_ELLIPSIS_CHAR)].rstrip()
103+
+ _ELLIPSIS_CHAR
104+
)
105+
else:
106+
result = _ELLIPSIS_CHAR[0] * IDStr.max_length
107+
if len(result) < IDStr.min_length:
108+
msg = f"IDStr.concatenate: result is too short: {result}"
109+
raise ValueError(msg)
110+
return IDStr(result)
111+
91112

92113
class ShortTruncatedStr(ConstrainedStr):
93114
# NOTE: Use to input e.g. titles or display names

packages/service-library/src/servicelib/redis_utils.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import logging
44
from collections.abc import Awaitable, Callable
55
from datetime import timedelta
6+
from typing import Any
67

78
import arrow
89

@@ -100,3 +101,10 @@ def start_exclusive_periodic_task(
100101
usr_tsk_task_name=task_name,
101102
**kwargs,
102103
)
104+
105+
106+
async def handle_redis_returns_union_types(result: Any | Awaitable[Any]) -> Any:
107+
"""Used to handle mypy issues with redis 5.x return types"""
108+
if isinstance(result, Awaitable):
109+
return await result
110+
return result

services/web/server/src/simcore_service_webserver/catalog/_api.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import logging
22
from collections.abc import Iterator
3-
from typing import Any
3+
from typing import Any, cast
44

55
from aiohttp import web
66
from aiohttp.web import Request
@@ -319,8 +319,11 @@ async def get_service_output(
319319
service = await client.get_service(
320320
ctx.app, ctx.user_id, service_key, service_version, ctx.product_name
321321
)
322-
return await ServiceOutputGetFactory.from_catalog_service_api_model(
323-
service=service, output_key=output_key
322+
return cast( # mypy -> aiocache is not typed.
323+
ServiceOutputGet,
324+
await ServiceOutputGetFactory.from_catalog_service_api_model(
325+
service=service, output_key=output_key
326+
),
324327
)
325328

326329

services/web/server/src/simcore_service_webserver/catalog/_models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from dataclasses import dataclass
44
from typing import Any, Final
55

6-
from aiocache import cached
6+
from aiocache import cached # type: ignore[import-untyped]
77
from models_library.api_schemas_webserver.catalog import (
88
ServiceInputGet,
99
ServiceInputKey,

services/web/server/src/simcore_service_webserver/db/plugin.py

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

55
import logging
66
from collections.abc import AsyncIterator
7-
from typing import Any
7+
from typing import Any, cast
88

99
from aiohttp import web
1010
from aiopg.sa import Engine, create_engine
@@ -31,7 +31,7 @@
3131
async def _ensure_pg_ready(settings: PostgresSettings) -> Engine:
3232

3333
_logger.info("Connecting to postgres with %s", f"{settings=}")
34-
engine = await create_engine(
34+
engine: Engine = await create_engine(
3535
settings.dsn,
3636
application_name=settings.POSTGRES_CLIENT_NAME,
3737
minsize=settings.POSTGRES_MINSIZE,
@@ -91,7 +91,7 @@ def get_engine_state(app: web.Application) -> dict[str, Any]:
9191

9292

9393
def get_database_engine(app: web.Application) -> Engine:
94-
return app[APP_DB_ENGINE_KEY]
94+
return cast(Engine, app[APP_DB_ENGINE_KEY])
9595

9696

9797
@app_module_setup(

services/web/server/src/simcore_service_webserver/dynamic_scheduler/api.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
NodeGetIdle,
1414
NodeGetUnknown,
1515
)
16+
from models_library.basic_types import IDStr
1617
from models_library.progress_bar import ProgressReport
1718
from models_library.projects import ProjectID
1819
from models_library.projects_nodes_io import NodeID
@@ -108,7 +109,7 @@ async def stop_dynamic_services_in_project(
108109
user_id,
109110
project_id,
110111
),
111-
description="stopping services",
112+
description=IDStr("stopping services"),
112113
)
113114
)
114115

@@ -123,7 +124,7 @@ async def stop_dynamic_services_in_project(
123124
save_state=save_state,
124125
),
125126
progress=progress_bar.sub_progress(
126-
1, description=f"{service.node_uuid}"
127+
1, description=IDStr(f"{service.node_uuid}")
127128
),
128129
)
129130
for service in running_dynamic_services

services/web/server/src/simcore_service_webserver/email/plugin.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@
55
MIME: Multipurpose Internet Mail Extensions
66
77
"""
8+
89
import logging
910

1011
import aiohttp_jinja2
11-
import jinja_app_loader
12+
import jinja_app_loader # type: ignore[import-untyped]
1213
from aiohttp import web
1314
from servicelib.aiohttp.application_setup import ModuleCategory, app_module_setup
1415

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ async def export_project(request: web.Request):
7575
)
7676

7777
headers = {"Content-Disposition": f'attachment; filename="{file_to_download.name}"'}
78-
78+
assert delete_tmp_dir # nosec
7979
return CleanupFileResponse(
8080
remove_tmp_dir_cb=delete_tmp_dir, path=file_to_download, headers=headers
8181
)

services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
FolderGetPage,
99
PutFolderBodyParams,
1010
)
11+
from models_library.basic_types import IDStr
1112
from models_library.folders import FolderID
1213
from models_library.rest_ordering import OrderBy, OrderDirection
1314
from models_library.rest_pagination import Page, PageQueryParameters
@@ -70,8 +71,9 @@ class FoldersPathParams(StrictRequestParams):
7071

7172

7273
class FolderListWithJsonStrQueryParams(PageQueryParameters):
73-
order_by: Json[OrderBy] = Field( # pylint: disable=unsubscriptable-object
74-
default=OrderBy(field="modified", direction=OrderDirection.DESC),
74+
# pylint: disable=unsubscriptable-object
75+
order_by: Json[OrderBy] = Field( # type: ignore[type-arg]
76+
default=OrderBy(field=IDStr("modified"), direction=OrderDirection.DESC),
7577
description="Order by field (modified_at|name|description) and direction (asc|desc). The default sorting order is ascending.",
7678
example='{"field": "name", "direction": "desc"}',
7779
alias="order_by",
@@ -89,7 +91,8 @@ def validate_order_by_field(cls, v):
8991
"name",
9092
"description",
9193
}:
92-
raise ValueError(f"We do not support ordering by provided field {v.field}")
94+
msg = f"We do not support ordering by provided field {v.field}"
95+
raise ValueError(msg)
9396
if v.field == "modified_at":
9497
v.field = "modified"
9598
return v

services/web/server/src/simcore_service_webserver/groups/_db.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,16 @@ async def _get_user_group(
5151
group = await result.fetchone()
5252
if not group:
5353
raise GroupNotFoundError(gid)
54+
assert isinstance(group, RowProxy) # nosec
5455
return group
5556

5657

5758
async def get_user_from_email(conn: SAConnection, email: str) -> RowProxy:
5859
result = await conn.execute(sa.select(users).where(users.c.email == email))
59-
user: RowProxy = await result.fetchone()
60+
user = await result.fetchone()
6061
if not user:
6162
raise UserNotFoundError(email=email)
63+
assert isinstance(user, RowProxy) # nosec
6264
return user
6365

6466

0 commit comments

Comments
 (0)