mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-28 10:50:26 +00:00
[fastapi] Handle ellipsis defaults in FAST002 autofix (FAST002) (#20810)
## Summary Implement handling of ellipsis (`...`) defaults in the `FAST002` autofix to correctly differentiate between required and optional parameters in FastAPI route definitions. Previously, the autofix did not properly handle cases where parameters used `...` as a default value (to indicate required parameters). This could lead to incorrect transformations when applying the autofix. This change updates the `FAST002` autofix logic to: - Correctly recognize `...` as a valid FastAPI required default. - Preserve the semantics of required parameters while still applying other autofix improvements. - Avoid incorrectly substituting or removing ellipsis defaults. Fixes https://github.com/astral-sh/ruff/issues/20800 ## Test Plan Added a new test fixture at: ```crates/ruff_linter/resources/test/fixtures/fastapi/FAST002_2.py```
This commit is contained in:
parent
692b7d7f0c
commit
a802d7a0ea
5 changed files with 725 additions and 7 deletions
87
crates/ruff_linter/resources/test/fixtures/fastapi/FAST002_2.py
vendored
Normal file
87
crates/ruff_linter/resources/test/fixtures/fastapi/FAST002_2.py
vendored
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
"""Test FAST002 ellipsis handling."""
|
||||
|
||||
from fastapi import Body, Cookie, FastAPI, Header, Query
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
# Cases that should be fixed - ellipsis should be removed
|
||||
|
||||
|
||||
@app.get("/test1")
|
||||
async def test_ellipsis_query(
|
||||
# This should become: param: Annotated[str, Query(description="Test param")]
|
||||
param: str = Query(..., description="Test param"),
|
||||
) -> str:
|
||||
return param
|
||||
|
||||
|
||||
@app.get("/test2")
|
||||
async def test_ellipsis_header(
|
||||
# This should become: auth: Annotated[str, Header(description="Auth header")]
|
||||
auth: str = Header(..., description="Auth header"),
|
||||
) -> str:
|
||||
return auth
|
||||
|
||||
|
||||
@app.post("/test3")
|
||||
async def test_ellipsis_body(
|
||||
# This should become: data: Annotated[dict, Body(description="Request body")]
|
||||
data: dict = Body(..., description="Request body"),
|
||||
) -> dict:
|
||||
return data
|
||||
|
||||
|
||||
@app.get("/test4")
|
||||
async def test_ellipsis_cookie(
|
||||
# This should become: session: Annotated[str, Cookie(description="Session ID")]
|
||||
session: str = Cookie(..., description="Session ID"),
|
||||
) -> str:
|
||||
return session
|
||||
|
||||
|
||||
@app.get("/test5")
|
||||
async def test_simple_ellipsis(
|
||||
# This should become: id: Annotated[str, Query()]
|
||||
id: str = Query(...),
|
||||
) -> str:
|
||||
return id
|
||||
|
||||
|
||||
@app.get("/test6")
|
||||
async def test_multiple_kwargs_with_ellipsis(
|
||||
# This should become: param: Annotated[str, Query(description="Test", min_length=1, max_length=10)]
|
||||
param: str = Query(..., description="Test", min_length=1, max_length=10),
|
||||
) -> str:
|
||||
return param
|
||||
|
||||
|
||||
# Cases with actual default values - these should preserve the default
|
||||
|
||||
|
||||
@app.get("/test7")
|
||||
async def test_with_default_value(
|
||||
# This should become: param: Annotated[str, Query(description="Test")] = "default"
|
||||
param: str = Query("default", description="Test"),
|
||||
) -> str:
|
||||
return param
|
||||
|
||||
|
||||
@app.get("/test8")
|
||||
async def test_with_default_none(
|
||||
# This should become: param: Annotated[str | None, Query(description="Test")] = None
|
||||
param: str | None = Query(None, description="Test"),
|
||||
) -> str:
|
||||
return param or "empty"
|
||||
|
||||
|
||||
@app.get("/test9")
|
||||
async def test_mixed_parameters(
|
||||
# First param should be fixed with default preserved
|
||||
optional_param: str = Query("default", description="Optional"),
|
||||
# Second param should not be fixed because of the preceding default
|
||||
required_param: str = Query(..., description="Required"),
|
||||
# Third param should be fixed with default preserved
|
||||
another_optional_param: int = Query(42, description="Another optional"),
|
||||
) -> str:
|
||||
return f"{required_param}-{optional_param}-{another_optional_param}"
|
||||
|
|
@ -18,6 +18,7 @@ mod tests {
|
|||
#[test_case(Rule::FastApiRedundantResponseModel, Path::new("FAST001.py"))]
|
||||
#[test_case(Rule::FastApiNonAnnotatedDependency, Path::new("FAST002_0.py"))]
|
||||
#[test_case(Rule::FastApiNonAnnotatedDependency, Path::new("FAST002_1.py"))]
|
||||
#[test_case(Rule::FastApiNonAnnotatedDependency, Path::new("FAST002_2.py"))]
|
||||
#[test_case(Rule::FastApiUnusedPathParameter, Path::new("FAST003.py"))]
|
||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.name(), path.to_string_lossy());
|
||||
|
|
@ -56,6 +57,7 @@ mod tests {
|
|||
// since `typing.Annotated` was added in Python 3.9
|
||||
#[test_case(Rule::FastApiNonAnnotatedDependency, Path::new("FAST002_0.py"))]
|
||||
#[test_case(Rule::FastApiNonAnnotatedDependency, Path::new("FAST002_1.py"))]
|
||||
#[test_case(Rule::FastApiNonAnnotatedDependency, Path::new("FAST002_2.py"))]
|
||||
fn rules_py38(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}_py38", rule_code.name(), path.to_string_lossy());
|
||||
let diagnostics = test_path(
|
||||
|
|
|
|||
|
|
@ -275,19 +275,42 @@ fn create_diagnostic(
|
|||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
|
||||
seen_default = true;
|
||||
format!(
|
||||
"{parameter_name}: {binding}[{annotation}, {default_}({kwarg_list})] \
|
||||
= {default_value}",
|
||||
// Check if the default argument is ellipsis (...), which in FastAPI means "required"
|
||||
let is_default_argument_ellipsis = matches!(
|
||||
dependency_call.default_argument.value(),
|
||||
ast::Expr::EllipsisLiteral(_)
|
||||
);
|
||||
|
||||
if is_default_argument_ellipsis && seen_default {
|
||||
// For ellipsis after a parameter with default, can't remove the default
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
if !is_default_argument_ellipsis {
|
||||
// For actual default values, mark that we've seen a default
|
||||
seen_default = true;
|
||||
}
|
||||
|
||||
let base_format = format!(
|
||||
"{parameter_name}: {binding}[{annotation}, {default_}({kwarg_list})]",
|
||||
parameter_name = parameter.name,
|
||||
annotation = checker.locator().slice(parameter.annotation.range()),
|
||||
default_ = checker
|
||||
.locator()
|
||||
.slice(map_callable(parameter.default).range()),
|
||||
default_value = checker
|
||||
);
|
||||
|
||||
if is_default_argument_ellipsis {
|
||||
// For ellipsis, don't add a default value since the parameter
|
||||
// should remain required after conversion to Annotated
|
||||
base_format
|
||||
} else {
|
||||
// For actual default values, preserve them
|
||||
let default_value = checker
|
||||
.locator()
|
||||
.slice(dependency_call.default_argument.value().range()),
|
||||
)
|
||||
.slice(dependency_call.default_argument.value().range());
|
||||
format!("{base_format} = {default_value}")
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if seen_default {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,303 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/fastapi/mod.rs
|
||||
---
|
||||
FAST002 [*] FastAPI dependency without `Annotated`
|
||||
--> FAST002_2.py:14:5
|
||||
|
|
||||
12 | async def test_ellipsis_query(
|
||||
13 | # This should become: param: Annotated[str, Query(description="Test param")]
|
||||
14 | param: str = Query(..., description="Test param"),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
15 | ) -> str:
|
||||
16 | return param
|
||||
|
|
||||
help: Replace with `typing.Annotated`
|
||||
1 | """Test FAST002 ellipsis handling."""
|
||||
2 |
|
||||
3 | from fastapi import Body, Cookie, FastAPI, Header, Query
|
||||
4 + from typing import Annotated
|
||||
5 |
|
||||
6 | app = FastAPI()
|
||||
7 |
|
||||
--------------------------------------------------------------------------------
|
||||
12 | @app.get("/test1")
|
||||
13 | async def test_ellipsis_query(
|
||||
14 | # This should become: param: Annotated[str, Query(description="Test param")]
|
||||
- param: str = Query(..., description="Test param"),
|
||||
15 + param: Annotated[str, Query(description="Test param")],
|
||||
16 | ) -> str:
|
||||
17 | return param
|
||||
18 |
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
FAST002 [*] FastAPI dependency without `Annotated`
|
||||
--> FAST002_2.py:22:5
|
||||
|
|
||||
20 | async def test_ellipsis_header(
|
||||
21 | # This should become: auth: Annotated[str, Header(description="Auth header")]
|
||||
22 | auth: str = Header(..., description="Auth header"),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
23 | ) -> str:
|
||||
24 | return auth
|
||||
|
|
||||
help: Replace with `typing.Annotated`
|
||||
1 | """Test FAST002 ellipsis handling."""
|
||||
2 |
|
||||
3 | from fastapi import Body, Cookie, FastAPI, Header, Query
|
||||
4 + from typing import Annotated
|
||||
5 |
|
||||
6 | app = FastAPI()
|
||||
7 |
|
||||
--------------------------------------------------------------------------------
|
||||
20 | @app.get("/test2")
|
||||
21 | async def test_ellipsis_header(
|
||||
22 | # This should become: auth: Annotated[str, Header(description="Auth header")]
|
||||
- auth: str = Header(..., description="Auth header"),
|
||||
23 + auth: Annotated[str, Header(description="Auth header")],
|
||||
24 | ) -> str:
|
||||
25 | return auth
|
||||
26 |
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
FAST002 [*] FastAPI dependency without `Annotated`
|
||||
--> FAST002_2.py:30:5
|
||||
|
|
||||
28 | async def test_ellipsis_body(
|
||||
29 | # This should become: data: Annotated[dict, Body(description="Request body")]
|
||||
30 | data: dict = Body(..., description="Request body"),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
31 | ) -> dict:
|
||||
32 | return data
|
||||
|
|
||||
help: Replace with `typing.Annotated`
|
||||
1 | """Test FAST002 ellipsis handling."""
|
||||
2 |
|
||||
3 | from fastapi import Body, Cookie, FastAPI, Header, Query
|
||||
4 + from typing import Annotated
|
||||
5 |
|
||||
6 | app = FastAPI()
|
||||
7 |
|
||||
--------------------------------------------------------------------------------
|
||||
28 | @app.post("/test3")
|
||||
29 | async def test_ellipsis_body(
|
||||
30 | # This should become: data: Annotated[dict, Body(description="Request body")]
|
||||
- data: dict = Body(..., description="Request body"),
|
||||
31 + data: Annotated[dict, Body(description="Request body")],
|
||||
32 | ) -> dict:
|
||||
33 | return data
|
||||
34 |
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
FAST002 [*] FastAPI dependency without `Annotated`
|
||||
--> FAST002_2.py:38:5
|
||||
|
|
||||
36 | async def test_ellipsis_cookie(
|
||||
37 | # This should become: session: Annotated[str, Cookie(description="Session ID")]
|
||||
38 | session: str = Cookie(..., description="Session ID"),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
39 | ) -> str:
|
||||
40 | return session
|
||||
|
|
||||
help: Replace with `typing.Annotated`
|
||||
1 | """Test FAST002 ellipsis handling."""
|
||||
2 |
|
||||
3 | from fastapi import Body, Cookie, FastAPI, Header, Query
|
||||
4 + from typing import Annotated
|
||||
5 |
|
||||
6 | app = FastAPI()
|
||||
7 |
|
||||
--------------------------------------------------------------------------------
|
||||
36 | @app.get("/test4")
|
||||
37 | async def test_ellipsis_cookie(
|
||||
38 | # This should become: session: Annotated[str, Cookie(description="Session ID")]
|
||||
- session: str = Cookie(..., description="Session ID"),
|
||||
39 + session: Annotated[str, Cookie(description="Session ID")],
|
||||
40 | ) -> str:
|
||||
41 | return session
|
||||
42 |
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
FAST002 [*] FastAPI dependency without `Annotated`
|
||||
--> FAST002_2.py:46:5
|
||||
|
|
||||
44 | async def test_simple_ellipsis(
|
||||
45 | # This should become: id: Annotated[str, Query()]
|
||||
46 | id: str = Query(...),
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
47 | ) -> str:
|
||||
48 | return id
|
||||
|
|
||||
help: Replace with `typing.Annotated`
|
||||
1 | """Test FAST002 ellipsis handling."""
|
||||
2 |
|
||||
3 | from fastapi import Body, Cookie, FastAPI, Header, Query
|
||||
4 + from typing import Annotated
|
||||
5 |
|
||||
6 | app = FastAPI()
|
||||
7 |
|
||||
--------------------------------------------------------------------------------
|
||||
44 | @app.get("/test5")
|
||||
45 | async def test_simple_ellipsis(
|
||||
46 | # This should become: id: Annotated[str, Query()]
|
||||
- id: str = Query(...),
|
||||
47 + id: Annotated[str, Query()],
|
||||
48 | ) -> str:
|
||||
49 | return id
|
||||
50 |
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
FAST002 [*] FastAPI dependency without `Annotated`
|
||||
--> FAST002_2.py:54:5
|
||||
|
|
||||
52 | async def test_multiple_kwargs_with_ellipsis(
|
||||
53 | # This should become: param: Annotated[str, Query(description="Test", min_length=1, max_length=10)]
|
||||
54 | param: str = Query(..., description="Test", min_length=1, max_length=10),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
55 | ) -> str:
|
||||
56 | return param
|
||||
|
|
||||
help: Replace with `typing.Annotated`
|
||||
1 | """Test FAST002 ellipsis handling."""
|
||||
2 |
|
||||
3 | from fastapi import Body, Cookie, FastAPI, Header, Query
|
||||
4 + from typing import Annotated
|
||||
5 |
|
||||
6 | app = FastAPI()
|
||||
7 |
|
||||
--------------------------------------------------------------------------------
|
||||
52 | @app.get("/test6")
|
||||
53 | async def test_multiple_kwargs_with_ellipsis(
|
||||
54 | # This should become: param: Annotated[str, Query(description="Test", min_length=1, max_length=10)]
|
||||
- param: str = Query(..., description="Test", min_length=1, max_length=10),
|
||||
55 + param: Annotated[str, Query(description="Test", min_length=1, max_length=10)],
|
||||
56 | ) -> str:
|
||||
57 | return param
|
||||
58 |
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
FAST002 [*] FastAPI dependency without `Annotated`
|
||||
--> FAST002_2.py:65:5
|
||||
|
|
||||
63 | async def test_with_default_value(
|
||||
64 | # This should become: param: Annotated[str, Query(description="Test")] = "default"
|
||||
65 | param: str = Query("default", description="Test"),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
66 | ) -> str:
|
||||
67 | return param
|
||||
|
|
||||
help: Replace with `typing.Annotated`
|
||||
1 | """Test FAST002 ellipsis handling."""
|
||||
2 |
|
||||
3 | from fastapi import Body, Cookie, FastAPI, Header, Query
|
||||
4 + from typing import Annotated
|
||||
5 |
|
||||
6 | app = FastAPI()
|
||||
7 |
|
||||
--------------------------------------------------------------------------------
|
||||
63 | @app.get("/test7")
|
||||
64 | async def test_with_default_value(
|
||||
65 | # This should become: param: Annotated[str, Query(description="Test")] = "default"
|
||||
- param: str = Query("default", description="Test"),
|
||||
66 + param: Annotated[str, Query(description="Test")] = "default",
|
||||
67 | ) -> str:
|
||||
68 | return param
|
||||
69 |
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
FAST002 [*] FastAPI dependency without `Annotated`
|
||||
--> FAST002_2.py:73:5
|
||||
|
|
||||
71 | async def test_with_default_none(
|
||||
72 | # This should become: param: Annotated[str | None, Query(description="Test")] = None
|
||||
73 | param: str | None = Query(None, description="Test"),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
74 | ) -> str:
|
||||
75 | return param or "empty"
|
||||
|
|
||||
help: Replace with `typing.Annotated`
|
||||
1 | """Test FAST002 ellipsis handling."""
|
||||
2 |
|
||||
3 | from fastapi import Body, Cookie, FastAPI, Header, Query
|
||||
4 + from typing import Annotated
|
||||
5 |
|
||||
6 | app = FastAPI()
|
||||
7 |
|
||||
--------------------------------------------------------------------------------
|
||||
71 | @app.get("/test8")
|
||||
72 | async def test_with_default_none(
|
||||
73 | # This should become: param: Annotated[str | None, Query(description="Test")] = None
|
||||
- param: str | None = Query(None, description="Test"),
|
||||
74 + param: Annotated[str | None, Query(description="Test")] = None,
|
||||
75 | ) -> str:
|
||||
76 | return param or "empty"
|
||||
77 |
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
FAST002 [*] FastAPI dependency without `Annotated`
|
||||
--> FAST002_2.py:81:5
|
||||
|
|
||||
79 | async def test_mixed_parameters(
|
||||
80 | # First param should be fixed with default preserved
|
||||
81 | optional_param: str = Query("default", description="Optional"),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
82 | # Second param should not be fixed because of the preceding default
|
||||
83 | required_param: str = Query(..., description="Required"),
|
||||
|
|
||||
help: Replace with `typing.Annotated`
|
||||
1 | """Test FAST002 ellipsis handling."""
|
||||
2 |
|
||||
3 | from fastapi import Body, Cookie, FastAPI, Header, Query
|
||||
4 + from typing import Annotated
|
||||
5 |
|
||||
6 | app = FastAPI()
|
||||
7 |
|
||||
--------------------------------------------------------------------------------
|
||||
79 | @app.get("/test9")
|
||||
80 | async def test_mixed_parameters(
|
||||
81 | # First param should be fixed with default preserved
|
||||
- optional_param: str = Query("default", description="Optional"),
|
||||
82 + optional_param: Annotated[str, Query(description="Optional")] = "default",
|
||||
83 | # Second param should not be fixed because of the preceding default
|
||||
84 | required_param: str = Query(..., description="Required"),
|
||||
85 | # Third param should be fixed with default preserved
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
FAST002 FastAPI dependency without `Annotated`
|
||||
--> FAST002_2.py:83:5
|
||||
|
|
||||
81 | optional_param: str = Query("default", description="Optional"),
|
||||
82 | # Second param should not be fixed because of the preceding default
|
||||
83 | required_param: str = Query(..., description="Required"),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
84 | # Third param should be fixed with default preserved
|
||||
85 | another_optional_param: int = Query(42, description="Another optional"),
|
||||
|
|
||||
help: Replace with `typing.Annotated`
|
||||
|
||||
FAST002 [*] FastAPI dependency without `Annotated`
|
||||
--> FAST002_2.py:85:5
|
||||
|
|
||||
83 | required_param: str = Query(..., description="Required"),
|
||||
84 | # Third param should be fixed with default preserved
|
||||
85 | another_optional_param: int = Query(42, description="Another optional"),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
86 | ) -> str:
|
||||
87 | return f"{required_param}-{optional_param}-{another_optional_param}"
|
||||
|
|
||||
help: Replace with `typing.Annotated`
|
||||
1 | """Test FAST002 ellipsis handling."""
|
||||
2 |
|
||||
3 | from fastapi import Body, Cookie, FastAPI, Header, Query
|
||||
4 + from typing import Annotated
|
||||
5 |
|
||||
6 | app = FastAPI()
|
||||
7 |
|
||||
--------------------------------------------------------------------------------
|
||||
83 | # Second param should not be fixed because of the preceding default
|
||||
84 | required_param: str = Query(..., description="Required"),
|
||||
85 | # Third param should be fixed with default preserved
|
||||
- another_optional_param: int = Query(42, description="Another optional"),
|
||||
86 + another_optional_param: Annotated[int, Query(description="Another optional")] = 42,
|
||||
87 | ) -> str:
|
||||
88 | return f"{required_param}-{optional_param}-{another_optional_param}"
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
|
@ -0,0 +1,303 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/fastapi/mod.rs
|
||||
---
|
||||
FAST002 [*] FastAPI dependency without `Annotated`
|
||||
--> FAST002_2.py:14:5
|
||||
|
|
||||
12 | async def test_ellipsis_query(
|
||||
13 | # This should become: param: Annotated[str, Query(description="Test param")]
|
||||
14 | param: str = Query(..., description="Test param"),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
15 | ) -> str:
|
||||
16 | return param
|
||||
|
|
||||
help: Replace with `typing_extensions.Annotated`
|
||||
1 | """Test FAST002 ellipsis handling."""
|
||||
2 |
|
||||
3 | from fastapi import Body, Cookie, FastAPI, Header, Query
|
||||
4 + from typing_extensions import Annotated
|
||||
5 |
|
||||
6 | app = FastAPI()
|
||||
7 |
|
||||
--------------------------------------------------------------------------------
|
||||
12 | @app.get("/test1")
|
||||
13 | async def test_ellipsis_query(
|
||||
14 | # This should become: param: Annotated[str, Query(description="Test param")]
|
||||
- param: str = Query(..., description="Test param"),
|
||||
15 + param: Annotated[str, Query(description="Test param")],
|
||||
16 | ) -> str:
|
||||
17 | return param
|
||||
18 |
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
FAST002 [*] FastAPI dependency without `Annotated`
|
||||
--> FAST002_2.py:22:5
|
||||
|
|
||||
20 | async def test_ellipsis_header(
|
||||
21 | # This should become: auth: Annotated[str, Header(description="Auth header")]
|
||||
22 | auth: str = Header(..., description="Auth header"),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
23 | ) -> str:
|
||||
24 | return auth
|
||||
|
|
||||
help: Replace with `typing_extensions.Annotated`
|
||||
1 | """Test FAST002 ellipsis handling."""
|
||||
2 |
|
||||
3 | from fastapi import Body, Cookie, FastAPI, Header, Query
|
||||
4 + from typing_extensions import Annotated
|
||||
5 |
|
||||
6 | app = FastAPI()
|
||||
7 |
|
||||
--------------------------------------------------------------------------------
|
||||
20 | @app.get("/test2")
|
||||
21 | async def test_ellipsis_header(
|
||||
22 | # This should become: auth: Annotated[str, Header(description="Auth header")]
|
||||
- auth: str = Header(..., description="Auth header"),
|
||||
23 + auth: Annotated[str, Header(description="Auth header")],
|
||||
24 | ) -> str:
|
||||
25 | return auth
|
||||
26 |
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
FAST002 [*] FastAPI dependency without `Annotated`
|
||||
--> FAST002_2.py:30:5
|
||||
|
|
||||
28 | async def test_ellipsis_body(
|
||||
29 | # This should become: data: Annotated[dict, Body(description="Request body")]
|
||||
30 | data: dict = Body(..., description="Request body"),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
31 | ) -> dict:
|
||||
32 | return data
|
||||
|
|
||||
help: Replace with `typing_extensions.Annotated`
|
||||
1 | """Test FAST002 ellipsis handling."""
|
||||
2 |
|
||||
3 | from fastapi import Body, Cookie, FastAPI, Header, Query
|
||||
4 + from typing_extensions import Annotated
|
||||
5 |
|
||||
6 | app = FastAPI()
|
||||
7 |
|
||||
--------------------------------------------------------------------------------
|
||||
28 | @app.post("/test3")
|
||||
29 | async def test_ellipsis_body(
|
||||
30 | # This should become: data: Annotated[dict, Body(description="Request body")]
|
||||
- data: dict = Body(..., description="Request body"),
|
||||
31 + data: Annotated[dict, Body(description="Request body")],
|
||||
32 | ) -> dict:
|
||||
33 | return data
|
||||
34 |
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
FAST002 [*] FastAPI dependency without `Annotated`
|
||||
--> FAST002_2.py:38:5
|
||||
|
|
||||
36 | async def test_ellipsis_cookie(
|
||||
37 | # This should become: session: Annotated[str, Cookie(description="Session ID")]
|
||||
38 | session: str = Cookie(..., description="Session ID"),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
39 | ) -> str:
|
||||
40 | return session
|
||||
|
|
||||
help: Replace with `typing_extensions.Annotated`
|
||||
1 | """Test FAST002 ellipsis handling."""
|
||||
2 |
|
||||
3 | from fastapi import Body, Cookie, FastAPI, Header, Query
|
||||
4 + from typing_extensions import Annotated
|
||||
5 |
|
||||
6 | app = FastAPI()
|
||||
7 |
|
||||
--------------------------------------------------------------------------------
|
||||
36 | @app.get("/test4")
|
||||
37 | async def test_ellipsis_cookie(
|
||||
38 | # This should become: session: Annotated[str, Cookie(description="Session ID")]
|
||||
- session: str = Cookie(..., description="Session ID"),
|
||||
39 + session: Annotated[str, Cookie(description="Session ID")],
|
||||
40 | ) -> str:
|
||||
41 | return session
|
||||
42 |
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
FAST002 [*] FastAPI dependency without `Annotated`
|
||||
--> FAST002_2.py:46:5
|
||||
|
|
||||
44 | async def test_simple_ellipsis(
|
||||
45 | # This should become: id: Annotated[str, Query()]
|
||||
46 | id: str = Query(...),
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
47 | ) -> str:
|
||||
48 | return id
|
||||
|
|
||||
help: Replace with `typing_extensions.Annotated`
|
||||
1 | """Test FAST002 ellipsis handling."""
|
||||
2 |
|
||||
3 | from fastapi import Body, Cookie, FastAPI, Header, Query
|
||||
4 + from typing_extensions import Annotated
|
||||
5 |
|
||||
6 | app = FastAPI()
|
||||
7 |
|
||||
--------------------------------------------------------------------------------
|
||||
44 | @app.get("/test5")
|
||||
45 | async def test_simple_ellipsis(
|
||||
46 | # This should become: id: Annotated[str, Query()]
|
||||
- id: str = Query(...),
|
||||
47 + id: Annotated[str, Query()],
|
||||
48 | ) -> str:
|
||||
49 | return id
|
||||
50 |
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
FAST002 [*] FastAPI dependency without `Annotated`
|
||||
--> FAST002_2.py:54:5
|
||||
|
|
||||
52 | async def test_multiple_kwargs_with_ellipsis(
|
||||
53 | # This should become: param: Annotated[str, Query(description="Test", min_length=1, max_length=10)]
|
||||
54 | param: str = Query(..., description="Test", min_length=1, max_length=10),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
55 | ) -> str:
|
||||
56 | return param
|
||||
|
|
||||
help: Replace with `typing_extensions.Annotated`
|
||||
1 | """Test FAST002 ellipsis handling."""
|
||||
2 |
|
||||
3 | from fastapi import Body, Cookie, FastAPI, Header, Query
|
||||
4 + from typing_extensions import Annotated
|
||||
5 |
|
||||
6 | app = FastAPI()
|
||||
7 |
|
||||
--------------------------------------------------------------------------------
|
||||
52 | @app.get("/test6")
|
||||
53 | async def test_multiple_kwargs_with_ellipsis(
|
||||
54 | # This should become: param: Annotated[str, Query(description="Test", min_length=1, max_length=10)]
|
||||
- param: str = Query(..., description="Test", min_length=1, max_length=10),
|
||||
55 + param: Annotated[str, Query(description="Test", min_length=1, max_length=10)],
|
||||
56 | ) -> str:
|
||||
57 | return param
|
||||
58 |
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
FAST002 [*] FastAPI dependency without `Annotated`
|
||||
--> FAST002_2.py:65:5
|
||||
|
|
||||
63 | async def test_with_default_value(
|
||||
64 | # This should become: param: Annotated[str, Query(description="Test")] = "default"
|
||||
65 | param: str = Query("default", description="Test"),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
66 | ) -> str:
|
||||
67 | return param
|
||||
|
|
||||
help: Replace with `typing_extensions.Annotated`
|
||||
1 | """Test FAST002 ellipsis handling."""
|
||||
2 |
|
||||
3 | from fastapi import Body, Cookie, FastAPI, Header, Query
|
||||
4 + from typing_extensions import Annotated
|
||||
5 |
|
||||
6 | app = FastAPI()
|
||||
7 |
|
||||
--------------------------------------------------------------------------------
|
||||
63 | @app.get("/test7")
|
||||
64 | async def test_with_default_value(
|
||||
65 | # This should become: param: Annotated[str, Query(description="Test")] = "default"
|
||||
- param: str = Query("default", description="Test"),
|
||||
66 + param: Annotated[str, Query(description="Test")] = "default",
|
||||
67 | ) -> str:
|
||||
68 | return param
|
||||
69 |
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
FAST002 [*] FastAPI dependency without `Annotated`
|
||||
--> FAST002_2.py:73:5
|
||||
|
|
||||
71 | async def test_with_default_none(
|
||||
72 | # This should become: param: Annotated[str | None, Query(description="Test")] = None
|
||||
73 | param: str | None = Query(None, description="Test"),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
74 | ) -> str:
|
||||
75 | return param or "empty"
|
||||
|
|
||||
help: Replace with `typing_extensions.Annotated`
|
||||
1 | """Test FAST002 ellipsis handling."""
|
||||
2 |
|
||||
3 | from fastapi import Body, Cookie, FastAPI, Header, Query
|
||||
4 + from typing_extensions import Annotated
|
||||
5 |
|
||||
6 | app = FastAPI()
|
||||
7 |
|
||||
--------------------------------------------------------------------------------
|
||||
71 | @app.get("/test8")
|
||||
72 | async def test_with_default_none(
|
||||
73 | # This should become: param: Annotated[str | None, Query(description="Test")] = None
|
||||
- param: str | None = Query(None, description="Test"),
|
||||
74 + param: Annotated[str | None, Query(description="Test")] = None,
|
||||
75 | ) -> str:
|
||||
76 | return param or "empty"
|
||||
77 |
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
FAST002 [*] FastAPI dependency without `Annotated`
|
||||
--> FAST002_2.py:81:5
|
||||
|
|
||||
79 | async def test_mixed_parameters(
|
||||
80 | # First param should be fixed with default preserved
|
||||
81 | optional_param: str = Query("default", description="Optional"),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
82 | # Second param should not be fixed because of the preceding default
|
||||
83 | required_param: str = Query(..., description="Required"),
|
||||
|
|
||||
help: Replace with `typing_extensions.Annotated`
|
||||
1 | """Test FAST002 ellipsis handling."""
|
||||
2 |
|
||||
3 | from fastapi import Body, Cookie, FastAPI, Header, Query
|
||||
4 + from typing_extensions import Annotated
|
||||
5 |
|
||||
6 | app = FastAPI()
|
||||
7 |
|
||||
--------------------------------------------------------------------------------
|
||||
79 | @app.get("/test9")
|
||||
80 | async def test_mixed_parameters(
|
||||
81 | # First param should be fixed with default preserved
|
||||
- optional_param: str = Query("default", description="Optional"),
|
||||
82 + optional_param: Annotated[str, Query(description="Optional")] = "default",
|
||||
83 | # Second param should not be fixed because of the preceding default
|
||||
84 | required_param: str = Query(..., description="Required"),
|
||||
85 | # Third param should be fixed with default preserved
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
FAST002 FastAPI dependency without `Annotated`
|
||||
--> FAST002_2.py:83:5
|
||||
|
|
||||
81 | optional_param: str = Query("default", description="Optional"),
|
||||
82 | # Second param should not be fixed because of the preceding default
|
||||
83 | required_param: str = Query(..., description="Required"),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
84 | # Third param should be fixed with default preserved
|
||||
85 | another_optional_param: int = Query(42, description="Another optional"),
|
||||
|
|
||||
help: Replace with `typing_extensions.Annotated`
|
||||
|
||||
FAST002 [*] FastAPI dependency without `Annotated`
|
||||
--> FAST002_2.py:85:5
|
||||
|
|
||||
83 | required_param: str = Query(..., description="Required"),
|
||||
84 | # Third param should be fixed with default preserved
|
||||
85 | another_optional_param: int = Query(42, description="Another optional"),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
86 | ) -> str:
|
||||
87 | return f"{required_param}-{optional_param}-{another_optional_param}"
|
||||
|
|
||||
help: Replace with `typing_extensions.Annotated`
|
||||
1 | """Test FAST002 ellipsis handling."""
|
||||
2 |
|
||||
3 | from fastapi import Body, Cookie, FastAPI, Header, Query
|
||||
4 + from typing_extensions import Annotated
|
||||
5 |
|
||||
6 | app = FastAPI()
|
||||
7 |
|
||||
--------------------------------------------------------------------------------
|
||||
83 | # Second param should not be fixed because of the preceding default
|
||||
84 | required_param: str = Query(..., description="Required"),
|
||||
85 | # Third param should be fixed with default preserved
|
||||
- another_optional_param: int = Query(42, description="Another optional"),
|
||||
86 + another_optional_param: Annotated[int, Query(description="Another optional")] = 42,
|
||||
87 | ) -> str:
|
||||
88 | return f"{required_param}-{optional_param}-{another_optional_param}"
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
Loading…
Add table
Add a link
Reference in a new issue