Skip to content

Commit d4d4515

Browse files
authored
Exclude children of subdirectories from symlink (#1690)
1 parent b126a46 commit d4d4515

File tree

5 files changed

+105
-3
lines changed

5 files changed

+105
-3
lines changed

cwltool/job.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def relink_initialworkdir(
7979
container_outdir: str,
8080
inplace_update: bool = False,
8181
) -> None:
82-
for _, vol in pathmapper.items():
82+
for _, vol in pathmapper.items_exclude_children():
8383
if not vol.staged:
8484
continue
8585

cwltool/pathmapper.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import stat
55
import urllib
66
import uuid
7+
from pathlib import Path
78
from typing import Dict, Iterator, List, Optional, Tuple, cast
89

910
from schema_salad.exceptions import ValidationException
@@ -191,6 +192,16 @@ def files(self) -> List[str]:
191192
def items(self) -> List[Tuple[str, MapperEnt]]:
192193
return list(self._pathmap.items())
193194

195+
def items_exclude_children(self) -> List[Tuple[str, MapperEnt]]:
196+
newitems = {}
197+
keys = [key for key, entry in self.items()]
198+
for key, entry in self.items():
199+
parents = Path(key).parents
200+
if any([Path(key_) in parents for key_ in keys]):
201+
continue
202+
newitems[key] = entry
203+
return list(newitems.items())
204+
194205
def reversemap(
195206
self,
196207
target: str,

cwltool/process.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -263,8 +263,9 @@ def stage_files(
263263
fix_conflicts: bool = False,
264264
) -> None:
265265
"""Link or copy files to their targets. Create them as needed."""
266+
items = pathmapper.items() if not symlink else pathmapper.items_exclude_children()
266267
targets = {} # type: Dict[str, MapperEnt]
267-
for key, entry in pathmapper.items():
268+
for key, entry in items:
268269
if "File" not in entry.type:
269270
continue
270271
if entry.target not in targets:
@@ -287,7 +288,7 @@ def stage_files(
287288
% (targets[entry.target].resolved, entry.resolved, entry.target)
288289
)
289290

290-
for key, entry in pathmapper.items():
291+
for key, entry in items:
291292
if not entry.staged:
292293
continue
293294
if not os.path.exists(os.path.dirname(entry.target)):

tests/test_iwdr.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,25 @@ def test_empty_file_creation() -> None:
2525
assert err_code == 0
2626

2727

28+
def test_passthrough_successive(tmp_path: Path) -> None:
29+
"""An empty file can be successively passed through a subdir of InitialWorkingDirectory."""
30+
err_code, _, _ = get_main_output(
31+
[
32+
"--outdir",
33+
str(tmp_path),
34+
get_data("tests/wf/iwdr-passthrough-successive.cwl"),
35+
]
36+
)
37+
assert err_code == 0
38+
children = sorted(
39+
tmp_path.glob("*")
40+
) # This input directory should be left pristine.
41+
assert len(children) == 1
42+
subdir = tmp_path / children[0]
43+
assert len(sorted(subdir.glob("*"))) == 1
44+
assert (subdir / "file").exists()
45+
46+
2847
@needs_docker
2948
def test_directory_literal_with_real_inputs_inside(tmp_path: Path) -> None:
3049
"""Cope with unmoveable files in the output directory created by Docker+IWDR."""
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#!/usr/bin/env cwl-runner
2+
3+
cwlVersion: v1.0
4+
class: Workflow
5+
6+
inputs: []
7+
8+
steps:
9+
# Create a test directory structure; could be done outside CWL and passed in as input.
10+
# This input directory should be left pristine.
11+
mkdirs:
12+
run:
13+
class: CommandLineTool
14+
baseCommand: [bash, '-c', 'mkdir dir dir/subdir && touch dir/subdir/file', '-']
15+
inputs: []
16+
outputs:
17+
mkdirs_out:
18+
type: Directory
19+
outputBinding:
20+
glob: dir
21+
in: []
22+
out: [mkdirs_out]
23+
24+
# Given an input directory, emit a subdirectory as output.
25+
passthrough1:
26+
run:
27+
class: CommandLineTool
28+
requirements:
29+
- class: InitialWorkDirRequirement
30+
listing:
31+
- entry: $(inputs.passthrough1_in)
32+
writable: false
33+
baseCommand: ["true"]
34+
inputs:
35+
passthrough1_in:
36+
type: Directory
37+
outputs:
38+
passthrough1_subdir:
39+
type: Directory
40+
outputBinding:
41+
glob: $(inputs.passthrough1_in.basename)/subdir
42+
in:
43+
passthrough1_in: mkdirs/mkdirs_out
44+
out: [passthrough1_subdir]
45+
46+
# Given a (sub-)directory, emit it unchanged.
47+
passthrough2:
48+
run:
49+
class: CommandLineTool
50+
requirements:
51+
- class: InitialWorkDirRequirement
52+
listing:
53+
- entry: $(inputs.passthrough2_in)
54+
writable: false
55+
baseCommand: ["true"]
56+
inputs:
57+
passthrough2_in:
58+
type: Directory
59+
outputs:
60+
passthrough2_subdir:
61+
type: Directory
62+
outputBinding:
63+
glob: $(inputs.passthrough2_in.basename)
64+
in:
65+
passthrough2_in: passthrough1/passthrough1_subdir
66+
out: [passthrough2_subdir]
67+
68+
outputs:
69+
out:
70+
type: Directory
71+
outputSource: passthrough2/passthrough2_subdir

0 commit comments

Comments
 (0)