Skip to content

Commit fee7cf7

Browse files
committed
update docs, add changelog, fix tests
1 parent c8566ca commit fee7cf7

File tree

10 files changed

+47
-66
lines changed

10 files changed

+47
-66
lines changed

changelog/12141.improvement.rst

Lines changed: 0 additions & 1 deletion
This file was deleted.

changelog/13241.deprecation.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
The legacy callable form of :func:`pytest.raises`, :func:`pytest.warns` and :func:`pytest.deprecated_call` has been deprecated. Use the context-manager form instead.

changelog/13241.improvement.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:func:`pytest.raises`, :func:`pytest.warns` and :func:`pytest.deprecated_call` now uses :class:`ParamSpec` for the type hint to the (now-deprecated) callable overload, instead of :class:`Any`. This allows type checkers to raise errors when passing incorrect function parameters.
2+
``func`` can now also be passed as a kwarg, which the type hint previously showed as possible but didn't accept.

doc/en/deprecations.rst

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,37 @@ Deprecated Features
1414
Below is a complete list of all pytest features which are considered deprecated. Using those features will issue
1515
:class:`~pytest.PytestWarning` or subclasses, which can be filtered using :ref:`standard warning filters <warnings>`.
1616

17+
legacy callable form of :func:`raises<pytest.raises>`, :func:`warns<pytest.warns>` and :func:`deprecated_call<pytest.deprecated_call>`
18+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
19+
20+
.. deprecated:: 8.4
21+
22+
Pytest created the callable form of :func:`pytest.raises`, :func:`pytest.warns` and :func:`pytest.deprecated_call` before
23+
the ``with`` statement was added in :pep:`python 2.5 <343>`. It has been kept for a long time, but is considered harder
24+
to read and doesn't allow passing `match` or other parameters.
25+
26+
.. code-block:: python
27+
28+
def my_warn(par1, par2, par3):
29+
warnings.warn(DeprecationWarning(f"{par1}{par2}{par3}"))
30+
return 6.28
31+
32+
33+
# deprecated callable form
34+
35+
excinfo = pytest.raises(ValueError, int, "hello")
36+
ret1 = pytest.warns(DeprecationWarning, my_warns, "a", "b", "c")
37+
ret2 = pytest.deprecated_call(my_warns, "d", "e", "f")
38+
39+
# context-manager form
40+
41+
with pytest.raises(ValueError) as excinfo:
42+
int("hello")
43+
with pytest.warns(DeprecationWarning):
44+
ret1 = my_warns("a", "b", "c")
45+
with pytest.deprecated_call():
46+
ret2 = my_warns("d", "e", "f")
47+
1748
1849
.. _sync-test-async-fixture:
1950

doc/en/how-to/assert.rst

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -194,30 +194,6 @@ exception at a specific level; exceptions contained directly in the top
194194
assert not excinfo.group_contains(RuntimeError, depth=2)
195195
assert not excinfo.group_contains(TypeError, depth=1)
196196
197-
Alternate form (legacy)
198-
~~~~~~~~~~~~~~~~~~~~~~~
199-
200-
There is an alternate form where you pass
201-
a function that will be executed, along ``*args`` and ``**kwargs``, and :func:`pytest.raises`
202-
will execute the function with the arguments and assert that the given exception is raised:
203-
204-
.. code-block:: python
205-
206-
def func(x):
207-
if x <= 0:
208-
raise ValueError("x needs to be larger than zero")
209-
210-
211-
pytest.raises(ValueError, func, x=-1)
212-
213-
The reporter will provide you with helpful output in case of failures such as *no
214-
exception* or *wrong exception*.
215-
216-
This form was the original :func:`pytest.raises` API, developed before the ``with`` statement was
217-
added to the Python language. Nowadays, this form is rarely used, with the context-manager form (using ``with``)
218-
being considered more readable.
219-
Nonetheless, this form is fully supported and not deprecated in any way.
220-
221197
xfail mark and pytest.raises
222198
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
223199

doc/en/how-to/capture-warnings.rst

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -337,13 +337,6 @@ Some examples:
337337
... warnings.warn("issue with foo() func")
338338
...
339339
340-
You can also call :func:`pytest.warns` on a function or code string:
341-
342-
.. code-block:: python
343-
344-
pytest.warns(expected_warning, func, *args, **kwargs)
345-
pytest.warns(expected_warning, "func(*args, **kwargs)")
346-
347340
The function also returns a list of all raised warnings (as
348341
``warnings.WarningMessage`` objects), which you can query for
349342
additional information:

