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
+ class FSAction (argparse .Action ):
221
+ objclass = None # type: Text
222
+
223
+ def __init__ (self , option_strings , dest , nargs = None , ** kwargs ):
224
+ # type: (List[Text], Text, Any, **Any) -> None
225
+ if nargs is not None :
226
+ raise ValueError ("nargs not allowed" )
227
+ super (FSAction , self ).__init__ (option_strings , dest , ** kwargs )
228
+
229
+ def __call__ (self , parser , namespace , values , option_string = None ):
230
+ # type: (argparse.ArgumentParser, argparse.Namespace, Union[AnyStr, Sequence[Any], None], AnyStr) -> None
231
+ setattr (namespace ,
232
+ self .dest , # type: ignore
233
+ {"class" : self .objclass ,
234
+ "location" : file_uri (str (os .path .abspath (cast (AnyStr , values ))))})
235
+
236
+
237
+ class FSAppendAction (argparse .Action ):
238
+ objclass = None # type: Text
239
+
240
+ def __init__ (self , option_strings , dest , nargs = None , ** kwargs ):
241
+ # type: (List[Text], Text, Any, **Any) -> None
242
+ if nargs is not None :
243
+ raise ValueError ("nargs not allowed" )
244
+ super (FSAppendAction , self ).__init__ (option_strings , dest , ** kwargs )
245
+
246
+ def __call__ (self , parser , namespace , values , option_string = None ):
247
+ # type: (argparse.ArgumentParser, argparse.Namespace, Union[AnyStr, Sequence[Any], None], AnyStr) -> None
248
+ g = getattr (namespace ,
249
+ self .dest # type: ignore
250
+ )
251
+ if not g :
252
+ g = []
253
+ setattr (namespace ,
254
+ self .dest , # type: ignore
255
+ g )
256
+ g .append (
257
+ {"class" : self .objclass ,
258
+ "location" : file_uri (str (os .path .abspath (cast (AnyStr , values ))))})
259
+
260
+
261
+ class FileAction (FSAction ):
262
+ objclass = "File"
263
+
264
+
265
+ class DirectoryAction (FSAction ):
266
+ objclass = "Directory"
267
+
268
+
269
+ class FileAppendAction (FSAppendAction ):
270
+ objclass = "File"
271
+
272
+
273
+ class DirectoryAppendAction (FSAppendAction ):
274
+ objclass = "Directory"
275
+
276
+
277
+ def add_argument (toolparser , name , inptype , records , description = "" ,
278
+ default = None ):
279
+ # type: (argparse.ArgumentParser, Text, Any, List[Text], Text, Any) -> None
280
+ if len (name ) == 1 :
281
+ flag = "-"
282
+ else :
283
+ flag = "--"
284
+
285
+ required = True
286
+ if isinstance (inptype , list ):
287
+ if inptype [0 ] == "null" :
288
+ required = False
289
+ if len (inptype ) == 2 :
290
+ inptype = inptype [1 ]
291
+ else :
292
+ _logger .debug (u"Can't make command line argument from %s" , inptype )
293
+ return None
294
+
295
+ ahelp = description .replace ("%" , "%%" )
296
+ action = None # type: Union[argparse.Action, Text]
297
+ atype = None # type: Any
298
+
299
+ if inptype == "File" :
300
+ action = cast (argparse .Action , FileAction )
301
+ elif inptype == "Directory" :
302
+ action = cast (argparse .Action , DirectoryAction )
303
+ elif isinstance (inptype , dict ) and inptype ["type" ] == "array" :
304
+ if inptype ["items" ] == "File" :
305
+ action = cast (argparse .Action , FileAppendAction )
306
+ elif inptype ["items" ] == "Directory" :
307
+ action = cast (argparse .Action , DirectoryAppendAction )
308
+ else :
309
+ action = "append"
310
+ elif isinstance (inptype , dict ) and inptype ["type" ] == "enum" :
311
+ atype = Text
312
+ elif isinstance (inptype , dict ) and inptype ["type" ] == "record" :
313
+ records .append (name )
314
+ for field in inptype ['fields' ]:
315
+ fieldname = name + "." + shortname (field ['name' ])
316
+ fieldtype = field ['type' ]
317
+ fielddescription = field .get ("doc" , "" )
318
+ add_argument (
319
+ toolparser , fieldname , fieldtype , records ,
320
+ fielddescription )
321
+ return
322
+ if inptype == "string" :
323
+ atype = Text
324
+ elif inptype == "int" :
325
+ atype = int
326
+ elif inptype == "double" :
327
+ atype = float
328
+ elif inptype == "float" :
329
+ atype = float
330
+ elif inptype == "boolean" :
331
+ action = "store_true"
332
+
333
+ if default :
334
+ required = False
335
+
336
+ if not atype and not action :
337
+ _logger .debug (u"Can't make command line argument from %s" , inptype )
338
+ return None
339
+
340
+ if inptype != "boolean" :
341
+ typekw = {'type' : atype }
342
+ else :
343
+ typekw = {}
344
+
345
+ toolparser .add_argument ( # type: ignore
346
+ flag + name , required = required , help = ahelp , action = action ,
347
+ default = default , ** typekw )
348
+
349
+
350
+ def generate_parser (toolparser , tool , namemap , records ):
351
+ # type: (argparse.ArgumentParser, Process, Dict[Text, Text], List[Text]) -> argparse.ArgumentParser
352
+ toolparser .add_argument ("job_order" , nargs = "?" , help = "Job input json file" )
353
+ namemap ["job_order" ] = "job_order"
354
+
355
+ for inp in tool .tool ["inputs" ]:
356
+ name = shortname (inp ["id" ])
357
+ namemap [name .replace ("-" , "_" )] = name
358
+ inptype = inp ["type" ]
359
+ description = inp .get ("doc" , "" )
360
+ default = inp .get ("default" , None )
361
+ add_argument (toolparser , name , inptype , records , description , default )
362
+
363
+ return toolparser
0 commit comments