Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix issue with feature views being detected as duplicated when imported #1905

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion sdk/python/feast/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# 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.

import inspect
from datetime import datetime
from typing import Dict, Optional

Expand Down Expand Up @@ -49,6 +49,8 @@ class Entity:
_created_timestamp: Optional[datetime]
_last_updated_timestamp: Optional[datetime]

defined_in: str

@log_exceptions
def __init__(
self,
Expand All @@ -75,6 +77,11 @@ def __init__(
self._created_timestamp: Optional[datetime] = None
self._last_updated_timestamp: Optional[datetime] = None

stack = inspect.stack()
# Get two levels up from current, to ignore usage.py
previous_stack_frame = stack[2]
self.defined_in = previous_stack_frame.filename
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we abstract that in an helper function and also test it in a test file ?
Don't think we need to test each class but at least the helper function

What do you think ?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A helper class probably makes sense since there's some state tracking being done. wdyt @achals ?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A helper class

yeah even better 👍

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


def __eq__(self, other):
if not isinstance(other, Entity):
raise TypeError("Comparisons should only involve Entity class objects.")
Expand Down
10 changes: 10 additions & 0 deletions sdk/python/feast/feature_service.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import inspect
from datetime import datetime
from typing import Dict, List, Optional, Union

Expand All @@ -14,6 +15,7 @@
FeatureServiceMeta,
FeatureServiceSpec,
)
from feast.usage import log_exceptions


class FeatureService:
Expand All @@ -36,6 +38,9 @@ class FeatureService:
created_timestamp: Optional[datetime] = None
last_updated_timestamp: Optional[datetime] = None

defined_in: str

@log_exceptions
def __init__(
self,
name: str,
Expand Down Expand Up @@ -69,6 +74,11 @@ def __init__(
self.created_timestamp = None
self.last_updated_timestamp = None

stack = inspect.stack()
# Get two levels up from current, to ignore usage.py
previous_stack_frame = stack[2]
self.defined_in = previous_stack_frame.filename

def __repr__(self):
items = (f"{k} = {v}" for k, v in self.__dict__.items())
return f"<{self.__class__.__name__}({', '.join(items)})>"
Expand Down
1 change: 1 addition & 0 deletions sdk/python/feast/feature_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@ def _get_features(
self.get_feature_service(_features.name)
)
else:
assert isinstance(_features, list)
_feature_refs = _features
return _feature_refs

Expand Down
9 changes: 8 additions & 1 deletion sdk/python/feast/feature_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# 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.

import inspect
from typing import Dict, List, MutableMapping, Optional, Union

import yaml
Expand All @@ -30,6 +30,7 @@
from feast.protos.feast.core.FeatureTable_pb2 import (
FeatureTableSpec as FeatureTableSpecProto,
)
from feast.usage import log_exceptions
from feast.value_type import ValueType


Expand All @@ -38,6 +39,7 @@ class FeatureTable:
Represents a collection of features and associated metadata.
"""

@log_exceptions
def __init__(
self,
name: str,
Expand All @@ -64,6 +66,11 @@ def __init__(
self._created_timestamp: Optional[Timestamp] = None
self._last_updated_timestamp: Optional[Timestamp] = None

stack = inspect.stack()
# Get two levels up from current, to ignore usage.py
previous_stack_frame = stack[2]
self.defined_in = previous_stack_frame.filename
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds like missing one get_calling_file_name() here

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh yeah missed this


def __str__(self):
return str(MessageToJson(self.to_proto()))

Expand Down
8 changes: 8 additions & 0 deletions sdk/python/feast/feature_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
# 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.
import inspect
import re
import warnings
from datetime import datetime, timedelta
Expand Down Expand Up @@ -79,6 +80,8 @@ class FeatureView:
last_updated_timestamp: Optional[datetime] = None
materialization_intervals: List[Tuple[datetime, datetime]]

defined_in: str

@log_exceptions
def __init__(
self,
Expand Down Expand Up @@ -141,6 +144,11 @@ def __init__(
self.created_timestamp: Optional[datetime] = None
self.last_updated_timestamp: Optional[datetime] = None

stack = inspect.stack()
# Get two levels up from current, to ignore usage.py
previous_stack_frame = stack[2]
self.defined_in = previous_stack_frame.filename

def __repr__(self):
items = (f"{k} = {v}" for k, v in self.__dict__.items())
return f"<{self.__class__.__name__}({', '.join(items)})>"
Expand Down
1 change: 1 addition & 0 deletions sdk/python/feast/infra/online_stores/redis.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ def online_read(

for entity_key in entity_keys:
redis_key_bin = _redis_key(project, entity_key)
print(redis_key_bin)
achals marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be removed ?

hset_keys = [_mmh3(f"{feature_view}:{k}") for k in requested_features]
ts_key = f"_ts:{feature_view}"
hset_keys.append(ts_key)
Expand Down
8 changes: 8 additions & 0 deletions sdk/python/feast/on_demand_feature_view.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import functools
import inspect
from types import MethodType
from typing import Dict, List, Union, cast

Expand Down Expand Up @@ -45,6 +46,8 @@ class OnDemandFeatureView:
inputs: Dict[str, Union[FeatureView, RequestDataSource]]
udf: MethodType

defined_in: str

@log_exceptions
def __init__(
self,
Expand All @@ -62,6 +65,11 @@ def __init__(
self.inputs = inputs
self.udf = udf

stack = inspect.stack()
# Get two levels up from current, to ignore usage.py
previous_stack_frame = stack[2]
self.defined_in = previous_stack_frame.filename

def to_proto(self) -> OnDemandFeatureViewProto:
"""
Converts an on demand feature view object to its protobuf representation.
Expand Down
15 changes: 10 additions & 5 deletions sdk/python/feast/repo_operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,15 +107,20 @@ def parse_repo(repo_root: Path) -> ParsedRepo:
for attr_name in dir(module):
obj = getattr(module, attr_name)
if isinstance(obj, FeatureTable):
res.feature_tables.append(obj)
if obj.defined_in is not None and obj.defined_in == module.__file__:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can module.file be None ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should maybe also raise exceptions in case defined_in is None

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. And no, module.file cannot be None

res.feature_tables.append(obj)
if isinstance(obj, FeatureView):
res.feature_views.append(obj)
if obj.defined_in is not None and obj.defined_in == module.__file__:
res.feature_views.append(obj)
elif isinstance(obj, Entity):
res.entities.append(obj)
if obj.defined_in is not None and obj.defined_in == module.__file__:
res.entities.append(obj)
elif isinstance(obj, FeatureService):
res.feature_services.append(obj)
if obj.defined_in is not None and obj.defined_in == module.__file__:
res.feature_services.append(obj)
elif isinstance(obj, OnDemandFeatureView):
res.on_demand_feature_views.append(obj)
if obj.defined_in is not None and obj.defined_in == module.__file__:
res.on_demand_feature_views.append(obj)
return res


Expand Down