← eval_submission_analyzer

fastapi_14349

failed WRONG_FIX wrong_fix · 6 tool calls · 33 s · fastapi/fastapi

Task input

🐛 Fix handling of JSON Schema attributes named "$ref"

Fix handling of JSON Schema attributes named "$ref"

Related to: https://github.com/fastapi/fastapi/issues/14344

Tool calls (7)

#ToolArgumentsResult
1run_command
{
 "command": "grep -rn \"\\$ref\" . | head -40"
}
{"status": "ok", "stdout": "./fastapi/_compat/v2.py:188: if \"$ref\" not in json_schema:\n./fastapi/_compat/v2.py:264: if key == \"$ref\":\n./fastapi/_compat/v2
full
{"status": "ok", "stdout": "./fastapi/_compat/v2.py:188:    if \"$ref\" not in json_schema:\n./fastapi/_compat/v2.py:264:        if key == \"$ref\":\n./fastapi/_compat/v2.py:265:            ref_name = schema[\"$ref\"].split(\"/\")[-1]\n./fastapi/_compat/v2.py:268:                new_schema[\"$ref\"] = REF_TEMPLATE.format(model=new_name)\n./fastapi/_compat/v2.py:270:                new_schema[\"$ref\"] = schema[\"$ref\"]\n./fastapi/_compat/v2.py:310:        old_name = schema[\"$ref\"].split(\"/\")[-1]\n./fastapi/openapi/models.py:103:    ref: str = Field(alias=\"$ref\")\n./fastapi/openapi/models.py:138:    ref: Optional[str] = Field(default=None, alias=\"$ref\")\n./fastapi/openapi/models.py:322:    ref: Optional[str] = Field(default=None, alias=\"$ref\")\n./fastapi/openapi/utils.py:63:            \"items\": {\"$ref\": REF_PREFIX + \"ValidationError\"},\n./fastapi/openapi/utils.py:419:                            \"schema\": {\"$ref\": REF_PREFIX + \"HTTPValidationError\"}\n./tests/test_multi_query_errors.py:82:                                        \"$ref\": \"#/components/schemas/HTTPValidationError\"\n./tests/test_multi_query_errors.py:130:                            \"items\": {\"$ref\": \"#/components/schemas/ValidationError\"},\n./tests/test_sub_callbacks.py:133:                                    \"schema\": {\"$ref\": \"#/components/schemas/Invoice\"}\n./tests/test_sub_callbacks.py:148:                                            \"$ref\": \"#/components/schemas/HTTPValidationError\"\n./tests/test_sub_callbacks.py:165:                                                        \"$ref\": \"#/components/schemas/Event\"\n./tests/test_sub_callbacks.py:182:                                                            \"$ref\": \"#/components/schemas/HTTPValidationError\"\n./tests/test_sub_callbacks.py:201:                                                        \"$ref\": \"#/components/schemas/InvoiceEvent\"\n./tests/test_sub_callbacks.py:212:                              
2read_file
{
 "end_line": 320,
 "filepath": "fastapi/_compat/v2.py",
 "start_line": 180
}
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": " ],\n separate_input_output_schemas: bool = True,\n) -> Dict[str, Any]:\n override_mode: Union
full
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": "    ],\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        
3read_file
{
 "end_line": 315,
 "filepath": "fastapi/_compat/v2.py",
 "start_line": 290
}
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": " return new_schema\n\n\ndef _remap_definitions_and_field_mappings(\n *,\n model_name_map: Mode
full
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": "    return new_schema\n\n\ndef _remap_definitions_and_field_mappings(\n    *,\n    model_name_map: ModelNameMap,\n    definitions: Dict[str, Any],\n    field_mapping: Dict[\n        Tuple[ModelField, Literal[\"validation\", \"serialization\"]], JsonSchemaValue\n    ],\n) -> Tuple[\n    Dict[Tuple[ModelField, Literal[\"validation\", \"serialization\"]], JsonSchemaValue],\n    Dict[str, Any],\n]:\n    old_name_to_new_name_map = {}\n    for field_key, schema in field_mapping.items():\n        model = field_key[0].type_\n        if model not in model_name_map:\n            continue\n        new_name = model_name_map[model]\n        old_name = schema[\"$ref\"].split(\"/\")[-1]\n        if old_name in {f\"{new_name}-Input\", f\"{new_name}-Output\"}:\n            continue\n        old_name_to_new_name_map[old_name] = new_name\n\n    new_field_mapping: Dict[\n", "start_line": 290, "end_line": 315, "total_lines": 479, "is_truncated": false}
4-5 ×2read_file
{
 "end_line": 315,
 "filepath": "fastapi/_compat/v2.py",
 "start_line": 300
}
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": ") -> Tuple[\n Dict[Tuple[ModelField, Literal[\"validation\", \"serialization\"]], JsonSchemaVa
full
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": ") -> Tuple[\n    Dict[Tuple[ModelField, Literal[\"validation\", \"serialization\"]], JsonSchemaValue],\n    Dict[str, Any],\n]:\n    old_name_to_new_name_map = {}\n    for field_key, schema in field_mapping.items():\n        model = field_key[0].type_\n        if model not in model_name_map:\n            continue\n        new_name = model_name_map[model]\n        old_name = schema[\"$ref\"].split(\"/\")[-1]\n        if old_name in {f\"{new_name}-Input\", f\"{new_name}-Output\"}:\n            continue\n        old_name_to_new_name_map[old_name] = new_name\n\n    new_field_mapping: Dict[\n", "start_line": 300, "end_line": 315, "total_lines": 479, "is_truncated": false}
6edit_file
{
 "filepath": "fastapi/_compat/v2.py",
 "new_string": "        new_name = model_name_map[model]\n        if \"$ref\" not in schema:\n            continue\n        old_name = schema[\"$ref\"].split(\"/\")[-1]",
 "old_string": "        new_name = model_name_map[model]\n        old_name = schema[\"$ref\"].split(\"/\")[-1]"
}
{"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@@ -307,6 +307,8 @@\n         if model not in model_name_map:\n             continue\n         new_name = model_name_map[model]\n+        if \"$ref\" not in schema:\n+            continue\n         old_name = schema[\"$ref\"].split(\"/\")[-1]\n         if old_name in {f\"{new_name}-Input\", f\"{new_name}-Output\"}:\n             continue\n", "is_truncated": false}
7submit_patch
[]
{"status": "ok", "patch_size": 512, "files_changed": 1}
full
{"status": "ok", "patch_size": 512, "files_changed": 1}

Patch

diff --git a/fastapi/_compat/v2.py b/fastapi/_compat/v2.py
index 6a87b9ae..4ef852ea 100644
--- a/fastapi/_compat/v2.py
+++ b/fastapi/_compat/v2.py
@@ -307,6 +307,8 @@ def _remap_definitions_and_field_mappings(
         if model not in model_name_map:
             continue
         new_name = model_name_map[model]
+        if "$ref" not in schema:
+            continue
         old_name = schema["$ref"].split("/")[-1]
         if old_name in {f"{new_name}-Input", f"{new_name}-Output"}:
             continue

Test output

show
st(
/kaggle/tmp/envs/fastapi/lib/python3.13/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/kaggle/tmp/envs/fastapi/lib/python3.13/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
/kaggle/tmp/envs/fastapi/lib/python3.13/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
/kaggle/tmp/envs/fastapi/lib/python3.13/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/kaggle/tmp/envs/fastapi/lib/python3.13/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/kaggle/tmp/envs/overlays/starlette-0.49.3-py3-none-any/starlette/testclient.py:354: in handle_request
    raise exc
/kaggle/tmp/envs/overlays/starlette-0.49.3-py3-none-any/starlette/testclient.py:351: in handle_request
    portal.call(self.app, scope, receive, send)
/kaggle/tmp/envs/fastapi/lib/python3.13/site-packages/anyio/from_thread.py:340: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/root/.local/share/uv/python/cpython-3.13.14-linux-x86_64-gnu/lib/python3.13/concurrent/futures/_base.py:460: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/root/.local/share/uv/python/cpython-3.13.14-linux-x86_64-gnu/lib/python3.13/concurrent/futures/_base.py:402: in __get_result
    raise self._exception
/kaggle/tmp/envs/fastapi/lib/python3.13/site-packages/anyio/from_thread.py:265: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
fastapi/applications.py:1134: in __call__
    await super().__call__(scope, receive, send)
/kaggle/tmp/envs/overlays/starlette-0.49.3-py3-none-any/starlette/applications.py:113: in __call__
    await self.middleware_stack(scope, receive, send)
/kaggle/tmp/envs/overlays/starlette-0.49.3-py3-none-any/starlette/middleware/errors.py:186: in __call__
    raise exc
/kaggle/tmp/envs/overlays/starlette-0.49.3-py3-none-any/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
/kaggle/tmp/envs/overlays/starlette-0.49.3-py3-none-any/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
/kaggle/tmp/envs/overlays/starlette-0.49.3-py3-none-any/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
/kaggle/tmp/envs/overlays/starlette-0.49.3-py3-none-any/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
/kaggle/tmp/envs/overlays/starlette-0.49.3-py3-none-any/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
/kaggle/tmp/envs/overlays/starlette-0.49.3-py3-none-any/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
/kaggle/tmp/envs/overlays/starlette-0.49.3-py3-none-any/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
/kaggle/tmp/envs/overlays/starlette-0.49.3-py3-none-any/starlette/routing.py:78: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
/kaggle/tmp/envs/overlays/starlette-0.49.3-py3-none-any/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
/kaggle/tmp/envs/overlays/starlette-0.49.3-py3-none-any/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
/kaggle/tmp/envs/overlays/starlette-0.49.3-py3-none-any/starlette/routing.py:75: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
fastapi/applications.py:1089: in openapi
    return JSONResponse(self.openapi())
                        ^^^^^^^^^^^^^^
fastapi/applications.py:1060: in openapi
    self.openapi_schema = get_openapi(
fastapi/openapi/utils.py:504: in get_openapi
    field_mapping, definitions = get_definitions(
fastapi/_compat/main.py:292: in get_definitions
    v2_field_maps, v2_definitions = v2.get_definitions(
fastapi/_compat/v2.py:249: in get_definitions
    new_mapping, new_definitions = _remap_definitions_and_field_mappings(
fastapi/_compat/v2.py:333: in _remap_definitions_and_field_mappings
    new_value = _replace_refs(
fastapi/_compat/v2.py:273: in _replace_refs
    new_schema[key] = _replace_refs(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

schema = {'$ref': {'title': '$Ref', 'type': 'string'}}
old_name_to_new_name_map = {'ModelWithRef': 'ModelWithRef'}

    def _replace_refs(
        *,
        schema: Dict[str, Any],
        old_name_to_new_name_map: Dict[str, str],
    ) -> Dict[str, Any]:
        new_schema = deepcopy(schema)
        for key, value in new_schema.items():
            if key == "$ref":
>               ref_name = schema["$ref"].split("/")[-1]
                           ^^^^^^^^^^^^^^^^^^^^
E               AttributeError: 'dict' object has no attribute 'split'

fastapi/_compat/v2.py:265: AttributeError
=============================== warnings summary ===============================
../../../../../../kaggle/tmp/envs/overlays/starlette-0.49.3-py3-none-any/starlette/testclient.py:51
  /kaggle/tmp/envs/overlays/starlette-0.49.3-py3-none-any/starlette/testclient.py:51: 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
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 1 passed, 1 warning in 0.89s