From c6970189230eb651f2d74459a7d447c284458101 Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Thu, 18 Jan 2018 15:12:18 -0600 Subject: [PATCH 1/7] ENH: Add _cmd_prefix class variable to CommandLine --- nipype/interfaces/base/core.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nipype/interfaces/base/core.py b/nipype/interfaces/base/core.py index 34f650cde2..f1833ec3f6 100644 --- a/nipype/interfaces/base/core.py +++ b/nipype/interfaces/base/core.py @@ -857,6 +857,7 @@ class must be instantiated with a command argument """ input_spec = CommandLineInputSpec + _cmd_prefix = '' _cmd = None _version = None _terminal_output = 'stream' @@ -915,7 +916,7 @@ def cmdline(self): """ `command` plus any arguments (args) validates arguments and generates command line""" self._check_mandatory_inputs() - allargs = [self.cmd] + self._parse_inputs() + allargs = [self._cmd_prefix + self.cmd] + self._parse_inputs() return ' '.join(allargs) @property From c1a71f34896d4d95b3424a9870132f25525955bc Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Thu, 18 Jan 2018 15:45:44 -0600 Subject: [PATCH 2/7] ENH: Update cmdline overrides --- nipype/interfaces/afni/base.py | 13 +++++-------- nipype/interfaces/freesurfer/preprocess.py | 5 +++-- nipype/interfaces/minc/minc.py | 6 +++--- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/nipype/interfaces/afni/base.py b/nipype/interfaces/afni/base.py index becfd828f7..f1a3a18429 100644 --- a/nipype/interfaces/afni/base.py +++ b/nipype/interfaces/afni/base.py @@ -294,13 +294,10 @@ class AFNIPythonCommandInputSpec(CommandLineInputSpec): class AFNIPythonCommand(AFNICommand): @property def cmd(self): - if spawn.find_executable(super(AFNIPythonCommand, - self).cmd) is not None: - return spawn.find_executable(super(AFNIPythonCommand, self).cmd) - else: - return super(AFNIPythonCommand, self).cmd + orig_cmd = super(AFNIPythonCommand, self).cmd + found = spawn.find_executable(orig_cmd) + return found if found is not None else orig_cmd @property - def cmdline(self): - return "{} {}".format(self.inputs.py27_path, - super(AFNIPythonCommand, self).cmdline) + def _cmd_prefix(self): + return "{} ".format(self.inputs.py27_path) diff --git a/nipype/interfaces/freesurfer/preprocess.py b/nipype/interfaces/freesurfer/preprocess.py index 3e70200ad3..b2d6173099 100644 --- a/nipype/interfaces/freesurfer/preprocess.py +++ b/nipype/interfaces/freesurfer/preprocess.py @@ -655,8 +655,9 @@ def cmdline(self): files = self._get_filelist(outdir) for infile, outfile in files: if not os.path.exists(outfile): - single_cmd = '%s %s %s' % (self.cmd, infile, - os.path.join(outdir, outfile)) + single_cmd = '%s%s %s %s' % (self._cmd_prefix, self.cmd, + infile, os.path.join(outdir, + outfile)) cmd.extend([single_cmd]) return '; '.join(cmd) diff --git a/nipype/interfaces/minc/minc.py b/nipype/interfaces/minc/minc.py index a520768bde..4c35248132 100644 --- a/nipype/interfaces/minc/minc.py +++ b/nipype/interfaces/minc/minc.py @@ -1742,15 +1742,15 @@ def _list_outputs(self): @property def cmdline(self): output_file_base = self.inputs.output_file_base + orig_cmdline = super(Blur, self).cmdline if isdefined(output_file_base): - return super(Blur, self).cmdline + return orig_cmdline else: # FIXME this seems like a bit of a hack. Can we force output_file # to show up in cmdline by default, even if it isn't specified in # the instantiation of Pik? - return '%s %s' % (super(Blur, self).cmdline, - self._gen_output_base()) + return '%s %s' % (orig_cmdline, self._gen_output_base()) class MathInputSpec(CommandLineInputSpec): From f8472dff475a8f5603cb9ed9cd5f5d1afaf0d960 Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Fri, 19 Jan 2018 12:13:09 -0600 Subject: [PATCH 3/7] TEST/RF: Check modified command + helper command --- nipype/interfaces/base/core.py | 14 ++++++++- nipype/interfaces/base/tests/test_core.py | 35 +++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/nipype/interfaces/base/core.py b/nipype/interfaces/base/core.py index f1833ec3f6..3c403451d9 100644 --- a/nipype/interfaces/base/core.py +++ b/nipype/interfaces/base/core.py @@ -988,7 +988,11 @@ def _run_interface(self, runtime, correct_return_codes=(0, )): # which $cmd executable_name = self.cmd.split()[0] - cmd_path = which(executable_name, env=runtime.environ) + + prefix_parts = self._cmd_prefix.split() + r_pre = prefix_parts.pop() if prefix_parts else '' + + cmd_path = which(r_pre + executable_name, env=runtime.environ) if cmd_path is None: raise IOError( @@ -996,6 +1000,14 @@ def _run_interface(self, runtime, correct_return_codes=(0, )): 'corresponding package is installed.' % (executable_name, runtime.hostname)) + if prefix_parts: + helper_command = which(prefix_parts[0], env=runtime.environ) + if helper_command is None: + raise IOError( + 'No command "%s" found on host %s. Please check that the ' + 'corresponding package is installed.' % (helper_command, + runtime.hostname)) + runtime.command_path = cmd_path runtime.dependencies = (get_dependencies(executable_name, runtime.environ) diff --git a/nipype/interfaces/base/tests/test_core.py b/nipype/interfaces/base/tests/test_core.py index 05a9f02b47..5170cbf861 100644 --- a/nipype/interfaces/base/tests/test_core.py +++ b/nipype/interfaces/base/tests/test_core.py @@ -480,3 +480,38 @@ def test_global_CommandLine_output(tmpdir): # Check default affects derived interfaces ci = BET() assert ci.terminal_output == 'file' + + +def test_CommandLine_prefix(tmpdir): + tmpdir.chdir() + oop = 'out/of/path' + os.makedirs(oop) + + script_name = 'test_script.sh' + script_path = os.path.join(oop, script_name) + with open(script_path, 'w') as script_f: + script_f.write('#!/usr/bin/env bash\necho Success!') + os.chmod(script_path, 0o755) + + ci = nib.CommandLine(command=script_name) + with pytest.raises(OSError): + ci.run() + + class OOPCLI(nib.CommandLine): + _cmd_prefix = oop + '/' + + ci = OOPCLI(command=script_name) + ci.run() + + class OOPShell(nib.CommandLine): + _cmd_prefix = 'bash {}/'.format(oop) + + ci = OOPShell(command=script_name) + ci.run() + + class OOPBadShell(nib.CommandLine): + _cmd_prefix = 'shell_dne {}/'.format(oop) + + ci = OOPBadShell(command=script_name) + with pytest.raises(OSError): + ci.run() From 73e4d9a53c89d3ae1aa7b855392092bc953b8cb2 Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Sat, 20 Jan 2018 12:26:37 -0600 Subject: [PATCH 4/7] FIX: OSError -> IOError --- nipype/interfaces/base/tests/test_core.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nipype/interfaces/base/tests/test_core.py b/nipype/interfaces/base/tests/test_core.py index 5170cbf861..2c8439403a 100644 --- a/nipype/interfaces/base/tests/test_core.py +++ b/nipype/interfaces/base/tests/test_core.py @@ -494,7 +494,7 @@ def test_CommandLine_prefix(tmpdir): os.chmod(script_path, 0o755) ci = nib.CommandLine(command=script_name) - with pytest.raises(OSError): + with pytest.raises(IOError): ci.run() class OOPCLI(nib.CommandLine): @@ -513,5 +513,5 @@ class OOPBadShell(nib.CommandLine): _cmd_prefix = 'shell_dne {}/'.format(oop) ci = OOPBadShell(command=script_name) - with pytest.raises(OSError): + with pytest.raises(IOError): ci.run() From d491c8b72ccd1afd44fe84de5e44e2bd77f5aa48 Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Sat, 20 Jan 2018 15:28:02 -0600 Subject: [PATCH 5/7] RF: Use shlex to correctly identify the executable --- nipype/interfaces/base/core.py | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/nipype/interfaces/base/core.py b/nipype/interfaces/base/core.py index 3c403451d9..7ed9413d2d 100644 --- a/nipype/interfaces/base/core.py +++ b/nipype/interfaces/base/core.py @@ -26,6 +26,7 @@ import platform import select import subprocess as sp +import shlex import sys from textwrap import wrap import simplejson as json @@ -987,12 +988,8 @@ def _run_interface(self, runtime, correct_return_codes=(0, )): runtime.environ.update(out_environ) # which $cmd - executable_name = self.cmd.split()[0] - - prefix_parts = self._cmd_prefix.split() - r_pre = prefix_parts.pop() if prefix_parts else '' - - cmd_path = which(r_pre + executable_name, env=runtime.environ) + executable_name = shlex.split(self._cmd_prefix + self.cmd)[0] + cmd_path = which(executable_name, env=runtime.environ) if cmd_path is None: raise IOError( @@ -1000,14 +997,6 @@ def _run_interface(self, runtime, correct_return_codes=(0, )): 'corresponding package is installed.' % (executable_name, runtime.hostname)) - if prefix_parts: - helper_command = which(prefix_parts[0], env=runtime.environ) - if helper_command is None: - raise IOError( - 'No command "%s" found on host %s. Please check that the ' - 'corresponding package is installed.' % (helper_command, - runtime.hostname)) - runtime.command_path = cmd_path runtime.dependencies = (get_dependencies(executable_name, runtime.environ) From 2d3028cb86dc59546392c7ce6eb2e43eb515bd46 Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Sat, 20 Jan 2018 17:31:59 -0600 Subject: [PATCH 6/7] ENH: Better emulate Python 3.3+ shutil.which --- nipype/utils/filemanip.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/nipype/utils/filemanip.py b/nipype/utils/filemanip.py index b88bcbd257..d1a9df6a04 100644 --- a/nipype/utils/filemanip.py +++ b/nipype/utils/filemanip.py @@ -774,12 +774,20 @@ def which(cmd, env=None, pathext=None): return filename return None + def isexec(path): + return os.path.isfile(path) and os.access(path, os.X_OK) + for ext in pathext: extcmd = cmd + ext - for directory in path.split(os.pathsep): - filename = op.join(directory, extcmd) - if op.exists(filename): - return filename + fpath, fname = os.path.split(extcmd) + if fpath: + if isexec(fpath): + return extcmd + else: + for directory in path.split(os.pathsep): + filename = op.join(directory, extcmd) + if isexec(filename): + return filename return None From 822d34de4e0ff8005ef6fd7673d2be33384f6924 Mon Sep 17 00:00:00 2001 From: Chris Markiewicz Date: Sat, 20 Jan 2018 20:29:58 -0600 Subject: [PATCH 7/7] FIX: Check correct path --- nipype/utils/filemanip.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nipype/utils/filemanip.py b/nipype/utils/filemanip.py index d1a9df6a04..24820bfbd8 100644 --- a/nipype/utils/filemanip.py +++ b/nipype/utils/filemanip.py @@ -781,7 +781,7 @@ def isexec(path): extcmd = cmd + ext fpath, fname = os.path.split(extcmd) if fpath: - if isexec(fpath): + if isexec(extcmd): return extcmd else: for directory in path.split(os.pathsep):