diff --git a/fsl/utils/run.py b/fsl/utils/run.py index b818503600d2d86b5e028cacf4a271921b37c008..af52da2bf34160a9e7a82a9817fde7aed2652e8c 100644 --- a/fsl/utils/run.py +++ b/fsl/utils/run.py @@ -34,6 +34,13 @@ execute them. """ +class FSLNotPresent(Exception): + """Error raised by the :func:`runfsl` function when ``$FSLDIR`` cannot + be found. + """ + pass + + @contextlib.contextmanager def dryrun(*args): """Context manager which causes all calls to :func:`run` to be logged but @@ -68,11 +75,29 @@ def _prepareArgs(args): return list(args) -def run(*args): +def run(*args, **kwargs): """Call a command and return its output. You can pass the command and arguments as a single string, or as a regular or unpacked sequence. + + An exception is raised if the command returns a non-zero exit code, unless + the ``returnCode`` option is set to ``True``. + + :arg err: Must be passed as a keyword argument. Defaults to + ``False``. If ``True``, standard error is captured and + returned. + + :arg ret: Must be passed as a keyword argument. Defaults to + ``False``. If ``True``, and the command's return code + is non-0, an exception is not raised. + + :returns: A string containing the command's standard output. Or, + if ``err is True`` and/or ``returnCode is True``, a tuple + containing the standard output, standard error (if + ``err``), and return code (if ``returnCode``). """ + err = kwargs.get('err', False) + ret = kwargs.get('ret', False) args = _prepareArgs(args) if DRY_RUN: @@ -81,13 +106,30 @@ def run(*args): log.debug('run: {}'.format(' '.join(args))) if DRY_RUN: - result = '<dryrun>' + stdout = '<dryrun>' + stderr = '' else: - result = sp.check_output(args).decode('utf-8').strip() + proc = sp.Popen(args, stdout=sp.PIPE, stderr=sp.PIPE) + stdout, stderr = proc.communicate() + retcode = proc.returncode + + stdout = stdout.decode('utf-8').strip() + stderr = stderr.decode('utf-8').strip() + + log.debug('stdout: {}'.format(stdout)) + log.debug('stderr: {}'.format(stderr)) + + if not ret and (retcode != 0): + raise RuntimeError('{} returned non-zero exit code: {}'.format( + args[0], retcode)) + + results = [stdout] - log.debug('result: {}'.format(result)) + if err: results.append(stderr) + if ret: results.append(retcode) - return result + if len(results) == 1: return results[0] + else: return tuple(results) def runfsl(*args): @@ -96,7 +138,7 @@ def runfsl(*args): """ if fslplatform.fsldir is None: - raise RuntimeError('$FSLDIR is not set - FSL cannot be found!') + raise FSLNotPresent('$FSLDIR is not set - FSL cannot be found!') args = _prepareArgs(args) args[0] = op.join(fslplatform.fsldir, 'bin', args[0])