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

Documentation: Examples using dict[str, UploadFile] don't work as advertised #2940

Open
4 tasks
guacs opened this issue Jan 3, 2024 · 2 comments
Open
4 tasks
Labels
Documentation 📚 This is related to documentation

Comments

@guacs
Copy link
Member

guacs commented Jan 3, 2024

Description

A msgpsec validation error is raised when annotating as data: dict[str, UploadFile]. This is because what we actually give msgspec is list of UploadFiles, but they have to converted into a dictionary before that. Also, I wasn't able to find any tests for these (maybe I missed them) which may mean this has never worked :D

URL to code causing the issue

No response

MCVE

from __future__ import annotations

from litestar import post
from litestar.datastructures import UploadFile
from litestar.enums import RequestEncodingType
from litestar.params import Body
from litestar.status_codes import HTTP_201_CREATED
from litestar.testing.helpers import create_test_client


@post(path="/")
async def upload_files_object(
    data: dict[str, UploadFile] = Body(media_type=RequestEncodingType.MULTI_PART)
) -> list[str]:
    return list(data.keys())


with create_test_client([upload_files_object]) as client:
    files = [("files", ("filename", b"1")), ("files", ("another_filename", b"1"))]
    response = client.post("/", files=files)

    assert response.status_code == HTTP_201_CREATED
    assert response.json() == ["filename", "another_filename"]

Steps to reproduce

1. Run the MCVE

Screenshots

No response

Logs

Traceback (most recent call last):
  File "/home/guacs/open-source/litestar/litestar/_signature/model.py", line 93, in _deserializer
    return default_deserializer(target_type, value)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/guacs/open-source/litestar/litestar/serialization/msgspec_hooks.py", line 118, in default_deserializer
    raise TypeError(f"Unsupported type: {type(value)!r}")
TypeError: Unsupported type: <class 'list'>

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/guacs/open-source/litestar/litestar/_signature/model.py", line 189, in parse_values_from_connection_kwargs
    return convert(kwargs, cls, strict=False, dec_hook=deserializer, str_keys=True).to_dict()
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
msgspec.ValidationError: Unsupported type: <class 'list'> - at `$.data[...]`

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/guacs/open-source/litestar/litestar/middleware/exceptions/middleware.py", line 191, in __call__
    await self.app(scope, receive, send)
  File "/home/guacs/open-source/litestar/litestar/routes/http.py", line 82, in handle
    response = await self._get_response_for_request(
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/guacs/open-source/litestar/litestar/routes/http.py", line 134, in _get_response_for_request
    return await self._call_handler_function(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/guacs/open-source/litestar/litestar/routes/http.py", line 154, in _call_handler_function
    response_data, cleanup_group = await self._get_response_data(
                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/guacs/open-source/litestar/litestar/routes/http.py", line 193, in _get_response_data
    parsed_kwargs = route_handler.signature_model.parse_values_from_connection_kwargs(
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/guacs/open-source/litestar/litestar/_signature/model.py", line 202, in parse_values_from_connection_kwargs
    raise cls._create_exception(messages=messages, connection=connection) from e
litestar.exceptions.http_exceptions.ValidationException: 400: Validation failed for POST http://testserver.local/
Traceback (most recent call last):
  File "/home/guacs/open-source/litestar/litestar/_signature/model.py", line 93, in _deserializer
    return default_deserializer(target_type, value)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/guacs/open-source/litestar/litestar/serialization/msgspec_hooks.py", line 118, in default_deserializer
    raise TypeError(f"Unsupported type: {type(value)!r}")
TypeError: Unsupported type: <class 'list'>

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/guacs/open-source/litestar/litestar/_signature/model.py", line 189, in parse_values_from_connection_kwargs
    return convert(kwargs, cls, strict=False, dec_hook=deserializer, str_keys=True).to_dict()
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
msgspec.ValidationError: Unsupported type: <class 'list'> - at `$.data[...]`

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/guacs/open-source/litestar/litestar/middleware/exceptions/middleware.py", line 191, in __call__
    await self.app(scope, receive, send)
  File "/home/guacs/open-source/litestar/litestar/routes/http.py", line 82, in handle
    response = await self._get_response_for_request(
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/guacs/open-source/litestar/litestar/routes/http.py", line 134, in _get_response_for_request
    return await self._call_handler_function(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/guacs/open-source/litestar/litestar/routes/http.py", line 154, in _call_handler_function
    response_data, cleanup_group = await self._get_response_data(
                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/guacs/open-source/litestar/litestar/routes/http.py", line 193, in _get_response_data
    parsed_kwargs = route_handler.signature_model.parse_values_from_connection_kwargs(
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/guacs/open-source/litestar/litestar/_signature/model.py", line 202, in parse_values_from_connection_kwargs
    raise cls._create_exception(messages=messages, connection=connection) from e
litestar.exceptions.http_exceptions.ValidationException: 400: Validation failed for POST http://testserver.local/

Litestar Version

Main

Platform

  • Linux
  • Mac
  • Windows
  • Other (Please specify in the description above)

Note

While we are open for sponsoring on GitHub Sponsors and
OpenCollective, we also utilize Polar.sh to engage in pledge-based sponsorship.

Check out all issues funded or available for funding on our Polar.sh dashboard

  • If you would like to see an issue prioritized, make a pledge towards it!
  • We receive the pledge once the issue is completed & verified
  • This, along with engagement in the community, helps us know which features are a priority to our users.
Fund with Polar
@guacs guacs added the Bug 🐛 This is something that is not working as expected label Jan 3, 2024
@provinzkraut
Copy link
Member

I think this might actually be the same as #2939.

The extraction as mentioned in the docs doesn't really make sense. If you have a multipart form upload with named files, you'll have both the field name and the file name, but the examples in the docs completely ignore the field name, which is not what happens, and which is not what should happen. For this to work, we'd need to support nested models for multipart, which isn't unreasonable, but just not implemented at the moment. IMO we should just fix the docs.

@litestar-org/maintainers?

@guacs
Copy link
Member Author

guacs commented Jan 5, 2024

I'm in agreement that fixing the docs is what we should do.

@provinzkraut provinzkraut changed the title Bug: Using dict[str, UploadFile] doesn't work and raises a validation error Documentation: Examples using dict[str, UploadFile] don't work as advertised Jan 21, 2024
@provinzkraut provinzkraut added Documentation 📚 This is related to documentation and removed Bug 🐛 This is something that is not working as expected labels Jan 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Documentation 📚 This is related to documentation
Projects
Status: Triage
Development

No branches or pull requests

2 participants