From d6767be52749682a70fabfcf288690e9a5e14424 Mon Sep 17 00:00:00 2001 From: Chris Filo Gorgolewski Date: Fri, 7 Feb 2014 14:59:00 +0100 Subject: [PATCH 01/21] OPEN - task 788: Add support for running interface from commandline http://github.com/nipy/nipype/issues/issue/788 Moved and cleaned up the run_interface tool. --- tools/run_interface.py => bin/nipype_cmd.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename tools/run_interface.py => bin/nipype_cmd.py (96%) diff --git a/tools/run_interface.py b/bin/nipype_cmd.py similarity index 96% rename from tools/run_interface.py rename to bin/nipype_cmd.py index ea9fceb825..9117862b6f 100644 --- a/tools/run_interface.py +++ b/bin/nipype_cmd.py @@ -23,7 +23,7 @@ def add_options(parser=None, module=None, function=None): __import__(module) interface = getattr(sys.modules[module],function)() - for k,v in interface.inputs.iteritems(): + for k,v in interface.inputs.items(): parser.add_option("-%s"%k[0], "--%s"%k, dest="IXI%s"%k, metavar=k, action='store',type='string', @@ -33,7 +33,7 @@ def add_options(parser=None, module=None, function=None): def run_instance(interface, options): if interface: print "setting function inputs" - for k,v in interface.inputs.iteritems(): + for k,v in interface.inputs.items(): optionskey = ''.join(('IXI',k)) if hasattr(options, optionskey): setattr(interface.inputs, k, From 9770dfb1dda1157ca80929c1dbe8455850cb72aa Mon Sep 17 00:00:00 2001 From: Chris Filo Gorgolewski Date: Fri, 7 Feb 2014 15:11:07 +0100 Subject: [PATCH 02/21] switched to argparse --- bin/nipype_cmd.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/bin/nipype_cmd.py b/bin/nipype_cmd.py index 9117862b6f..f7b61fe16f 100644 --- a/bin/nipype_cmd.py +++ b/bin/nipype_cmd.py @@ -5,7 +5,7 @@ """ # stdlib imports import os -from optparse import OptionParser +import argparse import sys def listClasses(module=None): @@ -24,10 +24,8 @@ def add_options(parser=None, module=None, function=None): interface = getattr(sys.modules[module],function)() for k,v in interface.inputs.items(): - parser.add_option("-%s"%k[0], "--%s"%k, dest="IXI%s"%k, - metavar=k, - action='store',type='string', - help="you need help?",default='') + parser.add_argument("--%s"%k, dest=k, + help=v.desc) return parser, interface def run_instance(interface, options): @@ -63,19 +61,21 @@ def get_modfunc(args): return module, function def parse_args(): - usage = "usage: %prog [options] module function" - parser = OptionParser(usage=usage,version="%prog 1.0", - conflict_handler="resolve") - parser.add_option("--run", dest="run", - action='store_true',help="Execute", - default=False) + parser = argparse.ArgumentParser(description='Nipype interface runner') + parser.add_argument("--run", dest="run", help="Execute", default=False) + +# +# usage = "usage: %prog [options] module function" +# parser = OptionParser(usage=usage,version="%prog 1.0", +# conflict_handler="resolve") + module, function = get_modfunc(sys.argv[1:]) parser, interface = add_options(parser, module, function) - (options, args) = parser.parse_args() - if options.run and interface: + args = parser.parse_args() + if args.run and interface: #assign inputs - run_instance(interface, options) + run_instance(interface, args) else: parser.print_help() if module and not function: From a3c7bd86eaee9b5958abd25a1fbebf08f470b19d Mon Sep 17 00:00:00 2001 From: Chris Filo Gorgolewski Date: Mon, 10 Feb 2014 19:43:48 +0100 Subject: [PATCH 03/21] The command now runs. --- bin/nipype_cmd.py | 61 +++++++++++++---------------------------------- 1 file changed, 17 insertions(+), 44 deletions(-) diff --git a/bin/nipype_cmd.py b/bin/nipype_cmd.py index f7b61fe16f..d0ff602bb7 100644 --- a/bin/nipype_cmd.py +++ b/bin/nipype_cmd.py @@ -24,63 +24,36 @@ def add_options(parser=None, module=None, function=None): interface = getattr(sys.modules[module],function)() for k,v in interface.inputs.items(): - parser.add_argument("--%s"%k, dest=k, + if hasattr(v, "mandatory") and v.mandatory: + parser.add_argument(k, help=v.desc) + else: + parser.add_argument("--%s"%k, dest=k, help=v.desc) return parser, interface def run_instance(interface, options): if interface: print "setting function inputs" - for k,v in interface.inputs.items(): - optionskey = ''.join(('IXI',k)) - if hasattr(options, optionskey): + for k,_ in interface.inputs.items(): + if getattr(options, k) != None: setattr(interface.inputs, k, - getattr(options, optionskey)) + getattr(options, k)) print interface.inputs - print "not really running anything" + res = interface.run() + print res.outputs -def get_modfunc(args): - module = None - function = None - posargs = [] - skip = False - for a in args: - if skip: - skip = False - continue - if a.startswith('--'): - pass - elif a.startswith('-'): - skip = True - else: - posargs.append(a) - if posargs: - module = posargs[0] - if len(posargs)==2: - function = posargs[1] - return module, function def parse_args(): parser = argparse.ArgumentParser(description='Nipype interface runner') - parser.add_argument("--run", dest="run", help="Execute", default=False) - -# -# usage = "usage: %prog [options] module function" -# parser = OptionParser(usage=usage,version="%prog 1.0", -# conflict_handler="resolve") + parser.add_argument("module", type=str, help="Module name") + parser.add_argument("interface", type=str, help="Interface name") + parsed = parser.parse_args(args=sys.argv[1:3]) - - module, function = get_modfunc(sys.argv[1:]) - parser, interface = add_options(parser, module, function) - args = parser.parse_args() - if args.run and interface: - #assign inputs - run_instance(interface, args) - else: - parser.print_help() - if module and not function: - listClasses(module) - parser.exit() + _, prog = os.path.split(sys.argv[0]) + interface_parser = argparse.ArgumentParser(description="Run %s"%parsed.interface, prog=" ".join([prog] + sys.argv[1:3])) + interface_parser, interface = add_options(interface_parser, parsed.module, parsed.interface) + args = interface_parser.parse_args(args=sys.argv[3:]) + run_instance(interface, args) #***************************************************************************** From ff9f298eeb705696f2fdd1d93d9710dc7c1a84ca Mon Sep 17 00:00:00 2001 From: Chris Filo Gorgolewski Date: Mon, 10 Feb 2014 19:59:52 +0100 Subject: [PATCH 04/21] added listing interfaces --- bin/nipype_cmd.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/bin/nipype_cmd.py b/bin/nipype_cmd.py index d0ff602bb7..2b9f202809 100644 --- a/bin/nipype_cmd.py +++ b/bin/nipype_cmd.py @@ -7,14 +7,17 @@ import os import argparse import sys +import inspect +from nipype.interfaces.base import Interface + def listClasses(module=None): if module: __import__(module) pkg = sys.modules[module] - print "Available functions:" + print "Available Interfaces:" for k,v in pkg.__dict__.items(): - if 'class' in str(v) and k != '__builtins__': + if inspect.isclass(v) and issubclass(v, Interface): print "\t%s"%k def add_options(parser=None, module=None, function=None): @@ -44,6 +47,11 @@ def run_instance(interface, options): def parse_args(): + + if len(sys.argv) == 2: + listClasses(sys.argv[1]) + return + parser = argparse.ArgumentParser(description='Nipype interface runner') parser.add_argument("module", type=str, help="Module name") parser.add_argument("interface", type=str, help="Interface name") From f0114c3c98f52b13fe3fdd8f612bb47cfdd68785 Mon Sep 17 00:00:00 2001 From: Chris Filo Gorgolewski Date: Mon, 10 Feb 2014 20:01:06 +0100 Subject: [PATCH 05/21] removed the .py extension --- bin/{nipype_cmd.py => nipype_cmd} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename bin/{nipype_cmd.py => nipype_cmd} (100%) diff --git a/bin/nipype_cmd.py b/bin/nipype_cmd similarity index 100% rename from bin/nipype_cmd.py rename to bin/nipype_cmd From 4153c97d3c8c2d8b976b6d271ea932ed39ed1443 Mon Sep 17 00:00:00 2001 From: Chris Filo Gorgolewski Date: Mon, 10 Feb 2014 20:12:14 +0100 Subject: [PATCH 06/21] fixed --help on top level --- bin/nipype_cmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/nipype_cmd b/bin/nipype_cmd index 2b9f202809..0e402ab64a 100644 --- a/bin/nipype_cmd +++ b/bin/nipype_cmd @@ -48,7 +48,7 @@ def run_instance(interface, options): def parse_args(): - if len(sys.argv) == 2: + if len(sys.argv) == 2 and not sys.argv[1].startswith("-"): listClasses(sys.argv[1]) return From 00aac836a0c7d03c2e66dbebf3e43b435594ab1d Mon Sep 17 00:00:00 2001 From: Chris Filo Gorgolewski Date: Tue, 17 Mar 2015 11:38:44 -0700 Subject: [PATCH 07/21] make nipype_cmd executable --- bin/nipype_cmd | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 bin/nipype_cmd diff --git a/bin/nipype_cmd b/bin/nipype_cmd old mode 100644 new mode 100755 From a056a8bab1b9a78b37b93b1f621cf237b6fa0a33 Mon Sep 17 00:00:00 2001 From: Chris Filo Gorgolewski Date: Tue, 17 Mar 2015 14:06:31 -0700 Subject: [PATCH 08/21] refactor, added tests --- bin/nipype_cmd | 65 ++-------------------------------- nipype/utils/nipype_cmd.py | 60 +++++++++++++++++++++++++++++++ nipype/utils/tests/test_cmd.py | 56 +++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+), 63 deletions(-) create mode 100644 nipype/utils/nipype_cmd.py create mode 100644 nipype/utils/tests/test_cmd.py diff --git a/bin/nipype_cmd b/bin/nipype_cmd index 0e402ab64a..bdedb0789f 100755 --- a/bin/nipype_cmd +++ b/bin/nipype_cmd @@ -1,69 +1,8 @@ #!/usr/bin/env python # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- # vi: set ft=python sts=4 ts=4 sw=4 et: -"""Script to auto-generate our API docs. -""" -# stdlib imports -import os -import argparse import sys -import inspect -from nipype.interfaces.base import Interface +from nipype.utils.nipype_cmd import main - -def listClasses(module=None): - if module: - __import__(module) - pkg = sys.modules[module] - print "Available Interfaces:" - for k,v in pkg.__dict__.items(): - if inspect.isclass(v) and issubclass(v, Interface): - print "\t%s"%k - -def add_options(parser=None, module=None, function=None): - interface = None - if parser and module and function: - __import__(module) - interface = getattr(sys.modules[module],function)() - - for k,v in interface.inputs.items(): - if hasattr(v, "mandatory") and v.mandatory: - parser.add_argument(k, help=v.desc) - else: - parser.add_argument("--%s"%k, dest=k, - help=v.desc) - return parser, interface - -def run_instance(interface, options): - if interface: - print "setting function inputs" - for k,_ in interface.inputs.items(): - if getattr(options, k) != None: - setattr(interface.inputs, k, - getattr(options, k)) - print interface.inputs - res = interface.run() - print res.outputs - - -def parse_args(): - - if len(sys.argv) == 2 and not sys.argv[1].startswith("-"): - listClasses(sys.argv[1]) - return - - parser = argparse.ArgumentParser(description='Nipype interface runner') - parser.add_argument("module", type=str, help="Module name") - parser.add_argument("interface", type=str, help="Interface name") - parsed = parser.parse_args(args=sys.argv[1:3]) - - _, prog = os.path.split(sys.argv[0]) - interface_parser = argparse.ArgumentParser(description="Run %s"%parsed.interface, prog=" ".join([prog] + sys.argv[1:3])) - interface_parser, interface = add_options(interface_parser, parsed.module, parsed.interface) - args = interface_parser.parse_args(args=sys.argv[3:]) - run_instance(interface, args) - - -#***************************************************************************** if __name__ == '__main__': - parse_args() + main(sys.argv) diff --git a/nipype/utils/nipype_cmd.py b/nipype/utils/nipype_cmd.py new file mode 100644 index 0000000000..669887e071 --- /dev/null +++ b/nipype/utils/nipype_cmd.py @@ -0,0 +1,60 @@ +import os +import argparse +import inspect +import sys +from nipype.interfaces.base import Interface + + +def listClasses(module=None): + if module: + __import__(module) + pkg = sys.modules[module] + print "Available Interfaces:" + for k,v in pkg.__dict__.items(): + if inspect.isclass(v) and issubclass(v, Interface): + print "\t%s"%k + +def add_options(parser=None, module=None, function=None): + interface = None + if parser and module and function: + __import__(module) + interface = getattr(sys.modules[module],function)() + + for k,v in interface.inputs.items(): + if hasattr(v, "mandatory") and v.mandatory: + parser.add_argument(k, help=v.desc) + else: + parser.add_argument("--%s"%k, dest=k, + help=v.desc) + return parser, interface + +def run_instance(interface, options): + if interface: + print "setting function inputs" + for input_name, _ in interface.inputs.items(): + if getattr(options, input_name) != None: + str_value = getattr(options, input_name) + casted_value = getattr(interface.inputs, input_name)(str_value) + setattr(interface.inputs, input_name, + casted_value) + print interface.inputs + res = interface.run() + print res.outputs + + +def main(argv): + + if len(argv) == 2 and not argv[1].startswith("-"): + listClasses(argv[1]) + sys.exit(0) + + parser = argparse.ArgumentParser(description='Nipype interface runner') + parser.add_argument("module", type=str, help="Module name") + parser.add_argument("interface", type=str, help="Interface name") + parsed = parser.parse_args(args=argv[1:3]) + + _, prog = os.path.split(argv[0]) + interface_parser = argparse.ArgumentParser(description="Run %s"%parsed.interface, prog=" ".join([prog] + argv[1:3])) + interface_parser, interface = add_options(interface_parser, parsed.module, parsed.interface) + args = interface_parser.parse_args(args=argv[3:]) + run_instance(interface, args) \ No newline at end of file diff --git a/nipype/utils/tests/test_cmd.py b/nipype/utils/tests/test_cmd.py new file mode 100644 index 0000000000..d898ba91ef --- /dev/null +++ b/nipype/utils/tests/test_cmd.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python + +from StringIO import StringIO +import unittest, sys +from nipype.utils import nipype_cmd +from contextlib import contextmanager + +@contextmanager +def capture_sys_output(): + caputure_out, capture_err = StringIO(), StringIO() + current_out, current_err = sys.stdout, sys.stderr + try: + sys.stdout, sys.stderr = caputure_out, capture_err + yield caputure_out, capture_err + finally: + sys.stdout, sys.stderr = current_out, current_err + + +class TestNipypeCMD(unittest.TestCase): + + def test_main_returns_2_on_empty(self): + with self.assertRaises(SystemExit) as cm: + with capture_sys_output() as (stdout, stderr): + nipype_cmd.main(['nipype_cmd']) + + exit_exception = cm.exception + self.assertEqual(exit_exception.code, 2) + + self.assertEqual(stderr.getvalue(), +"""usage: runfiles.py [-h] module interface +runfiles.py: error: too few arguments +""") + self.assertEqual(stdout.getvalue(), '') + + def test_main_returns_0_on_help(self): + with self.assertRaises(SystemExit) as cm: + with capture_sys_output() as (stdout, stderr): + nipype_cmd.main(['nipype_cmd', '-h']) + + exit_exception = cm.exception + self.assertEqual(exit_exception.code, 0) + + self.assertEqual(stderr.getvalue(), '') + self.assertEqual(stdout.getvalue(), +"""usage: runfiles.py [-h] module interface\n\nNipype interface runner + +positional arguments: + module Module name + interface Interface name + +optional arguments: + -h, --help show this help message and exit +""") + +if __name__ == '__main__': + unittest.main() \ No newline at end of file From f971e0e9a275f319f40a90bbf3c79e1391208ad9 Mon Sep 17 00:00:00 2001 From: Chris Filo Gorgolewski Date: Tue, 17 Mar 2015 15:05:00 -0700 Subject: [PATCH 09/21] Added more tests --- nipype/utils/tests/test_cmd.py | 94 ++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/nipype/utils/tests/test_cmd.py b/nipype/utils/tests/test_cmd.py index d898ba91ef..096e04c72b 100644 --- a/nipype/utils/tests/test_cmd.py +++ b/nipype/utils/tests/test_cmd.py @@ -51,6 +51,100 @@ def test_main_returns_0_on_help(self): optional arguments: -h, --help show this help message and exit """) + + def test_list_nipy_interfacesp(self): + with self.assertRaises(SystemExit) as cm: + with capture_sys_output() as (stdout, stderr): + nipype_cmd.main(['nipype_cmd', 'nipype.interfaces.nipy']) + + exit_exception = cm.exception + self.assertEqual(exit_exception.code, 0) + + self.assertEqual(stderr.getvalue(), '') + self.assertEqual(stdout.getvalue(), +"""Available Interfaces: + SpaceTimeRealigner + Similarity + ComputeMask + FitGLM + EstimateContrast + FmriRealign4d +""") + + def test_run_4d_realign_without_arguments(self): + with self.assertRaises(SystemExit) as cm: + with capture_sys_output() as (stdout, stderr): + nipype_cmd.main(['nipype_cmd', 'nipype.interfaces.nipy', 'FmriRealign4d']) + + exit_exception = cm.exception + self.assertEqual(exit_exception.code, 2) + + self.assertEqual(stderr.getvalue(), +"""usage: nipype_cmd nipype.interfaces.nipy FmriRealign4d [-h] + [--between_loops BETWEEN_LOOPS] + [--ignore_exception IGNORE_EXCEPTION] + [--loops LOOPS] + [--slice_order SLICE_ORDER] + [--speedup SPEEDUP] + [--start START] + [--time_interp TIME_INTERP] + [--tr_slices TR_SLICES] + in_file tr +nipype_cmd nipype.interfaces.nipy FmriRealign4d: error: too few arguments +""") + self.assertEqual(stdout.getvalue(), '') + + def test_run_4d_realign_help(self): + with self.assertRaises(SystemExit) as cm: + with capture_sys_output() as (stdout, stderr): + nipype_cmd.main(['nipype_cmd', 'nipype.interfaces.nipy', 'FmriRealign4d', '-h']) + + exit_exception = cm.exception + self.assertEqual(exit_exception.code, 0) + + self.assertEqual(stderr.getvalue(), '') + self.assertEqual(stdout.getvalue(), +"""usage: nipype_cmd nipype.interfaces.nipy FmriRealign4d [-h] + [--between_loops BETWEEN_LOOPS] + [--ignore_exception IGNORE_EXCEPTION] + [--loops LOOPS] + [--slice_order SLICE_ORDER] + [--speedup SPEEDUP] + [--start START] + [--time_interp TIME_INTERP] + [--tr_slices TR_SLICES] + in_file tr + +Run FmriRealign4d + +positional arguments: + in_file File to realign + tr TR in seconds + +optional arguments: + -h, --help show this help message and exit + --between_loops BETWEEN_LOOPS + loops used to realign different runs + --ignore_exception IGNORE_EXCEPTION + Print an error message instead of throwing an + exception in case the interface fails to run + --loops LOOPS loops within each run + --slice_order SLICE_ORDER + 0 based slice order. This would be equivalent to + enteringnp.argsort(spm_slice_order) for this field. + This effectsinterleaved acquisition. This field will + be deprecated infuture Nipy releases and be replaced + by actual sliceacquisition times. + --speedup SPEEDUP successive image sub-sampling factors for acceleration + --start START time offset into TR to align slices to + --time_interp TIME_INTERP + Assume smooth changes across time e.g., fmri series. + If you don't want slice timing correction set this to + undefined + --tr_slices TR_SLICES + TR slices +""") + if __name__ == '__main__': unittest.main() \ No newline at end of file From 2e3ef31bd27e9643d0b266786de053dd6ca77359 Mon Sep 17 00:00:00 2001 From: Chris Filo Gorgolewski Date: Tue, 17 Mar 2015 15:33:21 -0700 Subject: [PATCH 10/21] fixed runtime naming --- nipype/utils/nipype_cmd.py | 2 +- nipype/utils/tests/test_cmd.py | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/nipype/utils/nipype_cmd.py b/nipype/utils/nipype_cmd.py index 669887e071..69b084ae2b 100644 --- a/nipype/utils/nipype_cmd.py +++ b/nipype/utils/nipype_cmd.py @@ -48,7 +48,7 @@ def main(argv): listClasses(argv[1]) sys.exit(0) - parser = argparse.ArgumentParser(description='Nipype interface runner') + parser = argparse.ArgumentParser(description='Nipype interface runner', prog=argv[0]) parser.add_argument("module", type=str, help="Module name") parser.add_argument("interface", type=str, help="Interface name") parsed = parser.parse_args(args=argv[1:3]) diff --git a/nipype/utils/tests/test_cmd.py b/nipype/utils/tests/test_cmd.py index 096e04c72b..0d3ce64663 100644 --- a/nipype/utils/tests/test_cmd.py +++ b/nipype/utils/tests/test_cmd.py @@ -27,8 +27,8 @@ def test_main_returns_2_on_empty(self): self.assertEqual(exit_exception.code, 2) self.assertEqual(stderr.getvalue(), -"""usage: runfiles.py [-h] module interface -runfiles.py: error: too few arguments +"""usage: nipype_cmd [-h] module interface +nipype_cmd: error: too few arguments """) self.assertEqual(stdout.getvalue(), '') @@ -42,7 +42,9 @@ def test_main_returns_0_on_help(self): self.assertEqual(stderr.getvalue(), '') self.assertEqual(stdout.getvalue(), -"""usage: runfiles.py [-h] module interface\n\nNipype interface runner +"""usage: nipype_cmd [-h] module interface + +Nipype interface runner positional arguments: module Module name From d753840f3ccd6905e1e11e155416b9f34875e403 Mon Sep 17 00:00:00 2001 From: Chris Filo Gorgolewski Date: Tue, 17 Mar 2015 15:36:57 -0700 Subject: [PATCH 11/21] added argparse as a dependency --- nipype/info.py | 2 +- requirements.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/nipype/info.py b/nipype/info.py index 620f05bddf..359ad981ff 100644 --- a/nipype/info.py +++ b/nipype/info.py @@ -126,5 +126,5 @@ def get_nipype_gitversion(): VERSION = __version__ REQUIRES = ["nibabel>=1.0", "networkx>=1.0", "numpy>=1.3", "python-dateutil>1.0", "scipy>=0.7", "traits>=4.0", - "nose>=1.0"] + "nose>=1.0", "argparse>=1.1.0"] STATUS = 'stable' diff --git a/requirements.txt b/requirements.txt index f2889996a5..1b4f817d33 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,3 +5,4 @@ traits>=4.0 python-dateutil>=1.5 nibabel>=1.0 nose>=1.0 +argparse>=1.1 \ No newline at end of file From 2871bff66529eb638dc34540b2afa22abe2cdbe8 Mon Sep 17 00:00:00 2001 From: Chris Filo Gorgolewski Date: Tue, 17 Mar 2015 18:00:07 -0700 Subject: [PATCH 12/21] added argparse dependency on travis --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 46f09d9aba..863ff40d08 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,6 +29,7 @@ install: - pip install nibabel --use-mirrors - pip install python-coveralls --use-mirrors - pip install nose-cov --use-mirrors + - pip install argparse - pip install https://github.com/RDFLib/rdflib/archive/master.zip - pip install https://github.com/trungdong/prov/archive/rdf.zip - python setup.py install From 6e442d011561745b49f88dfc1ab227179123969d Mon Sep 17 00:00:00 2001 From: Chris Filo Gorgolewski Date: Tue, 17 Mar 2015 20:03:42 -0700 Subject: [PATCH 13/21] Fixed casting --- nipype/utils/nipype_cmd.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/nipype/utils/nipype_cmd.py b/nipype/utils/nipype_cmd.py index 69b084ae2b..cd6a028410 100644 --- a/nipype/utils/nipype_cmd.py +++ b/nipype/utils/nipype_cmd.py @@ -33,10 +33,19 @@ def run_instance(interface, options): print "setting function inputs" for input_name, _ in interface.inputs.items(): if getattr(options, input_name) != None: - str_value = getattr(options, input_name) - casted_value = getattr(interface.inputs, input_name)(str_value) - setattr(interface.inputs, input_name, - casted_value) + value = getattr(options, input_name) + #traits cannot cast from string to float or int + try: + value = float(value) + except: + pass + + try: + setattr(interface.inputs, input_name, + value) + except ValueError, e: + print "Error when setting the value of %s: '%s'"%(input_name, str(e)) + print interface.inputs res = interface.run() print res.outputs From 742d6499fe0a503af2cce110f2ca0c90d5e6a6b1 Mon Sep 17 00:00:00 2001 From: Chris Filo Gorgolewski Date: Tue, 17 Mar 2015 20:50:00 -0700 Subject: [PATCH 14/21] Revert "added argparse dependency on travis" This reverts commit a9163f0d40fd43e64406259f24bca83f0443d931. --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 863ff40d08..46f09d9aba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,7 +29,6 @@ install: - pip install nibabel --use-mirrors - pip install python-coveralls --use-mirrors - pip install nose-cov --use-mirrors - - pip install argparse - pip install https://github.com/RDFLib/rdflib/archive/master.zip - pip install https://github.com/trungdong/prov/archive/rdf.zip - python setup.py install From 97cd37c477ce870c46bce8ce32ed3bb99dd7017a Mon Sep 17 00:00:00 2001 From: Chris Filo Gorgolewski Date: Tue, 17 Mar 2015 20:50:17 -0700 Subject: [PATCH 15/21] Revert "added argparse as a dependency" This reverts commit 25fef6a1139bf5d5d50053bbacf91f0caa8511ec. --- nipype/info.py | 2 +- requirements.txt | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/nipype/info.py b/nipype/info.py index 359ad981ff..620f05bddf 100644 --- a/nipype/info.py +++ b/nipype/info.py @@ -126,5 +126,5 @@ def get_nipype_gitversion(): VERSION = __version__ REQUIRES = ["nibabel>=1.0", "networkx>=1.0", "numpy>=1.3", "python-dateutil>1.0", "scipy>=0.7", "traits>=4.0", - "nose>=1.0", "argparse>=1.1.0"] + "nose>=1.0"] STATUS = 'stable' diff --git a/requirements.txt b/requirements.txt index 1b4f817d33..f2889996a5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,4 +5,3 @@ traits>=4.0 python-dateutil>=1.5 nibabel>=1.0 nose>=1.0 -argparse>=1.1 \ No newline at end of file From 55022bca4fb9d2bfe1dd0b7b1e7b57b2e5e49b5b Mon Sep 17 00:00:00 2001 From: Chris Filo Gorgolewski Date: Wed, 18 Mar 2015 15:28:53 -0700 Subject: [PATCH 16/21] added documentation --- doc/users/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/users/index.rst b/doc/users/index.rst index 8ecbdf2e08..c5ebbae1df 100644 --- a/doc/users/index.rst +++ b/doc/users/index.rst @@ -37,6 +37,7 @@ saving_workflows spmmcr mipav + nipypecmd From 49f2bd96adca879c81ae16ffcedd6e2430677f78 Mon Sep 17 00:00:00 2001 From: Chris Filo Gorgolewski Date: Wed, 18 Mar 2015 15:31:15 -0700 Subject: [PATCH 17/21] updated change log --- CHANGES | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES b/CHANGES index ca43e827e1..ecf067f8af 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,7 @@ Next release ============ * ENH: Dropped support for now 7 years old Python 2.6 (https://github.com/nipy/nipype/pull/1069) * FIX: terminal_output is not mandatory anymore (https://github.com/nipy/nipype/pull/1070) +* ENH: Added "nipype_cmd" tool for running interfaces from the command line (https://github.com/nipy/nipype/pull/795) * FIX: Fixed Camino output naming (https://github.com/nipy/nipype/pull/1061) * ENH: Add the average distance to ErrorMap (https://github.com/nipy/nipype/pull/1039) * ENH: Inputs with name_source can be now chained in cascade (https://github.com/nipy/nipype/pull/938) From e4ed610096fc3b6557990eef15a3e073a9b952be Mon Sep 17 00:00:00 2001 From: Chris Filo Gorgolewski Date: Wed, 18 Mar 2015 15:50:12 -0700 Subject: [PATCH 18/21] added missing doc file --- doc/users/nipypecmd.rst | 68 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 doc/users/nipypecmd.rst diff --git a/doc/users/nipypecmd.rst b/doc/users/nipypecmd.rst new file mode 100644 index 0000000000..2fa86de840 --- /dev/null +++ b/doc/users/nipypecmd.rst @@ -0,0 +1,68 @@ +.. _nipypecmd: + +============================================================ +Running Nipype Interfaces from the command line (nipype_cmd) +============================================================ + +The primary use of Nipype_ is to build automated non-interactive pipelines. +However, sometimes there is a need to run some interfaces quickly from the command line. +This is especially useful when running Interfaces wrapping code that does not have +command line equivalents (nipy or SPM). Being able to run Nipype interfaces opens new +possibilities such as inclusion of SPM processing steps in bash scripts. + +To run Nipype Interafces you need to use the nipype_cmd tool that should already be installed. +The tool allows you to list Interfaces available in a certain package: + +.. testcode:: + + + $nipype_cmd nipype.interfaces.nipy + + Available Interfaces: + SpaceTimeRealigner + Similarity + ComputeMask + FitGLM + EstimateContrast + FmriRealign4d + +After selecting a particular Interface you can learn what inputs it requires: + +.. testcode:: + + + $nipype_cmd nipype.interfaces.nipy ComputeMask --help + + usage:nipype_cmd nipype.interfaces.nipy ComputeMask [-h] [--M M] [--cc CC] + [--ignore_exception IGNORE_EXCEPTION] + [--m M] + [--reference_volume REFERENCE_VOLUME] + mean_volume + + Run ComputeMask + + positional arguments: + mean_volume mean EPI image, used to compute the threshold for the + mask + + optional arguments: + -h, --help show this help message and exit + --M M upper fraction of the histogram to be discarded + --cc CC Keep only the largest connected component + --ignore_exception IGNORE_EXCEPTION + Print an error message instead of throwing an + exception in case the interface fails to run + --m M lower fraction of the histogram to be discarded + --reference_volume REFERENCE_VOLUME + reference volume used to compute the mask. If none is + give, the mean volume is used. + +Finally you can run run the Interface: + +.. testcode:: + + $nipype_cmd nipype.interfaces.nipy ComputeMask mean.nii.gz + +All that from the command line without having to start python interpreter manually. + +.. include:: ../links_names.txt From a58af1fcfa791b68856e8a653b33c120d3c1d32a Mon Sep 17 00:00:00 2001 From: Chris Filo Gorgolewski Date: Wed, 18 Mar 2015 19:22:10 -0700 Subject: [PATCH 19/21] Improved input options description. --- nipype/utils/nipype_cmd.py | 15 ++++++++------ nipype/utils/tests/test_cmd.py | 37 +++++++++++++++++++++------------- 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/nipype/utils/nipype_cmd.py b/nipype/utils/nipype_cmd.py index cd6a028410..2b514e54d8 100644 --- a/nipype/utils/nipype_cmd.py +++ b/nipype/utils/nipype_cmd.py @@ -19,18 +19,21 @@ def add_options(parser=None, module=None, function=None): if parser and module and function: __import__(module) interface = getattr(sys.modules[module],function)() - - for k,v in interface.inputs.items(): - if hasattr(v, "mandatory") and v.mandatory: - parser.add_argument(k, help=v.desc) + + inputs = interface.input_spec() + for name, spec in sorted(interface.inputs.traits(transient=None).items()): + desc = "\n".join(interface._get_trait_desc(inputs, name, spec))[len(name)+2:] + if hasattr(spec, "mandatory") and spec.mandatory: + parser.add_argument(name, help=desc) else: - parser.add_argument("--%s"%k, dest=k, - help=v.desc) + parser.add_argument("--%s"%name, dest=name, + help=desc) return parser, interface def run_instance(interface, options): if interface: print "setting function inputs" + for input_name, _ in interface.inputs.items(): if getattr(options, input_name) != None: value = getattr(options, input_name) diff --git a/nipype/utils/tests/test_cmd.py b/nipype/utils/tests/test_cmd.py index 0d3ce64663..b0868029c4 100644 --- a/nipype/utils/tests/test_cmd.py +++ b/nipype/utils/tests/test_cmd.py @@ -120,31 +120,40 @@ def test_run_4d_realign_help(self): Run FmriRealign4d positional arguments: - in_file File to realign - tr TR in seconds + in_file (a list of items which are an existing file name) File + to realign + tr (a float) TR in seconds optional arguments: -h, --help show this help message and exit --between_loops BETWEEN_LOOPS - loops used to realign different runs + (a list of items which are an integer, nipype default + value: [5]) loops used to realign different runs --ignore_exception IGNORE_EXCEPTION - Print an error message instead of throwing an - exception in case the interface fails to run - --loops LOOPS loops within each run + (a boolean, nipype default value: False) Print an + error message instead of throwing an exception in case + the interface fails to run + --loops LOOPS (a list of items which are an integer, nipype default + value: [5]) loops within each run --slice_order SLICE_ORDER - 0 based slice order. This would be equivalent to + (a list of items which are an integer) 0 based slice + order. This would be equivalent to enteringnp.argsort(spm_slice_order) for this field. This effectsinterleaved acquisition. This field will be deprecated infuture Nipy releases and be replaced - by actual sliceacquisition times. - --speedup SPEEDUP successive image sub-sampling factors for acceleration - --start START time offset into TR to align slices to + by actual sliceacquisition times. requires: + time_interp + --speedup SPEEDUP (a list of items which are an integer, nipype default + value: [5]) successive image sub-sampling factors for + acceleration + --start START (a float, nipype default value: 0.0) time offset into + TR to align slices to --time_interp TIME_INTERP - Assume smooth changes across time e.g., fmri series. - If you don't want slice timing correction set this to - undefined + (True) Assume smooth changes across time e.g., fmri + series. If you don't want slice timing correction set + this to undefined requires: slice_order --tr_slices TR_SLICES - TR slices + (a float) TR slices requires: time_interp """) From f4b94a4ae19b61e6f4640b0a9aa9ffb92efae09a Mon Sep 17 00:00:00 2001 From: Chris Filo Gorgolewski Date: Wed, 18 Mar 2015 19:46:48 -0700 Subject: [PATCH 20/21] make the tests more robust --- nipype/utils/tests/test_cmd.py | 53 ++-------------------------------- 1 file changed, 2 insertions(+), 51 deletions(-) diff --git a/nipype/utils/tests/test_cmd.py b/nipype/utils/tests/test_cmd.py index b0868029c4..9cedd77c5f 100644 --- a/nipype/utils/tests/test_cmd.py +++ b/nipype/utils/tests/test_cmd.py @@ -105,57 +105,8 @@ def test_run_4d_realign_help(self): self.assertEqual(exit_exception.code, 0) self.assertEqual(stderr.getvalue(), '') - self.assertEqual(stdout.getvalue(), -"""usage: nipype_cmd nipype.interfaces.nipy FmriRealign4d [-h] - [--between_loops BETWEEN_LOOPS] - [--ignore_exception IGNORE_EXCEPTION] - [--loops LOOPS] - [--slice_order SLICE_ORDER] - [--speedup SPEEDUP] - [--start START] - [--time_interp TIME_INTERP] - [--tr_slices TR_SLICES] - in_file tr - -Run FmriRealign4d - -positional arguments: - in_file (a list of items which are an existing file name) File - to realign - tr (a float) TR in seconds - -optional arguments: - -h, --help show this help message and exit - --between_loops BETWEEN_LOOPS - (a list of items which are an integer, nipype default - value: [5]) loops used to realign different runs - --ignore_exception IGNORE_EXCEPTION - (a boolean, nipype default value: False) Print an - error message instead of throwing an exception in case - the interface fails to run - --loops LOOPS (a list of items which are an integer, nipype default - value: [5]) loops within each run - --slice_order SLICE_ORDER - (a list of items which are an integer) 0 based slice - order. This would be equivalent to - enteringnp.argsort(spm_slice_order) for this field. - This effectsinterleaved acquisition. This field will - be deprecated infuture Nipy releases and be replaced - by actual sliceacquisition times. requires: - time_interp - --speedup SPEEDUP (a list of items which are an integer, nipype default - value: [5]) successive image sub-sampling factors for - acceleration - --start START (a float, nipype default value: 0.0) time offset into - TR to align slices to - --time_interp TIME_INTERP - (True) Assume smooth changes across time e.g., fmri - series. If you don't want slice timing correction set - this to undefined requires: slice_order - --tr_slices TR_SLICES - (a float) TR slices requires: time_interp -""") - + self.assertTrue("loops used to realign different runs" in stdout.getvalue()) + self.assertTrue("Run FmriRealign4d" in stdout.getvalue()) if __name__ == '__main__': unittest.main() \ No newline at end of file From fbe186d8fa1b6142cd4a0522ef1938e57458931c Mon Sep 17 00:00:00 2001 From: Chris Filo Gorgolewski Date: Wed, 18 Mar 2015 19:54:18 -0700 Subject: [PATCH 21/21] and even more robust... --- nipype/utils/tests/test_cmd.py | 1 - 1 file changed, 1 deletion(-) diff --git a/nipype/utils/tests/test_cmd.py b/nipype/utils/tests/test_cmd.py index 9cedd77c5f..55347ff4b9 100644 --- a/nipype/utils/tests/test_cmd.py +++ b/nipype/utils/tests/test_cmd.py @@ -105,7 +105,6 @@ def test_run_4d_realign_help(self): self.assertEqual(exit_exception.code, 0) self.assertEqual(stderr.getvalue(), '') - self.assertTrue("loops used to realign different runs" in stdout.getvalue()) self.assertTrue("Run FmriRealign4d" in stdout.getvalue()) if __name__ == '__main__':