Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion metaflow/plugins/metadata_providers/local.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ def filter_tasks_by_metadata(

if any(
meta.get("field_name") == field_name
and regex.match(meta.get("value", ""))
and regex.fullmatch(meta.get("value", ""))
for meta in metadata
):
matching_task_pathspecs.append(
Expand Down
38 changes: 38 additions & 0 deletions test/unit/test_local_metadata_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,41 @@ def test_deduce_run_id_from_meta_dir():
case["meta_path"], case["sub_type"]
)
assert case["expected_run_id"] == actual_run_id


def test_filter_tasks_by_metadata_does_not_match_prefixes(monkeypatch):
# A foreach with 11+ items produces execution paths like "middle:1" and
# "middle:10". Matching with re.match only anchors the start, so the
# pattern "middle:1" also selected "middle:10" and "middle:11", giving
# Task.parent_tasks/child_tasks the wrong tasks.
paths = {
"1": "middle:1",
"2": "middle:10",
"3": "middle:11",
"4": "middle:1,inner:0",
}

def fake_get_object(cls, obj_type, sub_type, filters, attempt, *args):
if sub_type == "task":
return [{"task_id": task_id} for task_id in sorted(paths)]
task_id = args[-1]
return [
{"field_name": "foreach-execution-path", "value": paths[task_id]},
]

monkeypatch.setattr(
LocalMetadataProvider, "get_object", classmethod(fake_get_object)
)

def filter_for(pattern):
return LocalMetadataProvider.filter_tasks_by_metadata(
"Flow", "run", "middle", "foreach-execution-path", pattern
)

# the exact path must not pull in the longer indices that start with it
assert filter_for("middle:1") == ["Flow/run/middle/1"]
assert filter_for("middle:10") == ["Flow/run/middle/2"]
# a nested foreach still resolves its children through the ",.*" pattern
assert filter_for("middle:1,.*") == ["Flow/run/middle/4"]
# and the match-all pattern keeps returning everything
assert len(filter_for(".*")) == len(paths)
Loading