diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 646f21034f2c724db9eb8514efdac5526585a9cd..cee34ab87f8abc68ffb1d6bb02690b5e5ddab02d 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,17 @@ This document contains the ``fslpy`` release history in reverse chronological order. +3.16.0 (Under development) +-------------------------- + + +Added +^^^^^ + +* New `silent` option to the :func:`.run` function = passing ``silent=True`` is + equivalent to passing ``log={'tee':False}`` (!428). + + 3.15.3 (Thursday 16th November 2023) ------------------------------------ diff --git a/fsl/tests/test_run.py b/fsl/tests/test_run.py index ca7f69108103693da7bc0ae5f6affb196fd2794a..28122faaef6880fede9b2f9319f7dc5136031579 100644 --- a/fsl/tests/test_run.py +++ b/fsl/tests/test_run.py @@ -20,7 +20,6 @@ import pytest import fsl.utils.tempdir as tempdir from fsl.utils.platform import platform as fslplatform import fsl.utils.run as run -import fsl.utils.fslsub as fslsub from . import make_random_image, mockFSLDIR, CaptureStdout, touch @@ -138,6 +137,13 @@ def test_run_tee(): assert stdout == expstdout assert capture.stdout == '' + + # disable forwarding via silent=True + with capture.reset(): + stdout = run.run('./script.sh 1 2 3', silent=True) + assert stdout == expstdout + assert capture.stdout == '' + with capture.reset(): stdout, stderr = run.run('./script.sh 1 2 3', stderr=True, log={'tee' : True}) @@ -289,6 +295,8 @@ def mock_fsl_sub(*cmd, **kwargs): name = op.basename(name) + kwargs.pop('log', None) + jid = '12345' output = run.run(cmd) @@ -323,7 +331,7 @@ def test_run_submit(): jid = run.run('fsltest', submit=True) assert jid == '12345' - stdout, stderr = fslsub.output(jid) + stdout, stderr = run.job_output(jid) assert stdout == 'test_script running\n' assert stderr == '' @@ -331,7 +339,7 @@ def test_run_submit(): kwargs = {'name' : 'abcde', 'ram' : '4GB'} jid = run.run('fsltest', submit=kwargs) assert jid == '12345' - stdout, stderr = fslsub.output(jid) + stdout, stderr = run.job_output(jid) experr = '\n'.join(['{}: {}'.format(k, kwargs[k]) for k in sorted(kwargs.keys())]) + '\n' assert stdout == 'test_script running\n' @@ -341,7 +349,7 @@ def test_run_submit(): kwargs = {'name' : 'abcde', 'ram' : '4GB'} jid = run.run('fsltest', submit=True, **kwargs) assert jid == '12345' - stdout, stderr = fslsub.output(jid) + stdout, stderr = run.job_output(jid) experr = '\n'.join(['{}: {}'.format(k, kwargs[k]) for k in sorted(kwargs.keys())]) + '\n' assert stdout == 'test_script running\n' @@ -482,7 +490,7 @@ def test_func_to_cmd(): for tmp_dir in (None, '.'): for clean in ('never', 'on_success', 'always'): for verbose in (False, True): - cmd = fslsub.func_to_cmd(_good_func, clean=clean, tmp_dir=tmp_dir, verbose=verbose) + cmd = run.func_to_cmd(_good_func, clean=clean, tmp_dir=tmp_dir, verbose=verbose) fn = cmd.split()[-1] assert op.exists(fn) stdout, stderr, exitcode = run.run(cmd, exitcode=True, stdout=True, stderr=True, @@ -497,7 +505,7 @@ def test_func_to_cmd(): else: assert stdout.strip() == 'hello' - cmd = fslsub.func_to_cmd(_bad_func, clean=clean, tmp_dir=tmp_dir) + cmd = run.func_to_cmd(_bad_func, clean=clean, tmp_dir=tmp_dir) fn = cmd.split()[-1] assert op.exists(fn) stdout, stderr, exitcode = run.run(cmd, exitcode=True, stdout=True, stderr=True, diff --git a/fsl/utils/run.py b/fsl/utils/run.py index 99187527d7e19772e7dbeac65173fa3c49ab6c08..04d382ff7f194b72b803ada418be02771931b34d 100644 --- a/fsl/utils/run.py +++ b/fsl/utils/run.py @@ -188,6 +188,9 @@ def run(*args, **kwargs): - cmd: Optional file-like or callable to which the command itself is logged. + :arg silent: Suppress standard output/error. Equivalent to passing + ``log={'tee' : False}``. Ignored if `log` is also passed. + All other keyword arguments are passed through to the ``subprocess.Popen`` object (via :func:`_realrun`), unless ``submit=True``, in which case they are passed through to the :func:`.fsl_sub` function. @@ -204,10 +207,11 @@ def run(*args, **kwargs): submit = kwargs.pop('submit', {}) cmdonly = kwargs.pop('cmdonly', False) logg = kwargs.pop('log', None) + silent = kwargs.pop('silent', False) args = prepareArgs(args) if logg is None: - logg = {} + logg = {'tee' : not silent} tee = logg.get('tee', True) logStdout = logg.get('stdout', None) @@ -237,7 +241,7 @@ def run(*args, **kwargs): # but harmless, as we've popped the "submit" arg above. if submit is not None: from fsl.wrappers import fsl_sub # pylint: disable=import-outside-toplevel # noqa: E501 - return fsl_sub(*args, **submit, **kwargs)[0].strip() + return fsl_sub(*args, log=logg, **submit, **kwargs)[0].strip() # Run directly - delegate to _realrun stdout, stderr, exitcode = _realrun( @@ -633,10 +637,10 @@ def hold(job_ids, hold_filename=None, timeout=10): submit = { 'jobhold' : _flatten_job_ids(job_ids), 'jobtime' : 1, - 'name' : '.hold' + 'name' : '.hold', } - run(f'touch {hold_filename}', submit=submit) + run(f'touch {hold_filename}', submit=submit, silent=True) while not op.exists(hold_filename): time.sleep(timeout) diff --git a/fsl/wrappers/wrapperutils.py b/fsl/wrappers/wrapperutils.py index 3fc318d2c1fca28ab7c2e67009a54bf8ea43cc04..1a931de2a4fef77cd486fa3bff453c8ff7438914 100644 --- a/fsl/wrappers/wrapperutils.py +++ b/fsl/wrappers/wrapperutils.py @@ -193,7 +193,16 @@ def genxwrapper(func, runner, funccmd=False): exitcode = kwargs.pop('exitcode', opts['exitcode']) submit = kwargs.pop('submit', opts['submit']) cmdonly = kwargs.pop('cmdonly', opts['cmdonly']) - logg = kwargs.pop('log', opts['log']) + silent = kwargs.pop('silent', False) + + # If silent=True, we need to explicitly set + # log, as the run function will otherwise + # ignore silent and preferentially use the + # value we pass for log. + if silent: + logg = kwargs.pop('log', {'tee' : False}) + else: + logg = kwargs.pop('log', opts['log']) if funccmd: cmd = run.func_to_cmd(func, args=args, kwargs=kwargs,