Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions reframe/utility/sanity.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -182,6 +183,36 @@ 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() <python: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
)

If ``file`` is None, :func:`print` will print its arguments to the
standard output. Unlike the builtin :func:`print() <python: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:
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
Expand Down
41 changes: 41 additions & 0 deletions unittests/test_sanity_functions.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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)
Expand Down