From 1bba0a97146575b496ac3a021f6a68e1be74ec0d Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Thu, 22 Nov 2018 10:05:10 -0800 Subject: [PATCH] Deprecate `raises(..., 'code(as_a_string)')` / `warns(..., 'code(as_a_string)') --- changelog/4435.deprecation.rst | 1 + doc/en/assert.rst | 5 ++- doc/en/deprecations.rst | 35 +++++++++++++++++++ doc/en/example/assertion/failure_demo.py | 6 ++-- doc/en/example/parametrize.rst | 3 +- src/_pytest/deprecated.py | 9 +++++ src/_pytest/python_api.py | 17 ++++----- src/_pytest/recwarn.py | 2 ++ testing/code/test_code.py | 2 +- testing/code/test_excinfo.py | 6 ++-- testing/code/test_source.py | 25 +++++-------- .../sub2/conftest.py | 2 +- testing/python/collect.py | 5 +-- testing/python/fixture.py | 3 +- testing/python/metafunc.py | 8 ++--- testing/python/raises.py | 17 ++++++--- testing/test_capture.py | 10 +++--- testing/test_config.py | 7 ++-- testing/test_monkeypatch.py | 8 ++--- testing/test_parseopt.py | 6 +--- testing/test_pluginmanager.py | 10 +++--- testing/test_pytester.py | 4 +-- testing/test_recwarn.py | 17 ++++++--- testing/test_runner.py | 17 +++------ testing/test_session.py | 8 ++--- testing/test_terminal.py | 3 +- 26 files changed, 140 insertions(+), 96 deletions(-) create mode 100644 changelog/4435.deprecation.rst diff --git a/changelog/4435.deprecation.rst b/changelog/4435.deprecation.rst new file mode 100644 index 000000000..f12f0bc6c --- /dev/null +++ b/changelog/4435.deprecation.rst @@ -0,0 +1 @@ +Deprecate ``raises(..., 'code(as_a_string)')`` and ``warns(..., 'code(as_a_string)')``. See https://docs.pytest.org/en/latest/deprecations.html#raises-warns-exec diff --git a/doc/en/assert.rst b/doc/en/assert.rst index 43fedebed..b13a071f6 100644 --- a/doc/en/assert.rst +++ b/doc/en/assert.rst @@ -100,10 +100,9 @@ If you want to write test code that works on Python 2.4 as well, you may also use two other ways to test for an expected exception:: pytest.raises(ExpectedException, func, *args, **kwargs) - pytest.raises(ExpectedException, "func(*args, **kwargs)") -both of which execute the specified function with args and kwargs and -asserts that the given ``ExpectedException`` is raised. The reporter will +which will execute the specified function with args and kwargs and +assert that the given ``ExpectedException`` is raised. The reporter will provide you with helpful output in case of failures such as *no exception* or *wrong exception*. diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index 3398c92a2..414e2e3f3 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -14,6 +14,41 @@ Below is a complete list of all pytest features which are considered deprecated. :class:`_pytest.warning_types.PytestWarning` or subclasses, which can be filtered using :ref:`standard warning filters `. +.. _raises-warns-exec: + +``raises`` / ``warns`` with a string as the second argument +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. deprecated:: 4.1 + +Use the context manager form of these instead. When necessary, invoke ``exec`` +directly. + +Example: + +.. code-block:: python + + pytest.raises(ZeroDivisionError, "1 / 0") + pytest.raises(SyntaxError, "a $ b") + + pytest.warns(DeprecationWarning, "my_function()") + pytest.warns(SyntaxWarning, "assert(1, 2)") + +Becomes: + +.. code-block:: python + + with pytest.raises(ZeroDivisionError): + 1 / 0 + with pytest.raises(SyntaxError): + exec("a $ b") # exec is required for invalid syntax + + with pytest.warns(DeprecationWarning): + my_function() + with pytest.warns(SyntaxWarning): + exec("assert(1, 2)") # exec is used to avoid a top-level warning + + Internal classes accessed through ``Node`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/en/example/assertion/failure_demo.py b/doc/en/example/assertion/failure_demo.py index 5bd95a37b..31a9f2577 100644 --- a/doc/en/example/assertion/failure_demo.py +++ b/doc/en/example/assertion/failure_demo.py @@ -165,11 +165,11 @@ def globf(x): class TestRaises(object): def test_raises(self): - s = "qwe" # NOQA - raises(TypeError, "int(s)") + s = "qwe" + raises(TypeError, int, s) def test_raises_doesnt(self): - raises(IOError, "int('3')") + raises(IOError, int, "3") def test_raise(self): raise ValueError("demo error") diff --git a/doc/en/example/parametrize.rst b/doc/en/example/parametrize.rst index 488f6e310..bb8ea5996 100644 --- a/doc/en/example/parametrize.rst +++ b/doc/en/example/parametrize.rst @@ -388,7 +388,8 @@ parametrizer`_ but in a lot less code:: assert a == b def test_zerodivision(self, a, b): - pytest.raises(ZeroDivisionError, "a/b") + with pytest.raises(ZeroDivisionError): + a / b Our test generator looks up a class-level definition which specifies which argument sets to use for each test function. Let's run it: diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index a34366280..ce3b91802 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -92,6 +92,15 @@ NODE_WARN = RemovedInPytest4Warning( "Node.warn(code, message) form has been deprecated, use Node.warn(warning_instance) instead." ) +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" +) + RECORD_XML_PROPERTY = RemovedInPytest4Warning( 'Fixture renamed from "record_xml_property" to "record_property" as user ' "properties are now available to all reporters.\n" diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index e9cc2bbde..7e5dc74a8 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -1,6 +1,9 @@ +from __future__ import absolute_import + import math import pprint import sys +import warnings from decimal import Decimal from numbers import Number @@ -14,6 +17,7 @@ from _pytest.compat import isclass from _pytest.compat import Mapping from _pytest.compat import Sequence from _pytest.compat import STRING_TYPES +from _pytest.deprecated import RAISES_EXEC from _pytest.outcomes import fail BASE_TYPE = (type, STRING_TYPES) @@ -604,9 +608,9 @@ def raises(expected_exception, *args, **kwargs): >>> with raises(ValueError, match=r'must be \d+$'): ... raise ValueError("value must be 42") - **Legacy forms** + **Legacy form** - The forms below are fully supported but are discouraged for new code because the + The form below is fully supported but discouraged for new code because the context manager form is regarded as more readable and less error-prone. It is possible to specify a callable by passing a to-be-called lambda:: @@ -623,14 +627,6 @@ def raises(expected_exception, *args, **kwargs): >>> raises(ZeroDivisionError, f, x=0) - It is also possible to pass a string to be evaluated at runtime:: - - >>> raises(ZeroDivisionError, "f(0)") - - - The string will be evaluated using the same ``locals()`` and ``globals()`` - at the moment of the ``raises`` call. - .. currentmodule:: _pytest._code Consult the API of ``excinfo`` objects: :class:`ExceptionInfo`. @@ -672,6 +668,7 @@ def raises(expected_exception, *args, **kwargs): raise TypeError(msg) return RaisesContext(expected_exception, message, match_expr) elif isinstance(args[0], str): + warnings.warn(RAISES_EXEC, stacklevel=2) code, = args assert isinstance(code, str) frame = sys._getframe(1) diff --git a/src/_pytest/recwarn.py b/src/_pytest/recwarn.py index 4f3ab7f29..f39f7aee7 100644 --- a/src/_pytest/recwarn.py +++ b/src/_pytest/recwarn.py @@ -11,6 +11,7 @@ import warnings import six import _pytest._code +from _pytest.deprecated import WARNS_EXEC from _pytest.fixtures import yield_fixture from _pytest.outcomes import fail @@ -89,6 +90,7 @@ def warns(expected_warning, *args, **kwargs): match_expr = kwargs.pop("match") 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) diff --git a/testing/code/test_code.py b/testing/code/test_code.py index df9f109ef..3362d4604 100644 --- a/testing/code/test_code.py +++ b/testing/code/test_code.py @@ -37,7 +37,7 @@ def test_code_with_class(): class A(object): pass - pytest.raises(TypeError, "_pytest._code.Code(A)") + pytest.raises(TypeError, _pytest._code.Code, A) def x(): diff --git a/testing/code/test_excinfo.py b/testing/code/test_excinfo.py index b4d64313c..4e36fb946 100644 --- a/testing/code/test_excinfo.py +++ b/testing/code/test_excinfo.py @@ -180,7 +180,8 @@ class TestTraceback_f_g_h(object): def test_traceback_cut_excludepath(self, testdir): p = testdir.makepyfile("def f(): raise ValueError") - excinfo = pytest.raises(ValueError, "p.pyimport().f()") + with pytest.raises(ValueError) as excinfo: + p.pyimport().f() basedir = py.path.local(pytest.__file__).dirpath() newtraceback = excinfo.traceback.cut(excludepath=basedir) for x in newtraceback: @@ -336,7 +337,8 @@ class TestTraceback_f_g_h(object): def test_excinfo_exconly(): excinfo = pytest.raises(ValueError, h) assert excinfo.exconly().startswith("ValueError") - excinfo = pytest.raises(ValueError, "raise ValueError('hello\\nworld')") + with pytest.raises(ValueError) as excinfo: + raise ValueError("hello\nworld") msg = excinfo.exconly(tryshort=True) assert msg.startswith("ValueError") assert msg.endswith("world") diff --git a/testing/code/test_source.py b/testing/code/test_source.py index 3ee46c1b8..0103acb70 100644 --- a/testing/code/test_source.py +++ b/testing/code/test_source.py @@ -6,6 +6,7 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +import ast import inspect import sys @@ -14,7 +15,6 @@ import six import _pytest._code import pytest from _pytest._code import Source -from _pytest._code.source import ast astonly = pytest.mark.nothing @@ -306,8 +306,6 @@ class TestSourceParsingAndCompiling(object): pytest.raises(SyntaxError, lambda: source.getstatementrange(0)) def test_compile_to_ast(self): - import ast - source = Source("x = 4") mod = source.compile(flag=ast.PyCF_ONLY_AST) assert isinstance(mod, ast.Module) @@ -317,10 +315,9 @@ class TestSourceParsingAndCompiling(object): co = self.source.compile() six.exec_(co, globals()) f(7) - excinfo = pytest.raises(AssertionError, "f(6)") + excinfo = pytest.raises(AssertionError, f, 6) frame = excinfo.traceback[-1].frame stmt = frame.code.fullsource.getstatement(frame.lineno) - # print "block", str(block) assert str(stmt).strip().startswith("assert") @pytest.mark.parametrize("name", ["", None, "my"]) @@ -361,17 +358,13 @@ def test_getline_finally(): def c(): pass - excinfo = pytest.raises( - TypeError, - """ - teardown = None - try: - c(1) - finally: - if teardown: - teardown() - """, - ) + with pytest.raises(TypeError) as excinfo: + teardown = None + try: + c(1) + finally: + if teardown: + teardown() source = excinfo.traceback[-1].statement assert str(source).strip() == "c(1)" diff --git a/testing/example_scripts/fixtures/fill_fixtures/test_conftest_funcargs_only_available_in_subdir/sub2/conftest.py b/testing/example_scripts/fixtures/fill_fixtures/test_conftest_funcargs_only_available_in_subdir/sub2/conftest.py index c37045454..00981c5dc 100644 --- a/testing/example_scripts/fixtures/fill_fixtures/test_conftest_funcargs_only_available_in_subdir/sub2/conftest.py +++ b/testing/example_scripts/fixtures/fill_fixtures/test_conftest_funcargs_only_available_in_subdir/sub2/conftest.py @@ -3,4 +3,4 @@ import pytest @pytest.fixture def arg2(request): - pytest.raises(Exception, "request.getfixturevalue('arg1')") + pytest.raises(Exception, request.getfixturevalue, "arg1") diff --git a/testing/python/collect.py b/testing/python/collect.py index 4ce3d120d..83fcdc3bd 100644 --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -325,7 +325,7 @@ class TestGenerator(object): assert len(colitems) == 1 gencol = colitems[0] assert isinstance(gencol, pytest.Generator) - pytest.raises(ValueError, "gencol.collect()") + pytest.raises(ValueError, gencol.collect) def test_generative_methods_with_explicit_names(self, testdir): modcol = testdir.getmodulecol( @@ -1103,7 +1103,8 @@ def test_modulecol_roundtrip(testdir): class TestTracebackCutting(object): def test_skip_simple(self): - excinfo = pytest.raises(pytest.skip.Exception, 'pytest.skip("xxx")') + with pytest.raises(pytest.skip.Exception) as excinfo: + pytest.skip("xxx") assert excinfo.traceback[-1].frame.code.name == "skip" assert excinfo.traceback[-1].ishidden() diff --git a/testing/python/fixture.py b/testing/python/fixture.py index 86cd29724..b7ce16eb5 100644 --- a/testing/python/fixture.py +++ b/testing/python/fixture.py @@ -906,7 +906,8 @@ class TestRequestMarking(object): assert "skipif" not in item1.keywords req1.applymarker(pytest.mark.skipif) assert "skipif" in item1.keywords - pytest.raises(ValueError, "req1.applymarker(42)") + with pytest.raises(ValueError): + req1.applymarker(42) def test_accesskeywords(self, testdir): testdir.makepyfile( diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index 243d50d2d..833eb5641 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -70,11 +70,11 @@ class TestMetafunc(object): pass metafunc = self.Metafunc(func) - pytest.raises(ValueError, "metafunc.addcall(id=None)") + pytest.raises(ValueError, metafunc.addcall, id=None) metafunc.addcall(id=1) - pytest.raises(ValueError, "metafunc.addcall(id=1)") - pytest.raises(ValueError, "metafunc.addcall(id='1')") + pytest.raises(ValueError, metafunc.addcall, id=1) + pytest.raises(ValueError, metafunc.addcall, id="1") metafunc.addcall(id=2) assert len(metafunc._calls) == 2 assert metafunc._calls[0].id == "1" @@ -108,7 +108,7 @@ class TestMetafunc(object): metafunc.addcall(funcargs={"x": 2}) metafunc.addcall(funcargs={"x": 3}) - pytest.raises(pytest.fail.Exception, "metafunc.addcall({'xyz': 0})") + pytest.raises(pytest.fail.Exception, metafunc.addcall, {"xyz": 0}) assert len(metafunc._calls) == 2 assert metafunc._calls[0].funcargs == {"x": 2} assert metafunc._calls[1].funcargs == {"x": 3} diff --git a/testing/python/raises.py b/testing/python/raises.py index 6ca19c677..e3a0c4a05 100644 --- a/testing/python/raises.py +++ b/testing/python/raises.py @@ -4,25 +4,32 @@ import six import pytest from _pytest.outcomes import Failed +from _pytest.warning_types import PytestDeprecationWarning class TestRaises(object): def test_raises(self): source = "int('qwe')" - excinfo = pytest.raises(ValueError, source) + 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): - pytest.raises(ValueError, "a,x = []") + with pytest.warns(PytestDeprecationWarning) as warninfo: + pytest.raises(ValueError, "a,x = []") + assert warninfo[0].filename == __file__ def test_raises_exec_correct_filename(self): - excinfo = pytest.raises(ValueError, 'int("s")') - assert __file__ in excinfo.traceback[-1].path + with pytest.warns(PytestDeprecationWarning): + excinfo = pytest.raises(ValueError, 'int("s")') + assert __file__ in excinfo.traceback[-1].path def test_raises_syntax_error(self): - pytest.raises(SyntaxError, "qwe qwe qwe") + with pytest.warns(PytestDeprecationWarning) as warninfo: + pytest.raises(SyntaxError, "qwe qwe qwe") + assert warninfo[0].filename == __file__ def test_raises_function(self): pytest.raises(ValueError, int, "hello") diff --git a/testing/test_capture.py b/testing/test_capture.py index 47aba70d4..17bb82967 100644 --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -87,7 +87,7 @@ class TestCaptureManager(object): try: capman = CaptureManager("fd") capman.start_global_capturing() - pytest.raises(AssertionError, "capman.start_global_capturing()") + pytest.raises(AssertionError, capman.start_global_capturing) capman.stop_global_capturing() finally: capouter.stop_capturing() @@ -798,10 +798,10 @@ class TestCaptureIO(object): f = capture.CaptureIO() if sys.version_info >= (3, 0): f.write("\u00f6") - pytest.raises(TypeError, "f.write(bytes('hello', 'UTF-8'))") + pytest.raises(TypeError, f.write, b"hello") else: - f.write(text_type("\u00f6", "UTF-8")) - f.write("hello") # bytes + f.write(u"\u00f6") + f.write(b"hello") s = f.getvalue() f.close() assert isinstance(s, text_type) @@ -1149,7 +1149,7 @@ class TestStdCapture(object): print("XXX which indicates an error in the underlying capturing") print("XXX mechanisms") with self.getcapture(): - pytest.raises(IOError, "sys.stdin.read()") + pytest.raises(IOError, sys.stdin.read) class TestStdCaptureFD(TestStdCapture): diff --git a/testing/test_config.py b/testing/test_config.py index 605d28aa0..fcb886d53 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -194,7 +194,7 @@ class TestConfigAPI(object): config = testdir.parseconfig("--hello=this") for x in ("hello", "--hello", "-X"): assert config.getoption(x) == "this" - pytest.raises(ValueError, "config.getoption('qweqwe')") + pytest.raises(ValueError, config.getoption, "qweqwe") @pytest.mark.skipif("sys.version_info[0] < 3") def test_config_getoption_unicode(self, testdir): @@ -211,7 +211,7 @@ class TestConfigAPI(object): def test_config_getvalueorskip(self, testdir): config = testdir.parseconfig() - pytest.raises(pytest.skip.Exception, "config.getvalueorskip('hello')") + pytest.raises(pytest.skip.Exception, config.getvalueorskip, "hello") verbose = config.getvalueorskip("verbose") assert verbose == config.option.verbose @@ -723,7 +723,8 @@ def test_config_in_subdirectory_colon_command_line_issue2148(testdir): def test_notify_exception(testdir, capfd): config = testdir.parseconfig() - excinfo = pytest.raises(ValueError, "raise ValueError(1)") + with pytest.raises(ValueError) as excinfo: + raise ValueError(1) config.notify_exception(excinfo) out, err = capfd.readouterr() assert "ValueError" in err diff --git a/testing/test_monkeypatch.py b/testing/test_monkeypatch.py index ebc233fbf..9e44b4975 100644 --- a/testing/test_monkeypatch.py +++ b/testing/test_monkeypatch.py @@ -27,7 +27,7 @@ def test_setattr(): x = 1 monkeypatch = MonkeyPatch() - pytest.raises(AttributeError, "monkeypatch.setattr(A, 'notexists', 2)") + pytest.raises(AttributeError, monkeypatch.setattr, A, "notexists", 2) monkeypatch.setattr(A, "y", 2, raising=False) assert A.y == 2 monkeypatch.undo() @@ -99,7 +99,7 @@ def test_delattr(): monkeypatch = MonkeyPatch() monkeypatch.delattr(A, "x") - pytest.raises(AttributeError, "monkeypatch.delattr(A, 'y')") + pytest.raises(AttributeError, monkeypatch.delattr, A, "y") monkeypatch.delattr(A, "y", raising=False) monkeypatch.setattr(A, "x", 5, raising=False) assert A.x == 5 @@ -156,7 +156,7 @@ def test_delitem(): monkeypatch.delitem(d, "x") assert "x" not in d monkeypatch.delitem(d, "y", raising=False) - pytest.raises(KeyError, "monkeypatch.delitem(d, 'y')") + pytest.raises(KeyError, monkeypatch.delitem, d, "y") assert not d monkeypatch.setitem(d, "y", 1700) assert d["y"] == 1700 @@ -182,7 +182,7 @@ def test_delenv(): name = "xyz1234" assert name not in os.environ monkeypatch = MonkeyPatch() - pytest.raises(KeyError, "monkeypatch.delenv(%r, raising=True)" % name) + pytest.raises(KeyError, monkeypatch.delenv, name, raising=True) monkeypatch.delenv(name, raising=False) monkeypatch.undo() os.environ[name] = "1" diff --git a/testing/test_parseopt.py b/testing/test_parseopt.py index 0dafa248b..3048c96bd 100644 --- a/testing/test_parseopt.py +++ b/testing/test_parseopt.py @@ -100,12 +100,8 @@ class TestParser(object): def test_group_shortopt_lowercase(self, parser): group = parser.getgroup("hello") - pytest.raises( - ValueError, - """ + with pytest.raises(ValueError): group.addoption("-x", action="store_true") - """, - ) assert len(group.options) == 0 group._addoption("-x", action="store_true") assert len(group.options) == 1 diff --git a/testing/test_pluginmanager.py b/testing/test_pluginmanager.py index 64d05d383..6137b2771 100644 --- a/testing/test_pluginmanager.py +++ b/testing/test_pluginmanager.py @@ -196,7 +196,7 @@ class TestPytestPluginManager(object): assert pm.is_registered(mod) values = pm.get_plugins() assert mod in values - pytest.raises(ValueError, "pm.register(mod)") + pytest.raises(ValueError, pm.register, mod) pytest.raises(ValueError, lambda: pm.register(mod)) # assert not pm.is_registered(mod2) assert pm.get_plugins() == values @@ -284,8 +284,8 @@ class TestPytestPluginManager(object): result.stdout.fnmatch_lines(["*1 passed*"]) def test_import_plugin_importname(self, testdir, pytestpm): - pytest.raises(ImportError, 'pytestpm.import_plugin("qweqwex.y")') - pytest.raises(ImportError, 'pytestpm.import_plugin("pytest_qweqwx.y")') + pytest.raises(ImportError, pytestpm.import_plugin, "qweqwex.y") + pytest.raises(ImportError, pytestpm.import_plugin, "pytest_qweqwx.y") testdir.syspathinsert() pluginname = "pytest_hello" @@ -301,8 +301,8 @@ class TestPytestPluginManager(object): assert plugin2 is plugin1 def test_import_plugin_dotted_name(self, testdir, pytestpm): - pytest.raises(ImportError, 'pytestpm.import_plugin("qweqwex.y")') - pytest.raises(ImportError, 'pytestpm.import_plugin("pytest_qweqwex.y")') + pytest.raises(ImportError, pytestpm.import_plugin, "qweqwex.y") + pytest.raises(ImportError, pytestpm.import_plugin, "pytest_qweqwex.y") testdir.syspathinsert() testdir.mkpydir("pkg").join("plug.py").write("x=3") diff --git a/testing/test_pytester.py b/testing/test_pytester.py index 0c28bc91b..669da6e17 100644 --- a/testing/test_pytester.py +++ b/testing/test_pytester.py @@ -71,7 +71,7 @@ def test_make_hook_recorder(testdir): recorder.unregister() recorder.clear() recorder.hook.pytest_runtest_logreport(report=rep) - pytest.raises(ValueError, "recorder.getfailures()") + pytest.raises(ValueError, recorder.getfailures) def test_parseconfig(testdir): @@ -174,7 +174,7 @@ def test_hookrecorder_basic(holder): call = rec.popcall("pytest_xyz") assert call.arg == 123 assert call._name == "pytest_xyz" - pytest.raises(pytest.fail.Exception, "rec.popcall('abc')") + pytest.raises(pytest.fail.Exception, rec.popcall, "abc") pm.hook.pytest_xyz_noarg() call = rec.popcall("pytest_xyz_noarg") assert call._name == "pytest_xyz_noarg" diff --git a/testing/test_recwarn.py b/testing/test_recwarn.py index 223521a5e..9bf6a2ffb 100644 --- a/testing/test_recwarn.py +++ b/testing/test_recwarn.py @@ -7,6 +7,7 @@ import warnings import pytest from _pytest.recwarn import WarningsRecorder +from _pytest.warning_types import PytestDeprecationWarning def test_recwarn_stacklevel(recwarn): @@ -44,7 +45,7 @@ class TestWarningsRecorderChecker(object): rec.clear() assert len(rec.list) == 0 assert values is rec.list - pytest.raises(AssertionError, "rec.pop()") + pytest.raises(AssertionError, rec.pop) @pytest.mark.issue(4243) def test_warn_stacklevel(self): @@ -214,9 +215,17 @@ class TestWarns(object): source1 = "warnings.warn('w1', RuntimeWarning)" source2 = "warnings.warn('w2', RuntimeWarning)" source3 = "warnings.warn('w3', RuntimeWarning)" - pytest.warns(RuntimeWarning, source1) - pytest.raises(pytest.fail.Exception, lambda: pytest.warns(UserWarning, source2)) - pytest.warns(RuntimeWarning, source3) + 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") def test_function(self): pytest.warns( diff --git a/testing/test_runner.py b/testing/test_runner.py index 2d047af70..d76f3da9b 100644 --- a/testing/test_runner.py +++ b/testing/test_runner.py @@ -700,17 +700,13 @@ def test_importorskip(monkeypatch): # check that importorskip reports the actual call # in this test the test_runner.py file assert path.purebasename == "test_runner" - pytest.raises(SyntaxError, "pytest.importorskip('x y z')") - pytest.raises(SyntaxError, "pytest.importorskip('x=y')") + pytest.raises(SyntaxError, pytest.importorskip, "x y z") + pytest.raises(SyntaxError, pytest.importorskip, "x=y") mod = types.ModuleType("hello123") mod.__version__ = "1.3" monkeypatch.setitem(sys.modules, "hello123", mod) - pytest.raises( - pytest.skip.Exception, - """ + with pytest.raises(pytest.skip.Exception): pytest.importorskip("hello123", minversion="1.3.1") - """, - ) mod2 = pytest.importorskip("hello123", minversion="1.3") assert mod2 == mod except pytest.skip.Exception: @@ -730,11 +726,8 @@ def test_importorskip_dev_module(monkeypatch): monkeypatch.setitem(sys.modules, "mockmodule", mod) mod2 = pytest.importorskip("mockmodule", minversion="0.12.0") assert mod2 == mod - pytest.raises( - pytest.skip.Exception, - """ - pytest.importorskip('mockmodule1', minversion='0.14.0')""", - ) + with pytest.raises(pytest.skip.Exception): + pytest.importorskip("mockmodule1", minversion="0.14.0") except pytest.skip.Exception: print(_pytest._code.ExceptionInfo.from_current()) pytest.fail("spurious skip") diff --git a/testing/test_session.py b/testing/test_session.py index 0dc98a703..32d71a18e 100644 --- a/testing/test_session.py +++ b/testing/test_session.py @@ -243,12 +243,8 @@ class TestNewSession(SessionTests): def test_plugin_specify(testdir): - pytest.raises( - ImportError, - """ - testdir.parseconfig("-p", "nqweotexistent") - """, - ) + with pytest.raises(ImportError): + testdir.parseconfig("-p", "nqweotexistent") # pytest.raises(ImportError, # "config.do_configure(config)" # ) diff --git a/testing/test_terminal.py b/testing/test_terminal.py index 60a64cdd6..0faf9b401 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -105,7 +105,8 @@ class TestTerminal(object): def test_internalerror(self, testdir, linecomp): modcol = testdir.getmodulecol("def test_one(): pass") rep = TerminalReporter(modcol.config, file=linecomp.stringio) - excinfo = pytest.raises(ValueError, "raise ValueError('hello')") + with pytest.raises(ValueError) as excinfo: + raise ValueError("hello") rep.pytest_internalerror(excinfo.getrepr()) linecomp.assert_contains_lines(["INTERNALERROR> *ValueError*hello*"])