Skip to content

Commit ba7467a

Browse files
committed
cleanup
1 parent 0fa43f5 commit ba7467a

File tree

3 files changed

+64
-18
lines changed

3 files changed

+64
-18
lines changed

services/web/server/src/simcore_service_webserver/exception_handling.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
from .exception_handling_base import exception_handling_decorator
1+
from .exception_handling_base import ExceptionHandlersMap, exception_handling_decorator
22
from .exception_handling_factory import (
33
ExceptionToHttpErrorMap,
44
HttpErrorInfo,
55
to_exceptions_handlers_map,
66
)
77

88
__all__: tuple[str, ...] = (
9+
"ExceptionHandlersMap",
910
"ExceptionToHttpErrorMap",
1011
"HttpErrorInfo",
1112
"exception_handling_decorator",

services/web/server/src/simcore_service_webserver/exception_handling_base.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,8 @@ def _sort_exceptions_by_specificity(
4444

4545

4646
class ExceptionHandlingContextManager(AbstractAsyncContextManager):
47-
"""
48-
49-
Essentially a dynamic try-except context manager to
50-
handle exceptions raised by a aiohttp handler, i.e. roughly
47+
"""Essentially a dynamic try-except context manager to
48+
handle exceptions raised by a web handler, i.e.
5149
```
5250
try:
5351
@@ -121,7 +119,10 @@ def get_response(self):
121119
def exception_handling_decorator(
122120
exception_handlers_map: dict[type[BaseException], AiohttpExceptionHandler]
123121
):
124-
""" """
122+
"""Creates a decorator to handle all registered exceptions raised in a given route handler
123+
124+
SEE usage example in test_exception_handling
125+
"""
125126

126127
def _decorator(handler: WebHandler):
127128
@functools.wraps(handler)
@@ -145,6 +146,9 @@ async def _wrapper(request: web.Request) -> web.StreamResponse:
145146
def exception_handling_middleware(
146147
exception_handlers_map: dict[type[BaseException], AiohttpExceptionHandler]
147148
) -> WebMiddleware:
149+
"""
150+
Creates a middleware to handle all registered exceptions raised in any app's route
151+
"""
148152
_handle_excs = exception_handling_decorator(
149153
exception_handlers_map=exception_handlers_map
150154
)

services/web/server/tests/unit/isolated/test_exception_handling.py

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,62 @@
77

88
from collections.abc import Callable
99

10+
import pytest
1011
from aiohttp import web
12+
from models_library.rest_error import ErrorGet
1113
from servicelib.aiohttp import status
12-
from simcore_service_webserver.exception_handling import exception_handling_decorator
14+
from simcore_service_webserver.exception_handling import (
15+
ExceptionHandlersMap,
16+
HttpErrorInfo,
17+
exception_handling_decorator,
18+
to_exceptions_handlers_map,
19+
)
20+
21+
22+
@pytest.fixture
23+
def exception_handlers_map(build_method: str) -> ExceptionHandlersMap:
24+
"""
25+
Two different ways to build the exception_handlers_map
26+
"""
27+
exception_handlers_map: ExceptionHandlersMap = {}
28+
29+
if build_method == "custom":
30+
31+
async def _value_error_as_422(
32+
request: web.Request, exception: BaseException
33+
) -> web.Response:
34+
# custom exception handler
35+
return web.json_response(
36+
reason=f"{build_method=}", status=status.HTTP_422_UNPROCESSABLE_ENTITY
37+
)
38+
39+
exception_handlers_map = {
40+
ValueError: _value_error_as_422,
41+
}
42+
43+
elif build_method == "http_map":
44+
exception_handlers_map = to_exceptions_handlers_map(
45+
{
46+
ValueError: HttpErrorInfo(
47+
status.HTTP_422_UNPROCESSABLE_ENTITY, f"{build_method=}"
48+
)
49+
}
50+
)
51+
else:
52+
pytest.fail(f"Undefined {build_method=}")
1353

54+
return exception_handlers_map
1455

15-
async def test_handling_exceptions_decorating_a_route(aiohttp_client: Callable):
1656

17-
# custom exception handler
18-
async def _value_error_as_422(
19-
request: web.Request, exception: BaseException
20-
) -> web.Response:
21-
return web.json_response(status=status.HTTP_422_UNPROCESSABLE_ENTITY)
57+
@pytest.mark.parametrize("build_method", ["custom", "http_map"])
58+
async def test_handling_exceptions_decorating_a_route(
59+
aiohttp_client: Callable,
60+
exception_handlers_map: ExceptionHandlersMap,
61+
build_method: str,
62+
):
2263

2364
# 1. create decorator
24-
exc_handling = exception_handling_decorator(
25-
exception_handlers_map={
26-
ValueError: _value_error_as_422,
27-
}
28-
)
65+
exc_handling = exception_handling_decorator(exception_handlers_map)
2966

3067
# adding new routes
3168
routes = web.RouteTableDef()
@@ -61,6 +98,10 @@ async def _handler(request: web.Request):
6198
# handled non-HTTPException exception
6299
resp = await client.get("/ValueError")
63100
assert resp.status == status.HTTP_422_UNPROCESSABLE_ENTITY
101+
if build_method == "http_map":
102+
body = await resp.json()
103+
error = ErrorGet.model_validate(body["error"])
104+
assert error.message == f"{build_method=}"
64105

65106
# undhandled non-HTTPException
66107
resp = await client.get("/IndexError")

0 commit comments

Comments
 (0)