Skip to content

Commit 03f30fe

Browse files
spicysharkcoshuchu
andauthored
fix: Read None elements of UUID and Decimal lists as None (#7039)
A None element in a UUID_LIST, TIME_UUID_LIST or DECIMAL_LIST is written as "" (see _LIST_NONE_DEFAULTS), but feast_value_type_to_python_type passed every element to UUID() or Decimal(). The write succeeded and reading the row back raised ValueError or InvalidOperation. Read "" back as None, as the unix timestamp list reader already does for its null sentinel. Signed-off-by: spicysharkco <[email protected]> Co-authored-by: Shuchu Han <[email protected]>
1 parent 8ce548c commit 03f30fe

2 files changed

Lines changed: 38 additions & 2 deletions

File tree

‎sdk/python/feast/type_map.py‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,15 +235,22 @@ def feast_value_type_to_python_type(
235235
if val_attr in ("uuid_val", "time_uuid_val"):
236236
return uuid_module.UUID(val) if isinstance(val, str) else val
237237
if val_attr in ("uuid_list_val", "time_uuid_list_val"):
238-
return [uuid_module.UUID(v) if isinstance(v, str) else v for v in val]
238+
# None elements are stored as "" (see _LIST_NONE_DEFAULTS)
239+
return [
240+
None if v == "" else uuid_module.UUID(v) if isinstance(v, str) else v
241+
for v in val
242+
]
239243
if val_attr in ("uuid_set_val", "time_uuid_set_val"):
240244
return {uuid_module.UUID(v) if isinstance(v, str) else v for v in val}
241245

242246
# Convert DECIMAL values to decimal.Decimal objects
243247
if val_attr == "decimal_val":
244248
return decimal.Decimal(val) if isinstance(val, str) else val
245249
if val_attr == "decimal_list_val":
246-
return [decimal.Decimal(v) if isinstance(v, str) else v for v in val]
250+
return [
251+
None if v == "" else decimal.Decimal(v) if isinstance(v, str) else v
252+
for v in val
253+
]
247254
if val_attr == "decimal_set_val":
248255
return {decimal.Decimal(v) if isinstance(v, str) else v for v in val}
249256

‎sdk/python/tests/unit/test_type_map.py‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1630,6 +1630,24 @@ def test_time_uuid_object_list_roundtrip(self):
16301630
assert all(isinstance(r, uuid.UUID) for r in result)
16311631
assert result == test_uuids
16321632

1633+
def test_uuid_list_with_none_roundtrip(self):
1634+
"""None elements in a UUID list are read back as None."""
1635+
test_uuid = uuid.uuid4()
1636+
protos = python_values_to_proto_values(
1637+
[[None, test_uuid, None]], ValueType.UUID_LIST
1638+
)
1639+
result = feast_value_type_to_python_type(protos[0])
1640+
assert result == [None, test_uuid, None]
1641+
1642+
def test_time_uuid_list_with_none_roundtrip(self):
1643+
"""None elements in a TIME_UUID list are read back as None."""
1644+
test_uuid = uuid.uuid1()
1645+
protos = python_values_to_proto_values(
1646+
[[test_uuid, None]], ValueType.TIME_UUID_LIST
1647+
)
1648+
result = feast_value_type_to_python_type(protos[0])
1649+
assert result == [test_uuid, None]
1650+
16331651
def test_uuid_set_roundtrip(self):
16341652
"""UUID set -> proto -> set of uuid.UUID objects roundtrip."""
16351653
test_uuids = {uuid.uuid4(), uuid.uuid4(), uuid.uuid4()}
@@ -1760,6 +1778,17 @@ def test_decimal_object_list_roundtrip(self):
17601778
assert all(isinstance(r, decimal.Decimal) for r in result)
17611779
assert result == vals
17621780

1781+
def test_decimal_list_with_none_roundtrip(self):
1782+
"""None elements in a DECIMAL_LIST are read back as None."""
1783+
import decimal
1784+
1785+
val = decimal.Decimal("1.5")
1786+
protos = python_values_to_proto_values(
1787+
[[None, val, None]], ValueType.DECIMAL_LIST
1788+
)
1789+
result = feast_value_type_to_python_type(protos[0])
1790+
assert result == [None, val, None]
1791+
17631792
def test_decimal_set_roundtrip(self):
17641793
"""DECIMAL_SET -> proto -> set of decimal.Decimal roundtrip."""
17651794
import decimal

0 commit comments

Comments
 (0)