Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not swallow outcomes.Exit in assertrepr_compare #4978

Merged
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
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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is moved out of the function only, which I'd like to have done anyway - but allows for easy monkeypatching.



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)