/usr/lib/python3/dist-packages/cwl/converter.py is in ctdconverter 2.0-4.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 | #!/usr/bin/env python
# encoding: utf-8
# instead of using cwlgen, we decided to use PyYAML directly
# we promptly found a problem with cwlgen, namely, it is not possible to construct something like:
# some_paramter:
# type: ['null', string]
# which kind of sucks, because this seems to be the way to state that a parameter is truly optional and has no default
# since cwlgen is just "fancy classes" around the yaml.dump() method, we implemented our own generation of yaml
import ruamel.yaml as yaml
from CTDopts.CTDopts import _InFile, _OutFile, ParameterGroup, _Choices, _NumericRange, _FileFormat, ModelError, _Null
from common import utils, logger
# all cwl-related properties are defined here
CWL_SHEBANG = "#!/usr/bin/env cwl-runner"
CURRENT_CWL_VERSION = 'v1.0'
CWL_VERSION = 'cwlVersion'
CLASS = 'class'
BASE_COMMAND = 'baseCommand'
INPUTS = 'inputs'
ID = 'id'
TYPE = 'type'
INPUT_BINDING = 'inputBinding'
OUTPUT_BINDING = 'outputBinding'
PREFIX = 'prefix'
OUTPUTS = 'outputs'
POSITION = 'position'
VALUE_FROM = 'valueFrom'
GLOB = 'glob'
LABEL = 'label'
DOC = 'doc'
DEFAULT = 'default'
# types
TYPE_NULL = 'null'
TYPE_BOOLEAN = 'boolean'
TYPE_INT = 'int'
TYPE_LONG = 'long'
TYPE_FLOAT = 'float'
TYPE_DOUBLE = 'double'
TYPE_STRING = 'string'
TYPE_FILE = 'File'
TYPE_DIRECTORY = 'Directory'
TYPE_TO_CWL_TYPE = {int: TYPE_INT, float: TYPE_DOUBLE, str: TYPE_STRING, bool: TYPE_BOOLEAN, _InFile: TYPE_FILE,
_OutFile: TYPE_FILE, _Choices: TYPE_STRING}
def add_specific_args(parser):
# no specific arguments for CWL conversion, for now
# however, this method has to be defined, otherwise ../convert.py won't work for CWL
pass
def get_preferred_file_extension():
return "cwl"
def convert_models(args, parsed_ctds):
# go through each ctd model and perform the conversion, easy as pie!
for parsed_ctd in parsed_ctds:
model = parsed_ctd.ctd_model
origin_file = parsed_ctd.input_file
output_file = parsed_ctd.suggested_output_file
logger.info("Converting %s (source %s)" % (model.name, utils.get_filename(origin_file)))
cwl_tool = convert_to_cwl(model, args)
logger.info("Writing to %s" % utils.get_filename(output_file), 1)
stream = open(output_file, 'w')
stream.write(CWL_SHEBANG + '\n\n')
stream.write("# This CWL file was automatically generated using CTDConverter.\n")
stream.write("# Visit https://github.com/WorkflowConversion/CTDConverter for more information.\n\n")
yaml.dump(cwl_tool, stream, default_flow_style=False)
stream.close()
# returns a dictionary
def convert_to_cwl(ctd_model, args):
# create cwl_tool object with the basic information
base_command = utils.extract_tool_executable_path(ctd_model, args.default_executable_path)
# add basic properties
cwl_tool = {}
cwl_tool[CWL_VERSION] = CURRENT_CWL_VERSION
cwl_tool[CLASS] = 'CommandLineTool'
cwl_tool[LABEL] = ctd_model.opt_attribs["description"]
cwl_tool[DOC] = utils.extract_tool_help_text(ctd_model)
cwl_tool[BASE_COMMAND] = base_command
# TODO: test with optional output files
# add inputs/outputs
for param in utils.extract_and_flatten_parameters(ctd_model):
if param.name in args.blacklisted_parameters:
continue
param_name = utils.extract_param_name(param)
cwl_fixed_param_name = fix_param_name(param_name)
hardcoded_value = args.parameter_hardcoder.get_hardcoded_value(param_name, ctd_model.name)
if isinstance(param.default, map):
param.default = list(param.default)
param_default = str(param.default) if param.default is not _Null and param.default is not None else None
if param.type is _OutFile:
create_lists_if_missing(cwl_tool, [INPUTS, OUTPUTS])
# we know the only outputs are of type _OutFile
# we need an input of type string that will contain the name of the output file
label = "Filename for %s output file" % param_name
input_name_for_output_filename = get_input_name_for_output_filename(param)
input_param = {}
input_param[ID] = input_name_for_output_filename
input_param[DOC] = label
input_param[LABEL] = label
if param_default is not None:
input_param[DEFAULT] = param_default
input_param[TYPE] = generate_cwl_param_type(param, TYPE_STRING)
insert_input_binding(ctd_model, param, hardcoded_value, input_param)
output_binding = {}
output_binding[GLOB] = "$(inputs.%s)" % input_name_for_output_filename
output_param = {}
output_param[ID] = cwl_fixed_param_name
output_param[OUTPUT_BINDING] = output_binding
output_param[DOC] = param.description
output_param[LABEL] = param.description
output_param[TYPE] = generate_cwl_param_type(param)
cwl_tool[INPUTS].append(input_param)
cwl_tool[OUTPUTS].append(output_param)
else:
create_lists_if_missing(cwl_tool, [INPUTS])
# we know that anything that is not an _OutFile is an input
input_param = {}
input_param[ID] = cwl_fixed_param_name
input_param[DOC] = param.description
input_param[LABEL] = param.description
if param_default is not None:
input_param[DEFAULT] = param_default
input_param[TYPE] = generate_cwl_param_type(param)
insert_input_binding(ctd_model, param, hardcoded_value, input_param)
cwl_tool[INPUTS].append(input_param)
return cwl_tool
def create_lists_if_missing(cwl_tool, keys):
for key in keys:
if key not in cwl_tool:
cwl_tool[key] = []
def get_input_name_for_output_filename(param):
assert param.type is _OutFile, "Only output files can get a generated filename input parameter."
return fix_param_name(utils.extract_param_name(param)) + "_filename"
def fix_param_name(param_name):
# IMPORTANT: there seems to be a problem in CWL if the prefix and the parameter name are the same, so we need to
# prepend something to the parameter name that will be registered in CWL, also, using colons in parameter
# names seems to bring all sorts of problems for cwl-runner
return 'param_' + param_name.replace(":", "_")
# in order to provide "true" optional params, the parameter type should be something like ['null', <CWLType>],
# for instance ['null', int]
def generate_cwl_param_type(param, forced_type=None):
cwl_type = TYPE_TO_CWL_TYPE[param.type] if forced_type is None else forced_type
return cwl_type if param.required else ['null', cwl_type]
# generate, and insert, the inputBinding
def insert_input_binding(ctd_model, param, hardcoded_value, cwl_input_param):
prefix = utils.extract_command_line_prefix(param, ctd_model)
prefix = None if prefix is None or not prefix.strip() else prefix
input_binding = {}
if prefix is not None:
input_binding[PREFIX] = prefix
if hardcoded_value is not None:
input_binding[VALUE_FROM] = hardcoded_value
if param.is_positional():
input_binding[POSITION] = param.position
# insert input binding if there's something in it
if input_binding:
cwl_input_param[INPUT_BINDING] = input_binding
|