Skip to content

Commit

Permalink
Remove PytestReturnNotNoneWarning and PytestUnhandledCoroutineWarning…
Browse files Browse the repository at this point in the history
…. Make tests fail instead of raising warning/exception. fix tests. add changelog.
  • Loading branch information
jakkdl committed Oct 25, 2024
1 parent 45c2ffb commit 216ec3c
Show file tree
Hide file tree
Showing 6 changed files with 28 additions and 59 deletions.
3 changes: 3 additions & 0 deletions src/_pytest/config/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from __future__ import annotations

from typing import final


@final
class UsageError(Exception):
"""Error in pytest usage or invocation."""

Expand Down
48 changes: 17 additions & 31 deletions src/_pytest/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@
from _pytest.config import Config
from _pytest.config import hookimpl
from _pytest.config.argparsing import Parser
from _pytest.config.exceptions import UsageError
from _pytest.deprecated import check_ispytest
from _pytest.fixtures import FixtureDef
from _pytest.fixtures import FixtureRequest
Expand All @@ -74,7 +73,6 @@
from _pytest.scope import Scope
from _pytest.stash import StashKey
from _pytest.warning_types import PytestCollectionWarning
from _pytest.warning_types import PytestReturnNotNoneWarning


if TYPE_CHECKING:
Expand Down Expand Up @@ -135,48 +133,36 @@ def pytest_configure(config: Config) -> None:
)


@final
class PytestUnhandledCoroutineError(UsageError):
"""An unraisable exception resulted in an error.
Unraisable exceptions are exceptions raised in :meth:`__del__ <object.__del__>`
implementations and similar situations when the exception cannot be raised
as normal.
"""


def async_warn_and_skip(nodeid: str) -> None:
msg = "async def functions are not natively supported and have been skipped.\n"
msg += (
def async_fail(nodeid: str) -> None:
msg = (

Check warning on line 137 in src/_pytest/python.py

View check run for this annotation

Codecov / codecov/patch

src/_pytest/python.py#L137

Added line #L137 was not covered by tests
"async def functions are not natively supported.\n"
"You need to install a suitable plugin for your async framework, for example:\n"
" - anyio\n"
" - pytest-asyncio\n"
" - pytest-tornasync\n"
" - pytest-trio\n"
" - pytest-twisted"
)
msg += " - anyio\n"
msg += " - pytest-asyncio\n"
msg += " - pytest-tornasync\n"
msg += " - pytest-trio\n"
msg += " - pytest-twisted"
raise PytestUnhandledCoroutineError(
msg.format(nodeid)
) # TODO: This is the warning to look at
skip(reason="async def function and no async plugin installed (see warnings)")
fail(msg, pytrace=False)

Check warning on line 146 in src/_pytest/python.py

View check run for this annotation

Codecov / codecov/patch

src/_pytest/python.py#L146

Added line #L146 was not covered by tests


@hookimpl(trylast=True)
def pytest_pyfunc_call(pyfuncitem: Function) -> object | None:
testfunction = pyfuncitem.obj
if is_async_function(testfunction):
async_warn_and_skip(pyfuncitem.nodeid)
async_fail(pyfuncitem.nodeid)

Check warning on line 153 in src/_pytest/python.py

View check run for this annotation

Codecov / codecov/patch

src/_pytest/python.py#L153

Added line #L153 was not covered by tests
funcargs = pyfuncitem.funcargs
testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames}
result = testfunction(**testargs)
if hasattr(result, "__await__") or hasattr(result, "__aiter__"):
async_warn_and_skip(pyfuncitem.nodeid)
async_fail(pyfuncitem.nodeid)

Check warning on line 158 in src/_pytest/python.py

View check run for this annotation

Codecov / codecov/patch

src/_pytest/python.py#L158

