From 681905cac72e6b6a9e6c881c236c3d65c16951cb Mon Sep 17 00:00:00 2001 From: Tommy Hughes Date: Mon, 17 Jun 2024 10:39:24 -0500 Subject: [PATCH] fix multiple --tags option Signed-off-by: Tommy Hughes --- sdk/python/feast/cli.py | 21 ++++++++++--------- sdk/python/feast/utils.py | 13 ++++++++++-- .../test_local_feature_store.py | 20 +++++++++++++++--- 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/sdk/python/feast/cli.py b/sdk/python/feast/cli.py index 76bcefa9ac6..1683dd07158 100644 --- a/sdk/python/feast/cli.py +++ b/sdk/python/feast/cli.py @@ -50,6 +50,7 @@ tagsOption = click.option( "--tags", help="Filter by tags (e.g. 'key:value, key:value, ...')", + default=[""], multiple=True, ) @@ -233,13 +234,13 @@ def data_source_describe(ctx: click.Context, name: str): @data_sources_cmd.command(name="list") @tagsOption @click.pass_context -def data_source_list(ctx: click.Context, tags: Optional[str]): +def data_source_list(ctx: click.Context, tags: list[str]): """ List all data sources """ store = create_feature_store(ctx) table = [] - tags_filter = utils.tags_str_to_dict(tags) + tags_filter = utils.tags_list_to_dict(tags) for datasource in store.list_data_sources(tags=tags_filter): table.append([datasource.name, datasource.__class__]) @@ -281,13 +282,13 @@ def entity_describe(ctx: click.Context, name: str): @entities_cmd.command(name="list") @tagsOption @click.pass_context -def entity_list(ctx: click.Context, tags: Optional[str]): +def entity_list(ctx: click.Context, tags: list[str]): """ List all entities """ store = create_feature_store(ctx) table = [] - tags_filter = utils.tags_str_to_dict(tags) + tags_filter = utils.tags_list_to_dict(tags) for entity in store.list_entities(tags=tags_filter): table.append([entity.name, entity.description, entity.value_type]) @@ -331,13 +332,13 @@ def feature_service_describe(ctx: click.Context, name: str): @feature_services_cmd.command(name="list") @tagsOption @click.pass_context -def feature_service_list(ctx: click.Context, tags: Optional[str]): +def feature_service_list(ctx: click.Context, tags: list[str]): """ List all feature services """ store = create_feature_store(ctx) feature_services = [] - tags_filter = utils.tags_str_to_dict(tags) + tags_filter = utils.tags_list_to_dict(tags) for feature_service in store.list_feature_services(tags=tags_filter): feature_names = [] for projection in feature_service.feature_view_projections: @@ -384,13 +385,13 @@ def feature_view_describe(ctx: click.Context, name: str): @feature_views_cmd.command(name="list") @tagsOption @click.pass_context -def feature_view_list(ctx: click.Context, tags: Optional[str]): +def feature_view_list(ctx: click.Context, tags: list[str]): """ List all feature views """ store = create_feature_store(ctx) table = [] - tags_filter = utils.tags_str_to_dict(tags) + tags_filter = utils.tags_list_to_dict(tags) for feature_view in [ *store.list_batch_feature_views(tags=tags_filter), *store.list_on_demand_feature_views(tags=tags_filter), @@ -449,13 +450,13 @@ def on_demand_feature_view_describe(ctx: click.Context, name: str): @on_demand_feature_views_cmd.command(name="list") @tagsOption @click.pass_context -def on_demand_feature_view_list(ctx: click.Context, tags: Optional[str]): +def on_demand_feature_view_list(ctx: click.Context, tags: list[str]): """ [Experimental] List all on demand feature views """ store = create_feature_store(ctx) table = [] - tags_filter = utils.tags_str_to_dict(tags) + tags_filter = utils.tags_list_to_dict(tags) for on_demand_feature_view in store.list_on_demand_feature_views(tags=tags_filter): table.append([on_demand_feature_view.name]) diff --git a/sdk/python/feast/utils.py b/sdk/python/feast/utils.py index dcd8b1d9273..0a7df069d79 100644 --- a/sdk/python/feast/utils.py +++ b/sdk/python/feast/utils.py @@ -266,9 +266,18 @@ def has_all_tags( return all(object_tags.get(key, None) == val for key, val in requested_tags.items()) -def tags_str_to_dict(tags: Optional[str] = None) -> Optional[dict[str, str]]: - if tags is None: +def tags_list_to_dict( + tags_list: Optional[list[str]] = None, +) -> Optional[dict[str, str]]: + if not tags_list: return None + tags_dict: dict[str, str] = {} + for tags_str in tags_list: + tags_dict.update(tags_str_to_dict(tags_str)) + return tags_dict + + +def tags_str_to_dict(tags: str = "") -> dict[str, str]: tags_list = ( str(tags).strip().strip("()").replace('"', "").replace("'", "").split(",") ) diff --git a/sdk/python/tests/unit/local_feast_tests/test_local_feature_store.py b/sdk/python/tests/unit/local_feast_tests/test_local_feature_store.py index 63eafe6fc9a..6b7856f347c 100644 --- a/sdk/python/tests/unit/local_feast_tests/test_local_feature_store.py +++ b/sdk/python/tests/unit/local_feast_tests/test_local_feature_store.py @@ -115,12 +115,26 @@ def test_apply_feature_view(test_feature_store): and feature_views[0].entities[0] == "fs1_my_entity_1" ) - assert utils.tags_str_to_dict() is None + assert utils.tags_str_to_dict() == {} + assert utils.tags_list_to_dict() is None + assert utils.tags_list_to_dict([]) is None + assert utils.tags_list_to_dict([""]) == {} + assert utils.tags_list_to_dict( + ( + "team : driver_performance, other:tag", + "blanktag:", + "other:two", + "other:3", + "missing", + ) + ) == {"team": "driver_performance", "other": "3", "blanktag": ""} assert utils.has_all_tags({}) tags_dict = {"team": "matchmaking"} tags_filter = utils.tags_str_to_dict("('team:matchmaking',)") assert tags_filter == tags_dict + tags_filter = utils.tags_list_to_dict(("team:matchmaking", "test")) + assert tags_dict == tags_dict # List Feature Views feature_views = test_feature_store.list_batch_feature_views(tags=tags_filter) @@ -141,7 +155,7 @@ def test_apply_feature_view(test_feature_store): ) tags_dict = {"team": "matchmaking", "tag": "two"} - tags_filter = utils.tags_str_to_dict("(' team :matchmaking, tag: two ',)") + tags_filter = utils.tags_list_to_dict((" team :matchmaking, tag: two ",)) assert tags_filter == tags_dict # List Feature Views @@ -162,7 +176,7 @@ def test_apply_feature_view(test_feature_store): ) tags_dict = {"missing": "tag"} - tags_filter = utils.tags_str_to_dict("('missing:tag,fdsa',fdas)") + tags_filter = utils.tags_list_to_dict(("missing:tag,fdsa", "fdas")) assert tags_filter == tags_dict # List Feature Views