Skip to content

Commit 181c481

Browse files
committed
added warning message when cannot determine path template for output, added check for integer types, added method to get description from spec
1 parent e7739e7 commit 181c481

File tree

1 file changed

+52
-14
lines changed

1 file changed

+52
-14
lines changed

nipype/utils/nipype2boutiques.py

Lines changed: 52 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def generate_boutiques_descriptor(
6363
'description'] = interface_name + ", as implemented in Nipype (module: " + module_name + ", interface: " + interface_name + ")."
6464
tool_desc['inputs'] = []
6565
tool_desc['output-files'] = []
66-
tool_desc['tool-version'] = interface.version if interface.version is not None else 'undefined'
66+
tool_desc['tool-version'] = interface.version if interface.version is not None else "No version provided."
6767
tool_desc['schema-version'] = '0.5'
6868
if container_image:
6969
tool_desc['container-image'] = {}
@@ -84,7 +84,7 @@ def generate_boutiques_descriptor(
8484

8585
# Generates tool outputs
8686
for name, spec in sorted(outputs.traits(transient=None).items()):
87-
output = get_boutiques_output(name, interface, tool_desc['inputs'],
87+
output = get_boutiques_output(outputs, name, spec, interface, tool_desc['inputs'],
8888
verbose)
8989
if output['path-template'] != "":
9090
tool_desc['output-files'].append(output)
@@ -105,6 +105,8 @@ def generate_boutiques_descriptor(
105105
with open(interface_name + '.json', 'w') as outfile:
106106
json.dump(tool_desc, outfile)
107107

108+
print("NOTE: Descriptors produced by this script may not entirely conform to the Nipype interface "
109+
"specs. Please check that the descriptor is correct before using it.")
108110
return json.dumps(tool_desc, indent=4, separators=(',', ': '))
109111

110112

@@ -125,26 +127,24 @@ def get_boutiques_input(inputs, interface, input_name, spec,
125127
Assumes that:
126128
* Input names are unique.
127129
"""
128-
if not spec.desc:
129-
spec.desc = "No description provided."
130130
spec_info = spec.full_info(inputs, input_name, None)
131131

132132
input = {}
133133
input['id'] = input_name
134134
input['name'] = input_name.replace('_', ' ').capitalize()
135135

136136
# Figure out the input type from its handler type
137-
input['type'] = get_type_from_handler_type(spec.handler)
137+
input_type = get_type_from_handler_type(spec.handler)
138+
input['type'] = input_type[0]
139+
if input_type[1]:
140+
input['integer'] = True
138141

139142
input['list'] = is_list(spec_info)
140143
input['value-key'] = "[" + input_name.upper(
141144
) + "]" # assumes that input names are unique
142145
input['command-line-flag'] = ("--%s" % input_name + " ").strip()
143146
input['tempvalue'] = None
144-
input['description'] = spec_info.capitalize(
145-
) + ". " + spec.desc.capitalize()
146-
if not input['description'].endswith('.'):
147-
input['description'] += '.'
147+
input['description'] = get_description_from_spec(inputs, input_name, spec)
148148
if not (hasattr(spec, "mandatory") and spec.mandatory):
149149
input['optional'] = True
150150
else:
@@ -180,12 +180,14 @@ def get_boutiques_input(inputs, interface, input_name, spec,
180180
return input
181181

182182

183-
def get_boutiques_output(name, interface, tool_inputs, verbose=False):
183+
def get_boutiques_output(outputs, name, spec, interface, tool_inputs, verbose=False):
184184
"""
185185
Returns a dictionary containing the Boutiques output corresponding to a Nipype output.
186186
187187
Args:
188+
* outputs: outputs of the Nipype interface.
188189
* name: name of the Nipype output.
190+
* spec: Nipype output spec.
189191
* interface: Nipype interface.
190192
* tool_inputs: list of tool inputs (as produced by method get_boutiques_input).
191193
@@ -211,9 +213,13 @@ def get_boutiques_output(name, interface, tool_inputs, verbose=False):
211213
output[
212214
'optional'] = True # no real way to determine if an output is always produced, regardless of the input values.
213215

216+
output['description'] = get_description_from_spec(outputs, name, spec)
217+
214218
# Path template creation.
215219

216220
output_value = interface._list_outputs()[name]
221+
222+
# If output value is defined, use its basename
217223
if output_value != "" and isinstance(
218224
output_value,
219225
str): # FIXME: this crashes when there are multiple output values.
@@ -233,11 +239,20 @@ def get_boutiques_output(name, interface, tool_inputs, verbose=False):
233239
) # FIXME: this only works if output is written in the current directory
234240
output['path-template'] = os.path.basename(output_value)
235241

242+
# If output value is undefined, create a placeholder for the path template
236243
if not output_value:
237244
# Look for an input with the same name and use this as the path template
245+
found = False
238246
for input in tool_inputs:
239247
if input['id'] == name:
240248
output['path-template'] = input['value-key']
249+
found = True
250+
break
251+
# If no input with the same name was found, warn the user they should provide it manually
252+
if not found:
253+
print("WARNING: Could not determine path template for output %s. Please provide one for the "
254+
"descriptor manually." % name)
255+
output['path-template'] = "WARNING: No path template provided."
241256
return output
242257

243258

@@ -258,15 +273,21 @@ def get_type_from_spec_info(spec_info):
258273

259274

260275
def get_type_from_handler_type(handler):
276+
'''
277+
Gets the input type from the spec handler type.
278+
Returns a tuple containing the type and a boolean to specify
279+
if the type is an integer.
280+
'''
261281
handler_type = type(handler).__name__
282+
print("TYPE", handler_type)
262283
if handler_type == "File" or handler_type == "Directory":
263-
return "File"
284+
return "File", False
264285
elif handler_type == "Int" or handler_type == "Float":
265-
return "Number"
286+
return "Number", handler_type == "Int"
266287
elif handler_type == "Bool":
267-
return "Flag"
288+
return "Flag", False
268289
else:
269-
return "String"
290+
return "String", False
270291

271292
def is_list(spec_info):
272293
'''
@@ -336,3 +357,20 @@ def must_generate_value(name, type, ignored_template_inputs, spec_info, spec,
336357
if not ignored_template_inputs:
337358
return True
338359
return not (name in ignored_template_inputs)
360+
361+
362+
def get_description_from_spec(object, name, spec):
363+
'''
364+
Generates a description based on the input or output spec.
365+
'''
366+
if not spec.desc:
367+
spec.desc = "No description provided."
368+
spec_info = spec.full_info(object, name, None)
369+
370+
boutiques_description = (spec_info.capitalize(
371+
) + ". " + spec.desc.capitalize()).replace("\n", '')
372+
373+
if not boutiques_description.endswith('.'):
374+
boutiques_description += '.'
375+
376+
return boutiques_description

0 commit comments

Comments
 (0)