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

Check python-multipart when using Form and raise early #1599

Closed
9 tasks done
tiangolo opened this issue Jun 19, 2020 · 6 comments
Closed
9 tasks done

Check python-multipart when using Form and raise early #1599

tiangolo opened this issue Jun 19, 2020 · 6 comments
Labels
confirmed feature New feature or request good first issue Good for newcomers reviewed

Comments

@tiangolo
Copy link
Owner

tiangolo commented Jun 19, 2020

First check

  • I added a very descriptive title to this issue.
  • I used the GitHub search to find a similar issue and didn't find it.
  • I searched the FastAPI documentation, with the integrated search.
  • I already searched in Google "How to X in FastAPI" and didn't find any information.
  • I already read and followed all the tutorial in the docs and didn't find an answer.
  • I already checked if it is not related to FastAPI but to Pydantic.
  • I already checked if it is not related to FastAPI but to Swagger UI.
  • I already checked if it is not related to FastAPI but to ReDoc.
  • After submitting this, I commit to:
    • Read open issues with questions until I find 2 issues where I can help someone and add a comment to help there.
    • Or, I already hit the "watch" button in this repository to receive notifications and I commit to help at least 2 people that ask questions in the future.
    • Implement a Pull Request for a confirmed bug.

Example

Here's a self-contained minimal, reproducible, example with my use case:

from fastapi import FastAPI, Form

app = FastAPI()


@app.post("/login/")
async def login(username: str = Form(...), password: str = Form(...)):
    return {"username": username}

Description

  • Create a new venv
  • Install only FastAPI and Uvicorn
  • Run that example
  • Try it in the docs
  • It returns a 400 response with:
{
  "detail": "There was an error parsing the body"
}

The problem is that it's trying to read form data (multipart), and for that it requires the package python-multipart installed.

It is documented in the docs in FastAPI and Starlette, but forgetting to install it and running the app ends up in that behavior that is not explicit nor obvious. Everything works normally, except that specific path operation, that only breaks after a receiving a request (which is worse).

To make things more complex, the package required is python-multipart. But there's another Python package called just multipart.

And both packages are imported as:

import multipart

This can lead to a problem that is really difficult to debug: an environment with both packages installed, in some runs could import the correct package and everything would work, but in other runs, it could import the other package, which would cause problems. That would make it one of those scary ghost bugs that disappear and appear. 🐛 👻

And even worse, the problems will only show up after receiving a request in a path operation that requires reading form data, not at start up.

The solution you would like

In FastAPI, the main ways to get form data are quite explicit, using Form, UploadFile, bytes.

That means that it's possible to detect that a path operation will try to read form data on initialization.

I would like FastAPI to raise an error and crash fast when there's a path operation that uses something that requires to read form data, right on initialization (while creating the instances).

In Starlette (or in FastAPI when using the request directly), there's no way to know if the code will try to access form data from the request.

But in FastAPI, as it is declared in function parameters that are read during initialization, it would be possible to error out early.

It should log an error saying that form data requires python-multipart and then raise the exception. This would be triggered right while starting an application.

But it would only raise an exception if there's a path operation that actually requires form data, otherwise, everything would work as normal.

It should ideally also detect if the imported multipart is the correct one or not. If the incorrect one is installed/imported, it should log an error informing that clearly, explaining that multipart is not compatible with python-multipart, that the user has to uninstall multipart and then install python-multipart.

There's a point in code that decides if a path operation should read from JSON or from Form data while creating the request body field for a path operation. Whatever does the check about python-multipart, would ideally run from that same point.

Describe alternatives you've considered

Leave it as is. But it's frustrating when it happens.

It took me a while (days) to debug that there could be 2 multipart packages conflicting. If it was so difficult for me (that I'm supposed to know enough about FastAPI, I guess 🤷 ), I imagine how frustrating that could be for someone new.

Environment

  • OS: [e.g. Linux / Windows / macOS]: All
  • FastAPI Version [e.g. 0.3.0]: All

To know the FastAPI version use:

python -c "import fastapi; print(fastapi.__version__)"
  • Python version: All

To know the Python version use:

python --version

Additional context

Tom created an issue in the repo suggesting a name change: Kludex/python-multipart#16

But that will probably take some time to happen (if it happens).

@tiangolo tiangolo added feature New feature or request confirmed good first issue Good for newcomers labels Jun 19, 2020
@chrisnguyn
Copy link
Contributor

@YKo20010 and I will be working on this issue!

@Kludex
Copy link
Sponsor Collaborator

Kludex commented Jun 20, 2020

I saw a lot of questions on StackOverflow related to this issue. Good one! :)

@tiangolo
Copy link
Owner Author

tiangolo commented Aug 8, 2020

This is available in FastAPI version 0.60.2 🚀 🎉

Thanks for the work on this @chrisngyn, @YKo20010, @kx-chen ! ☕ 🍰

@tiangolo tiangolo closed this as completed Aug 8, 2020
@svanschalkwyk
Copy link

This is still happening with 0.63.0
Python 3.9.7

@mbrav
Copy link

mbrav commented Feb 17, 2022

Getting this error with 0.74.0 on Python 3.9.10 with pyenv 2.2.3-7-g39f1b141 while following the Hash and verify the passwords tutorial from the docs:

Form data requires "python-multipart" to be installed. 
You can install "python-multipart" with: 

pip install python-multipart

Traceback (most recent call last):
  File "/home/user/.pyenv/versions/signup_api/lib/python3.9/site-packages/fastapi/dependencies/utils.py", line 94, in check_file_field
    from multipart import __version__  # type: ignore
ModuleNotFoundError: No module named 'multipart'
During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/user/Desktop/dev/signup_api/api/auth.py", line 116, in <module>
    async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
  File "/home/user/.pyenv/versions/signup_api/lib/python3.9/site-packages/fastapi/routing.py", line 582, in decorator
    self.add_api_route(
  File "/home/user/.pyenv/versions/signup_api/lib/python3.9/site-packages/fastapi/routing.py", line 525, in add_api_route
    route = route_class(
  File "/home/user/.pyenv/versions/signup_api/lib/python3.9/site-packages/fastapi/routing.py", line 413, in __init__
    self.body_field = get_body_field(dependant=self.dependant, name=self.unique_id)
  File "/home/user/.pyenv/versions/signup_api/lib/python3.9/site-packages/fastapi/dependencies/utils.py", line 750, in get_body_field
    check_file_field(final_field)
  File "/home/user/.pyenv/versions/signup_api/lib/python3.9/site-packages/fastapi/dependencies/utils.py", line 107, in check_file_field
    raise RuntimeError(multipart_not_installed_error)
RuntimeError: Form data requires "python-multipart" to be installed. 

@svanschalkwyk
Copy link

svanschalkwyk commented Feb 17, 2022

Strangest thing - the error only occurs now when running from within visual code (I have my env correct!). When I invoke from the cli, (with the same env!), it runs. I need a beer.

File "/home/steph/Projects/xxxxx/fastapi/prolog_api/fastapi_prolog/.venv/lib/python3.9/site-packages/fastapi/dependencies/utils.py", line 782, in get_body_field...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
confirmed feature New feature or request good first issue Good for newcomers reviewed
Projects
None yet
Development

No branches or pull requests

5 participants