Added line #L158 was not covered by tests
elif result is not None:
warnings.warn(
PytestReturnNotNoneWarning(
f"Expected None, but {pyfuncitem.nodeid} returned {result!r}, which will be an error in a "
"future version of pytest. Did you mean to use `assert` instead of `return`?"
)
fail(

Check warning on line 160 in src/_pytest/python.py

View check run for this annotation

Codecov / codecov/patch

src/_pytest/python.py#L160

Added line #L160 was not covered by tests
(
f"Expected None, but test returned {result!r}. "
"Did you mean to use `assert` instead of `return`?"
),
pytrace=False,
)
return True

Expand Down
18 changes: 0 additions & 18 deletions src/_pytest/warning_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,6 @@ class PytestRemovedIn9Warning(PytestDeprecationWarning):
__module__ = "pytest"


class PytestReturnNotNoneWarning(PytestWarning):
"""Warning emitted when a test function is returning value other than None."""

__module__ = "pytest"


@final
class PytestExperimentalApiWarning(PytestWarning, FutureWarning):
"""Warning category used to denote experiments in pytest.
Expand All @@ -77,18 +71,6 @@ def simple(cls, apiname: str) -> PytestExperimentalApiWarning:
return cls(f"{apiname} is an experimental api that may change over time")


@final
class PytestUnhandledCoroutineWarning(PytestReturnNotNoneWarning): # TODO: look at this
"""Warning emitted for an unhandled coroutine.
A coroutine was encountered when collecting test functions, but was not
handled by any async-aware plugin.
Coroutine test functions are not natively supported.
"""

__module__ = "pytest"


@final
class PytestUnknownMarkWarning(PytestWarning):
"""Warning emitted on use of unknown markers.
Expand Down
4 changes: 0 additions & 4 deletions src/pytest/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,6 @@
from _pytest.warning_types import PytestDeprecationWarning
from _pytest.warning_types import PytestExperimentalApiWarning
from _pytest.warning_types import PytestRemovedIn9Warning
from _pytest.warning_types import PytestReturnNotNoneWarning
from _pytest.warning_types import PytestUnhandledCoroutineWarning
from _pytest.warning_types import PytestUnhandledThreadExceptionWarning
from _pytest.warning_types import PytestUnknownMarkWarning
from _pytest.warning_types import PytestUnraisableExceptionWarning
Expand Down Expand Up @@ -142,10 +140,8 @@
"PytestDeprecationWarning",
"PytestExperimentalApiWarning",
"PytestRemovedIn9Warning",
"PytestReturnNotNoneWarning",
"Pytester",
"PytestPluginManager",
"PytestUnhandledCoroutineWarning",
"PytestUnhandledThreadExceptionWarning",
"PytestUnknownMarkWarning",
"PytestUnraisableExceptionWarning",
Expand Down
12 changes: 7 additions & 5 deletions testing/acceptance_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1235,7 +1235,7 @@ def test_usage_error_code(pytester: Pytester) -> None:
assert result.ret == ExitCode.USAGE_ERROR


def test_error_on_async_function(pytester: Pytester) -> None: # TODO: Change this
def test_error_on_async_function(pytester: Pytester) -> None:
# In the below we .close() the coroutine only to avoid
# "RuntimeWarning: coroutine 'test_2' was never awaited"
# which messes with other tests.
Expand All @@ -1254,16 +1254,16 @@ def test_3():
result = pytester.runpytest()
result.stdout.fnmatch_lines(
[
"*async def functions are not natively supported*",
"*test_async.py::test_1*",
"*test_async.py::test_2*",
"*test_async.py::test_3*",
"*async def functions are not natively supported*",
]
)
result.assert_outcomes(failed=3)


def test_error_on_async_gen_function(pytester: Pytester) -> None: # TODO: Change this
def test_error_on_async_gen_function(pytester: Pytester) -> None:
pytester.makepyfile(
test_async="""
async def test_1():
Expand All @@ -1277,12 +1277,13 @@ def test_3():
result = pytester.runpytest()
result.stdout.fnmatch_lines(
[
"*async def functions are not natively supported*",
"*test_async.py::test_1*",
"*test_async.py::test_2*",
"*test_async.py::test_3*",
"*async def functions are not natively supported*",
]
)
result.assert_outcomes(failed=3)


def test_pdb_can_be_rewritten(pytester: Pytester) -> None:
Expand Down Expand Up @@ -1368,14 +1369,15 @@ def test_no_brokenpipeerror_message(pytester: Pytester) -> None:
popen.stderr.close()


def test_function_return_non_none_warning(pytester: Pytester) -> None:
def test_function_return_non_none_error(pytester: Pytester) -> None:
pytester.makepyfile(
"""
def test_stuff():
return "something"
"""
)
res = pytester.runpytest()
res.assert_outcomes(failed=1)
res.stdout.fnmatch_lines(["*Did you mean to use `assert` instead of `return`?*"])


Expand Down
2 changes: 1 addition & 1 deletion testing/test_unittest.py
Original file line number Diff line number Diff line change
Expand Up @@ -1314,7 +1314,7 @@ def test_1(self):
assert tracked == []


def test_async_support(pytester: Pytester) -> None: # TODO: Change this
def test_async_support(pytester: Pytester) -> None:
pytest.importorskip("unittest.async_case")

pytester.copy_example("unittest/test_unittest_asyncio.py")
Expand Down

0 comments on commit 216ec3c

Please sign in to comment.