src/_pytest/python_api.py

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -824,7 +824,7 @@ def raises(
824824
*args: Any,
825825
**kwargs: Any,
826826
) -> RaisesContext[E] | _pytest._code.ExceptionInfo[E]:
827-
r"""Assert that a code block/function call raises an exception type, or one of its subclasses.
827+
r"""Assert that a code block raises an exception type, or one of its subclasses.
828828
829829
:param expected_exception:
830830
The expected exception type, or a tuple if one of multiple possible
@@ -930,25 +930,6 @@ def raises(
930930
931931
:ref:`assertraises` for more examples and detailed discussion.
932932
933-
**Legacy form**
934-
935-
It is possible to specify a callable by passing a to-be-called lambda::
936-
937-
>>> raises(ZeroDivisionError, lambda: 1/0)
938-
<ExceptionInfo ...>
939-
940-
or you can specify an arbitrary callable with arguments::
941-
942-
>>> def f(x): return 1/x
943-
...
944-
>>> raises(ZeroDivisionError, f, 0)
945-
<ExceptionInfo ...>
946-
>>> raises(ZeroDivisionError, f, x=0)
947-
<ExceptionInfo ...>
948-
949-
The form above is fully supported but discouraged for new code because the
950-
context manager form is regarded as more readable and less error-prone.
951-
952933
.. note::
953934
Similar to caught exception objects in Python, explicitly clearing
954935
local references to returned ``ExceptionInfo`` objects can

src/_pytest/recwarn.py

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,9 @@ def deprecated_call(func: Callable[P, T], *args: P.args, **kwargs: P.kwargs) ->
6262
def deprecated_call(
6363
func: Callable[..., Any] | None = None, *args: Any, **kwargs: Any
6464
) -> WarningsRecorder | Any:
65-
"""Assert that code produces a ``DeprecationWarning`` or ``PendingDeprecationWarning`` or ``FutureWarning``.
65+
"""Assert that a code block produces a ``DeprecationWarning`` or ``PendingDeprecationWarning`` or ``FutureWarning``.
6666
67-
This function can be used as a context manager::
67+
This function is used as a context manager::
6868
6969
>>> import warnings
7070
>>> def api_call_v2():
@@ -74,16 +74,14 @@ def deprecated_call(
7474
>>> import pytest
7575
>>> with pytest.deprecated_call():
7676
... assert api_call_v2() == 200
77+
>>> with pytest.deprecated_call(match="^use v3 of this api$") as warning_messages:
78+
... assert api_call_v2() == 200
7779
78-
It can also be used by passing a function and ``*args`` and ``**kwargs``,
79-
in which case it will ensure calling ``func(*args, **kwargs)`` produces one of
80-
the warnings types above. The return value is the return value of the function.
81-
82-
In the context manager form you may use the keyword argument ``match`` to assert
80+
You may use the keyword argument ``match`` to assert
8381
that the warning matches a text or regex.
8482
85-
The context manager produces a list of :class:`warnings.WarningMessage` objects,
86-
one for each warning raised.
83+
This helper produces a list of :class:`warnings.WarningMessage` objects, one for
84+
each warning emitted (regardless of whether it is an ``expected_warning`` or not).
8785
"""
8886
__tracebackhide__ = True
8987
# potential QoL: allow `with deprecated_call:` - i.e. no parens
@@ -119,7 +117,7 @@ def warns(
119117
*args: Any,
120118
**kwargs: Any,
121119
) -> WarningsChecker | Any:
122-
r"""Assert that code raises a particular class of warning.
120+
r"""Assert that a code block raises a particular class of warning.
123121
124122
Specifically, the parameter ``expected_warning`` can be a warning class or tuple
125123
of warning classes, and the code inside the ``with`` block must issue at least one
@@ -129,13 +127,13 @@ def warns(
129127
each warning emitted (regardless of whether it is an ``expected_warning`` or not).
130128
Since pytest 8.0, unmatched warnings are also re-emitted when the context closes.
131129
132-
This function can be used as a context manager::
130+
Use this function as a context manager::
133131
134132
>>> import pytest
135133
>>> with pytest.warns(RuntimeWarning):
136134
... warnings.warn("my warning", RuntimeWarning)
137135
138-
In the context manager form you may use the keyword argument ``match`` to assert
136+
You can use the keyword argument ``match`` to assert
139137
that the warning matches a text or regex::
140138
141139
>>> with pytest.warns(UserWarning, match='must be 0 or None'):

testing/example_scripts/fixtures/fill_fixtures/test_conftest_funcargs_only_available_in_subdir/sub2/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@
66

77
@pytest.fixture
88
def arg2(request):
9-
with pytest.raises(Exception): # noqa: B017
9+
with pytest.raises(Exception): # noqa: B017 # too general exception
1010
request.getfixturevalue("arg1")

testing/test_debugging.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ def test_1():
310310
)
311311
child = pytester.spawn_pytest(f"--pdb {p1}")
312312
child.expect(".*def test_1")
313-
child.expect(".*pytest.raises.*globalfunc")
313+
child.expect(r"with pytest.raises\(ValueError\)")
314314
child.expect("Pdb")
315315
child.sendline("globalfunc")
316316
child.expect(".*function")

0 commit comments

Comments
 (0)