Skip to content

Commit 7c19cf7

Browse files
authored
fix: Update feature view APIs to prefer keyword args (#2472)
* fix: Update feature view APIs to prefer keyword args Signed-off-by: Achal Shah <[email protected]> * cr Signed-off-by: Achal Shah <[email protected]> * fix tests Signed-off-by: Achal Shah <[email protected]>
1 parent 83a11c6 commit 7c19cf7

File tree

3 files changed

+76
-25
lines changed

3 files changed

+76
-25
lines changed

sdk/python/feast/base_feature_view.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,9 @@ class BaseFeatureView(ABC):
5252
@abstractmethod
5353
def __init__(
5454
self,
55-
name: str,
56-
features: List[Feature],
55+
*,
56+
name: Optional[str] = None,
57+
features: Optional[List[Feature]] = None,
5758
description: str = "",
5859
tags: Optional[Dict[str, str]] = None,
5960
owner: str = "",
@@ -72,8 +73,12 @@ def __init__(
7273
Raises:
7374
ValueError: A field mapping conflicts with an Entity or a Feature.
7475
"""
76+
if not name:
77+
raise ValueError("Name needs to be provided")
7578
self.name = name
76-
self.features = features
79+
80+
self.features = features or []
81+
7782
self.description = description
7883
self.tags = tags or {}
7984
self.owner = owner

sdk/python/feast/feature_view.py

Lines changed: 61 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ class FeatureView(BaseFeatureView):
7474

7575
name: str
7676
entities: List[str]
77-
ttl: timedelta
77+
ttl: Optional[timedelta]
7878
batch_source: DataSource
7979
stream_source: Optional[DataSource]
8080
features: List[Feature]
@@ -87,9 +87,10 @@ class FeatureView(BaseFeatureView):
8787
@log_exceptions
8888
def __init__(
8989
self,
90-
name: str,
91-
entities: List[str],
92-
ttl: Union[Duration, timedelta],
90+
*args,
91+
name: Optional[str] = None,
92+
entities: Optional[List[str]] = None,
93+
ttl: Optional[Union[Duration, timedelta]] = None,
9394
batch_source: Optional[DataSource] = None,
9495
stream_source: Optional[DataSource] = None,
9596
features: Optional[List[Feature]] = None,
@@ -121,6 +122,54 @@ def __init__(
121122
Raises:
122123
ValueError: A field mapping conflicts with an Entity or a Feature.
123124
"""
125+
126+
positional_attributes = ["name, entities, ttl"]
127+
128+
_name = name
129+
_entities = entities
130+
_ttl = ttl
131+
132+
if args:
133+
warnings.warn(
134+
(
135+
"feature view parameters should be specified as a keyword argument instead of a positional arg."
136+
"Feast 0.23+ will not support positional arguments to construct feature views"
137+
),
138+
DeprecationWarning,
139+
)
140+
if len(args) > len(positional_attributes):
141+
raise ValueError(
142+
f"Only {', '.join(positional_attributes)} are allowed as positional args when defining "
143+
f"feature views, for backwards compatibility."
144+
)
145+
if len(args) >= 1:
146+
_name = args[0]
147+
if len(args) >= 2:
148+
_entities = args[1]
149+
if len(args) >= 3:
150+
_ttl = args[2]
151+
152+
if not _name:
153+
raise ValueError("feature view name needs to be specified")
154+
155+
self.name = _name
156+
self.entities = _entities if _entities else [DUMMY_ENTITY_NAME]
157+
158+
if isinstance(_ttl, Duration):
159+
self.ttl = timedelta(seconds=int(_ttl.seconds))
160+
warnings.warn(
161+
(
162+
"The option to pass a Duration object to the ttl parameter is being deprecated. "
163+
"Please pass a timedelta object instead. Feast 0.21 and onwards will not support "
164+
"Duration objects."
165+
),
166+
DeprecationWarning,
167+
)
168+
elif isinstance(_ttl, timedelta) or _ttl is None:
169+
self.ttl = _ttl
170+
else:
171+
raise ValueError(f"unknown value type specified for ttl {type(_ttl)}")
172+
124173
_features = features or []
125174

126175
if stream_source is not None and isinstance(stream_source, PushSource):
@@ -138,7 +187,7 @@ def __init__(
138187
)
139188
self.batch_source = batch_source
140189

141-
cols = [entity for entity in entities] + [feat.name for feat in _features]
190+
cols = [entity for entity in self.entities] + [feat.name for feat in _features]
142191
for col in cols:
143192
if (
144193
self.batch_source.field_mapping is not None
@@ -150,22 +199,13 @@ def __init__(
150199
f"Entity or Feature name."
151200
)
152201

153-
super().__init__(name, _features, description, tags, owner)
154-
self.entities = entities if entities else [DUMMY_ENTITY_NAME]
155-
156-
if isinstance(ttl, Duration):
157-
self.ttl = timedelta(seconds=int(ttl.seconds))
158-
warnings.warn(
159-
(
160-
"The option to pass a Duration object to the ttl parameter is being deprecated. "
161-
"Please pass a timedelta object instead. Feast 0.21 and onwards will not support "
162-
"Duration objects."
163-
),
164-
DeprecationWarning,
165-
)
166-
else:
167-
self.ttl = ttl
168-
202+
super().__init__(
203+
name=name,
204+
features=_features,
205+
description=description,
206+
tags=tags,
207+
owner=owner,
208+
)
169209
self.online = online
170210
self.stream_source = stream_source
171211
self.materialization_intervals = []

sdk/python/feast/on_demand_feature_view.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,13 @@ def __init__(
101101
owner (optional): The owner of the on demand feature view, typically the email
102102
of the primary maintainer.
103103
"""
104-
super().__init__(name, features, description, tags, owner)
104+
super().__init__(
105+
name=name,
106+
features=features,
107+
description=description,
108+
tags=tags,
109+
owner=owner,
110+
)
105111
if inputs and sources:
106112
raise ValueError("At most one of `sources` or `inputs` can be specified.")
107113
elif inputs:

0 commit comments

Comments
 (0)