1
+ from __future__ import absolute_import
2
+ from __future__ import print_function
3
+
4
+ import argparse
5
+ import logging
6
+ import os
7
+
8
+ from typing import (Any , AnyStr , Dict , List , Sequence , Text , Union , cast )
9
+
10
+ from schema_salad .ref_resolver import file_uri
11
+ from .process import (Process , shortname )
12
+ from .resolver import ga4gh_tool_registries
13
+ from .software_requirements import (SOFTWARE_REQUIREMENTS_ENABLED )
14
+
15
+ _logger = logging .getLogger ("cwltool" )
16
+
17
+ defaultStreamHandler = logging .StreamHandler ()
18
+ _logger .addHandler (defaultStreamHandler )
19
+ _logger .setLevel (logging .INFO )
20
+
21
+
22
+ def arg_parser (): # type: () -> argparse.ArgumentParser
23
+ parser = argparse .ArgumentParser (description = 'Reference executor for Common Workflow Language' )
24
+ parser .add_argument ("--basedir" , type = Text )
25
+ parser .add_argument ("--outdir" , type = Text , default = os .path .abspath ('.' ),
26
+ help = "Output directory, default current directory" )
27
+
28
+ parser .add_argument ("--no-container" , action = "store_false" , default = True ,
29
+ help = "Do not execute jobs in a Docker container, even when specified by the CommandLineTool" ,
30
+ dest = "use_container" )
31
+
32
+ parser .add_argument ("--preserve-environment" , type = Text , action = "append" ,
33
+ help = "Preserve specific environment variable when running CommandLineTools. May be provided multiple times." ,
34
+ metavar = "ENVVAR" ,
35
+ default = ["PATH" ],
36
+ dest = "preserve_environment" )
37
+
38
+ parser .add_argument ("--preserve-entire-environment" , action = "store_true" ,
39
+ help = "Preserve entire parent environment when running CommandLineTools." ,
40
+ default = False ,
41
+ dest = "preserve_entire_environment" )
42
+
43
+ exgroup = parser .add_mutually_exclusive_group ()
44
+ exgroup .add_argument ("--rm-container" , action = "store_true" , default = True ,
45
+ help = "Delete Docker container used by jobs after they exit (default)" ,
46
+ dest = "rm_container" )
47
+
48
+ exgroup .add_argument ("--leave-container" , action = "store_false" ,
49
+ default = True , help = "Do not delete Docker container used by jobs after they exit" ,
50
+ dest = "rm_container" )
51
+
52
+ parser .add_argument ("--tmpdir-prefix" , type = Text ,
53
+ help = "Path prefix for temporary directories" ,
54
+ default = "tmp" )
55
+
56
+ exgroup = parser .add_mutually_exclusive_group ()
57
+ exgroup .add_argument ("--tmp-outdir-prefix" , type = Text ,
58
+ help = "Path prefix for intermediate output directories" ,
59
+ default = "tmp" )
60
+
61
+ exgroup .add_argument ("--cachedir" , type = Text , default = "" ,
62
+ help = "Directory to cache intermediate workflow outputs to avoid recomputing steps." )
63
+
64
+ exgroup = parser .add_mutually_exclusive_group ()
65
+ exgroup .add_argument ("--rm-tmpdir" , action = "store_true" , default = True ,
66
+ help = "Delete intermediate temporary directories (default)" ,
67
+ dest = "rm_tmpdir" )
68
+
69
+ exgroup .add_argument ("--leave-tmpdir" , action = "store_false" ,
70
+ default = True , help = "Do not delete intermediate temporary directories" ,
71
+ dest = "rm_tmpdir" )
72
+
73
+ exgroup = parser .add_mutually_exclusive_group ()
74
+ exgroup .add_argument ("--move-outputs" , action = "store_const" , const = "move" , default = "move" ,
75
+ help = "Move output files to the workflow output directory and delete intermediate output directories (default)." ,
76
+ dest = "move_outputs" )
77
+
78
+ exgroup .add_argument ("--leave-outputs" , action = "store_const" , const = "leave" , default = "move" ,
79
+ help = "Leave output files in intermediate output directories." ,
80
+ dest = "move_outputs" )
81
+
82
+ exgroup .add_argument ("--copy-outputs" , action = "store_const" , const = "copy" , default = "move" ,
83
+ help = "Copy output files to the workflow output directory, don't delete intermediate output directories." ,
84
+ dest = "move_outputs" )
85
+
86
+ exgroup = parser .add_mutually_exclusive_group ()
87
+ exgroup .add_argument ("--enable-pull" , default = True , action = "store_true" ,
88
+ help = "Try to pull Docker images" , dest = "enable_pull" )
89
+
90
+ exgroup .add_argument ("--disable-pull" , default = True , action = "store_false" ,
91
+ help = "Do not try to pull Docker images" , dest = "enable_pull" )
92
+
93
+ parser .add_argument ("--rdf-serializer" ,
94
+ help = "Output RDF serialization format used by --print-rdf (one of turtle (default), n3, nt, xml)" ,
95
+ default = "turtle" )
96
+
97
+ parser .add_argument ("--eval-timeout" ,
98
+ help = "Time to wait for a Javascript expression to evaluate before giving an error, default 20s." ,
99
+ type = float ,
100
+ default = 20 )
101
+
102
+ exgroup = parser .add_mutually_exclusive_group ()
103
+ exgroup .add_argument ("--print-rdf" , action = "store_true" ,
104
+ help = "Print corresponding RDF graph for workflow and exit" )
105
+ exgroup .add_argument ("--print-dot" , action = "store_true" ,
106
+ help = "Print workflow visualization in graphviz format and exit" )
107
+ exgroup .add_argument ("--print-pre" , action = "store_true" , help = "Print CWL document after preprocessing." )
108
+ exgroup .add_argument ("--print-deps" , action = "store_true" , help = "Print CWL document dependencies." )
109
+ exgroup .add_argument ("--print-input-deps" , action = "store_true" , help = "Print input object document dependencies." )
110
+ exgroup .add_argument ("--pack" , action = "store_true" , help = "Combine components into single document and print." )
111
+ exgroup .add_argument ("--version" , action = "store_true" , help = "Print version and exit" )
112
+ exgroup .add_argument ("--validate" , action = "store_true" , help = "Validate CWL document only." )
113
+ exgroup .add_argument ("--print-supported-versions" , action = "store_true" , help = "Print supported CWL specs." )
114
+
115
+ exgroup = parser .add_mutually_exclusive_group ()
116
+ exgroup .add_argument ("--strict" , action = "store_true" ,
117
+ help = "Strict validation (unrecognized or out of place fields are error)" ,
118
+ default = True , dest = "strict" )
119
+ exgroup .add_argument ("--non-strict" , action = "store_false" , help = "Lenient validation (ignore unrecognized fields)" ,
120
+ default = True , dest = "strict" )
121
+
122
+ parser .add_argument ("--skip-schemas" , action = "store_true" ,
123
+ help = "Skip loading of schemas" , default = True , dest = "skip_schemas" )
124
+
125
+ exgroup = parser .add_mutually_exclusive_group ()
126
+ exgroup .add_argument ("--verbose" , action = "store_true" , help = "Default logging" )
127
+ exgroup .add_argument ("--quiet" , action = "store_true" , help = "Only print warnings and errors." )
128
+ exgroup .add_argument ("--debug" , action = "store_true" , help = "Print even more logging" )
129
+
130
+ parser .add_argument ("--js-console" , action = "store_true" , help = "Enable javascript console output" )
131
+ parser .add_argument ("--user-space-docker-cmd" ,
132
+ help = "(Linux/OS X only) Specify a user space docker "
133
+ "command (like udocker or dx-docker) that will be "
134
+ "used to call 'pull' and 'run'" )
135
+
136
+ dependency_resolvers_configuration_help = argparse .SUPPRESS
137
+ dependencies_directory_help = argparse .SUPPRESS
138
+ use_biocontainers_help = argparse .SUPPRESS
139
+ conda_dependencies = argparse .SUPPRESS
140
+
141
+ if SOFTWARE_REQUIREMENTS_ENABLED :
142
+ dependency_resolvers_configuration_help = "Dependency resolver configuration file describing how to adapt 'SoftwareRequirement' packages to current system."
143
+ dependencies_directory_help = "Defaut root directory used by dependency resolvers configuration."
144
+ use_biocontainers_help = "Use biocontainers for tools without an explicitly annotated Docker container."
145
+ conda_dependencies = "Short cut to use Conda to resolve 'SoftwareRequirement' packages."
146
+
147
+ parser .add_argument ("--beta-dependency-resolvers-configuration" , default = None , help = dependency_resolvers_configuration_help )
148
+ parser .add_argument ("--beta-dependencies-directory" , default = None , help = dependencies_directory_help )
149
+ parser .add_argument ("--beta-use-biocontainers" , default = None , help = use_biocontainers_help , action = "store_true" )
150
+ parser .add_argument ("--beta-conda-dependencies" , default = None , help = conda_dependencies , action = "store_true" )
151
+
152
+ parser .add_argument ("--tool-help" , action = "store_true" , help = "Print command line help for tool" )
153
+
154
+ parser .add_argument ("--relative-deps" , choices = ['primary' , 'cwd' ],
155
+ default = "primary" , help = "When using --print-deps, print paths "
156
+ "relative to primary file or current working directory." )
157
+
158
+ parser .add_argument ("--enable-dev" , action = "store_true" ,
159
+ help = "Enable loading and running development versions "
160
+ "of CWL spec." , default = False )
161
+
162
+ parser .add_argument ("--enable-ext" , action = "store_true" ,
163
+ help = "Enable loading and running cwltool extensions "
164
+ "to CWL spec." , default = False )
165
+
166
+ parser .add_argument ("--default-container" ,
167
+ help = "Specify a default docker container that will be used if the workflow fails to specify one." )
168
+ parser .add_argument ("--no-match-user" , action = "store_true" ,
169
+ help = "Disable passing the current uid to 'docker run --user`" )
170
+ parser .add_argument ("--disable-net" , action = "store_true" ,
171
+ help = "Use docker's default networking for containers;"
172
+ " the default is to enable networking." )
173
+ parser .add_argument ("--custom-net" , type = Text ,
174
+ help = "Will be passed to `docker run` as the '--net' "
175
+ "parameter. Implies '--enable-net'." )
176
+
177
+ exgroup = parser .add_mutually_exclusive_group ()
178
+ exgroup .add_argument ("--enable-ga4gh-tool-registry" , action = "store_true" , help = "Enable resolution using GA4GH tool registry API" ,
179
+ dest = "enable_ga4gh_tool_registry" , default = True )
180
+ exgroup .add_argument ("--disable-ga4gh-tool-registry" , action = "store_false" , help = "Disable resolution using GA4GH tool registry API" ,
181
+ dest = "enable_ga4gh_tool_registry" , default = True )
182
+
183
+ parser .add_argument ("--add-ga4gh-tool-registry" , action = "append" , help = "Add a GA4GH tool registry endpoint to use for resolution, default %s" % ga4gh_tool_registries ,
184
+ dest = "ga4gh_tool_registries" , default = [])
185
+
186
+ parser .add_argument ("--on-error" ,
187
+ help = "Desired workflow behavior when a step fails. One of 'stop' or 'continue'. "
188
+ "Default is 'stop'." , default = "stop" , choices = ("stop" , "continue" ))
189
+
190
+ exgroup = parser .add_mutually_exclusive_group ()
191
+ exgroup .add_argument ("--compute-checksum" , action = "store_true" , default = True ,
192
+ help = "Compute checksum of contents while collecting outputs" ,
193
+ dest = "compute_checksum" )
194
+ exgroup .add_argument ("--no-compute-checksum" , action = "store_false" ,
195
+ help = "Do not compute checksum of contents while collecting outputs" ,
196
+ dest = "compute_checksum" )
197
+
198
+ parser .add_argument ("--relax-path-checks" , action = "store_true" ,
199
+ default = False , help = "Relax requirements on path names to permit "
200
+ "spaces and hash characters." , dest = "relax_path_checks" )
201
+ exgroup .add_argument ("--make-template" , action = "store_true" ,
202
+ help = "Generate a template input object" )
203
+
204
+ parser .add_argument ("--force-docker-pull" , action = "store_true" ,
205
+ default = False , help = "Pull latest docker image even if"
206
+ " it is locally present" , dest = "force_docker_pull" )
207
+ parser .add_argument ("--no-read-only" , action = "store_true" ,
208
+ default = False , help = "Do not set root directory in the"
209
+ " container as read-only" , dest = "no_read_only" )
210
+
211
+ parser .add_argument ("--overrides" , type = str ,
212
+ default = None , help = "Read process requirement overrides from file." )
213
+
214
+ parser .add_argument ("workflow" , type = Text , nargs = "?" , default = None )
215
+ parser .add_argument ("job_order" , nargs = argparse .REMAINDER )
216
+
217
+ return parser
218
+
219
+
220
+ def get_default_args ():
221
+ # type: () -> Dict[str, Any]
222
+ """
223
+ Get default values of cwltool's command line options
224
+ """
225
+ ap = arg_parser ()
226
+ args = ap .parse_args ()
227
+ return vars (args )
228
+
229
+
230
+ class FSAction (argparse .Action ):
231
+ objclass = None # type: Text
232
+
233
+ def __init__ (self , option_strings , dest , nargs = None , ** kwargs ):
234
+ # type: (List[Text], Text, Any, **Any) -> None
235
+ if nargs is not None :
236
+ raise ValueError ("nargs not allowed" )
237
+ super (FSAction , self ).__init__ (option_strings , dest , ** kwargs )
238
+
239
+ def __call__ (self , parser , namespace , values , option_string = None ):
240
+ # type: (argparse.ArgumentParser, argparse.Namespace, Union[AnyStr, Sequence[Any], None], AnyStr) -> None
241
+ setattr (namespace ,
242
+ self .dest , # type: ignore
243
+ {"class" : self .objclass ,
244
+ "location" : file_uri (str (os .path .abspath (cast (AnyStr , values ))))})
245
+
246
+
247
+ class FSAppendAction (argparse .Action ):
248
+ objclass = None # type: Text
249
+
250
+ def __init__ (self , option_strings , dest , nargs = None , ** kwargs ):
251
+ # type: (List[Text], Text, Any, **Any) -> None
252
+ if nargs is not None :
253
+ raise ValueError ("nargs not allowed" )
254
+ super (FSAppendAction , self ).__init__ (option_strings , dest , ** kwargs )
255
+
256
+ def __call__ (self , parser , namespace , values , option_string = None ):
257
+ # type: (argparse.ArgumentParser, argparse.Namespace, Union[AnyStr, Sequence[Any], None], AnyStr) -> None
258
+ g = getattr (namespace ,
259
+ self .dest # type: ignore
260
+ )
261
+ if not g :
262
+ g = []
263
+ setattr (namespace ,
264
+ self .dest , # type: ignore
265
+ g )
266
+ g .append (
267
+ {"class" : self .objclass ,
268
+ "location" : file_uri (str (os .path .abspath (cast (AnyStr , values ))))})
269
+
270
+
271
+ class FileAction (FSAction ):
272
+ objclass = "File"
273
+
274
+
275
+ class DirectoryAction (FSAction ):
276
+ objclass = "Directory"
277
+
278
+
279
+ class FileAppendAction (FSAppendAction ):
280
+ objclass = "File"
281
+
282
+
283
+ class DirectoryAppendAction (FSAppendAction ):
284
+ objclass = "Directory"
285
+
286
+
287
+ def add_argument (toolparser , name , inptype , records , description = "" ,
288
+ default = None ):
289
+ # type: (argparse.ArgumentParser, Text, Any, List[Text], Text, Any) -> None
290
+ if len (name ) == 1 :
291
+ flag = "-"
292
+ else :
293
+ flag = "--"
294
+
295
+ required = True
296
+ if isinstance (inptype , list ):
297
+ if inptype [0 ] == "null" :
298
+ required = False
299
+ if len (inptype ) == 2 :
300
+ inptype = inptype [1 ]
301
+ else :
302
+ _logger .debug (u"Can't make command line argument from %s" , inptype )
303
+ return None
304
+
305
+ ahelp = description .replace ("%" , "%%" )
306
+ action = None # type: Union[argparse.Action, Text]
307
+ atype = None # type: Any
308
+
309
+ if inptype == "File" :
310
+ action = cast (argparse .Action , FileAction )
311
+ elif inptype == "Directory" :
312
+ action = cast (argparse .Action , DirectoryAction )
313
+ elif isinstance (inptype , dict ) and inptype ["type" ] == "array" :
314
+ if inptype ["items" ] == "File" :
315
+ action = cast (argparse .Action , FileAppendAction )
316
+ elif inptype ["items" ] == "Directory" :
317
+ action = cast (argparse .Action , DirectoryAppendAction )
318
+ else :
319
+ action = "append"
320
+ elif isinstance (inptype , dict ) and inptype ["type" ] == "enum" :
321
+ atype = Text
322
+ elif isinstance (inptype , dict ) and inptype ["type" ] == "record" :
323
+ records .append (name )
324
+ for field in inptype ['fields' ]:
325
+ fieldname = name + "." + shortname (field ['name' ])
326
+ fieldtype = field ['type' ]
327
+ fielddescription = field .get ("doc" , "" )
328
+ add_argument (
329
+ toolparser , fieldname , fieldtype , records ,
330
+ fielddescription )
331
+ return
332
+ if inptype == "string" :
333
+ atype = Text
334
+ elif inptype == "int" :
335
+ atype = int
336
+ elif inptype == "double" :
337
+ atype = float
338
+ elif inptype == "float" :
339
+ atype = float
340
+ elif inptype == "boolean" :
341
+ action = "store_true"
342
+
343
+ if default :
344
+ required = False
345
+
346
+ if not atype and not action :
347
+ _logger .debug (u"Can't make command line argument from %s" , inptype )
348
+ return None
349
+
350
+ if inptype != "boolean" :
351
+ typekw = {'type' : atype }
352
+ else :
353
+ typekw = {}
354
+
355
+ toolparser .add_argument ( # type: ignore
356
+ flag + name , required = required , help = ahelp , action = action ,
357
+ default = default , ** typekw )
358
+
359
+
360
+ def generate_parser (toolparser , tool , namemap , records ):
361
+ # type: (argparse.ArgumentParser, Process, Dict[Text, Text], List[Text]) -> argparse.ArgumentParser
362
+ toolparser .add_argument ("job_order" , nargs = "?" , help = "Job input json file" )
363
+ namemap ["job_order" ] = "job_order"
364
+
365
+ for inp in tool .tool ["inputs" ]:
366
+ name = shortname (inp ["id" ])
367
+ namemap [name .replace ("-" , "_" )] = name
368
+ inptype = inp ["type" ]
369
+ description = inp .get ("doc" , "" )
370
+ default = inp .get ("default" , None )
371
+ add_argument (toolparser , name , inptype , records , description , default )
372
+
373
+ return toolparser
0 commit comments