Remove support for 'code as string' from pytest.raises and pytest.warns

This commit is contained in:
Bruno Oliveira 2019-06-30 11:40:24 -03:00
parent be91c4d932
commit 279733a30b
8 changed files with 58 additions and 99 deletions

View File

@ -2,3 +2,9 @@ As per our policy, the following features have been deprecated in the 4.X series
removed:
* ``Request.getfuncargvalue``: use ``Request.getfixturevalue`` instead.
* ``pytest.raises`` and ``pytest.warns`` no longer support strings as the second argument.
For more information consult
`Deprecations and Removals <https://docs.pytest.org/en/latest/deprecations.html>`__ in the docs.

View File

@ -80,12 +80,35 @@ The ``pytest.config`` global object is deprecated. Instead use
use the ``pytest_configure(config)`` hook. Note that many hooks can also access
the ``config`` object indirectly, through ``session.config`` or ``item.config`` for example.
Result log (``--result-log``)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 4.0
The ``--result-log`` option produces a stream of test reports which can be
analysed at runtime. It uses a custom format which requires users to implement their own
parser, but the team believes using a line-based format that can be parsed using standard
tools would provide a suitable and better alternative.
The current plan is to provide an alternative in the pytest 5.0 series and remove the ``--result-log``
option in pytest 6.0 after the new implementation proves satisfactory to all users and is deemed
stable.
The actual alternative is still being discussed in issue `#4488 <https://github.com/pytest-dev/pytest/issues/4488>`__.
Removed Features
----------------
As stated in our :ref:`backwards-compatibility` policy, deprecated features are removed only in major releases after
an appropriate period of deprecation has passed.
.. _raises-warns-exec:
``raises`` / ``warns`` with a string as the second argument
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 4.1
.. versionremoved:: 5.0
Use the context manager form of these instead. When necessary, invoke ``exec``
directly.
@ -116,27 +139,6 @@ Becomes:
Result log (``--result-log``)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 4.0
The ``--result-log`` option produces a stream of test reports which can be
analysed at runtime. It uses a custom format which requires users to implement their own
parser, but the team believes using a line-based format that can be parsed using standard
tools would provide a suitable and better alternative.
The current plan is to provide an alternative in the pytest 5.0 series and remove the ``--result-log``
option in pytest 6.0 after the new implementation proves satisfactory to all users and is deemed
stable.
The actual alternative is still being discussed in issue `#4488 <https://github.com/pytest-dev/pytest/issues/4488>`__.
Removed Features
----------------
As stated in our :ref:`backwards-compatibility` policy, deprecated features are removed only in major releases after
an appropriate period of deprecation has passed.
Using ``Class`` in custom Collectors
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -54,14 +54,6 @@ RESULT_LOG = PytestDeprecationWarning(
"See https://docs.pytest.org/en/latest/deprecations.html#result-log-result-log for more information."
)
RAISES_EXEC = PytestDeprecationWarning(
"raises(..., 'code(as_a_string)') is deprecated, use the context manager form or use `exec()` directly\n\n"
"See https://docs.pytest.org/en/latest/deprecations.html#raises-warns-exec"
)
WARNS_EXEC = PytestDeprecationWarning(
"warns(..., 'code(as_a_string)') is deprecated, use the context manager form or use `exec()` directly.\n\n"
"See https://docs.pytest.org/en/latest/deprecations.html#raises-warns-exec"
)
PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST = (
"Defining 'pytest_plugins' in a non-top-level conftest is no longer supported "

View File

@ -1,7 +1,6 @@
import inspect
import math
import pprint
import sys
import warnings
from collections.abc import Iterable
from collections.abc import Mapping
@ -667,23 +666,12 @@ def raises(expected_exception, *args, **kwargs):
msg += ", ".join(sorted(kwargs))
raise TypeError(msg)
return RaisesContext(expected_exception, message, match_expr)
elif isinstance(args[0], str):
warnings.warn(deprecated.RAISES_EXEC, stacklevel=2)
code, = args
assert isinstance(code, str)
frame = sys._getframe(1)
loc = frame.f_locals.copy()
loc.update(kwargs)
# print "raises frame scope: %r" % frame.f_locals
try:
code = _pytest._code.Source(code).compile(_genframe=frame)
exec(code, frame.f_globals, loc)
# XXX didn't mean f_globals == f_locals something special?
# this is destroyed here ...
except expected_exception:
return _pytest._code.ExceptionInfo.from_current()
else:
func = args[0]
if not callable(func):
raise TypeError(
"{!r} object (type: {}) must be callable".format(func, type(func))
)
try:
func(*args[1:], **kwargs)
except expected_exception:

View File

@ -1,12 +1,9 @@
""" recording warnings during test function execution. """
import inspect
import re
import sys
import warnings
import _pytest._code
from _pytest.deprecated import PYTEST_WARNS_UNKNOWN_KWARGS
from _pytest.deprecated import WARNS_EXEC
from _pytest.fixtures import yield_fixture
from _pytest.outcomes import fail
@ -86,19 +83,12 @@ def warns(expected_warning, *args, **kwargs):
PYTEST_WARNS_UNKNOWN_KWARGS.format(args=sorted(kwargs)), stacklevel=2
)
return WarningsChecker(expected_warning, match_expr=match_expr)
elif isinstance(args[0], str):
warnings.warn(WARNS_EXEC, stacklevel=2)
code, = args
assert isinstance(code, str)
frame = sys._getframe(1)
loc = frame.f_locals.copy()
loc.update(kwargs)
with WarningsChecker(expected_warning):
code = _pytest._code.Source(code).compile()
exec(code, frame.f_globals, loc)
else:
func = args[0]
if not callable(func):
raise TypeError(
"{!r} object (type: {}) must be callable".format(func, type(func))
)
with WarningsChecker(expected_warning):
return func(*args[1:], **kwargs)

