failed WRONG_FIX wrong_fix · 30 tool calls · 264 s · fastapi/fastapi
🐛 Fix optional sequence handling with new union syntax from Python 3.10 https://github.com/fastapi/fastapi/pull/14297 is only taking one union type into account. I can only recommend using https://github.com/pydantic/typing-inspection for these kind of checks (see https://typing-inspection.pydantic.dev/latest/usage/ for motivation).
| # | Tool | Arguments | Result |
|---|---|---|---|
| 1 | run_command | {
"command": "grep -rn \"union\" fastapi/params.py | head -20"
} | {"status": "ok", "stdout": "", "exit_code": 0}full{"status": "ok", "stdout": "", "exit_code": 0} |
| 2 | run_command | {
"command": "grep -rn \"Union\" fastapi/params.py | head -20"
} | {"status": "ok", "stdout": "4:from typing import Any, Callable, Dict, List, Optional, Sequence, Union\n33: default_factory: Union[Callable[[], Any], None] = _Unfull{"status": "ok", "stdout": "4:from typing import Any, Callable, Dict, List, Optional, Sequence, Union\n33: default_factory: Union[Callable[[], Any], None] = _Unset,\n36: alias_priority: Union[int, None] = _Unset,\n39: validation_alias: Union[str, None] = None,\n40: serialization_alias: Union[str, None] = None,\n56: discriminator: Union[str, None] = None,\n57: strict: Union[bool, None] = _Unset,\n58: multiple_of: Union[float, None] = _Unset,\n59: allow_inf_nan: Union[bool, None] = _Unset,\n60: max_digits: Union[int, None] = _Unset,\n61: decimal_places: Union[int, None] = _Unset,\n71: deprecated: Union[deprecated, str, bool, None] = None,\n73: json_schema_extra: Union[Dict[str, Any], None] = None,\n147: default_factory: Union[Callable[[], Any], None] = _Unset,\n150: alias_priority: Union[int, None] = _Unset,\n153: validation_alias: Union[str, None] = None,\n154: serialization_alias: Union[str, None] = None,\n170: discriminator: Union[str, None] = None,\n171: strict: Union[bool, None] = _Unset,\n172: multiple_of: Union[float, None] = _Unset,\n", "exit_code": 0} |
| 3 | run_command | {
"command": "grep -rn \"Union\" fastapi/utils.py | head -20"
} | {"status": "ok", "stdout": "12: Union,\n44:def is_body_allowed_for_status_code(status_code: Union[int, str, None]) -> bool:\n69: \"field (e.g. Union[Response, dfull{"status": "ok", "stdout": "12: Union,\n44:def is_body_allowed_for_status_code(status_code: Union[int, str, None]) -> bool:\n69: \"field (e.g. Union[Response, dict, None]) you can disable generating the \"\n81: required: Union[bool, UndefinedType] = Undefined,\n82: model_config: Union[Type[BaseConfig], None] = None,\n246: first_item: Union[DefaultPlaceholder, DefaultType],\n247: *extra_items: Union[DefaultPlaceholder, DefaultType],\n248:) -> Union[DefaultPlaceholder, DefaultType]:\n", "exit_code": 0} |
| 4 | run_command | {
"command": "grep -rn \"union\" . | grep \"Union\" | head -20"
} | {"status": "ok", "stdout": "./tests/test_union_inherited_body.py:20:def save_union_different_body(item: Union[ExtendedItem, Item]):\n./tests/test_union_body.py:full{"status": "ok", "stdout": "./tests/test_union_inherited_body.py:20:def save_union_different_body(item: Union[ExtendedItem, Item]):\n./tests/test_union_body.py:20:def save_union_body(item: Union[OtherItem, Item]):\n./tests/test_response_model_as_return_annotation.py:235:def no_response_model_annotation_union_return_model1() -> Union[User, Item]:\n./tests/test_response_model_as_return_annotation.py:240:def no_response_model_annotation_union_return_model2() -> Union[User, Item]:\n./tests/test_union_forms.py:22:def post_union_form(data: Annotated[Union[UserForm, CompanyForm], Form()]):\n./docs/ko/docs/tutorial/extra-models.md:178:<a href=\"https://docs.pydantic.dev/latest/concepts/types/#unions\" class=\"external-link\" target=\"_blank\">`Union`</a>\uc744 \uc815\uc758\ud560\ub54c\ub294 \ub354 \uad6c\uccb4\uc801\uc778 \ud0c0\uc785\uc744 \uba3c\uc800 \ud3ec\ud568\ud558\uace0, \ub35c \uad6c\uccb4\uc801\uc778 \ud0c0\uc785\uc744 \uadf8 \ub4a4\uc5d0 \ub098\uc5f4\ud574\uc57c\ud569\ub2c8\ub2e4. \uc544\ub798 \uc608\uc81c\uc5d0\uc11c\ub294 `Union[PlaneItem, CarItem]` \ub97c \ubcf4\uba74, \ub354 \uad6c\uccb4\uc801\uc778 `PlaneItem`\uc774 `CarItem`\ubcf4\ub2e4 \uc55e\uc5d0 \uc704\uce58\ud569\ub2c8\ub2e4.\n./docs/es/docs/python-types.md:399:En Python 3.10, como alternativa a usar los gen\u00e9ricos `Union` y `Optional`, puedes usar la <abbr title='tambi\u00e9n llamado \"operador OR a nivel de bits\", pero ese significado no es relevante aqu\u00ed'>barra vertical (`|`)</abbr> para declarar uniones de tipos, eso es mucho mejor y m\u00e1s simple.\n./docs/es/docs/tutorial/extra-models.md:177:Al definir una <a href=\"https://docs.pydantic.dev/latest/concepts/types/#unions\" class=\"external-link\" target=\"_blank\">`Union`</a>, incluye el tipo m\u00e1s espec\u00edfico primero, seguido por el tipo menos espec\u00edfico. En el ejemplo a continuaci\u00f3n, el m\u00e1s espec\u00edfico `PlaneItem` viene antes de `CarItem` en `Union[PlaneItem, CarItem]`.\n./docs/de/docs/python-types.md:279:## |
| 5 | run_command | {
"command": "grep -rn \"typing.Union\" fastapi/ | head -20"
} | {"status": "ok", "stdout": "fastapi/dependencies/utils.py:838: # Check if it's a Union type (covers both typing.Union and types.UnionType in Python 3.10+)\n", "full{"status": "ok", "stdout": "fastapi/dependencies/utils.py:838: # Check if it's a Union type (covers both typing.Union and types.UnionType in Python 3.10+)\n", "exit_code": 0} |
| 6 | read_file | {
"filepath": "fastapi/dependencies/utils.py",
"start_line": 800,
"end_line": 900
} | {"status": "ok", "filepath": "fastapi/dependencies/utils.py", "content": " params_to_process[key] = value\n else:\n params_to_process[key] = received_params.getfull{"status": "ok", "filepath": "fastapi/dependencies/utils.py", "content": " params_to_process[key] = value\n else:\n params_to_process[key] = received_params.get(key)\n\n if single_not_embedded_field:\n field_info = first_field.field_info\n assert isinstance(field_info, (params.Param, temp_pydantic_v1_params.Param)), (\n \"Params must be subclasses of Param\"\n )\n loc: Tuple[str, ...] = (field_info.in_.value,)\n v_, errors_ = _validate_value_with_model_field(\n field=first_field, value=params_to_process, values=values, loc=loc\n )\n return {first_field.name: v_}, errors_\n\n for field in fields:\n value = _get_multidict_value(field, received_params)\n field_info = field.field_info\n assert isinstance(field_info, (params.Param, temp_pydantic_v1_params.Param)), (\n \"Params must be subclasses of Param\"\n )\n loc = (field_info.in_.value, field.alias)\n v_, errors_ = _validate_value_with_model_field(\n field=field, value=value, values=values, loc=loc\n )\n if errors_:\n errors.extend(errors_)\n else:\n values[field.name] = v_\n return values, errors\n\n\ndef is_union_of_base_models(field_type: Any) -> bool:\n \"\"\"Check if field type is a Union where all members are BaseModel subclasses.\"\"\"\n from fastapi.types import UnionType\n\n origin = get_origin(field_type)\n\n # Check if it's a Union type (covers both typing.Union and types.UnionType in Python 3.10+)\n if origin is not Union and origin is not UnionType:\n return False\n\n union_args = get_args(field_type)\n\n for arg in union_args:\n if not _is_model_class(arg):\n return False\n\n return True\n\n\ndef _should_embed_body_fields(fields: List[ModelField]) -> bool:\n if not fields:\n return False\n # More than one dependenc |
| 7 | run_command | {
"command": "grep -rn \"is_union_of_base_models\" fastapi/ | head -20"
} | {"status": "ok", "stdout": "fastapi/dependencies/utils.py:832:def is_union_of_base_models(field_type: Any) -> bool:\nfastapi/dependencies/utils.py:869: and not full{"status": "ok", "stdout": "fastapi/dependencies/utils.py:832:def is_union_of_base_models(field_type: Any) -> bool:\nfastapi/dependencies/utils.py:869: and not is_union_of_base_models(first_field.type_)\n", "exit_code": 0} |
| 8 | run_command | {
"command": "cat > /tmp/test_union.py <<'EOF'\nfrom typing import Union, List, Optional\nfrom pydantic import BaseModel\nfrom fastapi.dependencies.utils import is_union_of_base_models\n\nclass ModelA(BaseModel):\n name: str\n\nclass ModelB(BaseModel):\n age: int\n\ndef test_unions():\n # Traditional Union\n assert is_union_of_base_models(Union[ModelA, ModelB]) is True\n # Optional (Union[ModelA, None])\n assert is_union_of_base_models(Optional[ModelA]) is False\n # Python 3.10 | syntax\n try:\n # This will fail on Python < 3.10\n union_type = ModelA | ModelB\n assert is_union_of_base_models(union_type) is True\n \n optional_type = ModelA | None\n assert is_union_of_base_models(optional_type) is False\n except TypeError:\n print(\"Python < 3.10, skipping | syntax tests\")\n\n # Not a union\n assert is_union_of_base_models(ModelA) is False\n assert is_union_of_base_models(List[ModelA]) is False\n\nif __name__ == \"__main__\":\n test_unions()\n print(\"Tests passed!\")\nEOF\npython3 /tmp/test_union.py\n"
} | {"status": "ok", "stdout": "Tests passed!\n", "exit_code": 0}full{"status": "ok", "stdout": "Tests passed!\n", "exit_code": 0} |
| 9 | run_command | {
"command": "cat > /tmp/test_union_complex.py <<'EOF'\nfrom typing import Union, List, Optional\nfrom pydantic import BaseModel\nfrom fastapi.dependencies.utils import is_union_of_base_models\n\nclass ModelA(BaseModel):\n name: str\n\nclass ModelB(BaseModel):\n age: int\n\ndef test_unions():\n # Traditional Union\n print(f\"Union[ModelA, ModelB]: {is_union_of_base_models(Union[ModelA, ModelB])}\")\n # Optional (Union[ModelA, None])\n print(f\"Optional[ModelA]: {is_union_of_base_models(Optional[ModelA])}\")\n \n try:\n # Python 3.10 | syntax\n union_type = ModelA | ModelB\n print(f\"ModelA | ModelB: {is_union_of_base_models(union_type)}\")\n \n optional_type = ModelA | None\n print(f\"ModelA | None: {is_union_of_base_models(optional_type)}\")\n \n # Union of 3 types\n union_type_3 = ModelA | ModelB | int\n print(f\"ModelA | ModelB | int: {is_union_of_base_models(union_type_3)}\")\n \n except TypeError:\n print(\"Python < 3.10, skipping | syntax tests\")\n\nif __name__ == \"__main__\":\n test_unions()\nEOF\npython3 /tmp/test_union_complex.py\n"
} | {"status": "ok", "stdout": "Union[ModelA, ModelB]: True\nOptional[ModelA]: False\nModelA | ModelB: True\nModelA | None: False\nModelA | ModelB | int: False\n", full{"status": "ok", "stdout": "Union[ModelA, ModelB]: True\nOptional[ModelA]: False\nModelA | ModelB: True\nModelA | None: False\nModelA | ModelB | int: False\n", "exit_code": 0} |
| 10 | read_file | {
"end_line": 850,
"filepath": "fastapi/dependencies/utils.py",
"start_line": 830
} | {"status": "ok", "filepath": "fastapi/dependencies/utils.py", "content": "\n\ndef is_union_of_base_models(field_type: Any) -> bool:\n \"\"\"Check if field type full{"status": "ok", "filepath": "fastapi/dependencies/utils.py", "content": "\n\ndef is_union_of_base_models(field_type: Any) -> bool:\n \"\"\"Check if field type is a Union where all members are BaseModel subclasses.\"\"\"\n from fastapi.types import UnionType\n\n origin = get_origin(field_type)\n\n # Check if it's a Union type (covers both typing.Union and types.UnionType in Python 3.10+)\n if origin is not Union and origin is not UnionType:\n return False\n\n union_args = get_args(field_type)\n\n for arg in union_args:\n if not _is_model_class(arg):\n return False\n\n return True\n\n\n", "start_line": 830, "end_line": 850, "total_lines": 1035, "is_truncated": false} |
| 11 | run_command | {
"command": "grep -rn \"_is_model_class\" fastapi/dependencies/utils.py"
} | {"status": "ok", "stdout": "30: _is_model_class,\n178: if len(fields) == 1 and _is_model_class(first_field.type_):\n520: _is_model_class(field.type_)\n845: if nfull{"status": "ok", "stdout": "30: _is_model_class,\n178: if len(fields) == 1 and _is_model_class(first_field.type_):\n520: _is_model_class(field.type_)\n845: if not _is_model_class(arg):\n868: and not _is_model_class(first_field.type_)\n938: and _is_model_class(first_field.type_)\n", "exit_code": 0} |
| 12 | read_file | {
"end_line": 50,
"filepath": "fastapi/dependencies/utils.py",
"start_line": 1
} | {"status": "ok", "filepath": "fastapi/dependencies/utils.py", "content": "import dataclasses\nimport inspect\nfrom contextlib import AsyncExitStack, contextmanafull{"status": "ok", "filepath": "fastapi/dependencies/utils.py", "content": "import dataclasses\nimport inspect\nfrom contextlib import AsyncExitStack, contextmanager\nfrom copy import copy, deepcopy\nfrom dataclasses import dataclass\nfrom typing import (\n Any,\n Callable,\n Coroutine,\n Dict,\n ForwardRef,\n List,\n Mapping,\n Optional,\n Sequence,\n Tuple,\n Type,\n Union,\n cast,\n)\n\nimport anyio\nfrom fastapi import params\nfrom fastapi._compat import (\n PYDANTIC_V2,\n ModelField,\n RequiredParam,\n Undefined,\n _is_error_wrapper,\n _is_model_class,\n copy_field_info,\n create_body_model,\n evaluate_forwardref,\n field_annotation_is_scalar,\n get_annotation_from_field_info,\n get_cached_model_fields,\n get_missing_field_error,\n is_bytes_field,\n is_bytes_sequence_field,\n is_scalar_field,\n is_scalar_sequence_field,\n is_sequence_field,\n is_uploadfile_or_nonable_uploadfile_annotation,\n is_uploadfile_sequence_annotation,\n lenient_issubclass,\n may_v1,\n sequence_types,\n serialize_sequence_value,\n value_is_sequence,\n)\n", "start_line": 1, "end_line": 50, "total_lines": 1035, "is_truncated": false} |
| 13 | run_command | {
"command": "cat > /tmp/repro_issue.py <<'EOF'\nfrom typing import Union, Optional, List\nfrom pydantic import BaseModel\nfrom fastapi.dependencies.utils import is_union_of_base_models\n\nclass ModelA(BaseModel):\n name: str\n\nclass ModelB(BaseModel):\n age: int\n\ndef test():\n # This should return True if we want it to be treated as a union of base models, \n # but the current implementation returns False because of None.\n # The issue description says \"optional sequence handling\", \n # which might mean Union[List[ModelA], None] or similar.\n \n print(f\"Union[ModelA, ModelB]: {is_union_of_base_models(Union[ModelA, ModelB])}\")\n print(f\"Optional[ModelA]: {is_union_of_base_models(Optional[ModelA])}\")\n \n try:\n print(f\"ModelA | ModelB: {is_union_of_base_models(ModelA | ModelB)}\")\n print(f\"ModelA | None: {is_union_of_base_models(ModelA | None)}\")\n except TypeError:\n print(\"Python < 3.10\")\n\nif __name__ == \"__main__\":\n test()\nEOF\npython3 /tmp/repro_issue.py\n"
} | {"status": "ok", "stdout": "Union[ModelA, ModelB]: True\nOptional[ModelA]: False\nModelA | ModelB: True\nModelA | None: False\n", "exit_code": 0}full{"status": "ok", "stdout": "Union[ModelA, ModelB]: True\nOptional[ModelA]: False\nModelA | ModelB: True\nModelA | None: False\n", "exit_code": 0} |
| 14 | run_command | {
"command": "grep -rn \"pydantic.typing_inspection\" ."
} | {"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}} |
| 15 | read_file | {
"end_line": 860,
"filepath": "fastapi/dependencies/utils.py",
"start_line": 830
} | {"status": "ok", "filepath": "fastapi/dependencies/utils.py", "content": "\n\ndef is_union_of_base_models(field_type: Any) -> bool:\n \"\"\"Check if field type full{"status": "ok", "filepath": "fastapi/dependencies/utils.py", "content": "\n\ndef is_union_of_base_models(field_type: Any) -> bool:\n \"\"\"Check if field type is a Union where all members are BaseModel subclasses.\"\"\"\n from fastapi.types import UnionType\n\n origin = get_origin(field_type)\n\n # Check if it's a Union type (covers both typing.Union and types.UnionType in Python 3.10+)\n if origin is not Union and origin is not UnionType:\n return False\n\n union_args = get_args(field_type)\n\n for arg in union_args:\n if not _is_model_class(arg):\n return False\n\n return True\n\n\ndef _should_embed_body_fields(fields: List[ModelField]) -> bool:\n if not fields:\n return False\n # More than one dependency could have the same field, it would show up as multiple\n # fields but it's the same one, so count them by name\n body_param_names_set = {field.name for field in fields}\n # A top level field has to be a single field, not multiple\n if len(body_param_names_set) > 1:\n return True\n first_field = fields[0]\n", "start_line": 830, "end_line": 860, "total_lines": 1035, "is_truncated": false} |
| 16 | run_command | {
"command": "cat > /tmp/test_optional_sequence.py <<'EOF'\nfrom typing import Union, List, Optional\nfrom pydantic import BaseModel\nfrom fastapi.dependencies.utils import is_union_of_base_models\n\nclass ModelA(BaseModel):\n name: str\n\ndef test_optional_sequence():\n # Optional sequence\n opt_seq = Optional[List[ModelA]]\n print(f\"Optional[List[ModelA]]: {is_union_of_base_models(opt_seq)}\")\n \n try:\n opt_seq_310 = List[ModelA] | None\n print(f\"List[ModelA] | None: {is_union_of_base_models(opt_seq_310)}\")\n except TypeError:\n print(\"Python < 3.10\")\n\nif __name__ == \"__main__\":\n test_optional_sequence()\nEOF\npython3 /tmp/test_optional_sequence.py\n"
} | {"status": "ok", "stdout": "Optional[List[ModelA]]: False\nList[ModelA] | None: False\n", "exit_code": 0}full{"status": "ok", "stdout": "Optional[List[ModelA]]: False\nList[ModelA] | None: False\n", "exit_code": 0} |
| 17 | run_command | {
"command": "cat > /tmp/test_is_model_class.py <<'EOF'\nfrom fastapi._compat import _is_model_class\nfrom pydantic import BaseModel\nfrom typing import List, Union\n\nclass ModelA(BaseModel):\n name: str\n\nprint(f\"ModelA: {_is_model_class(ModelA)}\")\nprint(f\"List[ModelA]: {_is_model_class(List[ModelA])}\")\nprint(f\"Union[ModelA, ModelB]: {_is_model_class(Union[ModelA, ModelA])}\")\nEOF\npython3 /tmp/test_is_model_class.py\n"
} | {"status": "ok", "stdout": "ModelA: True\nList[ModelA]: False\nUnion[ModelA, ModelB]: True\n", "exit_code": 0}full{"status": "ok", "stdout": "ModelA: True\nList[ModelA]: False\nUnion[ModelA, ModelB]: True\n", "exit_code": 0} |
| 18 | run_command | {
"command": "cat > /tmp/test_is_model_class_v2.py <<'EOF'\nfrom fastapi._compat import _is_model_class\nfrom pydantic import BaseModel\nfrom typing import List, Union, Optional\n\nclass ModelA(BaseModel):\n name: str\n\nclass ModelB(BaseModel):\n age: int\n\nprint(f\"ModelA: {_is_model_class(ModelA)}\")\nprint(f\"List[ModelA]: {_is_model_class(List[ModelA])}\")\nprint(f\"Optional[ModelA]: {_is_model_class(Optional[ModelA])}\")\nEOF\npython3 /tmp/test_is_model_class_v2.py\n"
} | {"status": "ok", "stdout": "ModelA: True\nList[ModelA]: False\nOptional[ModelA]: False\n", "exit_code": 0}full{"status": "ok", "stdout": "ModelA: True\nList[ModelA]: False\nOptional[ModelA]: False\n", "exit_code": 0} |
| 19 | run_command | {
"command": "grep -rn \"UnionType\" fastapi/ | head -20"
} | {"status": "ok", "stdout": "fastapi/dependencies/utils.py:834: from fastapi.types import UnionType\nfastapi/dependencies/utils.py:838: # Check if it's a Union tfull{"status": "ok", "stdout": "fastapi/dependencies/utils.py:834: from fastapi.types import UnionType\nfastapi/dependencies/utils.py:838: # Check if it's a Union type (covers both typing.Union and types.UnionType in Python 3.10+)\nfastapi/dependencies/utils.py:839: if origin is not Union and origin is not UnionType:\nfastapi/_compat/shared.py:20:from fastapi.types import UnionType\nfastapi/_compat/shared.py:39: types.UnionType,\nfastapi/_compat/shared.py:85: if origin is Union or origin is UnionType:\nfastapi/_compat/shared.py:111: if origin is Union or origin is UnionType:\nfastapi/_compat/shared.py:132: if origin is Union or origin is UnionType:\nfastapi/_compat/shared.py:151: if origin is Union or origin is UnionType:\nfastapi/_compat/shared.py:162: if origin is Union or origin is UnionType:\nfastapi/_compat/shared.py:171: if origin is Union or origin is UnionType:\nfastapi/_compat/shared.py:186: if origin is Union or origin is UnionType:\nfastapi/_compat/shared.py:203: if origin is Union or origin is UnionType:\nfastapi/types.py:8:UnionType = getattr(types, \"UnionType\", Union)\n", "exit_code": 0} |
| 20 | read_file | {
"end_line": 220,
"filepath": "fastapi/_compat/shared.py",
"start_line": 70
} | {"status": "ok", "filepath": "fastapi/_compat/shared.py", "content": " return isinstance(cls, type) and issubclass(cls, class_or_tuple) # type: ignore[arg-type]full{"status": "ok", "filepath": "fastapi/_compat/shared.py", "content": " return isinstance(cls, type) and issubclass(cls, class_or_tuple) # type: ignore[arg-type]\n except TypeError: # pragma: no cover\n if isinstance(cls, WithArgsTypes):\n return False\n raise # pragma: no cover\n\n\ndef _annotation_is_sequence(annotation: Union[Type[Any], None]) -> bool:\n if lenient_issubclass(annotation, (str, bytes)):\n return False\n return lenient_issubclass(annotation, sequence_types) # type: ignore[arg-type]\n\n\ndef field_annotation_is_sequence(annotation: Union[Type[Any], None]) -> bool:\n origin = get_origin(annotation)\n if origin is Union or origin is UnionType:\n for arg in get_args(annotation):\n if field_annotation_is_sequence(arg):\n return True\n return False\n return _annotation_is_sequence(annotation) or _annotation_is_sequence(\n get_origin(annotation)\n )\n\n\ndef value_is_sequence(value: Any) -> bool:\n return isinstance(value, sequence_types) and not isinstance(value, (str, bytes)) # type: ignore[arg-type]\n\n\ndef _annotation_is_complex(annotation: Union[Type[Any], None]) -> bool:\n return (\n lenient_issubclass(\n annotation, (BaseModel, may_v1.BaseModel, Mapping, UploadFile)\n )\n or _annotation_is_sequence(annotation)\n or is_dataclass(annotation)\n )\n\n\ndef field_annotation_is_complex(annotation: Union[Type[Any], None]) -> bool:\n origin = get_origin(annotation)\n if origin is Union or origin is UnionType:\n return any(field_annotation_is_complex(arg) for arg in get_args(annotation))\n\n if origin is Annotated:\n return field_annotation_is_complex(get_args(annotation)[0])\n\n return (\n _annotation_is_complex(annotation)\n or _annotation_is_complex(origin)\n or hasattr(origin, \"__pydantic_core_schema__\")\n or hasattr(origin, \"__get_pydantic |
| 21 | run_command | {
"command": "cat > /tmp/test_is_union_of_base_models_fix.py <<'EOF'\nfrom typing import Union, List, Optional, Any\nfrom pydantic import BaseModel\nfrom fastapi.dependencies.utils import is_union_of_base_models\nfrom typing import get_origin, get_args\n\nclass ModelA(BaseModel):\n name: str\n\nclass ModelB(BaseModel):\n age: int\n\ndef test_unions():\n # Traditional Union\n print(f\"Union[ModelA, ModelB]: {is_union_of_base_models(Union[ModelA, ModelB])}\")\n # Optional (Union[ModelA, None])\n print(f\"Optional[ModelA]: {is_union_of_base_models(Optional[ModelA])}\")\n \n try:\n # Python 3.10 | syntax\n print(f\"ModelA | ModelB: {is_union_of_base_models(ModelA | ModelB)}\")\n print(f\"ModelA | None: {is_union_of_base_models(ModelA | None)}\")\n except TypeError:\n print(\"Python < 3.10\")\n\nif __name__ == \"__main__\":\n test_unions()\nEOF\npython3 /tmp/test_is_union_of_base_models_fix.py\n"
} | {"status": "ok", "stdout": "Union[ModelA, ModelB]: True\nOptional[ModelA]: False\nModelA | ModelB: True\nModelA | None: False\n", "exit_code": 0}full{"status": "ok", "stdout": "Union[ModelA, ModelB]: True\nOptional[ModelA]: False\nModelA | ModelB: True\nModelA | None: False\n", "exit_code": 0} |
| 22 | edit_file | {
"filepath": "fastapi/dependencies/utils.py",
"new_string": " for arg in union_args:\\\\n if arg is not None and not _is_model_class(arg):\\\\n return False",
"old_string": " for arg in union_args:\\\\n if not _is_model_class(arg):\\\\n return False"
} | {"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)"} |
| 23 | read_file | {
"end_line": 846,
"filepath": "fastapi/dependencies/utils.py",
"start_line": 840
} | {"status": "ok", "filepath": "fastapi/dependencies/utils.py", "content": " return False\n\n union_args = get_args(field_type)\n\n for arg in union_args:\n if nofull{"status": "ok", "filepath": "fastapi/dependencies/utils.py", "content": " return False\n\n union_args = get_args(field_type)\n\n for arg in union_args:\n if not _is_model_class(arg):\n return False\n", "start_line": 840, "end_line": 846, "total_lines": 1035, "is_truncated": false} |
| 24 | edit_file | {
"filepath": "fastapi/dependencies/utils.py",
"new_string": " for arg in union_args:\n if arg is not None and not _is_model_class(arg):\n return False",
"old_string": " for arg in union_args:\n if not _is_model_class(arg):\n return False"
} | {"status": "ok", "filepath": "fastapi/dependencies/utils.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/fastapi/dependencies/utils.py\n+++ b/fastapifull{"status": "ok", "filepath": "fastapi/dependencies/utils.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/fastapi/dependencies/utils.py\n+++ b/fastapi/dependencies/utils.py\n@@ -842,7 +842,7 @@\n union_args = get_args(field_type)\n \n for arg in union_args:\n- if not _is_model_class(arg):\n+ if arg is not None and not _is_model_class(arg):\n return False\n \n return True\n", "is_truncated": false} |
| 25-26 ×2 | run_command | {
"command": "cat > /tmp/test_is_union_of_base_models_fix.py <<'EOF'\nfrom typing import Union, List, Optional, Any\nfrom pydantic import BaseModel\nfrom fastapi.dependencies.utils import is_union_of_base_models\nfrom typing import get_origin, get_args\n\nclass ModelA(BaseModel):\n name: str\n\nclass ModelB(BaseModel):\n age: int\n\ndef test_unions():\n # Traditional Union\n print(f\"Union[ModelA, ModelB]: {is_union_of_base_models(Union[ModelA, ModelB])}\")\n # Optional (Union[ModelA, None])\n print(f\"Optional[ModelA]: {is_union_of_base_models(Optional[ModelA])}\")\n \n try:\n # Python 3.10 | syntax\n print(f\"ModelA | ModelB: {is_union_of_base_models(ModelA | ModelB)}\")\n print(f\"ModelA | None: {is_union_of_base_models(ModelA | None)}\")\n except TypeError:\n print(\"Python < 3.10\")\n\nif __name__ == \"__main__\":\n test_unions()\nEOF\npython3 /tmp/test_is_union_of_base_models_fix.py\n"
} | {"status": "ok", "stdout": "Union[ModelA, ModelB]: True\nOptional[ModelA]: False\nModelA | ModelB: True\nModelA | None: False\n", "exit_code": 0}full{"status": "ok", "stdout": "Union[ModelA, ModelB]: True\nOptional[ModelA]: False\nModelA | ModelB: True\nModelA | None: False\n", "exit_code": 0} |
| 27 | run_command | {
"command": "cat > /tmp/test_is_union_of_base_models_fix.py <<'EOF'\nfrom typing import Union, List, Optional, Any\nfrom pydantic import BaseModel\nfrom fastapi.dependencies.utils import is_union_of_base_models\n\nclass ModelA(BaseModel):\n name: str\n\nclass ModelB(BaseModel):\n age: int\n\ndef test_unions():\n # Union[ModelA, ModelB] should be True\n print(f\"Union[ModelA, ModelB]: {is_union_of_base_models(Union[ModelA, ModelB])}\")\n # Optional[ModelA] (which is Union[ModelA, None]) should now be True\n print(f\"Optional[ModelA]: {is_union_of_base_models(Optional[ModelA])}\")\n \n try:\n # ModelA | ModelB should be True\n print(f\"ModelA | ModelB: {is_union_of_base_models(ModelA | ModelB)}\")\n # ModelA | None should now be True\n print(f\"ModelA | None: {is_union_of_base_models(ModelA | None)}\")\n except TypeError:\n print(\"Python < 3.10\")\n\nif __name__ == \"__main__\":\n test_unions()\nEOF\npython3 /tmp/test_is_union_of_base_models_fix.py\n"
} | {"status": "ok", "stdout": "Union[ModelA, ModelB]: True\nOptional[ModelA]: False\nModelA | ModelB: True\nModelA | None: False\n", "exit_code": 0}full{"status": "ok", "stdout": "Union[ModelA, ModelB]: True\nOptional[ModelA]: False\nModelA | ModelB: True\nModelA | None: False\n", "exit_code": 0} |
| 28 | run_command | {
"command": "cat > /tmp/test_debug.py <<'EOF'\nfrom typing import Union, Optional, get_args\nfrom pydantic import BaseModel\nfrom fastapi.dependencies.utils import is_union_of_base_models\n\nclass ModelA(BaseModel):\n name: str\n\nt = Optional[ModelA]\nprint(f\"Type: {t}\")\nprint(f\"Args: {get_args(t)}\")\n\n# In our current is_union_of_base_models:\n# for arg in union_args:\n# if arg is not None and not _is_model_class(arg):\n# return False\n# return True\n\ndef mock_is_union_of_base_models(field_type):\n from typing import get_origin, get_args\n from fastapi._compat import _is_model_class\n from fastapi.types import UnionType\n origin = get_origin(field_type)\n if origin is not Union and origin is not UnionType:\n return False\n union_args = get_args(field_type)\n print(f\"union_args: {union_args}\")\n for arg in union_args:\n print(f\"arg: {arg}, is None: {arg is None}, _is_model_class: {_is_model_class(arg)}\")\n if arg is not None and not _is_model_class(arg):\n return False\n return True\n\nprint(f\"Result: {mock_is_union_of_base_models(t)}\")\nEOF\npython3 /tmp/test_debug.py\n"
} | {"status": "ok", "stdout": "Type: typing.Optional[__main__.ModelA]\nArgs: (<class '__main__.ModelA'>, <class 'NoneType'>)\nunion_args: (<class '__main__.ModelA'full{"status": "ok", "stdout": "Type: typing.Optional[__main__.ModelA]\nArgs: (<class '__main__.ModelA'>, <class 'NoneType'>)\nunion_args: (<class '__main__.ModelA'>, <class 'NoneType'>)\narg: <class '__main__.ModelA'>, is None: False, _is_model_class: True\narg: <class 'NoneType'>, is None: False, _is_model_class: False\nResult: False\n", "exit_code": 0} |
| 29 | edit_file | {
"filepath": "fastapi/dependencies/utils.py",
"new_string": " for arg in union_args:\n if arg is not type(None) and not _is_model_class(arg):\n return False",
"old_string": " for arg in union_args:\n if arg is not None and not _is_model_class(arg):\n return False"
} | {"status": "ok", "filepath": "fastapi/dependencies/utils.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/fastapi/dependencies/utils.py\n+++ b/fastapifull{"status": "ok", "filepath": "fastapi/dependencies/utils.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/fastapi/dependencies/utils.py\n+++ b/fastapi/dependencies/utils.py\n@@ -842,7 +842,7 @@\n union_args = get_args(field_type)\n \n for arg in union_args:\n- if arg is not None and not _is_model_class(arg):\n+ if arg is not type(None) and not _is_model_class(arg):\n return False\n \n return True\n", "is_truncated": false} |
| 30 | run_command | {
"command": "cat > /tmp/test_is_union_of_base_models_fix.py <<'EOF'\nfrom typing import Union, List, Optional, Any\nfrom pydantic import BaseModel\nfrom fastapi.dependencies.utils import is_union_of_base_models\n\nclass ModelA(BaseModel):\n name: str\n\nclass ModelB(BaseModel):\n age: int\n\ndef test_unions():\n # Union[ModelA, ModelB] should be True\n print(f\"Union[ModelA, ModelB]: {is_union_of_base_models(Union[ModelA, ModelB])}\")\n # Optional[ModelA] (which is Union[ModelA, None]) should now be True\n print(f\"Optional[ModelA]: {is_union_of_base_models(Optional[ModelA])}\")\n \n try:\n # ModelA | ModelB should be True\n print(f\"ModelA | ModelB: {is_union_of_base_models(ModelA | ModelB)}\")\n # ModelA | None should now be True\n print(f\"ModelA | None: {is_union_of_base_models(ModelA | None)}\")\n except TypeError:\n print(\"Python < 3.10\")\n\nif __name__ == \"__main__\":\n test_unions()\nEOF\npython3 /tmp/test_is_union_of_base_models_fix.py\n"
} | {"status": "ok", "stdout": "Union[ModelA, ModelB]: True\nOptional[ModelA]: True\nModelA | ModelB: True\nModelA | None: True\n", "exit_code": 0}full{"status": "ok", "stdout": "Union[ModelA, ModelB]: True\nOptional[ModelA]: True\nModelA | ModelB: True\nModelA | None: True\n", "exit_code": 0} |
| 31 | submit_patch | [] | {"status": "ok", "patch_size": 469, "files_changed": 1}full{"status": "ok", "patch_size": 469, "files_changed": 1} |
diff --git a/fastapi/dependencies/utils.py b/fastapi/dependencies/utils.py
index ef3f5641..835dfd64 100644
--- a/fastapi/dependencies/utils.py
+++ b/fastapi/dependencies/utils.py
@@ -842,7 +842,7 @@ def is_union_of_base_models(field_type: Any) -> bool:
union_args = get_args(field_type)
for arg in union_args:
- if not _is_model_class(arg):
+ if arg is not type(None) and not _is_model_class(arg):
return False
return True
.s.......F
=================================== FAILURES ===================================
_________ test_serialize_sequence_value_with_optional_list_pipe_union __________
@needs_pydanticv2
@needs_py310
def test_serialize_sequence_value_with_optional_list_pipe_union():
"""Test that serialize_sequence_value handles optional lists correctly (with new syntax)."""
from fastapi._compat import v2
field_info = FieldInfo(annotation=list[str] | None)
field = v2.ModelField(name="items", field_info=field_info)
> result = v2.serialize_sequence_value(field=field, value=["a", "b", "c"])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
tests/test_compat.py:159:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
field = ModelField(field_info=FieldInfo(annotation=Union[list[str], NoneType], required=True), name='items', mode='validation')
value = ['a', 'b', 'c']
def serialize_sequence_value(*, field: ModelField, value: Any) -> Sequence[Any]:
origin_type = get_origin(field.field_info.annotation) or field.field_info.annotation
if origin_type is Union: # Handle optional sequences
union_args = get_args(field.field_info.annotation)
for union_arg in union_args:
if union_arg is type(None):
continue
origin_type = get_origin(union_arg) or union_arg
break
> assert issubclass(origin_type, shared.sequence_types) # type: ignore[arg-type]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E AssertionError
fastapi/_compat/v2.py:391: AssertionError
=============================== 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
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 8 passed, 1 skipped, 1 warning in 0.74s