diff --git a/changelog/5180.removal.rst b/changelog/5180.removal.rst new file mode 100644 index 000000000..1174a7cba --- /dev/null +++ b/changelog/5180.removal.rst @@ -0,0 +1,26 @@ +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``. + +* ``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. + +* ``pytest.config`` global variable. + +* ``tmpdir_factory.ensuretemp`` method. + +* ``pytest_logwarning`` hook. + +* ``RemovedInPytest4Warning`` warning type. + +* ``request`` is now a reserved name for fixtures. + + +For more information consult +`Deprecations and Removals `__ in the docs. diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index e2399dd41..052ab836a 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,12 +34,47 @@ in places where we or plugin authors must distinguish between fixture names and names supplied by non-fixture things such as ``pytest.mark.parametrize``. +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. + + +``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`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. deprecated:: 4.1 +.. 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 @@ -70,22 +105,12 @@ If you still have concerns about this deprecation and future removal, please com `issue #3974 `__. -``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. - .. _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 +141,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/doc/en/warnings.rst b/doc/en/warnings.rst index c2b256bb3..5934e035d 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/compat.py b/src/_pytest/compat.py index a83932c88..0525d6985 100644 --- a/src/_pytest/compat.py +++ b/src/_pytest/compat.py @@ -56,11 +56,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 c9310bcb5..d81565e13 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -17,6 +17,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 @@ -204,6 +205,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 +438,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/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 e31b9eb0e..c06908932 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -9,10 +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 -from _pytest.warning_types import UnformattedWarning - -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 @@ -23,82 +19,13 @@ 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" -) - -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`, " "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" "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 " - "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." -) - -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" -) - -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/fixtures.py b/src/_pytest/fixtures.py index 965a2e6e9..1d84a9b0e 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 @@ -28,8 +27,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 @@ -475,13 +472,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] @@ -945,9 +935,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): @@ -982,7 +975,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/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): """ 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/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py index 0887d6b9c..da4900dc9 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -10,7 +10,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 @@ -62,26 +61,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.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: diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index cbd833946..82305bb1c 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -1,8 +1,6 @@ import inspect import math import pprint -import sys -import warnings from collections.abc import Iterable from collections.abc import Mapping from collections.abc import Sized @@ -14,7 +12,6 @@ from typing import Union 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 @@ -531,7 +528,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. @@ -544,8 +541,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 @@ -659,36 +654,17 @@ 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 "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: - msg = "Unexpected keyword arguments passed to pytest.raises: " - 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() + return RaisesContext( + expected_exception, message=message, match_expr=match, **kwargs + ) 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..3ab83d1e3 100644 --- a/src/_pytest/recwarn.py +++ b/src/_pytest/recwarn.py @@ -1,12 +1,8 @@ """ 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 @@ -46,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 @@ -80,25 +76,13 @@ 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) - 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) + return WarningsChecker(expected_warning, match_expr=match, **kwargs) 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/src/_pytest/tmpdir.py b/src/_pytest/tmpdir.py index 48680c07e..123a583ad 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 @@ -88,19 +87,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 @@ -138,7 +124,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/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 41de690bc..427c64785 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -9,7 +9,6 @@ import py import pytest from _pytest.compat import importlib_metadata 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/deprecated_test.py b/testing/deprecated_test.py index 5cbb694b1..97b88e939 100644 --- a/testing/deprecated_test.py +++ b/testing/deprecated_test.py @@ -1,39 +1,5 @@ -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") - - -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") - - -def test_getfuncargvalue_is_deprecated(request): - pytest.deprecated_call(request.getfuncargvalue, "tmpdir") @pytest.mark.filterwarnings("default") @@ -78,142 +44,3 @@ def test_external_plugins_integrated(testdir, plugin): with pytest.warns(pytest.PytestConfigWarning): 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 - - 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() - result.stdout.fnmatch_lines( - [ - "*'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/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 c0c230ccf..e5bc25a1c 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(): @@ -599,8 +598,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 +611,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( @@ -1141,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( """ @@ -2200,7 +2170,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*"] @@ -3988,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", + ] + ) 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/python/raises.py b/testing/python/raises.py index a67f25534..1f5594c8a 100644 --- a/testing/python/raises.py +++ b/testing/python/raises.py @@ -2,35 +2,20 @@ import sys import pytest from _pytest.outcomes import Failed -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: @@ -169,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): """ diff --git a/testing/test_config.py b/testing/test_config.py index eb7f95271..18d02f168 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -1,3 +1,4 @@ +import os import sys import textwrap @@ -1241,3 +1242,140 @@ def test_config_blocked_default_plugins(testdir, plugin): result.stdout.fnmatch_lines(["* 1 failed in *"]) else: 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( + **{ + "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() diff --git a/testing/test_mark.py b/testing/test_mark.py index c22e9dbb5..8747d1c6b 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -8,12 +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( - "ignore:MarkInfo objects:pytest.RemovedInPytest4Warning" -) class TestMark: @@ -25,7 +19,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): @@ -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) @@ -1003,15 +997,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." - ) 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): 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( diff --git a/testing/test_tmpdir.py b/testing/test_tmpdir.py index c4c7ebe25..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): @@ -14,13 +13,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 +77,13 @@ 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) 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