Skip to content

Commit f73431c

Browse files
fix
Signed-off-by: Francisco Javier Arceo <farceo@redhat.com>
1 parent c360aef commit f73431c

File tree

7 files changed

+63
-55
lines changed

7 files changed

+63
-55
lines changed

sdk/python/feast/feature_store.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,7 +1020,7 @@ def apply(
10201020
# Create ODFV with same transformation logic and correct mode
10211021
# Include both FeatureViews and RequestSources in sources
10221022
sources_list = list(fv.source_views or [])
1023-
if hasattr(fv, 'source_request_sources') and fv.source_request_sources:
1023+
if hasattr(fv, "source_request_sources") and fv.source_request_sources:
10241024
sources_list.extend(fv.source_request_sources.values())
10251025

10261026
# Disable online serving for the original FeatureView since we're creating an ODFV for online serving
@@ -1298,18 +1298,24 @@ def get_historical_features(
12981298
source_feature_views = []
12991299

13001300
# Separate FeatureViews with transformations from regular ones
1301-
for (fv, features_list) in fvs:
1302-
if hasattr(fv, 'feature_transformation') and fv.feature_transformation is not None:
1301+
for fv, features_list in fvs:
1302+
if (
1303+
hasattr(fv, "feature_transformation")
1304+
and fv.feature_transformation is not None
1305+
):
13031306
# FeatureView with transformation - collect for post-processing
13041307
unified_transformation_views.append((fv, features_list))
13051308

13061309
# Extract source FeatureViews from the transformation view
1307-
if hasattr(fv, 'source') and fv.source:
1310+
if hasattr(fv, "source") and fv.source:
13081311
# Handle both single source and list of sources
13091312
sources = fv.source if isinstance(fv.source, list) else [fv.source]
13101313
for src in sources:
13111314
# Only add if it's actually a FeatureView, not a DataSource
1312-
if isinstance(src, FeatureView) and src not in source_feature_views:
1315+
if (
1316+
isinstance(src, FeatureView)
1317+
and src not in source_feature_views
1318+
):
13131319
source_feature_views.append(src)
13141320
else:
13151321
regular_feature_views.append(fv)

sdk/python/feast/feature_view.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,13 @@
2222

2323
from feast import utils
2424
from feast.base_feature_view import BaseFeatureView
25-
from feast.data_source import DataSource, KafkaSource, KinesisSource, PushSource, RequestSource
25+
from feast.data_source import (
26+
DataSource,
27+
KafkaSource,
28+
KinesisSource,
29+
PushSource,
30+
RequestSource,
31+
)
2632
from feast.entity import Entity
2733
from feast.feature_view_projection import FeatureViewProjection
2834
from feast.field import Field
@@ -702,7 +708,9 @@ def most_recent_end_time(self) -> Optional[datetime]:
702708
return max([interval[1] for interval in self.materialization_intervals])
703709

704710
@staticmethod
705-
def get_requested_unified_fvs(feature_refs, project, registry) -> List["FeatureView"]:
711+
def get_requested_unified_fvs(
712+
feature_refs, project, registry
713+
) -> List["FeatureView"]:
706714
"""
707715
Extract FeatureViews with transformations that are requested in feature_refs.
708716
@@ -714,14 +722,15 @@ def get_requested_unified_fvs(feature_refs, project, registry) -> List["FeatureV
714722
Returns:
715723
List of FeatureViews with transformations that match the feature_refs
716724
"""
717-
all_feature_views = registry.list_feature_views(
718-
project, allow_cache=True
719-
)
725+
all_feature_views = registry.list_feature_views(project, allow_cache=True)
720726
requested_unified_fvs: List[FeatureView] = []
721727

722728
for fv in all_feature_views:
723729
# Only include FeatureViews with transformations
724-
if hasattr(fv, 'feature_transformation') and fv.feature_transformation is not None:
730+
if (
731+
hasattr(fv, "feature_transformation")
732+
and fv.feature_transformation is not None
733+
):
725734
for feature in fv.features:
726735
if f"{fv.name}:{feature.name}" in feature_refs:
727736
requested_unified_fvs.append(fv)

sdk/python/feast/infra/offline_stores/ibis.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -498,9 +498,7 @@ def __init__(
498498
self._on_demand_feature_views: List[OnDemandFeatureView] = (
499499
on_demand_feature_views
500500
)
501-
self._unified_feature_views: List[FeatureView] = (
502-
unified_feature_views
503-
)
501+
self._unified_feature_views: List[FeatureView] = unified_feature_views
504502
self._full_feature_names = full_feature_names
505503
self._metadata = metadata
506504
self.data_source_writer = data_source_writer

sdk/python/feast/infra/offline_stores/offline_store.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -169,10 +169,15 @@ def to_arrow(
169169
# Handle unified FeatureViews with transformations
170170
if self.unified_feature_views:
171171
for unified_fv in self.unified_feature_views:
172-
if hasattr(unified_fv, 'feature_transformation') and unified_fv.feature_transformation is not None:
172+
if (
173+
hasattr(unified_fv, "feature_transformation")
174+
and unified_fv.feature_transformation is not None
175+
):
173176
# Apply the transformation using the transform_arrow method
174-
transformed_arrow = unified_fv.feature_transformation.transform_arrow(
175-
features_table, unified_fv.features
177+
transformed_arrow = (
178+
unified_fv.feature_transformation.transform_arrow(
179+
features_table, unified_fv.features
180+
)
176181
)
177182

178183
for col in transformed_arrow.column_names:

sdk/python/feast/infra/online_stores/online_store.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,8 @@ def get_online_features(
198198

199199
# Filter out features that were provided as transformation inputs to avoid overriding request-time data
200200
features_to_fetch = [
201-
feature for feature in requested_features
201+
feature
202+
for feature in requested_features
202203
if feature not in provided_transformation_input_features
203204
]
204205

@@ -214,9 +215,7 @@ def get_online_features(
214215
requested_features=features_to_fetch,
215216
)
216217

217-
feature_data = utils._convert_rows_to_protobuf(
218-
features_to_fetch, read_rows
219-
)
218+
feature_data = utils._convert_rows_to_protobuf(features_to_fetch, read_rows)
220219

221220
# Populate the result_rows with the Features from the OnlineStore inplace.
222221
utils._populate_response_from_feature_data(
@@ -297,7 +296,8 @@ async def query_table(table, requested_features):
297296

298297
# Filter out features that were provided as transformation inputs to avoid overriding request-time data
299298
features_to_fetch = [
300-
feature for feature in requested_features
299+
feature
300+
for feature in requested_features
301301
if feature not in provided_transformation_input_features
302302
]
303303

@@ -337,9 +337,7 @@ async def query_table(table, requested_features):
337337
if not features_to_fetch:
338338
continue
339339

340-
feature_data = utils._convert_rows_to_protobuf(
341-
features_to_fetch, read_rows
342-
)
340+
feature_data = utils._convert_rows_to_protobuf(features_to_fetch, read_rows)
343341

344342
# Populate the result_rows with the Features from the OnlineStore inplace.
345343
utils._populate_response_from_feature_data(

sdk/python/feast/utils.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -709,7 +709,6 @@ def _augment_response_with_on_demand_transforms(
709709
"""
710710
from feast.online_response import OnlineResponse
711711

712-
713712
requested_odfv_map = {odfv.name: odfv for odfv in requested_on_demand_feature_views}
714713
requested_odfv_feature_names = requested_odfv_map.keys()
715714

@@ -848,7 +847,7 @@ def _augment_response_with_on_demand_transforms(
848847
values_for_proto = feature_vector
849848
elif odfv.mode == "python":
850849
values_for_proto = [feature_vector]
851-
elif hasattr(feature_vector, 'to_numpy'):
850+
elif hasattr(feature_vector, "to_numpy"):
852851
# pandas Series/DataFrame column
853852
values_for_proto = feature_vector.to_numpy()
854853
else:
@@ -1284,9 +1283,13 @@ def _get_feature_views_to_use(
12841283
try:
12851284
# Look for the auto-generated OnDemandFeatureView for online serving
12861285
online_fv_name = f"{fv.name}_online"
1287-
online_fv = registry.get_on_demand_feature_view(online_fv_name, project, allow_cache)
1286+
online_fv = registry.get_on_demand_feature_view(
1287+
online_fv_name, project, allow_cache
1288+
)
12881289
od_fvs_to_use.append(
1289-
online_fv.with_projection(copy.copy(projection)) if projection else online_fv
1290+
online_fv.with_projection(copy.copy(projection))
1291+
if projection
1292+
else online_fv
12901293
)
12911294
except Exception:
12921295
# Fallback to the original FeatureView if auto-generated ODFV not found
@@ -1478,14 +1481,14 @@ def _prepare_entities_to_read_from_online_store(
14781481
transformation_input_features: Set[str] = set()
14791482
for odfv in requested_on_demand_feature_views:
14801483
# Check if this ODFV has transformations and source feature view projections
1481-
if hasattr(odfv, 'source_feature_view_projections'):
1484+
if hasattr(odfv, "source_feature_view_projections"):
14821485
for projection in odfv.source_feature_view_projections.values():
14831486
for feature in projection.features:
14841487
transformation_input_features.add(feature.name)
14851488
# Also check for unified FeatureViews with feature_transformation
1486-
elif hasattr(odfv, 'feature_transformation') and odfv.feature_transformation:
1489+
elif hasattr(odfv, "feature_transformation") and odfv.feature_transformation:
14871490
# For unified FeatureViews, check source_views if available
1488-
if hasattr(odfv, 'source_views') and odfv.source_views:
1491+
if hasattr(odfv, "source_views") and odfv.source_views:
14891492
for source_view in odfv.source_views:
14901493
for feature in source_view.features:
14911494
transformation_input_features.add(feature.name)
@@ -1519,7 +1522,11 @@ def _prepare_entities_to_read_from_online_store(
15191522
online_features_response = GetOnlineFeaturesResponse(results=[])
15201523
_populate_result_rows_from_columnar(
15211524
online_features_response=online_features_response,
1522-
data=dict(**join_key_values, **request_data_features, **transformation_input_features_data),
1525+
data=dict(
1526+
**join_key_values,
1527+
**request_data_features,
1528+
**transformation_input_features_data,
1529+
),
15231530
)
15241531

15251532
# Add the Entityless case after populating result rows to avoid having to remove

sdk/python/tests/unit/test_unified_python_transformation.py

Lines changed: 7 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,9 @@ def setUp(self):
6565
start_date = end_date - timedelta(days=15)
6666

6767
driver_entities = [1001, 1002, 1003, 1004, 1005]
68-
driver_df = create_driver_hourly_stats_df(
69-
driver_entities, start_date, end_date
70-
)
68+
driver_df = create_driver_hourly_stats_df(driver_entities, start_date, end_date)
7169
driver_stats_path = os.path.join(self.data_dir, "driver_stats.parquet")
72-
driver_df.to_parquet(
73-
path=driver_stats_path, allow_truncated_timestamps=True
74-
)
70+
driver_df.to_parquet(path=driver_stats_path, allow_truncated_timestamps=True)
7571

7672
driver = Entity(
7773
name="driver", join_keys=["driver_id"], value_type=ValueType.INT64
@@ -114,9 +110,7 @@ def setUp(self):
114110
@transformation(mode="pandas")
115111
def pandas_transform(inputs: pd.DataFrame) -> pd.DataFrame:
116112
df = pd.DataFrame()
117-
df["conv_rate_plus_acc_pandas"] = (
118-
inputs["conv_rate"] + inputs["acc_rate"]
119-
)
113+
df["conv_rate_plus_acc_pandas"] = inputs["conv_rate"] + inputs["acc_rate"]
120114
return df
121115

122116
sink_source = FileSource(name="sink-source", path="sink.parquet")
@@ -134,9 +128,7 @@ def pandas_transform(inputs: pd.DataFrame) -> pd.DataFrame:
134128
def python_transform(inputs: dict[str, Any]) -> dict[str, Any]:
135129
output: dict[str, Any] = {
136130
"conv_rate_plus_acc_python": conv_rate + acc_rate
137-
for conv_rate, acc_rate in zip(
138-
inputs["conv_rate"], inputs["acc_rate"]
139-
)
131+
for conv_rate, acc_rate in zip(inputs["conv_rate"], inputs["acc_rate"])
140132
}
141133
return output
142134

@@ -408,13 +400,9 @@ def setUp(self):
408400
start_date = end_date - timedelta(days=15)
409401

410402
driver_entities = [1001, 1002, 1003, 1004, 1005]
411-
driver_df = create_driver_hourly_stats_df(
412-
driver_entities, start_date, end_date
413-
)
403+
driver_df = create_driver_hourly_stats_df(driver_entities, start_date, end_date)
414404
driver_stats_path = os.path.join(self.data_dir, "driver_stats.parquet")
415-
driver_df.to_parquet(
416-
path=driver_stats_path, allow_truncated_timestamps=True
417-
)
405+
driver_df.to_parquet(path=driver_stats_path, allow_truncated_timestamps=True)
418406

419407
driver = Entity(name="driver", join_keys=["driver_id"])
420408

@@ -496,10 +484,7 @@ def python_all_types_transform(inputs: dict[str, Any]) -> dict[str, Any]:
496484
sink_source = FileSource(name="sink-source", path="sink.parquet")
497485
python_view = FeatureView(
498486
name="python_view",
499-
source=[
500-
driver_stats_fv,
501-
request_source
502-
],
487+
source=[driver_stats_fv, request_source],
503488
sink_source=sink_source,
504489
schema=[
505490
Field(name="highest_achieved_rank", dtype=String),

0 commit comments

Comments
 (0)