View File

@ -6,31 +6,17 @@ from _pytest.warning_types import PytestDeprecationWarning
class TestRaises:
def test_check_callable(self):
with pytest.raises(TypeError, match=r".* must be callable"):
pytest.raises(RuntimeError, "int('qwe')")
def test_raises(self):
source = "int('qwe')"
with pytest.warns(PytestDeprecationWarning):
excinfo = pytest.raises(ValueError, source)
code = excinfo.traceback[-1].frame.code
s = str(code.fullsource)
assert s == source
def test_raises_exec(self):
with pytest.warns(PytestDeprecationWarning) as warninfo:
pytest.raises(ValueError, "a,x = []")
assert warninfo[0].filename == __file__
def test_raises_exec_correct_filename(self):
with pytest.warns(PytestDeprecationWarning):
excinfo = pytest.raises(ValueError, 'int("s")')
assert __file__ in excinfo.traceback[-1].path
def test_raises_syntax_error(self):
with pytest.warns(PytestDeprecationWarning) as warninfo:
pytest.raises(SyntaxError, "qwe qwe qwe")
assert warninfo[0].filename == __file__
excinfo = pytest.raises(ValueError, int, "qwe")
assert "invalid literal" in str(excinfo.value)
def test_raises_function(self):
pytest.raises(ValueError, int, "hello")
excinfo = pytest.raises(ValueError, int, "hello")
assert "invalid literal" in str(excinfo.value)
def test_raises_callable_no_exception(self):
class A:

View File

@ -25,7 +25,8 @@ class TestMark:
def test_pytest_mark_notcallable(self):
mark = Mark()
pytest.raises((AttributeError, TypeError), mark)
with pytest.raises(TypeError):
mark()
def test_mark_with_param(self):
def some_function(abc):

View File

@ -3,7 +3,6 @@ import warnings
import pytest
from _pytest.recwarn import WarningsRecorder
from _pytest.warning_types import PytestDeprecationWarning
def test_recwarn_stacklevel(recwarn):
@ -206,22 +205,17 @@ class TestDeprecatedCall:
class TestWarns:
def test_strings(self):
def test_check_callable(self):
source = "warnings.warn('w1', RuntimeWarning)"
with pytest.raises(TypeError, match=r".* must be callable"):
pytest.warns(RuntimeWarning, source)
def test_several_messages(self):
# different messages, b/c Python suppresses multiple identical warnings
source1 = "warnings.warn('w1', RuntimeWarning)"
source2 = "warnings.warn('w2', RuntimeWarning)"
source3 = "warnings.warn('w3', RuntimeWarning)"
with pytest.warns(PytestDeprecationWarning) as warninfo: # yo dawg
pytest.warns(RuntimeWarning, source1)
pytest.raises(
pytest.fail.Exception, lambda: pytest.warns(UserWarning, source2)
)
pytest.warns(RuntimeWarning, source3)
assert len(warninfo) == 3
for w in warninfo:
assert w.filename == __file__
msg, = w.message.args
assert msg.startswith("warns(..., 'code(as_a_string)') is deprecated")
pytest.warns(RuntimeWarning, lambda: warnings.warn("w1", RuntimeWarning))
with pytest.raises(pytest.fail.Exception):
pytest.warns(UserWarning, lambda: warnings.warn("w2", RuntimeWarning))
pytest.warns(RuntimeWarning, lambda: warnings.warn("w3", RuntimeWarning))
def test_function(self):
pytest.warns(