Skip to content

Commit 61b40c7

Browse files
committed
Better support for $import or "run: " references to parent directories.
1 parent db787f1 commit 61b40c7

File tree

2 files changed

+50
-41
lines changed

2 files changed

+50
-41
lines changed

cwlupgrader/main.py

+47-38
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ def parse_args(args: List[str]) -> argparse.Namespace:
3131
"""Argument parser."""
3232
parser = argparse.ArgumentParser(
3333
description="Tool to upgrade CWL documents from one version to another. "
34-
"Supports upgrading 'draft-3', 'v1.0', and 'v1.1' to 'v1.2'",
34+
"Supports upgrading 'draft-3', 'v1.0', and 'v1.1' to 'v1.2'. For workflows "
35+
'that include "$import" or "run:" reference that refer to parent '
36+
"directories, start cwl-upgrader from the topmost directory of your project.",
3537
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
3638
)
3739
parser.add_argument(
@@ -40,11 +42,11 @@ def parse_args(args: List[str]) -> argparse.Namespace:
4042
parser.add_argument(
4143
"--v1.1-only",
4244
dest="v1_1_only",
43-
help="Don't upgrade past cwlVersion: v1.1",
45+
help="Don't upgrade past cwlVersion: v1.1.",
4446
action="store_true",
4547
)
4648
parser.add_argument(
47-
"--dir", help="Directory in which to save converted files", default=Path.cwd()
49+
"--dir", help="Directory in which to save converted files.", default=Path.cwd()
4850
)
4951
parser.add_argument(
5052
"inputs",
@@ -64,10 +66,21 @@ def main(args: Optional[List[str]] = None) -> int:
6466
def run(args: argparse.Namespace) -> int:
6567
"""Main function."""
6668
imports: Set[str] = set()
67-
if args.dir and not os.path.exists(args.dir):
68-
os.makedirs(args.dir)
69-
for path in args.inputs:
69+
if args.dir:
70+
out_dir = Path(args.dir)
71+
out_dir.mkdir(parents=True, exist_ok=True)
72+
else:
73+
out_dir = Path.cwd()
74+
for p in args.inputs:
75+
path = Path(os.path.normpath(p))
7076
_logger.info("Processing %s", path)
77+
if not p.startswith("../") and path.resolve() != path:
78+
# common_path = os.path.commonpath([out_dir.resolve(), path.resolve()])
79+
# current_out_dir = out_dir / os.path.relpath(path, start=common_path)
80+
current_out_dir = out_dir / path.parent
81+
current_out_dir.mkdir(parents=True, exist_ok=True)
82+
else:
83+
current_out_dir = out_dir
7184
document = load_cwl_document(path)
7285
if "cwlVersion" not in document:
7386
_logger.warn("No cwlVersion found in %s, skipping it.", path)
@@ -89,17 +102,17 @@ def run(args: argparse.Namespace) -> int:
89102
target_version = "latest"
90103
upgraded_document = upgrade_document(
91104
document,
92-
args.dir,
105+
current_out_dir,
93106
target_version=target_version,
94107
imports=imports,
95108
)
96-
write_cwl_document(upgraded_document, Path(path).name, args.dir)
109+
write_cwl_document(upgraded_document, current_out_dir / (path.name))
97110
return 0
98111

99112

100113
def upgrade_document(
101114
document: Any,
102-
output_dir: str,
115+
output_dir: Path,
103116
target_version: Optional[str] = "latest",
104117
imports: Optional[Set[str]] = None,
105118
) -> Any:
@@ -158,7 +171,7 @@ def upgrade_document(
158171
return main_updater(document, output_dir)
159172

160173

161-
def load_cwl_document(path: str) -> Any:
174+
def load_cwl_document(path: Union[str, Path]) -> Any:
162175
"""
163176
Load the given path using the Ruamel YAML round-trip loader.
164177
@@ -171,15 +184,14 @@ def load_cwl_document(path: str) -> Any:
171184
return document
172185

173186

174-
def write_cwl_document(document: Any, name: str, dirname: str) -> None:
187+
def write_cwl_document(document: Any, path: Path) -> None:
175188
"""
176189
Serialize the document using the Ruamel YAML round trip dumper.
177190
178191
Will also prepend "#!/usr/bin/env cwl-runner\n" and
179192
set the executable bit if it is a CWL document.
180193
"""
181194
ruamel.yaml.scalarstring.walk_tree(document)
182-
path = Path(dirname) / name
183195
with open(path, "w") as handle:
184196
if "cwlVersion" in document:
185197
handle.write("#!/usr/bin/env cwl-runner\n")
@@ -189,7 +201,7 @@ def write_cwl_document(document: Any, name: str, dirname: str) -> None:
189201

190202

191203
def process_imports(
192-
document: Any, imports: Set[str], updater: Callable[[Any, str], Any], outdir: str
204+
document: Any, imports: Set[str], updater: Callable[[Any, Path], Any], outdir: Path
193205
) -> None:
194206
"""Find any '$import's and process them."""
195207
if isinstance(document, CommentedMap):
@@ -200,14 +212,9 @@ def process_imports(
200212
import_doc = load_cwl_document(
201213
Path(document.lc.filename).parent / value
202214
)
203-
write_cwl_document(
204-
updater(
205-
import_doc,
206-
outdir,
207-
),
208-
Path(value).name,
209-
outdir,
210-
)
215+
new_path = (outdir / value).resolve()
216+
new_path.parent.mkdir(parents=True, exist_ok=True)
217+
write_cwl_document(updater(import_doc, outdir), new_path)
211218
imports.add(value)
212219
else:
213220
process_imports(value, imports, updater, outdir)
@@ -216,7 +223,7 @@ def process_imports(
216223
process_imports(entry, imports, updater, outdir)
217224

218225

219-
def v1_0_to_v1_1(document: CommentedMap, outdir: str) -> CommentedMap:
226+
def v1_0_to_v1_1(document: CommentedMap, outdir: Path) -> CommentedMap:
220227
"""CWL v1.0.x to v1.1 transformation loop."""
221228
_v1_0_to_v1_1(document, outdir)
222229
for key, value in document.items():
@@ -231,20 +238,20 @@ def v1_0_to_v1_1(document: CommentedMap, outdir: str) -> CommentedMap:
231238
return sort_v1_0(document)
232239

233240

234-
def v1_0_to_v1_2(document: CommentedMap, outdir: str) -> CommentedMap:
241+
def v1_0_to_v1_2(document: CommentedMap, outdir: Path) -> CommentedMap:
235242
"""CWL v1.0.x to v1.2 transformation."""
236243
document = v1_0_to_v1_1(document, outdir)
237244
document["cwlVersion"] = "v1.2"
238245
return document
239246

240247

241-
def v1_1_to_v1_2(document: CommentedMap, outdir: str) -> CommentedMap:
248+
def v1_1_to_v1_2(document: CommentedMap, outdir: Path) -> CommentedMap:
242249
"""CWL v1.1 to v1.2 transformation."""
243250
document["cwlVersion"] = "v1.2"
244251
return document
245252

246253

247-
def draft3_to_v1_0(document: CommentedMap, outdir: str) -> CommentedMap:
254+
def draft3_to_v1_0(document: CommentedMap, outdir: Path) -> CommentedMap:
248255
"""Transformation loop."""
249256
_draft3_to_v1_0(document, outdir)
250257
if isinstance(document, MutableMapping):
@@ -260,17 +267,17 @@ def draft3_to_v1_0(document: CommentedMap, outdir: str) -> CommentedMap:
260267
return sort_v1_0(document)
261268

262269

263-
def draft3_to_v1_1(document: CommentedMap, outdir: str) -> CommentedMap:
270+
def draft3_to_v1_1(document: CommentedMap, outdir: Path) -> CommentedMap:
264271
"""transformation loop."""
265272
return v1_0_to_v1_1(draft3_to_v1_0(document, outdir), outdir)
266273

267274

268-
def draft3_to_v1_2(document: CommentedMap, outdir: str) -> CommentedMap:
275+
def draft3_to_v1_2(document: CommentedMap, outdir: Path) -> CommentedMap:
269276
"""transformation loop."""
270277
return v1_1_to_v1_2(v1_0_to_v1_1(draft3_to_v1_0(document, outdir), outdir), outdir)
271278

272279

273-
def _draft3_to_v1_0(document: CommentedMap, outdir: str) -> CommentedMap:
280+
def _draft3_to_v1_0(document: CommentedMap, outdir: Path) -> CommentedMap:
274281
"""Inner loop for transforming draft-3 to v1.0."""
275282
if "class" in document:
276283
if document["class"] == "Workflow":
@@ -295,11 +302,11 @@ def _draft3_to_v1_0(document: CommentedMap, outdir: str) -> CommentedMap:
295302
return document
296303

297304

298-
def _draft3_to_v1_1(document: CommentedMap, outdir: str) -> CommentedMap:
305+
def _draft3_to_v1_1(document: CommentedMap, outdir: Path) -> CommentedMap:
299306
return v1_0_to_v1_1(_draft3_to_v1_0(document, outdir), outdir)
300307

301308

302-
def _draft3_to_v1_2(document: CommentedMap, outdir: str) -> CommentedMap:
309+
def _draft3_to_v1_2(document: CommentedMap, outdir: Path) -> CommentedMap:
303310
return _draft3_to_v1_1(document, outdir) # nothing needs doing for 1.2
304311

305312

@@ -318,7 +325,7 @@ def _draft3_to_v1_2(document: CommentedMap, outdir: str) -> CommentedMap:
318325
}
319326

320327

321-
def _v1_0_to_v1_1(document: CommentedMap, outdir: str) -> CommentedMap:
328+
def _v1_0_to_v1_1(document: CommentedMap, outdir: Path) -> CommentedMap:
322329
"""Inner loop for transforming draft-3 to v1.0."""
323330
if "class" in document:
324331
if document["class"] == "Workflow":
@@ -350,11 +357,13 @@ def _v1_0_to_v1_1(document: CommentedMap, outdir: str) -> CommentedMap:
350357
isinstance(entry["run"], str)
351358
and "#" not in entry["run"]
352359
):
353-
path = Path(document.lc.filename).parent / entry["run"]
354-
process = v1_0_to_v1_1(
355-
load_cwl_document(str(path)), outdir
356-
)
357-
write_cwl_document(process, path.name, outdir)
360+
path = (
361+
Path(document.lc.filename).parent / entry["run"]
362+
).resolve()
363+
process = v1_0_to_v1_1(load_cwl_document(path), outdir)
364+
new_path = (outdir / entry["run"]).resolve()
365+
new_path.parent.mkdir(parents=True, exist_ok=True)
366+
write_cwl_document(process, new_path)
358367
elif isinstance(entry["run"], str) and "#" in entry["run"]:
359368
pass # reference to $graph entry
360369
else:
@@ -397,11 +406,11 @@ def _v1_0_to_v1_1(document: CommentedMap, outdir: str) -> CommentedMap:
397406
return document
398407

399408

400-
def _v1_0_to_v1_2(document: CommentedMap, outdir: str) -> CommentedMap:
409+
def _v1_0_to_v1_2(document: CommentedMap, outdir: Path) -> CommentedMap:
401410
return _v1_0_to_v1_1(document, outdir) # nothing needs doing for v1.2
402411

403412

404-
def _v1_1_to_v1_2(document: CommentedMap, outdir: str) -> CommentedMap:
413+
def _v1_1_to_v1_2(document: CommentedMap, outdir: Path) -> CommentedMap:
405414
return document
406415

407416

tests/test_complete.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -37,21 +37,21 @@ def test_draft3_tool_long_form_arrays(tmp_path: Path) -> None:
3737
def test_invalid_target(tmp_path: Path) -> None:
3838
"""Test for invalid target version"""
3939
doc = load_cwl_document(get_data("testdata/v1.0/listing_deep1.cwl"))
40-
result = upgrade_document(doc, str(tmp_path), "invalid-version")
40+
result = upgrade_document(doc, tmp_path, "invalid-version")
4141
assert result is None
4242

4343

4444
def test_v1_0_to_v1_1(tmp_path: Path) -> None:
4545
"""Basic CWL v1.0 to CWL v1.1 test."""
4646
doc = load_cwl_document(get_data("testdata/v1.0/listing_deep1.cwl"))
47-
upgraded = upgrade_document(doc, str(tmp_path), "v1.1")
47+
upgraded = upgrade_document(doc, tmp_path, "v1.1")
4848
assert doc == upgraded
4949

5050

5151
def test_v1_1_to_v1_2(tmp_path: Path) -> None:
5252
"""Basic CWL v1.1 to CWL v1.2 test."""
5353
doc = load_cwl_document(get_data("testdata/v1.1/listing_deep1.cwl"))
54-
upgraded = upgrade_document(doc, str(tmp_path), "v1.2")
54+
upgraded = upgrade_document(doc, tmp_path, "v1.2")
5555
assert doc == upgraded
5656

5757

0 commit comments

Comments
 (0)