diff --git a/fsl/utils/run.py b/fsl/utils/run.py index f3a07b38fb483429842a73495d263517556e89b7..0f0fa0a080b8350d1a70e8e801eac01f5a93f8f7 100644 --- a/fsl/utils/run.py +++ b/fsl/utils/run.py @@ -11,18 +11,21 @@ run runfsl - fslsub + wait + dryrun """ import logging import contextlib +import collections import subprocess as sp import os.path as op import six -from fsl.utils.platform import platform as fslplatform +from fsl.utils.platform import platform as fslplatform +import fsl.utils.fslsub as fslsub log = logging.getLogger(__name__) @@ -81,26 +84,52 @@ 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. + The command can be run on a cluster by using the ``submit`` keyword + argument. + An exception is raised if the command returns a non-zero exit code, unless - the ``returnCode`` option is set to ``True``. + the ``ret`` option is set to ``True``. + + :arg submit: Must be passed as a keyword argument. Defaults to ``None``. + Accepted values are ``True`` or a + If ``True``, the command is submitted as a cluster job via + the :func:`.fslsub.submit` function. May also be a + dictionary containing arguments to that function. + + :arg err: Must be passed as a keyword argument. Defaults to + ``False``. If ``True``, standard error is captured and + returned. Ignored if ``submit`` is specified. + + :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. Ignored if ``submit`` is specified. + + :returns: If ``submit`` is provided, the cluster job ID is returned. + Otherwise if ``err is False and ret is False`` (the default) + a string containing the command's standard output. is + returned. Or, if ``err is True`` and/or ``ret is True``, a + tuple containing the standard output, standard error (if + ``err``), and return code (if ``ret``). + """ - :arg err: Must be passed as a keyword argument. Defaults to - ``False``. If ``True``, standard error is captured and - returned. + err = kwargs.get('err', False) + ret = kwargs.get('ret', False) + submit = kwargs.get('ret', None) + args = _prepareArgs(args) - :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. + if not bool(submit): + submit = None - :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``). - """ + if submit is not None: + err = False + ret = False + + if submit is True: + submit = dict() - err = kwargs.get('err', False) - ret = kwargs.get('ret', False) - args = _prepareArgs(args) + if submit is not None and not isinstance(submit, collections.Mapping): + raise ValueError('submit must be a mapping containing ' + 'options for fsl.utils.fslsub.submit') if DRY_RUN: log.debug('dryrun: {}'.format(' '.join(args))) @@ -108,8 +137,15 @@ def run(*args, **kwargs): log.debug('run: {}'.format(' '.join(args))) if DRY_RUN: - stdout = ' '.join(args) stderr = '' + if submit is not None: + stdout = ' '.join(args) + else: + stdout = '[submit] ' + ' '.join(args) + + elif submit is not None: + return fslsub.submit(' '.join(args), **submit) + else: proc = sp.Popen(args, stdout=sp.PIPE, stderr=sp.PIPE) stdout, stderr = proc.communicate() @@ -148,6 +184,6 @@ def runfsl(*args, **kwargs): return run(*args, **kwargs) -def fslsub(*args): - """Not implemented yet. """ - raise NotImplementedError('') +def wait(job_ids): + """Calls :func:`.fslsub.wait` for the given ``job_ids``. """ + return fslsub.wait(job_ids) diff --git a/fsl/wrappers/wrapperutils.py b/fsl/wrappers/wrapperutils.py index 0a3ec94f4d78d01f694856a4f3cef032cf34e4b9..b1a4a18e839853254625b00711727104ee5e51c0 100644 --- a/fsl/wrappers/wrapperutils.py +++ b/fsl/wrappers/wrapperutils.py @@ -141,8 +141,9 @@ def cmdwrapper(func): :func:`fsl.utils.run.run` function in a standardised manner. """ def wrapper(*args, **kwargs): + submit = kwargs.pop('submit', None) cmd = func(*args, **kwargs) - return run.run(cmd, err=True) + return run.run(cmd, err=True, submit=submit) return _update_wrapper(wrapper, func) @@ -152,8 +153,9 @@ def fslwrapper(func): :func:`fsl.utils.run.runfsl` function in a standardised manner. """ def wrapper(*args, **kwargs): + submit = kwargs.pop('submit', None) cmd = func(*args, **kwargs) - return run.runfsl(cmd, err=True) + return run.runfsl(cmd, err=True, submit=submit) return _update_wrapper(wrapper, func)