Skip to content
Merged
1 change: 1 addition & 0 deletions changelog/14392.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed a bug in :func:`pytest.raises(match=...) <pytest.raises>` "fully escaped" detection, causing the regex diff display to be shown in some instances when the raw string diff display should be shown instead.
7 changes: 4 additions & 3 deletions src/_pytest/raises.py
Original file line number Diff line number Diff line change
Expand Up @@ -345,9 +345,10 @@ def _check_raw_type(
def is_fully_escaped(s: str) -> bool:
# we know we won't compile with re.VERBOSE, so whitespace doesn't need to be escaped
metacharacters = "{}()+.*?^$[]|"
return not any(
c in metacharacters and (i == 0 or s[i - 1] != "\\") for (i, c) in enumerate(s)
)
# Strip all escape sequences (backslash + any char), then check if any
# metacharacter remains unescaped in the resulting string.
stripped = re.sub(r"\\.", "", s)
return not any(c in metacharacters for c in stripped)


def unescape(s: str) -> str:
Expand Down
16 changes: 16 additions & 0 deletions testing/python/raises.py
Original file line number Diff line number Diff line change
Expand Up @@ -444,3 +444,19 @@ def test_pipe_is_treated_as_regex_metacharacter(self) -> None:
assert not is_fully_escaped("foo|bar")
assert is_fully_escaped(r"foo\|bar")
assert unescape(r"foo\|bar") == "foo|bar"

def test_consecutive_backslashes_in_escape_check(self) -> None:
"""Consecutive backslashes escape each other, leaving the metachar unescaped."""
from _pytest.raises import is_fully_escaped

# r"\." -> one backslash escapes the dot -> fully escaped
assert is_fully_escaped(r"\.")
# r"\\." -> two backslashes: the first escapes the second, dot is unescaped
assert not is_fully_escaped(r"\\.")
# r"\\\." -> three backslashes: pair escapes pair, last escapes dot -> fully escaped
assert is_fully_escaped(r"\\\.")
# Same idea with pipe metachar
# "\\\\|" is the string \\| (2 backslashes + pipe): even count, pipe is unescaped
assert not is_fully_escaped("\\\\|")
# r"\\\\|" is the string \\\\| (4 backslashes + pipe): even count, pipe is unescaped
assert not is_fully_escaped(r"\\\\|")