resolved RESOLVED PASS · 6 tool calls · 14 s · fastapi/fastapi
🐛 Avoid accessing non-existing "$ref" key for Pydantic v2 compat remapping cf Discussion #14265
| # | Tool | Arguments | Result |
|---|---|---|---|
| 1 | run_command | {
"command": "grep -rn \"\\$ref\" fastapi | grep \"remapping\" -A 5 -B 5"
} | {"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 1", "details": {"stdout": "", "stderr": "", "exit_code": 1}}full{"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 1", "details": {"stdout": "", "stderr": "", "exit_code": 1}} |
| 2 | run_command | {
"command": "grep -rn \"\\$ref\" fastapi | head -n 20"
} | {"status": "ok", "stdout": "fastapi/openapi/models.py:103: ref: str = Field(alias=\"$ref\")\nfastapi/openapi/models.py:138: ref: Optional[str] = Field(default=Nfull{"status": "ok", "stdout": "fastapi/openapi/models.py:103: ref: str = Field(alias=\"$ref\")\nfastapi/openapi/models.py:138: ref: Optional[str] = Field(default=None, alias=\"$ref\")\nfastapi/openapi/models.py:322: ref: Optional[str] = Field(default=None, alias=\"$ref\")\nfastapi/openapi/utils.py:63: \"items\": {\"$ref\": REF_PREFIX + \"ValidationError\"},\nfastapi/openapi/utils.py:419: \"schema\": {\"$ref\": REF_PREFIX + \"HTTPValidationError\"}\nfastapi/_compat/v2.py:188: if \"$ref\" not in json_schema:\nfastapi/_compat/v2.py:264: if key == \"$ref\":\nfastapi/_compat/v2.py:265: value = schema[\"$ref\"]\nfastapi/_compat/v2.py:267: ref_name = schema[\"$ref\"].split(\"/\")[-1]\nfastapi/_compat/v2.py:270: new_schema[\"$ref\"] = REF_TEMPLATE.format(model=new_name)\nfastapi/_compat/v2.py:310: old_name = schema[\"$ref\"].split(\"/\")[-1]\n", "exit_code": 0} |
| 3 | read_file | {
"filepath": "fastapi/_compat/v2.py",
"start_line": 250,
"end_line": 320
} | {"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": " model_name_map=model_name_map,\n definitions=definitions, # type: ignore[arg-type]\n field_mafull{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": " 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_to_new_name_map[ref_name]\n new_schema[\"$ref\"] = REF_TEMPLATE.format(model=new_name)\n continue\n if isinstance(value, dict):\n new_schema[key] = _replace_refs(\n schema=value,\n old_name_to_new_name_map=old_name_to_new_name_map,\n )\n elif isinstance(value, list):\n new_value = []\n for item in value:\n if isinstance(item, dict):\n new_item = _replace_refs(\n schema=item,\n old_name_to_new_name_map=old_name_to_new_name_map,\n )\n new_value.append(new_item)\n\n else:\n new_value.append(item)\n new_schema[key] = new_value\n 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 |
| 4 | read_file | {
"end_line": 320,
"filepath": "fastapi/_compat/v2.py",
"start_line": 300
} | {"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": ") -> Tuple[\n Dict[Tuple[ModelField, Literal[\"validation\", \"serialization\"]], JsonSchemaVafull{"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 Tuple[ModelField, Literal[\"validation\", \"serialization\"]], JsonSchemaValue\n ] = {}\n for field_key, schema in field_mapping.items():\n new_schema = _replace_refs(\n schema=schema,\n", "start_line": 300, "end_line": 320, "total_lines": 479, "is_truncated": false} |
| 5 | edit_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]\n if old_name in {f\"{new_name}-Input\", f\"{new_name}-Output\"}:",
"old_string": " 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\"}:"
} | {"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@@ -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} |
| 6 | run_command | {
"command": "python -m pytest tests/test_schema_ref_pydantic_v2.py -q -x"
} | {"status": "ok", "stdout": ".. [100%]\n=============================== warnings summary ===============================\n../../../../../../../Users/jp/repos/kagfull{"status": "ok", "stdout": ".. [100%]\n=============================== warnings summary ===============================\n../../../../../../../Users/jp/repos/kaggle-gemini-coding-agent-post-training/.envs/overlays/starlette-0.50.0-py3-none-any/starlette/testclient.py:45\n /Users/jp/repos/kaggle-gemini-coding-agent-post-training/.envs/overlays/starlette-0.50.0-py3-none-any/starlette/testclient.py:45: DeprecationWarning: The anyio.abc.BlockingPortal alias is deprecated, use anyio.from_thread.BlockingPortal instead.\n _PortalFactoryType = Callable[[], AbstractContextManager[anyio.abc.BlockingPortal]]\n\n-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html\n2 passed, 1 warning in 0.40s\n", "exit_code": 0} |
| 7 | submit_patch | [] | {"status": "ok", "patch_size": 512, "files_changed": 1}full{"status": "ok", "patch_size": 512, "files_changed": 1} |
diff --git a/fastapi/_compat/v2.py b/fastapi/_compat/v2.py
index 5cd49343..51460d1d 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
.. [100%]
=============================== warnings summary ===============================
../../../../../../../Users/jp/repos/kaggle-gemini-coding-agent-post-training/.envs/overlays/starlette-0.50.0-py3-none-any/starlette/testclient.py:45
/Users/jp/repos/kaggle-gemini-coding-agent-post-training/.envs/overlays/starlette-0.50.0-py3-none-any/starlette/testclient.py:45: DeprecationWarning: The anyio.abc.BlockingPortal alias is deprecated, use anyio.from_thread.BlockingPortal instead.
_PortalFactoryType = Callable[[], AbstractContextManager[anyio.abc.BlockingPortal]]
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
2 passed, 1 warning in 0.43s