Skip to content

Commit b3f03a6

Browse files
committed
Implement Dependency hint to describe tool dependencies.
1 parent ef2e818 commit b3f03a6

File tree

4 files changed

+76
-5
lines changed

4 files changed

+76
-5
lines changed

cwltool/draft2tool.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ def rm_pending_output_callback(output_callback, jobcachepending,
203203
reffiles = set((f["path"] for f in builder.files))
204204

205205
j = self.makeJobRunner()
206+
j.tool_dependency_manager = self.tool_dependency_manager
206207
j.builder = builder
207208
j.joborder = builder.job
208209
j.stdin = None

cwltool/job.py

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,16 @@
2121
from typing import Union, Iterable, Callable, Any, Mapping, IO, cast, Tuple
2222
from .pathmapper import PathMapper
2323
import functools
24+
try:
25+
from galaxy.tools.deps.requirements import ToolRequirement
26+
except ImportError:
27+
ToolRequirement = None
2428

2529
_logger = logging.getLogger("cwltool")
2630

2731
needs_shell_quoting_re = re.compile(r"""(^$|[\s|&;()<>\'"$@])""")
2832

29-
FORCE_SHELLED_POPEN = os.getenv("CWLTOOL_FORCE_SHELL_POPEN", "1") == "1"
33+
FORCE_SHELLED_POPEN = os.getenv("CWLTOOL_FORCE_SHELL_POPEN", "0") == "1"
3034

3135

3236
def deref_links(outputs): # type: (Any) -> None
@@ -63,6 +67,7 @@ def __init__(self): # type: () -> None
6367
self.tmpdir = None # type: str
6468
self.environment = None # type: Dict[str,str]
6569
self.generatefiles = None # type: Dict[str,Union[Dict[str,str],str]]
70+
self.dependency_manager = None # type: DependencyManager
6671

6772
def run(self, dry_run=False, pull_image=True, rm_container=True,
6873
rm_tmpdir=True, move_outputs=True, **kwargs):
@@ -191,6 +196,17 @@ def run(self, dry_run=False, pull_image=True, rm_container=True,
191196
else:
192197
stdout_path = None
193198

199+
prefix = None
200+
job_dir = None
201+
if self.tool_dependency_manager is not None:
202+
dependencies = self._find_tool_dependencies()
203+
job_dir = tempfile.mkdtemp(prefix="cwltooljob")
204+
shell_commands = self.tool_dependency_manager.dependency_shell_commands(
205+
dependencies,
206+
job_directory=job_dir,
207+
)
208+
prefix = "\n".join(shell_commands)
209+
194210
rcode = shelled_popen(
195211
[str(x) for x in runtime + self.command_line],
196212
stdin_path=stdin_path,
@@ -252,6 +268,23 @@ def run(self, dry_run=False, pull_image=True, rm_container=True,
252268
_logger.debug(u"[job %s] Removing empty output directory %s", self.name, self.outdir)
253269
shutil.rmtree(self.outdir, True)
254270

271+
def _find_tool_dependencies(self):
272+
dependencies = []
273+
for hint in self.hints:
274+
hint_class = hint.get("class", "")
275+
if not hint_class:
276+
continue
277+
base_name = hint["class"].rsplit("/", 1)[-1]
278+
if base_name == "Dependency":
279+
requirement_desc = {}
280+
requirement_desc["type"] = "package"
281+
name = hint["name"].rsplit("#", 1)[-1]
282+
version = hint.get("version", "").rsplit("#", 1)[-1]
283+
requirement_desc["name"] = name
284+
requirement_desc["version"] = version or None
285+
dependencies.append(ToolRequirement.from_dict(requirement_desc))
286+
return dependencies
287+
255288

256289
SHELL_COMMAND_TEMPLATE = string.Template("""#!/bin/bash
257290
$prefix
@@ -308,6 +341,7 @@ def shelled_popen(commands,
308341
stdout_path,
309342
env,
310343
cwd,
344+
job_dir=None,
311345
prefix=None):
312346
if prefix is None and not FORCE_SHELLED_POPEN:
313347
if stdin_path is not None:
@@ -341,17 +375,19 @@ def shelled_popen(commands,
341375

342376
return rcode
343377
else:
378+
if job_dir is None:
379+
job_dir = tempfile.mkdtemp(prefix="cwltooljob")
380+
344381
template_kwds = dict(
345382
prefix=prefix or '',
346383
)
347384
job_script_contents = SHELL_COMMAND_TEMPLATE.substitute(
348385
**template_kwds
349386
)
350-
job_dir = tempfile.mkdtemp(prefix="cwltooljob")
351387
job_description = dict(
352388
commands=commands,
353389
cwd=cwd,
354-
env=env,
390+
env=env.copy(),
355391
stdout_path=stdout_path,
356392
stdin_path=stdin_path,
357393
)

cwltool/main.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,20 @@
2525
import rdflib
2626
import hashlib
2727
from typing import Union, Any, cast, Callable, Dict, Tuple, IO
28+
try:
29+
from galaxy.tools import deps
30+
except ImportError:
31+
deps = None
2832

2933
_logger = logging.getLogger("cwltool")
3034

3135
defaultStreamHandler = logging.StreamHandler()
3236
_logger.addHandler(defaultStreamHandler)
3337
_logger.setLevel(logging.INFO)
3438

39+
if deps is not None:
40+
deps.log = _logger
41+
3542

3643
def arg_parser(): # type: () -> argparse.ArgumentParser
3744
parser = argparse.ArgumentParser(description='Reference executor for Common Workflow Language')
@@ -129,6 +136,9 @@ def arg_parser(): # type: () -> argparse.ArgumentParser
129136
exgroup.add_argument("--quiet", action="store_true", help="Only print warnings and errors.")
130137
exgroup.add_argument("--debug", action="store_true", help="Print even more logging")
131138

139+
parser.add_argument("--dependency-resolvers-configuration", help="Dependency resolver configuration file describing how to adapt 'Dependency' hints to current system.", default=None)
140+
parser.add_argument("--dependencies-directory", help="Defaut root directory used by dependency resolvers configuration.", default=None)
141+
132142
parser.add_argument("--tool-help", action="store_true", help="Print command line help for tool")
133143

134144
parser.add_argument("--relative-deps", choices=['primary', 'cwd'], default="primary",
@@ -544,6 +554,12 @@ def main(argsl=None,
544554
if not hasattr(args, k):
545555
setattr(args, k, v)
546556

557+
if deps is not None:
558+
tool_dependencies_configuartion = DependenciesConfigruation(args)
559+
tool_dependency_manager = deps.build_dependency_manager(tool_dependencies_configuartion)
560+
else:
561+
tool_dependency_manager = None
562+
547563
if args.quiet:
548564
_logger.setLevel(logging.WARN)
549565
if args.debug:
@@ -588,8 +604,11 @@ def main(argsl=None,
588604
printdot(uri, processobj, document_loader.ctx, stdout)
589605
return 0
590606

607+
make_tool_kwargs = {
608+
'tool_dependency_manager': tool_dependency_manager,
609+
}
591610
tool = make_tool(document_loader, avsc_names, processobj, metadata,
592-
uri, makeTool, {})
611+
uri, makeTool, make_tool_kwargs)
593612
except (validate.ValidationException) as exc:
594613
_logger.error(u"Tool definition failed validation:\n%s", exc,
595614
exc_info=(exc if args.debug else False))
@@ -676,5 +695,20 @@ def main(argsl=None,
676695
_logger.removeHandler(stderr_handler)
677696
_logger.addHandler(defaultStreamHandler)
678697

698+
699+
class DependenciesConfigruation(object):
700+
701+
def __init__(self, args):
702+
conf_file = getattr(args, "dependency_resolvers_configuration", None)
703+
tool_dependency_dir = getattr(args, "dependencies_directory", None)
704+
if conf_file is not None and os.path.exists(conf_file):
705+
self.use_tool_dependencies = True
706+
if not tool_dependency_dir:
707+
tool_dependency_dir = os.path.abspath(os.path.dirname(conf_file))
708+
self.tool_dependency_dir = tool_dependency_dir
709+
self.dependency_resolvers_config_file = conf_file
710+
else:
711+
self.use_tool_dependencies = False
712+
679713
if __name__ == "__main__":
680714
sys.exit(main(sys.argv[1:]))

cwltool/process.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ def __init__(self, toolpath_object, **kwargs):
295295
avro.schema.make_avsc_object(self.outputs_record_schema, self.names)
296296
except avro.schema.SchemaParseException as e:
297297
raise validate.ValidationException(u"Got error `%s` while prcoessing outputs of %s:\n%s" % (str(e), self.tool["id"], json.dumps(self.outputs_record_schema, indent=4)))
298-
298+
self.tool_dependency_manager = kwargs.get("tool_dependency_manager", None)
299299

300300
def _init_job(self, joborder, **kwargs):
301301
# type: (Dict[str, str], str, **Any) -> Builder

0 commit comments

Comments
 (0)