From 0edfe80a15f62c8e0d7f3ed8aa431236a6bb1cba Mon Sep 17 00:00:00 2001 From: dariomesic Date: Tue, 28 Oct 2025 13:44:35 +0100 Subject: [PATCH 1/7] Clarify regex match error in pytest.raises --- src/_pytest/raises.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/_pytest/raises.py b/src/_pytest/raises.py index 2a53a5a148a..57ba5b7a5fa 100644 --- a/src/_pytest/raises.py +++ b/src/_pytest/raises.py @@ -523,8 +523,8 @@ def _check_match(self, e: BaseException) -> bool: # when they're similar it's not always obvious which is which self._fail_reason = ( f"Regex pattern did not match{maybe_specify_type}.\n" - f" Regex: {_match_pattern(self.match)!r}\n" - f" Input: {stringified_exception!r}" + f" Expected regex: {_match_pattern(self.match)!r}\n" + f" Actual message: {stringified_exception!r}" ) if _match_pattern(self.match) == stringified_exception: self._fail_reason += "\n Did you mean to `re.escape()` the regex?" From a20ff0f5ea6a504d77f4e6b21cbedc6b02016ca6 Mon Sep 17 00:00:00 2001 From: dariomesic <93346078+dariomesic@users.noreply.github.com> Date: Tue, 28 Oct 2025 13:55:45 +0100 Subject: [PATCH 2/7] Update src/_pytest/raises.py Co-authored-by: Pierre Sassoulas --- src/_pytest/raises.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_pytest/raises.py b/src/_pytest/raises.py index 57ba5b7a5fa..c1966a56144 100644 --- a/src/_pytest/raises.py +++ b/src/_pytest/raises.py @@ -524,7 +524,7 @@ def _check_match(self, e: BaseException) -> bool: self._fail_reason = ( f"Regex pattern did not match{maybe_specify_type}.\n" f" Expected regex: {_match_pattern(self.match)!r}\n" - f" Actual message: {stringified_exception!r}" + f" Actual exception message: {stringified_exception!r}" ) if _match_pattern(self.match) == stringified_exception: self._fail_reason += "\n Did you mean to `re.escape()` the regex?" From 75406e8ee7a3f36395a0c48c456c101695f11fd9 Mon Sep 17 00:00:00 2001 From: dariomesic <93346078+dariomesic@users.noreply.github.com> Date: Tue, 28 Oct 2025 14:01:26 +0100 Subject: [PATCH 3/7] Removed comment from maintainers Comment saying """ # I don't love "Regex"+"Input" vs something like "expected regex"+"exception message" # when they're similar it's not always obvious which is which" """ is now removed for clarity --- src/_pytest/raises.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/_pytest/raises.py b/src/_pytest/raises.py index c1966a56144..2fd19ea6bbf 100644 --- a/src/_pytest/raises.py +++ b/src/_pytest/raises.py @@ -518,9 +518,7 @@ def _check_match(self, e: BaseException) -> bool: diff = _diff_text(self.rawmatch, stringified_exception, dummy_highlighter) self._fail_reason = ("\n" if diff[0][0] == "-" else "") + "\n".join(diff) return False - - # I don't love "Regex"+"Input" vs something like "expected regex"+"exception message" - # when they're similar it's not always obvious which is which + self._fail_reason = ( f"Regex pattern did not match{maybe_specify_type}.\n" f" Expected regex: {_match_pattern(self.match)!r}\n" From 5b7f4a70ab2480e1c5c787c2deea577d681d5ed7 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 28 Oct 2025 13:02:07 +0000 Subject: [PATCH 4/7] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/_pytest/raises.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_pytest/raises.py b/src/_pytest/raises.py index 2fd19ea6bbf..922e3fc1895 100644 --- a/src/_pytest/raises.py +++ b/src/_pytest/raises.py @@ -518,7 +518,7 @@ def _check_match(self, e: BaseException) -> bool: diff = _diff_text(self.rawmatch, stringified_exception, dummy_highlighter) self._fail_reason = ("\n" if diff[0][0] == "-" else "") + "\n".join(diff) return False - + self._fail_reason = ( f"Regex pattern did not match{maybe_specify_type}.\n" f" Expected regex: {_match_pattern(self.match)!r}\n" From 0326426060caf6e35800c45aed18bc0f32b7089f Mon Sep 17 00:00:00 2001 From: dariomesic <93346078+dariomesic@users.noreply.github.com> Date: Wed, 29 Oct 2025 13:13:45 +0100 Subject: [PATCH 5/7] Update src/_pytest/raises.py Co-authored-by: Ran Benita --- src/_pytest/raises.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_pytest/raises.py b/src/_pytest/raises.py index 922e3fc1895..ad56a8c14d2 100644 --- a/src/_pytest/raises.py +++ b/src/_pytest/raises.py @@ -522,7 +522,7 @@ def _check_match(self, e: BaseException) -> bool: self._fail_reason = ( f"Regex pattern did not match{maybe_specify_type}.\n" f" Expected regex: {_match_pattern(self.match)!r}\n" - f" Actual exception message: {stringified_exception!r}" + f" Actual message: {stringified_exception!r}" ) if _match_pattern(self.match) == stringified_exception: self._fail_reason += "\n Did you mean to `re.escape()` the regex?" From 582107bfd0bddf789582e94b9f3b703a242b1539 Mon Sep 17 00:00:00 2001 From: dariomesic Date: Wed, 29 Oct 2025 14:45:50 +0100 Subject: [PATCH 6/7] FIX: Update raises.py and tests for new error message --- testing/python/raises.py | 10 ++-- testing/python/raises_group.py | 86 +++++++++++++++++----------------- 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/testing/python/raises.py b/testing/python/raises.py index 9e3fe304528..411a45cfa92 100644 --- a/testing/python/raises.py +++ b/testing/python/raises.py @@ -253,8 +253,8 @@ def test_raises_match(self) -> None: msg = "with base 16" expr = ( "Regex pattern did not match.\n" - f" Regex: {msg!r}\n" - " Input: \"invalid literal for int() with base 10: 'asdf'\"" + f" Expected regex: {msg!r}\n" + f" Actual message: \"invalid literal for int() with base 10: 'asdf'\"" ) with pytest.raises(AssertionError, match="^" + re.escape(expr) + "$"): with pytest.raises(ValueError, match=msg): @@ -289,7 +289,7 @@ def test_match_failure_string_quoting(self): with pytest.raises(AssertionError, match="'foo"): raise AssertionError("'bar") (msg,) = excinfo.value.args - assert msg == '''Regex pattern did not match.\n Regex: "'foo"\n Input: "'bar"''' + assert msg == '''Regex pattern did not match.\n Expected regex: "'foo"\n Actual message: "'bar"''' def test_match_failure_exact_string_message(self): message = "Oh here is a message with (42) numbers in parameters" @@ -299,8 +299,8 @@ def test_match_failure_exact_string_message(self): (msg,) = excinfo.value.args assert msg == ( "Regex pattern did not match.\n" - " Regex: 'Oh here is a message with (42) numbers in parameters'\n" - " Input: 'Oh here is a message with (42) numbers in parameters'\n" + " Expected regex: 'Oh here is a message with (42) numbers in parameters'\n" + " Actual message: 'Oh here is a message with (42) numbers in parameters'\n" " Did you mean to `re.escape()` the regex?" ) diff --git a/testing/python/raises_group.py b/testing/python/raises_group.py index e911ba52cbc..b164a3c2e2b 100644 --- a/testing/python/raises_group.py +++ b/testing/python/raises_group.py @@ -382,8 +382,8 @@ def test_match() -> None: with ( fails_raises_group( "Regex pattern did not match the `ExceptionGroup()`.\n" - " Regex: 'foo'\n" - " Input: 'bar'" + " Expected regex: 'foo'\n" + " Actual message: 'bar'" ), RaisesGroup(ValueError, match="foo"), ): @@ -396,8 +396,8 @@ def test_match() -> None: with ( fails_raises_group( "Regex pattern did not match the `ExceptionGroup()`.\n" - " Regex: 'foo'\n" - " Input: 'bar'\n" + " Expected regex: 'foo'\n" + " Actual message: 'bar'\n" " but matched the expected `ValueError`.\n" " You might want `RaisesGroup(RaisesExc(ValueError, match='foo'))`" ), @@ -570,8 +570,8 @@ def test_assert_message() -> None: " ExceptionGroup('', [RuntimeError()]):\n" " RaisesGroup(ValueError): `RuntimeError()` is not an instance of `ValueError`\n" " RaisesGroup(ValueError, match='a'): Regex pattern did not match the `ExceptionGroup()`.\n" - " Regex: 'a'\n" - " Input: ''\n" + " Expected regex: 'a'\n" + " Actual message: ''\n" " RuntimeError():\n" " RaisesGroup(ValueError): `RuntimeError()` is not an exception group\n" " RaisesGroup(ValueError, match='a'): `RuntimeError()` is not an exception group", @@ -634,8 +634,8 @@ def test_assert_message() -> None: fails_raises_group( # TODO: did not match Exceptiongroup('h(ell)o', ...) ? "Raised exception group did not match: Regex pattern did not match the `ExceptionGroup()`.\n" - " Regex: 'h(ell)o'\n" - " Input: 'h(ell)o'\n" + " Expected regex: 'h(ell)o'\n" + " Actual message: 'h(ell)o'\n" " Did you mean to `re.escape()` the regex?", add_prefix=False, # to see the full structure ), @@ -645,8 +645,8 @@ def test_assert_message() -> None: with ( fails_raises_group( "RaisesExc(match='h(ell)o'): Regex pattern did not match.\n" - " Regex: 'h(ell)o'\n" - " Input: 'h(ell)o'\n" + " Expected regex: 'h(ell)o'\n" + " Actual message: 'h(ell)o'\n" " Did you mean to `re.escape()` the regex?", ), RaisesGroup(RaisesExc(match="h(ell)o")), @@ -799,8 +799,8 @@ def test_suggestion_on_nested_and_brief_error() -> None: "The following raised exceptions did not find a match\n" " ExceptionGroup('^hello', [Exception()]):\n" " RaisesGroup(Exception, match='^hello'): Regex pattern did not match the `ExceptionGroup()`.\n" - " Regex: '^hello'\n" - " Input: '^hello'\n" + " Expected regex: '^hello'\n" + " Actual message: '^hello'\n" " Did you mean to `re.escape()` the regex?\n" " Unexpected nested `ExceptionGroup()`, expected `ValueError`" ), @@ -830,8 +830,8 @@ def test_assert_message_nested() -> None: " RaisesGroup(ValueError): `TypeError()` is not an instance of `ValueError`\n" " RaisesGroup(RaisesGroup(ValueError)): RaisesGroup(ValueError): `TypeError()` is not an exception group\n" " RaisesGroup(RaisesExc(TypeError, match='foo')): RaisesExc(TypeError, match='foo'): Regex pattern did not match.\n" - " Regex: 'foo'\n" - " Input: 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'\n" + " Expected regex: 'foo'\n" + " Actual message: 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'\n" " RaisesGroup(TypeError, ValueError): 1 matched exception. Too few exceptions raised, found no match for: [ValueError]\n" " ExceptionGroup('Exceptions from Trio nursery', [TypeError('cccccccccccccccccccccccccccccc'), TypeError('dddddddddddddddddddddddddddddd')]):\n" " RaisesGroup(ValueError): \n" @@ -856,12 +856,12 @@ def test_assert_message_nested() -> None: " The following raised exceptions did not find a match\n" " TypeError('cccccccccccccccccccccccccccccc'):\n" " RaisesExc(TypeError, match='foo'): Regex pattern did not match.\n" - " Regex: 'foo'\n" - " Input: 'cccccccccccccccccccccccccccccc'\n" + " Expected regex: 'foo'\n" + " Actual message: 'cccccccccccccccccccccccccccccc'\n" " TypeError('dddddddddddddddddddddddddddddd'):\n" " RaisesExc(TypeError, match='foo'): Regex pattern did not match.\n" - " Regex: 'foo'\n" - " Input: 'dddddddddddddddddddddddddddddd'\n" + " Expected regex: 'foo'\n" + " Actual message: 'dddddddddddddddddddddddddddddd'\n" " RaisesGroup(TypeError, ValueError): \n" " 1 matched exception. \n" " The following expected exceptions did not find a match:\n" @@ -945,8 +945,8 @@ def test_misordering_example() -> None: " It matches `ValueError` which was paired with `ValueError('foo')`\n" " It matches `ValueError` which was paired with `ValueError('foo')`\n" " RaisesExc(ValueError, match='foo'): Regex pattern did not match.\n" - " Regex: 'foo'\n" - " Input: 'bar'\n" + " Expected regex: 'foo'\n" + " Actual message: 'bar'\n" "There exist a possible match when attempting an exhaustive check, but RaisesGroup uses a greedy algorithm. Please make your expected exceptions more stringent with `RaisesExc` etc so the greedy algorithm can function." ), RaisesGroup( @@ -1036,34 +1036,34 @@ def test_identity_oopsies() -> None: "The following raised exceptions did not find a match\n" " ValueError('foo'):\n" " RaisesExc(match='bar'): Regex pattern did not match.\n" - " Regex: 'bar'\n" - " Input: 'foo'\n" + " Expected regex: 'bar'\n" + " Actual message: 'foo'\n" " RaisesExc(match='bar'): Regex pattern did not match.\n" - " Regex: 'bar'\n" - " Input: 'foo'\n" + " Expected regex: 'bar'\n" + " Actual message: 'foo'\n" " RaisesExc(match='bar'): Regex pattern did not match.\n" - " Regex: 'bar'\n" - " Input: 'foo'\n" + " Expected regex: 'bar'\n" + " Actual message: 'foo'\n" " ValueError('foo'):\n" " RaisesExc(match='bar'): Regex pattern did not match.\n" - " Regex: 'bar'\n" - " Input: 'foo'\n" + " Expected regex: 'bar'\n" + " Actual message: 'foo'\n" " RaisesExc(match='bar'): Regex pattern did not match.\n" - " Regex: 'bar'\n" - " Input: 'foo'\n" + " Expected regex: 'bar'\n" + " Actual message: 'foo'\n" " RaisesExc(match='bar'): Regex pattern did not match.\n" - " Regex: 'bar'\n" - " Input: 'foo'\n" + " Expected regex: 'bar'\n" + " Actual message: 'foo'\n" " ValueError('foo'):\n" " RaisesExc(match='bar'): Regex pattern did not match.\n" - " Regex: 'bar'\n" - " Input: 'foo'\n" + " Expected regex: 'bar'\n" + " Actual message: 'foo'\n" " RaisesExc(match='bar'): Regex pattern did not match.\n" - " Regex: 'bar'\n" - " Input: 'foo'\n" + " Expected regex: 'bar'\n" + " Actual message: 'foo'\n" " RaisesExc(match='bar'): Regex pattern did not match.\n" - " Regex: 'bar'\n" - " Input: 'foo'" + " Expected regex: 'bar'\n" + " Actual message: 'foo'" ), RaisesGroup(m, m, m), ): @@ -1120,7 +1120,7 @@ def test_raisesexc() -> None: # currently RaisesGroup says "Raised exception did not match" but RaisesExc doesn't... with pytest.raises( AssertionError, - match=wrap_escape("Regex pattern did not match.\n Regex: 'foo'\n Input: 'bar'"), + match=wrap_escape("Regex pattern did not match.\n Expected regex: 'foo'\n Actual message: 'bar'"), ): with RaisesExc(TypeError, match="foo"): raise TypeError("bar") @@ -1132,8 +1132,8 @@ def test_raisesexc_match() -> None: with ( fails_raises_group( "RaisesExc(ValueError, match='foo'): Regex pattern did not match.\n" - " Regex: 'foo'\n" - " Input: 'bar'" + " Expected regex: 'foo'\n" + " Actual message: 'bar'" ), RaisesGroup(RaisesExc(ValueError, match="foo")), ): @@ -1145,8 +1145,8 @@ def test_raisesexc_match() -> None: with ( fails_raises_group( "RaisesExc(match='foo'): Regex pattern did not match.\n" - " Regex: 'foo'\n" - " Input: 'bar'" + " Expected regex: 'foo'\n" + " Actual message: 'bar'" ), RaisesGroup(RaisesExc(match="foo")), ): From 99f36dfc926219bba1d625c923503185f755534b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 29 Oct 2025 13:46:17 +0000 Subject: [PATCH 7/7] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- testing/python/raises.py | 5 ++++- testing/python/raises_group.py | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/testing/python/raises.py b/testing/python/raises.py index 411a45cfa92..0bf02a8063f 100644 --- a/testing/python/raises.py +++ b/testing/python/raises.py @@ -289,7 +289,10 @@ def test_match_failure_string_quoting(self): with pytest.raises(AssertionError, match="'foo"): raise AssertionError("'bar") (msg,) = excinfo.value.args - assert msg == '''Regex pattern did not match.\n Expected regex: "'foo"\n Actual message: "'bar"''' + assert ( + msg + == '''Regex pattern did not match.\n Expected regex: "'foo"\n Actual message: "'bar"''' + ) def test_match_failure_exact_string_message(self): message = "Oh here is a message with (42) numbers in parameters" diff --git a/testing/python/raises_group.py b/testing/python/raises_group.py index b164a3c2e2b..0247b118c58 100644 --- a/testing/python/raises_group.py +++ b/testing/python/raises_group.py @@ -1120,7 +1120,9 @@ def test_raisesexc() -> None: # currently RaisesGroup says "Raised exception did not match" but RaisesExc doesn't... with pytest.raises( AssertionError, - match=wrap_escape("Regex pattern did not match.\n Expected regex: 'foo'\n Actual message: 'bar'"), + match=wrap_escape( + "Regex pattern did not match.\n Expected regex: 'foo'\n Actual message: 'bar'" + ), ): with RaisesExc(TypeError, match="foo"): raise TypeError("bar")