Skip to content

Commit 5859d21

Browse files
authored
Merge pull request #721 from common-workflow-language/warn_var_spool_cwl
Warn about using /var/spool/cwl
2 parents 078f1b5 + e97196a commit 5859d21

File tree

10 files changed

+150
-86
lines changed

10 files changed

+150
-86
lines changed

MANIFEST.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
include MANIFEST.in
12
include gittaggers.py Makefile cwltool.py
23
include tests/*
34
include tests/tmp1/tmp2/tmp3/.gitkeep

cwltool/builder.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@
88
import six
99
from six import iteritems, string_types
1010

11-
import avro
1211
import schema_salad.validate as validate
12+
import schema_salad.schema as schema
1313
from schema_salad.sourceline import SourceLine
14+
from schema_salad.schema import AvroSchemaFromJSONData
1415

1516
from rdflib import Graph, URIRef
1617
from rdflib.namespace import OWL, RDFS
@@ -25,8 +26,6 @@
2526

2627
_logger = logging.getLogger("cwltool")
2728

28-
AvroSchemaFromJSONData = avro.schema.make_avsc_object
29-
3029
CONTENT_LIMIT = 64 * 1024
3130

3231

@@ -85,7 +84,7 @@ def checkFormat(actualFile, inputFormats, ontology):
8584

8685
class Builder(object):
8786
def __init__(self): # type: () -> None
88-
self.names = None # type: avro.schema.Names
87+
self.names = None # type: schema.Names
8988
self.schemaDefs = None # type: Dict[Text, Dict[Text, Any]]
9089
self.files = None # type: List[Dict[Text, Text]]
9190
self.fs_access = None # type: StdFsAccess

cwltool/factory.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ def __init__(self,
5555

5656
def make(self, cwl):
5757
"""Instantiate a CWL object from a CWl document."""
58-
load = load_tool.load_tool(cwl, self.makeTool)
58+
load = load_tool.load_tool(cwl, self.makeTool,
59+
strict=self.execkwargs.get("strict", True))
5960
if isinstance(load, int):
6061
raise Exception("Error loading tool")
6162
return Callable(load, self)

cwltool/load_tool.py

Lines changed: 84 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,14 @@
1010
import json
1111
import copy
1212
from typing import (Any, Callable, Dict, Iterable, List, Mapping, Optional,
13-
Text, Tuple, Union, cast)
13+
Text, Tuple, Union, cast)
1414

1515
import requests.sessions
16-
from six import itervalues, string_types
16+
from six import iteritems, itervalues, string_types
1717
from six.moves import urllib
1818

19-
import schema_salad.schema as schema
20-
from avro.schema import Names
2119
from ruamel.yaml.comments import CommentedMap, CommentedSeq
20+
import schema_salad.schema as schema
2221
from schema_salad.ref_resolver import ContextType, Fetcher, Loader, file_uri
2322
from schema_salad.sourceline import cmap, SourceLine
2423
from schema_salad.validate import ValidationException
@@ -55,23 +54,23 @@
5554

5655
FetcherConstructorType = Callable[[Dict[Text, Union[Text, bool]],
5756
requests.sessions.Session], Fetcher]
57+
ResolverType = Callable[[Loader, Union[Text, Dict[Text, Any]]], Text]
5858

5959
loaders = {} # type: Dict[FetcherConstructorType, Loader]
6060

6161
def default_loader(fetcher_constructor):
6262
# type: (Optional[FetcherConstructorType]) -> Loader
6363
if fetcher_constructor in loaders:
6464
return loaders[fetcher_constructor]
65-
else:
66-
loader = Loader(jobloaderctx, fetcher_constructor=fetcher_constructor)
67-
loaders[fetcher_constructor] = loader
68-
return loader
65+
loader = Loader(jobloaderctx, fetcher_constructor=fetcher_constructor)
66+
loaders[fetcher_constructor] = loader
67+
return loader
6968

7069
def resolve_tool_uri(argsworkflow, # type: Text
71-
resolver=None, # type: Callable[[Loader, Union[Text, Dict[Text, Any]]], Text]
70+
resolver=None, # type: ResolverType
7271
fetcher_constructor=None, # type: FetcherConstructorType
7372
document_loader=None # type: Loader
74-
): # type: (...) -> Tuple[Text, Text]
73+
): # type: (...) -> Tuple[Text, Text]
7574

7675
uri = None # type: Text
7776
split = urllib.parse.urlsplit(argsworkflow)
@@ -107,7 +106,7 @@ def fetch_document(argsworkflow, # type: Union[Text, Dict[Text, Any]]
107106
workflowobj = None # type: CommentedMap
108107
if isinstance(argsworkflow, string_types):
109108
uri, fileuri = resolve_tool_uri(argsworkflow, resolver=resolver,
110-
document_loader=document_loader)
109+
document_loader=document_loader)
111110
workflowobj = document_loader.fetch(fileuri)
112111
elif isinstance(argsworkflow, dict):
113112
uri = "#" + Text(id(argsworkflow))
@@ -128,11 +127,12 @@ def _convert_stdstreams_to_files(workflowobj):
128127
outputs = workflowobj.get('outputs', [])
129128
if not isinstance(outputs, CommentedSeq):
130129
raise ValidationException('"outputs" section is not '
131-
'valid.')
130+
'valid.')
132131
for out in workflowobj.get('outputs', []):
133-
if type(out) is not CommentedMap:
134-
raise ValidationException("Output '{}' is not a "
135-
"valid OutputParameter.".format(out))
132+
if not isinstance(out, CommentedMap):
133+
raise ValidationException(
134+
"Output '{}' is not a valid "
135+
"OutputParameter.".format(out))
136136
for streamtype in ['stdout', 'stderr']:
137137
if out.get('type') == streamtype:
138138
if 'outputBinding' in out:
@@ -142,8 +142,11 @@ def _convert_stdstreams_to_files(workflowobj):
142142
if streamtype in workflowobj:
143143
filename = workflowobj[streamtype]
144144
else:
145-
filename = Text(hashlib.sha1(json.dumps(workflowobj,
146-
sort_keys=True).encode('utf-8')).hexdigest())
145+
filename = Text(
146+
hashlib.sha1(json.dumps(workflowobj,
147+
sort_keys=True
148+
).encode('utf-8')
149+
).hexdigest())
147150
workflowobj[streamtype] = filename
148151
out['type'] = 'File'
149152
out['outputBinding'] = cmap({'glob': filename})
@@ -174,9 +177,9 @@ def _add_blank_ids(workflowobj):
174177

175178
if isinstance(workflowobj, dict):
176179
if ("run" in workflowobj and
177-
isinstance(workflowobj["run"], dict) and
178-
"id" not in workflowobj["run"] and
179-
"$import" not in workflowobj["run"]):
180+
isinstance(workflowobj["run"], dict) and
181+
"id" not in workflowobj["run"] and
182+
"$import" not in workflowobj["run"]):
180183
workflowobj["run"]["id"] = Text(uuid.uuid4())
181184
for entry in itervalues(workflowobj):
182185
_add_blank_ids(entry)
@@ -195,8 +198,8 @@ def validate_document(document_loader, # type: Loader
195198
overrides=None, # type: List[Dict]
196199
metadata=None, # type: Optional[Dict]
197200
do_validate=True
198-
):
199-
# type: (...) -> Tuple[Loader, Names, Union[Dict[Text, Any], List[Dict[Text, Any]]], Dict[Text, Any], Text]
201+
):
202+
# type: (...) -> Tuple[Loader, schema.Names, Union[Dict[Text, Any], List[Dict[Text, Any]]], Dict[Text, Any], Text]
200203
"""Validate a CWL document."""
201204

202205
if isinstance(workflowobj, list):
@@ -205,7 +208,8 @@ def validate_document(document_loader, # type: Loader
205208
}, fn=uri)
206209

207210
if not isinstance(workflowobj, dict):
208-
raise ValueError("workflowjobj must be a dict, got '%s': %s" % (type(workflowobj), workflowobj))
211+
raise ValueError("workflowjobj must be a dict, got '{}': {}".format(
212+
type(workflowobj), workflowobj))
209213

210214
jobobj = None
211215
if "cwl:tool" in workflowobj:
@@ -220,6 +224,33 @@ def validate_document(document_loader, # type: Loader
220224

221225
workflowobj = fetch_document(uri, fetcher_constructor=fetcher_constructor)[1]
222226

227+
def var_spool_cwl_detector(obj, # type: Union[Mapping, Iterable, Text]
228+
item=None, # type: Optional[Any]
229+
obj_key=None, # type: Optional[Any]
230+
): # type: (...)->None
231+
""" Detects any textual reference to /var/spool/cwl. """
232+
if isinstance(obj, string_types):
233+
if "var/spool/cwl" in obj:
234+
message = SourceLine(
235+
item=item, key=obj_key, raise_type=Text,
236+
include_traceback=_logger.isEnabledFor(logging.DEBUG)).makeError(
237+
"Non-portable reference to /var/spool/cwl found: "
238+
"'{}'.\n Replace with /var/spool/cwl/ with "
239+
"$(runtime.outdir).".format(obj))
240+
if not strict:
241+
_logger.warning(message)
242+
else:
243+
raise ValidationException(message)
244+
else:
245+
return
246+
elif isinstance(obj, Mapping):
247+
for key, value in iteritems(obj):
248+
var_spool_cwl_detector(value, obj, key)
249+
elif isinstance(obj, Iterable):
250+
for element in obj:
251+
var_spool_cwl_detector(element, obj, None)
252+
var_spool_cwl_detector(workflowobj)
253+
223254
fileuri = urllib.parse.urldefrag(uri)[0]
224255
if "cwlVersion" not in workflowobj:
225256
if metadata and 'cwlVersion' in metadata:
@@ -233,7 +264,10 @@ def validate_document(document_loader, # type: Loader
233264
"will need to be upgraded first.")
234265

235266
if not isinstance(workflowobj["cwlVersion"], (str, Text)):
236-
raise Exception("'cwlVersion' must be a string, got %s" % type(workflowobj["cwlVersion"]))
267+
with SourceLine(workflowobj, "cwlVersion", ValidationException):
268+
raise ValidationException("'cwlVersion' must be a string, "
269+
"got {}".format(
270+
type(workflowobj["cwlVersion"])))
237271
# strip out version
238272
workflowobj["cwlVersion"] = re.sub(
239273
r"^(?:cwl:|https://w3id.org/cwl/cwl#)", "",
@@ -246,8 +280,10 @@ def validate_document(document_loader, # type: Loader
246280
version += " (with --enable-dev flag only)"
247281
versions.append(version)
248282
versions.sort()
249-
raise ValidationException("The CWL reference runner no longer supports pre CWL v1.0 documents. "
250-
"Supported versions are: \n{}".format("\n".join(versions)))
283+
raise ValidationException(
284+
"The CWL reference runner no longer supports pre CWL v1.0 "
285+
"documents. Supported versions are: "
286+
"\n{}".format("\n".join(versions)))
251287

252288
(sch_document_loader, avsc_names) = \
253289
process.get_schema(workflowobj["cwlVersion"])[:2]
@@ -263,7 +299,8 @@ def validate_document(document_loader, # type: Loader
263299
_add_blank_ids(workflowobj)
264300

265301
workflowobj["id"] = fileuri
266-
processobj, new_metadata = document_loader.resolve_all(workflowobj, fileuri, checklinks=do_validate)
302+
processobj, new_metadata = document_loader.resolve_all(
303+
workflowobj, fileuri, checklinks=do_validate)
267304
if not isinstance(processobj, (CommentedMap, CommentedSeq)):
268305
raise ValidationException("Workflow must be a dict or list.")
269306

@@ -295,13 +332,12 @@ def validate_document(document_loader, # type: Loader
295332

296333

297334
def make_tool(document_loader, # type: Loader
298-
avsc_names, # type: Names
299-
metadata, # type: Dict[Text, Any]
300-
uri, # type: Text
301-
makeTool, # type: Callable[..., Process]
302-
kwargs # type: dict
303-
):
304-
# type: (...) -> Process
335+
avsc_names, # type: schema.Names
336+
metadata, # type: Dict[Text, Any]
337+
uri, # type: Text
338+
makeTool, # type: Callable[..., Process]
339+
kwargs # type: Dict
340+
): # type: (...) -> Process
305341
"""Make a Python CWL object."""
306342
resolveduri = document_loader.resolve_ref(uri)[0]
307343

@@ -340,28 +376,30 @@ def make_tool(document_loader, # type: Loader
340376
return tool
341377

342378

343-
def load_tool(argsworkflow, # type: Union[Text, Dict[Text, Any]]
344-
makeTool, # type: Callable[..., Process]
345-
kwargs=None, # type: Dict
346-
enable_dev=False, # type: bool
347-
strict=True, # type: bool
348-
resolver=None, # type: Callable[[Loader, Union[Text, Dict[Text, Any]]], Text]
379+
def load_tool(argsworkflow, # type: Union[Text, Dict[Text, Any]]
380+
makeTool, # type: Callable[..., Process]
381+
kwargs=None, # type: Dict
382+
enable_dev=False, # type: bool
383+
strict=True, # type: bool
384+
resolver=None, # type: ResolverType
349385
fetcher_constructor=None, # type: FetcherConstructorType
350386
overrides=None
351-
):
352-
# type: (...) -> Process
387+
): # type: (...) -> Process
353388

354-
document_loader, workflowobj, uri = fetch_document(argsworkflow, resolver=resolver,
355-
fetcher_constructor=fetcher_constructor)
356-
document_loader, avsc_names, processobj, metadata, uri = validate_document(
389+
document_loader, workflowobj, uri = fetch_document(
390+
argsworkflow, resolver=resolver, fetcher_constructor=fetcher_constructor)
391+
document_loader, avsc_names, _, metadata, uri = validate_document(
357392
document_loader, workflowobj, uri, enable_dev=enable_dev,
358393
strict=strict, fetcher_constructor=fetcher_constructor,
359394
overrides=overrides, metadata=kwargs.get('metadata', None)
360395
if kwargs else None)
361396
return make_tool(document_loader, avsc_names, metadata, uri,
362397
makeTool, kwargs if kwargs else {})
363398

364-
def resolve_overrides(ov, ov_uri, baseurl): # type: (CommentedMap, Text, Text) -> List[Dict[Text, Any]]
399+
def resolve_overrides(ov, # Type: CommentedMap
400+
ov_uri, # Type: Text
401+
baseurl # type: Text
402+
): # type: (...) -> List[Dict[Text, Any]]
365403
ovloader = Loader(overrides_ctx)
366404
ret, _ = ovloader.resolve_all(ov, baseurl)
367405
if not isinstance(ret, CommentedMap):

0 commit comments

Comments
 (0)