Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Unwrap compound protobuf Value types in parse_typed
Signed-off-by: Nick Quinn <nicholas_quinn@apple.com>
  • Loading branch information
nickquinn408 committed Mar 16, 2026
commit 9ba35ea636fa96b2dba90b38bd3842110b08103c
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -264,3 +264,6 @@ Desktop.ini

# AgentReady reports
.agentready/

# Claude Code project settings
.claude/
13 changes: 8 additions & 5 deletions sdk/python/feast/infra/contrib/grpc_server.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import logging
import threading
from collections.abc import Mapping
from concurrent import futures
from typing import Optional, Union

Expand Down Expand Up @@ -37,11 +38,13 @@ def parse_typed(typed_features):
df = {}
for key, value in typed_features.items():
val_case = value.WhichOneof("val")
df[key] = [
None
if val_case is None or val_case == "null_val"
else getattr(value, val_case)
]
if val_case is None or val_case == "null_val":
df[key] = [None]
else:
raw = getattr(value, val_case)
if hasattr(raw, "val"):
raw = dict(raw.val) if isinstance(raw.val, Mapping) else list(raw.val)
df[key] = [raw]
return pd.DataFrame.from_dict(df)


Expand Down
42 changes: 41 additions & 1 deletion sdk/python/tests/unit/test_grpc_server_write_protos.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@
PushRequest,
WriteToOnlineStoreRequest,
)
from feast.protos.feast.types.Value_pb2 import Null, Value
from feast.protos.feast.types.Value_pb2 import (
Int64List,
Map,
Null,
StringSet,
Value,
)


def test_push_request_string_features():
Expand Down Expand Up @@ -117,3 +123,37 @@ def test_parse_typed_unset_val_becomes_none():
"""A Value with no oneof field set (WhichOneof returns None) must also produce None."""
df = parse_typed({"empty": Value()})
assert df["empty"].iloc[0] is None


def test_parse_typed_list_val_unwrapped_to_python_list():
"""Compound list values are unwrapped from their protobuf wrapper to a plain list."""
df = parse_typed(
{
"ids": Value(int64_list_val=Int64List(val=[1, 2, 3])),
}
)
assert df["ids"].iloc[0] == [1, 2, 3]


def test_parse_typed_set_val_unwrapped_to_python_list():
"""Compound set values are unwrapped from their protobuf wrapper to a plain list."""
df = parse_typed(
{
"tags": Value(string_set_val=StringSet(val=["a", "b"])),
}
)
assert sorted(df["tags"].iloc[0]) == ["a", "b"]


def test_parse_typed_map_val_unwrapped_to_python_dict():
"""Map values are unwrapped from their protobuf Map wrapper to a plain dict."""
df = parse_typed(
{
"scores": Value(
map_val=Map(val={"x": Value(float_val=1.0), "y": Value(float_val=2.0)})
),
}
)
result = df["scores"].iloc[0]
assert isinstance(result, dict)
assert set(result.keys()) == {"x", "y"}
Loading