resolved RESOLVED PASS · 32 tool calls · 291 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/fastapi | grep \"Python 3.10\" || grep -rn \"Union\" fastapi/fastapi | head -20"
} | {"status": "ok", "stdout": "", "stderr": "grep: fastapi/fastapi: No such file or directory\ngrep: fastapi/fastapi: No such file or directory\n", "exit_code": 0}full{"status": "ok", "stdout": "", "stderr": "grep: fastapi/fastapi: No such file or directory\ngrep: fastapi/fastapi: No such file or directory\n", "exit_code": 0} |
| 2 | run_command | {
"command": "ls -F"
} | {"status": "ok", "stdout": "CITATION.cff\nCONTRIBUTING.md\ndocs/\ndocs_src/\nfastapi/\nLICENSE\npdm_build.py\npyproject.toml\npytest.ini\nREADME.md\nrequirementfull{"status": "ok", "stdout": "CITATION.cff\nCONTRIBUTING.md\ndocs/\ndocs_src/\nfastapi/\nLICENSE\npdm_build.py\npyproject.toml\npytest.ini\nREADME.md\nrequirements-docs-tests.txt\nrequirements-docs.txt\nrequirements-github-actions.txt\nrequirements-tests.txt\nrequirements-translations.txt\nrequirements.txt\nscripts/\nSECURITY.md\ntests/\n", "stderr": "", "exit_code": 0} |
| 3 | run_command | {
"command": "grep -rn \"Union\" fastapi/ | grep \"3.10\" | 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", "stderr": "", "exit_code": 0} |
| 4 | run_command | {
"command": "sed -n '800,860p' fastapi/dependencies/utils.py"
} | {"status": "ok", "stdout": " params_to_process[key] = value\n else:\n params_to_process[key] = received_params.get(key)\n\n if single_not_embedded_field:\n fielfull{"status": "ok", "stdout": " 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 dependency could have the same field, it would show up |
| 5 | run_command | {
"command": "grep -rn \"UnionType\" fastapi/"
} | {"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/types.py:8:UnionType = getattr(types, \"UnionType\", Union)\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:\n", "stderr": "", "exit_code": 0} |
| 6 | run_command | {
"command": "sed -n '10,210p' fastapi/_compat/shared.py"
} | {"status": "ok", "stdout": " List,\n Mapping,\n Sequence,\n Set,\n Tuple,\n Type,\n Union,\n)\n\nfrom fastapi._compat import may_v1\nfrom fastapi.types import Ufull{"status": "ok", "stdout": " List,\n Mapping,\n Sequence,\n Set,\n Tuple,\n Type,\n Union,\n)\n\nfrom fastapi._compat import may_v1\nfrom fastapi.types import UnionType\nfrom pydantic import BaseModel\nfrom pydantic.version import VERSION as PYDANTIC_VERSION\nfrom starlette.datastructures import UploadFile\nfrom typing_extensions import Annotated, get_args, get_origin\n\n# Copy from Pydantic v2, compatible with v1\nif sys.version_info < (3, 9):\n # Pydantic no longer supports Python 3.8, this might be incorrect, but the code\n # this is used for is also never reached in this codebase, as it's a copy of\n # Pydantic's lenient_issubclass, just for compatibility with v1\n # TODO: remove when dropping support for Python 3.8\n WithArgsTypes: Tuple[Any, ...] = ()\nelif sys.version_info < (3, 10):\n WithArgsTypes: tuple[Any, ...] = (typing._GenericAlias, types.GenericAlias) # type: ignore[attr-defined]\nelse:\n WithArgsTypes: tuple[Any, ...] = (\n typing._GenericAlias, # type: ignore[attr-defined]\n types.GenericAlias,\n types.UnionType,\n ) # pyright: ignore[reportAttributeAccessIssue]\n\nPYDANTIC_VERSION_MINOR_TUPLE = tuple(int(x) for x in PYDANTIC_VERSION.split(\".\")[:2])\nPYDANTIC_V2 = PYDANTIC_VERSION_MINOR_TUPLE[0] == 2\n\n\nsequence_annotation_to_type = {\n Sequence: list,\n List: list,\n list: list,\n Tuple: tuple,\n tuple: tuple,\n Set: set,\n set: set,\n FrozenSet: frozenset,\n frozenset: frozenset,\n Deque: deque,\n deque: deque,\n}\n\nsequence_types = tuple(sequence_annotation_to_type.keys())\n\nUrl: Type[Any]\n\n\n# Copy of Pydantic v2, compatible with v1\ndef lenient_issubclass(\n cls: Any, class_or_tuple: Union[Type[Any], Tuple[Type[Any], ...], None]\n) -> bool:\n try:\n 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) |
| 7 | run_command | {
"command": "python3 - <<'PY'\nimport typing\nimport types\nfrom typing_extensions import get_origin, get_args\n\ndef check_field_annotation_is_sequence(annotation):\n # Simulating the logic in fastapi/_compat/shared.py\n # sequence_types = (Sequence, List, list, Tuple, tuple, Set, set, FrozenSet, frozenset, Deque, deque)\n from typing import Sequence, List, Tuple, Set, FrozenSet\n from collections import deque\n sequence_types = (Sequence, List, list, Tuple, tuple, Set, set, FrozenSet, frozenset, deque)\n \n def lenient_issubclass(cls, class_or_tuple):\n try:\n return isinstance(cls, type) and issubclass(cls, class_or_tuple)\n except TypeError:\n return False\n\n def _annotation_is_sequence(annotation):\n if lenient_issubclass(annotation, (str, bytes)):\n return False\n return lenient_issubclass(annotation, sequence_types)\n\n origin = get_origin(annotation)\n # This is the part we are testing\n UnionType = getattr(types, \"UnionType\", typing.Union)\n if origin is typing.Union or origin is UnionType:\n for arg in get_args(annotation):\n # We need to call field_annotation_is_sequence recursively, \n # but for this test we'll just use the inner logic.\n # Let's refine the structure to match the real function.\n pass\n return False\n\n# The actual function from the codebase\ndef field_annotation_is_sequence_impl(annotation, sequence_types, lenient_issubclass, _annotation_is_sequence):\n origin = get_origin(annotation)\n UnionType = getattr(types, \"UnionType\", typing.Union)\n if origin is typing.Union or origin is UnionType:\n for arg in get_args(annotation):\n # Recursive call\n if field_annotation_is_sequence_impl(arg, sequence_types, lenient_issubclass, _annotation_is_sequence):\n return True\n return False\n return _annotation_is_sequence(annotation) or _annotation_is_sequence(\n get_origin(annotation)\n )\n\ndef _annotation_is_sequence_impl(annotation, sequence_types, lenient_issubclass):\n if lenient_issubclass(annotation, (str, bytes)):\n return False\n return lenient_issubclass(annotation, sequence_types)\n\ndef lenient_issubclass_impl(cls, class_or_tuple):\n try:\n return isinstance(cls, type) and issubclass(cls, class_or_tuple)\n except TypeError:\n return False\n\nsequence_types = (typing.Sequence, typing.List, list, typing.Tuple, tuple, typing.Set, set, typing.FrozenSet, frozenset, \n # Use a dummy for deque since it might not be imported\n type(None)) # Just a placeholder\n\n# Need real sequence types\nfrom collections import deque\nsequence_types = (typing.Sequence, typing.List, list, typing.Tuple, tuple, typing.Set, set, typing.FrozenSet, frozenset, deque)\n\ndef test():\n # Python 3.10+ union syntax: list[int] | None\n # This should be recognized as a sequence.\n try:\n annotation = list[int] | None\n except TypeError:\n # Fallback for Python < 3.10\n annotation = typing.Union[typing.List[int], None]\n\n result = field_annotation_is_sequence_impl(\n annotation, \n sequence_types, \n lenient_issubclass_impl, \n lambda a: _annotation_is_sequence_impl(a, sequence_types, lenient_issubclass_impl)\n )\n print(f\"Annotation: {annotation}, Is sequence: {result}\")\n\ntest()\nPY\n"
} | {"status": "ok", "stdout": "Annotation: list[int] | None, Is sequence: True\n", "stderr": "", "exit_code": 0}full{"status": "ok", "stdout": "Annotation: list[int] | None, Is sequence: True\n", "stderr": "", "exit_code": 0} |
| 8 | run_command | {
"command": "python3 - <<'PY'\nimport typing\nimport types\nfrom typing_extensions import get_origin, get_args\n\n# Mocking the environment as in fastapi/_compat/shared.py\nfrom collections import deque\nfrom typing import Sequence, List, Tuple, Set, FrozenSet\n\nsequence_types = (Sequence, List, list, Tuple, tuple, Set, set, FrozenSet, frozenset, deque)\n\ndef lenient_issubclass(cls, class_or_tuple):\n try:\n return isinstance(cls, type) and issubclass(cls, class_or_tuple)\n except TypeError:\n return False\n\ndef _annotation_is_sequence(annotation):\n if lenient_issubclass(annotation, (str, bytes)):\n return False\n return lenient_issubclass(annotation, sequence_types)\n\ndef field_annotation_is_sequence(annotation):\n origin = get_origin(annotation)\n UnionType = getattr(types, \"UnionType\", typing.Union)\n if origin is typing.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\ndef test():\n # Case 1: list[int] | None\n ann1 = list[int] | None\n print(f\"list[int] | None: {field_annotation_is_sequence(ann1)}\")\n \n # Case 2: list[int] | str\n # This should be True because list[int] is a sequence.\n ann2 = list[int] | str\n print(f\"list[int] | str: {field_annotation_is_sequence(ann2)}\")\n\n # Case 3: str | int\n ann3 = str | int\n print(f\"str | int: {field_annotation_is_sequence(ann3)}\")\n\ntest()\nPY\n"
} | {"status": "ok", "stdout": "list[int] | None: True\nlist[int] | str: True\nstr | int: False\n", "stderr": "", "exit_code": 0}full{"status": "ok", "stdout": "list[int] | None: True\nlist[int] | str: True\nstr | int: False\n", "stderr": "", "exit_code": 0} |
| 9 | run_command | {
"command": "python3 - <<'PY'\nimport typing\nimport types\nfrom typing_extensions import get_origin, get_args\n\nfrom collections import deque\nfrom typing import Sequence, List, Tuple, Set, FrozenSet\n\nsequence_types = (Sequence, List, list, Tuple, tuple, Set, set, FrozenSet, frozenset, deque)\n\ndef lenient_issubclass(cls, class_or_tuple):\n try:\n return isinstance(cls, type) and issubclass(cls, class_or_tuple)\n except TypeError:\n return False\n\ndef _annotation_is_sequence(annotation):\n if lenient_issubclass(annotation, (str, bytes)):\n return False\n return lenient_issubclass(annotation, sequence_types)\n\ndef field_annotation_is_scalar(annotation):\n # Simulation of field_annotation_is_scalar\n # field_annotation_is_complex is what it uses.\n # In a real scenario, it would check if it's a sequence or BaseModel etc.\n if _annotation_is_sequence(annotation) or _annotation_is_sequence(get_origin(annotation)):\n return False\n return True\n\ndef field_annotation_is_scalar_sequence(annotation):\n origin = get_origin(annotation)\n UnionType = getattr(types, \"UnionType\", typing.Union)\n if origin is typing.Union or origin is UnionType:\n at_least_one_scalar_sequence = False\n for arg in get_args(annotation):\n if field_annotation_is_scalar_sequence(arg):\n at_least_one_scalar_sequence = True\n continue\n elif not field_annotation_is_scalar(arg):\n return False\n return at_least_one_scalar_sequence\n \n # return field_annotation_is_sequence(annotation) and all(field_annotation_is_scalar(sub) for sub in get_args(annotation))\n # Simulating field_annotation_is_sequence logic\n is_seq = _annotation_is_sequence(annotation) or _annotation_is_sequence(origin)\n if is_seq:\n return all(field_annotation_is_scalar(sub) for sub in get_args(annotation))\n return False\n\ndef test():\n # The problematic case: list[int] | None\n # list[int] is a scalar sequence.\n # None is a scalar.\n # So list[int] | None should be a scalar sequence.\n \n ann1 = list[int] | None\n print(f\"list[int] | None: {field_annotation_is_scalar_sequence(ann1)}\")\n \n # Case: list[int] | list[str]\n # list[int] is a scalar sequence, list[str] is a scalar sequence.\n # This should be a scalar sequence.\n ann2 = list[int] | list[str]\n print(f\"list[int] | list[str]: {field_annotation_is_scalar_sequence(ann2)}\")\n \n # Case: list[int] | dict[str, int]\n # dict[str, int] is complex but not a scalar sequence.\n # So this should return False.\n ann3 = list[int] | dict[str, int]\n print(f\"list[int] | dict[str, int]: {field_annotation_is_scalar_sequence(ann3)}\")\n\ntest()\nPY\n"
} | {"status": "ok", "stdout": "list[int] | None: True\nlist[int] | list[str]: True\nlist[int] | dict[str, int]: True\n", "stderr": "", "exit_code": 0}full{"status": "ok", "stdout": "list[int] | None: True\nlist[int] | list[str]: True\nlist[int] | dict[str, int]: True\n", "stderr": "", "exit_code": 0} |
| 10 | run_command | {
"command": "python3 - <<'PY'\nimport typing\nimport types\nfrom typing_extensions import get_origin, get_args\nfrom collections import deque\nfrom typing import Sequence, List, Tuple, Set, FrozenSet\n\nsequence_types = (Sequence, List, list, Tuple, tuple, Set, set, FrozenSet, frozenset, deque)\n\ndef lenient_issubclass(cls, class_or_tuple):\n try:\n return isinstance(cls, type) and issubclass(cls, class_or_tuple)\n except TypeError:\n return False\n\ndef _annotation_is_sequence(annotation):\n if lenient_issubclass(annotation, (str, bytes)):\n return False\n return lenient_issubclass(annotation, sequence_types)\n\ndef field_annotation_is_complex(annotation):\n # Simulating the logic: a complex type is a sequence or a BaseModel etc.\n # For this test, let's just say it's complex if it's a sequence or a dict.\n origin = get_origin(annotation)\n if origin is typing.Union or origin is getattr(types, \"UnionType\", typing.Union):\n return any(field_annotation_is_complex(arg) for arg in get_args(annotation))\n \n if _annotation_is_sequence(annotation) or _annotation_is_sequence(origin):\n return True\n if origin is dict:\n return True\n return False\n\ndef field_annotation_is_scalar(annotation):\n return not field_annotation_is_complex(annotation)\n\ndef field_annotation_is_scalar_sequence(annotation):\n origin = get_origin(annotation)\n UnionType = getattr(types, \"UnionType\", typing.Union)\n if origin is typing.Union or origin is UnionType:\n at_least_one_scalar_sequence = False\n for arg in get_args(annotation):\n if field_annotation_is_scalar_sequence(arg):\n at_least_one_scalar_sequence = True\n continue\n elif not field_annotation_is_scalar(arg):\n return False\n return at_least_one_scalar_sequence\n \n # Simulating field_annotation_is_sequence(annotation)\n is_seq = _annotation_is_sequence(annotation) or _annotation_is_sequence(origin)\n if is_seq:\n return all(field_annotation_is_scalar(sub) for sub in get_args(annotation))\n return False\n\ndef test():\n # list[int] is a scalar sequence.\n # None is a scalar.\n # list[int] | None should be True.\n ann1 = list[int] | None\n print(f\"list[int] | None: {field_annotation_is_scalar_sequence(ann1)}\")\n \n # list[int] | list[str]\n # Both are scalar sequences.\n # Should be True.\n ann2 = list[int] | list[str]\n print(f\"list[int] | list[str]: {field_annotation_is_scalar_sequence(ann2)}\")\n \n # list[int] | dict[str, int]\n # list[int] is scalar sequence.\n # dict[str, int] is complex but NOT a scalar.\n # This should return False.\n ann3 = list[int] | dict[str, int]\n print(f\"list[int] | dict[str, int]: {field_annotation_is_scalar_sequence(ann3)}\")\n\ntest()\nPY\n"
} | {"status": "ok", "stdout": "list[int] | None: True\nlist[int] | list[str]: True\nlist[int] | dict[str, int]: False\n", "stderr": "", "exit_code": 0}full{"status": "ok", "stdout": "list[int] | None: True\nlist[int] | list[str]: True\nlist[int] | dict[str, int]: False\n", "stderr": "", "exit_code": 0} |
| 11 | run_command | {
"command": "grep -rn \"typing-inspection\" ."
} | {"status": "error", "error_type": "CommandError", "error_message": "", "details": {"stdout": "", "stderr": "", "exit_code": 1}}full{"status": "error", "error_type": "CommandError", "error_message": "", "details": {"stdout": "", "stderr": "", "exit_code": 1}} |
| 12 | run_command | {
"command": "ls -F fastapi/types.py"
} | {"status": "ok", "stdout": "fastapi/types.py\n", "stderr": "", "exit_code": 0}full{"status": "ok", "stdout": "fastapi/types.py\n", "stderr": "", "exit_code": 0} |
| 13 | run_command | {
"command": "cat fastapi/types.py"
} | {"status": "ok", "stdout": "import types\nfrom enum import Enum\nfrom typing import Any, Callable, Dict, Optional, Set, Tuple, Type, TypeVar, Union\n\nfrom pydafull{"status": "ok", "stdout": "import types\nfrom enum import Enum\nfrom typing import Any, Callable, Dict, Optional, Set, Tuple, Type, TypeVar, Union\n\nfrom pydantic import BaseModel\n\nDecoratedCallable = TypeVar(\"DecoratedCallable\", bound=Callable[..., Any])\nUnionType = getattr(types, \"UnionType\", Union)\nModelNameMap = Dict[Union[Type[BaseModel], Type[Enum]], str]\nIncEx = Union[Set[int], Set[str], Dict[int, Any], Dict[str, Any]]\nDependencyCacheKey = Tuple[Optional[Callable[..., Any]], Tuple[str, ...], str]\n", "stderr": "", "exit_code": 0} |
| 14 | run_command | {
"command": "python3 - <<'PY'\nimport typing\nimport types\nfrom typing_extensions import get_origin, get_args\nfrom collections import deque\nfrom typing import Sequence, List, Tuple, Set, FrozenSet\n\nsequence_types = (Sequence, List, list, Tuple, tuple, Set, set, FrozenSet, frozenset, deque)\n\ndef lenient_issubclass(cls, class_or_tuple):\n try:\n return isinstance(cls, type) and issubclass(cls, class_or_tuple)\n except TypeError:\n return False\n\ndef _annotation_is_sequence(annotation):\n if lenient_issubclass(annotation, (str, bytes)):\n return False\n return lenient_issubclass(annotation, sequence_types)\n\ndef field_annotation_is_complex(annotation):\n origin = get_origin(annotation)\n UnionType = getattr(types, \"UnionType\", typing.Union)\n if origin is typing.Union or origin is UnionType:\n return any(field_annotation_is_complex(arg) for arg in get_args(annotation))\n \n if _annotation_is_sequence(annotation) or _annotation_is_sequence(origin):\n return True\n if origin is dict:\n return True\n return False\n\ndef field_annotation_is_scalar(annotation):\n return not field_annotation_is_complex(annotation)\n\ndef field_annotation_is_scalar_sequence(annotation):\n origin = get_origin(annotation)\n UnionType = getattr(types, \"UnionType\", typing.Union)\n if origin is typing.Union or origin is UnionType:\n at_least_one_scalar_sequence = False\n for arg in get_args(annotation):\n if field_annotation_is_scalar_sequence(arg):\n at_least_one_scalar_sequence = True\n continue\n elif not field_annotation_is_scalar(arg):\n return False\n return at_least_one_scalar_sequence\n \n is_seq = _annotation_is_sequence(annotation) or _annotation_is_sequence(origin)\n if is_seq:\n return all(field_annotation_is_scalar(sub) for sub in get_args(annotation))\n return False\n\ndef test():\n # list[int] | None\n ann1 = list[int] | None\n print(f\"list[int] | None: {field_annotation_is_scalar_sequence(ann1)}\")\n \n # list[int] | list[str]\n ann2 = list[int] | list[str]\n print(f\"list[int] | list[str]: {field_annotation_is_scalar_sequence(ann2)}\")\n \n # list[int] | dict[str, int]\n ann3 = list[int] | dict[str, int]\n print(f\"list[int] | dict[str, int]: {field_annotation_is_scalar_sequence(ann3)}\")\n\ntest()\nPY\n"
} | {"status": "ok", "stdout": "list[int] | None: True\nlist[int] | list[str]: True\nlist[int] | dict[str, int]: False\n", "stderr": "", "exit_code": 0}full{"status": "ok", "stdout": "list[int] | None: True\nlist[int] | list[str]: True\nlist[int] | dict[str, int]: False\n", "stderr": "", "exit_code": 0} |
| 15 | run_command | {
"command": "grep -rn \"typing-inspection\" ."
} | {"status": "error", "error_type": "CommandError", "error_message": "", "details": {"stdout": "", "stderr": "", "exit_code": 1}}full{"status": "error", "error_type": "CommandError", "error_message": "", "details": {"stdout": "", "stderr": "", "exit_code": 1}} |
| 16 | run_command | {
"command": "python3 - <<'PY'\nimport typing\nimport types\nfrom typing_extensions import get_origin, get_args\n\ndef test_union_types():\n # Testing how get_origin and get_args handle Python 3.10 union syntax\n try:\n ann = list[int] | None\n except TypeError:\n ann = typing.Union[typing.List[int], None]\n \n print(f\"Annotation: {ann}\")\n print(f\"Origin: {get_origin(ann)}\")\n print(f\"Args: {get_args(ann)}\")\n \n UnionType = getattr(types, \"UnionType\", typing.Union)\n print(f\"UnionType: {UnionType}\")\n print(f\"Origin is Union: {get_origin(ann) is typing.Union}\")\n print(f\"Origin is UnionType: {get_origin(ann) is UnionType}\")\n\ntest_union_types()\nPY\n"
} | {"status": "ok", "stdout": "Annotation: list[int] | None\nOrigin: <class 'types.UnionType'>\nArgs: (list[int], <class 'NoneType'>)\nUnionType: <class 'types.Unifull{"status": "ok", "stdout": "Annotation: list[int] | None\nOrigin: <class 'types.UnionType'>\nArgs: (list[int], <class 'NoneType'>)\nUnionType: <class 'types.UnionType'>\nOrigin is Union: False\nOrigin is UnionType: True\n", "stderr": "", "exit_code": 0} |
| 17 | run_command | {
"command": "python3 - <<'PY'\nfrom pathlib import Path\np = Path(\"fastapi/_compat/shared.py\")\ns = p.read_text()\n# The issue report mentions that the current implementation is only taking one union type into account.\n# Looking at field_annotation_is_scalar_sequence:\n# if origin is Union or origin is UnionType:\n# at_least_one_scalar_sequence = False\n# for arg in get_args(annotation):\n# if field_annotation_is_scalar_sequence(arg):\n# at_least_one_scalar_sequence = True\n# continue\n# elif not field_annotation_is_scalar(arg):\n# return False\n# return at_least_one_scalar_sequence\n\n# Wait, I don't see an obvious bug in the logic I just reviewed.\n# Let's re-read the PR mentioned: https://github.com/fastapi/fastapi/pull/14297\n# Since I cannot access external links, I have to rely on the description.\n# \"is only taking one union type into account\"\n\n# Maybe there is another function that handles unions?\n# let's search for \"Union or origin is UnionType\" in shared.py\nPY\n"
} | {"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}full{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0} |
| 18 | run_command | {
"command": "grep -n \"Union or origin is UnionType\" fastapi/_compat/shared.py"
} | {"status": "ok", "stdout": "85: if origin is Union or origin is UnionType:\n111: if origin is Union or origin is UnionType:\n132: if origin is Union or origin ifull{"status": "ok", "stdout": "85: if origin is Union or origin is UnionType:\n111: if origin is Union or origin is UnionType:\n132: if origin is Union or origin is UnionType:\n151: if origin is Union or origin is UnionType:\n162: if origin is Union or origin is UnionType:\n171: if origin is Union or origin is UnionType:\n186: if origin is Union or origin is UnionType:\n203: if origin is Union or origin is UnionType:\n", "stderr": "", "exit_code": 0} |
| 19 | run_command | {
"command": "grep -rn \"typing-inspection\" ."
} | {"status": "error", "error_type": "CommandError", "error_message": "", "details": {"stdout": "", "stderr": "", "exit_code": 1}}full{"status": "error", "error_type": "CommandError", "error_message": "", "details": {"stdout": "", "stderr": "", "exit_code": 1}} |
| 20 | run_command | {
"command": "python3 - <<'PY'\nimport typing\nimport types\nfrom typing_extensions import get_origin, get_args\n\ndef test_complex_union():\n # If we have a nested union like Union[Union[int, str], float]\n # Python 3.10+ usually flattens these.\n ann = (int | str) | float\n print(f\"Annotation: {ann}\")\n print(f\"Origin: {get_origin(ann)}\")\n print(f\"Args: {get_args(ann)}\")\n\ntest_complex_union()\nPY\n"
} | {"status": "ok", "stdout": "Annotation: int | str | float\nOrigin: <class 'types.UnionType'>\nArgs: (<class 'int'>, <class 'str'>, <class 'float'>)\n", "stderr"full{"status": "ok", "stdout": "Annotation: int | str | float\nOrigin: <class 'types.UnionType'>\nArgs: (<class 'int'>, <class 'str'>, <class 'float'>)\n", "stderr": "", "exit_code": 0} |
| 21 | run_command | {
"command": "python3 - <<'PY'\nfrom pathlib import Path\np = Path(\"fastapi/_compat/shared.py\")\ns = p.read_text()\n\n# I'll check for any other place where Unions are handled.\n# Maybe there's something with Optional?\n# In Python 3.10+, Optional[T] is Union[T, None].\n\n# Let's look at the code again.\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\n# This looks correct for checking if ANY of the union types is a sequence.\n\n# Let's search for \"get_origin(annotation) is Union\"\n# maybe it's missing \"or origin is UnionType\" somewhere?\nPY\n"
} | {"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}full{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0} |
| 22 | run_command | {
"command": "grep -rn \"origin is Union\" fastapi/ | grep -v \"UnionType\""
} | {"status": "error", "error_type": "CommandError", "error_message": "", "details": {"stdout": "", "stderr": "", "exit_code": 1}}full{"status": "error", "error_type": "CommandError", "error_message": "", "details": {"stdout": "", "stderr": "", "exit_code": 1}} |
| 23 | run_command | {
"command": "grep -rn \"origin is Union\" fastapi/ | head -20"
} | {"status": "ok", "stdout": "fastapi/_compat/shared.py:85: if origin is Union or origin is UnionType:\nfastapi/_compat/shared.py:111: if origin is Union or origifull{"status": "ok", "stdout": "fastapi/_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:\n", "stderr": "", "exit_code": 0} |
| 24 | run_command | {
"command": "grep -rn \"get_origin\" fastapi/ | grep -v \"UnionType\" | head -20"
} | {"status": "ok", "stdout": "fastapi/dependencies/utils.py:78:from typing_extensions import Annotated, Literal, get_args, get_origin\nfastapi/dependencies/utils.full{"status": "ok", "stdout": "fastapi/dependencies/utils.py:78:from typing_extensions import Annotated, Literal, get_args, get_origin\nfastapi/dependencies/utils.py:357: if get_origin(use_annotation) is Annotated:\nfastapi/dependencies/utils.py:836: origin = get_origin(field_type)\nfastapi/_compat/shared.py:24:from typing_extensions import Annotated, get_args, get_origin\nfastapi/_compat/shared.py:84: origin = get_origin(annotation)\nfastapi/_compat/shared.py:91: get_origin(annotation)\nfastapi/_compat/shared.py:110: origin = get_origin(annotation)\nfastapi/_compat/shared.py:131: origin = get_origin(annotation)\nfastapi/_compat/shared.py:150: origin = get_origin(annotation)\nfastapi/_compat/shared.py:161: origin = get_origin(annotation)\nfastapi/_compat/shared.py:170: origin = get_origin(annotation)\nfastapi/_compat/shared.py:185: origin = get_origin(annotation)\nfastapi/_compat/shared.py:202: origin = get_origin(annotation)\nfastapi/_compat/v2.py:36:from typing_extensions import Annotated, Literal, get_args, get_origin\nfastapi/_compat/v2.py:383: origin_type = get_origin(field.field_info.annotation) or field.field_info.annotation\nfastapi/_compat/v2.py:389: origin_type = get_origin(union_arg) or union_arg\nfastapi/_compat/v2.py:459: origin = get_origin(annotation)\n", "stderr": "", "exit_code": 0} |
| 25 | run_command | {
"command": "sed -n '380,400p' fastapi/_compat/v2.py"
} | {"status": "ok", "stdout": "\n\ndef serialize_sequence_value(*, field: ModelField, value: Any) -> Sequence[Any]:\n origin_type = get_origin(field.field_info.annfull{"status": "ok", "stdout": "\n\ndef serialize_sequence_value(*, field: ModelField, value: Any) -> Sequence[Any]:\n origin_type = get_origin(field.field_info.annotation) or field.field_info.annotation\n if origin_type is Union: # Handle optional sequences\n union_args = get_args(field.field_info.annotation)\n for union_arg in union_args:\n if union_arg is type(None):\n continue\n origin_type = get_origin(union_arg) or union_arg\n break\n assert issubclass(origin_type, shared.sequence_types) # type: ignore[arg-type]\n return shared.sequence_annotation_to_type[origin_type](value) # type: ignore[no-any-return]\n\n\ndef get_missing_field_error(loc: Tuple[str, ...]) -> Dict[str, Any]:\n error = ValidationError.from_exception_data(\n \"Field required\", [{\"type\": \"missing\", \"loc\": loc, \"input\": {}}]\n ).errors(include_url=False)[0]\n error[\"input\"] = None\n return error # type: ignore[return-value]\n", "stderr": "", "exit_code": 0} |
| 26 | run_command | {
"command": "python3 - <<'PY'\nimport typing\nimport types\nfrom typing_extensions import get_origin, get_args\n\ndef test_serialize_sequence_value():\n # Simulating the logic in fastapi/_compat/v2.py\n def serialize_sequence_value(annotation, value):\n origin_type = get_origin(annotation) or annotation\n \n # This is the part we suspect is broken:\n # if origin_type is Union:\n if origin_type is typing.Union:\n union_args = get_args(annotation)\n for union_arg in union_args:\n if union_arg is type(None):\n continue\n origin_type = get_origin(union_arg) or union_arg\n break\n \n print(f\"Annotation: {annotation}, Final origin_type: {origin_type}\")\n # return issubclass(origin_type, (list, tuple, set)) # simplified\n\n # Case 1: typing.Union[list[int], None]\n serialize_sequence_value(typing.Union[typing.List[int], None], [1, 2])\n \n # Case 2: list[int] | None (Python 3.10 syntax)\n try:\n serialize_sequence_value(list[int] | None, [1, 2])\n except TypeError:\n print(\"Python < 3.10\")\n\ntest_serialize_sequence_value()\nPY\n"
} | {"status": "ok", "stdout": "Annotation: typing.Optional[typing.List[int]], Final origin_type: <class 'list'>\nAnnotation: list[int] | None, Final origin_type: <full{"status": "ok", "stdout": "Annotation: typing.Optional[typing.List[int]], Final origin_type: <class 'list'>\nAnnotation: list[int] | None, Final origin_type: <class 'types.UnionType'>\n", "stderr": "", "exit_code": 0} |
| 27 | run_command | {
"command": "python3 - <<'PY'\nfrom pathlib import Path\np = Path(\"fastapi/_compat/v2.py\")\ns = p.read_text()\nold = \"\"\" if origin_type is Union: # Handle optional sequences\"\"\"\nnew = \"\"\" if origin_type is Union or origin_type is UnionType: # Handle optional sequences\"\"\"\n# Need to make sure UnionType is imported.\n# Let's check imports in v2.py\nPY\n"
} | {"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}full{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0} |
| 28 | run_command | {
"command": "sed -n '1,50p' fastapi/_compat/v2.py"
} | {"status": "ok", "stdout": "import re\nimport warnings\nfrom copy import copy, deepcopy\nfrom dataclasses import dataclass\nfrom enum import Enum\nfrom typing ifull{"status": "ok", "stdout": "import re\nimport warnings\nfrom copy import copy, deepcopy\nfrom dataclasses import dataclass\nfrom enum import Enum\nfrom typing import (\n Any,\n Dict,\n List,\n Sequence,\n Set,\n Tuple,\n Type,\n Union,\n cast,\n)\n\nfrom fastapi._compat import may_v1, shared\nfrom fastapi.openapi.constants import REF_TEMPLATE\nfrom fastapi.types import IncEx, ModelNameMap\nfrom pydantic import BaseModel, TypeAdapter, create_model\nfrom pydantic import PydanticSchemaGenerationError as PydanticSchemaGenerationError\nfrom pydantic import PydanticUndefinedAnnotation as PydanticUndefinedAnnotation\nfrom pydantic import ValidationError as ValidationError\nfrom pydantic._internal._schema_generation_shared import ( # type: ignore[attr-defined]\n GetJsonSchemaHandler as GetJsonSchemaHandler,\n)\nfrom pydantic._internal._typing_extra import eval_type_lenient\nfrom pydantic._internal._utils import lenient_issubclass as lenient_issubclass\nfrom pydantic.fields import FieldInfo as FieldInfo\nfrom pydantic.json_schema import GenerateJsonSchema as GenerateJsonSchema\nfrom pydantic.json_schema import JsonSchemaValue as JsonSchemaValue\nfrom pydantic_core import CoreSchema as CoreSchema\nfrom pydantic_core import PydanticUndefined, PydanticUndefinedType\nfrom pydantic_core import Url as Url\nfrom typing_extensions import Annotated, Literal, get_args, get_origin\n\ntry:\n from pydantic_core.core_schema import (\n with_info_plain_validator_function as with_info_plain_validator_function,\n )\nexcept ImportError: # pragma: no cover\n from pydantic_core.core_schema import (\n general_plain_validator_function as with_info_plain_validator_function, # noqa: F401\n )\n\nRequiredParam = PydanticUndefined\nUndefined = PydanticUndefined\nUndefinedType = PydanticUndefinedType\nevaluate_forwardref = eval_type_lenient\n", "stderr": "", "exit_code": 0} |
| 29 | run_command | {
"command": "python3 - <<'PY'\nfrom pathlib import Path\np = Path(\"fastapi/_compat/v2.py\")\ns = p.read_text()\n\n# Add UnionType import from fastapi.types\n# and update serialize_sequence_value\n\nif \"from fastapi.types import IncEx, ModelNameMap\" in s:\n s = s.replace(\n \"from fastapi.types import IncEx, ModelNameMap\",\n \"from fastapi.types import IncEx, ModelNameMap, UnionType\"\n )\n\n# I need to find the exact lines for the replace.\n# Using the previous sed output:\n# 386: if origin_type is Union: # Handle optional sequences\n# Let's use a safer replacement.\nold_line = \" if origin_type is Union: # Handle optional sequences\"\nnew_line = \" if origin_type is Union or origin_type is UnionType: # Handle optional sequences\"\n\nif old_line in s:\n s = s.replace(old_line, new_line)\n\np.write_text(s)\nPY\n"
} | {"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}full{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0} |
| 30 | run_command | {
"command": "python3 -m py_compile fastapi/_compat/v2.py"
} | {"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}full{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0} |
| 31 | run_command | {
"command": "python3 - <<'PY'\nimport typing\nimport types\nfrom typing_extensions import get_origin, get_args\nfrom fastapi.types import UnionType\n\ndef test_serialize_sequence_value_fixed():\n def serialize_sequence_value_impl(annotation):\n origin_type = get_origin(annotation) or annotation\n \n # Fixed logic\n if origin_type is typing.Union or origin_type is UnionType:\n union_args = get_args(annotation)\n for union_arg in union_args:\n if union_arg is type(None):\n continue\n origin_type = get_origin(union_arg) or union_arg\n break\n \n return origin_type\n\n # Case 1: typing.Union[list[int], None]\n ann1 = typing.Union[typing.List[int], None]\n print(f\"Union[List, None] -> {serialize_sequence_value_impl(ann1)}\")\n \n # Case 2: list[int] | None (Python 3.10 syntax)\n try:\n ann2 = list[int] | None\n print(f\"list[int] | None -> {serialize_sequence_value_impl(ann2)}\")\n except TypeError:\n print(\"Python < 3.10\")\n\ntest_serialize_sequence_value_fixed()\nPY\n"
} | {"status": "ok", "stdout": "Union[List, None] -> <class 'list'>\nlist[int] | None -> <class 'list'>\n", "stderr": "", "exit_code": 0}full{"status": "ok", "stdout": "Union[List, None] -> <class 'list'>\nlist[int] | None -> <class 'list'>\n", "stderr": "", "exit_code": 0} |
| 32 | run_command | {
"command": "submit_patch"
} | {"status": "error", "error_type": "CommandError", "error_message": "/bin/bash: submit_patch: command not found\n", "details": {"stdout": "", "stderr": "/bin/basfull{"status": "error", "error_type": "CommandError", "error_message": "/bin/bash: submit_patch: command not found\n", "details": {"stdout": "", "stderr": "/bin/bash: submit_patch: command not found\n", "exit_code": 127}} |
| 33 | submit_patch | {} | {"status": "ok", "patch_size": 1170, "files_changed": 1}full{"status": "ok", "patch_size": 1170, "files_changed": 1} |
diff --git a/fastapi/_compat/v2.py b/fastapi/_compat/v2.py
index 543a42dd..0faa7d5a 100644
--- a/fastapi/_compat/v2.py
+++ b/fastapi/_compat/v2.py
@@ -17,7 +17,7 @@ from typing import (
from fastapi._compat import may_v1, shared
from fastapi.openapi.constants import REF_TEMPLATE
-from fastapi.types import IncEx, ModelNameMap
+from fastapi.types import IncEx, ModelNameMap, UnionType
from pydantic import BaseModel, TypeAdapter, create_model
from pydantic import PydanticSchemaGenerationError as PydanticSchemaGenerationError
from pydantic import PydanticUndefinedAnnotation as PydanticUndefinedAnnotation
@@ -381,7 +381,7 @@ def copy_field_info(*, field_info: FieldInfo, annotation: Any) -> FieldInfo:
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
+ if origin_type is Union or origin_type is UnionType: # Handle optional sequences
union_args = get_args(field.field_info.annotation)
for union_arg in union_args:
if union_arg is type(None):
.s.........ss [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
10 passed, 3 skipped, 1 warning in 0.61s