From 9cbc7813b74a46cd4c943b7468d652e6c434555d Mon Sep 17 00:00:00 2001 From: Vasileios Karakasis Date: Sun, 17 Nov 2019 06:51:48 -0700 Subject: [PATCH 1/2] Add a print() sanity function --- reframe/utility/sanity.py | 32 +++++++++++++++++++++++ unittests/test_sanity_functions.py | 41 ++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/reframe/utility/sanity.py b/reframe/utility/sanity.py index cfce935026..a29341e1a2 100644 --- a/reframe/utility/sanity.py +++ b/reframe/utility/sanity.py @@ -62,6 +62,7 @@ import glob as pyglob import itertools import re +import sys import reframe.utility as util from reframe.core.deferrable import deferrable, _DeferredExpression @@ -182,6 +183,37 @@ def min(*args): return builtins.min(*args) +@deferrable +def print(*objects, sep=' ', end='\n', file=None, flush=False): + '''Replacement for the built-in :func:`print() ` function. + + The only difference is that this function returns the ``objects``, so that + you can use it transparently inside a complex sanity expression. For + example, you could write the following to print the matches returned from + the :func:`extractall()` function: + + .. code:: python + + self.sanity_patterns = sn.assert_eq( + sn.count(sn.print(sn.extract_all(...))), 10 + ) + + .. note:: + + The only difference to the standard builtin :func:`print() + ` function is that the default value here is ``None``. + This is only to ensure that the ``file`` argument is not bound always + to the ``sys.stdout`` when this function is deferred. If ``file`` is + ``None``, then it is set internally to ``sys.stdout``. + ''' + + if file is None: + file = sys.stdout + + builtins.print(*objects, sep=sep, end=end, file=file, flush=flush) + return objects + + @deferrable def reversed(seq): '''Replacement for the built-in diff --git a/unittests/test_sanity_functions.py b/unittests/test_sanity_functions.py index ee3f2557e8..acb02ac31d 100644 --- a/unittests/test_sanity_functions.py +++ b/unittests/test_sanity_functions.py @@ -1,8 +1,13 @@ +import contextlib +import io import itertools import os +import sys import unittest + from tempfile import NamedTemporaryFile + import reframe.utility.sanity as sn from reframe.core.exceptions import SanityError from unittests.fixtures import TEST_RESOURCES_CHECKS @@ -124,6 +129,42 @@ def test_min(self): l.append(0) self.assertEqual(0, sn.min(dl)) + def test_print_stdout(self): + stdout = io.StringIO() + with contextlib.redirect_stdout(stdout): + x, y = sn.evaluate(sn.print(1, sn.defer(2))) + + assert stdout.getvalue() == '1 2\n' + assert x == 1 + assert y == 2 + + def test_print_stderr(self): + stderr = io.StringIO() + with contextlib.redirect_stderr(stderr): + x, y = sn.evaluate(sn.print(1, sn.defer(2), file=sys.stderr)) + + assert stderr.getvalue() == '1 2\n' + assert x == 1 + assert y == 2 + + def test_print_separator(self): + stdout = io.StringIO() + with contextlib.redirect_stdout(stdout): + x, y = sn.evaluate(sn.print(1, sn.defer(2), sep='|')) + + assert stdout.getvalue() == '1|2\n' + assert x == 1 + assert y == 2 + + def test_print_end(self): + stdout = io.StringIO() + with contextlib.redirect_stdout(stdout): + x, y = sn.evaluate(sn.print(1, sn.defer(2), end='')) + + assert stdout.getvalue() == '1 2' + assert x == 1 + assert y == 2 + def test_reversed(self): l = [1, 2, 3] dr = sn.reversed(l) From d2ffb49ca2ffc6eba9d1b7cf30140119d53f487b Mon Sep 17 00:00:00 2001 From: Vasileios Karakasis Date: Fri, 22 Nov 2019 14:53:21 -0700 Subject: [PATCH 2/2] Fix documentation --- reframe/utility/sanity.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/reframe/utility/sanity.py b/reframe/utility/sanity.py index a29341e1a2..0584a72e41 100644 --- a/reframe/utility/sanity.py +++ b/reframe/utility/sanity.py @@ -198,13 +198,12 @@ def print(*objects, sep=' ', end='\n', file=None, flush=False): sn.count(sn.print(sn.extract_all(...))), 10 ) - .. note:: - - The only difference to the standard builtin :func:`print() - ` function is that the default value here is ``None``. - This is only to ensure that the ``file`` argument is not bound always - to the ``sys.stdout`` when this function is deferred. If ``file`` is - ``None``, then it is set internally to ``sys.stdout``. + If ``file`` is None, :func:`print` will print its arguments to the + standard output. Unlike the builtin :func:`print() ` + function, we don't bind the ``file`` argument to :attr:`sys.stdout` by + default. This would capture :attr:`sys.stdout` at the time this function + is defined and would prevent it from seeing changes to :attr:`sys.stdout`, + such as redirects, in the future. ''' if file is None: