From be91c4d932820d9e8b69ce63b480dfbcc2e3fa33 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sun, 30 Jun 2019 11:02:46 -0300 Subject: [PATCH 01/14] Remove Request.getfuncargvalue --- changelog/5180.removal.rst | 4 ++++ src/_pytest/deprecated.py | 3 --- src/_pytest/fixtures.py | 7 ------ testing/deprecated_test.py | 4 ---- testing/python/fixtures.py | 46 +++++++++++++------------------------- 5 files changed, 20 insertions(+), 44 deletions(-) create mode 100644 changelog/5180.removal.rst diff --git a/changelog/5180.removal.rst b/changelog/5180.removal.rst new file mode 100644 index 000000000..952a75c82 --- /dev/null +++ b/changelog/5180.removal.rst @@ -0,0 +1,4 @@ +As per our policy, the following features have been deprecated in the 4.X series and are now being +removed: + +* ``Request.getfuncargvalue``: use ``Request.getfixturevalue`` instead. diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index e31b9eb0e..4b68fd107 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -36,9 +36,6 @@ FIXTURE_NAMED_REQUEST = PytestDeprecationWarning( CFG_PYTEST_SECTION = "[pytest] section in {filename} files is no longer supported, change to [tool:pytest] instead." -GETFUNCARGVALUE = RemovedInPytest4Warning( - "getfuncargvalue is deprecated, use getfixturevalue" -) FUNCARGNAMES = PytestDeprecationWarning( "The `funcargnames` attribute was an alias for `fixturenames`, " diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 3262b65bb..2cde5725c 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -470,13 +470,6 @@ class FixtureRequest(FuncargnamesCompatAttr): """ return self._get_active_fixturedef(argname).cached_result[0] - def getfuncargvalue(self, argname): - """ Deprecated, use getfixturevalue. """ - from _pytest import deprecated - - warnings.warn(deprecated.GETFUNCARGVALUE, stacklevel=2) - return self.getfixturevalue(argname) - def _get_active_fixturedef(self, argname): try: return self._fixture_defs[argname] diff --git a/testing/deprecated_test.py b/testing/deprecated_test.py index 5cbb694b1..4ce89113d 100644 --- a/testing/deprecated_test.py +++ b/testing/deprecated_test.py @@ -32,10 +32,6 @@ def test_pytest_custom_cfg_unsupported(testdir): testdir.runpytest("-c", "custom.cfg") -def test_getfuncargvalue_is_deprecated(request): - pytest.deprecated_call(request.getfuncargvalue, "tmpdir") - - @pytest.mark.filterwarnings("default") def test_resultlog_is_deprecated(testdir): result = testdir.runpytest("--help") diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index c0c230ccf..63fb6294d 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -599,8 +599,7 @@ class TestRequestBasic: result = testdir.runpytest() result.stdout.fnmatch_lines(["* 2 passed in *"]) - @pytest.mark.parametrize("getfixmethod", ("getfixturevalue", "getfuncargvalue")) - def test_getfixturevalue(self, testdir, getfixmethod): + def test_getfixturevalue(self, testdir): item = testdir.getitem( """ import pytest @@ -613,35 +612,22 @@ class TestRequestBasic: def test_func(something): pass """ ) - import contextlib - - if getfixmethod == "getfuncargvalue": - warning_expectation = pytest.warns(DeprecationWarning) - else: - # see #1830 for a cleaner way to accomplish this - @contextlib.contextmanager - def expecting_no_warning(): - yield - - warning_expectation = expecting_no_warning() - req = item._request - with warning_expectation: - fixture_fetcher = getattr(req, getfixmethod) - with pytest.raises(FixtureLookupError): - fixture_fetcher("notexists") - val = fixture_fetcher("something") - assert val == 1 - val = fixture_fetcher("something") - assert val == 1 - val2 = fixture_fetcher("other") - assert val2 == 2 - val2 = fixture_fetcher("other") # see about caching - assert val2 == 2 - pytest._fillfuncargs(item) - assert item.funcargs["something"] == 1 - assert len(get_public_names(item.funcargs)) == 2 - assert "request" in item.funcargs + + with pytest.raises(FixtureLookupError): + req.getfixturevalue("notexists") + val = req.getfixturevalue("something") + assert val == 1 + val = req.getfixturevalue("something") + assert val == 1 + val2 = req.getfixturevalue("other") + assert val2 == 2 + val2 = req.getfixturevalue("other") # see about caching + assert val2 == 2 + pytest._fillfuncargs(item) + assert item.funcargs["something"] == 1 + assert len(get_public_names(item.funcargs)) == 2 + assert "request" in item.funcargs def test_request_addfinalizer(self, testdir): item = testdir.getitem( From 279733a30b700bc30848c4d4ff10f65c454e952b Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sun, 30 Jun 2019 11:40:24 -0300 Subject: [PATCH 02/14] Remove support for 'code as string' from pytest.raises and pytest.warns --- changelog/5180.removal.rst | 6 +++++ doc/en/deprecations.rst | 46 ++++++++++++++++++++------------------ src/_pytest/deprecated.py | 8 ------- src/_pytest/python_api.py | 20 ++++------------- src/_pytest/recwarn.py | 18 ++++----------- testing/python/raises.py | 30 +++++++------------------ testing/test_mark.py | 3 ++- testing/test_recwarn.py | 26 +++++++++------------ 8 files changed, 58 insertions(+), 99 deletions(-) diff --git a/changelog/5180.removal.rst b/changelog/5180.removal.rst index 952a75c82..bde3e03ff 100644 --- a/changelog/5180.removal.rst +++ b/changelog/5180.removal.rst @@ -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 `__ in the docs. diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index e2399dd41..344e1dd72 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -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 `__. + +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 `__. - -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 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index 4b68fd107..77c7c30ff 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -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 " diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index 374fa598f..f709057a3 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -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: diff --git a/src/_pytest/recwarn.py b/src/_pytest/recwarn.py index 006d97e7f..8733a893a 100644 --- a/src/_pytest/recwarn.py +++ b/src/_pytest/recwarn.py @@ -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) diff --git a/testing/python/raises.py b/testing/python/raises.py index c9ede412a..3ace96e7a 100644 --- a/testing/python/raises.py +++ b/testing/python/raises.py @@ -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: diff --git a/testing/test_mark.py b/testing/test_mark.py index c22e9dbb5..1544ffe56 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -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): diff --git a/testing/test_recwarn.py b/testing/test_recwarn.py index 1c68b3787..65fdd1682 100644 --- a/testing/test_recwarn.py +++ b/testing/test_recwarn.py @@ -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( From 13f7f27fd2f99db956de03112add27288d2ce6af Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sun, 30 Jun 2019 11:48:27 -0300 Subject: [PATCH 03/14] Remove 'message' parameter from pytest.raises --- changelog/5180.removal.rst | 4 +- doc/en/deprecations.rst | 75 ++++++++++++++++++++------------------ src/_pytest/deprecated.py | 6 --- src/_pytest/python_api.py | 7 ---- testing/deprecated_test.py | 6 --- testing/python/raises.py | 12 ------ 6 files changed, 42 insertions(+), 68 deletions(-) diff --git a/changelog/5180.removal.rst b/changelog/5180.removal.rst index bde3e03ff..2417672be 100644 --- a/changelog/5180.removal.rst +++ b/changelog/5180.removal.rst @@ -1,10 +1,12 @@ -As per our policy, the following features have been deprecated in the 4.X series and are now being +As per our policy, the following features have been deprecated in the 4.X series and are now removed: * ``Request.getfuncargvalue``: use ``Request.getfixturevalue`` instead. * ``pytest.raises`` and ``pytest.warns`` no longer support strings as the second argument. +* ``message`` parameter of ``pytest.raises``. + For more information consult `Deprecations and Removals `__ in the docs. diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index 344e1dd72..c97061f09 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -20,8 +20,8 @@ Below is a complete list of all pytest features which are considered deprecated. :ref:`standard warning filters `. -Removal of ``funcargnames`` alias for ``fixturenames`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +``funcargnames`` alias for ``fixturenames`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. deprecated:: 5.0 @@ -34,40 +34,6 @@ in places where we or plugin authors must distinguish between fixture names and names supplied by non-fixture things such as ``pytest.mark.parametrize``. -.. _`raises message deprecated`: - -``"message"`` parameter of ``pytest.raises`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. deprecated:: 4.1 - -It is a common mistake to think this parameter will match the exception message, while in fact -it only serves to provide a custom message in case the ``pytest.raises`` check fails. To prevent -users from making this mistake, and because it is believed to be little used, pytest is -deprecating it without providing an alternative for the moment. - -If you have a valid use case for this parameter, consider that to obtain the same results -you can just call ``pytest.fail`` manually at the end of the ``with`` statement. - -For example: - -.. code-block:: python - - with pytest.raises(TimeoutError, message="Client got unexpected message"): - wait_for(websocket.recv(), 0.5) - - -Becomes: - -.. code-block:: python - - with pytest.raises(TimeoutError): - wait_for(websocket.recv(), 0.5) - pytest.fail("Client got unexpected message") - - -If you still have concerns about this deprecation and future removal, please comment on -`issue #3974 `__. ``pytest.config`` global @@ -103,6 +69,43 @@ 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 message deprecated`: + +``"message"`` parameter of ``pytest.raises`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. versionremoved:: 5.0 + +It is a common mistake to think this parameter will match the exception message, while in fact +it only serves to provide a custom message in case the ``pytest.raises`` check fails. To prevent +users from making this mistake, and because it is believed to be little used, pytest is +deprecating it without providing an alternative for the moment. + +If you have a valid use case for this parameter, consider that to obtain the same results +you can just call ``pytest.fail`` manually at the end of the ``with`` statement. + +For example: + +.. code-block:: python + + with pytest.raises(TimeoutError, message="Client got unexpected message"): + wait_for(websocket.recv(), 0.5) + + +Becomes: + +.. code-block:: python + + with pytest.raises(TimeoutError): + wait_for(websocket.recv(), 0.5) + pytest.fail("Client got unexpected message") + + +If you still have concerns about this deprecation and future removal, please comment on +`issue #3974 `__. + + .. _raises-warns-exec: ``raises`` / ``warns`` with a string as the second argument diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index 77c7c30ff..bbb073520 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -42,12 +42,6 @@ FUNCARGNAMES = PytestDeprecationWarning( "since pytest 2.3 - use the newer attribute instead." ) -RAISES_MESSAGE_PARAMETER = PytestDeprecationWarning( - "The 'message' parameter is deprecated.\n" - "(did you mean to use `match='some regex'` to check the exception message?)\n" - "Please see:\n" - " https://docs.pytest.org/en/4.6-maintenance/deprecations.html#message-parameter-of-pytest-raises" -) RESULT_LOG = PytestDeprecationWarning( "--result-log is deprecated and scheduled for removal in pytest 6.0.\n" diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index f709057a3..9f5220766 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -1,7 +1,6 @@ import inspect import math import pprint -import warnings from collections.abc import Iterable from collections.abc import Mapping from collections.abc import Sized @@ -12,7 +11,6 @@ from numbers import Number from more_itertools.more import always_iterable import _pytest._code -from _pytest import deprecated from _pytest.compat import STRING_TYPES from _pytest.outcomes import fail @@ -538,8 +536,6 @@ def raises(expected_exception, *args, **kwargs): __ https://docs.python.org/3/library/re.html#regular-expression-syntax - :kwparam message: **(deprecated since 4.1)** if specified, provides a custom failure message - if the exception is not raised. See :ref:`the deprecation docs ` for a workaround. .. currentmodule:: _pytest._code @@ -656,9 +652,6 @@ def raises(expected_exception, *args, **kwargs): match_expr = None if not args: - if "message" in kwargs: - message = kwargs.pop("message") - warnings.warn(deprecated.RAISES_MESSAGE_PARAMETER, stacklevel=2) if "match" in kwargs: match_expr = kwargs.pop("match") if kwargs: diff --git a/testing/deprecated_test.py b/testing/deprecated_test.py index 4ce89113d..4db4a9c98 100644 --- a/testing/deprecated_test.py +++ b/testing/deprecated_test.py @@ -76,12 +76,6 @@ def test_external_plugins_integrated(testdir, plugin): testdir.parseconfig("-p", plugin) -def test_raises_message_argument_deprecated(): - with pytest.warns(pytest.PytestDeprecationWarning): - with pytest.raises(RuntimeError, message="foobar"): - raise RuntimeError - - def test_pytest_plugins_in_non_top_level_conftest_deprecated(testdir): from _pytest.deprecated import PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST diff --git a/testing/python/raises.py b/testing/python/raises.py index 3ace96e7a..4f514e322 100644 --- a/testing/python/raises.py +++ b/testing/python/raises.py @@ -2,7 +2,6 @@ import sys import pytest from _pytest.outcomes import Failed -from _pytest.warning_types import PytestDeprecationWarning class TestRaises: @@ -155,17 +154,6 @@ class TestRaises: else: assert False, "Expected pytest.raises.Exception" - def test_custom_raise_message(self): - message = "TEST_MESSAGE" - try: - with pytest.warns(PytestDeprecationWarning): - with pytest.raises(ValueError, message=message): - pass - except pytest.raises.Exception as e: - assert e.msg == message - else: - assert False, "Expected pytest.raises.Exception" - @pytest.mark.parametrize("method", ["function", "with"]) def test_raises_cyclic_reference(self, method): """ From 683b2632b4e32ed935854e42bafd18b0178b4368 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sun, 30 Jun 2019 12:02:11 -0300 Subject: [PATCH 04/14] Remove explicit kwargs handling from raises, warns and ParameterSet.param --- changelog/5180.removal.rst | 4 ++++ src/_pytest/deprecated.py | 13 ------------- src/_pytest/mark/structures.py | 20 ++++++-------------- src/_pytest/python_api.py | 8 +++----- src/_pytest/recwarn.py | 13 ++++++------- testing/deprecated_test.py | 9 --------- testing/test_mark.py | 13 ------------- 7 files changed, 19 insertions(+), 61 deletions(-) diff --git a/changelog/5180.removal.rst b/changelog/5180.removal.rst index 2417672be..e72b76d5a 100644 --- a/changelog/5180.removal.rst +++ b/changelog/5180.removal.rst @@ -7,6 +7,10 @@ removed: * ``message`` parameter of ``pytest.raises``. +* ``pytest.raises``, ``pytest.warns`` and ``ParameterSet.param`` now use native keyword-only + syntax. This might change the exception message from previous versions, but they still raise + ``TypeError`` on unknown keyword arguments as before. + For more information consult `Deprecations and Removals `__ in the docs. diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index bbb073520..dd2d65d15 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -10,7 +10,6 @@ in case of warnings which need to format their messages. """ from _pytest.warning_types import PytestDeprecationWarning from _pytest.warning_types import RemovedInPytest4Warning -from _pytest.warning_types import UnformattedWarning YIELD_TESTS = "yield tests were removed in pytest 4.0 - {name} will be ignored" @@ -73,15 +72,3 @@ PYTEST_LOGWARNING = PytestDeprecationWarning( "pytest_logwarning is deprecated, no longer being called, and will be removed soon\n" "please use pytest_warning_captured instead" ) - -PYTEST_WARNS_UNKNOWN_KWARGS = UnformattedWarning( - PytestDeprecationWarning, - "pytest.warns() got unexpected keyword arguments: {args!r}.\n" - "This will be an error in future versions.", -) - -PYTEST_PARAM_UNKNOWN_KWARGS = UnformattedWarning( - PytestDeprecationWarning, - "pytest.param() got unexpected keyword arguments: {args!r}.\n" - "This will be an error in future versions.", -) diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py index 1af7a9b42..aa4c8f828 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -9,7 +9,6 @@ import attr from ..compat import ascii_escaped from ..compat import getfslineno from ..compat import NOTSET -from _pytest.deprecated import PYTEST_PARAM_UNKNOWN_KWARGS from _pytest.outcomes import fail from _pytest.warning_types import PytestUnknownMarkWarning @@ -61,26 +60,19 @@ def get_empty_parameterset_mark(config, argnames, func): class ParameterSet(namedtuple("ParameterSet", "values, marks, id")): @classmethod - def param(cls, *values, **kwargs): - marks = kwargs.pop("marks", ()) + def param(cls, *values, marks=(), id=None): if isinstance(marks, MarkDecorator): marks = (marks,) else: assert isinstance(marks, (tuple, list, set)) - id_ = kwargs.pop("id", None) - if id_ is not None: - if not isinstance(id_, str): + if id is not None: + if not isinstance(id, str): raise TypeError( - "Expected id to be a string, got {}: {!r}".format(type(id_), id_) + "Expected id to be a string, got {}: {!r}".format(type(id), id) ) - id_ = ascii_escaped(id_) - - if kwargs: - warnings.warn( - PYTEST_PARAM_UNKNOWN_KWARGS.format(args=sorted(kwargs)), stacklevel=3 - ) - return cls(values, marks, id_) + id = ascii_escaped(id) + return cls(values, marks, id) @classmethod def extract_from(cls, parameterset, force_tuple=False): diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index 9f5220766..3266188a1 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -523,7 +523,7 @@ def _is_numpy_array(obj): # builtin pytest.raises helper -def raises(expected_exception, *args, **kwargs): +def raises(expected_exception, *args, match=None, **kwargs): r""" Assert that a code block/function call raises ``expected_exception`` or raise a failure exception otherwise. @@ -649,16 +649,14 @@ def raises(expected_exception, *args, **kwargs): raise TypeError(msg % type(exc)) message = "DID NOT RAISE {}".format(expected_exception) - match_expr = None if not args: - if "match" in kwargs: - match_expr = kwargs.pop("match") if kwargs: msg = "Unexpected keyword arguments passed to pytest.raises: " msg += ", ".join(sorted(kwargs)) + msg += "\nUse context-manager form instead?" raise TypeError(msg) - return RaisesContext(expected_exception, message, match_expr) + return RaisesContext(expected_exception, message, match) else: func = args[0] if not callable(func): diff --git a/src/_pytest/recwarn.py b/src/_pytest/recwarn.py index 8733a893a..8a1cad4a1 100644 --- a/src/_pytest/recwarn.py +++ b/src/_pytest/recwarn.py @@ -3,7 +3,6 @@ import inspect import re import warnings -from _pytest.deprecated import PYTEST_WARNS_UNKNOWN_KWARGS from _pytest.fixtures import yield_fixture from _pytest.outcomes import fail @@ -43,7 +42,7 @@ def deprecated_call(func=None, *args, **kwargs): return warns((DeprecationWarning, PendingDeprecationWarning), *args, **kwargs) -def warns(expected_warning, *args, **kwargs): +def warns(expected_warning, *args, match=None, **kwargs): r"""Assert that code raises a particular class of warning. Specifically, the parameter ``expected_warning`` can be a warning class or @@ -77,12 +76,12 @@ def warns(expected_warning, *args, **kwargs): """ __tracebackhide__ = True if not args: - match_expr = kwargs.pop("match", None) if kwargs: - warnings.warn( - PYTEST_WARNS_UNKNOWN_KWARGS.format(args=sorted(kwargs)), stacklevel=2 - ) - return WarningsChecker(expected_warning, match_expr=match_expr) + msg = "Unexpected keyword arguments passed to pytest.warns: " + msg += ", ".join(sorted(kwargs)) + msg += "\nUse context-manager form instead?" + raise TypeError(msg) + return WarningsChecker(expected_warning, match_expr=match) else: func = args[0] if not callable(func): diff --git a/testing/deprecated_test.py b/testing/deprecated_test.py index 4db4a9c98..b21a08703 100644 --- a/testing/deprecated_test.py +++ b/testing/deprecated_test.py @@ -2,7 +2,6 @@ import os import pytest from _pytest import deprecated -from _pytest.warning_types import PytestDeprecationWarning from _pytest.warnings import SHOW_PYTEST_WARNINGS_ARG pytestmark = pytest.mark.pytester_example_path("deprecated") @@ -199,11 +198,3 @@ def test_fixture_named_request(testdir): "*'request' is a reserved name for fixtures and will raise an error in future versions" ] ) - - -def test_pytest_warns_unknown_kwargs(): - with pytest.warns( - PytestDeprecationWarning, - match=r"pytest.warns\(\) got unexpected keyword arguments: \['foo'\]", - ): - pytest.warns(UserWarning, foo="hello") diff --git a/testing/test_mark.py b/testing/test_mark.py index 1544ffe56..ce5991563 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -8,7 +8,6 @@ from _pytest.mark import EMPTY_PARAMETERSET_OPTION from _pytest.mark import MarkGenerator as Mark from _pytest.nodes import Collector from _pytest.nodes import Node -from _pytest.warning_types import PytestDeprecationWarning from _pytest.warnings import SHOW_PYTEST_WARNINGS_ARG ignore_markinfo = pytest.mark.filterwarnings( @@ -1004,15 +1003,3 @@ def test_pytest_param_id_requires_string(): @pytest.mark.parametrize("s", (None, "hello world")) def test_pytest_param_id_allows_none_or_string(s): assert pytest.param(id=s) - - -def test_pytest_param_warning_on_unknown_kwargs(): - with pytest.warns(PytestDeprecationWarning) as warninfo: - # typo, should be marks= - pytest.param(1, 2, mark=pytest.mark.xfail()) - assert warninfo[0].filename == __file__ - msg, = warninfo[0].message.args - assert msg == ( - "pytest.param() got unexpected keyword arguments: ['mark'].\n" - "This will be an error in future versions." - ) From 647d89c44495a8105c2eec947a0c11fb55bc9e3c Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sun, 30 Jun 2019 12:15:29 -0300 Subject: [PATCH 05/14] Move code about 'pytest_plugins' error to a more appropriate place It is no longer deprecated, but part of the normal code for 'config' --- src/_pytest/config/__init__.py | 24 ++++--- src/_pytest/deprecated.py | 10 --- testing/deprecated_test.py | 118 --------------------------------- testing/test_config.py | 114 +++++++++++++++++++++++++++++++ 4 files changed, 128 insertions(+), 138 deletions(-) diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index c1bd2e7eb..cf6ecd385 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -204,6 +204,19 @@ def _prepareconfig(args=None, plugins=None): raise +def _fail_on_non_top_pytest_plugins(conftestpath, confcutdir): + msg = ( + "Defining 'pytest_plugins' in a non-top-level conftest is no longer supported:\n" + "It affects the entire test suite instead of just below the conftest as expected.\n" + " {}\n" + "Please move it to a top level conftest file at the rootdir:\n" + " {}\n" + "For more information, visit:\n" + " https://docs.pytest.org/en/latest/deprecations.html#pytest-plugins-in-non-top-level-conftest-files" + ) + fail(msg.format(conftestpath, confcutdir), pytrace=False) + + class PytestPluginManager(PluginManager): """ Overwrites :py:class:`pluggy.PluginManager ` to add pytest-specific @@ -424,16 +437,7 @@ class PytestPluginManager(PluginManager): and self._configured and not self._using_pyargs ): - from _pytest.deprecated import ( - PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST, - ) - - fail( - PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST.format( - conftestpath, self._confcutdir - ), - pytrace=False, - ) + _fail_on_non_top_pytest_plugins(conftestpath, self._confcutdir) except Exception: raise ConftestImportFailure(conftestpath, sys.exc_info()) diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index dd2d65d15..2598b2a9c 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -48,16 +48,6 @@ RESULT_LOG = PytestDeprecationWarning( ) -PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST = ( - "Defining 'pytest_plugins' in a non-top-level conftest is no longer supported " - "because it affects the entire directory tree in a non-explicit way.\n" - " {}\n" - "Please move it to a top level conftest file at the rootdir:\n" - " {}\n" - "For more information, visit:\n" - " https://docs.pytest.org/en/latest/deprecations.html#pytest-plugins-in-non-top-level-conftest-files" -) - PYTEST_CONFIG_GLOBAL = PytestDeprecationWarning( "the `pytest.config` global is deprecated. Please use `request.config` " "or `pytest_configure` (if you're a pytest plugin) instead." diff --git a/testing/deprecated_test.py b/testing/deprecated_test.py index b21a08703..1730e740a 100644 --- a/testing/deprecated_test.py +++ b/testing/deprecated_test.py @@ -1,8 +1,5 @@ -import os - import pytest from _pytest import deprecated -from _pytest.warnings import SHOW_PYTEST_WARNINGS_ARG pytestmark = pytest.mark.pytester_example_path("deprecated") @@ -75,121 +72,6 @@ def test_external_plugins_integrated(testdir, plugin): testdir.parseconfig("-p", plugin) -def test_pytest_plugins_in_non_top_level_conftest_deprecated(testdir): - from _pytest.deprecated import PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST - - testdir.makepyfile( - **{ - "subdirectory/conftest.py": """ - pytest_plugins=['capture'] - """ - } - ) - testdir.makepyfile( - """ - def test_func(): - pass - """ - ) - res = testdir.runpytest(SHOW_PYTEST_WARNINGS_ARG) - assert res.ret == 2 - msg = str(PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST).splitlines()[0] - res.stdout.fnmatch_lines( - ["*{msg}*".format(msg=msg), "*subdirectory{sep}conftest.py*".format(sep=os.sep)] - ) - - -@pytest.mark.parametrize("use_pyargs", [True, False]) -def test_pytest_plugins_in_non_top_level_conftest_unsupported_pyargs( - testdir, use_pyargs -): - """When using --pyargs, do not emit the warning about non-top-level conftest warnings (#4039, #4044)""" - from _pytest.deprecated import PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST - - files = { - "src/pkg/__init__.py": "", - "src/pkg/conftest.py": "", - "src/pkg/test_root.py": "def test(): pass", - "src/pkg/sub/__init__.py": "", - "src/pkg/sub/conftest.py": "pytest_plugins=['capture']", - "src/pkg/sub/test_bar.py": "def test(): pass", - } - testdir.makepyfile(**files) - testdir.syspathinsert(testdir.tmpdir.join("src")) - - args = ("--pyargs", "pkg") if use_pyargs else () - args += (SHOW_PYTEST_WARNINGS_ARG,) - res = testdir.runpytest(*args) - assert res.ret == (0 if use_pyargs else 2) - msg = str(PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST).splitlines()[0] - if use_pyargs: - assert msg not in res.stdout.str() - else: - res.stdout.fnmatch_lines(["*{msg}*".format(msg=msg)]) - - -def test_pytest_plugins_in_non_top_level_conftest_unsupported_no_top_level_conftest( - testdir -): - from _pytest.deprecated import PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST - - subdirectory = testdir.tmpdir.join("subdirectory") - subdirectory.mkdir() - testdir.makeconftest( - """ - pytest_plugins=['capture'] - """ - ) - testdir.tmpdir.join("conftest.py").move(subdirectory.join("conftest.py")) - - testdir.makepyfile( - """ - def test_func(): - pass - """ - ) - - res = testdir.runpytest_subprocess() - assert res.ret == 2 - msg = str(PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST).splitlines()[0] - res.stdout.fnmatch_lines( - ["*{msg}*".format(msg=msg), "*subdirectory{sep}conftest.py*".format(sep=os.sep)] - ) - - -def test_pytest_plugins_in_non_top_level_conftest_unsupported_no_false_positives( - testdir -): - from _pytest.deprecated import PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST - - subdirectory = testdir.tmpdir.join("subdirectory") - subdirectory.mkdir() - testdir.makeconftest( - """ - pass - """ - ) - testdir.tmpdir.join("conftest.py").move(subdirectory.join("conftest.py")) - - testdir.makeconftest( - """ - import warnings - warnings.filterwarnings('always', category=DeprecationWarning) - pytest_plugins=['capture'] - """ - ) - testdir.makepyfile( - """ - def test_func(): - pass - """ - ) - res = testdir.runpytest_subprocess() - assert res.ret == 0 - msg = str(PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST).splitlines()[0] - assert msg not in res.stdout.str() - - def test_fixture_named_request(testdir): testdir.copy_example() result = testdir.runpytest() diff --git a/testing/test_config.py b/testing/test_config.py index ff993e401..76202ca33 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -1,3 +1,4 @@ +import os import sys import textwrap @@ -1242,3 +1243,116 @@ def test_config_blocked_default_plugins(testdir, plugin): result.stdout.fnmatch_lines(["* 1 failed in *"]) else: assert result.stdout.lines == [""] + + +class TestPytestPluginsVariable: + def test_pytest_plugins_in_non_top_level_conftest_unsupported(self, testdir): + testdir.makepyfile( + **{ + "subdirectory/conftest.py": """ + pytest_plugins=['capture'] + """ + } + ) + testdir.makepyfile( + """ + def test_func(): + pass + """ + ) + res = testdir.runpytest() + assert res.ret == 2 + msg = "Defining 'pytest_plugins' in a non-top-level conftest is no longer supported" + res.stdout.fnmatch_lines( + [ + "*{msg}*".format(msg=msg), + "*subdirectory{sep}conftest.py*".format(sep=os.sep), + ] + ) + + @pytest.mark.parametrize("use_pyargs", [True, False]) + def test_pytest_plugins_in_non_top_level_conftest_unsupported_pyargs( + self, testdir, use_pyargs + ): + """When using --pyargs, do not emit the warning about non-top-level conftest warnings (#4039, #4044)""" + + files = { + "src/pkg/__init__.py": "", + "src/pkg/conftest.py": "", + "src/pkg/test_root.py": "def test(): pass", + "src/pkg/sub/__init__.py": "", + "src/pkg/sub/conftest.py": "pytest_plugins=['capture']", + "src/pkg/sub/test_bar.py": "def test(): pass", + } + testdir.makepyfile(**files) + testdir.syspathinsert(testdir.tmpdir.join("src")) + + args = ("--pyargs", "pkg") if use_pyargs else () + res = testdir.runpytest(*args) + assert res.ret == (0 if use_pyargs else 2) + msg = ( + msg + ) = "Defining 'pytest_plugins' in a non-top-level conftest is no longer supported" + if use_pyargs: + assert msg not in res.stdout.str() + else: + res.stdout.fnmatch_lines(["*{msg}*".format(msg=msg)]) + + def test_pytest_plugins_in_non_top_level_conftest_unsupported_no_top_level_conftest( + self, testdir + ): + subdirectory = testdir.tmpdir.join("subdirectory") + subdirectory.mkdir() + testdir.makeconftest( + """ + pytest_plugins=['capture'] + """ + ) + testdir.tmpdir.join("conftest.py").move(subdirectory.join("conftest.py")) + + testdir.makepyfile( + """ + def test_func(): + pass + """ + ) + + res = testdir.runpytest_subprocess() + assert res.ret == 2 + msg = "Defining 'pytest_plugins' in a non-top-level conftest is no longer supported" + res.stdout.fnmatch_lines( + [ + "*{msg}*".format(msg=msg), + "*subdirectory{sep}conftest.py*".format(sep=os.sep), + ] + ) + + def test_pytest_plugins_in_non_top_level_conftest_unsupported_no_false_positives( + self, testdir + ): + subdirectory = testdir.tmpdir.join("subdirectory") + subdirectory.mkdir() + testdir.makeconftest( + """ + pass + """ + ) + testdir.tmpdir.join("conftest.py").move(subdirectory.join("conftest.py")) + + testdir.makeconftest( + """ + import warnings + warnings.filterwarnings('always', category=DeprecationWarning) + pytest_plugins=['capture'] + """ + ) + testdir.makepyfile( + """ + def test_func(): + pass + """ + ) + res = testdir.runpytest_subprocess() + assert res.ret == 0 + msg = "Defining 'pytest_plugins' in a non-top-level conftest is no longer supported" + assert msg not in res.stdout.str() From f2b7809d5d2c04afc3f04f6989e0b327ca753b88 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sun, 30 Jun 2019 12:18:14 -0300 Subject: [PATCH 06/14] Move setup.cfg error message and tests to an appropriate location Those are not really deprecated anymore and are part of the normal code for config --- src/_pytest/config/findpaths.py | 7 +++---- src/_pytest/deprecated.py | 2 -- testing/deprecated_test.py | 24 ------------------------ testing/test_config.py | 24 ++++++++++++++++++++++++ 4 files changed, 27 insertions(+), 30 deletions(-) diff --git a/src/_pytest/config/findpaths.py b/src/_pytest/config/findpaths.py index fa2024470..abb248b1d 100644 --- a/src/_pytest/config/findpaths.py +++ b/src/_pytest/config/findpaths.py @@ -20,8 +20,6 @@ def getcfg(args, config=None): note: config is optional and used only to issue warnings explicitly (#2891). """ - from _pytest.deprecated import CFG_PYTEST_SECTION - inibasenames = ["pytest.ini", "tox.ini", "setup.cfg"] args = [x for x in args if not str(x).startswith("-")] if not args: @@ -97,6 +95,9 @@ def get_dirs_from_args(args): return [get_dir_from_path(path) for path in possible_paths if path.exists()] +CFG_PYTEST_SECTION = "[pytest] section in {filename} files is no longer supported, change to [tool:pytest] instead." + + def determine_setup(inifile, args, rootdir_cmd_arg=None, config=None): dirs = get_dirs_from_args(args) if inifile: @@ -107,8 +108,6 @@ def determine_setup(inifile, args, rootdir_cmd_arg=None, config=None): try: inicfg = iniconfig[section] if is_cfg_file and section == "pytest" and config is not None: - from _pytest.deprecated import CFG_PYTEST_SECTION - fail( CFG_PYTEST_SECTION.format(filename=str(inifile)), pytrace=False ) diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index 2598b2a9c..0cce0efb2 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -33,8 +33,6 @@ FIXTURE_NAMED_REQUEST = PytestDeprecationWarning( "'request' is a reserved name for fixtures and will raise an error in future versions" ) -CFG_PYTEST_SECTION = "[pytest] section in {filename} files is no longer supported, change to [tool:pytest] instead." - FUNCARGNAMES = PytestDeprecationWarning( "The `funcargnames` attribute was an alias for `fixturenames`, " diff --git a/testing/deprecated_test.py b/testing/deprecated_test.py index 1730e740a..e4a8f72bc 100644 --- a/testing/deprecated_test.py +++ b/testing/deprecated_test.py @@ -4,30 +4,6 @@ from _pytest import deprecated pytestmark = pytest.mark.pytester_example_path("deprecated") -def test_pytest_setup_cfg_unsupported(testdir): - testdir.makefile( - ".cfg", - setup=""" - [pytest] - addopts = --verbose - """, - ) - with pytest.raises(pytest.fail.Exception): - testdir.runpytest() - - -def test_pytest_custom_cfg_unsupported(testdir): - testdir.makefile( - ".cfg", - custom=""" - [pytest] - addopts = --verbose - """, - ) - with pytest.raises(pytest.fail.Exception): - testdir.runpytest("-c", "custom.cfg") - - @pytest.mark.filterwarnings("default") def test_resultlog_is_deprecated(testdir): result = testdir.runpytest("--help") diff --git a/testing/test_config.py b/testing/test_config.py index 76202ca33..9533144e7 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -1245,6 +1245,30 @@ def test_config_blocked_default_plugins(testdir, plugin): assert result.stdout.lines == [""] +class TestSetupCfg: + def test_pytest_setup_cfg_unsupported(self, testdir): + testdir.makefile( + ".cfg", + setup=""" + [pytest] + addopts = --verbose + """, + ) + with pytest.raises(pytest.fail.Exception): + testdir.runpytest() + + def test_pytest_custom_cfg_unsupported(self, testdir): + testdir.makefile( + ".cfg", + custom=""" + [pytest] + addopts = --verbose + """, + ) + with pytest.raises(pytest.fail.Exception): + testdir.runpytest("-c", "custom.cfg") + + class TestPytestPluginsVariable: def test_pytest_plugins_in_non_top_level_conftest_unsupported(self, testdir): testdir.makepyfile( From 7e58defc15e92d6f5af2c3bd5367b4ec877c8086 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sun, 30 Jun 2019 12:24:51 -0300 Subject: [PATCH 07/14] Remove 'pytest.config' --- changelog/5180.removal.rst | 2 ++ doc/en/deprecations.rst | 25 ++++++++++++------------- src/_pytest/deprecated.py | 5 ----- src/_pytest/main.py | 22 ---------------------- testing/test_pytester.py | 3 +-- 5 files changed, 15 insertions(+), 42 deletions(-) diff --git a/changelog/5180.removal.rst b/changelog/5180.removal.rst index e72b76d5a..49f896f94 100644 --- a/changelog/5180.removal.rst +++ b/changelog/5180.removal.rst @@ -11,6 +11,8 @@ removed: syntax. This might change the exception message from previous versions, but they still raise ``TypeError`` on unknown keyword arguments as before. +* ``pytest.config`` global variable. + For more information consult `Deprecations and Removals `__ in the docs. diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index c97061f09..052ab836a 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -34,19 +34,6 @@ in places where we or plugin authors must distinguish between fixture names and names supplied by non-fixture things such as ``pytest.mark.parametrize``. - - -``pytest.config`` global -~~~~~~~~~~~~~~~~~~~~~~~~ - -.. deprecated:: 4.1 - -The ``pytest.config`` global object is deprecated. Instead use -``request.config`` (via the ``request`` fixture) or if you are a plugin author -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``) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -63,6 +50,7 @@ stable. The actual alternative is still being discussed in issue `#4488 `__. + Removed Features ---------------- @@ -70,6 +58,17 @@ As stated in our :ref:`backwards-compatibility` policy, deprecated features are an appropriate period of deprecation has passed. +``pytest.config`` global +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. versionremoved:: 5.0 + +The ``pytest.config`` global object is deprecated. Instead use +``request.config`` (via the ``request`` fixture) or if you are a plugin author +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. + + .. _`raises message deprecated`: ``"message"`` parameter of ``pytest.raises`` diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index 0cce0efb2..2c853d48b 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -46,11 +46,6 @@ RESULT_LOG = PytestDeprecationWarning( ) -PYTEST_CONFIG_GLOBAL = PytestDeprecationWarning( - "the `pytest.config` global is deprecated. Please use `request.config` " - "or `pytest_configure` (if you're a pytest plugin) instead." -) - PYTEST_ENSURETEMP = RemovedInPytest4Warning( "pytest/tmpdir_factory.ensuretemp is deprecated, \n" "please use the tmp_path fixture or tmp_path_factory.mktemp" diff --git a/src/_pytest/main.py b/src/_pytest/main.py index f28bc68db..a5283d737 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -5,7 +5,6 @@ import functools import importlib import os import sys -import warnings import attr import py @@ -15,7 +14,6 @@ from _pytest import nodes from _pytest.config import directory_arg from _pytest.config import hookimpl from _pytest.config import UsageError -from _pytest.deprecated import PYTEST_CONFIG_GLOBAL from _pytest.outcomes import exit from _pytest.runner import collect_one_node @@ -179,26 +177,6 @@ def pytest_addoption(parser): ) -class _ConfigDeprecated: - def __init__(self, config): - self.__dict__["_config"] = config - - def __getattr__(self, attr): - warnings.warn(PYTEST_CONFIG_GLOBAL, stacklevel=2) - return getattr(self._config, attr) - - def __setattr__(self, attr, val): - warnings.warn(PYTEST_CONFIG_GLOBAL, stacklevel=2) - return setattr(self._config, attr, val) - - def __repr__(self): - return "{}({!r})".format(type(self).__name__, self._config) - - -def pytest_configure(config): - __import__("pytest").config = _ConfigDeprecated(config) # compatibility - - def wrap_session(config, doit): """Skeleton command line program""" session = Session(config) diff --git a/testing/test_pytester.py b/testing/test_pytester.py index 37b63f31a..f115ad3d0 100644 --- a/testing/test_pytester.py +++ b/testing/test_pytester.py @@ -72,8 +72,7 @@ def test_make_hook_recorder(testdir): def test_parseconfig(testdir): config1 = testdir.parseconfig() config2 = testdir.parseconfig() - assert config2 != config1 - assert config1 != pytest.config + assert config2 is not config1 def test_testdir_runs_with_plugin(testdir): From aa1955de72dbca1a907d4692487f707da14b5d96 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sun, 30 Jun 2019 12:27:29 -0300 Subject: [PATCH 08/14] Remove 'tmpdir_factory.ensuretemp' --- changelog/5180.removal.rst | 2 ++ src/_pytest/deprecated.py | 6 ------ src/_pytest/tmpdir.py | 15 --------------- testing/test_tmpdir.py | 16 ++++++---------- 4 files changed, 8 insertions(+), 31 deletions(-) diff --git a/changelog/5180.removal.rst b/changelog/5180.removal.rst index 49f896f94..dfe9c5f8d 100644 --- a/changelog/5180.removal.rst +++ b/changelog/5180.removal.rst @@ -13,6 +13,8 @@ removed: * ``pytest.config`` global variable. +* ``tmpdir_factory.ensuretemp`` method. + For more information consult `Deprecations and Removals `__ in the docs. diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index 2c853d48b..58ffee1ae 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -9,7 +9,6 @@ All constants defined in this module should be either PytestWarning instances or in case of warnings which need to format their messages. """ from _pytest.warning_types import PytestDeprecationWarning -from _pytest.warning_types import RemovedInPytest4Warning YIELD_TESTS = "yield tests were removed in pytest 4.0 - {name} will be ignored" @@ -46,11 +45,6 @@ RESULT_LOG = PytestDeprecationWarning( ) -PYTEST_ENSURETEMP = RemovedInPytest4Warning( - "pytest/tmpdir_factory.ensuretemp is deprecated, \n" - "please use the tmp_path fixture or tmp_path_factory.mktemp" -) - PYTEST_LOGWARNING = PytestDeprecationWarning( "pytest_logwarning is deprecated, no longer being called, and will be removed soon\n" "please use pytest_warning_captured instead" diff --git a/src/_pytest/tmpdir.py b/src/_pytest/tmpdir.py index f2c4d905c..c7a61b0d4 100644 --- a/src/_pytest/tmpdir.py +++ b/src/_pytest/tmpdir.py @@ -2,7 +2,6 @@ import os import re import tempfile -import warnings import attr import py @@ -85,19 +84,6 @@ class TempdirFactory: _tmppath_factory = attr.ib() - def ensuretemp(self, string, dir=1): - """ (deprecated) return temporary directory path with - the given string as the trailing part. It is usually - better to use the 'tmpdir' function argument which - provides an empty unique-per-test-invocation directory - and is guaranteed to be empty. - """ - # py.log._apiwarn(">1.1", "use tmpdir function argument") - from .deprecated import PYTEST_ENSURETEMP - - warnings.warn(PYTEST_ENSURETEMP, stacklevel=2) - return self.getbasetemp().ensure(string, dir=dir) - def mktemp(self, basename, numbered=True): """Create a subdirectory of the base temporary directory and return it. If ``numbered``, ensure the directory is unique by adding a number @@ -135,7 +121,6 @@ def pytest_configure(config): config._cleanup.append(mp.undo) mp.setattr(config, "_tmp_path_factory", tmppath_handler, raising=False) mp.setattr(config, "_tmpdirhandler", t, raising=False) - mp.setattr(pytest, "ensuretemp", t.ensuretemp, raising=False) @pytest.fixture(scope="session") diff --git a/testing/test_tmpdir.py b/testing/test_tmpdir.py index c4c7ebe25..f11ddbe68 100644 --- a/testing/test_tmpdir.py +++ b/testing/test_tmpdir.py @@ -14,13 +14,6 @@ def test_tmpdir_fixture(testdir): results.stdout.fnmatch_lines(["*1 passed*"]) -def test_ensuretemp(recwarn): - d1 = pytest.ensuretemp("hello") - d2 = pytest.ensuretemp("hello") - assert d1 == d2 - assert d1.check(dir=1) - - @attr.s class FakeConfig: basetemp = attr.ib() @@ -85,12 +78,15 @@ def test_basetemp(testdir): p = testdir.makepyfile( """ import pytest - def test_1(): - pytest.ensuretemp("hello") + def test_1(tmpdir_factory): + tmpdir_factory.mktemp('hello', numbered=False) """ ) - result = testdir.runpytest(p, "--basetemp=%s" % mytemp, SHOW_PYTEST_WARNINGS_ARG) + result = testdir.runpytest( + p, "--basetemp=%s" % mytemp, SHOW_PYTEST_WARNINGS_ARG, "-s" + ) assert result.ret == 0 + print(mytemp) assert mytemp.join("hello").check() From 7b354050335caaf8316cf5cf17d45af64649dbb1 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sun, 30 Jun 2019 12:28:40 -0300 Subject: [PATCH 09/14] Remove YIELD_TESTS from deprecated module as it is now an error --- src/_pytest/deprecated.py | 2 -- src/_pytest/python.py | 5 +++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index 58ffee1ae..fff7af2f9 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -10,8 +10,6 @@ in case of warnings which need to format their messages. """ from _pytest.warning_types import PytestDeprecationWarning -YIELD_TESTS = "yield tests were removed in pytest 4.0 - {name} will be ignored" - # set of plugins which have been integrated into the core; we use this list to ignore # them during registration to avoid conflicts DEPRECATED_EXTERNAL_PLUGINS = { diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 66d853060..801a16a5c 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -12,7 +12,6 @@ from textwrap import dedent import py import _pytest -from _pytest import deprecated from _pytest import fixtures from _pytest import nodes from _pytest._code import filter_traceback @@ -218,7 +217,9 @@ def pytest_pycollect_makeitem(collector, name, obj): elif getattr(obj, "__test__", True): if is_generator(obj): res = Function(name, parent=collector) - reason = deprecated.YIELD_TESTS.format(name=name) + reason = "yield tests were removed in pytest 4.0 - {name} will be ignored".format( + name=name + ) res.add_marker(MARK_GEN.xfail(run=False, reason=reason)) res.warn(PytestCollectionWarning(reason)) else: From 3a17c1b30bc59320f1601c0a54a039098a021c88 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sun, 30 Jun 2019 12:30:08 -0300 Subject: [PATCH 10/14] Remove 'pytest_logwarning' hook --- changelog/5180.removal.rst | 2 ++ src/_pytest/deprecated.py | 6 ------ src/_pytest/hookspec.py | 22 ---------------------- 3 files changed, 2 insertions(+), 28 deletions(-) diff --git a/changelog/5180.removal.rst b/changelog/5180.removal.rst index dfe9c5f8d..c78bbc876 100644 --- a/changelog/5180.removal.rst +++ b/changelog/5180.removal.rst @@ -15,6 +15,8 @@ removed: * ``tmpdir_factory.ensuretemp`` method. +* ``pytest_logwarning`` hook. + For more information consult `Deprecations and Removals `__ in the docs. diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index fff7af2f9..3d3f73a60 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -41,9 +41,3 @@ RESULT_LOG = PytestDeprecationWarning( "--result-log is deprecated and scheduled for removal in pytest 6.0.\n" "See https://docs.pytest.org/en/latest/deprecations.html#result-log-result-log for more information." ) - - -PYTEST_LOGWARNING = PytestDeprecationWarning( - "pytest_logwarning is deprecated, no longer being called, and will be removed soon\n" - "please use pytest_warning_captured instead" -) diff --git a/src/_pytest/hookspec.py b/src/_pytest/hookspec.py index 9e6d13fab..59fc569f4 100644 --- a/src/_pytest/hookspec.py +++ b/src/_pytest/hookspec.py @@ -1,7 +1,6 @@ """ hook specifications for pytest plugins, invoked from main.py and builtin plugins. """ from pluggy import HookspecMarker -from _pytest.deprecated import PYTEST_LOGWARNING hookspec = HookspecMarker("pytest") @@ -575,27 +574,6 @@ def pytest_terminal_summary(terminalreporter, exitstatus, config): """ -@hookspec(historic=True, warn_on_impl=PYTEST_LOGWARNING) -def pytest_logwarning(message, code, nodeid, fslocation): - """ - .. deprecated:: 3.8 - - This hook is will stop working in a future release. - - pytest no longer triggers this hook, but the - terminal writer still implements it to display warnings issued by - :meth:`_pytest.config.Config.warn` and :meth:`_pytest.nodes.Node.warn`. Calling those functions will be - an error in future releases. - - process a warning specified by a message, a code string, - a nodeid and fslocation (both of which may be None - if the warning is not tied to a particular node/location). - - .. note:: - This hook is incompatible with ``hookwrapper=True``. - """ - - @hookspec(historic=True) def pytest_warning_captured(warning_message, when, item): """ From c470ade0a5a6ac8bfa23ec66a0f72c2e171b3db5 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sun, 30 Jun 2019 12:56:27 -0300 Subject: [PATCH 11/14] Remove 'RemovedInPytest4Warning' --- changelog/5180.removal.rst | 2 ++ doc/en/warnings.rst | 2 -- src/_pytest/warning_types.py | 10 --------- src/_pytest/warnings.py | 5 ----- src/pytest.py | 3 +-- testing/acceptance_test.py | 7 ++----- testing/python/fixtures.py | 3 +-- testing/python/metafunc.py | 27 ++++++++++++------------- testing/test_mark.py | 12 +++-------- testing/test_tmpdir.py | 5 +---- testing/test_warnings.py | 39 ++++++++---------------------------- tox.ini | 3 --- 12 files changed, 31 insertions(+), 87 deletions(-) diff --git a/changelog/5180.removal.rst b/changelog/5180.removal.rst index c78bbc876..112c0150f 100644 --- a/changelog/5180.removal.rst +++ b/changelog/5180.removal.rst @@ -17,6 +17,8 @@ removed: * ``pytest_logwarning`` hook. +* ``RemovedInPytest4Warning`` warning type. + For more information consult `Deprecations and Removals `__ in the docs. diff --git a/doc/en/warnings.rst b/doc/en/warnings.rst index 8f8d6f3e1..c16aefc21 100644 --- a/doc/en/warnings.rst +++ b/doc/en/warnings.rst @@ -430,5 +430,3 @@ The following warning types ares used by pytest and are part of the public API: .. autoclass:: pytest.PytestUnhandledCoroutineWarning .. autoclass:: pytest.PytestUnknownMarkWarning - -.. autoclass:: pytest.RemovedInPytest4Warning diff --git a/src/_pytest/warning_types.py b/src/_pytest/warning_types.py index ac7e5ca48..80353ccbc 100644 --- a/src/_pytest/warning_types.py +++ b/src/_pytest/warning_types.py @@ -103,16 +103,6 @@ class PytestUnknownMarkWarning(PytestWarning): __module__ = "pytest" -class RemovedInPytest4Warning(PytestDeprecationWarning): - """ - Bases: :class:`pytest.PytestDeprecationWarning`. - - Warning class for features scheduled to be removed in pytest 4.0. - """ - - __module__ = "pytest" - - @attr.s class UnformattedWarning: """Used to hold warnings that need to format their message at runtime, as opposed to a direct message. diff --git a/src/_pytest/warnings.py b/src/_pytest/warnings.py index f47eee0d4..63d22477c 100644 --- a/src/_pytest/warnings.py +++ b/src/_pytest/warnings.py @@ -4,8 +4,6 @@ from contextlib import contextmanager import pytest -SHOW_PYTEST_WARNINGS_ARG = "-Walways::pytest.RemovedInPytest4Warning" - def _setoption(wmod, arg): """ @@ -74,9 +72,6 @@ def catch_warnings_for_item(config, ihook, when, item): warnings.filterwarnings("always", category=DeprecationWarning) warnings.filterwarnings("always", category=PendingDeprecationWarning) - warnings.filterwarnings("error", category=pytest.RemovedInPytest4Warning) - warnings.filterwarnings("error", category=pytest.PytestDeprecationWarning) - # filters should have this precedence: mark, cmdline options, ini # filters should be applied in the inverse order of precedence for arg in inifilters: diff --git a/src/pytest.py b/src/pytest.py index b4faf4978..b934e65cb 100644 --- a/src/pytest.py +++ b/src/pytest.py @@ -44,7 +44,7 @@ from _pytest.warning_types import PytestExperimentalApiWarning from _pytest.warning_types import PytestUnhandledCoroutineWarning from _pytest.warning_types import PytestUnknownMarkWarning from _pytest.warning_types import PytestWarning -from _pytest.warning_types import RemovedInPytest4Warning + set_trace = __pytestPDB.set_trace @@ -84,7 +84,6 @@ __all__ = [ "PytestWarning", "raises", "register_assert_rewrite", - "RemovedInPytest4Warning", "Session", "set_trace", "skip", diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index d2a348f40..803856ff8 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -9,7 +9,6 @@ import py import pytest from _pytest.main import ExitCode -from _pytest.warnings import SHOW_PYTEST_WARNINGS_ARG def prepend_pythonpath(*dirs): @@ -343,7 +342,7 @@ class TestGeneralUsage: """ ) p = testdir.makepyfile("""def test_func(x): pass""") - res = testdir.runpytest(p, SHOW_PYTEST_WARNINGS_ARG) + res = testdir.runpytest(p) assert res.ret == 0 res.stdout.fnmatch_lines(["*1 skipped*"]) @@ -356,9 +355,7 @@ class TestGeneralUsage: pass """ ) - res = testdir.runpytest( - p.basename + "::" + "test_func[1]", SHOW_PYTEST_WARNINGS_ARG - ) + res = testdir.runpytest(p.basename + "::" + "test_func[1]") assert res.ret == 0 res.stdout.fnmatch_lines(["*1 passed*"]) diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index 63fb6294d..c2484a8a2 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -7,7 +7,6 @@ from _pytest.fixtures import FixtureLookupError from _pytest.fixtures import FixtureRequest from _pytest.pathlib import Path from _pytest.pytester import get_public_names -from _pytest.warnings import SHOW_PYTEST_WARNINGS_ARG def test_getfuncargnames(): @@ -2186,7 +2185,7 @@ class TestFixtureMarker: pass """ ) - result = testdir.runpytest(SHOW_PYTEST_WARNINGS_ARG) + result = testdir.runpytest() assert result.ret != 0 result.stdout.fnmatch_lines( ["*ScopeMismatch*You tried*function*session*request*"] diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index 542557252..9173105df 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -9,7 +9,6 @@ from hypothesis import strategies import pytest from _pytest import fixtures from _pytest import python -from _pytest.warnings import SHOW_PYTEST_WARNINGS_ARG class TestMetafunc: @@ -915,7 +914,7 @@ class TestMetafuncFunctional: assert metafunc.cls == TestClass """ ) - result = testdir.runpytest(p, "-v", SHOW_PYTEST_WARNINGS_ARG) + result = testdir.runpytest(p, "-v") result.assert_outcomes(passed=2) def test_two_functions(self, testdir): @@ -931,7 +930,7 @@ class TestMetafuncFunctional: assert arg1 in (10, 20) """ ) - result = testdir.runpytest("-v", p, SHOW_PYTEST_WARNINGS_ARG) + result = testdir.runpytest("-v", p) result.stdout.fnmatch_lines( [ "*test_func1*0*PASS*", @@ -967,7 +966,7 @@ class TestMetafuncFunctional: assert hello == "world" """ ) - result = testdir.runpytest("-v", p, SHOW_PYTEST_WARNINGS_ARG) + result = testdir.runpytest("-v", p) result.stdout.fnmatch_lines(["*test_myfunc*hello*PASS*", "*1 passed*"]) def test_two_functions_not_same_instance(self, testdir): @@ -982,7 +981,7 @@ class TestMetafuncFunctional: self.x = 1 """ ) - result = testdir.runpytest("-v", p, SHOW_PYTEST_WARNINGS_ARG) + result = testdir.runpytest("-v", p) result.stdout.fnmatch_lines( ["*test_func*0*PASS*", "*test_func*1*PASS*", "*2 pass*"] ) @@ -1000,7 +999,7 @@ class TestMetafuncFunctional: self.val = 1 """ ) - result = testdir.runpytest(p, SHOW_PYTEST_WARNINGS_ARG) + result = testdir.runpytest(p) result.assert_outcomes(passed=1) def test_parametrize_functional2(self, testdir): @@ -1522,7 +1521,7 @@ class TestMarkersWithParametrization: assert n + 1 == expected """ testdir.makepyfile(s) - rec = testdir.inline_run("-m", "foo", SHOW_PYTEST_WARNINGS_ARG) + rec = testdir.inline_run("-m", "foo") passed, skipped, fail = rec.listoutcomes() assert len(passed) == 1 assert len(skipped) == 0 @@ -1562,7 +1561,7 @@ class TestMarkersWithParametrization: assert n + 1 == expected """ testdir.makepyfile(s) - reprec = testdir.inline_run(SHOW_PYTEST_WARNINGS_ARG) + reprec = testdir.inline_run() # xfail is skip?? reprec.assertoutcome(passed=2, skipped=1) @@ -1579,7 +1578,7 @@ class TestMarkersWithParametrization: assert n % 2 == 0 """ testdir.makepyfile(s) - reprec = testdir.inline_run(SHOW_PYTEST_WARNINGS_ARG) + reprec = testdir.inline_run() reprec.assertoutcome(passed=2, skipped=1) def test_xfail_with_arg(self, testdir): @@ -1595,7 +1594,7 @@ class TestMarkersWithParametrization: assert n + 1 == expected """ testdir.makepyfile(s) - reprec = testdir.inline_run(SHOW_PYTEST_WARNINGS_ARG) + reprec = testdir.inline_run() reprec.assertoutcome(passed=2, skipped=1) def test_xfail_with_kwarg(self, testdir): @@ -1611,7 +1610,7 @@ class TestMarkersWithParametrization: assert n + 1 == expected """ testdir.makepyfile(s) - reprec = testdir.inline_run(SHOW_PYTEST_WARNINGS_ARG) + reprec = testdir.inline_run() reprec.assertoutcome(passed=2, skipped=1) def test_xfail_with_arg_and_kwarg(self, testdir): @@ -1627,7 +1626,7 @@ class TestMarkersWithParametrization: assert n + 1 == expected """ testdir.makepyfile(s) - reprec = testdir.inline_run(SHOW_PYTEST_WARNINGS_ARG) + reprec = testdir.inline_run() reprec.assertoutcome(passed=2, skipped=1) @pytest.mark.parametrize("strict", [True, False]) @@ -1648,7 +1647,7 @@ class TestMarkersWithParametrization: strict=strict ) testdir.makepyfile(s) - reprec = testdir.inline_run(SHOW_PYTEST_WARNINGS_ARG) + reprec = testdir.inline_run() passed, failed = (2, 1) if strict else (3, 0) reprec.assertoutcome(passed=passed, failed=failed) @@ -1672,7 +1671,7 @@ class TestMarkersWithParametrization: assert n + 1 == expected """ testdir.makepyfile(s) - reprec = testdir.inline_run(SHOW_PYTEST_WARNINGS_ARG) + reprec = testdir.inline_run() reprec.assertoutcome(passed=2, skipped=2) def test_parametrize_ID_generation_string_int_works(self, testdir): diff --git a/testing/test_mark.py b/testing/test_mark.py index ce5991563..8747d1c6b 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -8,11 +8,6 @@ from _pytest.mark import EMPTY_PARAMETERSET_OPTION from _pytest.mark import MarkGenerator as Mark from _pytest.nodes import Collector from _pytest.nodes import Node -from _pytest.warnings import SHOW_PYTEST_WARNINGS_ARG - -ignore_markinfo = pytest.mark.filterwarnings( - "ignore:MarkInfo objects:pytest.RemovedInPytest4Warning" -) class TestMark: @@ -625,7 +620,6 @@ class TestFunctional: reprec = testdir.inline_run() reprec.assertoutcome(passed=1) - @ignore_markinfo def test_keyword_added_for_session(self, testdir): testdir.makeconftest( """ @@ -651,7 +645,7 @@ class TestFunctional: assert marker.kwargs == {} """ ) - reprec = testdir.inline_run("-m", "mark1", SHOW_PYTEST_WARNINGS_ARG) + reprec = testdir.inline_run("-m", "mark1") reprec.assertoutcome(passed=1) def assert_markers(self, items, **expected): @@ -689,7 +683,7 @@ class TestFunctional: assert True """ ) - reprec = testdir.inline_run(SHOW_PYTEST_WARNINGS_ARG) + reprec = testdir.inline_run() reprec.assertoutcome(skipped=1) @@ -989,7 +983,7 @@ def test_markers_from_parametrize(testdir): """ ) - result = testdir.runpytest(SHOW_PYTEST_WARNINGS_ARG) + result = testdir.runpytest() result.assert_outcomes(passed=4) diff --git a/testing/test_tmpdir.py b/testing/test_tmpdir.py index f11ddbe68..11556594b 100644 --- a/testing/test_tmpdir.py +++ b/testing/test_tmpdir.py @@ -5,7 +5,6 @@ import attr import pytest from _pytest import pathlib from _pytest.pathlib import Path -from _pytest.warnings import SHOW_PYTEST_WARNINGS_ARG def test_tmpdir_fixture(testdir): @@ -82,9 +81,7 @@ def test_basetemp(testdir): tmpdir_factory.mktemp('hello', numbered=False) """ ) - result = testdir.runpytest( - p, "--basetemp=%s" % mytemp, SHOW_PYTEST_WARNINGS_ARG, "-s" - ) + result = testdir.runpytest(p, "--basetemp=%s" % mytemp) assert result.ret == 0 print(mytemp) assert mytemp.join("hello").check() diff --git a/testing/test_warnings.py b/testing/test_warnings.py index 2ce83ae88..4d58c99e3 100644 --- a/testing/test_warnings.py +++ b/testing/test_warnings.py @@ -498,38 +498,15 @@ class TestDeprecationWarningsByDefault: @pytest.mark.parametrize("change_default", [None, "ini", "cmdline"]) -def test_removed_in_pytest4_warning_as_error(testdir, change_default): - testdir.makepyfile( - """ - import warnings, pytest - def test(): - warnings.warn(pytest.RemovedInPytest4Warning("some warning")) - """ - ) - if change_default == "ini": - testdir.makeini( - """ - [pytest] - filterwarnings = - ignore::pytest.RemovedInPytest4Warning - """ - ) - - args = ( - ("-Wignore::pytest.RemovedInPytest4Warning",) - if change_default == "cmdline" - else () - ) - result = testdir.runpytest(*args) - if change_default is None: - result.stdout.fnmatch_lines(["* 1 failed in *"]) - else: - assert change_default in ("ini", "cmdline") - result.stdout.fnmatch_lines(["* 1 passed in *"]) - - -@pytest.mark.parametrize("change_default", [None, "ini", "cmdline"]) +@pytest.mark.skip( + reason="This test should be enabled again before pytest 6.0 is released" +) def test_deprecation_warning_as_error(testdir, change_default): + """This ensures that PytestDeprecationWarnings raised by pytest are turned into errors. + + This test should be enabled as part of each major release, and skipped again afterwards + to ensure our deprecations are turning into warnings as expected. + """ testdir.makepyfile( """ import warnings, pytest diff --git a/tox.ini b/tox.ini index 9757b0983..832b97298 100644 --- a/tox.ini +++ b/tox.ini @@ -128,9 +128,6 @@ norecursedirs = testing/example_scripts xfail_strict=true filterwarnings = error - ignore:yield tests are deprecated, and scheduled to be removed in pytest 4.0:pytest.RemovedInPytest4Warning - ignore:Metafunc.addcall is deprecated and scheduled to be removed in pytest 4.0:pytest.RemovedInPytest4Warning - ignore::pytest.RemovedInPytest4Warning ignore:Module already imported so cannot be rewritten:pytest.PytestWarning # produced by path.local ignore:bad escape.*:DeprecationWarning:re From 85cc12e3284e2ad6126fd9c7adb69fcaf1b9be3b Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sun, 30 Jun 2019 12:58:50 -0300 Subject: [PATCH 12/14] Move FIXTURE_FUNCTION_CALL constant to the point of error This is no longer a deprecation so it makes sense to move it to the place where it is needed instead of leaving it in deprecated.py --- src/_pytest/deprecated.py | 7 ------- src/_pytest/fixtures.py | 10 ++++++---- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index 3d3f73a60..39c8c23cd 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -19,13 +19,6 @@ DEPRECATED_EXTERNAL_PLUGINS = { } -FIXTURE_FUNCTION_CALL = ( - 'Fixture "{name}" called directly. Fixtures are not meant to be called directly,\n' - "but are created automatically when test functions request them as parameters.\n" - "See https://docs.pytest.org/en/latest/fixture.html for more information about fixtures, and\n" - "https://docs.pytest.org/en/latest/deprecations.html#calling-fixtures-directly about how to update your code." -) - FIXTURE_NAMED_REQUEST = PytestDeprecationWarning( "'request' is a reserved name for fixtures and will raise an error in future versions" ) diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 2cde5725c..9a385fc42 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -26,7 +26,6 @@ from _pytest.compat import getlocation from _pytest.compat import is_generator from _pytest.compat import NOTSET from _pytest.compat import safe_getattr -from _pytest.deprecated import FIXTURE_FUNCTION_CALL from _pytest.deprecated import FIXTURE_NAMED_REQUEST from _pytest.outcomes import fail from _pytest.outcomes import TEST_OUTCOME @@ -933,9 +932,12 @@ def wrap_function_to_error_out_if_called_directly(function, fixture_marker): """Wrap the given fixture function so we can raise an error about it being called directly, instead of used as an argument in a test function. """ - message = FIXTURE_FUNCTION_CALL.format( - name=fixture_marker.name or function.__name__ - ) + message = ( + 'Fixture "{name}" called directly. Fixtures are not meant to be called directly,\n' + "but are created automatically when test functions request them as parameters.\n" + "See https://docs.pytest.org/en/latest/fixture.html for more information about fixtures, and\n" + "https://docs.pytest.org/en/latest/deprecations.html#calling-fixtures-directly about how to update your code." + ).format(name=fixture_marker.name or function.__name__) @functools.wraps(function) def result(*args, **kwargs): From 0ed7aa2db678fb8c35a617525efd48c3352fc256 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sun, 30 Jun 2019 13:14:38 -0300 Subject: [PATCH 13/14] Make 'request' a reserved name for fixtures --- changelog/5180.removal.rst | 2 ++ src/_pytest/compat.py | 4 +-- src/_pytest/config/__init__.py | 1 + src/_pytest/deprecated.py | 5 ---- src/_pytest/fixtures.py | 10 ++++--- testing/deprecated_test.py | 12 --------- .../test_fixture_named_request.py | 0 testing/python/fixtures.py | 26 ++++++++----------- 8 files changed, 23 insertions(+), 37 deletions(-) rename testing/example_scripts/{deprecated => fixtures}/test_fixture_named_request.py (100%) diff --git a/changelog/5180.removal.rst b/changelog/5180.removal.rst index 112c0150f..1174a7cba 100644 --- a/changelog/5180.removal.rst +++ b/changelog/5180.removal.rst @@ -19,6 +19,8 @@ removed: * ``RemovedInPytest4Warning`` warning type. +* ``request`` is now a reserved name for fixtures. + For more information consult `Deprecations and Removals `__ in the docs. diff --git a/src/_pytest/compat.py b/src/_pytest/compat.py index d238061b4..6b750e16f 100644 --- a/src/_pytest/compat.py +++ b/src/_pytest/compat.py @@ -50,11 +50,11 @@ def iscoroutinefunction(func): ) -def getlocation(function, curdir): +def getlocation(function, curdir=None): function = get_real_func(function) fn = py.path.local(inspect.getfile(function)) lineno = function.__code__.co_firstlineno - if fn.relto(curdir): + if curdir is not None and fn.relto(curdir): fn = fn.relto(curdir) return "%s:%d" % (fn, lineno + 1) diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index cf6ecd385..d7a20ad50 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -18,6 +18,7 @@ from pluggy import PluginManager import _pytest._code import _pytest.assertion +import _pytest.deprecated import _pytest.hookspec # the extension point definitions from .exceptions import PrintHelp from .exceptions import UsageError diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index 39c8c23cd..c06908932 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -19,11 +19,6 @@ DEPRECATED_EXTERNAL_PLUGINS = { } -FIXTURE_NAMED_REQUEST = PytestDeprecationWarning( - "'request' is a reserved name for fixtures and will raise an error in future versions" -) - - FUNCARGNAMES = PytestDeprecationWarning( "The `funcargnames` attribute was an alias for `fixturenames`, " "since pytest 2.3 - use the newer attribute instead." diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 9a385fc42..46ef8c642 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -2,7 +2,6 @@ import functools import inspect import itertools import sys -import warnings from collections import defaultdict from collections import deque from collections import OrderedDict @@ -26,7 +25,6 @@ from _pytest.compat import getlocation from _pytest.compat import is_generator from _pytest.compat import NOTSET from _pytest.compat import safe_getattr -from _pytest.deprecated import FIXTURE_NAMED_REQUEST from _pytest.outcomes import fail from _pytest.outcomes import TEST_OUTCOME @@ -971,7 +969,13 @@ class FixtureFunctionMarker: name = self.name or function.__name__ if name == "request": - warnings.warn(FIXTURE_NAMED_REQUEST) + location = getlocation(function) + fail( + "'request' is a reserved word for fixtures, use another name:\n {}".format( + location + ), + pytrace=False, + ) function._pytestfixturefunction = self return function diff --git a/testing/deprecated_test.py b/testing/deprecated_test.py index e4a8f72bc..97b88e939 100644 --- a/testing/deprecated_test.py +++ b/testing/deprecated_test.py @@ -1,8 +1,6 @@ import pytest from _pytest import deprecated -pytestmark = pytest.mark.pytester_example_path("deprecated") - @pytest.mark.filterwarnings("default") def test_resultlog_is_deprecated(testdir): @@ -46,13 +44,3 @@ def test_external_plugins_integrated(testdir, plugin): with pytest.warns(pytest.PytestConfigWarning): testdir.parseconfig("-p", plugin) - - -def test_fixture_named_request(testdir): - testdir.copy_example() - result = testdir.runpytest() - result.stdout.fnmatch_lines( - [ - "*'request' is a reserved name for fixtures and will raise an error in future versions" - ] - ) diff --git a/testing/example_scripts/deprecated/test_fixture_named_request.py b/testing/example_scripts/fixtures/test_fixture_named_request.py similarity index 100% rename from testing/example_scripts/deprecated/test_fixture_named_request.py rename to testing/example_scripts/fixtures/test_fixture_named_request.py diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index c2484a8a2..e5bc25a1c 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -1126,21 +1126,6 @@ class TestFixtureUsages: values = reprec.getfailedcollections() assert len(values) == 1 - def test_request_can_be_overridden(self, testdir): - testdir.makepyfile( - """ - import pytest - @pytest.fixture() - def request(request): - request.a = 1 - return request - def test_request(request): - assert request.a == 1 - """ - ) - reprec = testdir.inline_run("-Wignore::pytest.PytestDeprecationWarning") - reprec.assertoutcome(passed=1) - def test_usefixtures_marker(self, testdir): testdir.makepyfile( """ @@ -3973,3 +3958,14 @@ def test_fixture_param_shadowing(testdir): result.stdout.fnmatch_lines(["*::test_normal_fixture[[]a[]]*"]) result.stdout.fnmatch_lines(["*::test_normal_fixture[[]b[]]*"]) result.stdout.fnmatch_lines(["*::test_indirect[[]1[]]*"]) + + +def test_fixture_named_request(testdir): + testdir.copy_example("fixtures/test_fixture_named_request.py") + result = testdir.runpytest() + result.stdout.fnmatch_lines( + [ + "*'request' is a reserved word for fixtures, use another name:", + " *test_fixture_named_request.py:5", + ] + ) From dfe54cd82f55f17f3c9e6e078325f306a046b93b Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 3 Jul 2019 13:57:28 -0300 Subject: [PATCH 14/14] Let context-managers for raises and warns handle unknown keyword arguments As suggested during review --- src/_pytest/python_api.py | 9 +++------ src/_pytest/recwarn.py | 7 +------ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index 3266188a1..3d52bbbcf 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -651,12 +651,9 @@ def raises(expected_exception, *args, match=None, **kwargs): message = "DID NOT RAISE {}".format(expected_exception) if not args: - if kwargs: - msg = "Unexpected keyword arguments passed to pytest.raises: " - msg += ", ".join(sorted(kwargs)) - msg += "\nUse context-manager form instead?" - raise TypeError(msg) - return RaisesContext(expected_exception, message, match) + return RaisesContext( + expected_exception, message=message, match_expr=match, **kwargs + ) else: func = args[0] if not callable(func): diff --git a/src/_pytest/recwarn.py b/src/_pytest/recwarn.py index 8a1cad4a1..3ab83d1e3 100644 --- a/src/_pytest/recwarn.py +++ b/src/_pytest/recwarn.py @@ -76,12 +76,7 @@ def warns(expected_warning, *args, match=None, **kwargs): """ __tracebackhide__ = True if not args: - if kwargs: - msg = "Unexpected keyword arguments passed to pytest.warns: " - msg += ", ".join(sorted(kwargs)) - msg += "\nUse context-manager form instead?" - raise TypeError(msg) - return WarningsChecker(expected_warning, match_expr=match) + return WarningsChecker(expected_warning, match_expr=match, **kwargs) else: func = args[0] if not callable(func):