Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Request handler normalization does not play well with pydantic's @validate_arguments decorator #365

Open
aldem opened this issue May 26, 2023 · 1 comment

Comments

@aldem
Copy link

aldem commented May 26, 2023

I tried to use @validate_arguments in my async handler but this attempt failed.

My code:

from typing import Annotated
from pydantic import Field, validate_arguments

from blacksheep import Application, text

app = Application()

@app.route('/test')
@validate_arguments
async def something(i: Annotated[int, Field(ge=1, le=10)] = 1):
    return text(f"i={i}\n")

I use Python 3.9.2, pydantic == 1.10.7, blacksheep == 1.2.14

Once run with uvicorn, request attempt raises an exception:

Unhandled exception - "GET /test"
Traceback (most recent call last):
  File "blacksheep/baseapp.pyx", line 84, in blacksheep.baseapp.BaseApplication.handle
  File "/home/developer/src/python/pvenv/lib/python3.9/site-packages/blacksheep/server/normalization.py", line 545, in handler
    return ensure_response(response)
  File "/home/developer/src/python/pvenv/lib/python3.9/site-packages/blacksheep/server/normalization.py", line 132, in ensure_response
    return responses.json(result)
  File "/home/developer/src/python/pvenv/lib/python3.9/site-packages/blacksheep/server/responses.py", line 198, in json
    json_plugin.dumps(data).encode("utf8"),
  File "/home/developer/src/python/pvenv/lib/python3.9/site-packages/blacksheep/plugins/json.py", line 35, in dumps
    return self._dumps(obj)
  File "/home/developer/src/python/pvenv/lib/python3.9/site-packages/blacksheep/plugins/json.py", line 8, in default_json_dumps
    return dumps(obj, separators=(",", ":"))
  File "/home/developer/src/python/pvenv/lib/python3.9/site-packages/essentials/json.py", line 54, in dumps
    return json.dumps(
  File "/usr/lib/python3.9/json/__init__.py", line 234, in dumps
    return cls(
  File "/usr/lib/python3.9/json/encoder.py", line 199, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/usr/lib/python3.9/json/encoder.py", line 257, in iterencode
    return _iterencode(o, 0)
  File "/home/developer/src/python/pvenv/lib/python3.9/site-packages/essentials/json.py", line 18, in default
    return json.JSONEncoder.default(self, obj)
  File "/usr/lib/python3.9/json/encoder.py", line 179, in default
    raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type coroutine is not JSON serializable
/home/developer/src/python/pvenv/lib/python3.9/site-packages/blacksheep/server/application.py:790: RuntimeWarning: coroutine 'something' was never awaited
  response = await self.handle(request)
RuntimeWarning: Enable tracemalloc to get the object allocation traceback

Without decorator everything works as it should. I was able to fix this by modifying _get_async_wrapper_for_output function in normalization.py, though I am not sure if my solution does it right and could handle all cases - probably normalization code needs to be updated but I didn't look deep into:

def _get_async_wrapper_for_output(
    method: Callable[[Request], Any],
) -> Callable[[Request], Awaitable[Response]]:
    @wraps(method)
    async def handler(request: Request) -> Response:
        response = await method(request)
        # Handle consequences of @validate_arguments decorator applied to async code
        if inspect.isawaitable(response):
            response = await response
        return ensure_response(response)

    return handler
@RobertoPrevato
Copy link
Member

Hi @aldem
Thank You for the heads up. I will take a look at this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants