diff --git a/fastapi/_compat/v2.py b/fastapi/_compat/v2.py index 3d91814c0..3f527f7b2 100644 --- a/fastapi/_compat/v2.py +++ b/fastapi/_compat/v2.py @@ -3,6 +3,7 @@ import warnings from copy import copy, deepcopy from dataclasses import dataclass from enum import Enum +from types import UnionType from typing import ( Any, Dict, @@ -371,7 +372,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): diff --git a/tests/test_compat.py b/tests/test_compat.py index c3a97209a..13077d1f4 100644 --- a/tests/test_compat.py +++ b/tests/test_compat.py @@ -1,3 +1,6 @@ +import sys +import pytest + from typing import Any, Dict, List, Union from fastapi import FastAPI, UploadFile @@ -148,6 +151,22 @@ def test_serialize_sequence_value_with_optional_list(): assert isinstance(result, list) +@needs_pydanticv2 +@pytest.mark.skipif( + sys.version_info < (3, 10), + reason='Requires new union syntax', +) +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"]) + assert result == ["a", "b", "c"] + assert isinstance(result, list) + + @needs_pydanticv2 def test_serialize_sequence_value_with_none_first_in_union(): """Test that serialize_sequence_value handles Union[None, List[...]] correctly."""