Commit cbeca106 authored by Paul McCarthy's avatar Paul McCarthy 🚵
Browse files

Merge branch 'rel/3.3.3' into 'v3.3'

Rel/3.3.3

See merge request fsl/fslpy!260
parents 17ffd5f8 0941fd5f
...@@ -2,6 +2,20 @@ This document contains the ``fslpy`` release history in reverse chronological ...@@ -2,6 +2,20 @@ This document contains the ``fslpy`` release history in reverse chronological
order. order.
3.3.3 (Wednesday 13th October 2020)
-----------------------------------
Changed
^^^^^^^
* The :func:`.fileOrImage` (and related) decorators will not manipulate the
return value of a decorated function if an argument ``cmdonly=True`` is
passed. This is so that wrapper functions will directly return the command
that would be executed when ``cmdonly=True``.
3.3.2 (Tuesday 12th October 2020) 3.3.2 (Tuesday 12th October 2020)
--------------------------------- ---------------------------------
......
...@@ -47,7 +47,7 @@ import re ...@@ -47,7 +47,7 @@ import re
import string import string
__version__ = '3.3.2' __version__ = '3.3.3'
"""Current version number, as a string. """ """Current version number, as a string. """
......
...@@ -482,25 +482,27 @@ class FileOrThing(object): ...@@ -482,25 +482,27 @@ class FileOrThing(object):
the dictionary. the dictionary.
**Cluster submission** **Exceptions**
The above description holds in all situations, except when an argument The above description holds in all situations, except when arguments called
called ``submit`` is passed, and is set to a value which evaluates to ``submit`` and/or ``cmdonly`` are passed, and are set to values which
``True``. In this case, the ``FileOrThing`` decorator will pass all evaluate to ``True``. In this case, the ``FileOrThing`` decorator will pass
arguments straight through to the decorated function, and will return its all arguments straight through to the decorated function, and will return
return value unchanged. its return value unchanged.
This is because most functions that are decorated with the This is because most functions that are decorated with the
:func:`fileOrImage` or :func:`fileOrArray` decorators will invoke a call :func:`fileOrImage` or :func:`fileOrArray` decorators will invoke a call
to :func:`.run.run` or :func:`.runfsl`, where a value of ``submit=True`` to :func:`.run.run` or :func:`.runfsl`, where:
will cause the command to be executed asynchronously on a cluster
platform.
- a value of ``submit=True`` will cause the command to be executed
asynchronously on a cluster platform.
- a value of ``cmdonly=True`` will cause the command to *not* be executed,
but instead the command that would have been executed is returned.
A :exc:`ValueError` will be raised if the decorated function is called A :exc:`ValueError` will be raised if the decorated function is called
with ``submit=True``, and with any in-memory objects or ``LOAD`` symbols. with ``submit=True`` and/or ``cmdonly=True``, and with any in-memory
objects or ``LOAD`` symbols.
**Example** **Example**
...@@ -684,9 +686,11 @@ class FileOrThing(object): ...@@ -684,9 +686,11 @@ class FileOrThing(object):
# Special case - if fsl.utils.run[fsl] is # Special case - if fsl.utils.run[fsl] is
# being decorated (e.g. via cmdwrapper/ # being decorated (e.g. via cmdwrapper/
# fslwrapper), and submit=True, this call # fslwrapper), and submit=True or
# will ultimately submit the job to the # cmdonly=True, this call will ultimately
# cluster, and will return immediately. # submit the job to the cluster, or will
# return the command that would have been
# executed, and will return immediately.
# #
# We error if we are given any in-memory # We error if we are given any in-memory
# things, or LOAD symbols. # things, or LOAD symbols.
...@@ -694,7 +698,8 @@ class FileOrThing(object): ...@@ -694,7 +698,8 @@ class FileOrThing(object):
# n.b. testing values to be strings could # n.b. testing values to be strings could
# interfere with the fileOrText decorator. # interfere with the fileOrText decorator.
# Possible solution is to use pathlib? # Possible solution is to use pathlib?
if kwargs.get('submit', False): if kwargs.get('submit', False) or \
kwargs.get('cmdonly', False):
allargs = {**dict(zip(argnames, args)), **kwargs} allargs = {**dict(zip(argnames, args)), **kwargs}
for name, val in allargs.items(): for name, val in allargs.items():
if (name in self.__things) and \ if (name in self.__things) and \
......
...@@ -714,13 +714,15 @@ def test_fileOrThing_chained_outprefix(): ...@@ -714,13 +714,15 @@ def test_fileOrThing_chained_outprefix():
assert np.all(res['out_array'] == exparr) assert np.all(res['out_array'] == exparr)
def test_fileOrThing_submit(): def test_fileOrThing_submit_cmdonly():
@wutils.fileOrImage('input', 'output') @wutils.fileOrImage('input', 'output')
def func(input, output, submit=False): def func(input, output, submit=False, cmdonly=False):
if submit: if submit:
return 'submitted!' return 'submitted!'
if cmdonly:
return 'cmdonly!'
img = nib.load(input) img = nib.load(input)
img = nib.nifti1.Nifti1Image(np.asanyarray(img.dataobj) * 2, np.eye(4)) img = nib.nifti1.Nifti1Image(np.asanyarray(img.dataobj) * 2, np.eye(4))
...@@ -735,7 +737,8 @@ def test_fileOrThing_submit(): ...@@ -735,7 +737,8 @@ def test_fileOrThing_submit():
result = func(img, wutils.LOAD) result = func(img, wutils.LOAD)
assert np.all(np.asanyarray(result['output'].dataobj) == exp) assert np.all(np.asanyarray(result['output'].dataobj) == exp)
assert func('input.nii.gz', 'output.nii.gz', submit=True) == 'submitted!' assert func('input.nii.gz', 'output.nii.gz', submit=True) == 'submitted!'
assert func('input.nii.gz', 'output.nii.gz', cmdonly=True) == 'cmdonly!'
with pytest.raises(ValueError): with pytest.raises(ValueError):
func(img, wutils.LOAD, submit=True) func(img, wutils.LOAD, submit=True)
...@@ -766,7 +769,7 @@ def test_fslwrapper(): ...@@ -766,7 +769,7 @@ def test_fslwrapper():
with run.dryrun(): with run.dryrun():
assert func(1, 2)[0] == expected assert func(1, 2)[0] == expected
func(1, 2, cmdonly=True)[0] == list(shlex.split(expected)) assert func(1, 2, cmdonly=True) == list(shlex.split(expected))
_test_script = textwrap.dedent(""" _test_script = textwrap.dedent("""
...@@ -840,3 +843,21 @@ def test_fslwrapper_submit(): ...@@ -840,3 +843,21 @@ def test_fslwrapper_submit():
assert stdout.strip() == 'test_script running: 1 2' assert stdout.strip() == 'test_script running: 1 2'
assert stderr.strip() == experr assert stderr.strip() == experr
@pytest.mark.unixtest
def test_cmdwrapper_fileorthing_cmdonly():
test_func = wutils.fileOrImage('a')(wutils.cmdwrapper(_test_script_func))
newpath = op.pathsep.join(('.', os.environ['PATH']))
with tempdir.tempdir(), \
mock.patch.dict(os.environ, {'PATH' : newpath}):
with open('test_script', 'wt') as f:
f.write(_test_script)
os.chmod('test_script', 0o755)
ran = test_func('1', '2')
cmd = test_func('1', '2', cmdonly=True)
assert ran.stdout[0].strip() == 'test_script running: 1 2'
assert cmd == ['test_script', '1', '2']
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment