Skip to content

Commit 0d775c2

Browse files
authored
Add Registry operations for on demand feature views (feast-dev#1828)
Signed-off-by: Achal Shah <achals@gmail.com>
1 parent fc448dc commit 0d775c2

File tree

4 files changed

+136
-22
lines changed

4 files changed

+136
-22
lines changed

sdk/python/feast/cli.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,58 @@ def feature_view_list(ctx: click.Context):
236236
print(tabulate(table, headers=["NAME", "ENTITIES"], tablefmt="plain"))
237237

238238

239+
@cli.group(name="on-demand-feature-views")
240+
def on_demand_feature_views_cmd():
241+
"""
242+
Access feature views
243+
"""
244+
pass
245+
246+
247+
@on_demand_feature_views_cmd.command("describe")
248+
@click.argument("name", type=click.STRING)
249+
@click.pass_context
250+
def on_demand_feature_view_describe(ctx: click.Context, name: str):
251+
"""
252+
Describe an on demand feature view
253+
"""
254+
repo = ctx.obj["CHDIR"]
255+
cli_check_repo(repo)
256+
store = FeatureStore(repo_path=str(repo))
257+
258+
try:
259+
on_demand_feature_view = store.get_on_demand_feature_view(name)
260+
except FeastObjectNotFoundException as e:
261+
print(e)
262+
exit(1)
263+
264+
print(
265+
yaml.dump(
266+
yaml.safe_load(str(on_demand_feature_view)),
267+
default_flow_style=False,
268+
sort_keys=False,
269+
)
270+
)
271+
272+
273+
@on_demand_feature_views_cmd.command(name="list")
274+
@click.pass_context
275+
def on_demand_feature_view_list(ctx: click.Context):
276+
"""
277+
List all on demand feature views
278+
"""
279+
repo = ctx.obj["CHDIR"]
280+
cli_check_repo(repo)
281+
store = FeatureStore(repo_path=str(repo))
282+
table = []
283+
for on_demand_feature_view in store.list_on_demand_feature_views():
284+
table.append([on_demand_feature_view.name])
285+
286+
from tabulate import tabulate
287+
288+
print(tabulate(table, headers=["NAME"], tablefmt="plain"))
289+
290+
239291
@cli.command("apply", cls=NoOptionDefaultFormat)
240292
@click.option(
241293
"--skip-source-validation",

sdk/python/feast/errors.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,16 @@ def __init__(self, name, project=None):
4040
super().__init__(f"Feature view {name} does not exist")
4141

4242

43+
class OnDemandFeatureViewNotFoundException(FeastObjectNotFoundException):
44+
def __init__(self, name, project=None):
45+
if project:
46+
super().__init__(
47+
f"On demand feature view {name} does not exist in project {project}"
48+
)
49+
else:
50+
super().__init__(f"On demand feature view {name} does not exist")
51+
52+
4353
class FeatureTableNotFoundException(FeastObjectNotFoundException):
4454
def __init__(self, name, project=None):
4555
if project:

sdk/python/feast/feature_store.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,16 @@ def list_feature_views(self) -> List[FeatureView]:
170170
"""
171171
return self._registry.list_feature_views(self.project)
172172

173+
@log_exceptions_and_usage
174+
def list_on_demand_feature_views(self) -> List[OnDemandFeatureView]:
175+
"""
176+
Retrieves the list of on demand feature views from the registry.
177+
178+
Returns:
179+
A list of on demand feature views.
180+
"""
181+
return self._registry.list_on_demand_feature_views(self.project)
182+
173183
@log_exceptions_and_usage
174184
def get_entity(self, name: str) -> Entity:
175185
"""
@@ -218,6 +228,22 @@ def get_feature_view(self, name: str) -> FeatureView:
218228
"""
219229
return self._registry.get_feature_view(name, self.project)
220230

231+
@log_exceptions_and_usage
232+
def get_on_demand_feature_view(self, name: str) -> OnDemandFeatureView:
233+
"""
234+
Retrieves a feature view.
235+
236+
Args:
237+
name: Name of feature view.
238+
239+
Returns:
240+
The specified feature view.
241+
242+
Raises:
243+
FeatureViewNotFoundException: The feature view could not be found.
244+
"""
245+
return self._registry.get_on_demand_feature_view(name, self.project)
246+
221247
@log_exceptions_and_usage
222248
def delete_feature_view(self, name: str):
223249
"""

sdk/python/feast/registry.py

Lines changed: 48 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
FeatureServiceNotFoundException,
2929
FeatureTableNotFoundException,
3030
FeatureViewNotFoundException,
31+
OnDemandFeatureViewNotFoundException,
3132
S3RegistryBucketForbiddenAccess,
3233
S3RegistryBucketNotExist,
3334
)
@@ -339,6 +340,53 @@ def apply_on_demand_feature_view(
339340
if commit:
340341
self.commit()
341342

343+
def list_on_demand_feature_views(
344+
self, project: str, allow_cache: bool = False
345+
) -> List[OnDemandFeatureView]:
346+
"""
347+
Retrieve a list of on demand feature views from the registry
348+
349+
Args:
350+
allow_cache: Whether to allow returning on demand feature views from a cached registry
351+
project: Filter on demand feature views based on project name
352+
353+
Returns:
354+
List of on demand feature views
355+
"""
356+
357+
registry = self._get_registry_proto(allow_cache=allow_cache)
358+
on_demand_feature_views = []
359+
for on_demand_feature_view in registry.on_demand_feature_views:
360+
if on_demand_feature_view.spec.project == project:
361+
on_demand_feature_views.append(
362+
OnDemandFeatureView.from_proto(on_demand_feature_view)
363+
)
364+
return on_demand_feature_views
365+
366+
def get_on_demand_feature_view(
367+
self, name: str, project: str, allow_cache: bool = False
368+
) -> OnDemandFeatureView:
369+
"""
370+
Retrieves an on demand feature view.
371+
372+
Args:
373+
name: Name of on demand feature view
374+
project: Feast project that this on demand feature belongs to
375+
376+
Returns:
377+
Returns either the specified on demand feature view, or raises an exception if
378+
none is found
379+
"""
380+
registry = self._get_registry_proto(allow_cache=allow_cache)
381+
382+
for on_demand_feature_view in registry.on_demand_feature_views:
383+
if (
384+
on_demand_feature_view.spec.project == project
385+
and on_demand_feature_view.spec.name == name
386+
):
387+
return OnDemandFeatureView.from_proto(on_demand_feature_view)
388+
raise OnDemandFeatureViewNotFoundException(name, project=project)
389+
342390
def apply_materialization(
343391
self,
344392
feature_view: FeatureView,
@@ -420,28 +468,6 @@ def list_feature_views(
420468
feature_views.append(FeatureView.from_proto(feature_view_proto))
421469
return feature_views
422470

423-
def list_on_demand_feature_views(
424-
self, project: str, allow_cache: bool = False
425-
) -> List[OnDemandFeatureView]:
426-
"""
427-
Retrieve a list of on demand feature views from the registry
428-
429-
Args:
430-
allow_cache: Allow returning feature views from the cached registry
431-
project: Filter feature tables based on project name
432-
433-
Returns:
434-
List of on demand feature views
435-
"""
436-
registry_proto = self._get_registry_proto(allow_cache=allow_cache)
437-
on_demand_feature_views = []
438-
for on_demand_feature_view_proto in registry_proto.on_demand_feature_views:
439-
if on_demand_feature_view_proto.spec.project == project:
440-
on_demand_feature_views.append(
441-
OnDemandFeatureView.from_proto(on_demand_feature_view_proto)
442-
)
443-
return on_demand_feature_views
444-
445471
def get_feature_table(self, name: str, project: str) -> FeatureTable:
446472
"""
447473
Retrieves a feature table.

0 commit comments

Comments
 (0)