Skip to content

Commit 4c7cf7d

Browse files
authored
Merge branch 'master' into fix-648
2 parents ccdbeed + 14c93c6 commit 4c7cf7d

File tree

5 files changed

+402
-362
lines changed

5 files changed

+402
-362
lines changed

cwltool/argparser.py

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

cwltool/factory.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from typing import Any, Dict, Text, Tuple, Union
55

66
from . import load_tool, main, workflow
7+
from .argparser import get_default_args
78
from .process import Process
89

910

@@ -41,7 +42,13 @@ def __init__(self,
4142
# type: (...) -> None
4243
self.makeTool = makeTool
4344
self.executor = executor
44-
self.execkwargs = execkwargs
45+
46+
kwargs = get_default_args()
47+
kwargs.pop("job_order")
48+
kwargs.pop("workflow")
49+
kwargs.pop("outdir")
50+
kwargs.update(execkwargs)
51+
self.execkwargs = kwargs
4552

4653
def make(self, cwl):
4754
"""Instantiate a CWL object from a CWl document."""

0 commit comments

Comments
 (0)