Skip to content

Commit b5d82e0

Browse files
authored
🎨 Fixes Deprecation Warning on redis (#5996)
1 parent 25fbe83 commit b5d82e0

File tree

8 files changed

+94
-33
lines changed

8 files changed

+94
-33
lines changed

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ async def redis_client(
6969
yield client
7070

7171
await client.flushall()
72-
await client.close(close_connection_pool=True)
72+
await client.aclose(close_connection_pool=True)
7373

7474

7575
@pytest.fixture()
@@ -86,7 +86,7 @@ async def redis_locks_client(
8686
yield client
8787

8888
await client.flushall()
89-
await client.close(close_connection_pool=True)
89+
await client.aclose(close_connection_pool=True)
9090

9191

9292
@tenacity.retry(
@@ -103,4 +103,4 @@ async def wait_till_redis_responsive(redis_url: URL | str) -> None:
103103
msg = f"{redis_url=} not available"
104104
raise ConnectionError(msg)
105105
finally:
106-
await client.close(close_connection_pool=True)
106+
await client.aclose(close_connection_pool=True)

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

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
from .rest_models import ErrorItemType, ErrorType, LogMessageType
1919
from .rest_responses import (
2020
create_data_response,
21-
create_error_response,
21+
create_http_error,
2222
is_enveloped_from_map,
2323
is_enveloped_from_text,
2424
wrap_as_envelope,
@@ -44,7 +44,7 @@ def error_middleware_factory(
4444
_is_prod: bool = is_production_environ()
4545

4646
def _process_and_raise_unexpected_error(request: web.BaseRequest, err: Exception):
47-
resp = create_error_response(
47+
http_error = create_http_error(
4848
err,
4949
"Unexpected Server error",
5050
web.HTTPInternalServerError,
@@ -58,11 +58,11 @@ def _process_and_raise_unexpected_error(request: web.BaseRequest, err: Exception
5858
request.remote,
5959
request.method,
6060
request.path,
61-
resp.status,
61+
http_error.status,
6262
exc_info=err,
6363
stack_info=True,
6464
)
65-
raise resp
65+
raise http_error
6666

6767
@web.middleware
6868
async def _middleware_handler(request: web.Request, handler: Handler):
@@ -115,22 +115,22 @@ async def _middleware_handler(request: web.Request, handler: Handler):
115115
raise
116116

117117
except NotImplementedError as err:
118-
error_response = create_error_response(
118+
http_error = create_http_error(
119119
err,
120120
f"{err}",
121121
web.HTTPNotImplemented,
122122
skip_internal_error_details=_is_prod,
123123
)
124-
raise error_response from err
124+
raise http_error from err
125125

126126
except asyncio.TimeoutError as err:
127-
error_response = create_error_response(
127+
http_error = create_http_error(
128128
err,
129129
f"{err}",
130130
web.HTTPGatewayTimeout,
131131
skip_internal_error_details=_is_prod,
132132
)
133-
raise error_response from err
133+
raise http_error from err
134134

135135
except Exception as err: # pylint: disable=broad-except
136136
_process_and_raise_unexpected_error(request, err)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,4 @@ class ErrorType:
2727
logs: list[LogMessageType] = field(default_factory=list)
2828
errors: list[ErrorItemType] = field(default_factory=list)
2929
status: int = 400
30-
message: str = "Unexpected client error"
30+
message: str = "Unexpected error"

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

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from servicelib.aiohttp.status import HTTP_200_OK
1414

1515
from ..mimetype_constants import MIMETYPE_APPLICATION_JSON
16+
from ..status_utils import get_code_description
1617
from .rest_models import ErrorItemType, ErrorType
1718

1819
_ENVELOPE_KEYS = ("data", "error")
@@ -65,18 +66,20 @@ def create_data_response(
6566

6667
response = web.json_response(payload, dumps=json_dumps, status=status)
6768
except (TypeError, ValueError) as err:
68-
response = create_error_response(
69-
[
70-
err,
71-
],
72-
str(err),
73-
web.HTTPInternalServerError,
74-
skip_internal_error_details=skip_internal_error_details,
69+
response = exception_to_response(
70+
create_http_error(
71+
[
72+
err,
73+
],
74+
str(err),
75+
web.HTTPInternalServerError,
76+
skip_internal_error_details=skip_internal_error_details,
77+
)
7578
)
7679
return response
7780

7881

79-
def create_error_response(
82+
def create_http_error(
8083
errors: list[Exception] | Exception,
8184
reason: str | None = None,
8285
http_error_cls: type[HTTPError] = web.HTTPInternalServerError,
@@ -94,18 +97,23 @@ def create_error_response(
9497
# TODO: guarantee no throw!
9598

9699
is_internal_error: bool = http_error_cls == web.HTTPInternalServerError
100+
default_message = reason or get_code_description(http_error_cls.status_code)
97101

98102
if is_internal_error and skip_internal_error_details:
99103
error = ErrorType(
100104
errors=[],
101105
status=http_error_cls.status_code,
106+
message=default_message,
102107
)
103108
else:
109+
items = [ErrorItemType.from_error(err) for err in errors]
104110
error = ErrorType(
105-
errors=[ErrorItemType.from_error(err) for err in errors],
111+
errors=items,
106112
status=http_error_cls.status_code,
113+
message=items[0].message if items else default_message,
107114
)
108115

116+
assert not http_error_cls.empty_body # nosec
109117
payload = wrap_as_envelope(error=asdict(error))
110118

111119
return http_error_cls(
@@ -115,6 +123,18 @@ def create_error_response(
115123
)
116124

117125

126+
def exception_to_response(exc: HTTPError) -> web.Response:
127+
# Returning web.HTTPException is deprecated so here we have a converter to a response
128+
# so it can be used as
129+
# SEE https://github.com/aio-libs/aiohttp/issues/2415
130+
return web.Response(
131+
status=exc.status,
132+
headers=exc.headers,
133+
reason=exc.reason,
134+
text=exc.text,
135+
)
136+
137+
118138
# Inverse map from code to HTTPException classes
119139
def _collect_http_exceptions(exception_cls: type[HTTPException] = HTTPException):
120140
def _pred(obj) -> bool:

packages/service-library/tests/aiohttp/test_rest_responses.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import itertools
66

77
import pytest
8+
from aiohttp import web
89
from aiohttp.web_exceptions import (
910
HTTPBadRequest,
1011
HTTPError,
@@ -14,10 +15,14 @@
1415
HTTPNotModified,
1516
HTTPOk,
1617
)
18+
from servicelib.aiohttp import status
1719
from servicelib.aiohttp.rest_responses import (
1820
_STATUS_CODE_TO_HTTP_ERRORS,
21+
create_http_error,
22+
exception_to_response,
1923
get_http_error,
2024
)
25+
from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON
2126

2227
#
2328

@@ -53,3 +58,29 @@ def test_collected_http_errors_map(status_code: int, http_error_cls: type[HTTPEr
5358

5459
assert http_error_cls != HTTPError
5560
assert issubclass(http_error_cls, HTTPError)
61+
62+
63+
@pytest.mark.parametrize("skip_details", [True, False])
64+
def tests_exception_to_response(skip_details: bool):
65+
exception = create_http_error(
66+
errors=[RuntimeError("foo")],
67+
reason="Something whent wrong",
68+
http_error_cls=web.HTTPInternalServerError,
69+
skip_internal_error_details=skip_details,
70+
)
71+
72+
# For now until deprecated SEE https://github.com/aio-libs/aiohttp/issues/2415
73+
assert isinstance(exception, Exception)
74+
assert isinstance(exception, web.Response)
75+
assert hasattr(exception, "__http_exception__")
76+
77+
# until they have exception.make_response(), we user
78+
response = exception_to_response(exception)
79+
assert isinstance(response, web.Response)
80+
assert not isinstance(response, Exception)
81+
assert not hasattr(response, "__http_exception__")
82+
83+
assert response.content_type == MIMETYPE_APPLICATION_JSON
84+
assert response.status == status.HTTP_500_INTERNAL_SERVER_ERROR
85+
assert response.text
86+
assert response.body

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

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@
1010
from models_library.utils.json_serialization import json_dumps
1111
from pydantic import BaseModel, Field, ValidationError, parse_obj_as
1212
from pydantic.types import NonNegativeInt
13-
from servicelib.aiohttp.rest_responses import create_error_response, get_http_error
13+
from servicelib.aiohttp.rest_responses import (
14+
create_http_error,
15+
exception_to_response,
16+
get_http_error,
17+
)
1418
from servicelib.common_headers import (
1519
UNDEFINED_DEFAULT_SIMCORE_USER_AGENT_VALUE,
1620
X_SIMCORE_USER_AGENT,
@@ -162,15 +166,21 @@ async def start_computation(request: web.Request) -> web.Response:
162166
return envelope_json_response(data, status_cls=web.HTTPCreated)
163167

164168
except DirectorServiceError as exc:
165-
return create_error_response(
166-
exc,
167-
reason=exc.reason,
168-
http_error_cls=get_http_error(exc.status) or web.HTTPServiceUnavailable,
169+
return exception_to_response(
170+
create_http_error(
171+
exc,
172+
reason=exc.reason,
173+
http_error_cls=get_http_error(exc.status) or web.HTTPServiceUnavailable,
174+
)
169175
)
170176
except UserDefaultWalletNotFoundError as exc:
171-
return create_error_response(exc, http_error_cls=web.HTTPNotFound)
177+
return exception_to_response(
178+
create_http_error(exc, http_error_cls=web.HTTPNotFound)
179+
)
172180
except WalletNotEnoughCreditsError as exc:
173-
return create_error_response(exc, http_error_cls=web.HTTPPaymentRequired)
181+
return exception_to_response(
182+
create_http_error(exc, http_error_cls=web.HTTPPaymentRequired)
183+
)
174184

175185

176186
@routes.post(f"/{VTAG}/computations/{{project_id}}:stop", name="stop_computation")
@@ -203,7 +213,7 @@ async def stop_computation(request: web.Request) -> web.Response:
203213
raise web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON)
204214

205215
except DirectorServiceError as exc:
206-
return create_error_response(
216+
return create_http_error(
207217
exc,
208218
reason=exc.reason,
209219
http_error_cls=get_http_error(exc.status) or web.HTTPServiceUnavailable,
@@ -252,10 +262,10 @@ async def get_computation(request: web.Request) -> web.Response:
252262
dumps=json_dumps,
253263
)
254264
except DirectorServiceError as exc:
255-
return create_error_response(
265+
return create_http_error(
256266
exc,
257267
reason=exc.reason,
258268
http_error_cls=get_http_error(exc.status) or web.HTTPServiceUnavailable,
259269
)
260270
except ValidationError as exc:
261-
return create_error_response(exc, http_error_cls=web.HTTPInternalServerError)
271+
return create_http_error(exc, http_error_cls=web.HTTPInternalServerError)

services/web/server/tests/integration/01/test_garbage_collection.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ async def __delete_all_redis_keys__(redis_settings: RedisSettings):
8787
decode_responses=True,
8888
)
8989
await client.flushall()
90-
await client.close(close_connection_pool=True)
90+
await client.aclose(close_connection_pool=True)
9191

9292

9393
@pytest.fixture(scope="session")

services/web/server/tests/unit/with_dbs/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -579,7 +579,7 @@ async def redis_locks_client(
579579
yield client
580580

581581
await client.flushall()
582-
await client.close(close_connection_pool=True)
582+
await client.aclose(close_connection_pool=True)
583583

584584

585585
# SOCKETS FIXTURES --------------------------------------------------------

0 commit comments

Comments
 (0)