@@ -1967,77 +1967,87 @@ class TestArrowArrayStringListMaterialization:
19671967 2. TypeError: "bad argument type for built-in operation"
19681968 — when proto_type(val=<ndarray>) was called; protobuf rejects ndarrays.
19691969
1970- Both are fixed by _to_proto_safe_list, which converts ndarrays to plain Python
1971- lists and sanitizes None elements in a type-appropriate way:
1972- - STRING_LIST: None → "" (empty string)
1973- - All other list types: None elements are dropped (filtered out)
1970+ Both are fixed by _sanitize_list_value, which converts ndarrays to plain Python
1971+ lists and replaces None elements with a type-appropriate zero/empty default
1972+ (see _LIST_NONE_DEFAULTS).
19741973 """
19751974
1976- def test_to_proto_safe_list_ndarray (self ):
1975+ def test_sanitize_list_value_ndarray (self ):
19771976 """ndarray is converted to a plain Python list."""
1978- from feast .type_map import _to_proto_safe_list
1977+ from feast .type_map import _sanitize_list_value
19791978
19801979 arr = np .array (["foo" , "bar" ], dtype = object )
1981- result = _to_proto_safe_list (arr )
1980+ result = _sanitize_list_value (arr , ValueType . STRING_LIST )
19821981 assert result == ["foo" , "bar" ]
19831982 assert isinstance (result , list )
19841983
1985- def test_to_proto_safe_list_empty_ndarray (self ):
1986- """Empty ndarray is converted to an empty list ."""
1987- from feast .type_map import _to_proto_safe_list
1984+ def test_sanitize_list_value_empty_ndarray (self ):
1985+ """Empty ndarray is converted to None (treated as a missing row) ."""
1986+ from feast .type_map import _sanitize_list_value
19881987
19891988 arr = np .array ([], dtype = object )
1990- result = _to_proto_safe_list (arr )
1991- assert result == []
1992- assert isinstance (result , list )
1989+ result = _sanitize_list_value (arr , ValueType .STRING_LIST )
1990+ assert result is None
19931991
1994- def test_to_proto_safe_list_ndarray_with_none (self ):
1992+ def test_sanitize_list_value_ndarray_with_none (self ):
19951993 """None elements inside a STRING_LIST ndarray are replaced with empty string."""
1996- from feast .type_map import _to_proto_safe_list
1994+ from feast .type_map import _sanitize_list_value
19971995
19981996 arr = np .array (["foo" , None , "baz" ], dtype = object )
1999- result = _to_proto_safe_list (arr , ValueType .STRING_LIST )
1997+ result = _sanitize_list_value (arr , ValueType .STRING_LIST )
20001998 assert result == ["foo" , "" , "baz" ]
20011999
2002- def test_to_proto_safe_list_plain_list (self ):
2003- """Plain Python lists pass through unchanged (no None replacement needed) ."""
2004- from feast .type_map import _to_proto_safe_list
2000+ def test_sanitize_list_value_plain_list (self ):
2001+ """Plain Python lists without None pass through unchanged."""
2002+ from feast .type_map import _sanitize_list_value
20052003
20062004 lst = ["foo" , "bar" ]
2007- result = _to_proto_safe_list (lst )
2005+ result = _sanitize_list_value (lst , ValueType . STRING_LIST )
20082006 assert result == ["foo" , "bar" ]
20092007
2010- def test_to_proto_safe_list_plain_list_with_none (self ):
2008+ def test_sanitize_list_value_plain_list_with_none (self ):
20112009 """None elements in a STRING_LIST plain list are replaced with empty string."""
2012- from feast .type_map import _to_proto_safe_list
2010+ from feast .type_map import _sanitize_list_value
20132011
20142012 lst = ["foo" , None ]
2015- result = _to_proto_safe_list (lst , ValueType .STRING_LIST )
2013+ result = _sanitize_list_value (lst , ValueType .STRING_LIST )
20162014 assert result == ["foo" , "" ]
20172015
2018- def test_to_proto_safe_list_numeric_list_none_dropped (self ):
2019- """None elements in non-string lists are dropped, not replaced with a sentinel ."""
2020- from feast .type_map import _to_proto_safe_list
2016+ def test_sanitize_list_value_numeric_none_replaced (self ):
2017+ """None elements in numeric lists are replaced with a type-appropriate default ."""
2018+ from feast .type_map import _sanitize_list_value
20212019
2022- for vt in (
2023- ValueType .FLOAT_LIST ,
2024- ValueType .DOUBLE_LIST ,
2025- ValueType .INT32_LIST ,
2026- ValueType .INT64_LIST ,
2027- ValueType .BYTES_LIST ,
2028- ):
2029- result = _to_proto_safe_list ([1.0 , None , 2.0 ], vt )
2030- assert result == [1.0 , 2.0 ], (
2031- f"Expected None dropped for { vt } , got { result !r} "
2032- )
2020+ assert _sanitize_list_value ([1 , None , 2 ], ValueType .INT32_LIST ) == [1 , 0 , 2 ]
2021+ assert _sanitize_list_value ([1 , None , 2 ], ValueType .INT64_LIST ) == [1 , 0 , 2 ]
2022+ assert _sanitize_list_value ([1.0 , None , 2.0 ], ValueType .FLOAT_LIST ) == [
2023+ 1.0 ,
2024+ 0.0 ,
2025+ 2.0 ,
2026+ ]
2027+ assert _sanitize_list_value ([1.0 , None , 2.0 ], ValueType .DOUBLE_LIST ) == [
2028+ 1.0 ,
2029+ 0.0 ,
2030+ 2.0 ,
2031+ ]
2032+ assert _sanitize_list_value ([True , None , False ], ValueType .BOOL_LIST ) == [
2033+ True ,
2034+ False ,
2035+ False ,
2036+ ]
2037+
2038+ def test_sanitize_list_value_bytes_none_replaced (self ):
2039+ """None elements in BYTES_LIST are replaced with b''."""
2040+ from feast .type_map import _sanitize_list_value
2041+
2042+ result = _sanitize_list_value ([b"x" , None ], ValueType .BYTES_LIST )
2043+ assert result == [b"x" , b"" ]
20332044
2034- def test_to_proto_safe_list_scalar_passthrough (self ):
2045+ def test_sanitize_list_value_scalar_passthrough (self ):
20352046 """Non-list, non-ndarray values are returned unchanged."""
2036- from feast .type_map import _to_proto_safe_list
2047+ from feast .type_map import _sanitize_list_value
20372048
2038- assert _to_proto_safe_list ("hello" ) == "hello"
2039- assert _to_proto_safe_list (None ) is None
2040- assert _to_proto_safe_list (42 ) == 42
2049+ assert _sanitize_list_value ("hello" , ValueType .STRING_LIST ) == "hello"
2050+ assert _sanitize_list_value (42 , ValueType .INT32_LIST ) == 42
20412051
20422052 def test_string_list_from_ndarray (self ):
20432053 """STRING_LIST column with ndarray values materializes without TypeError."""
0 commit comments