From 6a657f360db5a666c75ba143e0d923ad05789b1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Wed, 29 Oct 2025 10:09:30 -0300 Subject: [PATCH 1/9] =?UTF-8?q?=F0=9F=90=9B=20Fix=20separation=20of=20sche?= =?UTF-8?q?mas=20with=20nested=20models=20introduced=20in=200.119.0=20(#14?= =?UTF-8?q?246)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fastapi/_compat/v2.py | 28 ++- tests/test_no_schema_split.py | 203 ++++++++++++++++++ .../test_multifile.py | 15 +- 3 files changed, 229 insertions(+), 17 deletions(-) create mode 100644 tests/test_no_schema_split.py diff --git a/fastapi/_compat/v2.py b/fastapi/_compat/v2.py index fb2c691d8..6a87b9ae9 100644 --- a/fastapi/_compat/v2.py +++ b/fastapi/_compat/v2.py @@ -207,11 +207,31 @@ def get_definitions( override_mode: Union[Literal["validation"], None] = ( None if separate_input_output_schemas else "validation" ) - flat_models = get_flat_models_from_fields(fields, known_models=set()) - flat_model_fields = [ - ModelField(field_info=FieldInfo(annotation=model), name=model.__name__) - for model in flat_models + validation_fields = [field for field in fields if field.mode == "validation"] + serialization_fields = [field for field in fields if field.mode == "serialization"] + flat_validation_models = get_flat_models_from_fields( + validation_fields, known_models=set() + ) + flat_serialization_models = get_flat_models_from_fields( + serialization_fields, known_models=set() + ) + flat_validation_model_fields = [ + ModelField( + field_info=FieldInfo(annotation=model), + name=model.__name__, + mode="validation", + ) + for model in flat_validation_models + ] + flat_serialization_model_fields = [ + ModelField( + field_info=FieldInfo(annotation=model), + name=model.__name__, + mode="serialization", + ) + for model in flat_serialization_models ] + flat_model_fields = flat_validation_model_fields + flat_serialization_model_fields input_types = {f.type_ for f in fields} unique_flat_model_fields = { f for f in flat_model_fields if f.type_ not in input_types diff --git a/tests/test_no_schema_split.py b/tests/test_no_schema_split.py new file mode 100644 index 000000000..b0b5958c1 --- /dev/null +++ b/tests/test_no_schema_split.py @@ -0,0 +1,203 @@ +# Test with parts from, and to verify the report in: +# https://github.com/fastapi/fastapi/discussions/14177 +# Made an issue in: +# https://github.com/fastapi/fastapi/issues/14247 +from enum import Enum +from typing import List + +from fastapi import FastAPI +from fastapi.testclient import TestClient +from inline_snapshot import snapshot +from pydantic import BaseModel, Field + +from tests.utils import pydantic_snapshot + + +class MessageEventType(str, Enum): + alpha = "alpha" + beta = "beta" + + +class MessageEvent(BaseModel): + event_type: MessageEventType = Field(default=MessageEventType.alpha) + output: str + + +class MessageOutput(BaseModel): + body: str = "" + events: List[MessageEvent] = [] + + +class Message(BaseModel): + input: str + output: MessageOutput + + +app = FastAPI(title="Minimal FastAPI App", version="1.0.0") + + +@app.post("/messages", response_model=Message) +async def create_message(input_message: str) -> Message: + return Message( + input=input_message, + output=MessageOutput(body=f"Processed: {input_message}"), + ) + + +client = TestClient(app) + + +def test_create_message(): + response = client.post("/messages", params={"input_message": "Hello"}) + assert response.status_code == 200, response.text + assert response.json() == { + "input": "Hello", + "output": {"body": "Processed: Hello", "events": []}, + } + + +def test_openapi_schema(): + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + assert response.json() == snapshot( + { + "openapi": "3.1.0", + "info": {"title": "Minimal FastAPI App", "version": "1.0.0"}, + "paths": { + "/messages": { + "post": { + "summary": "Create Message", + "operationId": "create_message_messages_post", + "parameters": [ + { + "name": "input_message", + "in": "query", + "required": True, + "schema": {"type": "string", "title": "Input Message"}, + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + } + } + }, + "components": { + "schemas": { + "HTTPValidationError": { + "properties": { + "detail": { + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + "type": "array", + "title": "Detail", + } + }, + "type": "object", + "title": "HTTPValidationError", + }, + "Message": { + "properties": { + "input": {"type": "string", "title": "Input"}, + "output": {"$ref": "#/components/schemas/MessageOutput"}, + }, + "type": "object", + "required": ["input", "output"], + "title": "Message", + }, + "MessageEvent": { + "properties": { + "event_type": pydantic_snapshot( + v2=snapshot( + { + "$ref": "#/components/schemas/MessageEventType", + "default": "alpha", + } + ), + v1=snapshot( + { + "allOf": [ + { + "$ref": "#/components/schemas/MessageEventType" + } + ], + "default": "alpha", + } + ), + ), + "output": {"type": "string", "title": "Output"}, + }, + "type": "object", + "required": ["output"], + "title": "MessageEvent", + }, + "MessageEventType": pydantic_snapshot( + v2=snapshot( + { + "type": "string", + "enum": ["alpha", "beta"], + "title": "MessageEventType", + } + ), + v1=snapshot( + { + "type": "string", + "enum": ["alpha", "beta"], + "title": "MessageEventType", + "description": "An enumeration.", + } + ), + ), + "MessageOutput": { + "properties": { + "body": {"type": "string", "title": "Body", "default": ""}, + "events": { + "items": {"$ref": "#/components/schemas/MessageEvent"}, + "type": "array", + "title": "Events", + "default": [], + }, + }, + "type": "object", + "title": "MessageOutput", + }, + "ValidationError": { + "properties": { + "loc": { + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + "type": "array", + "title": "Location", + }, + "msg": {"type": "string", "title": "Message"}, + "type": {"type": "string", "title": "Error Type"}, + }, + "type": "object", + "required": ["loc", "msg", "type"], + "title": "ValidationError", + }, + } + }, + } + ) diff --git a/tests/test_pydantic_v1_v2_multifile/test_multifile.py b/tests/test_pydantic_v1_v2_multifile/test_multifile.py index 4472bd73e..e66d102fb 100644 --- a/tests/test_pydantic_v1_v2_multifile/test_multifile.py +++ b/tests/test_pydantic_v1_v2_multifile/test_multifile.py @@ -1028,17 +1028,6 @@ def test_openapi_schema(): "type": "object", "title": "HTTPValidationError", }, - "SubItem-Output": { - "properties": { - "new_sub_name": { - "type": "string", - "title": "New Sub Name", - } - }, - "type": "object", - "required": ["new_sub_name"], - "title": "SubItem", - }, "ValidationError": { "properties": { "loc": { @@ -1113,11 +1102,11 @@ def test_openapi_schema(): "title": "New Description", }, "new_sub": { - "$ref": "#/components/schemas/SubItem-Output" + "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__SubItem" }, "new_multi": { "items": { - "$ref": "#/components/schemas/SubItem-Output" + "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__SubItem" }, "type": "array", "title": "New Multi", From c01b5dd96f2b201dd3e97738d92a16f7f1278482 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 29 Oct 2025 13:09:57 +0000 Subject: [PATCH 2/9] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index fb4c26701..f6158beee 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -7,6 +7,10 @@ hide: ## Latest Changes +### Fixes + +* 🐛 Fix separation of schemas with nested models introduced in 0.119.0. PR [#14246](https://github.com/fastapi/fastapi/pull/14246) by [@tiangolo](https://github.com/tiangolo). + ### Internal * ⬆ Bump actions/download-artifact from 5 to 6. PR [#14236](https://github.com/fastapi/fastapi/pull/14236) by [@dependabot[bot]](https://github.com/apps/dependabot). From 35aa12b9bd638eb9f7b7d0be2a8439831df61899 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Wed, 29 Oct 2025 10:43:11 -0300 Subject: [PATCH 3/9] =?UTF-8?q?=F0=9F=94=A7=20Add=20sponsor:=20SerpApi=20(?= =?UTF-8?q?#14248)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + docs/en/data/sponsors.yml | 3 +++ docs/en/docs/img/sponsors/serpapi-banner.png | Bin 0 -> 6458 bytes docs/en/docs/img/sponsors/serpapi.png | Bin 0 -> 10694 bytes docs/en/overrides/main.html | 6 ++++++ 5 files changed, 10 insertions(+) create mode 100644 docs/en/docs/img/sponsors/serpapi-banner.png create mode 100644 docs/en/docs/img/sponsors/serpapi.png diff --git a/README.md b/README.md index 4fd87298e..09cd38da1 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,7 @@ The key features are: + diff --git a/docs/en/data/sponsors.yml b/docs/en/data/sponsors.yml index 7a015e404..943b92adb 100644 --- a/docs/en/data/sponsors.yml +++ b/docs/en/data/sponsors.yml @@ -26,6 +26,9 @@ gold: - url: https://docs.railway.com/guides/fastapi?utm_medium=integration&utm_source=docs&utm_campaign=fastapi title: Deploy enterprise applications at startup speed img: https://fastapi.tiangolo.com/img/sponsors/railway.png + - url: https://serpapi.com/?utm_source=fastapi_website + title: "SerpApi: Web Search API" + img: https://fastapi.tiangolo.com/img/sponsors/serpapi.png silver: - url: https://databento.com/?utm_source=fastapi&utm_medium=sponsor&utm_content=display title: Pay as you go for market data diff --git a/docs/en/docs/img/sponsors/serpapi-banner.png b/docs/en/docs/img/sponsors/serpapi-banner.png new file mode 100644 index 0000000000000000000000000000000000000000..3c3fd629eb08f6e174d83e675e0fa086cb2271d6 GIT binary patch literal 6458 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 UcVKxRT>t<807*qoM6N<$f)*jfDgXcg literal 0 HcmV?d00001 diff --git a/docs/en/docs/img/sponsors/serpapi.png b/docs/en/docs/img/sponsors/serpapi.png new file mode 100644 index 0000000000000000000000000000000000000000..d7258ef70a4f326d24ea4d607534feaef2039a84 GIT binary patch literal 10694 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¢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 literal 0 HcmV?d00001 diff --git a/docs/en/overrides/main.html b/docs/en/overrides/main.html index c7ffaef5d..be31bd75c 100644 --- a/docs/en/overrides/main.html +++ b/docs/en/overrides/main.html @@ -80,6 +80,12 @@ + {% endblock %} From d2a703d5cc45df171cae50747293e1e17fbe3261 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 29 Oct 2025 13:43:35 +0000 Subject: [PATCH 4/9] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index f6158beee..53b3393f6 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -13,6 +13,7 @@ hide: ### Internal +* 🔧 Add sponsor: SerpApi. PR [#14248](https://github.com/fastapi/fastapi/pull/14248) by [@tiangolo](https://github.com/tiangolo). * ⬆ Bump actions/download-artifact from 5 to 6. PR [#14236](https://github.com/fastapi/fastapi/pull/14236) by [@dependabot[bot]](https://github.com/apps/dependabot). * ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#14237](https://github.com/fastapi/fastapi/pull/14237) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci). * ⬆ Bump actions/upload-artifact from 4 to 5. PR [#14235](https://github.com/fastapi/fastapi/pull/14235) by [@dependabot[bot]](https://github.com/apps/dependabot). From 22ccca21fc13b7ac138e277490ec4d05b4f5094a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Wed, 29 Oct 2025 14:44:41 +0100 Subject: [PATCH 5/9] =?UTF-8?q?=F0=9F=94=96=20Release=20version=200.120.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 2 ++ fastapi/__init__.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 53b3393f6..e2d1514c7 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -7,6 +7,8 @@ hide: ## Latest Changes +## 0.120.2 + ### Fixes * 🐛 Fix separation of schemas with nested models introduced in 0.119.0. PR [#14246](https://github.com/fastapi/fastapi/pull/14246) by [@tiangolo](https://github.com/tiangolo). diff --git a/fastapi/__init__.py b/fastapi/__init__.py index 787c52dda..a4c17a6bd 100644 --- a/fastapi/__init__.py +++ b/fastapi/__init__.py @@ -1,6 +1,6 @@ """FastAPI framework, high performance, easy to learn, fast to code, ready for production""" -__version__ = "0.120.1" +__version__ = "0.120.2" from starlette import status as status From c144f9fbd356e7e378a6b42cff68cf4a4667111a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Thu, 30 Oct 2025 01:51:50 -0300 Subject: [PATCH 6/9] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor=20internals?= =?UTF-8?q?=20of=20dependencies,=20simplify=20using=20dataclasses=20(#1425?= =?UTF-8?q?4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fastapi/dependencies/utils.py | 4 ++-- fastapi/params.py | 25 ++++++------------------- tests/test_params_repr.py | 11 +---------- 3 files changed, 9 insertions(+), 31 deletions(-) diff --git a/fastapi/dependencies/utils.py b/fastapi/dependencies/utils.py index aa06dd2a9..e13b53095 100644 --- a/fastapi/dependencies/utils.py +++ b/fastapi/dependencies/utils.py @@ -160,8 +160,8 @@ def get_sub_dependant( security_requirement = None security_scopes = security_scopes or [] if isinstance(depends, params.Security): - dependency_scopes = depends.scopes - security_scopes.extend(dependency_scopes) + if depends.scopes: + security_scopes.extend(depends.scopes) if isinstance(dependency, SecurityBase): use_scopes: List[str] = [] if isinstance(dependency, (OAuth2, OpenIdConnect)): diff --git a/fastapi/params.py b/fastapi/params.py index e85375018..2dc04be14 100644 --- a/fastapi/params.py +++ b/fastapi/params.py @@ -1,4 +1,5 @@ import warnings +from dataclasses import dataclass from enum import Enum from typing import Any, Callable, Dict, List, Optional, Sequence, Union @@ -761,26 +762,12 @@ class File(Form): # type: ignore[misc] ) +@dataclass class Depends: - def __init__( - self, dependency: Optional[Callable[..., Any]] = None, *, use_cache: bool = True - ): - self.dependency = dependency - self.use_cache = use_cache - - def __repr__(self) -> str: - attr = getattr(self.dependency, "__name__", type(self.dependency).__name__) - cache = "" if self.use_cache else ", use_cache=False" - return f"{self.__class__.__name__}({attr}{cache})" + dependency: Optional[Callable[..., Any]] = None + use_cache: bool = True +@dataclass class Security(Depends): - def __init__( - self, - dependency: Optional[Callable[..., Any]] = None, - *, - scopes: Optional[Sequence[str]] = None, - use_cache: bool = True, - ): - super().__init__(dependency=dependency, use_cache=use_cache) - self.scopes = scopes or [] + scopes: Optional[Sequence[str]] = None diff --git a/tests/test_params_repr.py b/tests/test_params_repr.py index bfc7bed09..baa172497 100644 --- a/tests/test_params_repr.py +++ b/tests/test_params_repr.py @@ -1,7 +1,7 @@ from typing import Any, List from dirty_equals import IsOneOf -from fastapi.params import Body, Cookie, Depends, Header, Param, Path, Query +from fastapi.params import Body, Cookie, Header, Param, Path, Query test_data: List[Any] = ["teststr", None, ..., 1, []] @@ -141,12 +141,3 @@ def test_body_repr_number(): def test_body_repr_list(): assert repr(Body([])) == "Body([])" - - -def test_depends_repr(): - assert repr(Depends()) == "Depends(NoneType)" - assert repr(Depends(get_user)) == "Depends(get_user)" - assert repr(Depends(use_cache=False)) == "Depends(NoneType, use_cache=False)" - assert ( - repr(Depends(get_user, use_cache=False)) == "Depends(get_user, use_cache=False)" - ) From 9d1a384f4f904aaf72be5a73a2f721f92fad3e9e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 30 Oct 2025 04:52:12 +0000 Subject: [PATCH 7/9] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index e2d1514c7..30c645e6c 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -7,6 +7,10 @@ hide: ## Latest Changes +### Refactors + +* ♻️ Refactor internals of dependencies, simplify using dataclasses. PR [#14254](https://github.com/fastapi/fastapi/pull/14254) by [@tiangolo](https://github.com/tiangolo). + ## 0.120.2 ### Fixes From bb88a0f94a9633b861f90f6752e397980a7cfea9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Thu, 30 Oct 2025 01:58:49 -0300 Subject: [PATCH 8/9] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor=20internals?= =?UTF-8?q?=20of=20dependencies,=20simplify=20code=20and=20remove=20`get?= =?UTF-8?q?=5Fparam=5Fsub=5Fdependant`=20(#14255)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fastapi/dependencies/utils.py | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/fastapi/dependencies/utils.py b/fastapi/dependencies/utils.py index e13b53095..18f6a234e 100644 --- a/fastapi/dependencies/utils.py +++ b/fastapi/dependencies/utils.py @@ -125,23 +125,6 @@ def ensure_multipart_is_installed() -> None: raise RuntimeError(multipart_not_installed_error) from None -def get_param_sub_dependant( - *, - param_name: str, - depends: params.Depends, - path: str, - security_scopes: Optional[List[str]] = None, -) -> Dependant: - assert depends.dependency - return get_sub_dependant( - depends=depends, - dependency=depends.dependency, - path=path, - name=param_name, - security_scopes=security_scopes, - ) - - def get_parameterless_sub_dependant(*, depends: params.Depends, path: str) -> Dependant: assert callable(depends.dependency), ( "A parameter-less dependency must have a callable dependency" @@ -282,9 +265,6 @@ def get_dependant( security_scopes: Optional[List[str]] = None, use_cache: bool = True, ) -> Dependant: - path_param_names = get_path_param_names(path) - endpoint_signature = get_typed_signature(call) - signature_params = endpoint_signature.parameters dependant = Dependant( call=call, name=name, @@ -292,6 +272,9 @@ def get_dependant( security_scopes=security_scopes, use_cache=use_cache, ) + path_param_names = get_path_param_names(path) + endpoint_signature = get_typed_signature(call) + signature_params = endpoint_signature.parameters for param_name, param in signature_params.items(): is_path_param = param_name in path_param_names param_details = analyze_param( @@ -301,10 +284,12 @@ def get_dependant( is_path_param=is_path_param, ) if param_details.depends is not None: - sub_dependant = get_param_sub_dependant( - param_name=param_name, + assert param_details.depends.dependency + sub_dependant = get_sub_dependant( depends=param_details.depends, + dependency=param_details.depends.dependency, path=path, + name=param_name, security_scopes=security_scopes, ) dependant.dependencies.append(sub_dependant) From 1fc586c3a5ae457457c5c70adddb07c7e9185f16 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 30 Oct 2025 04:59:14 +0000 Subject: [PATCH 9/9] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 30c645e6c..bbcbc74b7 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -9,6 +9,7 @@ hide: ### Refactors +* ♻️ Refactor internals of dependencies, simplify code and remove `get_param_sub_dependant`. PR [#14255](https://github.com/fastapi/fastapi/pull/14255) by [@tiangolo](https://github.com/tiangolo). * ♻️ Refactor internals of dependencies, simplify using dataclasses. PR [#14254](https://github.com/fastapi/fastapi/pull/14254) by [@tiangolo](https://github.com/tiangolo). ## 0.120.2