Skip to content

Commit b91978e

Browse files
alan-gauthier-jtntkathole
authored andcommitted
chore: refacto from pr feedback
Signed-off-by: Alan Gauthier <alan.gauthier@jobteaser.com>
1 parent b18f71e commit b91978e

1 file changed

Lines changed: 36 additions & 25 deletions

File tree

sdk/python/feast/type_map.py

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -909,6 +909,16 @@ def _convert_list_values_to_proto(
909909
]
910910

911911

912+
def _is_array_like(value: Any) -> bool:
913+
"""Return True if *value* is array-like (numpy array or any sized,
914+
non-string, non-bytes container). Array-like values in a scalar
915+
feature column cannot be mapped to a protobuf scalar field and are
916+
therefore always treated as null."""
917+
return isinstance(value, np.ndarray) or (
918+
hasattr(value, "__len__") and not isinstance(value, (str, bytes))
919+
)
920+
921+
912922
def _convert_scalar_values_to_proto(
913923
feast_value_type: ValueType,
914924
values: List[Any],
@@ -929,30 +939,29 @@ def _convert_scalar_values_to_proto(
929939
return [ProtoValue()] * len(values)
930940

931941
if feast_value_type == ValueType.UNIX_TIMESTAMP:
932-
out = []
933-
for value in values:
934-
if isinstance(value, np.ndarray) or (
935-
hasattr(value, "__len__") and not isinstance(value, (str, bytes))
936-
):
937-
# Array-like value in a scalar UNIX_TIMESTAMP column: treat as null.
938-
out.append(ProtoValue())
939-
elif value is None:
940-
out.append(ProtoValue())
942+
out: List[Any] = [None] * len(values)
943+
clean_indices: List[int] = []
944+
clean_values: List[Any] = []
945+
for i, value in enumerate(values):
946+
if _is_array_like(value) or value is None:
947+
out[i] = ProtoValue()
941948
else:
942-
(ts,) = _python_datetime_to_int_timestamp([value])
943-
out.append(ProtoValue(unix_timestamp_val=ts)) # type: ignore
949+
clean_indices.append(i)
950+
clean_values.append(value)
951+
if clean_values:
952+
timestamps = _python_datetime_to_int_timestamp(clean_values)
953+
for i, ts in zip(clean_indices, timestamps):
954+
out[i] = ProtoValue(unix_timestamp_val=ts) # type: ignore
944955
return out
945956

946957
field_name, func, valid_scalar_types = PYTHON_SCALAR_VALUE_TYPE_TO_PROTO_VALUE[
947958
feast_value_type
948959
]
949960

950-
# Validate scalar types — skip for array-like samples (they will be treated
951-
# as null or raw values in the conversion loop below).
952-
if valid_scalar_types and not (
953-
isinstance(sample, np.ndarray)
954-
or (hasattr(sample, "__len__") and not isinstance(sample, (str, bytes)))
955-
):
961+
# Validate scalar types. The caller guarantees that *sample* is not
962+
# array-like (array-like values are filtered out when picking the sample
963+
# for scalar columns in python_values_to_proto_values).
964+
if valid_scalar_types:
956965
try:
957966
is_zero = sample == 0 or sample == 0.0
958967
except (ValueError, TypeError):
@@ -972,9 +981,7 @@ def _convert_scalar_values_to_proto(
972981
if feast_value_type == ValueType.BOOL:
973982
out = []
974983
for value in values:
975-
if isinstance(value, np.ndarray) or (
976-
hasattr(value, "__len__") and not isinstance(value, (str, bytes))
977-
):
984+
if _is_array_like(value):
978985
# Array-like value in a scalar BOOL column: treat as null.
979986
out.append(ProtoValue())
980987
elif not pd.isnull(value):
@@ -996,9 +1003,7 @@ def _convert_scalar_values_to_proto(
9961003
for value in values:
9971004
if isinstance(value, ProtoValue):
9981005
out.append(value)
999-
elif isinstance(value, np.ndarray) or (
1000-
hasattr(value, "__len__") and not isinstance(value, (str, bytes))
1001-
):
1006+
elif _is_array_like(value):
10021007
# Array-like value in a scalar column: always treat as null.
10031008
# pd.isnull() is vectorised and would return an ndarray here,
10041009
# making `not pd.isnull(value)` raise ValueError.
@@ -1145,12 +1150,18 @@ def _python_value_to_proto_value(
11451150
if "set" in type_name_lower:
11461151
return _python_set_to_proto_values(feast_value_type, values)
11471152

1148-
# Scalar types
1153+
# Scalar types — pick a sample that is not array-like so that the type
1154+
# validation in _convert_scalar_values_to_proto always receives a plain
1155+
# scalar (array-like values in a scalar column are treated as null).
11491156
if (
11501157
feast_value_type in PYTHON_SCALAR_VALUE_TYPE_TO_PROTO_VALUE
11511158
or feast_value_type == ValueType.UNIX_TIMESTAMP
11521159
):
1153-
return _convert_scalar_values_to_proto(feast_value_type, values, sample)
1160+
scalar_sample = next(
1161+
(v for v in values if _non_empty_value(v) and not _is_array_like(v)),
1162+
None,
1163+
)
1164+
return _convert_scalar_values_to_proto(feast_value_type, values, scalar_sample)
11541165

11551166
raise Exception(f"Unsupported data type: {feast_value_type}")
11561167

0 commit comments

Comments
 (0)