← eval4_submission_v1b

fastapi_13207

resolved RESOLVED PASS · 22 tool calls · 248 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 (23)

#ToolArgumentsResult
1run_command
{
 "command": "grep -rn \"separate_input_output_schemas\" fastapi | head -20"
}
{"status": "ok", "stdout": "fastapi/_compat/may_v1.py:65: separate_input_output_schemas: bool = True,\nfastapi/_compat/v1.py:243: separate_input_output_schemas:
full
{"status": "ok", "stdout": "fastapi/_compat/may_v1.py:65:        separate_input_output_schemas: bool = True,\nfastapi/_compat/v1.py:243:    separate_input_output_schemas: bool = True,\nfastapi/_compat/v1.py:259:    separate_input_output_schemas: bool = True,\nfastapi/_compat/main.py:271:    separate_input_output_schemas: bool = True,\nfastapi/_compat/main.py:284:            separate_input_output_schemas=separate_input_output_schemas,\nfastapi/_compat/main.py:295:                separate_input_output_schemas=separate_input_output_schemas,\nfastapi/_compat/main.py:309:            separate_input_output_schemas=separate_input_output_schemas,\nfastapi/_compat/main.py:322:    separate_input_output_schemas: bool = True,\nfastapi/_compat/main.py:331:            separate_input_output_schemas=separate_input_output_schemas,\nfastapi/_compat/main.py:341:            separate_input_output_schemas=separate_input_output_schemas,\nfastapi/_compat/v2.py:181:    separate_input_output_schemas: bool = True,\nfastapi/_compat/v2.py:184:        None if separate_input_output_schemas else \"validation\"\nfastapi/_compat/v2.py:201:    separate_input_output_schemas: bool = True,\nfastapi/_compat/v2.py:208:        None if separate_input_output_schemas else \"validation\"\nfastapi/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,\n", "exit_code": 0}
2read_file
{
 "filepath": "fastapi/openapi/utils.py",
 "start_line": 100,
 "end_line": 200
}
{"status": "ok", "filepath": "fastapi/openapi/utils.py", "content": " Tuple[ModelField, Literal[\"validation\", \"serialization\"]], JsonSchemaValue\n ],\n sepa
full
{"status": "ok", "filepath": "fastapi/openapi/utils.py", "content": "        Tuple[ModelField, Literal[\"validation\", \"serialization\"]], JsonSchemaValue\n    ],\n    separate_input_output_schemas: bool = True,\n) -> List[Dict[str, Any]]:\n    parameters = []\n    flat_dependant = get_flat_dependant(dependant, skip_repeats=True)\n    path_params = _get_flat_fields_from_params(flat_dependant.path_params)\n    query_params = _get_flat_fields_from_params(flat_dependant.query_params)\n    header_params = _get_flat_fields_from_params(flat_dependant.header_params)\n    cookie_params = _get_flat_fields_from_params(flat_dependant.cookie_params)\n    parameter_groups = [\n        (ParamTypes.path, path_params),\n        (ParamTypes.query, query_params),\n        (ParamTypes.header, header_params),\n        (ParamTypes.cookie, cookie_params),\n    ]\n    default_convert_underscores = True\n    if len(flat_dependant.header_params) == 1:\n        first_field = flat_dependant.header_params[0]\n        if lenient_issubclass(first_field.type_, BaseModel):\n            default_convert_underscores = getattr(\n                first_field.field_info, \"convert_underscores\", True\n            )\n    for param_type, param_group in parameter_groups:\n        for param in param_group:\n            field_info = param.field_info\n            # field_info = cast(Param, field_info)\n            if not getattr(field_info, \"include_in_schema\", True):\n                continue\n            param_schema = get_schema_from_model_field(\n                field=param,\n                model_name_map=model_name_map,\n                field_mapping=field_mapping,\n                separate_input_output_schemas=separate_input_output_schemas,\n            )\n            name = param.alias\n            convert_underscores = getattr(\n                param.field_info,\n                \"convert_underscores\",\n                default_convert_underscores,\n            )\n            if (\n                
3run_command
{
 "command": "grep -rn \"get_schema_from_model_field\" fastapi | head -20"
}
{"status": "ok", "stdout": "fastapi/_compat/__init__.py:23:from .main import get_schema_from_model_field as get_schema_from_model_field\nfastapi/_compat/v1.py:2
full
{"status": "ok", "stdout": "fastapi/_compat/__init__.py:23:from .main import get_schema_from_model_field as get_schema_from_model_field\nfastapi/_compat/v1.py:236:def get_schema_from_model_field(\nfastapi/_compat/main.py:314:def get_schema_from_model_field(\nfastapi/_compat/main.py:327:        return v1.get_schema_from_model_field(\nfastapi/_compat/main.py:337:        return v2.get_schema_from_model_field(\nfastapi/_compat/v2.py:174:def get_schema_from_model_field(\nfastapi/openapi/utils.py:13:    get_schema_from_model_field,\nfastapi/openapi/utils.py:129:            param_schema = get_schema_from_model_field(\nfastapi/openapi/utils.py:180:    body_schema = get_schema_from_model_field(\nfastapi/openapi/utils.py:352:                        response_schema = get_schema_from_model_field(\nfastapi/openapi/utils.py:385:                        additional_field_schema = get_schema_from_model_field(\n", "exit_code": 0}
4read_file
{
 "end_line": 220,
 "filepath": "fastapi/_compat/v2.py",
 "start_line": 150
}
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": " # ModelField to its JSON Schema.\n return id(self)\n\n\ndef get_annotation_from_field_info(\n
full
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": "        # ModelField to its JSON Schema.\n        return id(self)\n\n\ndef get_annotation_from_field_info(\n    annotation: Any, field_info: FieldInfo, field_name: str\n) -> Any:\n    return annotation\n\n\ndef _model_rebuild(model: Type[BaseModel]) -> None:\n    model.model_rebuild()\n\n\ndef _model_dump(\n    model: BaseModel, mode: Literal[\"json\", \"python\"] = \"json\", **kwargs: Any\n) -> Any:\n    return model.model_dump(mode=mode, **kwargs)\n\n\ndef _get_model_config(model: BaseModel) -> Any:\n    return model.model_config\n\n\ndef get_schema_from_model_field(\n    *,\n    field: ModelField,\n    model_name_map: ModelNameMap,\n    field_mapping: Dict[\n        Tuple[ModelField, Literal[\"validation\", \"serialization\"]], JsonSchemaValue\n    ],\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 
5read_file
{
 "end_line": 300,
 "filepath": "fastapi/_compat/v2.py",
 "start_line": 220
}
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": " field_info=FieldInfo(annotation=model),\n name=model.__name__,\n mode=\"validation\",\n )\n f
full
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": "            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_description = cast(str, item_def[\"description\"]).split(\"\\f\")[0]\n            item_def[\"description\"] = item_description\n    new_mapping, new_definitions = _remap_definitions_and_field_mappings(\n        model_name_map=model_name_map,\n        definitions=definitions,  # type: ignore[arg-type]\n        field_mapping=field_mapping,\n    )\n    return new_mapping, new_definitions\n\n\ndef _replace_refs(\n    *,\n    schema: Dict[str, Any],\n    old_name_to_new_name_map: Dict[str, str],\n) -> Dict[str, Any]:\n    new_schema = deepcopy(schema)\n    for key, value in new_schema.items():\n        if key == \"$ref\":\n            value = schema[\"$ref\"]\n            if isinstance(value, str):\n                ref_name = schema[\"$ref\"].split(\"/\")[-1]\n                if ref_name in old_name_to_new_name_map:\n                    new_name = old_name
6run_command
{
 "command": "cat <<EOF > /tmp/repro.py\nfrom fastapi import FastAPI\nfrom pydantic import BaseModel, computed_field\nfrom fastapi.testclient import TestClient\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(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    openapi_schema = app.openapi()\n    # The response schema for /item should include 'total'\n    # In OpenAPI 3.0, this is usually under components/schemas\n    schemas = openapi_schema[\"components\"][\"schemas\"]\n    item_schema = schemas[\"Item\"]\n    properties = item_schema[\"properties\"]\n    \n    print(f\"Properties: {list(properties.keys())}\")\n    assert \"total\" in properties, \"Computed field 'total' missing from OpenAPI schema when separate_input_output_schemas=False\"\n\nif __name__ == \"__main__\":\n    try:\n        test_openapi_schema()\n        print(\"Test passed!\")\n    except AssertionError as e:\n        print(f\"Test failed: {e}\")\nEOF\npython3 /tmp/repro.py"
}
{"status": "ok", "stdout": "Properties: ['name', 'price']\nTest failed: Computed field 'total' missing from OpenAPI schema when separate_input_output_schemas=Fa
full
{"status": "ok", "stdout": "Properties: ['name', 'price']\nTest failed: Computed field 'total' missing from OpenAPI schema when separate_input_output_schemas=False\n", "exit_code": 0}
7read_file
{
 "end_line": 200,
 "filepath": "fastapi/_compat/v2.py",
 "start_line": 170
}
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": "def _get_model_config(model: BaseModel) -> Any:\n return model.model_config\n\n\ndef get_schem
full
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": "def _get_model_config(model: BaseModel) -> Any:\n    return model.model_config\n\n\ndef get_schema_from_model_field(\n    *,\n    field: ModelField,\n    model_name_map: ModelNameMap,\n    field_mapping: Dict[\n        Tuple[ModelField, Literal[\"validation\", \"serialization\"]], JsonSchemaValue\n    ],\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", "start_line": 170, "end_line": 200, "total_lines": 486, "is_truncated": false}
8edit_file
{
 "filepath": "fastapi/_compat/v2.py",
 "new_string": "    override_mode: Union[Literal[\"validation\"], None] = (\n        None if separate_input_output_schemas else \"serialization\"\n    )",
 "old_string": "    override_mode: Union[Literal[\"validation\"], None] = (\n        None if separate_input_output_schemas else \"validation\"\n    )"
}
{"status": "error", "error_type": "FileEditError", "error_message": "old_string matched 2 occurrences; provide more context or set allow_multiple"}
full
{"status": "error", "error_type": "FileEditError", "error_message": "old_string matched 2 occurrences; provide more context or set allow_multiple"}
9edit_file
{
 "filepath": "fastapi/_compat/v2.py",
 "new_string": "def get_schema_from_model_field(\n    *,\n    field: ModelField,\n    model_name_map: ModelNameMap,\n    field_mapping: Dict[\n        Tuple[ModelField, Literal[\"validation\", \"serialization\"]], JsonSchemaValue\n    ],\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 \"serialization\"\n    )\n    # This expects that GenerateJsonSchema was already used to generate the definitions",
 "old_string": "def get_schema_from_model_field(\n    *,\n    field: ModelField,\n    model_name_map: ModelNameMap,\n    field_mapping: Dict[\n        Tuple[ModelField, Literal[\"validation\", \"serialization\"]], JsonSchemaValue\n    ],\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"
}
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/fastapi/_compat/v2.py\n+++ b/fastapi/_compat/v2.py\n
full
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/fastapi/_compat/v2.py\n+++ b/fastapi/_compat/v2.py\n@@ -181,7 +181,7 @@\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+        None if separate_input_output_schemas else \"serialization\"\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", "is_truncated": false}
10run_command
{
 "command": "python3 /tmp/repro.py"
}
{"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 1", "details": {"stdout": "", "stderr": "Traceback (most recen
full
{"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 1", "details": {"stdout": "", "stderr": "Traceback (most recent call last):\n  File \"/tmp/swe_work/eval4_submission_v1b/fastapi_13207/a/tmp/repro.py\", line 35, in <module>\n    test_openapi_schema()\n    ~~~~~~~~~~~~~~~~~~~^^\n  File \"/tmp/swe_work/eval4_submission_v1b/fastapi_13207/a/tmp/repro.py\", line 23, in test_openapi_schema\n    openapi_schema = app.openapi()\n  File \"/workspace/fastapi/applications.py\", line 1065, in openapi\n    self.openapi_schema = get_openapi(\n                          ~~~~~~~~~~~^\n        title=self.title,\n        ^^^^^^^^^^^^^^^^^\n    ...<12 lines>...\n        external_docs=self.openapi_external_docs,\n        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n    )\n    ^\n  File \"/workspace/fastapi/openapi/utils.py\", line 511, in get_openapi\n    result = get_openapi_path(\n        route=route,\n    ...<3 lines>...\n        separate_input_output_schemas=separate_input_output_schemas,\n    )\n  File \"/workspace/fastapi/openapi/utils.py\", line 352, in get_openapi_path\n    response_schema = get_schema_from_model_field(\n        field=route.response_field,\n    ...<2 lines>...\n        separate_input_output_schemas=separate_input_output_schemas,\n    )\n  File \"/workspace/fastapi/_compat/main.py\", line 337, in get_schema_from_model_field\n    return v2.get_schema_from_model_field(\n           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^\n        field=field,  # type: ignore[arg-type]\n        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n    ...<2 lines>...\n        separate_input_output_schemas=separate_input_output_schemas,\n        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n    )\n    ^\n  File \"/workspace/fastapi/_compat/v2.py\", line 187, in get_schema_from_model_field\n    json_schema = field_mapping[(field, override_mode or field.mode)]\n                  ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nKeyError: (ModelF
11read_file
{
 "end_line": 260,
 "filepath": "fastapi/_compat/v2.py",
 "start_line": 220
}
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": " field_info=FieldInfo(annotation=model),\n name=model.__name__,\n mode=\"validation\",\n )\n f
full
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": "            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_description = cast(str, item_def[\"description\"]).split(\"\\f\")[0]\n            item_def[\"description\"] = item_description\n    new_mapping, new_definitions = _remap_definitions_and_field_mappings(\n        model_name_map=model_name_map,\n        definitions=definitions,  # type: ignore[arg-type]\n        field_mapping=field_mapping,\n    )\n    return new_mapping, new_definitions\n\n\ndef _replace_refs(\n    *,\n    schema: Dict[str, Any],\n    old_name_to_new_name_map: Dict[str, str],\n", "start_line": 220, "end_line": 260, "total_lines": 486, "is_truncated": false}
12edit_file
{
 "filepath": "fastapi/_compat/v2.py",
 "new_string": "    override_mode: Union[Literal[\"validation\"], None] = (\n        None if separate_input_output_schemas else \"validation\"\n    )",
 "old_string": "    override_mode: Union[Literal[\"validation\"], None] = (\n        None if separate_input_output_schemas else \"serialization\"\n    )"
}
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/fastapi/_compat/v2.py\n+++ b/fastapi/_compat/v2.py\n
full
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/fastapi/_compat/v2.py\n+++ b/fastapi/_compat/v2.py\n@@ -181,7 +181,7 @@\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 \"serialization\"\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", "is_truncated": false}
13read_file
{
 "end_line": 260,
 "filepath": "fastapi/_compat/v2.py",
 "start_line": 220
}
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": " field_info=FieldInfo(annotation=model),\n name=model.__name__,\n mode=\"validation\",\n )\n f
full
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": "            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_description = cast(str, item_def[\"description\"]).split(\"\\f\")[0]\n            item_def[\"description\"] = item_description\n    new_mapping, new_definitions = _remap_definitions_and_field_mappings(\n        model_name_map=model_name_map,\n        definitions=definitions,  # type: ignore[arg-type]\n        field_mapping=field_mapping,\n    )\n    return new_mapping, new_definitions\n\n\ndef _replace_refs(\n    *,\n    schema: Dict[str, Any],\n    old_name_to_new_name_map: Dict[str, str],\n", "start_line": 220, "end_line": 260, "total_lines": 486, "is_truncated": false}
14edit_file
{
 "filepath": "fastapi/_compat/v2.py",
 "new_string": "    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    ]",
 "old_string": "    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    ]"
}
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "occurrences": 1, "strategy": "exact", "diff": "", "is_truncated": false}
full
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "occurrences": 1, "strategy": "exact", "diff": "", "is_truncated": false}
15edit_file
{
 "filepath": "fastapi/_compat/v2.py",
 "new_string": "    inputs = [\n        (field, (override_mode if override_mode is not None else field.mode), field._type_adapter.core_schema)\n        for field in list(fields) + list(unique_flat_model_fields)\n    ]",
 "old_string": "    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    ]"
}
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/fastapi/_compat/v2.py\n+++ b/fastapi/_compat/v2.py\n
full
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/fastapi/_compat/v2.py\n+++ b/fastapi/_compat/v2.py\n@@ -238,7 +238,7 @@\n     }\n \n     inputs = [\n-        (field, override_mode or field.mode, field._type_adapter.core_schema)\n+        (field, (override_mode if override_mode is not None else 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", "is_truncated": false}
16edit_file
{
 "filepath": "fastapi/_compat/v2.py",
 "new_string": "    override_mode: Union[Literal[\"validation\"], None] = (\n        None if separate_input_output_schemas else \"serialization\"\n    )",
 "old_string": "    override_mode: Union[Literal[\"validation\"], None] = (\n        None if separate_input_output_schemas else \"validation\"\n    )"
}
{"status": "error", "error_type": "FileEditError", "error_message": "old_string matched 2 occurrences; provide more context or set allow_multiple"}
full
{"status": "error", "error_type": "FileEditError", "error_message": "old_string matched 2 occurrences; provide more context or set allow_multiple"}
17edit_file
{
 "filepath": "fastapi/_compat/v2.py",
 "new_string": "def 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 \"serialization\"\n    )\n    validation_fields = [field for field in fields if field.mode == \"validation\"]",
 "old_string": "def 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\"]"
}
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/fastapi/_compat/v2.py\n+++ b/fastapi/_compat/v2.py\n
full
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/fastapi/_compat/v2.py\n+++ b/fastapi/_compat/v2.py\n@@ -205,7 +205,7 @@\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+        None if separate_input_output_schemas else \"serialization\"\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", "is_truncated": false}
18run_command
{
 "command": "python3 /tmp/repro.py"
}
{"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 1", "details": {"stdout": "", "stderr": "Traceback (most recen
full
{"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 1", "details": {"stdout": "", "stderr": "Traceback (most recent call last):\n  File \"/tmp/swe_work/eval4_submission_v1b/fastapi_13207/a/tmp/repro.py\", line 35, in <module>\n    test_openapi_schema()\n    ~~~~~~~~~~~~~~~~~~~^^\n  File \"/tmp/swe_work/eval4_submission_v1b/fastapi_13207/a/tmp/repro.py\", line 23, in test_openapi_schema\n    openapi_schema = app.openapi()\n  File \"/workspace/fastapi/applications.py\", line 1065, in openapi\n    self.openapi_schema = get_openapi(\n                          ~~~~~~~~~~~^\n        title=self.title,\n        ^^^^^^^^^^^^^^^^^\n    ...<12 lines>...\n        external_docs=self.openapi_external_docs,\n        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n    )\n    ^\n  File \"/workspace/fastapi/openapi/utils.py\", line 511, in get_openapi\n    result = get_openapi_path(\n        route=route,\n    ...<3 lines>...\n        separate_input_output_schemas=separate_input_output_schemas,\n    )\n  File \"/workspace/fastapi/openapi/utils.py\", line 352, in get_openapi_path\n    response_schema = get_schema_from_model_field(\n        field=route.response_field,\n    ...<2 lines>...\n        separate_input_output_schemas=separate_input_output_schemas,\n    )\n  File \"/workspace/fastapi/_compat/main.py\", line 337, in get_schema_from_model_field\n    return v2.get_schema_from_model_field(\n           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^\n        field=field,  # type: ignore[arg-type]\n        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n    ...<2 lines>...\n        separate_input_output_schemas=separate_input_output_schemas,\n        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n    )\n    ^\n  File \"/workspace/fastapi/_compat/v2.py\", line 187, in get_schema_from_model_field\n    json_schema = field_mapping[(field, override_mode or field.mode)]\n                  ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nKeyError: (ModelF
19edit_file
{
 "filepath": "fastapi/_compat/v2.py",
 "new_string": "    override_mode: Union[Literal[\"validation\"], None] = (\n        None if separate_input_output_schemas else \"serialization\"\n    )\n    # This expects that GenerateJsonSchema was already used to generate the definitions\n    json_schema = field_mapping[(field, override_mode if override_mode is not None else field.mode)]",
 "old_string": "    override_mode: Union[Literal[\"validation\"], None] = (\n        None if separate_input_output_schemas else \"serialization\"\n    )\n    # This expects that GenerateJsonSchema was already used to generate the definitions\n    json_schema = field_mapping[(field, override_mode or field.mode)]"
}
{"status": "error", "error_type": "FileEditError", "error_message": "old_string not found in file (tried exact, flexible and regex matching)"}
full
{"status": "error", "error_type": "FileEditError", "error_message": "old_string not found in file (tried exact, flexible and regex matching)"}
20read_file
{
 "end_line": 200,
 "filepath": "fastapi/_compat/v2.py",
 "start_line": 170
}
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": "def _get_model_config(model: BaseModel) -> Any:\n return model.model_config\n\n\ndef get_schem
full
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": "def _get_model_config(model: BaseModel) -> Any:\n    return model.model_config\n\n\ndef get_schema_from_model_field(\n    *,\n    field: ModelField,\n    model_name_map: ModelNameMap,\n    field_mapping: Dict[\n        Tuple[ModelField, Literal[\"validation\", \"serialization\"]], JsonSchemaValue\n    ],\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", "start_line": 170, "end_line": 200, "total_lines": 486, "is_truncated": false}
21edit_file
{
 "filepath": "fastapi/_compat/v2.py",
 "new_string": "    override_mode: Union[Literal[\"validation\"], None] = (\n        None if separate_input_output_schemas else \"serialization\"\n    )\n    # This expects that GenerateJsonSchema was already used to generate the definitions\n    json_schema = field_mapping[(field, override_mode if override_mode is not None else field.mode)]",
 "old_string": "    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)]"
}
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/fastapi/_compat/v2.py\n+++ b/fastapi/_compat/v2.py\n
full
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/fastapi/_compat/v2.py\n+++ b/fastapi/_compat/v2.py\n@@ -181,10 +181,10 @@\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+        None if separate_input_output_schemas else \"serialization\"\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+    json_schema = field_mapping[(field, override_mode if override_mode is not None else 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", "is_truncated": false}
22run_command
{
 "command": "python3 /tmp/repro.py"
}
{"status": "ok", "stdout": "Properties: ['name', 'price', 'total']\nTest passed!\n", "exit_code": 0}
full
{"status": "ok", "stdout": "Properties: ['name', 'price', 'total']\nTest passed!\n", "exit_code": 0}
23submit_patch
[]
{"status": "ok", "patch_size": 1849, "files_changed": 1}
full
{"status": "ok", "patch_size": 1849, "files_changed": 1}

Patch

diff --git a/fastapi/_compat/v2.py b/fastapi/_compat/v2.py
index 3d91814c..b476c669 100644
--- a/fastapi/_compat/v2.py
+++ b/fastapi/_compat/v2.py
@@ -181,10 +181,10 @@ def get_schema_from_model_field(
     separate_input_output_schemas: bool = True,
 ) -> Dict[str, Any]:
     override_mode: Union[Literal["validation"], None] = (
-        None if separate_input_output_schemas else "validation"
+        None if separate_input_output_schemas else "serialization"
     )
     # This expects that GenerateJsonSchema was already used to generate the definitions
-    json_schema = field_mapping[(field, override_mode or field.mode)]
+    json_schema = field_mapping[(field, override_mode if override_mode is not None else field.mode)]
     if "$ref" not in json_schema:
         # TODO remove when deprecating Pydantic v1
         # Ref: https://github.com/pydantic/pydantic/blob/d61792cc42c80b13b23e3ffa74bc37ec7c77f7d1/pydantic/schema.py#L207
@@ -205,7 +205,7 @@ def get_definitions(
 ]:
     schema_generator = GenerateJsonSchema(ref_template=REF_TEMPLATE)
     override_mode: Union[Literal["validation"], None] = (
-        None if separate_input_output_schemas else "validation"
+        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"]
@@ -238,7 +238,7 @@ def get_definitions(
     }
 
     inputs = [
-        (field, override_mode or field.mode, field._type_adapter.core_schema)
+        (field, (override_mode if override_mode is not None else field.mode), field._type_adapter.core_schema)
         for field in list(fields) + list(unique_flat_model_fields)
     ]
     field_mapping, definitions = schema_generator.generate_definitions(inputs=inputs)

Test output

show
......                                                                   [100%]
=============================== warnings summary ===============================
../../../../../../kaggle/tmp/envs/overlays/starlette-0.50.0-py3-none-any/starlette/testclient.py:45
  /kaggle/tmp/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.
    _PortalFactoryType = Callable[[], AbstractContextManager[anyio.abc.BlockingPortal]]

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
6 passed, 1 warning in 0.74s