-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
add paramspec to callable forms of raises/warns/deprecated_call, rewrite tests to use CM form #13241
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
jakkdl
wants to merge
4
commits into
pytest-dev:main
Choose a base branch
from
jakkdl:raises_paramspec
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
add paramspec to callable forms of raises/warns/deprecated_call, rewrite tests to use CM form #13241
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
:func:`pytest.raises`, :func:`pytest.warns` and :func:`pytest.deprecated_call` now uses :class:`ParamSpec` for the type hint to the (old and not recommended) callable overload, instead of :class:`Any`. This allows type checkers to raise errors when passing incorrect function parameters. | ||
``func`` can now also be passed as a kwarg, which the type hint previously showed as possible but didn't accept. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,8 +17,11 @@ | |
|
||
|
||
if TYPE_CHECKING: | ||
from typing_extensions import ParamSpec | ||
from typing_extensions import Self | ||
|
||
P = ParamSpec("P") | ||
|
||
import warnings | ||
|
||
from _pytest.deprecated import check_ispytest | ||
|
@@ -49,7 +52,7 @@ def deprecated_call( | |
|
||
|
||
@overload | ||
def deprecated_call(func: Callable[..., T], *args: Any, **kwargs: Any) -> T: ... | ||
def deprecated_call(func: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T: ... | ||
|
||
|
||
def deprecated_call( | ||
|
@@ -67,23 +70,24 @@ def deprecated_call( | |
>>> import pytest | ||
>>> with pytest.deprecated_call(): | ||
... assert api_call_v2() == 200 | ||
>>> with pytest.deprecated_call(match="^use v3 of this api$") as warning_messages: | ||
... assert api_call_v2() == 200 | ||
|
||
It can also be used by passing a function and ``*args`` and ``**kwargs``, | ||
jakkdl marked this conversation as resolved.
Show resolved
Hide resolved
|
||
in which case it will ensure calling ``func(*args, **kwargs)`` produces one of | ||
the warnings types above. The return value is the return value of the function. | ||
|
||
In the context manager form you may use the keyword argument ``match`` to assert | ||
You may use the keyword argument ``match`` to assert | ||
that the warning matches a text or regex. | ||
|
||
The context manager produces a list of :class:`warnings.WarningMessage` objects, | ||
one for each warning raised. | ||
The return value is a list of :class:`warnings.WarningMessage` objects, | ||
one for each warning emitted | ||
(regardless of whether it is an ``expected_warning`` or not). | ||
""" | ||
__tracebackhide__ = True | ||
if func is not None: | ||
args = (func, *args) | ||
return warns( | ||
(DeprecationWarning, PendingDeprecationWarning, FutureWarning), *args, **kwargs | ||
) | ||
# Potential QoL: allow `with deprecated_call:` - i.e. no parens | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems weird to me :) |
||
dep_warnings = (DeprecationWarning, PendingDeprecationWarning, FutureWarning) | ||
if func is None: | ||
return warns(dep_warnings, *args, **kwargs) | ||
|
||
with warns(dep_warnings): | ||
return func(*args, **kwargs) | ||
|
||
|
||
@overload | ||
|
@@ -97,16 +101,16 @@ def warns( | |
@overload | ||
def warns( | ||
expected_warning: type[Warning] | tuple[type[Warning], ...], | ||
func: Callable[..., T], | ||
*args: Any, | ||
**kwargs: Any, | ||
func: Callable[P, T], | ||
*args: P.args, | ||
**kwargs: P.kwargs, | ||
) -> T: ... | ||
|
||
|
||
def warns( | ||
expected_warning: type[Warning] | tuple[type[Warning], ...] = Warning, | ||
func: Callable[..., object] | None = None, | ||
*args: Any, | ||
match: str | re.Pattern[str] | None = None, | ||
**kwargs: Any, | ||
) -> WarningsChecker | Any: | ||
r"""Assert that code raises a particular class of warning. | ||
|
@@ -119,13 +123,13 @@ def warns( | |
each warning emitted (regardless of whether it is an ``expected_warning`` or not). | ||
Since pytest 8.0, unmatched warnings are also re-emitted when the context closes. | ||
|
||
This function can be used as a context manager:: | ||
This function should be used as a context manager:: | ||
|
||
>>> import pytest | ||
>>> with pytest.warns(RuntimeWarning): | ||
... warnings.warn("my warning", RuntimeWarning) | ||
|
||
In the context manager form you may use the keyword argument ``match`` to assert | ||
The ``match`` keyword argument can be used to assert | ||
that the warning matches a text or regex:: | ||
|
||
>>> with pytest.warns(UserWarning, match='must be 0 or None'): | ||
|
@@ -151,7 +155,8 @@ def warns( | |
|
||
""" | ||
__tracebackhide__ = True | ||
if not args: | ||
if func is None and not args: | ||
match: str | re.Pattern[str] | None = kwargs.pop("match", None) | ||
if kwargs: | ||
argnames = ", ".join(sorted(kwargs)) | ||
raise TypeError( | ||
|
@@ -160,11 +165,10 @@ def warns( | |
) | ||
return WarningsChecker(expected_warning, match_expr=match, _ispytest=True) | ||
else: | ||
func = args[0] | ||
if not callable(func): | ||
raise TypeError(f"{func!r} object (type: {type(func)}) must be callable") | ||
with WarningsChecker(expected_warning, _ispytest=True): | ||
return func(*args[1:], **kwargs) | ||
return func(*args, **kwargs) | ||
|
||
|
||
class WarningsRecorder(warnings.catch_warnings): | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed, let's remove this warning.
Removing the "Nonetheless" seems fine.
I also think we can remove the "The reporter will provide ..." paragraph to make the section shorter.