Skip to content

Commit c2a6d85

Browse files
author
Anton Khodak
committed
Move functions for parsing command line arguments to a separate file
1 parent 52c276d commit c2a6d85

File tree

2 files changed

+373
-360
lines changed

2 files changed

+373
-360
lines changed

cwltool/argparser.py

Lines changed: 363 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,363 @@
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

Comments
 (0)