This commit is contained in:
DJ Melisso 2025-12-16 21:09:33 +00:00 committed by GitHub
commit 0a9eef94c0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 125 additions and 1 deletions

View File

@ -1,3 +1,4 @@
import copy
import http.client
import inspect
import warnings
@ -378,7 +379,7 @@ def get_openapi_path(
additional_status_code,
additional_response,
) in route.responses.items():
process_response = additional_response.copy()
process_response = copy.deepcopy(additional_response)
process_response.pop("model", None)
status_code_key = str(additional_status_code).upper()
if status_code_key == "DEFAULT":

View File

@ -0,0 +1,123 @@
"""
Regression test: Ensure app-level responses with Union models and content/examples
don't accumulate duplicate $ref entries in anyOf arrays.
See https://github.com/fastapi/fastapi/pull/14463
"""
from typing import Union
from fastapi import FastAPI
from fastapi.testclient import TestClient
from pydantic import BaseModel
class ModelA(BaseModel):
a: str
class ModelB(BaseModel):
b: str
app = FastAPI(
responses={
500: {
"model": Union[ModelA, ModelB],
"content": {"application/json": {"examples": {"Case A": {"value": "a"}}}},
}
}
)
@app.get("/route1")
async def route1():
pass # pragma: no cover
@app.get("/route2")
async def route2():
pass # pragma: no cover
client = TestClient(app)
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/route1": {
"get": {
"summary": "Route1",
"operationId": "route1_route1_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"500": {
"description": "Internal Server Error",
"content": {
"application/json": {
"schema": {
"anyOf": [
{"$ref": "#/components/schemas/ModelA"},
{"$ref": "#/components/schemas/ModelB"},
],
"title": "Response 500 Route1 Route1 Get",
},
"examples": {"Case A": {"value": "a"}},
}
},
},
},
}
},
"/route2": {
"get": {
"summary": "Route2",
"operationId": "route2_route2_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"500": {
"description": "Internal Server Error",
"content": {
"application/json": {
"schema": {
"anyOf": [
{"$ref": "#/components/schemas/ModelA"},
{"$ref": "#/components/schemas/ModelB"},
],
"title": "Response 500 Route2 Route2 Get",
},
"examples": {"Case A": {"value": "a"}},
}
},
},
},
}
},
},
"components": {
"schemas": {
"ModelA": {
"properties": {"a": {"type": "string", "title": "A"}},
"type": "object",
"required": ["a"],
"title": "ModelA",
},
"ModelB": {
"properties": {"b": {"type": "string", "title": "B"}},
"type": "object",
"required": ["b"],
"title": "ModelB",
},
}
},
}