mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-28 12:55:05 +00:00

## Summary The initial purpose was to fix #15043, where code like this: ```python from fastapi import FastAPI, Query app = FastAPI() @app.get("/test") def handler(echo: str = Query("")): return echo ``` was being fixed to the invalid code below: ```python from typing import Annotated from fastapi import FastAPI, Query app = FastAPI() @app.get("/test") def handler(echo: Annotated[str, Query("")]): # changed return echo ``` As @MichaReiser pointed out, the correct fix is: ```python from typing import Annotated from fastapi import FastAPI, Query app = FastAPI() @app.get("/test") def handler(echo: Annotated[str, Query()] = ""): # changed return echo ``` After fixing the issue for `Query`, I realized that other classes like `Path`, `Body`, `Cookie`, `Header`, `File`, and `Form` also looked susceptible to this issue. The last few commits should handle these too, which I think means this will also close #12913. I had to reorder the arguments to the `do_stuff` test case because the new fix removes some default argument values (eg for `Path`: `some_path_param: str = Path()` becomes `some_path_param: Annotated[str, Path()]`). There's also #14484 related to this rule. I'm happy to take a stab at that here or in a follow up PR too. ## Test Plan `cargo test` I also checked the fixed output with `uv run --with fastapi FAST002_0.py`, but it required making a bunch of additional changes to the test file that I wasn't sure we wanted in this PR. --------- Co-authored-by: Micha Reiser <micha@reiser.io>
99 lines
1.9 KiB
Python
99 lines
1.9 KiB
Python
from fastapi import (
|
|
FastAPI,
|
|
APIRouter,
|
|
Query,
|
|
Path,
|
|
Body,
|
|
Cookie,
|
|
Header,
|
|
File,
|
|
Form,
|
|
Depends,
|
|
Security,
|
|
)
|
|
from pydantic import BaseModel
|
|
|
|
app = FastAPI()
|
|
router = APIRouter()
|
|
|
|
|
|
# Fixable errors
|
|
|
|
@app.get("/items/")
|
|
def get_items(
|
|
current_user: User = Depends(get_current_user),
|
|
some_security_param: str = Security(get_oauth2_user),
|
|
):
|
|
pass
|
|
|
|
|
|
@app.post("/stuff/")
|
|
def do_stuff(
|
|
some_path_param: str = Path(),
|
|
some_cookie_param: str = Cookie(),
|
|
some_file_param: UploadFile = File(),
|
|
some_form_param: str = Form(),
|
|
some_query_param: str | None = Query(default=None),
|
|
some_body_param: str = Body("foo"),
|
|
some_header_param: int = Header(default=5),
|
|
):
|
|
# do stuff
|
|
pass
|
|
|
|
@app.get("/users/")
|
|
def get_users(
|
|
skip: int,
|
|
limit: int,
|
|
current_user: User = Depends(get_current_user),
|
|
):
|
|
pass
|
|
|
|
@app.get("/users/")
|
|
def get_users(
|
|
current_user: User = Depends(get_current_user),
|
|
skip: int = 0,
|
|
limit: int = 10,
|
|
):
|
|
pass
|
|
|
|
|
|
@app.get("/items/{item_id}")
|
|
async def read_items(*, item_id: int = Path(title="The ID of the item to get"), q: str):
|
|
pass
|
|
|
|
# Non fixable errors
|
|
|
|
@app.get("/users/")
|
|
def get_users(
|
|
skip: int = 0,
|
|
limit: int = 10,
|
|
current_user: User = Depends(get_current_user),
|
|
):
|
|
pass
|
|
|
|
|
|
# Unchanged
|
|
|
|
|
|
@app.post("/stuff/")
|
|
def do_stuff(
|
|
no_default: Body("foo"),
|
|
no_type_annotation=str,
|
|
no_fastapi_default: str = BaseModel(),
|
|
):
|
|
pass
|
|
|
|
|
|
# OK
|
|
|
|
@app.post("/stuff/")
|
|
def do_stuff(
|
|
some_path_param: Annotated[str, Path()],
|
|
some_cookie_param: Annotated[str, Cookie()],
|
|
some_file_param: Annotated[UploadFile, File()],
|
|
some_form_param: Annotated[str, Form()],
|
|
some_query_param: Annotated[str | None, Query()] = None,
|
|
some_body_param: Annotated[str, Body()] = "foo",
|
|
some_header_param: Annotated[int, Header()] = 5,
|
|
):
|
|
pass
|