diff --git a/changelog/4978.bugfix.rst b/changelog/4978.bugfix.rst new file mode 100644 index 000000000..259daa8da --- /dev/null +++ b/changelog/4978.bugfix.rst @@ -0,0 +1 @@ +``outcomes.Exit`` is not swallowed in ``assertrepr_compare`` anymore. diff --git a/src/_pytest/assertion/util.py b/src/_pytest/assertion/util.py index c7a46cba0..ab01c314c 100644 --- a/src/_pytest/assertion/util.py +++ b/src/_pytest/assertion/util.py @@ -9,6 +9,7 @@ import six import _pytest._code from ..compat import Sequence +from _pytest import outcomes from _pytest._io.saferepr import saferepr # The _reprcompare attribute on the util module is used by the new assertion @@ -102,6 +103,38 @@ except NameError: basestring = str +def issequence(x): + return isinstance(x, Sequence) and not isinstance(x, basestring) + + +def istext(x): + return isinstance(x, basestring) + + +def isdict(x): + return isinstance(x, dict) + + +def isset(x): + return isinstance(x, (set, frozenset)) + + +def isdatacls(obj): + return getattr(obj, "__dataclass_fields__", None) is not None + + +def isattrs(obj): + return getattr(obj, "__attrs_attrs__", None) is not None + + +def isiterable(obj): + try: + iter(obj) + return not istext(obj) + except TypeError: + return False + + def assertrepr_compare(config, op, left, right): """Return specialised explanations for some operators/operands""" width = 80 - 15 - len(op) - 2 # 15 chars indentation, 1 space around op @@ -110,31 +143,6 @@ def assertrepr_compare(config, op, left, right): summary = u"%s %s %s" % (ecu(left_repr), op, ecu(right_repr)) - def issequence(x): - return isinstance(x, Sequence) and not isinstance(x, basestring) - - def istext(x): - return isinstance(x, basestring) - - def isdict(x): - return isinstance(x, dict) - - def isset(x): - return isinstance(x, (set, frozenset)) - - def isdatacls(obj): - return getattr(obj, "__dataclass_fields__", None) is not None - - def isattrs(obj): - return getattr(obj, "__attrs_attrs__", None) is not None - - def isiterable(obj): - try: - iter(obj) - return not istext(obj) - except TypeError: - return False - verbose = config.getoption("verbose") explanation = None try: @@ -162,6 +170,8 @@ def assertrepr_compare(config, op, left, right): elif op == "not in": if istext(left) and istext(right): explanation = _notin_text(left, right, verbose) + except outcomes.Exit: + raise except Exception: explanation = [ u"(pytest_assertion plugin: representation of details failed. " diff --git a/testing/test_assertion.py b/testing/test_assertion.py index e4fe56c6f..330b711af 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -11,6 +11,7 @@ import six import _pytest.assertion as plugin import pytest +from _pytest import outcomes from _pytest.assertion import truncate from _pytest.assertion import util @@ -1305,3 +1306,13 @@ def test_issue_1944(testdir): "AttributeError: 'Module' object has no attribute '_obj'" not in result.stdout.str() ) + + +def test_exit_from_assertrepr_compare(monkeypatch): + def raise_exit(obj): + outcomes.exit("Quitting debugger") + + monkeypatch.setattr(util, "istext", raise_exit) + + with pytest.raises(outcomes.Exit, match="Quitting debugger"): + callequal(1, 1)