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: removed:
* ``Request.getfuncargvalue``: use ``Request.getfixturevalue`` instead. * ``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 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. 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-exec:
``raises`` / ``warns`` with a string as the second argument ``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`` Use the context manager form of these instead. When necessary, invoke ``exec``
directly. 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 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." "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 = ( PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST = (
"Defining 'pytest_plugins' in a non-top-level conftest is no longer supported " "Defining 'pytest_plugins' in a non-top-level conftest is no longer supported "

View File

@ -1,7 +1,6 @@
import inspect import inspect
import math import math
import pprint import pprint
import sys
import warnings import warnings
from collections.abc import Iterable from collections.abc import Iterable
from collections.abc import Mapping from collections.abc import Mapping
@ -667,23 +666,12 @@ def raises(expected_exception, *args, **kwargs):
msg += ", ".join(sorted(kwargs)) msg += ", ".join(sorted(kwargs))
raise TypeError(msg) raise TypeError(msg)
return RaisesContext(expected_exception, message, match_expr) 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: else:
func = args[0] func = args[0]
if not callable(func):
raise TypeError(
"{!r} object (type: {}) must be callable".format(func, type(func))
)
try: try:
func(*args[1:], **kwargs) func(*args[1:], **kwargs)
except expected_exception: except expected_exception:

View File

@ -1,12 +1,9 @@
""" recording warnings during test function execution. """ """ recording warnings during test function execution. """
import inspect import inspect
import re import re
import sys
import warnings import warnings
import _pytest._code
from _pytest.deprecated import PYTEST_WARNS_UNKNOWN_KWARGS from _pytest.deprecated import PYTEST_WARNS_UNKNOWN_KWARGS
from _pytest.deprecated import WARNS_EXEC
from _pytest.fixtures import yield_fixture from _pytest.fixtures import yield_fixture
from _pytest.outcomes import fail 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 PYTEST_WARNS_UNKNOWN_KWARGS.format(args=sorted(kwargs)), stacklevel=2
) )
return WarningsChecker(expected_warning, match_expr=match_expr) 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: else:
func = args[0] func = args[0]
if not callable(func):
raise TypeError(
"{!r} object (type: {}) must be callable".format(func, type(func))
)
with WarningsChecker(expected_warning): with WarningsChecker(expected_warning):
return func(*args[1:], **kwargs) return func(*args[1:], **kwargs)

View File

@ -6,31 +6,17 @@ from _pytest.warning_types import PytestDeprecationWarning
class TestRaises: 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): def test_raises(self):
source = "int('qwe')" excinfo = pytest.raises(ValueError, int, "qwe")
with pytest.warns(PytestDeprecationWarning): assert "invalid literal" in str(excinfo.value)
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__
def test_raises_function(self): 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): def test_raises_callable_no_exception(self):
class A: class A:

View File

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

View File

@ -3,7 +3,6 @@ import warnings
import pytest import pytest
from _pytest.recwarn import WarningsRecorder from _pytest.recwarn import WarningsRecorder
from _pytest.warning_types import PytestDeprecationWarning
def test_recwarn_stacklevel(recwarn): def test_recwarn_stacklevel(recwarn):
@ -206,22 +205,17 @@ class TestDeprecatedCall:
class TestWarns: 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 # different messages, b/c Python suppresses multiple identical warnings
source1 = "warnings.warn('w1', RuntimeWarning)" pytest.warns(RuntimeWarning, lambda: warnings.warn("w1", RuntimeWarning))
source2 = "warnings.warn('w2', RuntimeWarning)" with pytest.raises(pytest.fail.Exception):
source3 = "warnings.warn('w3', RuntimeWarning)" pytest.warns(UserWarning, lambda: warnings.warn("w2", RuntimeWarning))
with pytest.warns(PytestDeprecationWarning) as warninfo: # yo dawg pytest.warns(RuntimeWarning, lambda: warnings.warn("w3", RuntimeWarning))
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")
def test_function(self): def test_function(self):
pytest.warns( pytest.warns(