mirror of
https://github.com/copier-org/copier.git
synced 2025-05-05 15:32:54 +00:00
Add jinja2 extension for yield tag, allow _render_path to generate multiple paths and contexts when yield tag is used. The tag is only allowed in path render contexts. When rendering within a file, it doesn't make sense. Use the normal for tag there. If you use yield, you'll get an exception. Fixes https://github.com/copier-org/copier/issues/1271
250 lines
7.5 KiB
Python
250 lines
7.5 KiB
Python
import warnings
|
|
|
|
import pytest
|
|
|
|
import copier
|
|
from copier.errors import MultipleYieldTagsError, YieldTagInFileError
|
|
from tests.helpers import build_file_tree
|
|
|
|
|
|
def test_folder_loop(tmp_path_factory: pytest.TempPathFactory) -> None:
|
|
src, dst = map(tmp_path_factory.mktemp, ("src", "dst"))
|
|
build_file_tree(
|
|
{
|
|
src / "copier.yml": "",
|
|
src
|
|
/ "folder_loop"
|
|
/ "{% yield item from strings %}{{ item }}{% endyield %}"
|
|
/ "{{ item }}.txt.jinja": "Hello {{ item }}",
|
|
}
|
|
)
|
|
with warnings.catch_warnings():
|
|
warnings.simplefilter("error")
|
|
copier.run_copy(
|
|
str(src),
|
|
dst,
|
|
data={
|
|
"strings": ["a", "b", "c"],
|
|
},
|
|
defaults=True,
|
|
overwrite=True,
|
|
)
|
|
|
|
expected_files = [dst / f"folder_loop/{i}/{i}.txt" for i in ["a", "b", "c"]]
|
|
|
|
for f in expected_files:
|
|
assert f.exists()
|
|
assert f.read_text() == f"Hello {f.parent.name}"
|
|
|
|
all_files = [p for p in dst.rglob("*") if p.is_file()]
|
|
unexpected_files = set(all_files) - set(expected_files)
|
|
|
|
assert not unexpected_files, f"Unexpected files found: {unexpected_files}"
|
|
|
|
|
|
def test_nested_folder_loop(tmp_path_factory: pytest.TempPathFactory) -> None:
|
|
src, dst = map(tmp_path_factory.mktemp, ("src", "dst"))
|
|
build_file_tree(
|
|
{
|
|
src / "copier.yml": "",
|
|
src
|
|
/ "nested_folder_loop"
|
|
/ "{% yield string_item from strings %}{{ string_item }}{% endyield %}"
|
|
/ "{% yield integer_item from integers %}{{ integer_item }}{% endyield %}"
|
|
/ "{{ string_item }}_{{ integer_item }}.txt.jinja": "Hello {{ string_item }} {{ integer_item }}",
|
|
}
|
|
)
|
|
with warnings.catch_warnings():
|
|
warnings.simplefilter("error")
|
|
copier.run_copy(
|
|
str(src),
|
|
dst,
|
|
data={
|
|
"strings": ["a", "b"],
|
|
"integers": [1, 2, 3],
|
|
},
|
|
defaults=True,
|
|
overwrite=True,
|
|
)
|
|
|
|
expected_files = [
|
|
dst / f"nested_folder_loop/{s}/{i}/{s}_{i}.txt"
|
|
for s in ["a", "b"]
|
|
for i in [1, 2, 3]
|
|
]
|
|
|
|
for f in expected_files:
|
|
assert f.exists()
|
|
assert f.read_text() == f"Hello {f.parent.parent.name} {f.parent.name}"
|
|
|
|
all_files = [p for p in dst.rglob("*") if p.is_file()]
|
|
unexpected_files = set(all_files) - set(expected_files)
|
|
|
|
assert not unexpected_files, f"Unexpected files found: {unexpected_files}"
|
|
|
|
|
|
def test_file_loop(tmp_path_factory: pytest.TempPathFactory) -> None:
|
|
src, dst = map(tmp_path_factory.mktemp, ("src", "dst"))
|
|
build_file_tree(
|
|
{
|
|
src / "copier.yml": "",
|
|
src
|
|
/ "file_loop"
|
|
/ "{% yield string_item from strings %}{{ string_item }}{% endyield %}.jinja": "Hello {{ string_item }}",
|
|
}
|
|
)
|
|
with warnings.catch_warnings():
|
|
warnings.simplefilter("error")
|
|
copier.run_copy(
|
|
str(src),
|
|
dst,
|
|
data={
|
|
"strings": [
|
|
"a.txt",
|
|
"b.txt",
|
|
"c.txt",
|
|
"",
|
|
], # if rendred as '.jinja', it will not be created
|
|
},
|
|
defaults=True,
|
|
overwrite=True,
|
|
)
|
|
|
|
expected_files = [dst / f"file_loop/{i}.txt" for i in ["a", "b", "c"]]
|
|
for f in expected_files:
|
|
assert f.exists()
|
|
assert f.read_text() == f"Hello {f.stem}.txt"
|
|
|
|
all_files = [p for p in dst.rglob("*") if p.is_file()]
|
|
unexpected_files = set(all_files) - set(expected_files)
|
|
|
|
assert not unexpected_files, f"Unexpected files found: {unexpected_files}"
|
|
|
|
|
|
def test_folder_loop_dict_items(tmp_path_factory: pytest.TempPathFactory) -> None:
|
|
src, dst = map(tmp_path_factory.mktemp, ("src", "dst"))
|
|
build_file_tree(
|
|
{
|
|
src / "copier.yml": "",
|
|
src
|
|
/ "folder_loop_dict_items"
|
|
/ "{% yield dict_item from dicts %}{{ dict_item.folder_name }}{% endyield %}"
|
|
/ "{{ dict_item.file_name }}.txt.jinja": "Hello {{ '-'.join(dict_item.content) }}",
|
|
}
|
|
)
|
|
|
|
dicts = [
|
|
{
|
|
"folder_name": "folder_a",
|
|
"file_name": "file_a",
|
|
"content": ["folder_a", "file_a"],
|
|
},
|
|
{
|
|
"folder_name": "folder_b",
|
|
"file_name": "file_b",
|
|
"content": ["folder_b", "file_b"],
|
|
},
|
|
{
|
|
"folder_name": "folder_c",
|
|
"file_name": "file_c",
|
|
"content": ["folder_c", "file_c"],
|
|
},
|
|
]
|
|
|
|
with warnings.catch_warnings():
|
|
warnings.simplefilter("error")
|
|
|
|
copier.run_copy(
|
|
str(src),
|
|
dst,
|
|
data={"dicts": dicts},
|
|
defaults=True,
|
|
overwrite=True,
|
|
)
|
|
|
|
expected_files = [
|
|
dst / f"folder_loop_dict_items/{d['folder_name']}/{d['file_name']}.txt"
|
|
for d in [
|
|
{"folder_name": "folder_a", "file_name": "file_a"},
|
|
{"folder_name": "folder_b", "file_name": "file_b"},
|
|
{"folder_name": "folder_c", "file_name": "file_c"},
|
|
]
|
|
]
|
|
|
|
for f in expected_files:
|
|
assert f.exists()
|
|
assert f.read_text() == f"Hello {'-'.join([f.parts[-2], f.stem])}"
|
|
|
|
all_files = [p for p in dst.rglob("*") if p.is_file()]
|
|
unexpected_files = set(all_files) - set(expected_files)
|
|
|
|
assert not unexpected_files, f"Unexpected files found: {unexpected_files}"
|
|
|
|
|
|
def test_raise_yield_tag_in_file(tmp_path_factory: pytest.TempPathFactory) -> None:
|
|
src, dst = map(tmp_path_factory.mktemp, ("src", "dst"))
|
|
build_file_tree(
|
|
{
|
|
src / "copier.yml": "",
|
|
src
|
|
/ "file.txt.jinja": "{% yield item from strings %}{{ item }}{% endyield %}",
|
|
}
|
|
)
|
|
|
|
with pytest.raises(YieldTagInFileError, match="file.txt.jinja"):
|
|
copier.run_copy(
|
|
str(src),
|
|
dst,
|
|
data={
|
|
"strings": ["a", "b", "c"],
|
|
},
|
|
defaults=True,
|
|
overwrite=True,
|
|
)
|
|
|
|
|
|
def test_raise_multiple_yield_tags(tmp_path_factory: pytest.TempPathFactory) -> None:
|
|
src, dst = map(tmp_path_factory.mktemp, ("src", "dst"))
|
|
# multiple yield tags, not nested
|
|
file_name = "{% yield item1 from strings %}{{ item1 }}{% endyield %}{% yield item2 from strings %}{{ item2 }}{% endyield %}"
|
|
|
|
build_file_tree(
|
|
{
|
|
src / "copier.yml": "",
|
|
src / file_name: "",
|
|
}
|
|
)
|
|
|
|
with pytest.raises(MultipleYieldTagsError, match="item"):
|
|
copier.run_copy(
|
|
str(src),
|
|
dst,
|
|
data={
|
|
"strings": ["a", "b", "c"],
|
|
},
|
|
defaults=True,
|
|
overwrite=True,
|
|
)
|
|
|
|
src, dst = map(tmp_path_factory.mktemp, ("src", "dst"))
|
|
# multiple yield tags, nested
|
|
file_name = "{% yield item1 from strings %}{% yield item2 from strings %}{{ item1 }}{{ item2 }}{% endyield %}{% endyield %}"
|
|
|
|
build_file_tree(
|
|
{
|
|
src / "copier.yml": "",
|
|
src / file_name: "",
|
|
}
|
|
)
|
|
|
|
with pytest.raises(MultipleYieldTagsError, match="item"):
|
|
copier.run_copy(
|
|
str(src),
|
|
dst,
|
|
data={
|
|
"strings": ["a", "b", "c"],
|
|
},
|
|
defaults=True,
|
|
overwrite=True,
|
|
)
|