← pod_r3_v1g

fastapi_13207

failed CONTEXT UNSUBMITTED agent_error · 30 tool calls · 220 s · fastapi/fastapi

Task input

🐛 Fix OpenAPI schema support for computed fields when using `separate_input_output_schemas=False`

This demonstrates how the output schema differs when the FastAPI app runs with different values for `separate_input_output_schemas` - namely, the computed fields are omitted.

Initially reported here: https://github.com/fastapi/fastapi/discussions/10342

Tool calls (30)

#ToolArgumentsResult
1run_command
{
 "command": "grep -rn \"separate_input_output_schemas\" . | head -20"
}
{"status": "ok", "stdout": "./tests/test_openapi_separate_input_output_schemas.py:27:def get_app_client(separate_input_output_schemas: bool = True) -> TestClien
full
{"status": "ok", "stdout": "./tests/test_openapi_separate_input_output_schemas.py:27:def get_app_client(separate_input_output_schemas: bool = True) -> TestClient:\n./tests/test_openapi_separate_input_output_schemas.py:28:    app = FastAPI(separate_input_output_schemas=separate_input_output_schemas)\n./tests/test_openapi_separate_input_output_schemas.py:55:    client_no = get_app_client(separate_input_output_schemas=False)\n./tests/test_openapi_separate_input_output_schemas.py:68:    client_no = get_app_client(separate_input_output_schemas=False)\n./tests/test_openapi_separate_input_output_schemas.py:89:    client_no = get_app_client(separate_input_output_schemas=False)\n./tests/test_openapi_separate_input_output_schemas.py:116:    client_no = get_app_client(separate_input_output_schemas=False)\n./tests/test_openapi_separate_input_output_schemas.py:360:    client = get_app_client(separate_input_output_schemas=False)\n./docs/ru/docs/how-to/separate-openapi-schemas.md:86:\u0412 \u0442\u0430\u043a\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043e\u0442\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u044d\u0442\u0443 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0432 FastAPI \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430 `separate_input_output_schemas=False`.\n./docs/ru/docs/how-to/separate-openapi-schemas.md:90:\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 `separate_input_output_schemas` \u043f\u043e\u044f\u0432\u0438\u043b\u0430\u0441\u044c \u0432 FastAPI `0.102.0`. \ud83e\udd13\n./docs/pt/docs/how-to/separate-openapi-schemas.md:86:Nesse caso, voc\u00ea pode desativar esse recurso no **FastAPI**, com o par\u00e2metro `separate_input_output_schemas=False`.\n./docs/pt/docs/how-to/separate-openapi-schemas.md:90:O suporte para `separate_input_output_schemas` foi adicionado no FastAPI `0.102.0`. \ud83e\ud
2run_command
{
 "command": "grep -rn \"separate_input_output_schemas\" fastapi/ | grep \".py\""
}
{"status": "ok", "stdout": "fastapi/applications.py:791: separate_input_output_schemas: Annotated[\nfastapi/applications.py:875: self.separate_input_output_sche
full
{"status": "ok", "stdout": "fastapi/applications.py:791:        separate_input_output_schemas: Annotated[\nfastapi/applications.py:875:        self.separate_input_output_schemas = separate_input_output_schemas\nfastapi/applications.py:1078:                separate_input_output_schemas=self.separate_input_output_schemas,\nfastapi/openapi/utils.py:102:    separate_input_output_schemas: bool = True,\nfastapi/openapi/utils.py:133:                separate_input_output_schemas=separate_input_output_schemas,\nfastapi/openapi/utils.py:175:    separate_input_output_schemas: bool = True,\nfastapi/openapi/utils.py:184:        separate_input_output_schemas=separate_input_output_schemas,\nfastapi/openapi/utils.py:258:    separate_input_output_schemas: bool = True,\nfastapi/openapi/utils.py:288:                separate_input_output_schemas=separate_input_output_schemas,\nfastapi/openapi/utils.py:309:                    separate_input_output_schemas=separate_input_output_schemas,\nfastapi/openapi/utils.py:326:                            separate_input_output_schemas=separate_input_output_schemas,\nfastapi/openapi/utils.py:356:                            separate_input_output_schemas=separate_input_output_schemas,\nfastapi/openapi/utils.py:389:                            separate_input_output_schemas=separate_input_output_schemas,\nfastapi/openapi/utils.py:481:    separate_input_output_schemas: bool = True,\nfastapi/openapi/utils.py:507:        separate_input_output_schemas=separate_input_output_schemas,\nfastapi/openapi/utils.py:516:                separate_input_output_schemas=separate_input_output_schemas,\nfastapi/openapi/utils.py:535:                separate_input_output_schemas=separate_input_output_schemas,\nfastapi/_compat/v1.py:243:    separate_input_output_schemas: bool = True,\nfastapi/_compat/v1.py:259:    separate_input_output_schemas: bool = True,\nfastapi/_compat/may_v1.py:65:        separate_input_output_schemas: bool = True,\nfastapi/_compat/main.py:271:    separat
3run_command
{
 "command": "cat <<'PY' > /tmp/repro.py\nfrom fastapi import FastAPI\nfrom pydantic import BaseModel, computed_field\nfrom fastapi.testclient import TestClient\nimport json\n\napp = FastAPI(separate_input_output_schemas=False)\n\nclass Item(BaseModel):\n    name: str\n    price: float\n\n    @computed_field\n    @property\n    def total_cost(self) -> float:\n        return self.price * 1.2\n\n@app.get(\"/item\", response_model=Item)\ndef get_item():\n    return Item(name=\"Test\", price=10.0)\n\nclient = TestClient(app)\n\ndef test_openapi_schema():\n    response = client.get(\"/openapi.json\")\n    schema = response.json()\n    \n    # Find the Item schema\n    components = schema.get(\"components\", {}).get(\"schemas\", {})\n    item_schema = components.get(\"Item\")\n    \n    if item_schema is None:\n        print(\"Item schema not found in OpenAPI JSON\")\n        return\n\n    properties = item_schema.get(\"properties\", {})\n    print(f\"Properties: {list(properties.keys())}\")\n    \n    if \"total_cost\" not in properties:\n        print(\"FAILED: total_cost is missing from the schema when separate_input_output_schemas=False\")\n    else:\n        print(\"SUCCESS: total_cost is present in the schema\")\n\nif __name__ == \"__main__\":\n    test_openapi_schema()\nPY\npython3 /tmp/repro.py\n"
}
{"status": "ok", "stdout": "Properties: ['name', 'price']\nFAILED: total_cost is missing from the schema when separate_input_output_schemas=False\n", "stderr":
full
{"status": "ok", "stdout": "Properties: ['name', 'price']\nFAILED: total_cost is missing from the schema when separate_input_output_schemas=False\n", "stderr": "", "exit_code": 0}
4run_command
{
 "command": "cat <<'PY' > /tmp/repro_separate.py\nfrom fastapi import FastAPI\nfrom pydantic import BaseModel, computed_field\nfrom fastapi.testclient import TestClient\nimport json\n\napp = FastAPI(separate_input_output_schemas=True)\n\nclass Item(BaseModel):\n    name: str\n    price: float\n\n    @computed_field\n    @property\n    def total_cost(self) -> float:\n        return self.price * 1.2\n\n@app.get(\"/item\", response_model=Item)\ndef get_item():\n    return Item(name=\"Test\", price=10.0)\n\nclient = TestClient(app)\n\ndef test_openapi_schema():\n    response = client.get(\"/openapi.json\")\n    schema = response.json()\n    \n    # Find the Item schema\n    components = schema.get(\"components\", {}).get(\"schemas\", {})\n    \n    # When separate_input_output_schemas=True, we expect Item and Item-item_get_response (or similar)\n    print(f\"Schemas: {list(components.keys())}\")\n    \n    # Look for any schema that might be the output schema for Item\n    found_total_cost = False\n    for schema_name, schema_val in components.items():\n        properties = schema_val.get(\"properties\", {})\n        if \"total_cost\" in properties:\n            print(f\"Found total_cost in schema: {schema_name}\")\n            found_total_cost = True\n    \n    if not found_total_cost:\n        print(\"FAILED: total_cost is missing from all schemas when separate_input_output_schemas=True\")\n    else:\n        print(\"SUCCESS: total_cost is present in at least one schema\")\n\nif __name__ == \"__main__\":\n    test_openapi_schema()\nPY\npython3 /tmp/repro_separate.py\n"
}
{"status": "ok", "stdout": "Schemas: ['Item']\nFound total_cost in schema: Item\nSUCCESS: total_cost is present in at least one schema\n", "stderr": "", "exit_c
full
{"status": "ok", "stdout": "Schemas: ['Item']\nFound total_cost in schema: Item\nSUCCESS: total_cost is present in at least one schema\n", "stderr": "", "exit_code": 0}
5run_command
{
 "command": "sed -n '180,210p' fastapi/_compat/v2.py"
}
{"status": "ok", "stdout": " ],\n separate_input_output_schemas: bool = True,\n) -> Dict[str, Any]:\n override_mode: Union[Literal[\"validation\"], None] = (\n
full
{"status": "ok", "stdout": "    ],\n    separate_input_output_schemas: bool = True,\n) -> Dict[str, Any]:\n    override_mode: Union[Literal[\"validation\"], None] = (\n        None if separate_input_output_schemas else \"validation\"\n    )\n    # This expects that GenerateJsonSchema was already used to generate the definitions\n    json_schema = field_mapping[(field, override_mode or field.mode)]\n    if \"$ref\" not in json_schema:\n        # TODO remove when deprecating Pydantic v1\n        # Ref: https://github.com/pydantic/pydantic/blob/d61792cc42c80b13b23e3ffa74bc37ec7c77f7d1/pydantic/schema.py#L207\n        json_schema[\"title\"] = field.field_info.title or field.alias.title().replace(\n            \"_\", \" \"\n        )\n    return json_schema\n\n\ndef get_definitions(\n    *,\n    fields: Sequence[ModelField],\n    model_name_map: ModelNameMap,\n    separate_input_output_schemas: bool = True,\n) -> Tuple[\n    Dict[Tuple[ModelField, Literal[\"validation\", \"serialization\"]], JsonSchemaValue],\n    Dict[str, Dict[str, Any]],\n]:\n    schema_generator = GenerateJsonSchema(ref_template=REF_TEMPLATE)\n    override_mode: Union[Literal[\"validation\"], None] = (\n        None if separate_input_output_schemas else \"validation\"\n    )\n    validation_fields = [field for field in fields if field.mode == \"validation\"]\n", "stderr": "", "exit_code": 0}
6run_command
{
 "command": "sed -n '180,250p' fastapi/_compat/v2.py"
}
{"status": "ok", "stdout": " ],\n separate_input_output_schemas: bool = True,\n) -> Dict[str, Any]:\n override_mode: Union[Literal[\"validation\"], None] = (\n
full
{"status": "ok", "stdout": "    ],\n    separate_input_output_schemas: bool = True,\n) -> Dict[str, Any]:\n    override_mode: Union[Literal[\"validation\"], None] = (\n        None if separate_input_output_schemas else \"validation\"\n    )\n    # This expects that GenerateJsonSchema was already used to generate the definitions\n    json_schema = field_mapping[(field, override_mode or field.mode)]\n    if \"$ref\" not in json_schema:\n        # TODO remove when deprecating Pydantic v1\n        # Ref: https://github.com/pydantic/pydantic/blob/d61792cc42c80b13b23e3ffa74bc37ec7c77f7d1/pydantic/schema.py#L207\n        json_schema[\"title\"] = field.field_info.title or field.alias.title().replace(\n            \"_\", \" \"\n        )\n    return json_schema\n\n\ndef get_definitions(\n    *,\n    fields: Sequence[ModelField],\n    model_name_map: ModelNameMap,\n    separate_input_output_schemas: bool = True,\n) -> Tuple[\n    Dict[Tuple[ModelField, Literal[\"validation\", \"serialization\"]], JsonSchemaValue],\n    Dict[str, Dict[str, Any]],\n]:\n    schema_generator = GenerateJsonSchema(ref_template=REF_TEMPLATE)\n    override_mode: Union[Literal[\"validation\"], None] = (\n        None if separate_input_output_schemas else \"validation\"\n    )\n    validation_fields = [field for field in fields if field.mode == \"validation\"]\n    serialization_fields = [field for field in fields if field.mode == \"serialization\"]\n    flat_validation_models = get_flat_models_from_fields(\n        validation_fields, known_models=set()\n    )\n    flat_serialization_models = get_flat_models_from_fields(\n        serialization_fields, known_models=set()\n    )\n    flat_validation_model_fields = [\n        ModelField(\n            field_info=FieldInfo(annotation=model),\n            name=model.__name__,\n            mode=\"validation\",\n        )\n        for model in flat_validation_models\n    ]\n    flat_serialization_model_fields = [\n        ModelField(\n            field_info=Fi
7run_command
{
 "command": "sed -n '200,250p' fastapi/_compat/v2.py"
}
{"status": "ok", "stdout": " model_name_map: ModelNameMap,\n separate_input_output_schemas: bool = True,\n) -> Tuple[\n Dict[Tuple[ModelField, Literal[\"validat
full
{"status": "ok", "stdout": "    model_name_map: ModelNameMap,\n    separate_input_output_schemas: bool = True,\n) -> Tuple[\n    Dict[Tuple[ModelField, Literal[\"validation\", \"serialization\"]], JsonSchemaValue],\n    Dict[str, Dict[str, Any]],\n]:\n    schema_generator = GenerateJsonSchema(ref_template=REF_TEMPLATE)\n    override_mode: Union[Literal[\"validation\"], None] = (\n        None if separate_input_output_schemas else \"validation\"\n    )\n    validation_fields = [field for field in fields if field.mode == \"validation\"]\n    serialization_fields = [field for field in fields if field.mode == \"serialization\"]\n    flat_validation_models = get_flat_models_from_fields(\n        validation_fields, known_models=set()\n    )\n    flat_serialization_models = get_flat_models_from_fields(\n        serialization_fields, known_models=set()\n    )\n    flat_validation_model_fields = [\n        ModelField(\n            field_info=FieldInfo(annotation=model),\n            name=model.__name__,\n            mode=\"validation\",\n        )\n        for model in flat_validation_models\n    ]\n    flat_serialization_model_fields = [\n        ModelField(\n            field_info=FieldInfo(annotation=model),\n            name=model.__name__,\n            mode=\"serialization\",\n        )\n        for model in flat_serialization_models\n    ]\n    flat_model_fields = flat_validation_model_fields + flat_serialization_model_fields\n    input_types = {f.type_ for f in fields}\n    unique_flat_model_fields = {\n        f for f in flat_model_fields if f.type_ not in input_types\n    }\n\n    inputs = [\n        (field, override_mode or field.mode, field._type_adapter.core_schema)\n        for field in list(fields) + list(unique_flat_model_fields)\n    ]\n    field_mapping, definitions = schema_generator.generate_definitions(inputs=inputs)\n    for item_def in cast(Dict[str, Dict[str, Any]], definitions).values():\n        if \"description\" in item_def:\n            item_descr
8run_command
{
 "command": "cat <<'PY' > /tmp/repro.py\nfrom fastapi import FastAPI\nfrom pydantic import BaseModel, computed_field\nfrom fastapi.testclient import TestClient\nimport json\n\napp = FastAPI(separate_input_output_schemas=False)\n\nclass Item(BaseModel):\n    name: str\n    price: float\n\n    @computed_field\n    @property\n    def total_cost(self) -> float:\n        return self.price * 1.2\n\n@app.get(\"/item\", response_model=Item)\ndef get_item():\n    return Item(name=\"Test\", price=10.0)\n\nclient = TestClient(app)\n\ndef test_openapi_schema():\n    response = client.get(\"/openapi.json\")\n    schema = response.json()\n    \n    # Find the Item schema\n    components = schema.get(\"components\", {}).get(\"schemas\", {})\n    item_schema = components.get(\"Item\")\n    \n    if item_schema is None:\n        print(\"Item schema not found in OpenAPI JSON\")\n        return\n\n    properties = item_schema.get(\"properties\", {})\n    print(f\"Properties: {list(properties.keys())}\")\n    \n    if \"total_cost\" not in properties:\n        print(\"FAILED: total_cost is missing from the schema when separate_input_output_schemas=False\")\n    else:\n        print(\"SUCCESS: total_cost is present in the schema\")\n\nif __name__ == \"__main__\":\n    test_openapi_schema()\nPY\npython3 /tmp/repro.py\n"
}
{"status": "ok", "stdout": "Properties: ['name', 'price']\nFAILED: total_cost is missing from the schema when separate_input_output_schemas=False\n", "stderr":
full
{"status": "ok", "stdout": "Properties: ['name', 'price']\nFAILED: total_cost is missing from the schema when separate_input_output_schemas=False\n", "stderr": "", "exit_code": 0}
9run_command
{
 "command": "sed -n '180,210p' fastapi/_compat/v2.py"
}
{"status": "ok", "stdout": " ],\n separate_input_output_schemas: bool = True,\n) -> Dict[str, Any]:\n override_mode: Union[Literal[\"validation\"], None] = (\n
full
{"status": "ok", "stdout": "    ],\n    separate_input_output_schemas: bool = True,\n) -> Dict[str, Any]:\n    override_mode: Union[Literal[\"validation\"], None] = (\n        None if separate_input_output_schemas else \"validation\"\n    )\n    # This expects that GenerateJsonSchema was already used to generate the definitions\n    json_schema = field_mapping[(field, override_mode or field.mode)]\n    if \"$ref\" not in json_schema:\n        # TODO remove when deprecating Pydantic v1\n        # Ref: https://github.com/pydantic/pydantic/blob/d61792cc42c80b13b23e3ffa74bc37ec7c77f7d1/pydantic/schema.py#L207\n        json_schema[\"title\"] = field.field_info.title or field.alias.title().replace(\n            \"_\", \" \"\n        )\n    return json_schema\n\n\ndef get_definitions(\n    *,\n    fields: Sequence[ModelField],\n    model_name_map: ModelNameMap,\n    separate_input_output_schemas: bool = True,\n) -> Tuple[\n    Dict[Tuple[ModelField, Literal[\"validation\", \"serialization\"]], JsonSchemaValue],\n    Dict[str, Dict[str, Any]],\n]:\n    schema_generator = GenerateJsonSchema(ref_template=REF_TEMPLATE)\n    override_mode: Union[Literal[\"validation\"], None] = (\n        None if separate_input_output_schemas else \"validation\"\n    )\n    validation_fields = [field for field in fields if field.mode == \"validation\"]\n", "stderr": "", "exit_code": 0}
10run_command
{
 "command": "python3 - <<'PY'\nfrom fastapi._compat.v2 import get_definitions\nfrom pydantic import BaseModel, computed_field\nfrom typing import Sequence, Any, Dict, Tuple\nfrom fastapi._compat.v2 import ModelField, FieldInfo\n\nclass Item(BaseModel):\n    name: str\n    price: float\n    @computed_field\n    @property\n    def total_cost(self) -> float:\n        return self.price * 1.2\n\n# Mocking the necessary parts for get_definitions\n# This is hard because it depends on internal FastAPI/Pydantic structures.\n# Let's instead try to see what happens if we change override_mode to \"serialization\"\n# when separate_input_output_schemas=False.\nPY\n"
}
{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}
11run_command
{
 "command": "sed -n '180,210p' fastapi/_compat/v2.py"
}
{"status": "ok", "stdout": " ],\n separate_input_output_schemas: bool = True,\n) -> Dict[str, Any]:\n override_mode: Union[Literal[\"validation\"], None] = (\n
full
{"status": "ok", "stdout": "    ],\n    separate_input_output_schemas: bool = True,\n) -> Dict[str, Any]:\n    override_mode: Union[Literal[\"validation\"], None] = (\n        None if separate_input_output_schemas else \"validation\"\n    )\n    # This expects that GenerateJsonSchema was already used to generate the definitions\n    json_schema = field_mapping[(field, override_mode or field.mode)]\n    if \"$ref\" not in json_schema:\n        # TODO remove when deprecating Pydantic v1\n        # Ref: https://github.com/pydantic/pydantic/blob/d61792cc42c80b13b23e3ffa74bc37ec7c77f7d1/pydantic/schema.py#L207\n        json_schema[\"title\"] = field.field_info.title or field.alias.title().replace(\n            \"_\", \" \"\n        )\n    return json_schema\n\n\ndef get_definitions(\n    *,\n    fields: Sequence[ModelField],\n    model_name_map: ModelNameMap,\n    separate_input_output_schemas: bool = True,\n) -> Tuple[\n    Dict[Tuple[ModelField, Literal[\"validation\", \"serialization\"]], JsonSchemaValue],\n    Dict[str, Dict[str, Any]],\n]:\n    schema_generator = GenerateJsonSchema(ref_template=REF_TEMPLATE)\n    override_mode: Union[Literal[\"validation\"], None] = (\n        None if separate_input_output_schemas else \"validation\"\n    )\n    validation_fields = [field for field in fields if field.mode == \"validation\"]\n", "stderr": "", "exit_code": 0}
12run_command
{
 "command": "cd /workspace && python3 - <<'PY'\nfrom pathlib import Path\np = Path(\"fastapi/_compat/v2.py\")\ns = p.read_text()\nold = \"\"\"    override_mode: Union[Literal[\"validation\"], None] = (\n        None if separate_input_output_schemas else \"validation\"\n    )\"\"\"\nnew = \"\"\"    override_mode: Union[Literal[\"validation\"], None] = (\n        None if separate_input_output_schemas else \"serialization\"\n    )\"\"\"\n# We need to check if this occurs twice.\n# Looking at the sed output, it occurs in two functions.\n# One is likely get_schema_for_field (or similar) and the other is get_definitions.\n# Let's be more precise.\nPY\n"
}
{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}
13run_command
{
 "command": "grep -n \"override_mode: Union\\[Literal\\[\\\"validation\\\"\\], None\\] = (\" fastapi/_compat/v2.py"
}
{"status": "ok", "stdout": "183: override_mode: Union[Literal[\"validation\"], None] = (\n207: override_mode: Union[Literal[\"validation\"], None] = (\n", "stde
full
{"status": "ok", "stdout": "183:    override_mode: Union[Literal[\"validation\"], None] = (\n207:    override_mode: Union[Literal[\"validation\"], None] = (\n", "stderr": "", "exit_code": 0}
14run_command
{
 "command": "sed -n '180,190p' fastapi/_compat/v2.py"
}
{"status": "ok", "stdout": " ],\n separate_input_output_schemas: bool = True,\n) -> Dict[str, Any]:\n override_mode: Union[Literal[\"validation\"], None] = (\n
full
{"status": "ok", "stdout": "    ],\n    separate_input_output_schemas: bool = True,\n) -> Dict[str, Any]:\n    override_mode: Union[Literal[\"validation\"], None] = (\n        None if separate_input_output_schemas else \"validation\"\n    )\n    # This expects that GenerateJsonSchema was already used to generate the definitions\n    json_schema = field_mapping[(field, override_mode or field.mode)]\n    if \"$ref\" not in json_schema:\n        # TODO remove when deprecating Pydantic v1\n        # Ref: https://github.com/pydantic/pydantic/blob/d61792cc42c80b13b23e3ffa74bc37ec7c77f7d1/pydantic/schema.py#L207\n", "stderr": "", "exit_code": 0}
15run_command
{
 "command": "sed -n '200,215p' fastapi/_compat/v2.py"
}
{"status": "ok", "stdout": " model_name_map: ModelNameMap,\n separate_input_output_schemas: bool = True,\n) -> Tuple[\n Dict[Tuple[ModelField, Literal[\"validat
full
{"status": "ok", "stdout": "    model_name_map: ModelNameMap,\n    separate_input_output_schemas: bool = True,\n) -> Tuple[\n    Dict[Tuple[ModelField, Literal[\"validation\", \"serialization\"]], JsonSchemaValue],\n    Dict[str, Dict[str, Any]],\n]:\n    schema_generator = GenerateJsonSchema(ref_template=REF_TEMPLATE)\n    override_mode: Union[Literal[\"validation\"], None] = (\n        None if separate_input_output_schemas else \"validation\"\n    )\n    validation_fields = [field for field in fields if field.mode == \"validation\"]\n    serialization_fields = [field for field in fields if field.mode == \"serialization\"]\n    flat_validation_models = get_flat_models_from_fields(\n        validation_fields, known_models=set()\n    )\n    flat_serialization_models = get_flat_models_from_fields(\n", "stderr": "", "exit_code": 0}
16run_command
{
 "command": "cd /workspace && python3 - <<'PY'\nfrom pathlib import Path\np = Path(\"fastapi/_compat/v2.py\")\ns = p.read_text()\n\n# First occurrence: in a function that returns a Dict[str, Any] (likely get_schema_for_field)\n# Second occurrence: in get_definitions\n\n# Let's replace both \"validation\" with \"serialization\" when separate_input_output_schemas is False.\n# But wait, if we use \"serialization\", we get computed fields.\n# If we use \"validation\", we don't.\n# When separate_input_output_schemas=False, we want the output schema to be the one used for the response.\n# The response schema should include computed fields.\n# In Pydantic v2, computed fields are part of the serialization schema, not the validation schema.\n\n# Let's try replacing both.\nold_text = 'None if separate_input_output_schemas else \"validation\"'\nnew_text = 'None if separate_input_output_schemas else \"serialization\"'\ns = s.replace(old_text, new_text)\np.write_text(s)\nPY\npython3 /tmp/repro.py\n"
}
{"status": "ok", "stdout": "Properties: ['name', 'price', 'total_cost']\nSUCCESS: total_cost is present in the schema\n", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "Properties: ['name', 'price', 'total_cost']\nSUCCESS: total_cost is present in the schema\n", "stderr": "", "exit_code": 0}
17run_command
{
 "command": "python3 /tmp/repro_separate.py"
}
{"status": "ok", "stdout": "Schemas: ['Item']\nFound total_cost in schema: Item\nSUCCESS: total_cost is present in at least one schema\n", "stderr": "", "exit_c
full
{"status": "ok", "stdout": "Schemas: ['Item']\nFound total_cost in schema: Item\nSUCCESS: total_cost is present in at least one schema\n", "stderr": "", "exit_code": 0}
18run_command
{
 "command": "cat <<'PY' > /tmp/test_all_schemas.py\nfrom fastapi import FastAPI\nfrom pydantic import BaseModel, computed_field\nfrom fastapi.testclient import TestClient\nimport json\n\ndef test_separate_false():\n    app = FastAPI(separate_input_output_schemas=False)\n    class Item(BaseModel):\n        name: str\n        price: float\n        @computed_field\n        @property\n        def total_cost(self) -> float:\n            return self.price * 1.2\n    @app.get(\"/item\", response_model=Item)\n    def get_item():\n        return Item(name=\"Test\", price=10.0)\n    client = TestClient(app)\n    response = client.get(\"/openapi.json\")\n    schema = response.json()\n    properties = schema[\"components\"][\"schemas\"][\"Item\"][\"properties\"]\n    assert \"total_cost\" in properties, \"total_cost missing when separate_input_output_schemas=False\"\n    print(\"test_separate_false passed\")\n\ndef test_separate_true():\n    app = FastAPI(separate_input_output_schemas=True)\n    class Item(BaseModel):\n        name: str\n        price: float\n        @computed_field\n        @property\n        def total_cost(self) -> float:\n            return self.price * 1.2\n    @app.get(\"/item\", response_model=Item)\n    def get_item():\n        return Item(name=\"Test\", price=10.0)\n    client = TestClient(app)\n    response = client.get(\"/openapi.json\")\n    schema = response.json()\n    # When separate_input_output_schemas=True, we expect separate schemas for input and output.\n    # The output schema should have total_cost.\n    found = False\n    for s in schema[\"components\"][\"schemas\"].values():\n        if \"total_cost\" in s.get(\"properties\", {}):\n            found = True\n            break\n    assert found, \"total_cost missing when separate_input_output_schemas=True\"\n    print(\"test_separate_true passed\")\n\nif __name__ == \"__main__\":\n    test_separate_false()\n    test_separate_true()\nPY\npython3 /tmp/test_all_schemas.py\n"
}
{"status": "ok", "stdout": "test_separate_false passed\ntest_separate_true passed\n", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "test_separate_false passed\ntest_separate_true passed\n", "stderr": "", "exit_code": 0}
19run_command
{
 "command": "python -m pytest tests/test_openapi_separate_input_output_schemas.py -q"
}
{"status": "error", "error_type": "CommandError", "error_message": ".....F [100%]\n=================================== FAILURES ================================
full
{"status": "error", "error_type": "CommandError", "error_message": ".....F                                                                   [100%]\n=================================== FAILURES ===================================\n_______________________ test_openapi_schema_no_separate ________________________\n\n    @needs_pydanticv2\n    def test_openapi_schema_no_separate():\n        client = get_app_client(separate_input_output_schemas=False)\n        response = client.get(\"/openapi.json\")\n        assert response.status_code == 200, response.text\n>       assert response.json() == {\n            \"openapi\": \"3.1.0\",\n            \"info\": {\"title\": \"FastAPI\", \"version\": \"0.1.0\"},\n            \"paths\": {\n                \"/items/\": {\n                    \"get\": {\n                        \"summary\": \"Read Items\",\n                        \"operationId\": \"read_items_items__get\",\n                        \"responses\": {\n                            \"200\": {\n                                \"description\": \"Successful Response\",\n                                \"content\": {\n                                    \"application/json\": {\n                                        \"schema\": {\n                                            \"items\": {\"$ref\": \"#/components/schemas/Item\"},\n                                            \"type\": \"array\",\n                                            \"title\": \"Response Read Items Items  Get\",\n                                        }\n                                    }\n                                },\n                            }\n                        },\n                    },\n                    \"post\": {\n                        \"summary\": \"Create Item\",\n                        \"operationId\": \"create_item_items__post\",\n                        \"requestBody\": {\n                            \"content\": {\n                                \"application/json\":
20run_command
{
 "command": "cat tests/test_openapi_separate_input_output_schemas.py | grep -n \"test_openapi_schema_no_separate\" -A 1"
}
{"status": "ok", "stdout": "359:def test_openapi_schema_no_separate():\n360- client = get_app_client(separate_input_output_schemas=False)\n", "stderr": "", "exi
full
{"status": "ok", "stdout": "359:def test_openapi_schema_no_separate():\n360-    client = get_app_client(separate_input_output_schemas=False)\n", "stderr": "", "exit_code": 0}
21run_command
{
 "command": "sed -n '359,420p' tests/test_openapi_separate_input_output_schemas.py"
}
{"status": "ok", "stdout": "def test_openapi_schema_no_separate():\n client = get_app_client(separate_input_output_schemas=False)\n response = client.get(\"/ope
full
{"status": "ok", "stdout": "def test_openapi_schema_no_separate():\n    client = get_app_client(separate_input_output_schemas=False)\n    response = client.get(\"/openapi.json\")\n    assert response.status_code == 200, response.text\n    assert response.json() == {\n        \"openapi\": \"3.1.0\",\n        \"info\": {\"title\": \"FastAPI\", \"version\": \"0.1.0\"},\n        \"paths\": {\n            \"/items/\": {\n                \"get\": {\n                    \"summary\": \"Read Items\",\n                    \"operationId\": \"read_items_items__get\",\n                    \"responses\": {\n                        \"200\": {\n                            \"description\": \"Successful Response\",\n                            \"content\": {\n                                \"application/json\": {\n                                    \"schema\": {\n                                        \"items\": {\"$ref\": \"#/components/schemas/Item\"},\n                                        \"type\": \"array\",\n                                        \"title\": \"Response Read Items Items  Get\",\n                                    }\n                                }\n                            },\n                        }\n                    },\n                },\n                \"post\": {\n                    \"summary\": \"Create Item\",\n                    \"operationId\": \"create_item_items__post\",\n                    \"requestBody\": {\n                        \"content\": {\n                            \"application/json\": {\n                                \"schema\": {\"$ref\": \"#/components/schemas/Item\"}\n                            }\n                        },\n                        \"required\": True,\n                    },\n                    \"responses\": {\n                        \"200\": {\n                            \"description\": \"Successful Response\",\n                            \"content\": {\n                                \"
22run_command
{
 "command": "cat tests/test_openapi_separate_input_output_schemas.py | grep -n \"class Item\" -A 10"
}
{"status": "ok", "stdout": "19:class Item(BaseModel):\n20- name: str\n21- description: Optional[str] = None\n22- sub: Optional[SubItem] = None\n23- if PYDANTIC_
full
{"status": "ok", "stdout": "19:class Item(BaseModel):\n20-    name: str\n21-    description: Optional[str] = None\n22-    sub: Optional[SubItem] = None\n23-    if PYDANTIC_V2:\n24-        model_config = {\"json_schema_serialization_defaults_required\": True}\n25-\n26-\n27-def get_app_client(separate_input_output_schemas: bool = True) -> TestClient:\n28-    app = FastAPI(separate_input_output_schemas=separate_input_output_schemas)\n29-\n", "stderr": "", "exit_code": 0}
23run_command
{
 "command": "grep -n \"computed_field\" tests/test_openapi_separate_input_output_schemas.py"
}
{"status": "error", "error_type": "CommandError", "error_message": "", "details": {"stdout": "", "stderr": "", "exit_code": 1}}
full
{"status": "error", "error_type": "CommandError", "error_message": "", "details": {"stdout": "", "stderr": "", "exit_code": 1}}
24run_command
{
 "command": "python -m pytest tests/test_openapi_separate_input_output_schemas.py -q -k \"test_openapi_schema_no_separate\""
}
{"status": "error", "error_type": "CommandError", "error_message": "F [100%]\n=================================== FAILURES ===================================\n
full
{"status": "error", "error_type": "CommandError", "error_message": "F                                                                        [100%]\n=================================== FAILURES ===================================\n_______________________ test_openapi_schema_no_separate ________________________\n\n    @needs_pydanticv2\n    def test_openapi_schema_no_separate():\n        client = get_app_client(separate_input_output_schemas=False)\n        response = client.get(\"/openapi.json\")\n        assert response.status_code == 200, response.text\n>       assert response.json() == {\n            \"openapi\": \"3.1.0\",\n            \"info\": {\"title\": \"FastAPI\", \"version\": \"0.1.0\"},\n            \"paths\": {\n                \"/items/\": {\n                    \"get\": {\n                        \"summary\": \"Read Items\",\n                        \"operationId\": \"read_items_items__get\",\n                        \"responses\": {\n                            \"200\": {\n                                \"description\": \"Successful Response\",\n                                \"content\": {\n                                    \"application/json\": {\n                                        \"schema\": {\n                                            \"items\": {\"$ref\": \"#/components/schemas/Item\"},\n                                            \"type\": \"array\",\n                                            \"title\": \"Response Read Items Items  Get\",\n                                        }\n                                    }\n                                },\n                            }\n                        },\n                    },\n                    \"post\": {\n                        \"summary\": \"Create Item\",\n                        \"operationId\": \"create_item_items__post\",\n                        \"requestBody\": {\n                            \"content\": {\n                                \"application/json\":
25run_command
{
 "command": "cd /workspace && python3 - <<'PY'\nfrom pathlib import Path\np = Path(\"fastapi/_compat/v2.py\")\ns = p.read_text()\nold_text = 'None if separate_input_output_schemas else \"validation\"'\nnew_text = 'None if separate_input_output_schemas else \"serialization\"'\ns = s.replace(old_text, new_text)\np.write_text(s)\nPY\n"
}
{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}
26run_command
{
 "command": "python -m pytest tests/test_openapi_separate_input_output_schemas.py -q -k \"test_openapi_schema_no_separate\""
}
{"status": "error", "error_type": "CommandError", "error_message": "F [100%]\n=================================== FAILURES ===================================\n
full
{"status": "error", "error_type": "CommandError", "error_message": "F                                                                        [100%]\n=================================== FAILURES ===================================\n_______________________ test_openapi_schema_no_separate ________________________\n\n    @needs_pydanticv2\n    def test_openapi_schema_no_separate():\n        client = get_app_client(separate_input_output_schemas=False)\n        response = client.get(\"/openapi.json\")\n        assert response.status_code == 200, response.text\n>       assert response.json() == {\n            \"openapi\": \"3.1.0\",\n            \"info\": {\"title\": \"FastAPI\", \"version\": \"0.1.0\"},\n            \"paths\": {\n                \"/items/\": {\n                    \"get\": {\n                        \"summary\": \"Read Items\",\n                        \"operationId\": \"read_items_items__get\",\n                        \"responses\": {\n                            \"200\": {\n                                \"description\": \"Successful Response\",\n                                \"content\": {\n                                    \"application/json\": {\n                                        \"schema\": {\n                                            \"items\": {\"$ref\": \"#/components/schemas/Item\"},\n                                            \"type\": \"array\",\n                                            \"title\": \"Response Read Items Items  Get\",\n                                        }\n                                    }\n                                },\n                            }\n                        },\n                    },\n                    \"post\": {\n                        \"summary\": \"Create Item\",\n                        \"operationId\": \"create_item_items__post\",\n                        \"requestBody\": {\n                            \"content\": {\n                                \"application/json\":
27run_command
{
 "command": "cd /workspace && python3 - <<'PY'\nfrom pathlib import Path\np = Path(\"fastapi/_compat/v2.py\")\ns = p.read_text()\nold_text = 'None if separate_input_output_schemas else \"serialization\"'\nnew_text = 'None if separate_input_output_schemas else \"validation\"'\ns = s.replace(old_text, new_text)\np.write_text(s)\nPY\n"
}
{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}
28run_command
{
 "command": "python -m pytest tests/test_openapi_separate_input_output_schemas.py -q -k \"test_openapi_schema_no_separate\""
}
{"status": "ok", "stdout": ". [100%]\n=============================== warnings summary ===============================\n../../../../../../../Users/jp/repos/kagg
full
{"status": "ok", "stdout": ".                                                                        [100%]\n=============================== warnings summary ===============================\n../../../../../../../Users/jp/repos/kaggle-gemini-coding-agent-post-training/.envs/overlays/starlette-0.50.0-py3-none-any/starlette/testclient.py:45\n  /Users/jp/repos/kaggle-gemini-coding-agent-post-training/.envs/overlays/starlette-0.50.0-py3-none-any/starlette/testclient.py:45: DeprecationWarning: The anyio.abc.BlockingPortal alias is deprecated, use anyio.from_thread.BlockingPortal instead.\n    _PortalFactoryType = Callable[[], AbstractContextManager[anyio.abc.BlockingPortal]]\n\n-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html\n1 passed, 5 deselected, 1 warning in 0.39s\n", "stderr": "", "exit_code": 0}
29run_command
{
 "command": "cd /workspace && python3 - <<'PY'\nfrom pathlib import Path\np = Path(\"fastapi/_compat/v2.py\")\ns = p.read_text()\n\n# We want to use \"serialization\" ONLY when we are dealing with output schemas.\n# In get_definitions, we generate both validation and serialization fields.\n# In get_schema_for_field, we are getting the schema for a specific field.\n\n# Let's look at get_schema_for_field (the first occurrence)\n# and get_definitions (the second occurrence).\n\n# If separate_input_output_schemas is False, we want the \"combined\" schema.\n# For Pydantic v2, the \"serialization\" mode includes computed fields.\n# The \"validation\" mode does not.\n\n# If we are in get_definitions, we are creating the pool of definitions.\n# If separate_input_output_schemas is False, we probably want to use \"validation\" \n# as the default for the combined schema, BUT we want computed fields.\n# This is tricky because Pydantic v2 separates them.\n\n# Let's try to only change the first occurrence (get_schema_for_field) \n# and see if that's enough, or if we need to change how get_definitions works.\n\n# Actually, the first occurrence is in get_schema_for_field:\n# override_mode: Union[Literal[\"validation\"], None] = (\n#     None if separate_input_output_schemas else \"validation\"\n# )\n# If we change this to \"serialization\", then get_schema_for_field will return the serialization schema.\n\n# The second occurrence is in get_definitions:\n# override_mode: Union[Literal[\"validation\"], None] = (\n#     None if separate_input_output_schemas else \"validation\"\n# )\n# This override_mode is used in the 'inputs' list:\n# inputs = [\n#     (field, override_mode or field.mode, field._type_adapter.core_schema)\n#     for field in list(fields) + list(unique_flat_model_fields)\n# ]\n# If override_mode is \"validation\", then all fields (even those with mode=\"serialization\") \n# will be generated with \"validation\" mode. This is why computed fields (which are \"serialization\" mode) \n# are lost.\n\n# If we change the second occurrence to \"serialization\", then all fields will be generated \n# with \"serialization\" mode. This includes computed fields, but might remove \n# some validation-only fields (though usually they are the same).\n\n# Let's try changing ONLY the second occurrence.\nPY\n"
}
{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}
30run_command
{
 "command": "cd /workspace && python3 - <<'PY'\nfrom pathlib import Path\np = Path(\"fastapi/_compat/v2.py\")\ns = p.read_text()\n\n# Replace only the second occurrence of the override_mode line.\n# The first one is around line 183, the second around 207.\nlines = s.splitlines()\nlines[207] = '    override_mode: Union[Literal[\"validation\"], None] = ('\nlines[208] = '        None if separate_input_output_schemas else \"serialization\"'\n# Wait, the line numbers might have shifted. Let's use a more robust way.\n\n# We can find all occurrences and replace the second one.\nimport re\npattern = r'override_mode: Union\\[Literal\\[\"validation\"\\], None\\] = \\(\\s+None if separate_input_output_schemas else \"validation\"\\s+\\)'\nmatches = list(re.finditer(pattern, s))\nif len(matches) >= 2:\n    start, end = matches[1].span()\n    s = s[:start] + '    override_mode: Union[Literal[\"validation\"], None] = (\\n        None if separate_input_output_schemas else \"serialization\"\\n    )' + s[end:]\n\np.write_text(s)\nPY\npython3 /tmp/repro.py\n"
}
{"status": "error", "error_type": "CommandError", "error_message": "Traceback (most recent call last):\n File \"/tmp/repro.py\", line 1, in <module>\n from fast
full
{"status": "error", "error_type": "CommandError", "error_message": "Traceback (most recent call last):\n  File \"/tmp/repro.py\", line 1, in <module>\n    from fastapi import FastAPI\n  File \"/workspace/fastapi/__init__.py\", line 7, in <module>\n    from .applications import FastAPI as FastAPI\n  File \"/workspace/fastapi/applications.py\", line 17, in <module>\n    from fastapi import routing\n  File \"/workspace/fastapi/routing.py\", line 28, in <module>\n    from fastapi import params, temp_pydantic_v1_params\n  File \"/workspace/fastapi/params.py\", line 6, in <module>\n    from fastapi.openapi.models import Example\n  File \"/workspace/fastapi/openapi/models.py\", line 4, in <module>\n    from fastapi._compat import (\n    ...<6 lines>...\n    )\n  File \"/workspace/fastapi/_compat/__init__.py\", line 1, in <module>\n    from .main import BaseConfig as BaseConfig\n  File \"/workspace/fastapi/_compat/main.py\", line 21, in <module>\n    from .v2 import BaseConfig as BaseConfig\n  File \"/workspace/fastapi/_compat/v2.py\", line 207\n    override_mode: Union[Literal[\"validation\"], None] = (\nIndentationError: unexpected indent\n", "details": {"stdout": "", "stderr": "Traceback (most recent call last):\n  File \"/tmp/repro.py\", line 1, in <module>\n    from fastapi import FastAPI\n  File \"/workspace/fastapi/__init__.py\", line 7, in <module>\n    from .applications import FastAPI as FastAPI\n  File \"/workspace/fastapi/applications.py\", line 17, in <module>\n    from fastapi import routing\n  File \"/workspace/fastapi/routing.py\", line 28, in <module>\n    from fastapi import params, temp_pydantic_v1_params\n  File \"/workspace/fastapi/params.py\", line 6, in <module>\n    from fastapi.openapi.models import Example\n  File \"/workspace/fastapi/openapi/models.py\", line 4, in <module>\n    from fastapi._compat import (\n    ...<6 lines>...\n    )\n  File \"/workspace/fastapi/_compat/__init__.py\", line 1, in <module>\n    from .main import BaseConfig as Base

Patch

diff --git a/fastapi/_compat/v2.py b/fastapi/_compat/v2.py
index 3d91814c..2870c80d 100644
--- a/fastapi/_compat/v2.py
+++ b/fastapi/_compat/v2.py
@@ -204,8 +204,8 @@ def get_definitions(
     Dict[str, Dict[str, Any]],
 ]:
     schema_generator = GenerateJsonSchema(ref_template=REF_TEMPLATE)
-    override_mode: Union[Literal["validation"], None] = (
-        None if separate_input_output_schemas else "validation"
+        override_mode: Union[Literal["validation"], None] = (
+        None if separate_input_output_schemas else "serialization"
     )
     validation_fields = [field for field in fields if field.mode == "validation"]
     serialization_fields = [field for field in fields if field.mode == "serialization"]

Test output

show
==================================== ERRORS ====================================
________________ ERROR collecting tests/test_computed_fields.py ________________
/Users/jp/repos/kaggle-gemini-coding-agent-post-training/.envs/fastapi/lib/python3.13/site-packages/_pytest/python.py:508: in importtestmodule
    mod = import_path(
/Users/jp/repos/kaggle-gemini-coding-agent-post-training/.envs/fastapi/lib/python3.13/site-packages/_pytest/pathlib.py:596: in import_path
    importlib.import_module(module_name)
/Users/jp/.local/share/uv/python/cpython-3.13.15-macos-aarch64-none/lib/python3.13/importlib/__init__.py:88: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
<frozen importlib._bootstrap>:1395: in _gcd_import
    ???
<frozen importlib._bootstrap>:1360: in _find_and_load
    ???
<frozen importlib._bootstrap>:1331: in _find_and_load_unlocked
    ???
<frozen importlib._bootstrap>:935: in _load_unlocked
    ???
/Users/jp/repos/kaggle-gemini-coding-agent-post-training/.envs/fastapi/lib/python3.13/site-packages/_pytest/assertion/rewrite.py:188: in exec_module
    exec(co, module.__dict__)
tests/test_computed_fields.py:2: in <module>
    from fastapi import FastAPI
fastapi/__init__.py:7: in <module>
    from .applications import FastAPI as FastAPI
fastapi/applications.py:17: in <module>
    from fastapi import routing
fastapi/routing.py:28: in <module>
    from fastapi import params, temp_pydantic_v1_params
fastapi/params.py:6: in <module>
    from fastapi.openapi.models import Example
fastapi/openapi/models.py:4: in <module>
    from fastapi._compat import (
fastapi/_compat/__init__.py:1: in <module>
    from .main import BaseConfig as BaseConfig
fastapi/_compat/main.py:21: in <module>
    from .v2 import BaseConfig as BaseConfig
E     File "/private/tmp/swe_work/pod_r3_v1g/fastapi_13207/b/workspace/fastapi/_compat/v2.py", line 207
E       override_mode: Union[Literal["validation"], None] = (
E   IndentationError: unexpected indent
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!
1 error in 0.27s