From 4026cbba710aeba0904b9d14c1bac9360f500f6f Mon Sep 17 00:00:00 2001
From: Paul McCarthy <pauldmccarthy@gmail.com>
Date: Mon, 16 Mar 2020 15:13:41 +0000
Subject: [PATCH] RF: FileOrThing passes-through if submit=True, and errors if
 in-memory or LOAD is used in conjunction with submit=True

---
 fsl/wrappers/wrapperutils.py | 64 ++++++++++++++++++++++++++++++------
 1 file changed, 54 insertions(+), 10 deletions(-)

diff --git a/fsl/wrappers/wrapperutils.py b/fsl/wrappers/wrapperutils.py
index 81fb86826..41ed391e2 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
-- 
GitLab