Skip to content

Commit 5e05ffe

Browse files
committed
Merge pull request #795 from chrisfilo/enh/nipypecmd
nipype_cmd - run any Interface from a command line
2 parents d0356ad + fbe186d commit 5e05ffe

File tree

7 files changed

+261
-88
lines changed

7 files changed

+261
-88
lines changed

CHANGES

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ Next release
22
============
33
* ENH: Dropped support for now 7 years old Python 2.6 (https://github.com/nipy/nipype/pull/1069)
44
* FIX: terminal_output is not mandatory anymore (https://github.com/nipy/nipype/pull/1070)
5+
* ENH: Added "nipype_cmd" tool for running interfaces from the command line (https://github.com/nipy/nipype/pull/795)
56
* FIX: Fixed Camino output naming (https://github.com/nipy/nipype/pull/1061)
67
* ENH: Add the average distance to ErrorMap (https://github.com/nipy/nipype/pull/1039)
78
* ENH: Inputs with name_source can be now chained in cascade (https://github.com/nipy/nipype/pull/938)

bin/nipype_cmd

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.nipype_cmd import main
6+
7+
if __name__ == '__main__':
8+
main(sys.argv)

doc/users/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
saving_workflows
3838
spmmcr
3939
mipav
40+
nipypecmd
4041

4142

4243

doc/users/nipypecmd.rst

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
.. _nipypecmd:
2+
3+
============================================================
4+
Running Nipype Interfaces from the command line (nipype_cmd)
5+
============================================================
6+
7+
The primary use of Nipype_ is to build automated non-interactive pipelines.
8+
However, sometimes there is a need to run some interfaces quickly from the command line.
9+
This is especially useful when running Interfaces wrapping code that does not have
10+
command line equivalents (nipy or SPM). Being able to run Nipype interfaces opens new
11+
possibilities such as inclusion of SPM processing steps in bash scripts.
12+
13+
To run Nipype Interafces you need to use the nipype_cmd tool that should already be installed.
14+
The tool allows you to list Interfaces available in a certain package:
15+
16+
.. testcode::
17+
18+
19+
$nipype_cmd nipype.interfaces.nipy
20+
21+
Available Interfaces:
22+
SpaceTimeRealigner
23+
Similarity
24+
ComputeMask
25+
FitGLM
26+
EstimateContrast
27+
FmriRealign4d
28+
29+
After selecting a particular Interface you can learn what inputs it requires:
30+
31+
.. testcode::
32+
33+
34+
$nipype_cmd nipype.interfaces.nipy ComputeMask --help
35+
36+
usage:nipype_cmd nipype.interfaces.nipy ComputeMask [-h] [--M M] [--cc CC]
37+
[--ignore_exception IGNORE_EXCEPTION]
38+
[--m M]
39+
[--reference_volume REFERENCE_VOLUME]
40+
mean_volume
41+
42+
Run ComputeMask
43+
44+
positional arguments:
45+
mean_volume mean EPI image, used to compute the threshold for the
46+
mask
47+
48+
optional arguments:
49+
-h, --help show this help message and exit
50+
--M M upper fraction of the histogram to be discarded
51+
--cc CC Keep only the largest connected component
52+
--ignore_exception IGNORE_EXCEPTION
53+
Print an error message instead of throwing an
54+
exception in case the interface fails to run
55+
--m M lower fraction of the histogram to be discarded
56+
--reference_volume REFERENCE_VOLUME
57+
reference volume used to compute the mask. If none is
58+
give, the mean volume is used.
59+
60+
Finally you can run run the Interface:
61+
62+
.. testcode::
63+
64+
$nipype_cmd nipype.interfaces.nipy ComputeMask mean.nii.gz
65+
66+
All that from the command line without having to start python interpreter manually.
67+
68+
.. include:: ../links_names.txt

nipype/utils/nipype_cmd.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import os
2+
import argparse
3+
import inspect
4+
import sys
5+
from nipype.interfaces.base import Interface
6+
7+
8+
def listClasses(module=None):
9+
if module:
10+
__import__(module)
11+
pkg = sys.modules[module]
12+
print "Available Interfaces:"
13+
for k,v in pkg.__dict__.items():
14+
if inspect.isclass(v) and issubclass(v, Interface):
15+
print "\t%s"%k
16+
17+
def add_options(parser=None, module=None, function=None):
18+
interface = None
19+
if parser and module and function:
20+
__import__(module)
21+
interface = getattr(sys.modules[module],function)()
22+
23+
inputs = interface.input_spec()
24+
for name, spec in sorted(interface.inputs.traits(transient=None).items()):
25+
desc = "\n".join(interface._get_trait_desc(inputs, name, spec))[len(name)+2:]
26+
if hasattr(spec, "mandatory") and spec.mandatory:
27+
parser.add_argument(name, help=desc)
28+
else:
29+
parser.add_argument("--%s"%name, dest=name,
30+
help=desc)
31+
return parser, interface
32+
33+
def run_instance(interface, options):
34+
if interface:
35+
print "setting function inputs"
36+
37+
for input_name, _ in interface.inputs.items():
38+
if getattr(options, input_name) != None:
39+
value = getattr(options, input_name)
40+
#traits cannot cast from string to float or int
41+
try:
42+
value = float(value)
43+
except:
44+
pass
45+
46+
try:
47+
setattr(interface.inputs, input_name,
48+
value)
49+
except ValueError, e:
50+
print "Error when setting the value of %s: '%s'"%(input_name, str(e))
51+
52+
print interface.inputs
53+
res = interface.run()
54+
print res.outputs
55+
56+
57+
def main(argv):
58+
59+
if len(argv) == 2 and not argv[1].startswith("-"):
60+
listClasses(argv[1])
61+
sys.exit(0)
62+
63+
parser = argparse.ArgumentParser(description='Nipype interface runner', prog=argv[0])
64+
parser.add_argument("module", type=str, help="Module name")
65+
parser.add_argument("interface", type=str, help="Interface name")
66+
parsed = parser.parse_args(args=argv[1:3])
67+
68+
_, prog = os.path.split(argv[0])
69+
interface_parser = argparse.ArgumentParser(description="Run %s"%parsed.interface, prog=" ".join([prog] + argv[1:3]))
70+
interface_parser, interface = add_options(interface_parser, parsed.module, parsed.interface)
71+
args = interface_parser.parse_args(args=argv[3:])
72+
run_instance(interface, args)

nipype/utils/tests/test_cmd.py

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
#!/usr/bin/env python
2+
3+
from StringIO import StringIO
4+
import unittest, sys
5+
from nipype.utils import nipype_cmd
6+
from contextlib import contextmanager
7+
8+
@contextmanager
9+
def capture_sys_output():
10+
caputure_out, capture_err = StringIO(), StringIO()
11+
current_out, current_err = sys.stdout, sys.stderr
12+
try:
13+
sys.stdout, sys.stderr = caputure_out, capture_err
14+
yield caputure_out, capture_err
15+
finally:
16+
sys.stdout, sys.stderr = current_out, current_err
17+
18+
19+
class TestNipypeCMD(unittest.TestCase):
20+
21+
def test_main_returns_2_on_empty(self):
22+
with self.assertRaises(SystemExit) as cm:
23+
with capture_sys_output() as (stdout, stderr):
24+
nipype_cmd.main(['nipype_cmd'])
25+
26+
exit_exception = cm.exception
27+
self.assertEqual(exit_exception.code, 2)
28+
29+
self.assertEqual(stderr.getvalue(),
30+
"""usage: nipype_cmd [-h] module interface
31+
nipype_cmd: error: too few arguments
32+
""")
33+
self.assertEqual(stdout.getvalue(), '')
34+
35+
def test_main_returns_0_on_help(self):
36+
with self.assertRaises(SystemExit) as cm:
37+
with capture_sys_output() as (stdout, stderr):
38+
nipype_cmd.main(['nipype_cmd', '-h'])
39+
40+
exit_exception = cm.exception
41+
self.assertEqual(exit_exception.code, 0)
42+
43+
self.assertEqual(stderr.getvalue(), '')
44+
self.assertEqual(stdout.getvalue(),
45+
"""usage: nipype_cmd [-h] module interface
46+
47+
Nipype interface runner
48+
49+
positional arguments:
50+
module Module name
51+
interface Interface name
52+
53+
optional arguments:
54+
-h, --help show this help message and exit
55+
""")
56+
57+
def test_list_nipy_interfacesp(self):
58+
with self.assertRaises(SystemExit) as cm:
59+
with capture_sys_output() as (stdout, stderr):
60+
nipype_cmd.main(['nipype_cmd', 'nipype.interfaces.nipy'])
61+
62+
exit_exception = cm.exception
63+
self.assertEqual(exit_exception.code, 0)
64+
65+
self.assertEqual(stderr.getvalue(), '')
66+
self.assertEqual(stdout.getvalue(),
67+
"""Available Interfaces:
68+
SpaceTimeRealigner
69+
Similarity
70+
ComputeMask
71+
FitGLM
72+
EstimateContrast
73+
FmriRealign4d
74+
""")
75+
76+
def test_run_4d_realign_without_arguments(self):
77+
with self.assertRaises(SystemExit) as cm:
78+
with capture_sys_output() as (stdout, stderr):
79+
nipype_cmd.main(['nipype_cmd', 'nipype.interfaces.nipy', 'FmriRealign4d'])
80+
81+
exit_exception = cm.exception
82+
self.assertEqual(exit_exception.code, 2)
83+
84+
self.assertEqual(stderr.getvalue(),
85+
"""usage: nipype_cmd nipype.interfaces.nipy FmriRealign4d [-h]
86+
[--between_loops BETWEEN_LOOPS]
87+
[--ignore_exception IGNORE_EXCEPTION]
88+
[--loops LOOPS]
89+
[--slice_order SLICE_ORDER]
90+
[--speedup SPEEDUP]
91+
[--start START]
92+
[--time_interp TIME_INTERP]
93+
[--tr_slices TR_SLICES]
94+
in_file tr
95+
nipype_cmd nipype.interfaces.nipy FmriRealign4d: error: too few arguments
96+
""")
97+
self.assertEqual(stdout.getvalue(), '')
98+
99+
def test_run_4d_realign_help(self):
100+
with self.assertRaises(SystemExit) as cm:
101+
with capture_sys_output() as (stdout, stderr):
102+
nipype_cmd.main(['nipype_cmd', 'nipype.interfaces.nipy', 'FmriRealign4d', '-h'])
103+
104+
exit_exception = cm.exception
105+
self.assertEqual(exit_exception.code, 0)
106+
107+
self.assertEqual(stderr.getvalue(), '')
108+
self.assertTrue("Run FmriRealign4d" in stdout.getvalue())
109+
110+
if __name__ == '__main__':
111+
unittest.main()

tools/run_interface.py

Lines changed: 0 additions & 88 deletions
This file was deleted.

0 commit comments

Comments
 (0)