Skip to content

Commit 6abf57e

Browse files
committed
docstring and changelog improvements
1 parent 0f1a5f9 commit 6abf57e

File tree

2 files changed

+80
-28
lines changed

2 files changed

+80
-28
lines changed

changelog/13192.feature.2.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
The context-manager form of :func:`pytest.raises` now has a ``check`` parameter that takes a callable that should return `True` when passed the raised exception if it should be accepted.
1+
You can now pass :func:`with pytest.raises(check=fn): <pytest.raises>`, where ``fn`` is a function which takes a raised exception and returns a boolean. The test fails if no exception was raised (as usual with :func:pytest.raises), passes if an exception is raised and fn returns True (as well as ``match`` and the type matching, if specified), and raises an exception if fn returns False (which also fails the test).

src/_pytest/raises.py

+79-27
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ def raises(
113113
exception types are expected. Note that subclasses of the passed exceptions
114114
will also match.
115115
116+
This is not a required parameter, you may opt to only use ``match`` and/or
117+
``check`` for verifying the raised exception.
118+
116119
:kwparam str | re.Pattern[str] | None match:
117120
If specified, a string containing a regular expression,
118121
or a regular expression object, that is tested against the string
@@ -127,6 +130,13 @@ def raises(
127130
When using ``pytest.raises`` as a function, you can use:
128131
``pytest.raises(Exc, func, match="passed on").match("my pattern")``.)
129132
133+
:kwparam Callable[[BaseException], bool] check
134+
If specified, a callable that will be called with the exception as a parameter
135+
after checking the type and the match regex if specified.
136+
If it returns ``True`` it will be considered a match, if not it will
137+
be considered a failed match.
138+
139+
130140
Use ``pytest.raises`` as a context manager, which will capture the exception of the given
131141
type, or any of its subclasses::
132142
@@ -526,13 +536,27 @@ class RaisesExc(AbstractRaises[BaseExcT_co_default]):
526536
"""
527537
.. versionadded:: 8.4
528538
529-
Helper class to be used together with RaisesGroup when you want to specify requirements on sub-exceptions.
539+
540+
This is the class constructed when calling :func:`pytest.raises`, but may be used
541+
directly as a helper class with :class:`RaisesGroup` when you want to specify
542+
requirements on sub-exceptions.
530543
531544
You don't need this if you only want to specify the type, since :class:`RaisesGroup`
532545
accepts ``type[BaseException]``.
533546
534-
The type is checked with :func:`isinstance`, and does not need to be an exact match.
535-
If that is wanted you can use the ``check`` parameter.
547+
:param type[BaseException] | tuple[type[BaseException]] | None expected_exception:
548+
The expected type, or one of several possible types.
549+
May be ``None`` in order to only make use of ``match`` and/or ``check``
550+
551+
The type is checked with :func:`isinstance`, and does not need to be an exact match.
552+
If that is wanted you can use the ``check`` parameter.
553+
:kwparam str | Pattern[str] match
554+
A regex to match.
555+
:kwparam Callable[[BaseException], bool] check
556+
If specified, a callable that will be called with the exception as a parameter
557+
after checking the type and the match regex if specified.
558+
If it returns ``True`` it will be considered a match, if not it will
559+
be considered a failed match.
536560
537561
:meth:`RaisesExc.matches` can also be used standalone to check individual exceptions.
538562
@@ -695,49 +719,77 @@ class RaisesGroup(AbstractRaises[BaseExceptionGroup[BaseExcT_co]]):
695719
:meth:`ExceptionInfo.group_contains` also tries to handle exception groups,
696720
but it is very bad at checking that you *didn't* get unexpected exceptions.
697721
698-
699722
The catching behaviour differs from :ref:`except* <except_star>`, being much
700723
stricter about the structure by default.
701724
By using ``allow_unwrapped=True`` and ``flatten_subgroups=True`` you can match
702725
:ref:`except* <except_star>` fully when expecting a single exception.
703726
704-
#. All specified exceptions must be present, *and no others*.
705-
706-
* If you expect a variable number of exceptions you need to use
707-
:func:`pytest.raises(ExceptionGroup) <pytest.raises>` and manually check
708-
the contained exceptions. Consider making use of :meth:`RaisesExc.matches`.
727+
:param args:
728+
Any number of exception types, :class:`RaisesGroup` or :class:`RaisesExc`
729+
to specify the exceptions contained in this exception.
730+
All specified exceptions must be present in the raised group, *and no others*.
709731
710-
#. It will only catch exceptions wrapped in an exceptiongroup by default.
732+
If you expect a variable number of exceptions you need to use
733+
:func:`pytest.raises(ExceptionGroup) <pytest.raises>` and manually check
734+
the contained exceptions. Consider making use of :meth:`RaisesExc.matches`.
711735
712-
* With ``allow_unwrapped=True`` you can specify a single expected exception (or :class:`RaisesExc`) and it will
713-
match the exception even if it is not inside an :exc:`ExceptionGroup`.
714-
If you expect one of several different exception types you need to use a :class:`RaisesExc` object.
715-
716-
#. By default it cares about the full structure with nested :exc:`ExceptionGroup`'s. You can specify nested
717-
:exc:`ExceptionGroup`'s by passing :class:`RaisesGroup` objects as expected exceptions.
736+
It does not care about the order of the exceptions, so
737+
``RaisesGroup(ValueError, TypeError)``
738+
is equivalent to
739+
``RaisesGroup(TypeError, ValueError)``.
740+
:kwparam str | re.Pattern[str] | None match:
741+
If specified, a string containing a regular expression,
742+
or a regular expression object, that is tested against the string
743+
representation of the exception group and its :pep:`678` `__notes__`
744+
using :func:`re.search`.
718745
719-
* With ``flatten_subgroups=True`` it will "flatten" the raised :exc:`ExceptionGroup`,
720-
extracting all exceptions inside any nested :exc:`ExceptionGroup`, before matching.
746+
To match a literal string that may contain :ref:`special characters
747+
<re-syntax>`, the pattern can first be escaped with :func:`re.escape`.
721748
722-
It does not care about the order of the exceptions, so
723-
``RaisesGroup(ValueError, TypeError)``
724-
is equivalent to
725-
``RaisesGroup(TypeError, ValueError)``.
749+
Note that " (5 subgroups)" will be stripped from the ``repr`` before matching.
750+
:kwparam Callable[[E], bool] check:
751+
If specified, a callable that will be called with the group as a parameter
752+
after successfully matching the expected exceptions. If it returns ``True``
753+
it will be considered a match, if not it will be considered a failed match.
754+
:kwparam bool allow_unwrapped:
755+
If expecting a single exception or :class:`RaisesExc` it will match even
756+
if the exception is not inside an exceptiongroup.
757+
758+
Using this together with ``match``, ``check`` or expecting multiple exceptions
759+
will raise an error.
760+
:kwparam bool flatten_subgroups:
761+
"flatten" any groups inside the raised exception group, extracting all exceptions
762+
inside any nested groups, before matching. Without this it expects you to
763+
fully specify the nesting structure by passing :class:`RaisesGroup` as expected
764+
parameter.
726765
727766
Examples::
728767
729768
with RaisesGroup(ValueError):
730769
raise ExceptionGroup("", (ValueError(),))
770+
# match
731771
with RaisesGroup(
732-
ValueError, ValueError, RaisesExc(TypeError, match="expected int")
772+
ValueError,
773+
ValueError,
774+
RaisesExc(TypeError, match="^expected int$"),
775+
match="^my group$",
733776
):
734-
...
777+
raise ExceptionGroup(
778+
"my group",
779+
[
780+
ValueError(),
781+
TypeError("expected int"),
782+
ValueError(),
783+
],
784+
)
785+
# check
735786
with RaisesGroup(
736787
KeyboardInterrupt,
737-
match="hello",
738-
check=lambda x: type(x) is BaseExceptionGroup,
788+
match="^hello$",
789+
check=lambda x: isinstance(x.__cause__, ValueError),
739790
):
740-
...
791+
raise BaseExceptionGroup("hello", [KeyboardInterrupt()]) from ValueError
792+
# nested groups
741793
with RaisesGroup(RaisesGroup(ValueError)):
742794
raise ExceptionGroup("", (ExceptionGroup("", (ValueError(),)),))
743795

0 commit comments

Comments
 (0)