Source code for cwlgen.commandlinetool
import logging
# External libraries
import ruamel.yaml
from cwlgen.commandlinebinding import CommandLineBinding
from .common import CWL_VERSIONS, DEF_VERSION, CWL_SHEBANG, Namespaces, Parameter
from .requirements import *
from .utils import literal, literal_presenter, Serializable, value_or_default
logging.basicConfig(level=logging.INFO)
_LOGGER = logging.getLogger(__name__)
# Function(s) ------------------------------
# Class(es) ------------------------------
[docs]class CommandOutputBinding(Serializable):
"""
Describes how to generate an output parameter based on the files produced.
"""
[docs] def __init__(self, glob=None, load_contents=None, output_eval=None):
"""
:param glob: Find corresponding file(s)
:type glob: STRING
:param load_contents: For each file matched, read up to the 1st 64 KiB of text and
place it in the contents field
:type load_contents: BOOLEAN
:param output_eval: Evaluate an expression to generate the output value
:type output_eval: STRING
"""
self.glob = glob
self.loadContents = load_contents
self.outputEval = output_eval
[docs]class CommandInputParameter(Parameter):
"""
An input parameter for a :class:`cwlgen.CommandLineTool`.
"""
parse_types = {"inputBinding": [CommandLineBinding]}
[docs] def __init__(
self,
param_id,
label=None,
secondary_files=None,
param_format=None,
streamable=None,
doc=None,
input_binding=None,
default=None,
param_type=None,
):
"""
:param param_id: unique identifier for this parameter
:type param_id: STRING
:param label: short, human-readable label
:type label: STRING
:param secondary_files: If type is a file, describes files that must be
included alongside the primary file(s)
:type secondary_files: STRING
:param param_format: If type is a file, uri to ontology of the format or exact format.
:type param_format: STRING
:param streamable: If type is a file, true indicates that the file is read or written
sequentially without seeking
:type streamable: BOOLEAN
:param doc: documentation
:type doc: STRING
:param input_binding: describes how to handle the input
:type input_binding: :class:`cwlgen.CommandLineBinding` object
:param default: default value
:type default: STRING
:param param_type: type of data assigned to the parameter corresponding to CWLType
:type param_type: STRING
"""
Parameter.__init__(
self,
param_id=param_id,
label=label,
secondary_files=secondary_files,
param_format=param_format,
streamable=streamable,
doc=doc,
param_type=param_type,
)
self.inputBinding = input_binding
self.default = default
[docs]class CommandOutputParameter(Parameter):
"""
An output parameter for a :class:`cwlgen.CommandLineTool`.
"""
parse_types = {"outputBinding": [CommandOutputBinding]}
[docs] def __init__(
self,
param_id,
label=None,
secondary_files=None,
param_format=None,
streamable=None,
doc=None,
output_binding=None,
param_type=None,
):
"""
:param param_id: unique identifier for this parameter
:type param_id: STRING
:param label: short, human-readable label
:type label: STRING
:param secondary_files: If type is a file, describes files that must be
included alongside the primary file(s)
:type secondary_files: STRING
:param param_format: If type is a file, uri to ontology of the format or exact format
:type param_format: STRING
:param streamable: If type is a file, true indicates that the file is read or written
sequentially without seeking
:type streamable: BOOLEAN
:param doc: documentation
:type doc: STRING
:param output_binding: describes how to handle the output
:type output_binding: :class:`cwlgen.CommandOutputBinding` object
:param param_type: type of data assigned to the parameter corresponding to CWLType
:type param_type: STRING
"""
Parameter.__init__(
self,
param_id,
label,
secondary_files,
param_format,
streamable,
doc,
param_type,
)
self.outputBinding = output_binding
[docs]class CommandLineTool(Serializable):
"""
Contain all informations to describe a CWL command line tool.
"""
__CLASS__ = "CommandLineTool"
required_fields = ["inputs", "outputs"]
parse_types = {'inputs': [[CommandInputParameter]], "outputs": [[CommandOutputParameter]]}
ignore_fields_on_parse = ["namespaces", "class", "requirements"]
ignore_fields_on_convert = ["namespaces", "class", "metadata", "requirements"]
[docs] def __init__(
self,
tool_id=None,
base_command=None,
label=None,
doc=None,
cwl_version="v1.0",
stdin=None,
stderr=None,
stdout=None,
path=None,
requirements=None,
hints=None,
inputs=None,
outputs=None,
arguments=None,
):
"""
:param tool_id: Unique identifier for this tool
:type tool_id: str
:param base_command: command line for the tool
:type base_command: str | list[STRING]
:param label: label of this tool
:type label: str
:param doc: documentation for the tool, usually longer than the label
:type doc: str
:param cwl_version: version of the CWL tool
:type cwl_version: str
:param stdin: path to a file whose contents must be piped into stdin
:type stdin: str
:param stderr: capture stderr into the given file
:type stderr: str
:param stdout: capture stdout into the given file
:type stdout: str
inputs (:class:`cwlgen.CommandInputParameter` objects),
outputs (:class:`cwlgen.CommandOutputParameter` objects),
arguments (:class:`cwlgen.CommandLineBinding` objects),
hints (any | :class:`cwlgen.Requirement` objects)
and requirements (:class:`cwlgen.Requirement` objects)
are stored in lists which are initialized empty.
"""
if cwl_version not in CWL_VERSIONS:
_LOGGER.warning(
"CWL version {} is not recognized as a valid version.".format(
cwl_version
)
)
_LOGGER.warning("CWL version is set up to {}.".format(DEF_VERSION))
cwl_version = DEF_VERSION
self.cwlVersion = cwl_version
self.id = tool_id
self.label = label
self.requirements = value_or_default(requirements, []) # List of objects inheriting from [Requirement]
self.hints = value_or_default(hints, []) # List of objects inheriting from [Requirement]
self.inputs = value_or_default(inputs, []) # List of [CommandInputParameter] objects
self.outputs = value_or_default(outputs, []) # List of [CommandOutputParameter] objects
self.baseCommand = base_command
self.arguments = value_or_default(arguments, []) # List of [CommandLineBinding] objects
self.doc = doc
self.stdin = stdin
self.stderr = stderr
self.stdout = stdout
self.successCodes = []
self.temporaryFailCodes = []
self.permanentFailCodes = []
self._path = path
self.namespaces = Namespaces()
self.metadata = {}
def get_dict(self):
d = super(CommandLineTool, self).get_dict()
d["class"] = self.__CLASS__
if self.metadata:
for key, value in self.metadata.__dict__.items():
d["s:" + key] = value
# - Add Namespaces
d[self.namespaces.name] = {}
for k, v in self.namespaces.__dict__.items():
if "$" not in v:
d[self.namespaces.name][k] = v
if "inputs" not in d:
# Tool can have no inputs but still needs to be bound
d["inputs"] = {}
if "outputs" not in d:
d["outputs"] = {}
if self.requirements:
d["requirements"] = {r.get_class(): r.get_dict() for r in self.requirements}
if self.hints:
d["hints"] = {r.get_class(): r.get_dict() for r in self.hints}
return d
@classmethod
def parse_dict(cls, d):
clt = super(CommandLineTool, cls).parse_dict(d)
reqs = d.get("requirements")
if reqs:
if isinstance(reqs, list):
clt.requirements = [Requirement.parse_dict(r) for r in reqs]
elif isinstance(reqs, dict):
# splat operator here would be so nice {**r, "class": c}
clt.requirements = []
for c, r in reqs.items():
rdict = {'class': c}
rdict.update(r)
clt.requirements.append(Requirement.parse_dict(rdict))
hnts = d.get("hints")
if hnts:
if isinstance(hnts, list):
clt.hints = [Requirement.parse_dict(r) for r in hnts]
elif isinstance(hnts, dict):
# splat operator here would be so nice {**r, "class": c}
clt.hints = []
for c, r in hnts.items():
rdict = {'class': c}
rdict.update(r)
clt.hints.append(Requirement.parse_dict(rdict))
return clt
def export_string(self):
ruamel.yaml.add_representer(literal, literal_presenter)
cwl_tool = self.get_dict()
return ruamel.yaml.dump(cwl_tool, default_flow_style=False)
[docs] def export(self, outfile=None):
"""
Export the tool in CWL either on STDOUT or in outfile.
"""
rep = self.export_string()
# Write CWL file in YAML
if outfile is None:
six.print_(CWL_SHEBANG, "\n", sep="")
six.print_(rep)
else:
out_write = open(outfile, "w")
out_write.write(CWL_SHEBANG + "\n\n")
out_write.write(rep)
out_write.close()