Skip to content
Snippets Groups Projects
Commit 73090bd9 authored by Paul McCarthy's avatar Paul McCarthy :mountain_bicyclist:
Browse files

Added new cmdwrapper and fslwrapper decorators so the run calls can

be standardised. Subsequent refactorings to _FileOrThing class, to make
sure that wrapper functions can be updated correctly. runfsl function
passes kwargs through to run.
parent 51123396
No related branches found
No related tags found
No related merge requests found
...@@ -132,7 +132,7 @@ def run(*args, **kwargs): ...@@ -132,7 +132,7 @@ def run(*args, **kwargs):
else: return tuple(results) else: return tuple(results)
def runfsl(*args): def runfsl(*args, **kwargs):
"""Call a FSL command and return its output. This function simply prepends """Call a FSL command and return its output. This function simply prepends
``$FSLDIR/bin/`` to the command before passing it to :func:`run`. ``$FSLDIR/bin/`` to the command before passing it to :func:`run`.
""" """
...@@ -143,7 +143,7 @@ def runfsl(*args): ...@@ -143,7 +143,7 @@ def runfsl(*args):
args = _prepareArgs(args) args = _prepareArgs(args)
args[0] = op.join(fslplatform.fsldir, 'bin', args[0]) args[0] = op.join(fslplatform.fsldir, 'bin', args[0])
return run(*args) return run(*args, **kwargs)
def fslsub(*args): def fslsub(*args):
......
...@@ -8,13 +8,71 @@ ...@@ -8,13 +8,71 @@
"""This module contains functions and decorators used by the FSL wrapper """This module contains functions and decorators used by the FSL wrapper
functions. functions.
.. autosummary::
:nosignatures:
applyArgStyle The :func:`cmdwrapper` and :func:`fslwrapper` functions are conenience
required decorators which allow you to write your wrapper function such that it simply
fileOrImage generates the command-line needed to respectively run a standard shell
fileOrArray command or a FSL command. For example::
@fslwrapper
def fslreorient2std(input, output):
return ['fslreorient2std', input, output]
When this ``fslreorient2std`` function is called, the ``fslwrapper`` decorator
will take care of invoking the command in a standardised way.
.. note:: The :func:`fslwrapper` and :func:`cmdwrapper` should always be
the _first_ decorator applied to a function.
The :func:`applyArgStyle` function can be used to automatically generate
keyword arguments into command-line arguments, based on a set of standard
patterns. For example::
@fslwrapper
def flirt(src, ref, **kwargs):
cmd = ['flirt', '-in', src, '-ref', ref]
return cmd + applyArgStyle('-=', **kwargs)
The :func:`fileOrImage` and :func:`fileOrArray` functions can be used to
decorate a wrapper function such that in-memory ``nibabel`` images or Numpy
arrays can be passed in as arguments - they will be automatically saved out to
files, and then the file names passed into the wrapper function. For exmaple::
@fileOrImage('src', 'ref')
@fslwrapper
def flirt(src, ref, **kwargs):
cmd = ['flirt', '-in', src, '-ref', ref]
return cmd + applyArgStyle('-=', **kwargs)
Now this ``flirt`` function can be called either with file names, or
``nibabel`` images.
Command outputs can also be loaded back into memory by using the special
:data:`LOAD` value when calling a wrapper function. For example::
@fileOrImage('src', 'ref', 'out')
@fslwrapper
def flirt(src, ref, **kwargs):
cmd = ['flirt', '-in', src, '-ref', ref]
return cmd + applyArgStyle('-=', **kwargs)
If we set the ``out`` argument to ``LOAD``, the output image will be loaded
and returned::
src = nib.load('src.nii')
ref = nib.load('ref.nii')
aligned = flirt(src, ref, out=LOAD)['out']
""" """
...@@ -32,6 +90,7 @@ import nibabel as nib ...@@ -32,6 +90,7 @@ import nibabel as nib
import numpy as np import numpy as np
import fsl.utils.tempdir as tempdir import fsl.utils.tempdir as tempdir
import fsl.utils.run as run
import fsl.data.image as fslimage import fsl.data.image as fslimage
...@@ -67,6 +126,28 @@ def _unwrap(func): ...@@ -67,6 +126,28 @@ def _unwrap(func):
return func return func
def cmdwrapper(func):
"""This decorator can be used on functions which generate a command line.
It will pass the return value of the function to the
:func:`fsl.utils.run.run` function in a standardised manner.
"""
def wrapper(*args, **kwargs):
cmd = func(*args, **kwargs)
return run.run(cmd, err=True)
return _update_wrapper(wrapper, func)
def fslwrapper(func):
"""This decorator can be used on functions which generate a FSL command
line. It will pass the return value of the function to the
:func:`fsl.utils.run.runfsl` function in a standardised manner.
"""
def wrapper(*args, **kwargs):
cmd = func(*args, **kwargs)
return run.runfsl(cmd, err=True)
return _update_wrapper(wrapper, func)
SHOW_IF_TRUE = object() SHOW_IF_TRUE = object()
"""Constant to be used in the ``valmap`` passed to the :func:`applyArgStyle` """Constant to be used in the ``valmap`` passed to the :func:`applyArgStyle`
function. function.
...@@ -421,9 +502,11 @@ class _FileOrThing(object): ...@@ -421,9 +502,11 @@ class _FileOrThing(object):
return self.__output return self.__output
def __init__(self, prepIn, prepOut, load, *things): def __init__(self, func, prepIn, prepOut, load, *things):
"""Initialise a ``_FileOrThing`` decorator. """Initialise a ``_FileOrThing`` decorator.
:arg func: The function to be decorated.
:arg prepIn: Function which returns a file name to be used in :arg prepIn: Function which returns a file name to be used in
place of an input argument. place of an input argument.
...@@ -449,48 +532,18 @@ class _FileOrThing(object): ...@@ -449,48 +532,18 @@ class _FileOrThing(object):
- The argument value that was passed in - The argument value that was passed in
""" """
self.__func = func
self.__prepIn = prepIn self.__prepIn = prepIn
self.__prepOut = prepOut self.__prepOut = prepOut
self.__load = load self.__load = load
self.__things = things self.__things = things
self.__func = None
def __call__(self, *args, **kwargs): def __call__(self, *args, **kwargs):
"""Creates and returns the decorated function. """
# the first call will be our decorated
# function getting passed in.
if self.__func is None:
func = args[0]
self.__func = func
return self
# Subsequent calls will be calls
# to the decorated function.
else:
return self.__wrapper(*args, **kwargs)
def __get__(self, instance, owner):
"""Called when this ``_FileOrThing`` has been used to decorate a method
of a class. When it is accessed on an instance, the instance is added
as the first argument to the wrapper function.
"""
if instance is None:
return self
else:
wrapper = functools.partial(self.__call__, instance)
return _update_wrapper(wrapper, self.__call__)
def __wrapper(self, *args, **kwargs):
"""Function which calls ``func``, ensuring that any arguments of """Function which calls ``func``, ensuring that any arguments of
type ``Thing`` are saved to temporary files, and any arguments type ``Thing`` are saved to temporary files, and any arguments
with the value :data:`LOAD` are loaded and returned. with the value :data:`LOAD` are loaded and returned.
:arg func: The function being wrapped.
All other arguments are passed through to ``func``. All other arguments are passed through to ``func``.
""" """
...@@ -619,7 +672,15 @@ def fileOrImage(*imgargs): ...@@ -619,7 +672,15 @@ def fileOrImage(*imgargs):
img = nib.load(path) img = nib.load(path)
return nib.nifti1.Nifti1Image(img.get_data(), None, img.header) return nib.nifti1.Nifti1Image(img.get_data(), None, img.header)
return _FileOrThing(prepIn, prepOut, load, *imgargs) def decorator(func):
fot = _FileOrThing(func, prepIn, prepOut, load, *imgargs)
def wrapper(*args, **kwargs):
return fot(*args, **kwargs)
return _update_wrapper(wrapper, func)
return decorator
def fileOrArray(*arrargs): def fileOrArray(*arrargs):
...@@ -643,4 +704,12 @@ def fileOrArray(*arrargs): ...@@ -643,4 +704,12 @@ def fileOrArray(*arrargs):
load = np.loadtxt load = np.loadtxt
return _FileOrThing(prepIn, prepOut, load, *arrargs) def decorator(func):
fot = _FileOrThing(func, prepIn, prepOut, load, *arrargs)
def wrapper(*args, **kwargs):
return fot(*args, **kwargs)
return _update_wrapper(wrapper, func)
return decorator
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment