|
1 | 1 | # mypy: allow-untyped-defs |
2 | 2 | from __future__ import annotations |
3 | 3 |
|
4 | | -from collections.abc import Callable |
5 | 4 | from collections.abc import Collection |
6 | 5 | from collections.abc import Mapping |
7 | 6 | from collections.abc import Sequence |
|
10 | 9 | import math |
11 | 10 | from numbers import Complex |
12 | 11 | import pprint |
13 | | -import re |
14 | 12 | import sys |
15 | 13 | from typing import Any |
16 | | -from typing import overload |
17 | 14 | from typing import TYPE_CHECKING |
18 | | -from typing import TypeVar |
19 | | - |
20 | | -from _pytest._code import ExceptionInfo |
21 | | -from _pytest.outcomes import fail |
22 | | -from _pytest.raises_group import RaisesExc |
23 | 15 |
|
24 | 16 |
|
25 | 17 | if TYPE_CHECKING: |
26 | 18 | from numpy import ndarray |
27 | 19 |
|
28 | | - E = TypeVar("E", bound=BaseException, default=BaseException) |
29 | | - |
30 | 20 |
|
31 | 21 | def _compare_approx( |
32 | 22 | full_object: object, |
@@ -778,230 +768,3 @@ def _as_numpy_array(obj: object) -> ndarray | None: |
778 | 768 | elif hasattr(obj, "__array__") or hasattr("obj", "__array_interface__"): |
779 | 769 | return np.asarray(obj) |
780 | 770 | return None |
781 | | - |
782 | | - |
783 | | -# builtin pytest.raises helper |
784 | | -# FIXME: This should probably me moved to 'src/_pytest.raises_group.py' |
785 | | -# (and rename the file to 'raises.py') |
786 | | -# since it's much more closely tied to those than to the other stuff in this file. |
787 | | - |
788 | | - |
789 | | -@overload |
790 | | -def raises( |
791 | | - expected_exception: type[E] | tuple[type[E], ...], |
792 | | - *, |
793 | | - match: str | re.Pattern[str] | None = ..., |
794 | | - check: Callable[[E], bool] = ..., |
795 | | -) -> RaisesExc[E]: ... |
796 | | - |
797 | | - |
798 | | -@overload |
799 | | -def raises( |
800 | | - *, |
801 | | - match: str | re.Pattern[str], |
802 | | - # If exception_type is not provided, check() must do any typechecks itself. |
803 | | - check: Callable[[BaseException], bool] = ..., |
804 | | -) -> RaisesExc[BaseException]: ... |
805 | | - |
806 | | - |
807 | | -@overload |
808 | | -def raises(*, check: Callable[[BaseException], bool]) -> RaisesExc[BaseException]: ... |
809 | | - |
810 | | - |
811 | | -@overload |
812 | | -def raises( |
813 | | - expected_exception: type[E] | tuple[type[E], ...], |
814 | | - func: Callable[..., Any], |
815 | | - *args: Any, |
816 | | - **kwargs: Any, |
817 | | -) -> ExceptionInfo[E]: ... |
818 | | - |
819 | | - |
820 | | -def raises( |
821 | | - expected_exception: type[E] | tuple[type[E], ...] | None = None, |
822 | | - *args: Any, |
823 | | - **kwargs: Any, |
824 | | -) -> RaisesExc[BaseException] | ExceptionInfo[E]: |
825 | | - r"""Assert that a code block/function call raises an exception type, or one of its subclasses. |
826 | | -
|
827 | | - :param expected_exception: |
828 | | - The expected exception type, or a tuple if one of multiple possible |
829 | | - exception types are expected. Note that subclasses of the passed exceptions |
830 | | - will also match. |
831 | | -
|
832 | | - :kwparam str | re.Pattern[str] | None match: |
833 | | - If specified, a string containing a regular expression, |
834 | | - or a regular expression object, that is tested against the string |
835 | | - representation of the exception and its :pep:`678` `__notes__` |
836 | | - using :func:`re.search`. |
837 | | -
|
838 | | - To match a literal string that may contain :ref:`special characters |
839 | | - <re-syntax>`, the pattern can first be escaped with :func:`re.escape`. |
840 | | -
|
841 | | - (This is only used when ``pytest.raises`` is used as a context manager, |
842 | | - and passed through to the function otherwise. |
843 | | - When using ``pytest.raises`` as a function, you can use: |
844 | | - ``pytest.raises(Exc, func, match="passed on").match("my pattern")``.) |
845 | | -
|
846 | | - Use ``pytest.raises`` as a context manager, which will capture the exception of the given |
847 | | - type, or any of its subclasses:: |
848 | | -
|
849 | | - >>> import pytest |
850 | | - >>> with pytest.raises(ZeroDivisionError): |
851 | | - ... 1/0 |
852 | | -
|
853 | | - If the code block does not raise the expected exception (:class:`ZeroDivisionError` in the example |
854 | | - above), or no exception at all, the check will fail instead. |
855 | | -
|
856 | | - You can also use the keyword argument ``match`` to assert that the |
857 | | - exception matches a text or regex:: |
858 | | -
|
859 | | - >>> with pytest.raises(ValueError, match='must be 0 or None'): |
860 | | - ... raise ValueError("value must be 0 or None") |
861 | | -
|
862 | | - >>> with pytest.raises(ValueError, match=r'must be \d+$'): |
863 | | - ... raise ValueError("value must be 42") |
864 | | -
|
865 | | - The ``match`` argument searches the formatted exception string, which includes any |
866 | | - `PEP-678 <https://peps.python.org/pep-0678/>`__ ``__notes__``: |
867 | | -
|
868 | | - >>> with pytest.raises(ValueError, match=r"had a note added"): # doctest: +SKIP |
869 | | - ... e = ValueError("value must be 42") |
870 | | - ... e.add_note("had a note added") |
871 | | - ... raise e |
872 | | -
|
873 | | - The context manager produces an :class:`ExceptionInfo` object which can be used to inspect the |
874 | | - details of the captured exception:: |
875 | | -
|
876 | | - >>> with pytest.raises(ValueError) as exc_info: |
877 | | - ... raise ValueError("value must be 42") |
878 | | - >>> assert exc_info.type is ValueError |
879 | | - >>> assert exc_info.value.args[0] == "value must be 42" |
880 | | -
|
881 | | - .. warning:: |
882 | | -
|
883 | | - Given that ``pytest.raises`` matches subclasses, be wary of using it to match :class:`Exception` like this:: |
884 | | -
|
885 | | - # Careful, this will catch ANY exception raised. |
886 | | - with pytest.raises(Exception): |
887 | | - some_function() |
888 | | -
|
889 | | - Because :class:`Exception` is the base class of almost all exceptions, it is easy for this to hide |
890 | | - real bugs, where the user wrote this expecting a specific exception, but some other exception is being |
891 | | - raised due to a bug introduced during a refactoring. |
892 | | -
|
893 | | - Avoid using ``pytest.raises`` to catch :class:`Exception` unless certain that you really want to catch |
894 | | - **any** exception raised. |
895 | | -
|
896 | | - .. note:: |
897 | | -
|
898 | | - When using ``pytest.raises`` as a context manager, it's worthwhile to |
899 | | - note that normal context manager rules apply and that the exception |
900 | | - raised *must* be the final line in the scope of the context manager. |
901 | | - Lines of code after that, within the scope of the context manager will |
902 | | - not be executed. For example:: |
903 | | -
|
904 | | - >>> value = 15 |
905 | | - >>> with pytest.raises(ValueError) as exc_info: |
906 | | - ... if value > 10: |
907 | | - ... raise ValueError("value must be <= 10") |
908 | | - ... assert exc_info.type is ValueError # This will not execute. |
909 | | -
|
910 | | - Instead, the following approach must be taken (note the difference in |
911 | | - scope):: |
912 | | -
|
913 | | - >>> with pytest.raises(ValueError) as exc_info: |
914 | | - ... if value > 10: |
915 | | - ... raise ValueError("value must be <= 10") |
916 | | - ... |
917 | | - >>> assert exc_info.type is ValueError |
918 | | -
|
919 | | - **Expecting exception groups** |
920 | | -
|
921 | | - When expecting exceptions wrapped in :exc:`BaseExceptionGroup` or |
922 | | - :exc:`ExceptionGroup`, you should instead use :class:`pytest.RaisesGroup`. |
923 | | -
|
924 | | - **Using with** ``pytest.mark.parametrize`` |
925 | | -
|
926 | | - When using :ref:`pytest.mark.parametrize ref` |
927 | | - it is possible to parametrize tests such that |
928 | | - some runs raise an exception and others do not. |
929 | | -
|
930 | | - See :ref:`parametrizing_conditional_raising` for an example. |
931 | | -
|
932 | | - .. seealso:: |
933 | | -
|
934 | | - :ref:`assertraises` for more examples and detailed discussion. |
935 | | -
|
936 | | - **Legacy form** |
937 | | -
|
938 | | - It is possible to specify a callable by passing a to-be-called lambda:: |
939 | | -
|
940 | | - >>> raises(ZeroDivisionError, lambda: 1/0) |
941 | | - <ExceptionInfo ...> |
942 | | -
|
943 | | - or you can specify an arbitrary callable with arguments:: |
944 | | -
|
945 | | - >>> def f(x): return 1/x |
946 | | - ... |
947 | | - >>> raises(ZeroDivisionError, f, 0) |
948 | | - <ExceptionInfo ...> |
949 | | - >>> raises(ZeroDivisionError, f, x=0) |
950 | | - <ExceptionInfo ...> |
951 | | -
|
952 | | - The form above is fully supported but discouraged for new code because the |
953 | | - context manager form is regarded as more readable and less error-prone. |
954 | | -
|
955 | | - .. note:: |
956 | | - Similar to caught exception objects in Python, explicitly clearing |
957 | | - local references to returned ``ExceptionInfo`` objects can |
958 | | - help the Python interpreter speed up its garbage collection. |
959 | | -
|
960 | | - Clearing those references breaks a reference cycle |
961 | | - (``ExceptionInfo`` --> caught exception --> frame stack raising |
962 | | - the exception --> current frame stack --> local variables --> |
963 | | - ``ExceptionInfo``) which makes Python keep all objects referenced |
964 | | - from that cycle (including all local variables in the current |
965 | | - frame) alive until the next cyclic garbage collection run. |
966 | | - More detailed information can be found in the official Python |
967 | | - documentation for :ref:`the try statement <python:try>`. |
968 | | - """ |
969 | | - __tracebackhide__ = True |
970 | | - |
971 | | - if not args: |
972 | | - if set(kwargs) - {"match", "check", "expected_exception"}: |
973 | | - msg = "Unexpected keyword arguments passed to pytest.raises: " |
974 | | - msg += ", ".join(sorted(kwargs)) |
975 | | - msg += "\nUse context-manager form instead?" |
976 | | - raise TypeError(msg) |
977 | | - |
978 | | - if expected_exception is None: |
979 | | - return RaisesExc(**kwargs) |
980 | | - return RaisesExc(expected_exception, **kwargs) |
981 | | - |
982 | | - if not expected_exception: |
983 | | - raise ValueError( |
984 | | - f"Expected an exception type or a tuple of exception types, but got `{expected_exception!r}`. " |
985 | | - f"Raising exceptions is already understood as failing the test, so you don't need " |
986 | | - f"any special code to say 'this should never raise an exception'." |
987 | | - ) |
988 | | - func = args[0] |
989 | | - if not callable(func): |
990 | | - raise TypeError(f"{func!r} object (type: {type(func)}) must be callable") |
991 | | - with RaisesExc(expected_exception) as excinfo: |
992 | | - func(*args[1:], **kwargs) |
993 | | - try: |
994 | | - return excinfo |
995 | | - finally: |
996 | | - del excinfo |
997 | | - |
998 | | - |
999 | | -# note: RaisesExc/RaisesGroup uses fail() internally, so this alias |
1000 | | -# indicates (to [internal] plugins?) that `pytest.raises` will |
1001 | | -# raise `_pytest.outcomes.Failed`, where |
1002 | | -# `outcomes.Failed is outcomes.fail.Exception is raises.Exception` |
1003 | | -# note: this is *not* the same as `_pytest.main.Failed` |
1004 | | -# note: mypy does not recognize this attribute, and it's not possible |
1005 | | -# to use a protocol/decorator like the others in outcomes due to |
1006 | | -# https://github.com/python/mypy/issues/18715 |
1007 | | -raises.Exception = fail.Exception # type: ignore[attr-defined] |
0 commit comments