Skip to content

Commit d7b5dc7

Browse files
authored
Merge pull request #855 from nipype/copyfile-workflow-fix
Copyfile_workflow conflicting filenames bugfix
2 parents 9b14a72 + 3d1c21c commit d7b5dc7

File tree

3 files changed

+41
-4
lines changed

3 files changed

+41
-4
lines changed

pydra/engine/result.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,12 +233,16 @@ def copyfile_workflow(
233233
) -> workflow.Outputs:
234234
"""if file in the wf results, the file will be copied to the workflow directory"""
235235

236+
clashes_to_avoid: set[Path] = set()
236237
for field in attrs_fields(outputs):
237238
value = getattr(outputs, field.name)
238239
# if the field is a path or it can contain a path _copyfile_single_value is run
239240
# to move all files and directories to the workflow directory
240241
new_value = copy_nested_files(
241-
value, wf_path, mode=FileSet.CopyMode.hardlink_or_copy
242+
value,
243+
wf_path,
244+
mode=FileSet.CopyMode.hardlink_or_copy,
245+
clashes_to_avoid=clashes_to_avoid,
242246
)
243247
setattr(outputs, field.name, new_value)
244248
return outputs

pydra/engine/tests/test_result.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
1-
from pydra.engine.result import Result, Runtime
1+
from pathlib import Path
2+
from fileformats.text import TextFile
3+
from pydra.compose import python
4+
from pydra.engine.result import Result, Runtime, copyfile_workflow
5+
6+
7+
@python.define(outputs=["d", "e", "f"])
8+
def MockTask(
9+
a: TextFile, b: TextFile, c: TextFile
10+
) -> tuple[TextFile, TextFile, TextFile]:
11+
return a, b, c
212

313

414
def test_runtime():
@@ -8,9 +18,30 @@ def test_runtime():
818
assert hasattr(runtime, "cpu_peak_percent")
919

1020

11-
def test_result(tmp_path):
21+
def test_result(tmp_path: Path):
1222
result = Result(cache_dir=tmp_path)
1323
assert hasattr(result, "runtime")
1424
assert hasattr(result, "outputs")
1525
assert hasattr(result, "errored")
1626
assert getattr(result, "errored") is False
27+
28+
29+
def test_copyfile_workflow_conflicting_filenames(tmp_path: Path) -> None:
30+
"""Copy outputs to the workflow output directory with conflicting filenames.
31+
The filenames should be disambiguated to avoid clashes"""
32+
file1 = TextFile.sample(stem="out")
33+
file2 = TextFile.sample(stem="out")
34+
file3 = TextFile.sample(stem="out")
35+
36+
workflow_dir = tmp_path / "output"
37+
mock = MockTask(a=file1, b=file2, c=file3)
38+
outputs = mock()
39+
workflow_dir.mkdir()
40+
41+
copyfile_workflow(workflow_dir, outputs)
42+
43+
assert sorted(p.stem for p in workflow_dir.iterdir()) == [
44+
"out",
45+
"out (1)",
46+
"out (2)",
47+
]

pydra/utils/typing.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1206,6 +1206,7 @@ def copy_nested_files(
12061206
value: ty.Any,
12071207
dest_dir: os.PathLike,
12081208
supported_modes: generic.FileSet.CopyMode = generic.FileSet.CopyMode.any,
1209+
clashes_to_avoid: set[Path] | None = None,
12091210
**kwargs,
12101211
) -> ty.Any:
12111212
"""Copies all "file-sets" found within the nested value (e.g. dict, list,...) into the
@@ -1229,7 +1230,8 @@ def copy_nested_files(
12291230

12301231
# Set to keep track of file paths that have already been copied
12311232
# to allow FileSet.copy to avoid name clashes
1232-
clashes_to_avoid = set()
1233+
if clashes_to_avoid is None:
1234+
clashes_to_avoid = set()
12331235

12341236
def copy_fileset(fileset: generic.FileSet):
12351237
try:

0 commit comments

Comments
 (0)