Skip to content

Commit 6670a19

Browse files
author
Anton Khodak
authored
Merge pull request #570 from common-workflow-language/fix/#560
Generate the same document for a previously packed CWL tool/workflow
2 parents 06808a3 + 6c15b37 commit 6670a19

File tree

5 files changed

+108
-6
lines changed

5 files changed

+108
-6
lines changed

cwltool/pack.py

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
from __future__ import absolute_import
22
import copy
3+
import re
34
from typing import Any, Callable, Dict, List, Set, Text, Union, cast
45

56
from schema_salad.ref_resolver import Loader
67
from six.moves import urllib
8+
from ruamel.yaml.comments import CommentedSeq, CommentedMap
79

810
from .process import shortname, uniquename
911
import six
@@ -64,7 +66,12 @@ def replace_refs(d, rewrite, stem, newstem):
6466
if v in rewrite:
6567
d[s] = rewrite[v]
6668
elif v.startswith(stem):
67-
d[s] = newstem + v[len(stem):]
69+
id_ = v[len(stem):]
70+
# prevent appending newstems if tool is already packed
71+
if id_.startswith(newstem.strip("#")):
72+
d[s] = "#" + id_
73+
else:
74+
d[s] = newstem + id_
6875
replace_refs(v, rewrite, stem, newstem)
6976

7077
def import_embed(d, seen):
@@ -106,12 +113,16 @@ def loadref(b, u):
106113

107114
mainpath, _ = urllib.parse.urldefrag(uri)
108115

109-
def rewrite_id(r, mainuri):
110-
# type: (Text, Text) -> None
116+
def rewrite_id(r, mainuri, document_packed=False):
117+
# type: (Text, Text, bool) -> None
111118
if r == mainuri:
112119
rewrite[r] = "#main"
113120
elif r.startswith(mainuri) and r[len(mainuri)] in ("#", "/"):
114-
pass
121+
if document_packed:
122+
# rewrite tool and mainuri ids in a packed document
123+
tool_id = re.search("#[^/]*$", r)
124+
if tool_id:
125+
rewrite[r] = tool_id.group()
115126
else:
116127
path, frag = urllib.parse.urldefrag(r)
117128
if path == mainpath:
@@ -122,16 +133,20 @@ def rewrite_id(r, mainuri):
122133

123134
sortedids = sorted(ids)
124135

136+
is_document_packed = all(id.startswith(uri) for id in sortedids)
125137
for r in sortedids:
126138
if r in document_loader.idx:
127-
rewrite_id(r, uri)
139+
rewrite_id(r, uri, is_document_packed)
128140

129141
packed = {"$graph": [], "cwlVersion": metadata["cwlVersion"]
130142
} # type: Dict[Text, Any]
131143

132144
schemas = set() # type: Set[Text]
133145
for r in sorted(runs):
134146
dcr, metadata = document_loader.resolve_ref(r)
147+
if isinstance(dcr, CommentedSeq):
148+
dcr = dcr[0]
149+
dcr = cast(CommentedMap, dcr)
135150
if not isinstance(dcr, dict):
136151
continue
137152
for doc in (dcr, metadata):
@@ -141,7 +156,7 @@ def rewrite_id(r, mainuri):
141156
if dcr.get("class") not in ("Workflow", "CommandLineTool", "ExpressionTool"):
142157
continue
143158
dc = cast(Dict[Text, Any], copy.deepcopy(dcr))
144-
v = rewrite[r]
159+
v = rewrite.get(r, r + "#main")
145160
dc["id"] = v
146161
for n in ("name", "cwlVersion", "$namespaces", "$schemas"):
147162
if n in dc:

tests/test_pack.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,32 @@ def test_pack_missing_cwlVersion(self):
6060
packed = json.loads(print_pack(document_loader, processobj, uri, metadata))
6161

6262
self.assertEqual('v1.0', packed["cwlVersion"])
63+
64+
def test_pack_idempotence_tool(self):
65+
"""Test to ensure that pack produces exactly the same document for
66+
an already packed document"""
67+
68+
# Testing single tool
69+
self._pack_idempotently("tests/wf/hello_single_tool.cwl")
70+
71+
def test_pack_idempotence_workflow(self):
72+
"""Test to ensure that pack produces exactly the same document for
73+
an already packed document"""
74+
75+
# Testing workflow
76+
self._pack_idempotently("tests/wf/count-lines1-wf.cwl")
77+
78+
def _pack_idempotently(self, document):
79+
self.maxDiff = None
80+
document_loader, workflowobj, uri = fetch_document(
81+
get_data(document))
82+
document_loader, avsc_names, processobj, metadata, uri = validate_document(
83+
document_loader, workflowobj, uri)
84+
# generate pack output dict
85+
packed = json.loads(print_pack(document_loader, processobj, uri, metadata))
86+
87+
document_loader, workflowobj, uri2 = fetch_document(packed)
88+
document_loader, avsc_names, processobj, metadata, uri2 = validate_document(
89+
document_loader, workflowobj, uri)
90+
double_packed = json.loads(print_pack(document_loader, processobj, uri2, metadata))
91+
self.assertEqual(packed, double_packed)

tests/wf/count-lines1-wf.cwl

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#!/usr/bin/env cwl-runner
2+
class: Workflow
3+
cwlVersion: v1.0
4+
5+
inputs:
6+
file1:
7+
type: File
8+
9+
outputs:
10+
count_output:
11+
type: int
12+
outputSource: step2/output
13+
14+
steps:
15+
step1:
16+
run: wc-tool.cwl
17+
in:
18+
file1: file1
19+
out: [output]
20+
21+
step2:
22+
run: parseInt-tool.cwl
23+
in:
24+
file1: step1/output
25+
out: [output]

tests/wf/parseInt-tool.cwl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#!/usr/bin/env cwl-runner
2+
3+
class: ExpressionTool
4+
requirements:
5+
- class: InlineJavascriptRequirement
6+
cwlVersion: v1.0
7+
8+
inputs:
9+
file1:
10+
type: File
11+
inputBinding: { loadContents: true }
12+
13+
outputs:
14+
output: int
15+
16+
expression: "$({'output': parseInt(inputs.file1.contents)})"

tests/wf/wc-tool.cwl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#!/usr/bin/env cwl-runner
2+
3+
class: CommandLineTool
4+
cwlVersion: v1.0
5+
6+
inputs:
7+
file1: File
8+
9+
outputs:
10+
output:
11+
type: File
12+
outputBinding: { glob: output }
13+
14+
baseCommand: [wc, -l]
15+
16+
stdin: $(inputs.file1.path)
17+
stdout: output

0 commit comments

Comments
 (0)