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

Merge branch 'enh/tbss_wrappers' into 'master'

Enh/tbss wrappers

See merge request fsl/fslpy!260
parents ded1c5af 2e878669
No related branches found
No related tags found
No related merge requests found
...@@ -3,4 +3,5 @@ Michiel Cottaar <michiel.cottaar@ndcn.ox.ac.uk> ...@@ -3,4 +3,5 @@ Michiel Cottaar <michiel.cottaar@ndcn.ox.ac.uk>
Matthew Webster <matthew.webster@ndcn.ox.ac.uk> Matthew Webster <matthew.webster@ndcn.ox.ac.uk>
Sean Fitzgibbon <sean.fitzgibbon@ndcn.ox.ac.uk> Sean Fitzgibbon <sean.fitzgibbon@ndcn.ox.ac.uk>
Martin Craig <martin.craig@eng.ox.ac.uk> Martin Craig <martin.craig@eng.ox.ac.uk>
Taylor Hanayik <taylor.hanayik@ndcn.ox.ac.uk> Taylor Hanayik <taylor.hanayik@ndcn.ox.ac.uk>
\ No newline at end of file Evan Edmond <evan.edmond@ndcn.ox.ac.uk>
\ No newline at end of file
...@@ -2,6 +2,26 @@ This document contains the ``fslpy`` release history in reverse chronological ...@@ -2,6 +2,26 @@ This document contains the ``fslpy`` release history in reverse chronological
order. order.
3.4.0 (Tuesday 20th October 2020)
---------------------------------
Added
^^^^^
* New :mod:`.tbss` wrapper functions for `TBSS
<https://fsl.fmrib.ox.ac.uk/fsl/fslwiki/TBSS>`_ commands.
Changed
^^^^^^^
* Calls to functions in the :mod:`.assertions` module are disabled when a
wrapper function is called with ``cmdonly=True``.
3.3.3 (Wednesday 13th October 2020) 3.3.3 (Wednesday 13th October 2020)
----------------------------------- -----------------------------------
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
fsl.wrappers.fugue fsl.wrappers.fugue
fsl.wrappers.melodic fsl.wrappers.melodic
fsl.wrappers.misc fsl.wrappers.misc
fsl.wrappers.tbss
fsl.wrappers.wrapperutils fsl.wrappers.wrapperutils
.. automodule:: fsl.wrappers .. automodule:: fsl.wrappers
......
``fsl.wrappers.tbss``
=====================
.. automodule:: fsl.wrappers.tbss
:members:
:undoc-members:
:show-inheritance:
...@@ -33,25 +33,32 @@ import fsl.utils.ensure as ensure ...@@ -33,25 +33,32 @@ import fsl.utils.ensure as ensure
import fsl.data.melodicanalysis as fslma import fsl.data.melodicanalysis as fslma
_DISABLE_ASSERTIONS = False _DISABLE_ASSERTIONS = 0
""" """Semaphore used by the :func:`disabled` context manager. """
"""
@contextlib.contextmanager @contextlib.contextmanager
def disabled(): def disabled(disable=True):
"""Context manager which allows assertion checks to be temporarily """Context manager which allows assertion checks to be temporarily
disabled. disabled.
If calls to this function are nested, only one of the calls need to be made
with ``disable=True`` for assertions to be disabled; any other calls which
are part of the call stack which set ``disable=False`` will have no effect.
:arg disable: Set to ``True`` (the default) to disable assertions,
or ``False`` to enable them.
""" """
global _DISABLE_ASSERTIONS global _DISABLE_ASSERTIONS
oldval = _DISABLE_ASSERTIONS if disable:
_DISABLE_ASSERTIONS = True _DISABLE_ASSERTIONS += 1
try: try:
yield yield
finally: finally:
_DISABLE_ASSERTIONS = oldval if disable:
_DISABLE_ASSERTIONS -= 1
def _canDisable(func): def _canDisable(func):
...@@ -59,7 +66,7 @@ def _canDisable(func): ...@@ -59,7 +66,7 @@ def _canDisable(func):
via the :func:`disabled` context manager. via the :func:`disabled` context manager.
""" """
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
if not _DISABLE_ASSERTIONS: if _DISABLE_ASSERTIONS == 0:
return func(*args, **kwargs) return func(*args, **kwargs)
return wrapper return wrapper
......
...@@ -47,7 +47,7 @@ import re ...@@ -47,7 +47,7 @@ import re
import string import string
__version__ = '3.4.0.dev0' __version__ = '3.5.0.dev0'
"""Current version number, as a string. """ """Current version number, as a string. """
......
...@@ -109,3 +109,4 @@ from .misc import (fslreorient2std, # noqa ...@@ -109,3 +109,4 @@ from .misc import (fslreorient2std, # noqa
slicer, slicer,
cluster, cluster,
gps) gps)
from . import tbss # noqa
#!/usr/bin/env python3
#
# tbss.py - Wrappers for FSL command-line tools for tract based spatial
# statistics (TBSS).
#
# Author: Evan Edmond <eedmond@gmail.com>
#
"""This module contains wrapper functions for various `TBSS
<https://fsl.fmrib.ox.ac.uk/fsl/fslwiki/TBSS/>`_ command-line tools.
"""
import fsl.utils.assertions as asrt
from . import wrapperutils as wutils
@wutils.fslwrapper
def preproc(*images):
"""Wrapper for the ``tbss_1_preproc`` command.
Usage: ``tbss_1_preproc(<image1>, <image2>, ...)``
"""
for img in images:
asrt.assertIsNifti(img)
return ["tbss_1_preproc"] + list(images)
@wutils.fslwrapper
def reg(**kwargs):
"""Wrapper for the ``tbss_2_reg`` command.
Refer to the ``tbss_2_reg`` command-line help for details on all arguments.
"""
valmap = {
'T' : wutils.SHOW_IF_TRUE,
'n' : wutils.SHOW_IF_TRUE,
}
cmd = ["tbss_2_reg"]
cmd += wutils.applyArgStyle("-", valmap=valmap, valsep=" ", **kwargs)
return cmd
@wutils.fslwrapper
def postreg(**kwargs):
"""Wrapper for the ``tbss_3_postreg`` command.
Refer to the ``tbss_3_postreg`` command-line help for details on all
arguments.
"""
valmap = {
'T' : wutils.SHOW_IF_TRUE,
'S' : wutils.SHOW_IF_TRUE,
}
cmd = ["tbss_3_postreg"]
cmd += wutils.applyArgStyle("-", valmap=valmap, **kwargs)
return cmd
@wutils.fslwrapper
def prestats(threshold):
"""Wrapper for the ``tbss_4_prestats`` command.
The normal recommendation for <threshold> is 0.2
"""
return ["tbss_4_prestats", f'{threshold}']
@wutils.fslwrapper
def non_FA(alt_img_root):
"""Wrapper for the ``tbss_non_FA`` command.
e.g.: ``tbss_non_FA("L2")``
"""
return ["tbss_non_FA", alt_img_root]
@wutils.fileOrImage("stats_image", "mean_FA", "output")
@wutils.fslwrapper
def fill(stats_image, threshold, mean_FA, output, **kwargs):
"""Wrapper for the ``tbss_fill`` command.
Refer to the ``tbss_fill`` command-line help for details on all arguments.
"""
valmap = {
'n' : wutils.SHOW_IF_TRUE,
}
cmd = ["tbss_fill", stats_image, f'{threshold}', mean_FA, output]
cmd += wutils.applyArgStyle("-", valmap=valmap, **kwargs)
return cmd
...@@ -108,10 +108,11 @@ import six ...@@ -108,10 +108,11 @@ import six
import nibabel as nib import nibabel as nib
import numpy as np import numpy as np
import fsl.utils.run as run import fsl.utils.run as run
import fsl.utils.path as fslpath import fsl.utils.assertions as asrt
import fsl.utils.tempdir as tempdir import fsl.utils.path as fslpath
import fsl.data.image as fslimage import fsl.utils.tempdir as tempdir
import fsl.data.image as fslimage
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
...@@ -185,7 +186,14 @@ def genxwrapper(func, runner): ...@@ -185,7 +186,14 @@ def genxwrapper(func, runner):
submit = kwargs.pop('submit', None) submit = kwargs.pop('submit', None)
cmdonly = kwargs.pop('cmdonly', False) cmdonly = kwargs.pop('cmdonly', False)
log = kwargs.pop('log', {'tee' : True}) log = kwargs.pop('log', {'tee' : True})
cmd = func(*args, **kwargs)
# many wrapper functions use fsl.utils.assertions
# statements to check that input arguments are
# valid. Disable these if the cmdonly argument is
# being used to generate a command without running
# it.
with asrt.disabled(cmdonly):
cmd = func(*args, **kwargs)
return runner(cmd, return runner(cmd,
stderr=stderr, stderr=stderr,
......
...@@ -357,3 +357,31 @@ def test_gps(): ...@@ -357,3 +357,31 @@ def test_gps():
expected = (gps + ' --ndir=128 --out=bvecs', expected = (gps + ' --ndir=128 --out=bvecs',
('--optws', '--ranseed=123')) ('--optws', '--ranseed=123'))
assert checkResult(result.stdout[0], *expected) assert checkResult(result.stdout[0], *expected)
def test_tbss():
exes = {
'preproc' : 'tbss_1_preproc',
'reg' : 'tbss_2_reg',
'postreg' : 'tbss_3_postreg',
'prestats' : 'tbss_4_prestats',
'non_FA' : 'tbss_non_FA',
'fill' : 'tbss_fill'
}
with asrt.disabled(), \
run.dryrun(), \
mockFSLDIR(bin=exes.values()) as fsldir:
for k in exes:
exes[k] = op.join(fsldir, 'bin', exes[k])
assert fw.tbss.preproc('1', '2')[0] == ' '.join([exes['preproc'], '1', '2'])
assert fw.tbss.reg(T=True)[0] == ' '.join([exes['reg'], '-T'])
assert fw.tbss.reg(n=True)[0] == ' '.join([exes['reg'], '-n'])
assert fw.tbss.reg(t='target')[0] == ' '.join([exes['reg'], '-t', 'target'])
assert fw.tbss.postreg(S=True)[0] == ' '.join([exes['postreg'], '-S'])
assert fw.tbss.postreg(T=True)[0] == ' '.join([exes['postreg'], '-T'])
assert fw.tbss.prestats(0.3)[0] == ' '.join([exes['prestats'], '0.3'])
assert fw.tbss.non_FA('alt')[0] == ' '.join([exes['non_FA'], 'alt'])
assert fw.tbss.fill('stat', 0.4, 'mean_fa', 'output', n=True).stdout[0] == \
' '.join([exes['fill'], 'stat', '0.4', 'mean_fa', 'output', '-n'])
...@@ -22,12 +22,13 @@ import nibabel as nib ...@@ -22,12 +22,13 @@ import nibabel as nib
import fsl.utils.tempdir as tempdir import fsl.utils.tempdir as tempdir
import fsl.utils.run as run import fsl.utils.run as run
import fsl.utils.assertions as asrt
import fsl.utils.fslsub as fslsub import fsl.utils.fslsub as fslsub
import fsl.data.image as fslimage import fsl.data.image as fslimage
import fsl.wrappers.wrapperutils as wutils import fsl.wrappers.wrapperutils as wutils
from .. import mockFSLDIR, cleardir, checkdir, testdir from .. import mockFSLDIR, cleardir, checkdir, testdir, touch
from ..test_run import mock_submit from ..test_run import mock_submit
...@@ -861,3 +862,19 @@ def test_cmdwrapper_fileorthing_cmdonly(): ...@@ -861,3 +862,19 @@ def test_cmdwrapper_fileorthing_cmdonly():
cmd = test_func('1', '2', cmdonly=True) cmd = test_func('1', '2', cmdonly=True)
assert ran.stdout[0].strip() == 'test_script running: 1 2' assert ran.stdout[0].strip() == 'test_script running: 1 2'
assert cmd == ['test_script', '1', '2'] assert cmd == ['test_script', '1', '2']
def test_cmdwrapper_cmdonly_assert():
@wutils.cmdwrapper
def func():
asrt.assertFileExists('file')
return ['echo', 'hello']
with tempdir.tempdir():
with pytest.raises(AssertionError):
func()
touch('file')
assert func()[0].strip() == 'hello'
os.remove('file')
assert func(cmdonly=True) == ['echo', 'hello']
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