From 3235ce34432fa55bfa3bd12679b1d65deec273d2 Mon Sep 17 00:00:00 2001 From: Kevin Zhang Date: Wed, 20 Apr 2022 16:31:49 -0700 Subject: [PATCH 01/21] Migrate over Signed-off-by: Kevin Zhang --- .../validating-historical-features.md | 8 ++--- .../java-demo/feature_repo/driver_repo.py | 12 +++---- sdk/python/feast/__init__.py | 4 +++ sdk/python/feast/on_demand_feature_view.py | 24 +++++++------- .../feature_repos/repo_configuration.py | 7 ++--- .../feature_repos/universal/feature_views.py | 31 +++++++++++++++++++ .../test_universal_odfv_feature_inference.py | 15 ++++----- .../tests/unit/test_on_demand_feature_view.py | 2 +- 8 files changed, 70 insertions(+), 33 deletions(-) diff --git a/docs/tutorials/validating-historical-features.md b/docs/tutorials/validating-historical-features.md index addd309902a..4757b0e9327 100644 --- a/docs/tutorials/validating-historical-features.md +++ b/docs/tutorials/validating-historical-features.md @@ -134,7 +134,7 @@ taxi_entity = Entity(name='taxi', join_keys=['taxi_id']) ```python -trips_stats_fv = FeatureView( +trips_stats_fv = BaseFeatureView( name='trip_stats', entities=['taxi'], features=[ @@ -160,9 +160,9 @@ trips_stats_fv = FeatureView( Field("avg_trip_seconds", Float64), Field("earned_per_hour", Float64), ], - sources={ - "stats": trips_stats_fv - } + sources=[ + trips_stats_fv, + ] ) def on_demand_stats(inp): out = pd.DataFrame() diff --git a/examples/java-demo/feature_repo/driver_repo.py b/examples/java-demo/feature_repo/driver_repo.py index ce9469647f6..f13c5b166af 100644 --- a/examples/java-demo/feature_repo/driver_repo.py +++ b/examples/java-demo/feature_repo/driver_repo.py @@ -7,7 +7,7 @@ from google.protobuf.duration_pb2 import Duration from feast.field import Field -from feast import Entity, Feature, FeatureView, FileSource, ValueType +from feast import Entity, Feature, BaseFeatureView, FileSource, ValueType driver_hourly_stats = FileSource( path="data/driver_stats_with_string.parquet", @@ -15,7 +15,7 @@ created_timestamp_column="created", ) driver = Entity(name="driver_id", value_type=ValueType.INT64, description="driver id",) -driver_hourly_stats_view = FeatureView( +driver_hourly_stats_view = BaseFeatureView( name="driver_hourly_stats", entities=["driver_id"], ttl=Duration(seconds=86400000), @@ -43,10 +43,10 @@ # Define an on demand feature view which can generate new features based on # existing feature views and RequestSource features @on_demand_feature_view( - inputs={ - "driver_hourly_stats": driver_hourly_stats_view, - "vals_to_add": input_request, - }, + inputs=[ + driver_hourly_stats_view, + input_request, + ], schema=[ Field(name="conv_rate_plus_val1", dtype=Float64), Field(name="conv_rate_plus_val2", dtype=Float64), diff --git a/sdk/python/feast/__init__.py b/sdk/python/feast/__init__.py index 5127e03b560..74cc889078a 100644 --- a/sdk/python/feast/__init__.py +++ b/sdk/python/feast/__init__.py @@ -21,6 +21,8 @@ from .feature_view import FeatureView from .field import Field from .on_demand_feature_view import OnDemandFeatureView +from .base_feature_view import BaseFeatureView +from .stream_feature_view import StreamFeatureView from .repo_config import RepoConfig from .request_feature_view import RequestFeatureView from .value_type import ValueType @@ -38,6 +40,7 @@ pass __all__ = [ + "BaseFeatureView" "Entity", "KafkaSource", "KinesisSource", @@ -49,6 +52,7 @@ "OnDemandFeatureView", "RepoConfig", "SourceType", + "StreamFeatureView", "ValueType", "BigQuerySource", "FileSource", diff --git a/sdk/python/feast/on_demand_feature_view.py b/sdk/python/feast/on_demand_feature_view.py index a807f3b4a40..c3e3aab21fe 100644 --- a/sdk/python/feast/on_demand_feature_view.py +++ b/sdk/python/feast/on_demand_feature_view.py @@ -7,6 +7,8 @@ import dill import pandas as pd +from feast.batch_feature_view import BatchFeatureView +from feast.stream_feature_view import StreamFeatureView from feast.base_feature_view import BaseFeatureView from feast.data_source import RequestSource from feast.errors import RegistryInferenceFailure, SpecifiedFeaturesNotPresentError @@ -73,7 +75,7 @@ def __init__( name: Optional[str] = None, features: Optional[List[Feature]] = None, sources: Optional[ - Dict[str, Union[FeatureView, FeatureViewProjection, RequestSource]] + List[Union[BatchFeatureView, StreamFeatureView, RequestSource]] ] = None, udf: Optional[MethodType] = None, inputs: Optional[ @@ -92,11 +94,11 @@ def __init__( features (deprecated): The list of features in the output of the on demand feature view, after the transformation has been applied. sources (optional): A map from input source names to the actual input sources, - which may be feature views, feature view projections, or request data sources. + which may be feature views, or request data sources. These sources serve as inputs to the udf, which will refer to them by name. udf (optional): The user defined transformation function, which must take pandas dataframes as inputs. - inputs (optional): A map from input source names to the actual input sources, + inputs (optional): (Deprecated) A map from input source names to the actual input sources, which may be feature views, feature view projections, or request data sources. These sources serve as inputs to the udf, which will refer to them by name. schema (optional): The list of features in the output of the on demand feature @@ -199,14 +201,14 @@ def __init__( assert _sources is not None self.source_feature_view_projections: Dict[str, FeatureViewProjection] = {} self.source_request_sources: Dict[str, RequestSource] = {} - for source_name, odfv_source in _sources.items(): + for odfv_source in _sources: if isinstance(odfv_source, RequestSource): - self.source_request_sources[source_name] = odfv_source + self.source_request_sources[odfv_source.name] = odfv_source elif isinstance(odfv_source, FeatureViewProjection): - self.source_feature_view_projections[source_name] = odfv_source + self.source_feature_view_projections[odfv_source.name] = odfv_source else: self.source_feature_view_projections[ - source_name + odfv_source.name ] = odfv_source.projection if _udf is None: @@ -222,8 +224,8 @@ def __copy__(self): fv = OnDemandFeatureView( name=self.name, schema=self.features, - sources=dict( - **self.source_feature_view_projections, **self.source_request_sources, + sources=list( + **self.source_feature_view_projections.values(), **self.source_request_sources.values(), ), udf=self.udf, description=self.description, @@ -476,7 +478,7 @@ def get_requested_odfvs(feature_refs, project, registry): def on_demand_feature_view( *args, features: Optional[List[Feature]] = None, - sources: Optional[Dict[str, Union[FeatureView, RequestSource]]] = None, + sources: Optional[List[Union[BatchFeatureView, StreamFeatureView, RequestSource]]] = None, inputs: Optional[Dict[str, Union[FeatureView, RequestSource]]] = None, schema: Optional[List[Field]] = None, description: str = "", @@ -490,7 +492,7 @@ def on_demand_feature_view( features (deprecated): The list of features in the output of the on demand feature view, after the transformation has been applied. sources (optional): A map from input source names to the actual input sources, - which may be feature views, feature view projections, or request data sources. + which may be feature views, or request data sources. These sources serve as inputs to the udf, which will refer to them by name. inputs (optional): A map from input source names to the actual input sources, which may be feature views, feature view projections, or request data sources. diff --git a/sdk/python/tests/integration/feature_repos/repo_configuration.py b/sdk/python/tests/integration/feature_repos/repo_configuration.py index 9902f7c7b8e..4647cc41c2b 100644 --- a/sdk/python/tests/integration/feature_repos/repo_configuration.py +++ b/sdk/python/tests/integration/feature_repos/repo_configuration.py @@ -39,6 +39,7 @@ create_conv_rate_request_source, create_customer_daily_profile_feature_view, create_driver_hourly_stats_feature_view, + create_driver_hourly_stats_base_feature_view, create_field_mapping_feature_view, create_global_stats_feature_view, create_location_stats_feature_view, @@ -311,15 +312,13 @@ def construct_universal_feature_views( data_sources: UniversalDataSources, with_odfv: bool = True, ) -> UniversalFeatureViews: driver_hourly_stats = create_driver_hourly_stats_feature_view(data_sources.driver) + driver_hourly_stats_base_feature_view = create_driver_hourly_stats_base_feature_view(data_sources.driver) return UniversalFeatureViews( customer=create_customer_daily_profile_feature_view(data_sources.customer), global_fv=create_global_stats_feature_view(data_sources.global_ds), driver=driver_hourly_stats, driver_odfv=conv_rate_plus_100_feature_view( - { - "driver": driver_hourly_stats, - "input_request": create_conv_rate_request_source(), - } + [driver_hourly_stats_base_feature_view, create_conv_rate_request_source()] ) if with_odfv else None, diff --git a/sdk/python/tests/integration/feature_repos/universal/feature_views.py b/sdk/python/tests/integration/feature_repos/universal/feature_views.py index a6786528e1f..705c209c86e 100644 --- a/sdk/python/tests/integration/feature_repos/universal/feature_views.py +++ b/sdk/python/tests/integration/feature_repos/universal/feature_views.py @@ -14,6 +14,7 @@ ) from feast.data_source import DataSource, RequestSource from feast.types import Array, FeastType, Float32, Float64, Int32 +from sdk.python.feast.base_feature_view import BaseFeatureView from tests.integration.feature_repos.universal.entities import location @@ -149,6 +150,20 @@ def create_item_embeddings_feature_view(source, infer_features: bool = False): ) return item_embeddings_feature_view +def create_item_embeddings_base_feature_view(source, infer_features: bool = False): + item_embeddings_feature_view = BaseFeatureView( + name="item_embeddings", + entities=["item"], + schema=None + if infer_features + else [ + Field(name="embedding_double", dtype=Array(Float64)), + Field(name="embedding_float", dtype=Array(Float32)), + ], + batch_source=source, + ttl=timedelta(hours=2), + ) + return item_embeddings_feature_view def create_driver_hourly_stats_feature_view(source, infer_features: bool = False): driver_stats_feature_view = FeatureView( @@ -166,6 +181,22 @@ def create_driver_hourly_stats_feature_view(source, infer_features: bool = False ) return driver_stats_feature_view +def create_driver_hourly_stats_base_feature_view(source, infer_features: bool = False): + driver_stats_feature_view = BaseFeatureView( + name="driver_stats", + entities=["driver"], + schema=None + if infer_features + else [ + Field(name="conv_rate", dtype=Float32), + Field(name="acc_rate", dtype=Float32), + Field(name="avg_daily_trips", dtype=Int32), + ], + source=source, + ttl=timedelta(hours=2), + ) + return driver_stats_feature_view + def create_customer_daily_profile_feature_view(source, infer_features: bool = False): customer_profile_feature_view = FeatureView( diff --git a/sdk/python/tests/integration/registration/test_universal_odfv_feature_inference.py b/sdk/python/tests/integration/registration/test_universal_odfv_feature_inference.py index 9dced8f13ad..ef6f6375646 100644 --- a/sdk/python/tests/integration/registration/test_universal_odfv_feature_inference.py +++ b/sdk/python/tests/integration/registration/test_universal_odfv_feature_inference.py @@ -7,12 +7,13 @@ from feast.errors import SpecifiedFeaturesNotPresentError from feast.infra.offline_stores.file_source import FileSource from feast.types import Float64 +from sdk.python.tests.integration.feature_repos.universal.feature_views import create_driver_hourly_stats_base_feature_view from tests.integration.feature_repos.universal.entities import customer, driver, item from tests.integration.feature_repos.universal.feature_views import ( conv_rate_plus_100_feature_view, create_conv_rate_request_source, - create_driver_hourly_stats_feature_view, - create_item_embeddings_feature_view, + create_driver_hourly_stats_base_feature_view, + create_item_embeddings_base_feature_view, create_similarity_request_source, similarity_feature_view, ) @@ -26,7 +27,7 @@ def test_infer_odfv_features(environment, universal_data_sources, infer_features (entities, datasets, data_sources) = universal_data_sources - driver_hourly_stats = create_driver_hourly_stats_feature_view(data_sources.driver) + driver_hourly_stats = create_driver_hourly_stats_base_feature_view(data_sources.driver) request_source = create_conv_rate_request_source() driver_odfv = conv_rate_plus_100_feature_view( {"driver": driver_hourly_stats, "input_request": request_source}, @@ -59,13 +60,13 @@ def test_infer_odfv_list_features(environment, infer_features, tmp_path): timestamp_field="event_timestamp", created_timestamp_column="created", ) - items = create_item_embeddings_feature_view(fake_items_src) + item_feature_view = create_item_embeddings_base_feature_view(fake_items_src) sim_odfv = similarity_feature_view( - {"items": items, "input_request": create_similarity_request_source()}, + [item_feature_view, create_similarity_request_source()], infer_features=infer_features, ) store = environment.feature_store - store.apply([item(), items, sim_odfv]) + store.apply([item(), item_feature_view, sim_odfv]) odfv = store.get_on_demand_feature_view("similarity") assert len(odfv.features) == 2 @@ -78,7 +79,7 @@ def test_infer_odfv_features_with_error(environment, universal_data_sources): (entities, datasets, data_sources) = universal_data_sources features = [Field(name="conv_rate_plus_200", dtype=Float64)] - driver_hourly_stats = create_driver_hourly_stats_feature_view(data_sources.driver) + driver_hourly_stats = create_driver_hourly_stats_base_feature_view(data_sources.driver) request_source = create_conv_rate_request_source() driver_odfv = conv_rate_plus_100_feature_view( {"driver": driver_hourly_stats, "input_request": request_source}, 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 9d45cfbb0b7..56593d38bdd 100644 --- a/sdk/python/tests/unit/test_on_demand_feature_view.py +++ b/sdk/python/tests/unit/test_on_demand_feature_view.py @@ -45,7 +45,7 @@ def test_hash(): ], source=file_source, ) - sources = {"my-feature-view": feature_view} + sources = [feature_view] on_demand_feature_view_1 = OnDemandFeatureView( name="my-on-demand-feature-view", sources=sources, From 27b7b6e40c93c27980cfa263f4b46dd615177621 Mon Sep 17 00:00:00 2001 From: Kevin Zhang Date: Wed, 20 Apr 2022 17:01:48 -0700 Subject: [PATCH 02/21] Fix Signed-off-by: Kevin Zhang --- sdk/python/feast/on_demand_feature_view.py | 40 ++++++++++++------- .../feature_repos/universal/feature_views.py | 2 +- .../registration/test_inference.py | 8 ++-- .../integration/registration/test_registry.py | 4 +- .../test_universal_odfv_feature_inference.py | 1 - 5 files changed, 32 insertions(+), 23 deletions(-) diff --git a/sdk/python/feast/on_demand_feature_view.py b/sdk/python/feast/on_demand_feature_view.py index c3e3aab21fe..1a27bdb5d88 100644 --- a/sdk/python/feast/on_demand_feature_view.py +++ b/sdk/python/feast/on_demand_feature_view.py @@ -125,8 +125,7 @@ def __init__( ), DeprecationWarning, ) - - _sources = sources or inputs + _sources = sources or [] if inputs and sources: raise ValueError("At most one of `sources` or `inputs` can be specified.") elif inputs: @@ -137,7 +136,8 @@ def __init__( ), DeprecationWarning, ) - + for source in inputs.values(): + _sources.append(source) _udf = udf if args: @@ -171,7 +171,9 @@ def __init__( DeprecationWarning, ) if len(args) >= 3: - _sources = args[2] + _inputs = args[2] + for source in _inputs.values(): + _sources.append(source) warnings.warn( ( "The `inputs` parameter is being deprecated. Please use `sources` instead. " @@ -197,7 +199,8 @@ def __init__( tags=tags, owner=owner, ) - + print("Asdf") + print(_sources) assert _sources is not None self.source_feature_view_projections: Dict[str, FeatureViewProjection] = {} self.source_request_sources: Dict[str, RequestSource] = {} @@ -304,23 +307,26 @@ def from_proto(cls, on_demand_feature_view_proto: OnDemandFeatureViewProto): Returns: A OnDemandFeatureView object based on the on-demand feature view protobuf. """ - sources = {} + sources = [] for ( - source_name, + _, on_demand_source, ) in on_demand_feature_view_proto.spec.sources.items(): if on_demand_source.WhichOneof("source") == "feature_view": - sources[source_name] = FeatureView.from_proto( + sources.append( + FeatureView.from_proto( on_demand_source.feature_view - ).projection + ).projection) elif on_demand_source.WhichOneof("source") == "feature_view_projection": - sources[source_name] = FeatureViewProjection.from_proto( + sources.append( + FeatureViewProjection.from_proto( on_demand_source.feature_view_projection - ) + )) else: - sources[source_name] = RequestSource.from_proto( + sources.append( + RequestSource.from_proto( on_demand_source.request_data_source - ) + )) on_demand_feature_view_obj = cls( name=on_demand_feature_view_proto.spec.name, schema=[ @@ -520,7 +526,7 @@ def on_demand_feature_view( DeprecationWarning, ) - _sources = sources or inputs + _sources = sources or [] if inputs and sources: raise ValueError("At most one of `sources` or `inputs` can be specified.") elif inputs: @@ -531,6 +537,8 @@ def on_demand_feature_view( ), DeprecationWarning, ) + for source in inputs.values(): + _sources.append(source) if args: warnings.warn( @@ -561,7 +569,9 @@ def on_demand_feature_view( DeprecationWarning, ) if len(args) >= 2: - _sources = args[1] + _inputs = args[1] + for source in _inputs.values(): + _sources.append(source) warnings.warn( ( "The `inputs` parameter is being deprecated. Please use `sources` instead. " diff --git a/sdk/python/tests/integration/feature_repos/universal/feature_views.py b/sdk/python/tests/integration/feature_repos/universal/feature_views.py index 705c209c86e..fbc2e83d797 100644 --- a/sdk/python/tests/integration/feature_repos/universal/feature_views.py +++ b/sdk/python/tests/integration/feature_repos/universal/feature_views.py @@ -5,6 +5,7 @@ import pandas as pd from feast import ( + BaseFeatureView, Feature, FeatureView, Field, @@ -14,7 +15,6 @@ ) from feast.data_source import DataSource, RequestSource from feast.types import Array, FeastType, Float32, Float64, Int32 -from sdk.python.feast.base_feature_view import BaseFeatureView from tests.integration.feature_repos.universal.entities import location diff --git a/sdk/python/tests/integration/registration/test_inference.py b/sdk/python/tests/integration/registration/test_inference.py index 6e9aff1f034..c6819ac3c0c 100644 --- a/sdk/python/tests/integration/registration/test_inference.py +++ b/sdk/python/tests/integration/registration/test_inference.py @@ -173,7 +173,7 @@ def test_on_demand_features_type_inference(): ) @on_demand_feature_view( - sources={"date_request": date_request}, + sources=[date_request], schema=[ Field(name="output", dtype=UnixTimestamp), Field(name="string_output", dtype=String), @@ -245,7 +245,7 @@ def test_datasource_inference(request_source_schema): Feature(name="output", dtype=ValueType.UNIX_TIMESTAMP), Feature(name="string_output", dtype=ValueType.STRING), ], - sources={"date_request": date_request}, + sources=[date_request], ) def test_view(features_df: pd.DataFrame) -> pd.DataFrame: data = pd.DataFrame() @@ -256,7 +256,7 @@ def test_view(features_df: pd.DataFrame) -> pd.DataFrame: test_view.infer_features() @on_demand_feature_view( - sources={"date_request": date_request}, + sources=[date_request], schema=[ Field(name="output", dtype=UnixTimestamp), Field(name="object_output", dtype=String), @@ -272,7 +272,7 @@ def invalid_test_view(features_df: pd.DataFrame) -> pd.DataFrame: invalid_test_view.infer_features() @on_demand_feature_view( - sources={"date_request": date_request}, + sources=[date_request], features=[ Feature(name="output", dtype=ValueType.UNIX_TIMESTAMP), Feature(name="missing", dtype=ValueType.STRING), diff --git a/sdk/python/tests/integration/registration/test_registry.py b/sdk/python/tests/integration/registration/test_registry.py index 5f72fb7125b..f011d73d2dd 100644 --- a/sdk/python/tests/integration/registration/test_registry.py +++ b/sdk/python/tests/integration/registration/test_registry.py @@ -267,7 +267,7 @@ def test_modify_feature_views_success(test_registry, request_source_schema): Feature(name="odfv1_my_feature_1", dtype=ValueType.STRING), Feature(name="odfv1_my_feature_2", dtype=ValueType.INT32), ], - sources={"request_source": request_source}, + sources=[request_source], ) def odfv1(feature_df: pd.DataFrame) -> pd.DataFrame: data = pd.DataFrame() @@ -287,7 +287,7 @@ def odfv1(feature_df: pd.DataFrame) -> pd.DataFrame: Feature(name="odfv1_my_feature_1", dtype=ValueType.FLOAT), Feature(name="odfv1_my_feature_2", dtype=ValueType.INT32), ], - sources={"request_source": request_source}, + sources=[request_source], ) def odfv1(feature_df: pd.DataFrame) -> pd.DataFrame: data = pd.DataFrame() diff --git a/sdk/python/tests/integration/registration/test_universal_odfv_feature_inference.py b/sdk/python/tests/integration/registration/test_universal_odfv_feature_inference.py index ef6f6375646..b404da1b8a4 100644 --- a/sdk/python/tests/integration/registration/test_universal_odfv_feature_inference.py +++ b/sdk/python/tests/integration/registration/test_universal_odfv_feature_inference.py @@ -7,7 +7,6 @@ from feast.errors import SpecifiedFeaturesNotPresentError from feast.infra.offline_stores.file_source import FileSource from feast.types import Float64 -from sdk.python.tests.integration.feature_repos.universal.feature_views import create_driver_hourly_stats_base_feature_view from tests.integration.feature_repos.universal.entities import customer, driver, item from tests.integration.feature_repos.universal.feature_views import ( conv_rate_plus_100_feature_view, From 6f01fd3c963ace4dc5934fd5103db1df97fd1d1a Mon Sep 17 00:00:00 2001 From: Kevin Zhang Date: Wed, 20 Apr 2022 17:43:23 -0700 Subject: [PATCH 03/21] Fix Signed-off-by: Kevin Zhang --- sdk/python/feast/__init__.py | 7 +- sdk/python/feast/on_demand_feature_view.py | 103 +++++++++++++----- .../feature_repos/repo_configuration.py | 6 +- .../feature_repos/universal/feature_views.py | 12 +- .../test_universal_odfv_feature_inference.py | 14 ++- 5 files changed, 100 insertions(+), 42 deletions(-) diff --git a/sdk/python/feast/__init__.py b/sdk/python/feast/__init__.py index 74cc889078a..1280d729a49 100644 --- a/sdk/python/feast/__init__.py +++ b/sdk/python/feast/__init__.py @@ -7,6 +7,7 @@ from feast.infra.offline_stores.redshift_source import RedshiftSource from feast.infra.offline_stores.snowflake_source import SnowflakeSource +from .base_feature_view import BaseFeatureView from .data_source import ( KafkaSource, KinesisSource, @@ -21,10 +22,9 @@ from .feature_view import FeatureView from .field import Field from .on_demand_feature_view import OnDemandFeatureView -from .base_feature_view import BaseFeatureView -from .stream_feature_view import StreamFeatureView from .repo_config import RepoConfig from .request_feature_view import RequestFeatureView +from .stream_feature_view import StreamFeatureView from .value_type import ValueType logging.basicConfig( @@ -40,8 +40,7 @@ pass __all__ = [ - "BaseFeatureView" - "Entity", + "BaseFeatureView" "Entity", "KafkaSource", "KinesisSource", "Feature", diff --git a/sdk/python/feast/on_demand_feature_view.py b/sdk/python/feast/on_demand_feature_view.py index 1a27bdb5d88..8df9afd6fbd 100644 --- a/sdk/python/feast/on_demand_feature_view.py +++ b/sdk/python/feast/on_demand_feature_view.py @@ -7,9 +7,8 @@ import dill import pandas as pd -from feast.batch_feature_view import BatchFeatureView -from feast.stream_feature_view import StreamFeatureView from feast.base_feature_view import BaseFeatureView +from feast.batch_feature_view import BatchFeatureView from feast.data_source import RequestSource from feast.errors import RegistryInferenceFailure, SpecifiedFeaturesNotPresentError from feast.feature import Feature @@ -27,6 +26,7 @@ from feast.protos.feast.core.OnDemandFeatureView_pb2 import ( UserDefinedFunction as UserDefinedFunctionProto, ) +from feast.stream_feature_view import StreamFeatureView from feast.type_map import ( feast_value_type_to_pandas_type, python_type_to_feast_value_type, @@ -136,8 +136,18 @@ def __init__( ), DeprecationWarning, ) - for source in inputs.values(): - _sources.append(source) + for _, source in inputs.items(): + if isinstance(source, FeatureView): + _sources.append(feature_view_to_batch_feature_view(source)) + elif isinstance(source, FeatureViewProjection): + _sources.append(BatchFeatureView( + name=source.name, + schema=source.features, + )) + elif isinstance(source, RequestSource): + _sources.append(source) + else: + raise ValueError("input can only accept FeatureView, FeatureViewProjection, or RequestSource") _udf = udf if args: @@ -172,8 +182,18 @@ def __init__( ) if len(args) >= 3: _inputs = args[2] - for source in _inputs.values(): - _sources.append(source) + for _, source in _inputs.items(): + if isinstance(source, FeatureView): + _sources.append(feature_view_to_batch_feature_view(source)) + elif isinstance(source, FeatureViewProjection): + _sources.append(BatchFeatureView( + name=source.name, + schema=source.features, + )) + elif isinstance(source, RequestSource): + _sources.append(source) + else: + raise ValueError("input can only accept FeatureView, FeatureViewProjection, or RequestSource") warnings.warn( ( "The `inputs` parameter is being deprecated. Please use `sources` instead. " @@ -199,8 +219,6 @@ def __init__( tags=tags, owner=owner, ) - print("Asdf") - print(_sources) assert _sources is not None self.source_feature_view_projections: Dict[str, FeatureViewProjection] = {} self.source_request_sources: Dict[str, RequestSource] = {} @@ -228,7 +246,8 @@ def __copy__(self): name=self.name, schema=self.features, sources=list( - **self.source_feature_view_projections.values(), **self.source_request_sources.values(), + **self.source_feature_view_projections.values(), + **self.source_request_sources.values(), ), udf=self.udf, description=self.description, @@ -308,25 +327,21 @@ def from_proto(cls, on_demand_feature_view_proto: OnDemandFeatureViewProto): A OnDemandFeatureView object based on the on-demand feature view protobuf. """ sources = [] - for ( - _, - on_demand_source, - ) in on_demand_feature_view_proto.spec.sources.items(): + for (_, on_demand_source,) in on_demand_feature_view_proto.spec.sources.items(): if on_demand_source.WhichOneof("source") == "feature_view": sources.append( - FeatureView.from_proto( - on_demand_source.feature_view - ).projection) + FeatureView.from_proto(on_demand_source.feature_view).projection + ) elif on_demand_source.WhichOneof("source") == "feature_view_projection": sources.append( FeatureViewProjection.from_proto( - on_demand_source.feature_view_projection - )) + on_demand_source.feature_view_projection + ) + ) else: sources.append( - RequestSource.from_proto( - on_demand_source.request_data_source - )) + RequestSource.from_proto(on_demand_source.request_data_source) + ) on_demand_feature_view_obj = cls( name=on_demand_feature_view_proto.spec.name, schema=[ @@ -484,7 +499,9 @@ def get_requested_odfvs(feature_refs, project, registry): def on_demand_feature_view( *args, features: Optional[List[Feature]] = None, - sources: Optional[List[Union[BatchFeatureView, StreamFeatureView, RequestSource]]] = None, + sources: Optional[ + List[Union[BatchFeatureView, StreamFeatureView, RequestSource]] + ] = None, inputs: Optional[Dict[str, Union[FeatureView, RequestSource]]] = None, schema: Optional[List[Field]] = None, description: str = "", @@ -537,8 +554,18 @@ def on_demand_feature_view( ), DeprecationWarning, ) - for source in inputs.values(): - _sources.append(source) + for _, source in inputs.items(): + if isinstance(source, FeatureView): + _sources.append(feature_view_to_batch_feature_view(source)) + elif isinstance(source, FeatureViewProjection): + _sources.append(BatchFeatureView( + name=source.name, + schema=source.features, + )) + elif isinstance(source, RequestSource): + _sources.append(source) + else: + raise ValueError("input can only accept FeatureView, FeatureViewProjection, or RequestSource") if args: warnings.warn( @@ -570,9 +597,19 @@ def on_demand_feature_view( ) if len(args) >= 2: _inputs = args[1] - for source in _inputs.values(): - _sources.append(source) - warnings.warn( + for _, source in _inputs.items(): + if isinstance(source, FeatureView): + _sources.append(feature_view_to_batch_feature_view(source)) + elif isinstance(source, FeatureViewProjection): + _sources.append(BatchFeatureView( + name=source.name, + schema=source.features, + )) + elif isinstance(source, RequestSource): + _sources.append(source) + else: + raise ValueError("input can only accept FeatureView, FeatureViewProjection, or RequestSource") + warnings.warn( ( "The `inputs` parameter is being deprecated. Please use `sources` instead. " "Feast 0.21 and onwards will not support the `inputs` parameter." @@ -599,3 +636,15 @@ def decorator(user_function): return on_demand_feature_view_obj return decorator + +def feature_view_to_batch_feature_view(fv: FeatureView) -> BatchFeatureView: + return BatchFeatureView( + name=fv.name, + entities=fv.entities, + ttl=fv.ttl, + tags=fv.tags, + online=fv.online, + owner=fv.owner, + schema=fv.schema, + source=fv.source, + ) \ No newline at end of file diff --git a/sdk/python/tests/integration/feature_repos/repo_configuration.py b/sdk/python/tests/integration/feature_repos/repo_configuration.py index 4647cc41c2b..cc094a055aa 100644 --- a/sdk/python/tests/integration/feature_repos/repo_configuration.py +++ b/sdk/python/tests/integration/feature_repos/repo_configuration.py @@ -38,8 +38,8 @@ conv_rate_plus_100_feature_view, create_conv_rate_request_source, create_customer_daily_profile_feature_view, + create_driver_hourly_stats_batch_feature_view, create_driver_hourly_stats_feature_view, - create_driver_hourly_stats_base_feature_view, create_field_mapping_feature_view, create_global_stats_feature_view, create_location_stats_feature_view, @@ -312,7 +312,9 @@ def construct_universal_feature_views( data_sources: UniversalDataSources, with_odfv: bool = True, ) -> UniversalFeatureViews: driver_hourly_stats = create_driver_hourly_stats_feature_view(data_sources.driver) - driver_hourly_stats_base_feature_view = create_driver_hourly_stats_base_feature_view(data_sources.driver) + driver_hourly_stats_base_feature_view = create_driver_hourly_stats_batch_feature_view( + data_sources.driver + ) return UniversalFeatureViews( customer=create_customer_daily_profile_feature_view(data_sources.customer), global_fv=create_global_stats_feature_view(data_sources.global_ds), diff --git a/sdk/python/tests/integration/feature_repos/universal/feature_views.py b/sdk/python/tests/integration/feature_repos/universal/feature_views.py index fbc2e83d797..e1dce1ce9e2 100644 --- a/sdk/python/tests/integration/feature_repos/universal/feature_views.py +++ b/sdk/python/tests/integration/feature_repos/universal/feature_views.py @@ -15,6 +15,7 @@ ) from feast.data_source import DataSource, RequestSource from feast.types import Array, FeastType, Float32, Float64, Int32 +from sdk.python.feast.batch_feature_view import BatchFeatureView from tests.integration.feature_repos.universal.entities import location @@ -150,8 +151,9 @@ def create_item_embeddings_feature_view(source, infer_features: bool = False): ) return item_embeddings_feature_view -def create_item_embeddings_base_feature_view(source, infer_features: bool = False): - item_embeddings_feature_view = BaseFeatureView( + +def create_item_embeddings_batch_feature_view(source, infer_features: bool = False): + item_embeddings_feature_view = BatchFeatureView( name="item_embeddings", entities=["item"], schema=None @@ -165,6 +167,7 @@ def create_item_embeddings_base_feature_view(source, infer_features: bool = Fals ) return item_embeddings_feature_view + def create_driver_hourly_stats_feature_view(source, infer_features: bool = False): driver_stats_feature_view = FeatureView( name="driver_stats", @@ -181,8 +184,9 @@ def create_driver_hourly_stats_feature_view(source, infer_features: bool = False ) return driver_stats_feature_view -def create_driver_hourly_stats_base_feature_view(source, infer_features: bool = False): - driver_stats_feature_view = BaseFeatureView( + +def create_driver_hourly_stats_batch_feature_view(source, infer_features: bool = False): + driver_stats_feature_view = BatchFeatureView( name="driver_stats", entities=["driver"], schema=None diff --git a/sdk/python/tests/integration/registration/test_universal_odfv_feature_inference.py b/sdk/python/tests/integration/registration/test_universal_odfv_feature_inference.py index b404da1b8a4..daefd45f805 100644 --- a/sdk/python/tests/integration/registration/test_universal_odfv_feature_inference.py +++ b/sdk/python/tests/integration/registration/test_universal_odfv_feature_inference.py @@ -11,8 +11,8 @@ from tests.integration.feature_repos.universal.feature_views import ( conv_rate_plus_100_feature_view, create_conv_rate_request_source, - create_driver_hourly_stats_base_feature_view, - create_item_embeddings_base_feature_view, + create_driver_hourly_stats_batch_feature_view, + create_item_embeddings_batch_feature_view, create_similarity_request_source, similarity_feature_view, ) @@ -26,7 +26,9 @@ def test_infer_odfv_features(environment, universal_data_sources, infer_features (entities, datasets, data_sources) = universal_data_sources - driver_hourly_stats = create_driver_hourly_stats_base_feature_view(data_sources.driver) + driver_hourly_stats = create_driver_hourly_stats_batch_feature_view( + data_sources.driver + ) request_source = create_conv_rate_request_source() driver_odfv = conv_rate_plus_100_feature_view( {"driver": driver_hourly_stats, "input_request": request_source}, @@ -59,7 +61,7 @@ def test_infer_odfv_list_features(environment, infer_features, tmp_path): timestamp_field="event_timestamp", created_timestamp_column="created", ) - item_feature_view = create_item_embeddings_base_feature_view(fake_items_src) + item_feature_view = create_item_embeddings_batch_feature_view(fake_items_src) sim_odfv = similarity_feature_view( [item_feature_view, create_similarity_request_source()], infer_features=infer_features, @@ -78,7 +80,9 @@ def test_infer_odfv_features_with_error(environment, universal_data_sources): (entities, datasets, data_sources) = universal_data_sources features = [Field(name="conv_rate_plus_200", dtype=Float64)] - driver_hourly_stats = create_driver_hourly_stats_base_feature_view(data_sources.driver) + driver_hourly_stats = create_driver_hourly_stats_batch_feature_view( + data_sources.driver + ) request_source = create_conv_rate_request_source() driver_odfv = conv_rate_plus_100_feature_view( {"driver": driver_hourly_stats, "input_request": request_source}, From d77f9a1c5c6b56b467ed141aea5315c915d07110 Mon Sep 17 00:00:00 2001 From: Kevin Zhang Date: Wed, 20 Apr 2022 17:50:01 -0700 Subject: [PATCH 04/21] Fix Signed-off-by: Kevin Zhang --- sdk/python/feast/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sdk/python/feast/__init__.py b/sdk/python/feast/__init__.py index 1280d729a49..551c899b9b6 100644 --- a/sdk/python/feast/__init__.py +++ b/sdk/python/feast/__init__.py @@ -40,7 +40,8 @@ pass __all__ = [ - "BaseFeatureView" "Entity", + "BaseFeatureView", + "Entity", "KafkaSource", "KinesisSource", "Feature", From fb49cf33fe8618bb4b3e1e985b8d508c963924bd Mon Sep 17 00:00:00 2001 From: Kevin Zhang Date: Wed, 20 Apr 2022 17:51:13 -0700 Subject: [PATCH 05/21] temp fix Signed-off-by: Kevin Zhang --- sdk/python/feast/on_demand_feature_view.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/python/feast/on_demand_feature_view.py b/sdk/python/feast/on_demand_feature_view.py index 8df9afd6fbd..7e88245f74b 100644 --- a/sdk/python/feast/on_demand_feature_view.py +++ b/sdk/python/feast/on_demand_feature_view.py @@ -602,8 +602,8 @@ def on_demand_feature_view( _sources.append(feature_view_to_batch_feature_view(source)) elif isinstance(source, FeatureViewProjection): _sources.append(BatchFeatureView( - name=source.name, - schema=source.features, + name=source.name, # type: ignore + schema=source.features, # type: ignore )) elif isinstance(source, RequestSource): _sources.append(source) From 14fe923ad88bd8eeb863fa76727c4f80c15dae43 Mon Sep 17 00:00:00 2001 From: Kevin Zhang Date: Wed, 20 Apr 2022 17:53:46 -0700 Subject: [PATCH 06/21] Fix lint Signed-off-by: Kevin Zhang --- sdk/python/feast/on_demand_feature_view.py | 62 +++++++++++-------- .../feature_repos/universal/feature_views.py | 3 +- 2 files changed, 36 insertions(+), 29 deletions(-) diff --git a/sdk/python/feast/on_demand_feature_view.py b/sdk/python/feast/on_demand_feature_view.py index 7e88245f74b..e7eeeca3e79 100644 --- a/sdk/python/feast/on_demand_feature_view.py +++ b/sdk/python/feast/on_demand_feature_view.py @@ -140,14 +140,15 @@ def __init__( if isinstance(source, FeatureView): _sources.append(feature_view_to_batch_feature_view(source)) elif isinstance(source, FeatureViewProjection): - _sources.append(BatchFeatureView( - name=source.name, - schema=source.features, - )) + _sources.append( + BatchFeatureView(name=source.name, schema=source.features,) + ) elif isinstance(source, RequestSource): _sources.append(source) else: - raise ValueError("input can only accept FeatureView, FeatureViewProjection, or RequestSource") + raise ValueError( + "input can only accept FeatureView, FeatureViewProjection, or RequestSource" + ) _udf = udf if args: @@ -186,14 +187,15 @@ def __init__( if isinstance(source, FeatureView): _sources.append(feature_view_to_batch_feature_view(source)) elif isinstance(source, FeatureViewProjection): - _sources.append(BatchFeatureView( - name=source.name, - schema=source.features, - )) + _sources.append( + BatchFeatureView(name=source.name, schema=source.features,) + ) elif isinstance(source, RequestSource): _sources.append(source) else: - raise ValueError("input can only accept FeatureView, FeatureViewProjection, or RequestSource") + raise ValueError( + "input can only accept FeatureView, FeatureViewProjection, or RequestSource" + ) warnings.warn( ( "The `inputs` parameter is being deprecated. Please use `sources` instead. " @@ -558,14 +560,15 @@ def on_demand_feature_view( if isinstance(source, FeatureView): _sources.append(feature_view_to_batch_feature_view(source)) elif isinstance(source, FeatureViewProjection): - _sources.append(BatchFeatureView( - name=source.name, - schema=source.features, - )) + _sources.append( + BatchFeatureView(name=source.name, schema=source.features,) + ) elif isinstance(source, RequestSource): _sources.append(source) else: - raise ValueError("input can only accept FeatureView, FeatureViewProjection, or RequestSource") + raise ValueError( + "input can only accept FeatureView, FeatureViewProjection, or RequestSource" + ) if args: warnings.warn( @@ -601,21 +604,25 @@ def on_demand_feature_view( if isinstance(source, FeatureView): _sources.append(feature_view_to_batch_feature_view(source)) elif isinstance(source, FeatureViewProjection): - _sources.append(BatchFeatureView( - name=source.name, # type: ignore - schema=source.features, # type: ignore - )) + _sources.append( + BatchFeatureView( + name=source.name, # type: ignore + schema=source.features, # type: ignore + ) + ) elif isinstance(source, RequestSource): _sources.append(source) else: - raise ValueError("input can only accept FeatureView, FeatureViewProjection, or RequestSource") + raise ValueError( + "input can only accept FeatureView, FeatureViewProjection, or RequestSource" + ) warnings.warn( - ( - "The `inputs` parameter is being deprecated. Please use `sources` instead. " - "Feast 0.21 and onwards will not support the `inputs` parameter." - ), - DeprecationWarning, - ) + ( + "The `inputs` parameter is being deprecated. Please use `sources` instead. " + "Feast 0.21 and onwards will not support the `inputs` parameter." + ), + DeprecationWarning, + ) if not _sources: raise ValueError("The `sources` parameter must be specified.") @@ -637,6 +644,7 @@ def decorator(user_function): return decorator + def feature_view_to_batch_feature_view(fv: FeatureView) -> BatchFeatureView: return BatchFeatureView( name=fv.name, @@ -647,4 +655,4 @@ def feature_view_to_batch_feature_view(fv: FeatureView) -> BatchFeatureView: owner=fv.owner, schema=fv.schema, source=fv.source, - ) \ No newline at end of file + ) diff --git a/sdk/python/tests/integration/feature_repos/universal/feature_views.py b/sdk/python/tests/integration/feature_repos/universal/feature_views.py index e1dce1ce9e2..860989005be 100644 --- a/sdk/python/tests/integration/feature_repos/universal/feature_views.py +++ b/sdk/python/tests/integration/feature_repos/universal/feature_views.py @@ -3,9 +3,9 @@ import numpy as np import pandas as pd +from sdk.python.feast.batch_feature_view import BatchFeatureView from feast import ( - BaseFeatureView, Feature, FeatureView, Field, @@ -15,7 +15,6 @@ ) from feast.data_source import DataSource, RequestSource from feast.types import Array, FeastType, Float32, Float64, Int32 -from sdk.python.feast.batch_feature_view import BatchFeatureView from tests.integration.feature_repos.universal.entities import location From 46eec614308d3bfe646f481818e8e0f08b619279 Mon Sep 17 00:00:00 2001 From: Kevin Zhang Date: Wed, 20 Apr 2022 17:54:27 -0700 Subject: [PATCH 07/21] Fix lint Signed-off-by: Kevin Zhang --- sdk/python/feast/on_demand_feature_view.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/python/feast/on_demand_feature_view.py b/sdk/python/feast/on_demand_feature_view.py index e7eeeca3e79..4efc058a26d 100644 --- a/sdk/python/feast/on_demand_feature_view.py +++ b/sdk/python/feast/on_demand_feature_view.py @@ -85,7 +85,7 @@ def __init__( description: str = "", tags: Optional[Dict[str, str]] = None, owner: str = "", - ): + ): # noqa: C901 """ Creates an OnDemandFeatureView object. From 3742cf1990fdadae866b0b7892f5332af70da87c Mon Sep 17 00:00:00 2001 From: Kevin Zhang Date: Wed, 20 Apr 2022 17:55:01 -0700 Subject: [PATCH 08/21] Fix lint Signed-off-by: Kevin Zhang --- sdk/python/feast/on_demand_feature_view.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/python/feast/on_demand_feature_view.py b/sdk/python/feast/on_demand_feature_view.py index 4efc058a26d..33c43cc3c38 100644 --- a/sdk/python/feast/on_demand_feature_view.py +++ b/sdk/python/feast/on_demand_feature_view.py @@ -69,7 +69,7 @@ class OnDemandFeatureView(BaseFeatureView): owner: str @log_exceptions - def __init__( + def __init__( # noqa: C901 self, *args, name: Optional[str] = None, @@ -85,7 +85,7 @@ def __init__( description: str = "", tags: Optional[Dict[str, str]] = None, owner: str = "", - ): # noqa: C901 + ): """ Creates an OnDemandFeatureView object. From f1f8f277d6e8fc6b002917a8aad8a158f11aa12d Mon Sep 17 00:00:00 2001 From: Kevin Zhang Date: Wed, 20 Apr 2022 17:55:36 -0700 Subject: [PATCH 09/21] Fix Signed-off-by: Kevin Zhang --- sdk/python/feast/on_demand_feature_view.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/python/feast/on_demand_feature_view.py b/sdk/python/feast/on_demand_feature_view.py index 33c43cc3c38..0c0238d2ec9 100644 --- a/sdk/python/feast/on_demand_feature_view.py +++ b/sdk/python/feast/on_demand_feature_view.py @@ -69,7 +69,7 @@ class OnDemandFeatureView(BaseFeatureView): owner: str @log_exceptions - def __init__( # noqa: C901 + def __init__( # noqa: C901 self, *args, name: Optional[str] = None, From 7ef6462ee8344cd5956195ff39fdaaa036c3f56b Mon Sep 17 00:00:00 2001 From: Kevin Zhang Date: Wed, 20 Apr 2022 17:56:33 -0700 Subject: [PATCH 10/21] Fix import Signed-off-by: Kevin Zhang --- .../tests/integration/feature_repos/universal/feature_views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/python/tests/integration/feature_repos/universal/feature_views.py b/sdk/python/tests/integration/feature_repos/universal/feature_views.py index 860989005be..0db156bb97c 100644 --- a/sdk/python/tests/integration/feature_repos/universal/feature_views.py +++ b/sdk/python/tests/integration/feature_repos/universal/feature_views.py @@ -3,9 +3,9 @@ import numpy as np import pandas as pd -from sdk.python.feast.batch_feature_view import BatchFeatureView from feast import ( + BatchFeatureView, Feature, FeatureView, Field, From 4c147ee82cc67942839986ddfcedc91184a28448 Mon Sep 17 00:00:00 2001 From: Kevin Zhang Date: Wed, 20 Apr 2022 18:04:22 -0700 Subject: [PATCH 11/21] Fix Signed-off-by: Kevin Zhang --- sdk/python/feast/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sdk/python/feast/__init__.py b/sdk/python/feast/__init__.py index 551c899b9b6..6f97b5a3662 100644 --- a/sdk/python/feast/__init__.py +++ b/sdk/python/feast/__init__.py @@ -24,6 +24,7 @@ from .on_demand_feature_view import OnDemandFeatureView from .repo_config import RepoConfig from .request_feature_view import RequestFeatureView +from .batch_feature_view import BatchFeatureView from .stream_feature_view import StreamFeatureView from .value_type import ValueType @@ -40,7 +41,7 @@ pass __all__ = [ - "BaseFeatureView", + "BatchFeatureView", "Entity", "KafkaSource", "KinesisSource", From e0078b125a004930c819f20e51e3e1b84bbfb435 Mon Sep 17 00:00:00 2001 From: Kevin Zhang Date: Thu, 21 Apr 2022 10:28:18 -0700 Subject: [PATCH 12/21] Fix Signed-off-by: Kevin Zhang --- sdk/python/feast/on_demand_feature_view.py | 6 ++---- .../integration/feature_repos/universal/feature_views.py | 6 +++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/sdk/python/feast/on_demand_feature_view.py b/sdk/python/feast/on_demand_feature_view.py index 0c0238d2ec9..b4a2f541e35 100644 --- a/sdk/python/feast/on_demand_feature_view.py +++ b/sdk/python/feast/on_demand_feature_view.py @@ -244,13 +244,11 @@ def proto_class(self) -> Type[OnDemandFeatureViewProto]: return OnDemandFeatureViewProto def __copy__(self): + fv = OnDemandFeatureView( name=self.name, schema=self.features, - sources=list( - **self.source_feature_view_projections.values(), - **self.source_request_sources.values(), - ), + sources=list(self.source_feature_view_projections.values()) + list(self.source_request_sources.values()), udf=self.udf, description=self.description, tags=self.tags, diff --git a/sdk/python/tests/integration/feature_repos/universal/feature_views.py b/sdk/python/tests/integration/feature_repos/universal/feature_views.py index 0db156bb97c..63d39a7afb4 100644 --- a/sdk/python/tests/integration/feature_repos/universal/feature_views.py +++ b/sdk/python/tests/integration/feature_repos/universal/feature_views.py @@ -151,7 +151,7 @@ def create_item_embeddings_feature_view(source, infer_features: bool = False): return item_embeddings_feature_view -def create_item_embeddings_batch_feature_view(source, infer_features: bool = False): +def create_item_embeddings_batch_feature_view(source, infer_features: bool = False) -> BatchFeatureView: item_embeddings_feature_view = BatchFeatureView( name="item_embeddings", entities=["item"], @@ -161,7 +161,7 @@ def create_item_embeddings_batch_feature_view(source, infer_features: bool = Fal Field(name="embedding_double", dtype=Array(Float64)), Field(name="embedding_float", dtype=Array(Float32)), ], - batch_source=source, + source=source, ttl=timedelta(hours=2), ) return item_embeddings_feature_view @@ -184,7 +184,7 @@ def create_driver_hourly_stats_feature_view(source, infer_features: bool = False return driver_stats_feature_view -def create_driver_hourly_stats_batch_feature_view(source, infer_features: bool = False): +def create_driver_hourly_stats_batch_feature_view(source, infer_features: bool = False) -> BatchFeatureView: driver_stats_feature_view = BatchFeatureView( name="driver_stats", entities=["driver"], From 0b9d4fde7a8e5b7920fb935af0e3abeb47b04764 Mon Sep 17 00:00:00 2001 From: Kevin Zhang Date: Thu, 21 Apr 2022 10:29:06 -0700 Subject: [PATCH 13/21] Fix lint Signed-off-by: Kevin Zhang --- sdk/python/feast/__init__.py | 3 +-- sdk/python/feast/on_demand_feature_view.py | 3 ++- .../integration/feature_repos/universal/feature_views.py | 8 ++++++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/sdk/python/feast/__init__.py b/sdk/python/feast/__init__.py index 6f97b5a3662..62cc52215c9 100644 --- a/sdk/python/feast/__init__.py +++ b/sdk/python/feast/__init__.py @@ -7,7 +7,7 @@ from feast.infra.offline_stores.redshift_source import RedshiftSource from feast.infra.offline_stores.snowflake_source import SnowflakeSource -from .base_feature_view import BaseFeatureView +from .batch_feature_view import BatchFeatureView from .data_source import ( KafkaSource, KinesisSource, @@ -24,7 +24,6 @@ from .on_demand_feature_view import OnDemandFeatureView from .repo_config import RepoConfig from .request_feature_view import RequestFeatureView -from .batch_feature_view import BatchFeatureView from .stream_feature_view import StreamFeatureView from .value_type import ValueType diff --git a/sdk/python/feast/on_demand_feature_view.py b/sdk/python/feast/on_demand_feature_view.py index b4a2f541e35..4f76fe980b3 100644 --- a/sdk/python/feast/on_demand_feature_view.py +++ b/sdk/python/feast/on_demand_feature_view.py @@ -248,7 +248,8 @@ def __copy__(self): fv = OnDemandFeatureView( name=self.name, schema=self.features, - sources=list(self.source_feature_view_projections.values()) + list(self.source_request_sources.values()), + sources=list(self.source_feature_view_projections.values()) + + list(self.source_request_sources.values()), udf=self.udf, description=self.description, tags=self.tags, diff --git a/sdk/python/tests/integration/feature_repos/universal/feature_views.py b/sdk/python/tests/integration/feature_repos/universal/feature_views.py index 63d39a7afb4..26c2513995f 100644 --- a/sdk/python/tests/integration/feature_repos/universal/feature_views.py +++ b/sdk/python/tests/integration/feature_repos/universal/feature_views.py @@ -151,7 +151,9 @@ def create_item_embeddings_feature_view(source, infer_features: bool = False): return item_embeddings_feature_view -def create_item_embeddings_batch_feature_view(source, infer_features: bool = False) -> BatchFeatureView: +def create_item_embeddings_batch_feature_view( + source, infer_features: bool = False +) -> BatchFeatureView: item_embeddings_feature_view = BatchFeatureView( name="item_embeddings", entities=["item"], @@ -184,7 +186,9 @@ def create_driver_hourly_stats_feature_view(source, infer_features: bool = False return driver_stats_feature_view -def create_driver_hourly_stats_batch_feature_view(source, infer_features: bool = False) -> BatchFeatureView: +def create_driver_hourly_stats_batch_feature_view( + source, infer_features: bool = False +) -> BatchFeatureView: driver_stats_feature_view = BatchFeatureView( name="driver_stats", entities=["driver"], From 390772c65aa954e54d6be6271e9dff53152c6ee9 Mon Sep 17 00:00:00 2001 From: Kevin Zhang Date: Thu, 21 Apr 2022 11:12:05 -0700 Subject: [PATCH 14/21] Fix Signed-off-by: Kevin Zhang --- sdk/python/feast/on_demand_feature_view.py | 1 - .../test_universal_odfv_feature_inference.py | 4 +- .../tests/unit/test_on_demand_feature_view.py | 62 ++++++++++++++++++- 3 files changed, 62 insertions(+), 5 deletions(-) diff --git a/sdk/python/feast/on_demand_feature_view.py b/sdk/python/feast/on_demand_feature_view.py index 4f76fe980b3..fc54ec032a0 100644 --- a/sdk/python/feast/on_demand_feature_view.py +++ b/sdk/python/feast/on_demand_feature_view.py @@ -543,7 +543,6 @@ def on_demand_feature_view( ), DeprecationWarning, ) - _sources = sources or [] if inputs and sources: raise ValueError("At most one of `sources` or `inputs` can be specified.") diff --git a/sdk/python/tests/integration/registration/test_universal_odfv_feature_inference.py b/sdk/python/tests/integration/registration/test_universal_odfv_feature_inference.py index daefd45f805..e18c60fad98 100644 --- a/sdk/python/tests/integration/registration/test_universal_odfv_feature_inference.py +++ b/sdk/python/tests/integration/registration/test_universal_odfv_feature_inference.py @@ -31,7 +31,7 @@ def test_infer_odfv_features(environment, universal_data_sources, infer_features ) request_source = create_conv_rate_request_source() driver_odfv = conv_rate_plus_100_feature_view( - {"driver": driver_hourly_stats, "input_request": request_source}, + [driver_hourly_stats, request_source], infer_features=infer_features, ) @@ -85,7 +85,7 @@ def test_infer_odfv_features_with_error(environment, universal_data_sources): ) request_source = create_conv_rate_request_source() driver_odfv = conv_rate_plus_100_feature_view( - {"driver": driver_hourly_stats, "input_request": request_source}, + [driver_hourly_stats, request_source], features=features, ) 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 56593d38bdd..2b63da29301 100644 --- a/sdk/python/tests/unit/test_on_demand_feature_view.py +++ b/sdk/python/tests/unit/test_on_demand_feature_view.py @@ -11,12 +11,15 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +from datetime import date import pandas as pd - +import pytest from feast.feature_view import FeatureView from feast.field import Field from feast.infra.offline_stores.file_source import FileSource -from feast.on_demand_feature_view import OnDemandFeatureView +from feast import RequestSource +from feast.types import String, UnixTimestamp +from feast.on_demand_feature_view import OnDemandFeatureView, on_demand_feature_view from feast.types import Float32 @@ -100,3 +103,58 @@ def test_hash(): on_demand_feature_view_4, } assert len(s4) == 3 + +def test_inputs_parameter_deprecation_in_odfv(): + date_request = RequestSource( + name="date_request", schema=[Field(name="some_date", dtype=UnixTimestamp)], + ) + with pytest.warns(DeprecationWarning): + @on_demand_feature_view( + inputs={"date_request": date_request}, + schema=[ + Field(name="output", dtype=UnixTimestamp), + Field(name="string_output", dtype=String), + ], + ) + def test_view(features_df: pd.DataFrame) -> pd.DataFrame: + data = pd.DataFrame() + data["output"] = features_df["some_date"] + data["string_output"] = features_df["some_date"].astype(pd.StringDtype()) + return data + odfv = test_view + assert odfv.name == "test_view" + assert len(odfv.source_request_sources) == 1 + assert odfv.source_request_sources["date_request"].name == "date_request" + assert odfv.source_request_sources["date_request"].schema == date_request.schema + + with pytest.raises(ValueError): + @on_demand_feature_view( + inputs={"date_request": date_request}, + sources=[date_request], + schema=[ + Field(name="output", dtype=UnixTimestamp), + Field(name="string_output", dtype=String), + ], + ) + def incorrect_testview(features_df: pd.DataFrame) -> pd.DataFrame: + data = pd.DataFrame() + data["output"] = features_df["some_date"] + data["string_output"] = features_df["some_date"].astype(pd.StringDtype()) + return data + + + @on_demand_feature_view( + inputs={"odfv": date_request}, + schema=[ + Field(name="output", dtype=UnixTimestamp), + Field(name="string_output", dtype=String), + ], + ) + def test_correct_view(features_df: pd.DataFrame) -> pd.DataFrame: + data = pd.DataFrame() + data["output"] = features_df["some_date"] + data["string_output"] = features_df["some_date"].astype(pd.StringDtype()) + return data + odfv = test_correct_view + assert odfv.name == "test_correct_view" + assert odfv.source_request_sources["date_request"].schema == date_request.schema From f6ab5dae30132c5abfe21f8efdca3091b87b87fe Mon Sep 17 00:00:00 2001 From: Kevin Zhang Date: Thu, 21 Apr 2022 11:13:06 -0700 Subject: [PATCH 15/21] Fix lint Signed-off-by: Kevin Zhang --- .../test_universal_odfv_feature_inference.py | 6 ++---- .../tests/unit/test_on_demand_feature_view.py | 14 +++++++++----- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/sdk/python/tests/integration/registration/test_universal_odfv_feature_inference.py b/sdk/python/tests/integration/registration/test_universal_odfv_feature_inference.py index e18c60fad98..04cb56367bd 100644 --- a/sdk/python/tests/integration/registration/test_universal_odfv_feature_inference.py +++ b/sdk/python/tests/integration/registration/test_universal_odfv_feature_inference.py @@ -31,8 +31,7 @@ def test_infer_odfv_features(environment, universal_data_sources, infer_features ) request_source = create_conv_rate_request_source() driver_odfv = conv_rate_plus_100_feature_view( - [driver_hourly_stats, request_source], - infer_features=infer_features, + [driver_hourly_stats, request_source], infer_features=infer_features, ) feast_objects = [driver_hourly_stats, driver_odfv, driver(), customer()] @@ -85,8 +84,7 @@ def test_infer_odfv_features_with_error(environment, universal_data_sources): ) request_source = create_conv_rate_request_source() driver_odfv = conv_rate_plus_100_feature_view( - [driver_hourly_stats, request_source], - features=features, + [driver_hourly_stats, request_source], features=features, ) feast_objects = [driver_hourly_stats, driver_odfv, driver(), customer()] 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 2b63da29301..33435b8557e 100644 --- a/sdk/python/tests/unit/test_on_demand_feature_view.py +++ b/sdk/python/tests/unit/test_on_demand_feature_view.py @@ -11,16 +11,16 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -from datetime import date + import pandas as pd import pytest + +from feast import RequestSource from feast.feature_view import FeatureView from feast.field import Field from feast.infra.offline_stores.file_source import FileSource -from feast import RequestSource -from feast.types import String, UnixTimestamp from feast.on_demand_feature_view import OnDemandFeatureView, on_demand_feature_view -from feast.types import Float32 +from feast.types import Float32, String, UnixTimestamp def udf1(features_df: pd.DataFrame) -> pd.DataFrame: @@ -104,11 +104,13 @@ def test_hash(): } assert len(s4) == 3 + def test_inputs_parameter_deprecation_in_odfv(): date_request = RequestSource( name="date_request", schema=[Field(name="some_date", dtype=UnixTimestamp)], ) with pytest.warns(DeprecationWarning): + @on_demand_feature_view( inputs={"date_request": date_request}, schema=[ @@ -121,6 +123,7 @@ def test_view(features_df: pd.DataFrame) -> pd.DataFrame: data["output"] = features_df["some_date"] data["string_output"] = features_df["some_date"].astype(pd.StringDtype()) return data + odfv = test_view assert odfv.name == "test_view" assert len(odfv.source_request_sources) == 1 @@ -128,6 +131,7 @@ def test_view(features_df: pd.DataFrame) -> pd.DataFrame: assert odfv.source_request_sources["date_request"].schema == date_request.schema with pytest.raises(ValueError): + @on_demand_feature_view( inputs={"date_request": date_request}, sources=[date_request], @@ -142,7 +146,6 @@ def incorrect_testview(features_df: pd.DataFrame) -> pd.DataFrame: data["string_output"] = features_df["some_date"].astype(pd.StringDtype()) return data - @on_demand_feature_view( inputs={"odfv": date_request}, schema=[ @@ -155,6 +158,7 @@ def test_correct_view(features_df: pd.DataFrame) -> pd.DataFrame: data["output"] = features_df["some_date"] data["string_output"] = features_df["some_date"].astype(pd.StringDtype()) return data + odfv = test_correct_view assert odfv.name == "test_correct_view" assert odfv.source_request_sources["date_request"].schema == date_request.schema From 20470332e0cdf2e08c176b7a846e7f169c990cb9 Mon Sep 17 00:00:00 2001 From: Kevin Zhang Date: Thu, 21 Apr 2022 11:50:52 -0700 Subject: [PATCH 16/21] Fix integration tests Signed-off-by: Kevin Zhang --- sdk/python/feast/feature_view.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sdk/python/feast/feature_view.py b/sdk/python/feast/feature_view.py index 7060870780b..791e2e00ee8 100644 --- a/sdk/python/feast/feature_view.py +++ b/sdk/python/feast/feature_view.py @@ -17,6 +17,7 @@ from typing import Dict, List, Optional, Tuple, Type, Union from google.protobuf.duration_pb2 import Duration +from isort import stream from feast import utils from feast.base_feature_view import BaseFeatureView @@ -439,8 +440,9 @@ def from_proto(cls, feature_view_proto: FeatureViewProto): else feature_view_proto.spec.ttl.ToTimedelta() ), source=batch_source, - stream_source=stream_source, ) + if stream_source: + feature_view.stream_source = stream_source # FeatureViewProjections are not saved in the FeatureView proto. # Create the default projection. From 4a2125f317ff5392e334706745719e7e83549cdb Mon Sep 17 00:00:00 2001 From: Kevin Zhang Date: Thu, 21 Apr 2022 11:52:15 -0700 Subject: [PATCH 17/21] Fix integration tests Signed-off-by: Kevin Zhang --- sdk/python/feast/feature_view.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sdk/python/feast/feature_view.py b/sdk/python/feast/feature_view.py index 791e2e00ee8..65a4914a8f7 100644 --- a/sdk/python/feast/feature_view.py +++ b/sdk/python/feast/feature_view.py @@ -17,7 +17,6 @@ from typing import Dict, List, Optional, Tuple, Type, Union from google.protobuf.duration_pb2 import Duration -from isort import stream from feast import utils from feast.base_feature_view import BaseFeatureView From 4b9dd34c2a889622e19bb52fb01d1795ce0b464a Mon Sep 17 00:00:00 2001 From: Kevin Zhang Date: Fri, 22 Apr 2022 10:39:11 -0700 Subject: [PATCH 18/21] Fix Signed-off-by: Kevin Zhang --- .../validating-historical-features.md | 4 +-- .../java-demo/feature_repo/driver_repo.py | 4 +-- sdk/python/feast/on_demand_feature_view.py | 31 ++++--------------- 3 files changed, 10 insertions(+), 29 deletions(-) diff --git a/docs/tutorials/validating-historical-features.md b/docs/tutorials/validating-historical-features.md index 4757b0e9327..70be38eced2 100644 --- a/docs/tutorials/validating-historical-features.md +++ b/docs/tutorials/validating-historical-features.md @@ -107,7 +107,7 @@ pyarrow.parquet.write_table(entities_2019_table, "entities.parquet") import pyarrow.parquet import pandas as pd -from feast import FeatureView, Entity, FeatureStore, Field +from feast import FeatureView, Entity, FeatureStore, Field, BatchFeatureView from feast.types import Float64, Int64 from feast.value_type import ValueType from feast.data_format import ParquetFormat @@ -134,7 +134,7 @@ taxi_entity = Entity(name='taxi', join_keys=['taxi_id']) ```python -trips_stats_fv = BaseFeatureView( +trips_stats_fv = BatchFeatureView( name='trip_stats', entities=['taxi'], features=[ diff --git a/examples/java-demo/feature_repo/driver_repo.py b/examples/java-demo/feature_repo/driver_repo.py index f13c5b166af..c9bdc7ce770 100644 --- a/examples/java-demo/feature_repo/driver_repo.py +++ b/examples/java-demo/feature_repo/driver_repo.py @@ -7,7 +7,7 @@ from google.protobuf.duration_pb2 import Duration from feast.field import Field -from feast import Entity, Feature, BaseFeatureView, FileSource, ValueType +from feast import Entity, Feature, BatchFeatureView, FileSource, ValueType driver_hourly_stats = FileSource( path="data/driver_stats_with_string.parquet", @@ -15,7 +15,7 @@ created_timestamp_column="created", ) driver = Entity(name="driver_id", value_type=ValueType.INT64, description="driver id",) -driver_hourly_stats_view = BaseFeatureView( +driver_hourly_stats_view = BatchFeatureView( name="driver_hourly_stats", entities=["driver_id"], ttl=Duration(seconds=86400000), diff --git a/sdk/python/feast/on_demand_feature_view.py b/sdk/python/feast/on_demand_feature_view.py index fc54ec032a0..5f8b45ff9bc 100644 --- a/sdk/python/feast/on_demand_feature_view.py +++ b/sdk/python/feast/on_demand_feature_view.py @@ -75,7 +75,7 @@ def __init__( # noqa: C901 name: Optional[str] = None, features: Optional[List[Feature]] = None, sources: Optional[ - List[Union[BatchFeatureView, StreamFeatureView, RequestSource]] + List[Union[BatchFeatureView, StreamFeatureView, RequestSource, FeatureViewProjection]] ] = None, udf: Optional[MethodType] = None, inputs: Optional[ @@ -139,11 +139,7 @@ def __init__( # noqa: C901 for _, source in inputs.items(): if isinstance(source, FeatureView): _sources.append(feature_view_to_batch_feature_view(source)) - elif isinstance(source, FeatureViewProjection): - _sources.append( - BatchFeatureView(name=source.name, schema=source.features,) - ) - elif isinstance(source, RequestSource): + elif isinstance(source, RequestSource) or isinstance(source, FeatureViewProjection): _sources.append(source) else: raise ValueError( @@ -186,11 +182,7 @@ def __init__( # noqa: C901 for _, source in _inputs.items(): if isinstance(source, FeatureView): _sources.append(feature_view_to_batch_feature_view(source)) - elif isinstance(source, FeatureViewProjection): - _sources.append( - BatchFeatureView(name=source.name, schema=source.features,) - ) - elif isinstance(source, RequestSource): + elif isinstance(source, RequestSource) or isinstance(source, FeatureViewProjection): _sources.append(source) else: raise ValueError( @@ -501,7 +493,7 @@ def on_demand_feature_view( *args, features: Optional[List[Feature]] = None, sources: Optional[ - List[Union[BatchFeatureView, StreamFeatureView, RequestSource]] + List[Union[BatchFeatureView, StreamFeatureView, RequestSource, FeatureViewProjection]] ] = None, inputs: Optional[Dict[str, Union[FeatureView, RequestSource]]] = None, schema: Optional[List[Field]] = None, @@ -557,11 +549,7 @@ def on_demand_feature_view( for _, source in inputs.items(): if isinstance(source, FeatureView): _sources.append(feature_view_to_batch_feature_view(source)) - elif isinstance(source, FeatureViewProjection): - _sources.append( - BatchFeatureView(name=source.name, schema=source.features,) - ) - elif isinstance(source, RequestSource): + elif isinstance(source, RequestSource) or isinstance(source, FeatureViewProjection): _sources.append(source) else: raise ValueError( @@ -601,14 +589,7 @@ def on_demand_feature_view( for _, source in _inputs.items(): if isinstance(source, FeatureView): _sources.append(feature_view_to_batch_feature_view(source)) - elif isinstance(source, FeatureViewProjection): - _sources.append( - BatchFeatureView( - name=source.name, # type: ignore - schema=source.features, # type: ignore - ) - ) - elif isinstance(source, RequestSource): + elif isinstance(source, RequestSource) or isinstance(source, FeatureViewProjection): _sources.append(source) else: raise ValueError( From 99e0cf54fabfb6e54ec9a80df24da02fa4e2747b Mon Sep 17 00:00:00 2001 From: Kevin Zhang Date: Fri, 22 Apr 2022 10:40:07 -0700 Subject: [PATCH 19/21] lilnt Signed-off-by: Kevin Zhang --- sdk/python/feast/on_demand_feature_view.py | 34 ++++++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/sdk/python/feast/on_demand_feature_view.py b/sdk/python/feast/on_demand_feature_view.py index 5f8b45ff9bc..f976cbd4b77 100644 --- a/sdk/python/feast/on_demand_feature_view.py +++ b/sdk/python/feast/on_demand_feature_view.py @@ -75,7 +75,14 @@ def __init__( # noqa: C901 name: Optional[str] = None, features: Optional[List[Feature]] = None, sources: Optional[ - List[Union[BatchFeatureView, StreamFeatureView, RequestSource, FeatureViewProjection]] + List[ + Union[ + BatchFeatureView, + StreamFeatureView, + RequestSource, + FeatureViewProjection, + ] + ] ] = None, udf: Optional[MethodType] = None, inputs: Optional[ @@ -139,7 +146,9 @@ def __init__( # noqa: C901 for _, source in inputs.items(): if isinstance(source, FeatureView): _sources.append(feature_view_to_batch_feature_view(source)) - elif isinstance(source, RequestSource) or isinstance(source, FeatureViewProjection): + elif isinstance(source, RequestSource) or isinstance( + source, FeatureViewProjection + ): _sources.append(source) else: raise ValueError( @@ -182,7 +191,9 @@ def __init__( # noqa: C901 for _, source in _inputs.items(): if isinstance(source, FeatureView): _sources.append(feature_view_to_batch_feature_view(source)) - elif isinstance(source, RequestSource) or isinstance(source, FeatureViewProjection): + elif isinstance(source, RequestSource) or isinstance( + source, FeatureViewProjection + ): _sources.append(source) else: raise ValueError( @@ -493,7 +504,14 @@ def on_demand_feature_view( *args, features: Optional[List[Feature]] = None, sources: Optional[ - List[Union[BatchFeatureView, StreamFeatureView, RequestSource, FeatureViewProjection]] + List[ + Union[ + BatchFeatureView, + StreamFeatureView, + RequestSource, + FeatureViewProjection, + ] + ] ] = None, inputs: Optional[Dict[str, Union[FeatureView, RequestSource]]] = None, schema: Optional[List[Field]] = None, @@ -549,7 +567,9 @@ def on_demand_feature_view( for _, source in inputs.items(): if isinstance(source, FeatureView): _sources.append(feature_view_to_batch_feature_view(source)) - elif isinstance(source, RequestSource) or isinstance(source, FeatureViewProjection): + elif isinstance(source, RequestSource) or isinstance( + source, FeatureViewProjection + ): _sources.append(source) else: raise ValueError( @@ -589,7 +609,9 @@ def on_demand_feature_view( for _, source in _inputs.items(): if isinstance(source, FeatureView): _sources.append(feature_view_to_batch_feature_view(source)) - elif isinstance(source, RequestSource) or isinstance(source, FeatureViewProjection): + elif isinstance(source, RequestSource) or isinstance( + source, FeatureViewProjection + ): _sources.append(source) else: raise ValueError( From e232ef29ff37e9de94e7fff57f8fbb4525be6818 Mon Sep 17 00:00:00 2001 From: Kevin Zhang Date: Fri, 22 Apr 2022 11:05:10 -0700 Subject: [PATCH 20/21] Fix lint? Signed-off-by: Kevin Zhang --- sdk/python/feast/on_demand_feature_view.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/python/feast/on_demand_feature_view.py b/sdk/python/feast/on_demand_feature_view.py index f976cbd4b77..8104ce8e2ca 100644 --- a/sdk/python/feast/on_demand_feature_view.py +++ b/sdk/python/feast/on_demand_feature_view.py @@ -68,7 +68,7 @@ class OnDemandFeatureView(BaseFeatureView): tags: Dict[str, str] owner: str - @log_exceptions + @log_exceptions # noqa: C901 def __init__( # noqa: C901 self, *args, From 05617db028558e6533f71b549bda5a391dc31224 Mon Sep 17 00:00:00 2001 From: Kevin Zhang Date: Fri, 22 Apr 2022 12:24:31 -0700 Subject: [PATCH 21/21] Fix Signed-off-by: Kevin Zhang --- sdk/python/feast/on_demand_feature_view.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/python/feast/on_demand_feature_view.py b/sdk/python/feast/on_demand_feature_view.py index 8104ce8e2ca..d2cec18e520 100644 --- a/sdk/python/feast/on_demand_feature_view.py +++ b/sdk/python/feast/on_demand_feature_view.py @@ -68,7 +68,7 @@ class OnDemandFeatureView(BaseFeatureView): tags: Dict[str, str] owner: str - @log_exceptions # noqa: C901 + @log_exceptions # noqa: C901 def __init__( # noqa: C901 self, *args,