Skip to content

Commit e11b61c

Browse files
alexk101daavoo
andauthored
Modified recursive dictionary parsing to correctly handle lists in dvc plot templates (#134)
* Modified recursive dictionary parsing to correctly handle lists in dvc plot templates * Rename dict_find_value -> find_value. Extend tests. * Update type hint --------- Co-authored-by: daavoo <daviddelaiglesiacastro@gmail.com>
1 parent 635eaff commit e11b61c

File tree

3 files changed

+43
-22
lines changed

3 files changed

+43
-22
lines changed

src/dvc_render/vega_templates.py

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -61,16 +61,21 @@ def list_replace_value(l: list, name: str, value: str) -> list: # noqa: E741
6161
return x
6262

6363

64-
def dict_find_value(d: dict, value: str) -> bool:
65-
for v in d.values():
66-
if isinstance(v, dict):
67-
if dict_find_value(v, value):
68-
return True
69-
if isinstance(v, str):
70-
if v == value:
71-
return True
72-
if isinstance(v, list):
73-
return any(dict_find_value(e, value) for e in v)
64+
def find_value(d: Union[dict, list, str], value: str) -> bool:
65+
if isinstance(d, dict):
66+
for v in d.values():
67+
if isinstance(v, dict):
68+
if find_value(v, value):
69+
return True
70+
if isinstance(v, str):
71+
if v == value:
72+
return True
73+
if isinstance(v, list):
74+
if any(find_value(e, value) for e in v):
75+
return True
76+
elif isinstance(d, str):
77+
if d == value:
78+
return True
7479
return False
7580

7681

@@ -120,7 +125,7 @@ def reset(self):
120125

121126
def has_anchor(self, name) -> bool:
122127
"Check if ANCHOR formatted with name is in content."
123-
found = dict_find_value(self.content, self.anchor(name))
128+
found = find_value(self.content, self.anchor(name))
124129
return found
125130

126131
def fill_anchor(self, name, value) -> None:

tests/test_templates.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
Template,
1111
TemplateContentDoesNotMatch,
1212
TemplateNotFoundError,
13-
dict_find_value,
1413
dump_templates,
14+
find_value,
1515
get_template,
1616
)
1717

@@ -110,7 +110,8 @@ def test_escape_special_characters():
110110
({"key": "value"}, "value"),
111111
({"key": {"subkey": "value"}}, "value"),
112112
({"key": [{"subkey": "value"}]}, "value"),
113+
({"key1": [{"subkey": "foo"}], "key2": {"subkey2": "value"}}, "value"),
113114
],
114115
)
115-
def test_dict_find_value(content_dict, value_name):
116-
assert dict_find_value(content_dict, value_name)
116+
def test_find_value(content_dict, value_name):
117+
assert find_value(content_dict, value_name)

tests/test_vega.py

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -110,20 +110,35 @@ def test_bad_template_on_init():
110110
Template("name", "content")
111111

112112

113-
def test_bad_template_on_missing_data(tmp_dir):
114-
template_content = {"data": {"values": "BAD_ANCHOR"}}
115-
tmp_dir.gen("bar.json", json.dumps(template_content))
113+
@pytest.mark.parametrize(
114+
"bad_content,good_content",
115+
(
116+
(
117+
{"data": {"values": "BAD_ANCHOR"}},
118+
{"data": {"values": Template.anchor("data")}},
119+
),
120+
(
121+
{"mark": {"type": "bar"}, "data": {"values": "BAD_ANCHOR"}},
122+
{"mark": {"type": "bar"}, "data": {"values": Template.anchor("data")}},
123+
),
124+
(
125+
{"repeat": ["quintile"], "spec": {"data": {"values": "BAD_ANCHOR"}}},
126+
{
127+
"repeat": ["quintile"],
128+
"spec": {"data": {"values": Template.anchor("data")}},
129+
},
130+
),
131+
),
132+
)
133+
def test_bad_template_on_missing_data(tmp_dir, bad_content, good_content):
134+
tmp_dir.gen("bar.json", json.dumps(bad_content))
116135
datapoints = [{"val": 2}, {"val": 3}]
117136
renderer = VegaRenderer(datapoints, "foo", template="bar.json")
118137

119138
with pytest.raises(BadTemplateError):
120139
renderer.get_filled_template()
121140

122-
template_content = {
123-
"mark": {"type": "bar"},
124-
"data": {"values": Template.anchor("data")},
125-
}
126-
tmp_dir.gen("bar.json", json.dumps(template_content))
141+
tmp_dir.gen("bar.json", json.dumps(good_content))
127142
renderer = VegaRenderer(datapoints, "foo", template="bar.json")
128143
assert renderer.get_filled_template()
129144

0 commit comments

Comments
 (0)