aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/pytest/py3/_pytest/python_api.py
diff options
context:
space:
mode:
authorrobot-piglet <robot-piglet@yandex-team.com>2025-05-05 12:31:52 +0300
committerrobot-piglet <robot-piglet@yandex-team.com>2025-05-05 12:41:33 +0300
commit6ff49ec58061f642c3a2f83c61eba12820787dfc (patch)
treec733ec9bdb15ed280080d31dea8725bfec717acd /contrib/python/pytest/py3/_pytest/python_api.py
parenteefca8305c6a545cc6b16dca3eb0d91dcef2adcd (diff)
downloadydb-6ff49ec58061f642c3a2f83c61eba12820787dfc.tar.gz
Intermediate changes
commit_hash:8b3bb826b17db8329ed1221f545c0645f12c552d
Diffstat (limited to 'contrib/python/pytest/py3/_pytest/python_api.py')
-rw-r--r--contrib/python/pytest/py3/_pytest/python_api.py81
1 files changed, 47 insertions, 34 deletions
diff --git a/contrib/python/pytest/py3/_pytest/python_api.py b/contrib/python/pytest/py3/_pytest/python_api.py
index 183356100c5..2c0ba09bb1d 100644
--- a/contrib/python/pytest/py3/_pytest/python_api.py
+++ b/contrib/python/pytest/py3/_pytest/python_api.py
@@ -1,17 +1,19 @@
-import math
-import pprint
from collections.abc import Collection
from collections.abc import Sized
from decimal import Decimal
+import math
from numbers import Complex
+import pprint
from types import TracebackType
from typing import Any
from typing import Callable
from typing import cast
from typing import ContextManager
+from typing import final
from typing import List
from typing import Mapping
from typing import Optional
+from typing import overload
from typing import Pattern
from typing import Sequence
from typing import Tuple
@@ -20,24 +22,13 @@ from typing import TYPE_CHECKING
from typing import TypeVar
from typing import Union
-if TYPE_CHECKING:
- from numpy import ndarray
-
-
import _pytest._code
-from _pytest.compat import final
from _pytest.compat import STRING_TYPES
-from _pytest.compat import overload
from _pytest.outcomes import fail
-def _non_numeric_type_error(value, at: Optional[str]) -> TypeError:
- at_str = f" at {at}" if at else ""
- return TypeError(
- "cannot make approximate comparisons to non-numeric values: {!r} {}".format(
- value, at_str
- )
- )
+if TYPE_CHECKING:
+ from numpy import ndarray
def _compare_approx(
@@ -247,9 +238,7 @@ class ApproxMapping(ApproxBase):
with numeric values (the keys can be anything)."""
def __repr__(self) -> str:
- return "approx({!r})".format(
- {k: self._approx_scalar(v) for k, v in self.expected.items()}
- )
+ return f"approx({({k: self._approx_scalar(v) for k, v in self.expected.items()})!r})"
def _repr_compare(self, other_side: Mapping[object, float]) -> List[str]:
import math
@@ -324,9 +313,7 @@ class ApproxSequenceLike(ApproxBase):
seq_type = type(self.expected)
if seq_type not in (tuple, list):
seq_type = list
- return "approx({!r})".format(
- seq_type(self._approx_scalar(x) for x in self.expected)
- )
+ return f"approx({seq_type(self._approx_scalar(x) for x in self.expected)!r})"
def _repr_compare(self, other_side: Sequence[float]) -> List[str]:
import math
@@ -706,7 +693,6 @@ def approx(expected, rel=None, abs=None, nan_ok: bool = False) -> ApproxBase:
``approx`` falls back to strict equality for nonnumeric types instead
of raising ``TypeError``.
"""
-
# Delegate the comparison to a class that knows how to deal with the type
# of the expected value (e.g. int, float, list, dict, numpy.array, etc).
#
@@ -805,34 +791,35 @@ def raises( # noqa: F811
def raises( # noqa: F811
expected_exception: Union[Type[E], Tuple[Type[E], ...]], *args: Any, **kwargs: Any
) -> Union["RaisesContext[E]", _pytest._code.ExceptionInfo[E]]:
- r"""Assert that a code block/function call raises an exception.
+ r"""Assert that a code block/function call raises an exception type, or one of its subclasses.
- :param typing.Type[E] | typing.Tuple[typing.Type[E], ...] expected_exception:
+ :param expected_exception:
The expected exception type, or a tuple if one of multiple possible
- exception types are expected.
- :kwparam str | typing.Pattern[str] | None match:
+ exception types are expected. Note that subclasses of the passed exceptions
+ will also match.
+
+ :kwparam str | re.Pattern[str] | None match:
If specified, a string containing a regular expression,
or a regular expression object, that is tested against the string
- representation of the exception using :func:`re.search`.
+ representation of the exception and its :pep:`678` `__notes__`
+ using :func:`re.search`.
To match a literal string that may contain :ref:`special characters
<re-syntax>`, the pattern can first be escaped with :func:`re.escape`.
- (This is only used when :py:func:`pytest.raises` is used as a context manager,
+ (This is only used when ``pytest.raises`` is used as a context manager,
and passed through to the function otherwise.
- When using :py:func:`pytest.raises` as a function, you can use:
+ When using ``pytest.raises`` as a function, you can use:
``pytest.raises(Exc, func, match="passed on").match("my pattern")``.)
- .. currentmodule:: _pytest._code
-
Use ``pytest.raises`` as a context manager, which will capture the exception of the given
- type::
+ type, or any of its subclasses::
>>> import pytest
>>> with pytest.raises(ZeroDivisionError):
... 1/0
- If the code block does not raise the expected exception (``ZeroDivisionError`` in the example
+ If the code block does not raise the expected exception (:class:`ZeroDivisionError` in the example
above), or no exception at all, the check will fail instead.
You can also use the keyword argument ``match`` to assert that the
@@ -844,6 +831,14 @@ def raises( # noqa: F811
>>> with pytest.raises(ValueError, match=r'must be \d+$'):
... raise ValueError("value must be 42")
+ The ``match`` argument searches the formatted exception string, which includes any
+ `PEP-678 <https://peps.python.org/pep-0678/>`__ ``__notes__``:
+
+ >>> with pytest.raises(ValueError, match=r"had a note added"): # doctest: +SKIP
+ ... e = ValueError("value must be 42")
+ ... e.add_note("had a note added")
+ ... raise e
+
The context manager produces an :class:`ExceptionInfo` object which can be used to inspect the
details of the captured exception::
@@ -852,6 +847,20 @@ def raises( # noqa: F811
>>> assert exc_info.type is ValueError
>>> assert exc_info.value.args[0] == "value must be 42"
+ .. warning::
+
+ Given that ``pytest.raises`` matches subclasses, be wary of using it to match :class:`Exception` like this::
+
+ with pytest.raises(Exception): # Careful, this will catch ANY exception raised.
+ some_function()
+
+ Because :class:`Exception` is the base class of almost all exceptions, it is easy for this to hide
+ real bugs, where the user wrote this expecting a specific exception, but some other exception is being
+ raised due to a bug introduced during a refactoring.
+
+ Avoid using ``pytest.raises`` to catch :class:`Exception` unless certain that you really want to catch
+ **any** exception raised.
+
.. note::
When using ``pytest.raises`` as a context manager, it's worthwhile to
@@ -864,7 +873,7 @@ def raises( # noqa: F811
>>> with pytest.raises(ValueError) as exc_info:
... if value > 10:
... raise ValueError("value must be <= 10")
- ... assert exc_info.type is ValueError # this will not execute
+ ... assert exc_info.type is ValueError # This will not execute.
Instead, the following approach must be taken (note the difference in
scope)::
@@ -883,6 +892,10 @@ def raises( # noqa: F811
See :ref:`parametrizing_conditional_raising` for an example.
+ .. seealso::
+
+ :ref:`assertraises` for more examples and detailed discussion.
+
**Legacy form**
It is possible to specify a callable by passing a to-be-called lambda::