Skip to content

Commit

Permalink
Do not swallow outcomes.Exit in assertrepr_compare
Browse files Browse the repository at this point in the history
  • Loading branch information
blueyed committed Mar 26, 2019
1 parent 15d6088 commit 0d00be4
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 24 deletions.
1 change: 1 addition & 0 deletions changelog/4978.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
``outcomes.Exit`` is not swallowed in ``assertrepr_compare`` anymore.
58 changes: 34 additions & 24 deletions src/_pytest/assertion/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

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
Expand Down Expand Up @@ -102,38 +103,45 @@ def _format_lines(lines):
basestring = str


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
left_repr = saferepr(left, maxsize=int(width // 2))
right_repr = saferepr(right, maxsize=width - len(left_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)

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 isset(x):
return isinstance(x, (set, frozenset))

def istext(x):
return isinstance(x, basestring)

def isdict(x):
return isinstance(x, dict)
def isdatacls(obj):
return getattr(obj, "__dataclass_fields__", None) is not None

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 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 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
left_repr = saferepr(left, maxsize=int(width // 2))
right_repr = saferepr(right, maxsize=width - len(left_repr))

summary = u"%s %s %s" % (ecu(left_repr), op, ecu(right_repr))

verbose = config.getoption("verbose")
explanation = None
Expand Down Expand Up @@ -162,6 +170,8 @@ def isiterable(obj):
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. "
Expand Down
11 changes: 11 additions & 0 deletions testing/test_assertion.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import _pytest.assertion as plugin
import pytest
from _pytest import outcomes
from _pytest.assertion import truncate
from _pytest.assertion import util

Expand Down Expand Up @@ -1305,3 +1306,13 @@ def f():
"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)

0 comments on commit 0d00be4

Please sign in to comment.