Skip to content

Commit 2492977

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

File tree

4 files changed

+67
-3
lines changed

4 files changed

+67
-3
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: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
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

@@ -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+
prefix, base_name = hint["class"].rsplit("/", 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,13 +375,15 @@ 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,

cwltool/main.py

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

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

@@ -129,6 +133,8 @@ def arg_parser(): # type: () -> argparse.ArgumentParser
129133
exgroup.add_argument("--quiet", action="store_true", help="Only print warnings and errors.")
130134
exgroup.add_argument("--debug", action="store_true", help="Print even more logging")
131135

136+
parser.add_argument("--dependency-resolvers-configuration", help="Dependency resolver configuration file describing how to adapt 'Dependency' hints to current system.")
137+
132138
parser.add_argument("--tool-help", action="store_true", help="Print command line help for tool")
133139

134140
parser.add_argument("--relative-deps", choices=['primary', 'cwd'], default="primary",
@@ -544,6 +550,12 @@ def main(argsl=None,
544550
if not hasattr(args, k):
545551
setattr(args, k, v)
546552

553+
if build_dependency_manager is not None:
554+
tool_dependencies_configuartion = DependenciesConfigruation(args)
555+
tool_dependency_manager = build_dependency_manager(tool_dependencies_configuartion)
556+
else:
557+
tool_dependency_manager = None
558+
547559
if args.quiet:
548560
_logger.setLevel(logging.WARN)
549561
if args.debug:
@@ -588,8 +600,11 @@ def main(argsl=None,
588600
printdot(uri, processobj, document_loader.ctx, stdout)
589601
return 0
590602

603+
make_tool_kwargs = {
604+
'tool_dependency_manager': tool_dependency_manager,
605+
}
591606
tool = make_tool(document_loader, avsc_names, processobj, metadata,
592-
uri, makeTool, {})
607+
uri, makeTool, make_tool_kwargs)
593608
except (validate.ValidationException) as exc:
594609
_logger.error(u"Tool definition failed validation:\n%s", exc,
595610
exc_info=(exc if args.debug else False))
@@ -676,5 +691,17 @@ def main(argsl=None,
676691
_logger.removeHandler(stderr_handler)
677692
_logger.addHandler(defaultStreamHandler)
678693

694+
695+
class DependenciesConfigruation(object):
696+
697+
def __init__(self, args):
698+
conf_file = getattr(args, "dependency_resolvers_configuration", None)
699+
if os.path.exists(conf_file):
700+
self.use_tool_dependencies = True
701+
self.tool_dependency_dir = os.path.dirname(conf_file)
702+
self.dependency_resolvers_config_file = conf_file
703+
else:
704+
self.use_tool_dependencies = False
705+
679706
if __name__ == "__main__":
680707
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)