diff --git a/fsl/wrappers/wrapperutils.py b/fsl/wrappers/wrapperutils.py index 81fb86826849b6a4eb55c9012f71c0c61cf40cc6..41ed391e2852f8a05a7198ca728a8f5f4f8dd285 100644 --- a/fsl/wrappers/wrapperutils.py +++ b/fsl/wrappers/wrapperutils.py @@ -154,12 +154,12 @@ def cmdwrapper(func): :func:`fsl.utils.run.run` function in a standardised manner. """ def wrapper(*args, **kwargs): - stdout = kwargs.pop('stdout', True) - stderr = kwargs.pop('stderr', True) + stdout = kwargs.pop('stdout', True) + stderr = kwargs.pop('stderr', True) exitcode = kwargs.pop('exitcode', False) - submit = kwargs.pop('submit', None) - log = kwargs.pop('log', {'tee' : True}) - cmd = func(*args, **kwargs) + submit = kwargs.pop('submit', None) + log = kwargs.pop('log', {'tee' : True}) + cmd = func(*args, **kwargs) return run.run(cmd, stderr=stderr, log=log, @@ -175,12 +175,12 @@ def fslwrapper(func): :func:`fsl.utils.run.runfsl` function in a standardised manner. """ def wrapper(*args, **kwargs): - stdout = kwargs.pop('stdout', True) - stderr = kwargs.pop('stderr', True) + stdout = kwargs.pop('stdout', True) + stderr = kwargs.pop('stderr', True) exitcode = kwargs.pop('exitcode', False) - submit = kwargs.pop('submit', None) - log = kwargs.pop('log', {'tee' : True}) - cmd = func(*args, **kwargs) + submit = kwargs.pop('submit', None) + log = kwargs.pop('log', {'tee' : True}) + cmd = func(*args, **kwargs) return run.runfsl(cmd, stderr=stderr, log=log, @@ -452,6 +452,25 @@ class _FileOrThing(object): generated by the function will not be present in the dictionary. + **Cluster submission** + + + The above description holds in all situations, except when an argument + called ``submit`` is passed, and is set to ``True``. In this case, the + ``_FileOrThing`` decorator will pass all arguments straight through to the + decorated function, and will return its return value unchanged. + + + This is because most functions that are decorated with the + :func:`fileOrImage` or :func:`fileOrArray` decorators will invoke a call + to :func:`.run` or :func:`.runfsl`, where a value of ``submit=True`` will + cause the command to be executed asynchronously on a cluster platform. + + + A :exc:`ValueError` will be raised if the decorated function is called + with ``submit=True``, and with any in-memory objects or ``LOAD`` symbols. + + **Example** @@ -528,9 +547,13 @@ class _FileOrThing(object): The decorated function's actual return value is accessible via the :meth:`output` property. """ + + def __init__(self, output): + super().__init__() self.__output = output + @property def output(self): """Access the return value of the decorated function. """ @@ -604,6 +627,27 @@ class _FileOrThing(object): func = self.__func argnames = namedPositionals(func, args) + # Special case - if fsl.utils.run[fsl] is + # being decorated (e.g. via cmdwrapper/ + # fslwrapper), and submit=True, this call + # will ultimately submit the job to the + # cluster, and will return immediately. + # + # We error if we are given any in-memory + # things, or LOAD symbols. + # + # n.b. testing values to be strings could + # interfere with the fileOrText decorator. + # Possible solution is to use pathlib? + if kwargs.get('submit', False): + allargs = {**dict(zip(argnames, args)), **kwargs} + for name, val in allargs.items(): + if (name in self.__things) and \ + (not isinstance(val, six.string_types)): + raise ValueError('Cannot use in-memory objects ' + 'or LOAD with submit=True!') + return func(*args, **kwargs) + # If this _FileOrThing is being called # by another _FileOrThing don't create # another working directory. We do this