1
1
from __future__ import absolute_import
2
+
2
3
import copy
3
- import os
4
4
import logging
5
- from typing import Any , Callable , Dict , List , Text , Type , Union , Set
6
-
7
- import six
8
- from six import iteritems , string_types
5
+ from typing import (Any , Callable , Dict , List , # pylint: disable=unused-import
6
+ Optional , Set , Text , Type , Union )
9
7
8
+ from rdflib import Graph , URIRef # pylint: disable=unused-import
9
+ from rdflib .namespace import OWL , RDFS
10
+ import schema_salad .schema # pylint: disable=unused-import
10
11
import schema_salad .validate as validate
11
- import schema_salad .schema as schema
12
- from schema_salad .sourceline import SourceLine
13
12
from schema_salad .schema import AvroSchemaFromJSONData
14
-
15
- from rdflib import Graph , URIRef
16
- from rdflib .namespace import OWL , RDFS
13
+ from schema_salad .sourceline import SourceLine
14
+ from six import iteritems , string_types
17
15
18
16
from . import expression
19
17
from .errors import WorkflowException
20
- from .mutation import MutationManager
21
- from .pathmapper import (PathMapper , get_listing , normalizeFilesDirs ,
22
- visit_class )
23
- from .stdfsaccess import StdFsAccess
24
- from .utils import (aslist , get_feature , docker_windows_path_adjust ,
18
+ from .loghandler import _logger
19
+ from .mutation import MutationManager # pylint: disable=unused-import
20
+ from .pathmapper import (PathMapper , # pylint: disable=unused-import
21
+ get_listing , normalizeFilesDirs , visit_class )
22
+ from .stdfsaccess import StdFsAccess # pylint: disable=unused-import
23
+ from .utils import (aslist , docker_windows_path_adjust , get_feature ,
25
24
json_dumps , onWindows )
26
25
27
- _logger = logging .getLogger ("cwltool" )
28
-
29
26
CONTENT_LIMIT = 64 * 1024
30
27
31
28
32
29
def substitute (value , replace ): # type: (Text, Text) -> Text
33
30
if replace [0 ] == "^" :
34
31
return substitute (value [0 :value .rindex ('.' )], replace [1 :])
35
- else :
36
- return value + replace
32
+ return value + replace
37
33
38
34
def formatSubclassOf (fmt , cls , ontology , visited ):
39
- # type: (Text, Text, Graph, Set[Text]) -> bool
35
+ # type: (Text, Text, Optional[ Graph] , Set[Text]) -> bool
40
36
"""Determine if `fmt` is a subclass of `cls`."""
41
37
42
38
if URIRef (fmt ) == URIRef (cls ):
@@ -69,49 +65,76 @@ def formatSubclassOf(fmt, cls, ontology, visited):
69
65
70
66
return False
71
67
72
- def checkFormat (actualFile , inputFormats , ontology ):
73
- # type: (Union[Dict[Text, Any], List, Text], Union[List[Text], Text], Graph) -> None
74
- for af in aslist (actualFile ):
75
- if not af :
68
+ def check_format (actual_file , # type: Union[Dict[Text, Any], List, Text]
69
+ input_formats , # type: Union[List[Text], Text]
70
+ ontology # type: Optional[Graph]
71
+ ): # type: (...) -> None
72
+ """ Confirms that the format present is valid for the allowed formats."""
73
+ for afile in aslist (actual_file ):
74
+ if not afile :
76
75
continue
77
- if "format" not in af :
76
+ if "format" not in afile :
78
77
raise validate .ValidationException (
79
- u"File has no 'format' defined: %s" % json_dumps (af , indent = 4 ))
80
- for inpf in aslist (inputFormats ):
81
- if af ["format" ] == inpf or formatSubclassOf (af ["format" ], inpf , ontology , set ()):
78
+ u"File has no 'format' defined: {}" .format (
79
+ json_dumps (afile , indent = 4 )))
80
+ for inpf in aslist (input_formats ):
81
+ if afile ["format" ] == inpf or \
82
+ formatSubclassOf (afile ["format" ], inpf , ontology , set ()):
82
83
return
83
84
raise validate .ValidationException (
84
- u"File has an incompatible format: %s" % json_dumps (af , indent = 4 ))
85
+ u"File has an incompatible format: {}" .format (
86
+ json_dumps (afile , indent = 4 )))
85
87
86
88
class Builder (object ):
87
- def __init__ (self ): # type: () -> None
88
- self .names = None # type: schema.Names
89
- self .schemaDefs = None # type: Dict[Text, Dict[Text, Any]]
90
- self .files = None # type: List[Dict[Text, Text]]
91
- self .fs_access = None # type: StdFsAccess
92
- self .job = None # type: Dict[Text, Union[Dict[Text, Any], List, Text]]
93
- self .requirements = None # type: List[Dict[Text, Any]]
94
- self .hints = None # type: List[Dict[Text, Any]]
95
- self .outdir = None # type: Text
96
- self .tmpdir = None # type: Text
97
- self .resources = None # type: Dict[Text, Union[int, Text]]
98
- self .bindings = [] # type: List[Dict[Text, Any]]
99
- self .timeout = None # type: int
100
- self .pathmapper = None # type: PathMapper
101
- self .stagedir = None # type: Text
102
- self .make_fs_access = None # type: Type[StdFsAccess]
103
- self .debug = False # type: bool
104
- self .js_console = False # type: bool
105
- self .mutation_manager = None # type: MutationManager
106
- self .force_docker_pull = False # type: bool
107
- self .formatgraph = None # type: Graph
89
+ def __init__ (self ,
90
+ job , # type: Dict[Text, Union[Dict[Text, Any], List, Text]]
91
+ files , # type: List[Dict[Text, Text]]
92
+ bindings , # type: List[Dict[Text, Any]]
93
+ schemaDefs , # type: Dict[Text, Dict[Text, Any]]
94
+ names , # type: schema_salad.schema.Names
95
+ requirements , # type: List[Dict[Text, Any]]
96
+ hints , # type: List[Dict[Text, Any]]
97
+ timeout , # type: float
98
+ debug , # type: bool
99
+ resources , # type: Dict[Text, Union[int, Text, None]]
100
+ js_console , # type: bool
101
+ mutation_manager , # type: MutationManager
102
+ formatgraph , # type: Optional[Graph]
103
+ make_fs_access , # type: Type[StdFsAccess]
104
+ fs_access , # type: StdFsAccess
105
+ force_docker_pull , # type: bool
106
+ loadListing , # type: Text
107
+ outdir , # type: Text
108
+ tmpdir , # type: Text
109
+ stagedir , # type: Text
110
+ job_script_provider , # type: Optional[Any]
111
+ ): # type: (...) -> None
112
+ self .names = names
113
+ self .schemaDefs = schemaDefs
114
+ self .files = files
115
+ self .fs_access = fs_access
116
+ self .job = job
117
+ self .requirements = requirements
118
+ self .hints = hints
119
+ self .outdir = outdir
120
+ self .tmpdir = tmpdir
121
+ self .resources = resources
122
+ self .bindings = bindings
123
+ self .timeout = timeout
124
+ self .pathmapper = None # type: Optional[PathMapper]
125
+ self .stagedir = stagedir
126
+ self .make_fs_access = make_fs_access
127
+ self .debug = debug
128
+ self .js_console = js_console
129
+ self .mutation_manager = mutation_manager
130
+ self .force_docker_pull = force_docker_pull
131
+ self .formatgraph = formatgraph
108
132
109
133
# One of "no_listing", "shallow_listing", "deep_listing"
110
- # Will be default "no_listing" for CWL v1.1
111
- self .loadListing = "deep_listing" # type: Union[None, str]
134
+ self .loadListing = loadListing
112
135
113
- self .find_default_container = None # type: Callable[[], Text]
114
- self .job_script_provider = None # type: Any
136
+ self .find_default_container = None # type: Optional[ Callable[[], Text] ]
137
+ self .job_script_provider = job_script_provider
115
138
116
139
def build_job_script (self , commands ):
117
140
# type: (List[Text]) -> Text
@@ -121,14 +144,19 @@ def build_job_script(self, commands):
121
144
else :
122
145
return None
123
146
124
- def bind_input (self , schema , datum , lead_pos = None , tail_pos = None , discover_secondaryFiles = False ):
125
- # type: (Dict[Text, Any], Any, Union[int, List[int]], List[int], bool) -> List[Dict[Text, Any]]
147
+ def bind_input (self ,
148
+ schema , # type: Dict[Text, Any]
149
+ datum , # type: Any
150
+ discover_secondaryFiles , # type: bool
151
+ lead_pos = None , # type: Optional[Union[int, List[int]]]
152
+ tail_pos = None , # type: Optional[List[int]]
153
+ ): # type: (...) -> List[Dict[Text, Any]]
126
154
if tail_pos is None :
127
155
tail_pos = []
128
156
if lead_pos is None :
129
157
lead_pos = []
130
158
bindings = [] # type: List[Dict[Text,Text]]
131
- binding = None # type: Dict[Text,Any]
159
+ binding = None # type: Optional[ Dict[Text,Any] ]
132
160
value_from_expression = False
133
161
if "inputBinding" in schema and isinstance (schema ["inputBinding" ], dict ):
134
162
binding = copy .copy (schema ["inputBinding" ])
@@ -146,7 +174,7 @@ def bind_input(self, schema, datum, lead_pos=None, tail_pos=None, discover_secon
146
174
if isinstance (schema ["type" ], list ):
147
175
bound_input = False
148
176
for t in schema ["type" ]:
149
- if isinstance (t , ( str , Text ) ) and self .names .has_name (t , "" ):
177
+ if isinstance (t , string_types ) and self .names .has_name (t , "" ):
150
178
avsc = self .names .get_name (t , "" )
151
179
elif isinstance (t , dict ) and "name" in t and self .names .has_name (t ["name" ], "" ):
152
180
avsc = self .names .get_name (t ["name" ], "" )
@@ -238,9 +266,12 @@ def bind_input(self, schema, datum, lead_pos=None, tail_pos=None, discover_secon
238
266
239
267
if "format" in schema :
240
268
try :
241
- checkFormat (datum , self .do_eval (schema ["format" ]), self .formatgraph )
269
+ check_format (datum , self .do_eval (schema ["format" ]),
270
+ self .formatgraph )
242
271
except validate .ValidationException as ve :
243
- raise WorkflowException ("Expected value of '%s' to have format %s but\n %s" % (schema ["name" ], schema ["format" ], ve ))
272
+ raise WorkflowException (
273
+ "Expected value of '%s' to have format %s but\n "
274
+ " %s" % (schema ["name" ], schema ["format" ], ve ))
244
275
245
276
def _capture_files (f ):
246
277
self .files .append (f )
@@ -269,28 +300,32 @@ def tostr(self, value): # type: (Any) -> Text
269
300
270
301
# Path adjust for windows file path when passing to docker, docker accepts unix like path only
271
302
(docker_req , docker_is_req ) = get_feature (self , "DockerRequirement" )
272
- if onWindows () and docker_req is not None : # docker_req is none only when there is no dockerRequirement mentioned in hints and Requirement
273
- return docker_windows_path_adjust (value ["path" ])
303
+ if onWindows () and docker_req is not None :
304
+ # docker_req is none only when there is no dockerRequirement
305
+ # mentioned in hints and Requirement
306
+ path = docker_windows_path_adjust (value ["path" ])
307
+ assert path is not None
308
+ return path
274
309
return value ["path" ]
275
310
else :
276
311
return Text (value )
277
312
278
- def generate_arg (self , binding ): # type: (Dict[Text,Any]) -> List[Text]
313
+ def generate_arg (self , binding ): # type: (Dict[Text, Any]) -> List[Text]
279
314
value = binding .get ("datum" )
280
315
if "valueFrom" in binding :
281
316
with SourceLine (binding , "valueFrom" , WorkflowException , _logger .isEnabledFor (logging .DEBUG )):
282
317
value = self .do_eval (binding ["valueFrom" ], context = value )
283
318
284
- prefix = binding .get ("prefix" )
319
+ prefix = binding .get ("prefix" ) # type: Optional[Text]
285
320
sep = binding .get ("separate" , True )
286
321
if prefix is None and not sep :
287
322
with SourceLine (binding , "separate" , WorkflowException , _logger .isEnabledFor (logging .DEBUG )):
288
323
raise WorkflowException ("'separate' option can not be specified without prefix" )
289
324
290
- l = [] # type: List[Dict[Text,Text]]
325
+ argl = [] # type: List[Dict[Text,Text]]
291
326
if isinstance (value , list ):
292
327
if binding .get ("itemSeparator" ) and value :
293
- l = [binding ["itemSeparator" ].join ([self .tostr (v ) for v in value ])]
328
+ argl = [binding ["itemSeparator" ].join ([self .tostr (v ) for v in value ])]
294
329
elif binding .get ("valueFrom" ):
295
330
value = [self .tostr (v ) for v in value ]
296
331
return ([prefix ] if prefix else []) + value
@@ -299,36 +334,41 @@ def generate_arg(self, binding): # type: (Dict[Text,Any]) -> List[Text]
299
334
else :
300
335
return []
301
336
elif isinstance (value , dict ) and value .get ("class" ) in ("File" , "Directory" ):
302
- l = [value ]
337
+ argl = [value ]
303
338
elif isinstance (value , dict ):
304
339
return [prefix ] if prefix else []
305
340
elif value is True and prefix :
306
341
return [prefix ]
307
342
elif value is False or value is None or (value is True and not prefix ):
308
343
return []
309
344
else :
310
- l = [value ]
345
+ argl = [value ]
311
346
312
347
args = []
313
- for j in l :
348
+ for j in argl :
314
349
if sep :
315
350
args .extend ([prefix , self .tostr (j )])
316
351
else :
352
+ assert prefix is not None
317
353
args .append (prefix + self .tostr (j ))
318
354
319
355
return [a for a in args if a is not None ]
320
356
321
- def do_eval (self , ex , context = None , pull_image = True , recursive = False , strip_whitespace = True ):
322
- # type: (Union[Dict[Text, Text], Text], Any, bool, bool, bool ) -> Any
357
+ def do_eval (self , ex , context = None , recursive = False , strip_whitespace = True ):
358
+ # type: (Union[Dict[Text, Text], Text], Any, bool, bool) -> Any
323
359
if recursive :
324
360
if isinstance (ex , dict ):
325
- return {k : self .do_eval (v , context , pull_image , recursive ) for k , v in iteritems (ex )}
361
+ return {k : self .do_eval (v , context , recursive )
362
+ for k , v in iteritems (ex )}
326
363
if isinstance (ex , list ):
327
- return [self .do_eval (v , context , pull_image , recursive ) for v in ex ]
364
+ return [self .do_eval (v , context , recursive )
365
+ for v in ex ]
366
+ if context is None and isinstance (ex , string_types ) and "self" in ex :
367
+ return None
328
368
return expression .do_eval (ex , self .job , self .requirements ,
329
369
self .outdir , self .tmpdir ,
330
370
self .resources ,
331
- context = context , pull_image = pull_image ,
371
+ context = context ,
332
372
timeout = self .timeout ,
333
373
debug = self .debug ,
334
374
js_console = self .js_console ,
0 commit comments