From 4f281b2ade61aa663603fa35882fefba8c9acb33 Mon Sep 17 00:00:00 2001 From: Jacob Klegar Date: Tue, 2 Mar 2021 16:50:48 -0500 Subject: [PATCH 1/5] FeatureView and FeatureStore classes Signed-off-by: Jacob Klegar --- sdk/python/feast/big_query_source.py | 32 ++++++++++++++++++ sdk/python/feast/feature_store.py | 31 +++++++++++++++++ sdk/python/feast/feature_store_config.py | 39 ++++++++++++++++++++++ sdk/python/feast/feature_view.py | 42 ++++++++++++++++++++++++ 4 files changed, 144 insertions(+) create mode 100644 sdk/python/feast/big_query_source.py create mode 100644 sdk/python/feast/feature_store.py create mode 100644 sdk/python/feast/feature_store_config.py create mode 100644 sdk/python/feast/feature_view.py diff --git a/sdk/python/feast/big_query_source.py b/sdk/python/feast/big_query_source.py new file mode 100644 index 0000000000..7ed58b4959 --- /dev/null +++ b/sdk/python/feast/big_query_source.py @@ -0,0 +1,32 @@ +# Copyright 2019 The Feast Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# 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 typing import Dict, Optional + + +class BigQuerySource: + def __init__( + self, + table_ref: Optional[str], + event_timestamp_column: str, + created_timestamp_column: Optional[str], + field_mapping: Optional[Dict[str, str]], + query: Optional[str], + ): + assert (table_ref is None) != (query is None), "Exactly one of table_ref and query should be specified" + self.table_ref = table_ref + self.event_timestamp_column = event_timestamp_column + self.created_timestamp_column = created_timestamp_column + self.field_mapping = field_mapping + self.query = query + return diff --git a/sdk/python/feast/feature_store.py b/sdk/python/feast/feature_store.py new file mode 100644 index 0000000000..863c62a5e4 --- /dev/null +++ b/sdk/python/feast/feature_store.py @@ -0,0 +1,31 @@ +# Copyright 2019 The Feast Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# 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 typing import Optional +from feast.feature_store_config import Config + + +class FeatureStore: + def __init__( + self, + config_path: Optional[str], + config: Optional[Config], + ): + assert (config_path is None or config is None), "You cannot specify both config_path and config" + if config is not None: + self.config = config + elif config_path is not None: + self.config = Config.from_config_path(config_path) + else: + self.config = Config() + return diff --git a/sdk/python/feast/feature_store_config.py b/sdk/python/feast/feature_store_config.py new file mode 100644 index 0000000000..8a6f742a96 --- /dev/null +++ b/sdk/python/feast/feature_store_config.py @@ -0,0 +1,39 @@ +# Copyright 2019 The Feast Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# 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 os.path import expanduser, join +from typing import Optional +import yaml + + +class Config: + def __init__( + self, + provider: Optional[str], + online_store: Optional[str], + metadata_store: Optional[str], + ): + self.provider = provider if (provider is not None) else "local" + self.online_store = online_store if (online_store is not None) else "local" + self.metadata_store = metadata_store if (metadata_store is not None) else join(expanduser("~"), ".feast", "metadata_store") + return + + @classmethod + def from_config_path(cls, config_path: str): + with open(config_path, "r") as f: + config_dict = yaml.safe_load(f) + return cls( + provider=config_dict.get("provider"), + online_store=config_dict.get("online_store"), + metadata_store=config_dict.get("metadata_store"), + ) diff --git a/sdk/python/feast/feature_view.py b/sdk/python/feast/feature_view.py new file mode 100644 index 0000000000..d88ee6b49b --- /dev/null +++ b/sdk/python/feast/feature_view.py @@ -0,0 +1,42 @@ +# Copyright 2019 The Feast Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# 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 datetime +from typing import Dict, List + +from feast.big_query_source import BigQuerySource +from feast.entity import Entity +from feast.feature import Feature + + +class FeatureView: + def __init__( + self, + name: str, + entities: List[Entity], + features: List[Feature], + tags: Dict[str, str], + ttl: str, + online: bool, + inputs: BigQuerySource, + feature_start_time: datetime, + ): + self.name = name + self.entities = entities + self.features = features + self.tags = tags + self.ttl = ttl + self.online = online + self.inputs = inputs + self.feature_start_time = feature_start_time + return From 28c8508ee6b5439873107aea1e461a58d82748ac Mon Sep 17 00:00:00 2001 From: Jacob Klegar Date: Tue, 2 Mar 2021 20:15:16 -0500 Subject: [PATCH 2/5] Address comments and fix lint Signed-off-by: Jacob Klegar --- sdk/python/feast/big_query_source.py | 7 ++++++- sdk/python/feast/feature_store.py | 13 ++++++++----- sdk/python/feast/feature_store_config.py | 10 ++++++++-- sdk/python/feast/feature_view.py | 4 ++++ 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/sdk/python/feast/big_query_source.py b/sdk/python/feast/big_query_source.py index 7ed58b4959..5fe3df1bd2 100644 --- a/sdk/python/feast/big_query_source.py +++ b/sdk/python/feast/big_query_source.py @@ -15,6 +15,10 @@ class BigQuerySource: + """ + Represents a BigQuery table reference or BigQuery query that returns a set of features. + """ + def __init__( self, table_ref: Optional[str], @@ -23,7 +27,8 @@ def __init__( field_mapping: Optional[Dict[str, str]], query: Optional[str], ): - assert (table_ref is None) != (query is None), "Exactly one of table_ref and query should be specified" + if (table_ref is None) != (query is None): + raise Exception("Exactly one of table_ref and query should be specified") self.table_ref = table_ref self.event_timestamp_column = event_timestamp_column self.created_timestamp_column = created_timestamp_column diff --git a/sdk/python/feast/feature_store.py b/sdk/python/feast/feature_store.py index 863c62a5e4..175813a37c 100644 --- a/sdk/python/feast/feature_store.py +++ b/sdk/python/feast/feature_store.py @@ -12,20 +12,23 @@ # See the License for the specific language governing permissions and # limitations under the License. from typing import Optional + from feast.feature_store_config import Config class FeatureStore: + """ + Fat client to interact with the Feast feature store. + """ + def __init__( - self, - config_path: Optional[str], - config: Optional[Config], + self, config_path: Optional[str], config: Optional[Config], ): - assert (config_path is None or config is None), "You cannot specify both config_path and config" + if config_path is None or config is None: + raise Exception("You cannot specify both config_path and config") if config is not None: self.config = config elif config_path is not None: self.config = Config.from_config_path(config_path) else: self.config = Config() - return diff --git a/sdk/python/feast/feature_store_config.py b/sdk/python/feast/feature_store_config.py index 8a6f742a96..696a2ba3be 100644 --- a/sdk/python/feast/feature_store_config.py +++ b/sdk/python/feast/feature_store_config.py @@ -13,10 +13,15 @@ # limitations under the License. from os.path import expanduser, join from typing import Optional + import yaml class Config: + """ + Python representation of the FeatureStore config that the user can specify via a yaml file. + """ + def __init__( self, provider: Optional[str], @@ -25,8 +30,9 @@ def __init__( ): self.provider = provider if (provider is not None) else "local" self.online_store = online_store if (online_store is not None) else "local" - self.metadata_store = metadata_store if (metadata_store is not None) else join(expanduser("~"), ".feast", "metadata_store") - return + self.metadata_store = ( + metadata_store if (metadata_store is not None) else "./metadata_store" + ) @classmethod def from_config_path(cls, config_path: str): diff --git a/sdk/python/feast/feature_view.py b/sdk/python/feast/feature_view.py index d88ee6b49b..a66bdf8eaf 100644 --- a/sdk/python/feast/feature_view.py +++ b/sdk/python/feast/feature_view.py @@ -20,6 +20,10 @@ class FeatureView: + """ + Represents a collection of features that will be served online. + """ + def __init__( self, name: str, From 3186e622b7ce205dc307ec31a9c05d21381df973 Mon Sep 17 00:00:00 2001 From: Jacob Klegar Date: Tue, 2 Mar 2021 20:37:20 -0500 Subject: [PATCH 3/5] remove unnecessary import Signed-off-by: Jacob Klegar --- sdk/python/feast/feature_store_config.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sdk/python/feast/feature_store_config.py b/sdk/python/feast/feature_store_config.py index 696a2ba3be..5a0d8f8ffb 100644 --- a/sdk/python/feast/feature_store_config.py +++ b/sdk/python/feast/feature_store_config.py @@ -11,7 +11,6 @@ # 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 os.path import expanduser, join from typing import Optional import yaml From a0cf99a2e64d81316d1ec64af7b14f2364d96d1c Mon Sep 17 00:00:00 2001 From: Jacob Klegar Date: Fri, 5 Mar 2021 10:19:19 -0500 Subject: [PATCH 4/5] Update docstrings per comments Signed-off-by: Jacob Klegar --- sdk/python/feast/feature_store.py | 2 +- sdk/python/feast/feature_store_config.py | 2 +- sdk/python/feast/feature_view.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sdk/python/feast/feature_store.py b/sdk/python/feast/feature_store.py index 175813a37c..b6a1371bff 100644 --- a/sdk/python/feast/feature_store.py +++ b/sdk/python/feast/feature_store.py @@ -18,7 +18,7 @@ class FeatureStore: """ - Fat client to interact with the Feast feature store. + A FeatureStore object is used to define, create, and retrieve features. """ def __init__( diff --git a/sdk/python/feast/feature_store_config.py b/sdk/python/feast/feature_store_config.py index 5a0d8f8ffb..aa378c018d 100644 --- a/sdk/python/feast/feature_store_config.py +++ b/sdk/python/feast/feature_store_config.py @@ -18,7 +18,7 @@ class Config: """ - Python representation of the FeatureStore config that the user can specify via a yaml file. + Configuration object that contains all possible configuration options for a FeatureStore. """ def __init__( diff --git a/sdk/python/feast/feature_view.py b/sdk/python/feast/feature_view.py index a66bdf8eaf..1047678dca 100644 --- a/sdk/python/feast/feature_view.py +++ b/sdk/python/feast/feature_view.py @@ -21,7 +21,7 @@ class FeatureView: """ - Represents a collection of features that will be served online. + A FeatureView defines a logical grouping of servable features. """ def __init__( From 1709a4ff171d400045e488227e75202196a3bf21 Mon Sep 17 00:00:00 2001 From: Jacob Klegar Date: Fri, 5 Mar 2021 18:49:43 -0500 Subject: [PATCH 5/5] Add docstring and address comment Signed-off-by: Jacob Klegar --- sdk/python/feast/feature_store.py | 2 +- sdk/python/feast/feature_store_config.py | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/sdk/python/feast/feature_store.py b/sdk/python/feast/feature_store.py index b6a1371bff..aea899e1ab 100644 --- a/sdk/python/feast/feature_store.py +++ b/sdk/python/feast/feature_store.py @@ -29,6 +29,6 @@ def __init__( if config is not None: self.config = config elif config_path is not None: - self.config = Config.from_config_path(config_path) + self.config = Config.from_path(config_path) else: self.config = Config() diff --git a/sdk/python/feast/feature_store_config.py b/sdk/python/feast/feature_store_config.py index aa378c018d..b26d1e0bd5 100644 --- a/sdk/python/feast/feature_store_config.py +++ b/sdk/python/feast/feature_store_config.py @@ -34,7 +34,16 @@ def __init__( ) @classmethod - def from_config_path(cls, config_path: str): + def from_path(cls, config_path: str): + """ + Construct the configuration object from a filepath containing a yaml file. + + Example yaml file: + + provider: gcp + online_store: firestore + metadata_store: gs://my_bucket/metadata_store + """ with open(config_path, "r") as f: config_dict = yaml.safe_load(f) return cls(