Skip to content

Commit 5c8d8d5

Browse files
committed
Implement Dependency hint to describe tool dependencies.
1 parent cec71f7 commit 5c8d8d5

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[u"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
@@ -64,6 +68,7 @@ def __init__(self): # type: () -> None
6468
self.tmpdir = None # type: str
6569
self.environment = None # type: Dict[str,str]
6670
self.generatefiles = None # type: Dict[str,Union[Dict[str,str],str]]
71+
self.dependency_manager = None # type: DependencyManager
6772

6873
def run(self, dry_run=False, pull_image=True, rm_container=True,
6974
rm_tmpdir=True, move_outputs=True, **kwargs):
@@ -203,6 +208,17 @@ def run(self, dry_run=False, pull_image=True, rm_container=True,
203208
else:
204209
stdout_path = None
205210

211+
prefix = None
212+
job_dir = None
213+
if self.tool_dependency_manager is not None:
214+
dependencies = self._find_tool_dependencies()
215+
job_dir = tempfile.mkdtemp(prefix="cwltooljob")
216+
shell_commands = self.tool_dependency_manager.dependency_shell_commands(
217+
dependencies,
218+
job_directory=job_dir,
219+
)
220+
prefix = "\n".join(shell_commands)
221+
206222
rcode = shelled_popen(
207223
[str(x) for x in runtime + self.command_line],
208224
stdin_path=stdin_path,
@@ -265,6 +281,23 @@ def run(self, dry_run=False, pull_image=True, rm_container=True,
265281
_logger.debug(u"[job %s] Removing empty output directory %s", self.name, self.outdir)
266282
shutil.rmtree(self.outdir, True)
267283

284+
def _find_tool_dependencies(self):
285+
dependencies = []
286+
for hint in self.hints:
287+
hint_class = hint.get("class", "")
288+
if not hint_class:
289+
continue
290+
base_name = hint["class"].rsplit("/", 1)[-1]
291+
if base_name == "Dependency":
292+
requirement_desc = {}
293+
requirement_desc["type"] = "package"
294+
name = hint["name"].rsplit("#", 1)[-1]
295+
version = hint.get("version", "").rsplit("#", 1)[-1]
296+
requirement_desc["name"] = name
297+
requirement_desc["version"] = version or None
298+
dependencies.append(ToolRequirement.from_dict(requirement_desc))
299+
return dependencies
300+
268301

269302
SHELL_COMMAND_TEMPLATE = string.Template("""#!/bin/bash
270303
$prefix
@@ -331,6 +364,7 @@ def shelled_popen(commands,
331364
stderr_path,
332365
env,
333366
cwd,
367+
job_dir=None,
334368
prefix=None):
335369
if prefix is None and not FORCE_SHELLED_POPEN:
336370
if stdin_path is not None:
@@ -373,17 +407,19 @@ def shelled_popen(commands,
373407

374408
return rcode
375409
else:
410+
if job_dir is None:
411+
job_dir = tempfile.mkdtemp(prefix="cwltooljob")
412+
376413
template_kwds = dict(
377414
prefix=prefix or '',
378415
)
379416
job_script_contents = SHELL_COMMAND_TEMPLATE.substitute(
380417
**template_kwds
381418
)
382-
job_dir = tempfile.mkdtemp(prefix="cwltooljob")
383419
job_description = dict(
384420
commands=commands,
385421
cwd=cwd,
386-
env=env,
422+
env=env.copy(),
387423
stdout_path=stdout_path,
388424
stderr_path=stderr_path,
389425
stdin_path=stdin_path,

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",
@@ -552,6 +562,12 @@ def main(argsl=None,
552562
if not hasattr(args, k):
553563
setattr(args, k, v)
554564

565+
if deps is not None:
566+
tool_dependencies_configuartion = DependenciesConfigruation(args)
567+
tool_dependency_manager = deps.build_dependency_manager(tool_dependencies_configuartion)
568+
else:
569+
tool_dependency_manager = None
570+
555571
if args.quiet:
556572
_logger.setLevel(logging.WARN)
557573
if args.debug:
@@ -596,8 +612,11 @@ def main(argsl=None,
596612
printdot(uri, processobj, document_loader.ctx, stdout)
597613
return 0
598614

615+
make_tool_kwargs = {
616+
'tool_dependency_manager': tool_dependency_manager,
617+
}
599618
tool = make_tool(document_loader, avsc_names, metadata, uri,
600-
makeTool, {})
619+
makeTool, make_tool_kwargs)
601620
except (validate.ValidationException) as exc:
602621
_logger.error(u"Tool definition failed validation:\n%s", exc,
603622
exc_info=(exc if args.debug else False))
@@ -691,5 +710,20 @@ def main(argsl=None,
691710
_logger.removeHandler(stderr_handler)
692711
_logger.addHandler(defaultStreamHandler)
693712

713+
714+
class DependenciesConfigruation(object):
715+
716+
def __init__(self, args):
717+
conf_file = getattr(args, "dependency_resolvers_configuration", None)
718+
tool_dependency_dir = getattr(args, "dependencies_directory", None)
719+
if conf_file is not None and os.path.exists(conf_file):
720+
self.use_tool_dependencies = True
721+
if not tool_dependency_dir:
722+
tool_dependency_dir = os.path.abspath(os.path.dirname(conf_file))
723+
self.tool_dependency_dir = tool_dependency_dir
724+
self.dependency_resolvers_config_file = conf_file
725+
else:
726+
self.use_tool_dependencies = False
727+
694728
if __name__ == "__main__":
695729
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[unicode, unicode], **Any) -> Builder

0 commit comments

Comments
 (0)