mirror of https://github.com/tiangolo/fastapi.git
🐛 Fix using `Annotated` in routers or path operations decorated multiple times (#9315)
* Fix: copy FieldInfo from Annotated arguments We need to copy the field_info to prevent ourselves from mutating it. This allows multiple path or nested routers ,etc. * 📝 Add comment in fastapi/dependencies/utils.py Co-authored-by: Nadav Zingerman <7372858+nzig@users.noreply.github.com> * ✅ Extend and tweak tests for Annotated * ✅ Tweak coverage, it's probably covered by a different version of Python --------- Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com> Co-authored-by: Nadav Zingerman <7372858+nzig@users.noreply.github.com>
This commit is contained in:
parent
6ce6c8954c
commit
fdf66c825e
|
|
@ -1,7 +1,7 @@
|
|||
import dataclasses
|
||||
import inspect
|
||||
from contextlib import contextmanager
|
||||
from copy import deepcopy
|
||||
from copy import copy, deepcopy
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
|
|
@ -383,7 +383,8 @@ def analyze_param(
|
|||
), f"Cannot specify multiple `Annotated` FastAPI arguments for {param_name!r}"
|
||||
fastapi_annotation = next(iter(fastapi_annotations), None)
|
||||
if isinstance(fastapi_annotation, FieldInfo):
|
||||
field_info = fastapi_annotation
|
||||
# Copy `field_info` because we mutate `field_info.default` below.
|
||||
field_info = copy(fastapi_annotation)
|
||||
assert field_info.default is Undefined or field_info.default is Required, (
|
||||
f"`{field_info.__class__.__name__}` default value cannot be set in"
|
||||
f" `Annotated` for {param_name!r}. Set the default value with `=` instead."
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import pytest
|
||||
from fastapi import FastAPI, Query
|
||||
from fastapi import APIRouter, FastAPI, Query
|
||||
from fastapi.testclient import TestClient
|
||||
from typing_extensions import Annotated
|
||||
|
||||
|
|
@ -224,3 +224,44 @@ def test_get(path, expected_status, expected_response):
|
|||
response = client.get(path)
|
||||
assert response.status_code == expected_status
|
||||
assert response.json() == expected_response
|
||||
|
||||
|
||||
def test_multiple_path():
|
||||
@app.get("/test1")
|
||||
@app.get("/test2")
|
||||
async def test(var: Annotated[str, Query()] = "bar"):
|
||||
return {"foo": var}
|
||||
|
||||
response = client.get("/test1")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"foo": "bar"}
|
||||
|
||||
response = client.get("/test1", params={"var": "baz"})
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"foo": "baz"}
|
||||
|
||||
response = client.get("/test2")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"foo": "bar"}
|
||||
|
||||
response = client.get("/test2", params={"var": "baz"})
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"foo": "baz"}
|
||||
|
||||
|
||||
def test_nested_router():
|
||||
app = FastAPI()
|
||||
|
||||
router = APIRouter(prefix="/nested")
|
||||
|
||||
@router.get("/test")
|
||||
async def test(var: Annotated[str, Query()] = "bar"):
|
||||
return {"foo": var}
|
||||
|
||||
app.include_router(router)
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
response = client.get("/nested/test")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"foo": "bar"}
|
||||
|
|
|
|||
Loading…
Reference in New Issue