Skip to content

Commit 73f3a73

Browse files
committed
Merge pull request #1118 from glatard/master
ENH: a tool to export Nipype interfaces in the Boutiques JSON format (https://github.com/boutiques), importable in CBRAIN.
2 parents b1013d9 + ca9b77e commit 73f3a73

File tree

2 files changed

+124
-0
lines changed

2 files changed

+124
-0
lines changed

bin/nipype2boutiques

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#!/usr/bin/env python
2+
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
3+
# vi: set ft=python sts=4 ts=4 sw=4 et:
4+
import sys
5+
from nipype.utils.nipype2boutiques import main
6+
7+
if __name__ == '__main__':
8+
main(sys.argv)

nipype/utils/nipype2boutiques.py

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# This tool exports a Nipype interface in the Boutiques (https://github.com/boutiques) JSON format.
2+
# Boutiques tools can be imported in CBRAIN (https://github.com/aces/cbrain) among other platforms.
3+
#
4+
# Limitations:
5+
# * optional inputs are ignored because they are not supported in Boutiques.
6+
# * inputs with cardinality "Multiple" (InputMultiPath in Nipype) are not supported. Same limitation for the outputs.
7+
# * value-templates are wrong when output files are not created in the execution directory (e.g. when a sub-directory is created).
8+
9+
import os
10+
import argparse
11+
import inspect
12+
import sys
13+
import json
14+
import tempfile
15+
16+
from nipype.interfaces.base import Interface
17+
18+
def create_tempfile():
19+
fileTemp = tempfile.NamedTemporaryFile(delete = False)
20+
fileTemp.write("hello")
21+
fileTemp.close()
22+
return fileTemp.name
23+
24+
def print_inputs(tool_name, module=None, function=None):
25+
interface = None
26+
if module and function:
27+
__import__(module)
28+
interface = getattr(sys.modules[module],function)()
29+
30+
inputs = interface.input_spec()
31+
outputs = interface.output_spec()
32+
33+
command_line = "nipype_cmd "+str(module)+" "+tool_name+" "
34+
tool_desc = {}
35+
tool_desc['name'] = tool_name
36+
tool_desc['description'] = "Tool description goes here"
37+
38+
tool_inputs = []
39+
input_counter = 0
40+
tool_outputs = []
41+
42+
for name, spec in sorted(interface.inputs.traits(transient=None).items()):
43+
44+
#FIXME: optional inputs are not supported yet
45+
if not ( hasattr(spec, "mandatory") and spec.mandatory ):
46+
continue
47+
48+
input = {}
49+
input['name'] = name
50+
type = spec.full_info(inputs, name, None)
51+
if not "an existing file name" in type:
52+
type = "String"
53+
else:
54+
type = "File"
55+
input['type'] = type
56+
input['description'] = "\n".join(interface._get_trait_desc(inputs, name, spec))[len(name)+2:].replace("\n\t\t",". ")
57+
command_line_key = "["+str(input_counter)+"_"+name.upper()+"]"
58+
input_counter += 1
59+
input['command-line-key'] = command_line_key
60+
input['cardinality'] = "Single"
61+
tool_inputs.append(input)
62+
63+
if not ( hasattr(spec, "mandatory") and spec.mandatory ):
64+
command_line+= "--%s"%name+" " # unreachable code as long as optional inputs are not supported
65+
66+
command_line+= command_line_key+" "
67+
68+
# add value to input so that output names can be generated
69+
tempfile_name = create_tempfile()
70+
input['tempfile_name'] = tempfile_name
71+
if type == "File":
72+
setattr(interface.inputs,name,os.path.abspath(tempfile_name))
73+
74+
for name,spec in sorted(outputs.traits(transient=None).items()):
75+
76+
output = {}
77+
output['name'] = name
78+
output['type'] = "File"
79+
output['description'] = "No description provided"
80+
output['command-line-key'] = ""
81+
output['value-template'] = ""
82+
output_value = interface._list_outputs()[name]
83+
if output_value != "" and isinstance(output_value,str): # FIXME: this crashes when there are multiple output values.
84+
# go find from which input file it was built
85+
for input in tool_inputs:
86+
base_file_name = os.path.splitext(os.path.basename(input['tempfile_name']))[0]
87+
if base_file_name in output_value:
88+
output_value = os.path.basename(output_value.replace(base_file_name,input['command-line-key'])) # FIXME: this only works if output is written in the current directory
89+
output['value-template'] = os.path.basename(output_value)
90+
91+
output['cardinality'] = "Single"
92+
if output['value-template'] != "": # outputs with no templates would certainly crash.
93+
tool_outputs.append(output)
94+
95+
# remove all temporary file names from inputs
96+
for input in tool_inputs:
97+
del input['tempfile_name']
98+
99+
tool_desc['inputs'] = tool_inputs
100+
tool_desc['outputs'] = tool_outputs
101+
tool_desc['command-line'] = command_line
102+
tool_desc['docker-image'] = 'docker.io/robdimsdale/nipype'
103+
tool_desc['docker-index'] = 'http://index.docker.io'
104+
tool_desc['schema-version'] = '0.1'
105+
print json.dumps(tool_desc, indent=4, separators=(',', ': '))
106+
107+
def main(argv):
108+
109+
parser = argparse.ArgumentParser(description='Nipype Boutiques exporter', prog=argv[0])
110+
parser.add_argument("module", type=str, help="Module name")
111+
parser.add_argument("interface", type=str, help="Interface name")
112+
parsed = parser.parse_args(args=argv[1:3])
113+
114+
_, prog = os.path.split(argv[0])
115+
interface_parser = argparse.ArgumentParser(description="Run %s"%parsed.interface, prog=" ".join([prog] + argv[1:3]))
116+
print_inputs(argv[2],parsed.module, parsed.interface)

0 commit comments

Comments
 (0)