1717
1818
1919if TYPE_CHECKING :
20+ from typing_extensions import ParamSpec
2021 from typing_extensions import Self
2122
23+ P = ParamSpec ("P" )
24+
2225import warnings
2326
2427from _pytest .deprecated import check_ispytest
@@ -49,7 +52,7 @@ def deprecated_call(
4952
5053
5154@overload
52- def deprecated_call (func : Callable [... , T ], * args : Any , ** kwargs : Any ) -> T : ...
55+ def deprecated_call (func : Callable [P , T ], * args : P . args , ** kwargs : P . kwargs ) -> T : ...
5356
5457
5558def deprecated_call (
@@ -67,6 +70,8 @@ def deprecated_call(
6770 >>> import pytest
6871 >>> with pytest.deprecated_call():
6972 ... assert api_call_v2() == 200
73+ >>> with pytest.deprecated_call(match="^use v3 of this api$") as warning_messages:
74+ ... assert api_call_v2() == 200
7075
7176 It can also be used by passing a function and ``*args`` and ``**kwargs``,
7277 in which case it will ensure calling ``func(*args, **kwargs)`` produces one of
@@ -76,14 +81,17 @@ def deprecated_call(
7681 that the warning matches a text or regex.
7782
7883 The context manager produces a list of :class:`warnings.WarningMessage` objects,
79- one for each warning raised.
84+ one for each warning emitted
85+ (regardless of whether it is an ``expected_warning`` or not).
8086 """
8187 __tracebackhide__ = True
82- if func is not None :
83- args = (func , * args )
84- return warns (
85- (DeprecationWarning , PendingDeprecationWarning , FutureWarning ), * args , ** kwargs
86- )
88+ # Potential QoL: allow `with deprecated_call:` - i.e. no parens
89+ dep_warnings = (DeprecationWarning , PendingDeprecationWarning , FutureWarning )
90+ if func is None :
91+ return warns (dep_warnings , * args , ** kwargs )
92+
93+ with warns (dep_warnings ):
94+ return func (* args , ** kwargs )
8795
8896
8997@overload
@@ -97,16 +105,16 @@ def warns(
97105@overload
98106def warns (
99107 expected_warning : type [Warning ] | tuple [type [Warning ], ...],
100- func : Callable [... , T ],
101- * args : Any ,
102- ** kwargs : Any ,
108+ func : Callable [P , T ],
109+ * args : P . args ,
110+ ** kwargs : P . kwargs ,
103111) -> T : ...
104112
105113
106114def warns (
107115 expected_warning : type [Warning ] | tuple [type [Warning ], ...] = Warning ,
116+ func : Callable [..., object ] | None = None ,
108117 * args : Any ,
109- match : str | re .Pattern [str ] | None = None ,
110118 ** kwargs : Any ,
111119) -> WarningsChecker | Any :
112120 r"""Assert that code raises a particular class of warning.
@@ -151,7 +159,8 @@ def warns(
151159
152160 """
153161 __tracebackhide__ = True
154- if not args :
162+ if func is None and not args :
163+ match : str | re .Pattern [str ] | None = kwargs .pop ("match" , None )
155164 if kwargs :
156165 argnames = ", " .join (sorted (kwargs ))
157166 raise TypeError (
@@ -160,11 +169,10 @@ def warns(
160169 )
161170 return WarningsChecker (expected_warning , match_expr = match , _ispytest = True )
162171 else :
163- func = args [0 ]
164172 if not callable (func ):
165173 raise TypeError (f"{ func !r} object (type: { type (func )} ) must be callable" )
166174 with WarningsChecker (expected_warning , _ispytest = True ):
167- return func (* args [ 1 :] , ** kwargs )
175+ return func (* args , ** kwargs )
168176
169177
170178class WarningsRecorder (warnings .catch_warnings ):
0 commit comments