mirror of
https://github.com/fastapi/fastapi.git
synced 2026-03-29 12:11:59 -04:00
🐛 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:
@@ -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"}
|
||||
|
||||
Reference in New Issue
Block a user