diff --git a/README.md b/README.md index 306b135076e..1052a93c11e 100644 --- a/README.md +++ b/README.md @@ -202,7 +202,7 @@ The list below contains the functionality that contributors are planning to deve * [x] Python Client * [x] [Python feature server](https://docs.feast.dev/reference/feature-servers/python-feature-server) * [x] [Java feature server (alpha)](https://github.com/feast-dev/feast/blob/master/infra/charts/feast/README.md) - * [x] [Go feature server (alpha)](https://github.com/feast-dev/feast/blob/master/go/README.md) + * [x] [Go feature server (alpha)](https://docs.feast.dev/reference/feature-servers/go-feature-server) * **Data Quality Management (See [RFC](https://docs.google.com/document/d/110F72d4NTv80p35wDSONxhhPBqWRwbZXG4f9mNEMd98/edit))** * [x] Data profiling and validation (Great Expectations) * **Feature Discovery and Governance** @@ -249,4 +249,4 @@ Thanks goes to these incredible people: - + \ No newline at end of file diff --git a/sdk/python/feast/on_demand_feature_view.py b/sdk/python/feast/on_demand_feature_view.py index 0ae87b5e35a..7eb4bab26b7 100644 --- a/sdk/python/feast/on_demand_feature_view.py +++ b/sdk/python/feast/on_demand_feature_view.py @@ -722,6 +722,7 @@ def get_requested_odfvs( def on_demand_feature_view( *, + name: Optional[str] = None, entities: Optional[List[Entity]] = None, schema: list[Field], sources: list[ @@ -742,6 +743,7 @@ def on_demand_feature_view( Creates an OnDemandFeatureView object with the given user function as udf. Args: + name (optional): The name of the on demand feature view. If not provided, the name will be the name of the user function. entities (Optional): The list of names of entities that this feature view is associated with. schema: The list of features in the output of the on demand feature view, after the transformation has been applied. @@ -791,7 +793,7 @@ def decorator(user_function): transformation = SubstraitTransformation.from_ibis(user_function, sources) on_demand_feature_view_obj = OnDemandFeatureView( - name=user_function.__name__, + name=name if name is not None else user_function.__name__, sources=sources, schema=schema, feature_transformation=transformation, diff --git a/sdk/python/tests/unit/test_on_demand_feature_view.py b/sdk/python/tests/unit/test_on_demand_feature_view.py index 4b30bd6be99..69724779cdd 100644 --- a/sdk/python/tests/unit/test_on_demand_feature_view.py +++ b/sdk/python/tests/unit/test_on_demand_feature_view.py @@ -24,6 +24,7 @@ OnDemandFeatureView, PandasTransformation, PythonTransformation, + on_demand_feature_view, ) from feast.types import Float32 @@ -356,3 +357,65 @@ def test_on_demand_feature_view_stored_writes(): assert transformed_output["output3"] is not None and isinstance( transformed_output["output3"], datetime.datetime ) + + +def test_function_call_syntax(): + CUSTOM_FUNCTION_NAME = "custom-function-name" + file_source = FileSource(name="my-file-source", path="test.parquet") + feature_view = FeatureView( + name="my-feature-view", + entities=[], + schema=[ + Field(name="feature1", dtype=Float32), + Field(name="feature2", dtype=Float32), + ], + source=file_source, + ) + sources = [feature_view] + + def transform_features(features_df: pd.DataFrame) -> pd.DataFrame: + df = pd.DataFrame() + df["output1"] = features_df["feature1"] + df["output2"] = features_df["feature2"] + return df + + odfv = on_demand_feature_view( + sources=sources, + schema=[ + Field(name="output1", dtype=Float32), + Field(name="output2", dtype=Float32), + ], + )(transform_features) + + assert odfv.name == transform_features.__name__ + assert isinstance(odfv, OnDemandFeatureView) + + proto = odfv.to_proto() + assert proto.spec.name == transform_features.__name__ + + deserialized = OnDemandFeatureView.from_proto(proto) + assert deserialized.name == transform_features.__name__ + + def another_transform(features_df: pd.DataFrame) -> pd.DataFrame: + df = pd.DataFrame() + df["output1"] = features_df["feature1"] + df["output2"] = features_df["feature2"] + return df + + odfv_custom = on_demand_feature_view( + name=CUSTOM_FUNCTION_NAME, + sources=sources, + schema=[ + Field(name="output1", dtype=Float32), + Field(name="output2", dtype=Float32), + ], + )(another_transform) + + assert odfv_custom.name == CUSTOM_FUNCTION_NAME + assert isinstance(odfv_custom, OnDemandFeatureView) + + proto = odfv_custom.to_proto() + assert proto.spec.name == CUSTOM_FUNCTION_NAME + + deserialized = OnDemandFeatureView.from_proto(proto) + assert deserialized.name == CUSTOM_FUNCTION_NAME