From bc533c983d7ac431033d6e112324a3bd9fbe55f8 Mon Sep 17 00:00:00 2001 From: Arseny Leontev Date: Sat, 24 Sep 2022 01:20:32 +0400 Subject: [PATCH 1/6] =?UTF-8?q?=E2=9C=A8Remove=20empty=20responses?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fastapi/openapi/utils.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/fastapi/openapi/utils.py b/fastapi/openapi/utils.py index 86e15b46d..71429e913 100644 --- a/fastapi/openapi/utils.py +++ b/fastapi/openapi/utils.py @@ -193,6 +193,7 @@ def get_openapi_path( *, route: routing.APIRoute, model_name_map: Dict[type, str], operation_ids: Set[str] ) -> Tuple[Dict[str, Any], Dict[str, Any], Dict[str, Any]]: path = {} + empty_responses: List[int] = [] security_schemes: Dict[str, Any] = {} definitions: Dict[str, Any] = {} assert route.methods is not None, "Methods must be a list" @@ -296,7 +297,7 @@ def get_openapi_path( additional_response, ) in route.responses.items(): process_response = additional_response.copy() - process_response.pop("model", None) + process_rseponse_model = process_response.pop("model", None) status_code_key = str(additional_status_code).upper() if status_code_key == "DEFAULT": status_code_key = "default" @@ -306,6 +307,8 @@ def get_openapi_path( assert isinstance( process_response, dict ), "An additional response must be a dict" + if not process_response and not process_rseponse_model: + empty_responses.append(additional_status_code) field = route.response_fields.get(additional_status_code) additional_field_schema: Optional[Dict[str, Any]] = None if field: @@ -354,6 +357,9 @@ def get_openapi_path( ) if route.openapi_extra: deep_dict_update(operation, route.openapi_extra) + if empty_responses: + for response_status_code in empty_responses: + del operation["responses"][str(response_status_code)] path[method.lower()] = operation return path, security_schemes, definitions From 468c9d0dce03ad991b776692dfa29a794926ef9e Mon Sep 17 00:00:00 2001 From: Arseny Leontev Date: Sat, 24 Sep 2022 01:46:23 +0400 Subject: [PATCH 2/6] Fix type hint --- fastapi/openapi/utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fastapi/openapi/utils.py b/fastapi/openapi/utils.py index 71429e913..951027e5a 100644 --- a/fastapi/openapi/utils.py +++ b/fastapi/openapi/utils.py @@ -193,7 +193,7 @@ def get_openapi_path( *, route: routing.APIRoute, model_name_map: Dict[type, str], operation_ids: Set[str] ) -> Tuple[Dict[str, Any], Dict[str, Any], Dict[str, Any]]: path = {} - empty_responses: List[int] = [] + empty_responses: List[str] = [] security_schemes: Dict[str, Any] = {} definitions: Dict[str, Any] = {} assert route.methods is not None, "Methods must be a list" @@ -308,7 +308,7 @@ def get_openapi_path( process_response, dict ), "An additional response must be a dict" if not process_response and not process_rseponse_model: - empty_responses.append(additional_status_code) + empty_responses.append(str(additional_status_code)) field = route.response_fields.get(additional_status_code) additional_field_schema: Optional[Dict[str, Any]] = None if field: @@ -359,7 +359,7 @@ def get_openapi_path( deep_dict_update(operation, route.openapi_extra) if empty_responses: for response_status_code in empty_responses: - del operation["responses"][str(response_status_code)] + del operation["responses"][response_status_code] path[method.lower()] = operation return path, security_schemes, definitions From 4f8d765e5966d946b14387599f9fa7a092c49254 Mon Sep 17 00:00:00 2001 From: Arseny Leontev Date: Sat, 24 Sep 2022 03:10:36 +0400 Subject: [PATCH 3/6] Test empty responses value --- tests/test_additional_response_empty.py | 69 +++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 tests/test_additional_response_empty.py diff --git a/tests/test_additional_response_empty.py b/tests/test_additional_response_empty.py new file mode 100644 index 000000000..097030381 --- /dev/null +++ b/tests/test_additional_response_empty.py @@ -0,0 +1,69 @@ +from typing import Dict + +from fastapi import FastAPI +from fastapi.testclient import TestClient +from pydantic import BaseModel + +app = FastAPI() + + +class Items(BaseModel): + items: Dict[str, int] + + +@app.post("/foo", responses={422: {}}) +def foo(items: Items): + return items.items + + +client = TestClient(app) + + +openapi_schema = { + "openapi": "3.0.2", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/foo": { + "post": { + "summary": "Foo", + "operationId": "foo_foo_post", + "requestBody": { + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/Items"} + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + } + }, + } + } + }, + "components": { + "schemas": { + "Items": { + "title": "Items", + "required": ["items"], + "type": "object", + "properties": { + "items": { + "title": "Items", + "type": "object", + "additionalProperties": {"type": "integer"}, + } + }, + } + } + }, +} + + +def test_additional_properties_schema(): + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + assert response.json() == openapi_schema From a73499ecca666969f5a07319105b6cb72dff7525 Mon Sep 17 00:00:00 2001 From: Arseny Leontev Date: Sat, 24 Sep 2022 03:19:42 +0400 Subject: [PATCH 4/6] fix coverage --- tests/test_additional_response_empty.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_additional_response_empty.py b/tests/test_additional_response_empty.py index 097030381..c5c8fa3df 100644 --- a/tests/test_additional_response_empty.py +++ b/tests/test_additional_response_empty.py @@ -13,7 +13,7 @@ class Items(BaseModel): @app.post("/foo", responses={422: {}}) def foo(items: Items): - return items.items + pass # pragma: no cover client = TestClient(app) From 8f8779dd812a5b46220d461ad2a2b4820c55984e Mon Sep 17 00:00:00 2001 From: Arseny Leontev Date: Sat, 24 Sep 2022 21:23:55 +0400 Subject: [PATCH 5/6] Typo fix --- fastapi/openapi/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fastapi/openapi/utils.py b/fastapi/openapi/utils.py index 951027e5a..8cd21a63e 100644 --- a/fastapi/openapi/utils.py +++ b/fastapi/openapi/utils.py @@ -297,7 +297,7 @@ def get_openapi_path( additional_response, ) in route.responses.items(): process_response = additional_response.copy() - process_rseponse_model = process_response.pop("model", None) + process_response_model = process_response.pop("model", None) status_code_key = str(additional_status_code).upper() if status_code_key == "DEFAULT": status_code_key = "default" @@ -307,7 +307,7 @@ def get_openapi_path( assert isinstance( process_response, dict ), "An additional response must be a dict" - if not process_response and not process_rseponse_model: + if not process_response and not process_response_model: empty_responses.append(str(additional_status_code)) field = route.response_fields.get(additional_status_code) additional_field_schema: Optional[Dict[str, Any]] = None From bcac4cc1a808c91dfadcf033712aed13fff183b5 Mon Sep 17 00:00:00 2001 From: Yurii Motov Date: Thu, 10 Jul 2025 22:06:07 +0200 Subject: [PATCH 6/6] Fix openapi version in test --- tests/test_additional_response_empty.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_additional_response_empty.py b/tests/test_additional_response_empty.py index c5c8fa3df..628386cbd 100644 --- a/tests/test_additional_response_empty.py +++ b/tests/test_additional_response_empty.py @@ -20,7 +20,7 @@ client = TestClient(app) openapi_schema = { - "openapi": "3.0.2", + "openapi": "3.1.0", "info": {"title": "FastAPI", "version": "0.1.0"}, "paths": { "/foo": {