ruff/crates/ruff_linter/resources/test/fixtures/fastapi/FAST002_0.py
Brent Westbrook 1a77a75935
[FastAPI] Update Annotated fixes (FAST002) (#15462)
## 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>
2025-01-15 13:05:53 -05:00

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