resolved RESOLVED PASS · 21 tool calls · 268 s · fastapi/fastapi
🐛 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 | Arguments | Result |
|---|---|---|---|
| 1 | run_command | {
"command": "grep -rn \"separate_input_output_schemas\" ."
} | {"status": "ok", "stdout": "./fastapi/_compat/may_v1.py:65: separate_input_output_schemas: bool = True,\n./fastapi/_compat/v1.py:243: separate_input_output_schefull{"status": "ok", "stdout": "./fastapi/_compat/may_v1.py:65: separate_input_output_schemas: bool = True,\n./fastapi/_compat/v1.py:243: separate_input_output_schemas: bool = True,\n./fastapi/_compat/v1.py:259: separate_input_output_schemas: bool = True,\n./fastapi/_compat/main.py:271: separate_input_output_schemas: bool = True,\n./fastapi/_compat/main.py:284: separate_input_output_schemas=separate_input_output_schemas,\n./fastapi/_compat/main.py:295: separate_input_output_schemas=separate_input_output_schemas,\n./fastapi/_compat/main.py:309: separate_input_output_schemas=separate_input_output_schemas,\n./fastapi/_compat/main.py:322: separate_input_output_schemas: bool = True,\n./fastapi/_compat/main.py:331: separate_input_output_schemas=separate_input_output_schemas,\n./fastapi/_compat/main.py:341: separate_input_output_schemas=separate_input_output_schemas,\n./fastapi/_compat/v2.py:181: separate_input_output_schemas: bool = True,\n./fastapi/_compat/v2.py:184: None if separate_input_output_schemas else \"validation\"\n./fastapi/_compat/v2.py:201: separate_input_output_schemas: bool = True,\n./fastapi/_compat/v2.py:208: None if separate_input_output_schemas else \"validation\"\n./fastapi/applications.py:791: separate_input_output_schemas: Annotated[\n./fastapi/applications.py:875: self.separate_input_output_schemas = separate_input_output_schemas\n./fastapi/applications.py:1078: separate_input_output_schemas=self.separate_input_output_schemas,\n./fastapi/openapi/utils.py:102: separate_input_output_schemas: bool = True,\n./fastapi/openapi/utils.py:133: separate_input_output_schemas=separate_input_output_schemas,\n./fastapi/openapi/utils.py:175: separate_input_output_schemas: bool = True,\n./fastapi/openapi/utils.py:184: separate_input_output_schemas=separate_input_output_schemas,\n./fastapi/openapi/utils.py:258: se |
| 2 | read_file | {
"filepath": "fastapi/openapi/utils.py",
"start_line": 100,
"end_line": 540
} | {"status": "ok", "filepath": "fastapi/openapi/utils.py", "content": " Tuple[ModelField, Literal[\"validation\", \"serialization\"]], JsonSchemaValue\n ],\n sepafull{"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 |
| 3 | read_file | {
"end_line": 480,
"filepath": "fastapi/openapi/utils.py",
"start_line": 170
} | {"status": "ok", "filepath": "fastapi/openapi/utils.py", "content": " body_field: Optional[ModelField],\n model_name_map: ModelNameMap,\n field_mapping: Dict[\nfull{"status": "ok", "filepath": "fastapi/openapi/utils.py", "content": " body_field: Optional[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) -> Optional[Dict[str, Any]]:\n if not body_field:\n return None\n assert _is_model_field(body_field)\n body_schema = get_schema_from_model_field(\n field=body_field,\n model_name_map=model_name_map,\n field_mapping=field_mapping,\n separate_input_output_schemas=separate_input_output_schemas,\n )\n field_info = cast(Body, body_field.field_info)\n request_media_type = field_info.media_type\n required = body_field.required\n request_body_oai: Dict[str, Any] = {}\n if required:\n request_body_oai[\"required\"] = required\n request_media_content: Dict[str, Any] = {\"schema\": body_schema}\n if field_info.openapi_examples:\n request_media_content[\"examples\"] = jsonable_encoder(\n field_info.openapi_examples\n )\n elif field_info.example != Undefined:\n request_media_content[\"example\"] = jsonable_encoder(field_info.example)\n request_body_oai[\"content\"] = {request_media_type: request_media_content}\n return request_body_oai\n\n\ndef generate_operation_id(\n *, route: routing.APIRoute, method: str\n) -> str: # pragma: nocover\n warnings.warn(\n \"fastapi.openapi.utils.generate_operation_id() was deprecated, \"\n \"it is not used internally, and will be removed soon\",\n DeprecationWarning,\n stacklevel=2,\n )\n if route.operation_id:\n return route.operation_id\n path: str = route.path_format\n return generate_operation_id_for_path(name=route.name, path=path, method=method)\n\n\ndef generate_operation_summary(*, route: routing.APIRoute, method: str) -> str:\n if route.summary:\n re |
| 4 | read_file | {
"end_line": 480,
"filepath": "fastapi/openapi/utils.py",
"start_line": 320
} | {"status": "ok", "filepath": "fastapi/openapi/utils.py", "content": " cb_definitions,\n ) = get_openapi_path(\n route=callback,\n operation_ids=operation_ids,\nfull{"status": "ok", "filepath": "fastapi/openapi/utils.py", "content": " cb_definitions,\n ) = get_openapi_path(\n route=callback,\n operation_ids=operation_ids,\n model_name_map=model_name_map,\n field_mapping=field_mapping,\n separate_input_output_schemas=separate_input_output_schemas,\n )\n callbacks[callback.name] = {callback.path: cb_path}\n operation[\"callbacks\"] = callbacks\n if route.status_code is not None:\n status_code = str(route.status_code)\n else:\n # It would probably make more sense for all response classes to have an\n # explicit default status_code, and to extract it from them, instead of\n # doing this inspection tricks, that would probably be in the future\n # TODO: probably make status_code a default class attribute for all\n # responses in Starlette\n response_signature = inspect.signature(current_response_class.__init__)\n status_code_param = response_signature.parameters.get(\"status_code\")\n if status_code_param is not None:\n if isinstance(status_code_param.default, int):\n status_code = str(status_code_param.default)\n operation.setdefault(\"responses\", {}).setdefault(status_code, {})[\n \"description\"\n ] = route.response_description\n if route_response_media_type and is_body_allowed_for_status_code(\n route.status_code\n ):\n response_schema = {\"type\": \"string\"}\n if lenient_issubclass(current_response_class, JSONResponse):\n if route.response_field:\n |
| 5 | read_file | {
"end_line": 170,
"filepath": "fastapi/openapi/utils.py",
"start_line": 1
} | {"status": "ok", "filepath": "fastapi/openapi/utils.py", "content": "import http.client\nimport inspect\nimport warnings\nfrom typing import Any, Dict, List, Opfull{"status": "ok", "filepath": "fastapi/openapi/utils.py", "content": "import http.client\nimport inspect\nimport warnings\nfrom typing import Any, Dict, List, Optional, Sequence, Set, Tuple, Type, Union, cast\n\nfrom fastapi import routing\nfrom fastapi._compat import (\n JsonSchemaValue,\n ModelField,\n Undefined,\n get_compat_model_name_map,\n get_definitions,\n get_schema_from_model_field,\n lenient_issubclass,\n)\nfrom fastapi.datastructures import DefaultPlaceholder\nfrom fastapi.dependencies.models import Dependant\nfrom fastapi.dependencies.utils import (\n _get_flat_fields_from_params,\n get_flat_dependant,\n get_flat_params,\n)\nfrom fastapi.encoders import jsonable_encoder\nfrom fastapi.openapi.constants import METHODS_WITH_BODY, REF_PREFIX\nfrom fastapi.openapi.models import OpenAPI\nfrom fastapi.params import Body, ParamTypes\nfrom fastapi.responses import Response\nfrom fastapi.types import ModelNameMap\nfrom fastapi.utils import (\n deep_dict_update,\n generate_operation_id_for_path,\n is_body_allowed_for_status_code,\n)\nfrom pydantic import BaseModel\nfrom starlette.responses import JSONResponse\nfrom starlette.routing import BaseRoute\nfrom typing_extensions import Literal\n\nfrom .._compat import _is_model_field\n\nvalidation_error_definition = {\n \"title\": \"ValidationError\",\n \"type\": \"object\",\n \"properties\": {\n \"loc\": {\n \"title\": \"Location\",\n \"type\": \"array\",\n \"items\": {\"anyOf\": [{\"type\": \"string\"}, {\"type\": \"integer\"}]},\n },\n \"msg\": {\"title\": \"Message\", \"type\": \"string\"},\n \"type\": {\"title\": \"Error Type\", \"type\": \"string\"},\n },\n \"required\": [\"loc\", \"msg\", \"type\"],\n}\n\nvalidation_error_response_definition = {\n \"title\": \"HTTPValidationError\",\n \"type\": \"object\",\n \"properties\": {\n \"detail\": {\n \"title\": \"Detail\",\n |
| 6 | run_command | {
"command": "grep -rn \"get_schema_from_model_field\" ."
} | {"status": "ok", "stdout": "./fastapi/_compat/__init__.py:23:from .main import get_schema_from_model_field as get_schema_from_model_field\n./fastapi/_compat/v1.full{"status": "ok", "stdout": "./fastapi/_compat/__init__.py:23:from .main import get_schema_from_model_field as get_schema_from_model_field\n./fastapi/_compat/v1.py:236:def get_schema_from_model_field(\n./fastapi/_compat/main.py:314:def get_schema_from_model_field(\n./fastapi/_compat/main.py:327: return v1.get_schema_from_model_field(\n./fastapi/_compat/main.py:337: return v2.get_schema_from_model_field(\n./fastapi/_compat/v2.py:174:def get_schema_from_model_field(\n./fastapi/openapi/utils.py:13: get_schema_from_model_field,\n./fastapi/openapi/utils.py:129: param_schema = get_schema_from_model_field(\n./fastapi/openapi/utils.py:180: body_schema = get_schema_from_model_field(\n./fastapi/openapi/utils.py:352: response_schema = get_schema_from_model_field(\n./fastapi/openapi/utils.py:385: additional_field_schema = get_schema_from_model_field(\n", "exit_code": 0} |
| 7 | read_file | {
"end_line": 210,
"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_schemfull{"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 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", "start_line": 170, "end_line": 210, "total_lines": 486, "is_truncated": false} |
| 8 | read_file | {
"end_line": 350,
"filepath": "fastapi/_compat/main.py",
"start_line": 250
} | {"status": "ok", "filepath": "fastapi/_compat/main.py", "content": "\n v2_model_fields = [\n field for field in fields if isinstance(field, v2.ModelField)\n ]\nfull{"status": "ok", "filepath": "fastapi/_compat/main.py", "content": "\n v2_model_fields = [\n field for field in fields if isinstance(field, v2.ModelField)\n ]\n v2_flat_models = v2.get_flat_models_from_fields(\n v2_model_fields, known_models=set()\n )\n all_flat_models = all_flat_models.union(v2_flat_models)\n\n model_name_map = v2.get_model_name_map(all_flat_models)\n return model_name_map\n from fastapi._compat import v1\n\n model_name_map = v1.get_model_name_map(all_flat_models)\n return model_name_map\n\n\ndef get_definitions(\n *,\n fields: List[ModelField],\n model_name_map: ModelNameMap,\n separate_input_output_schemas: bool = True,\n) -> Tuple[\n Dict[\n Tuple[ModelField, Literal[\"validation\", \"serialization\"]],\n may_v1.JsonSchemaValue,\n ],\n Dict[str, Dict[str, Any]],\n]:\n if sys.version_info < (3, 14):\n v1_fields = [field for field in fields if isinstance(field, may_v1.ModelField)]\n v1_field_maps, v1_definitions = may_v1.get_definitions(\n fields=v1_fields,\n model_name_map=model_name_map,\n separate_input_output_schemas=separate_input_output_schemas,\n )\n if not PYDANTIC_V2:\n return v1_field_maps, v1_definitions\n else:\n from . import v2\n\n v2_fields = [field for field in fields if isinstance(field, v2.ModelField)]\n v2_field_maps, v2_definitions = v2.get_definitions(\n fields=v2_fields,\n model_name_map=model_name_map,\n separate_input_output_schemas=separate_input_output_schemas,\n )\n all_definitions = {**v1_definitions, **v2_definitions}\n all_field_maps = {**v1_field_maps, **v2_field_maps}\n return all_field_maps, all_definitions\n\n # Pydantic v1 is not supported since Python 3.14\n else:\n from . import v2\n\n |
| 9 | read_file | {
"end_line": 486,
"filepath": "fastapi/_compat/v2.py",
"start_line": 211
} | {"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": " serialization_fields = [field for field in fields if field.mode == \"serialization\"]\n flat_full{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": " 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_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: D |
| 10 | run_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)\nresponse = client.get(\"/openapi.json\")\nopenapi_json = response.json()\n\n# Check if 'total' is in the schema for Item\n# The Item schema should be in components/schemas\nitem_schema = openapi_json[\"components\"][\"schemas\"][\"Item\"]\nprint(f\"Item schema properties: {item_schema.get('properties', {}).keys()}\")\nassert \"total\" in item_schema.get(\"properties\", {}), \"Computed field 'total' missing from OpenAPI schema when separate_input_output_schemas=False\"\nprint(\"Success!\")\nEOF\npython3 /tmp/repro.py\n"
} | {"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 1", "details": {"stdout": "Item schema properties: dict_keys([full{"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 1", "details": {"stdout": "Item schema properties: dict_keys(['name', 'price'])\n", "stderr": "Traceback (most recent call last):\n File \"/tmp/swe_work/eval_submission/fastapi_13207/a/tmp/repro.py\", line 28, in <module>\n assert \"total\" in item_schema.get(\"properties\", {}), \"Computed field 'total' missing from OpenAPI schema when separate_input_output_schemas=False\"\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nAssertionError: Computed field 'total' missing from OpenAPI schema when separate_input_output_schemas=False\n", "exit_code": 1}} |
| 11 | read_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_schemfull{"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} |
| 12 | edit_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"} |
| 13 | edit_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\nfull{"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} |
| 14 | run_command | {
"command": "python3 /tmp/repro.py"
} | {"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 1", "details": {"stdout": "", "stderr": "Traceback (most recenfull{"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/eval_submission/fastapi_13207/a/tmp/repro.py\", line 21, in <module>\n response = client.get(\"/openapi.json\")\n File \"/kaggle/tmp/envs/overlays/starlette-0.50.0-py3-none-any/starlette/testclient.py\", line 473, in get\n return super().get(\n ~~~~~~~~~~~^\n url,\n ^^^^\n ...<6 lines>...\n extensions=extensions,\n ^^^^^^^^^^^^^^^^^^^^^^\n )\n ^\n File \"/kaggle/tmp/envs/fastapi/lib/python3.13/site-packages/httpx/_client.py\", line 1053, in get\n return self.request(\n ~~~~~~~~~~~~^\n \"GET\",\n ^^^^^^\n ...<7 lines>...\n extensions=extensions,\n ^^^^^^^^^^^^^^^^^^^^^^\n )\n ^\n File \"/kaggle/tmp/envs/overlays/starlette-0.50.0-py3-none-any/starlette/testclient.py\", line 445, in request\n return super().request(\n ~~~~~~~~~~~~~~~^\n method,\n ^^^^^^^\n ...<11 lines>...\n extensions=extensions,\n ^^^^^^^^^^^^^^^^^^^^^^\n )\n ^\n File \"/kaggle/tmp/envs/fastapi/lib/python3.13/site-packages/httpx/_client.py\", line 825, in request\n return self.send(request, auth=auth, follow_redirects=follow_redirects)\n ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/kaggle/tmp/envs/fastapi/lib/python3.13/site-packages/httpx/_client.py\", line 914, in send\n response = self._send_handling_auth(\n request,\n ...<2 lines>...\n history=[],\n )\n File \"/kaggle/tmp/envs/fastapi/lib/python3.13/site-packages/httpx/_client.py\", line 942, in _send_handling_auth\n response = self._send_handling_redirects(\n request,\n follow_redirects=follow_redirects,\n history=history,\n )\n File \"/kaggle/tmp/envs/fastapi/lib/python3.13/site-packages/httpx/_client. |
| 15 | run_command | {
"command": "python3 /tmp/repro.py 2>&1 | tail -n 20"
} | {"status": "ok", "stdout": " File \"/workspace/fastapi/openapi/utils.py\", line 352, in get_openapi_path\n response_schema = get_schema_from_model_field(\n fielfull{"status": "ok", "stdout": " 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: (ModelField(field_info=FieldInfo(annotation=Item, required=True), name='Response_get_item_item_get', mode='serialization'), 'serialization')\n", "exit_code": 0} |
| 16 | read_file | {
"end_line": 250,
"filepath": "fastapi/_compat/v2.py",
"start_line": 211
} | {"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": " serialization_fields = [field for field in fields if field.mode == \"serialization\"]\n flat_full{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": " 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_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", "start_line": 211, "end_line": 250, "total_lines": 486, "is_truncated": false} |
| 17 | edit_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 # This expects that GenerateJsonSchema was already used to generate the definitions\n # If separate_input_output_schemas is False, we want to use the serialization schema\n # for the output, but the field.mode might be 'validation'.\n # However, the field_mapping is keyed by (field, mode).\n # If separate_input_output_schemas is False, we should try to get 'serialization'\n # if it exists, otherwise fall back to 'validation'.\n \n mode = override_mode or field.mode\n if not separate_input_output_schemas and mode == \"validation\":\n if (field, \"serialization\") in field_mapping:\n mode = \"serialization\"\n \n json_schema = field_mapping[(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": "ok", "filepath": "fastapi/_compat/v2.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/fastapi/_compat/v2.py\n+++ b/fastapi/_compat/v2.py\nfull{"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,21 @@\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+ # If separate_input_output_schemas is False, we want to use the serialization schema\n+ # for the output, but the field.mode might be 'validation'.\n+ # However, the field_mapping is keyed by (field, mode).\n+ # If separate_input_output_schemas is False, we should try to get 'serialization'\n+ # if it exists, otherwise fall back to 'validation'.\n+ \n+ mode = override_mode or field.mode\n+ if not separate_input_output_schemas and mode == \"validation\":\n+ if (field, \"serialization\") in field_mapping:\n+ mode = \"serialization\"\n+ \n+ json_schema = field_mapping[(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} |
| 18 | run_command | {
"command": "python3 /tmp/repro.py"
} | {"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 1", "details": {"stdout": "Item schema properties: dict_keys([full{"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 1", "details": {"stdout": "Item schema properties: dict_keys(['name', 'price'])\n", "stderr": "Traceback (most recent call last):\n File \"/tmp/swe_work/eval_submission/fastapi_13207/a/tmp/repro.py\", line 28, in <module>\n assert \"total\" in item_schema.get(\"properties\", {}), \"Computed field 'total' missing from OpenAPI schema when separate_input_output_schemas=False\"\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nAssertionError: Computed field 'total' missing from OpenAPI schema when separate_input_output_schemas=False\n", "exit_code": 1}} |
| 19 | read_file | {
"end_line": 260,
"filepath": "fastapi/_compat/v2.py",
"start_line": 211
} | {"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": " model_name_map: ModelNameMap,\n separate_input_output_schemas: bool = True,\n) -> Tuple[\n Difull{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": " 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\ |
| 20 | edit_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 validation_fields = [field for field in fields if field.mode == \"validation\"]",
"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\"]"
} | {"status": "ok", "filepath": "fastapi/_compat/v2.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/fastapi/_compat/v2.py\n+++ b/fastapi/_compat/v2.py\nfull{"status": "ok", "filepath": "fastapi/_compat/v2.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/fastapi/_compat/v2.py\n+++ b/fastapi/_compat/v2.py\n@@ -216,7 +216,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} |
| 21 | run_command | {
"command": "python3 /tmp/repro.py"
} | {"status": "ok", "stdout": "Item schema properties: dict_keys(['name', 'price', 'total'])\nSuccess!\n", "exit_code": 0}full{"status": "ok", "stdout": "Item schema properties: dict_keys(['name', 'price', 'total'])\nSuccess!\n", "exit_code": 0} |
| 22 | submit_patch | [] | {"status": "ok", "patch_size": 1757, "files_changed": 1}full{"status": "ok", "patch_size": 1757, "files_changed": 1} |
diff --git a/fastapi/_compat/v2.py b/fastapi/_compat/v2.py
index 3d91814c..871432c2 100644
--- a/fastapi/_compat/v2.py
+++ b/fastapi/_compat/v2.py
@@ -184,7 +184,18 @@ def get_schema_from_model_field(
None if separate_input_output_schemas else "validation"
)
# This expects that GenerateJsonSchema was already used to generate the definitions
- json_schema = field_mapping[(field, override_mode or field.mode)]
+ # If separate_input_output_schemas is False, we want to use the serialization schema
+ # for the output, but the field.mode might be 'validation'.
+ # However, the field_mapping is keyed by (field, mode).
+ # If separate_input_output_schemas is False, we should try to get 'serialization'
+ # if it exists, otherwise fall back to 'validation'.
+
+ mode = override_mode or field.mode
+ if not separate_input_output_schemas and mode == "validation":
+ if (field, "serialization") in field_mapping:
+ mode = "serialization"
+
+ json_schema = field_mapping[(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 +216,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"]
...... [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