Remove config.warn, Node.warn; pytest_logwarning issues a warning when implemented
Fix #3078
This commit is contained in:
parent
26d202a7bd
commit
fd48cd57f9
|
@ -0,0 +1,3 @@
|
||||||
|
Remove legacy internal warnings system: ``config.warn``, ``Node.warn``. The ``pytest_logwarning`` now issues a warning when implemented.
|
||||||
|
|
||||||
|
See our `docs <https://docs.pytest.org/en/latest/deprecations.html#config-warn-and-node-warn>`__ on information on how to update your code.
|
|
@ -74,34 +74,6 @@ Becomes:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
``Config.warn`` and ``Node.warn``
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
.. deprecated:: 3.8
|
|
||||||
|
|
||||||
Those methods were part of the internal pytest warnings system, but since ``3.8`` pytest is using the builtin warning
|
|
||||||
system for its own warnings, so those two functions are now deprecated.
|
|
||||||
|
|
||||||
``Config.warn`` should be replaced by calls to the standard ``warnings.warn``, example:
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
config.warn("C1", "some warning")
|
|
||||||
|
|
||||||
Becomes:
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
warnings.warn(pytest.PytestWarning("some warning"))
|
|
||||||
|
|
||||||
``Node.warn`` now supports two signatures:
|
|
||||||
|
|
||||||
* ``node.warn(PytestWarning("some message"))``: is now the **recommended** way to call this function.
|
|
||||||
The warning instance must be a PytestWarning or subclass.
|
|
||||||
|
|
||||||
* ``node.warn("CI", "some message")``: this code/message form is now **deprecated** and should be converted to the warning instance form above.
|
|
||||||
|
|
||||||
|
|
||||||
Calling fixtures directly
|
Calling fixtures directly
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -350,7 +322,33 @@ This should be updated to make use of standard fixture mechanisms:
|
||||||
You can consult `funcarg comparison section in the docs <https://docs.pytest.org/en/latest/funcarg_compare.html>`_ for
|
You can consult `funcarg comparison section in the docs <https://docs.pytest.org/en/latest/funcarg_compare.html>`_ for
|
||||||
more information.
|
more information.
|
||||||
|
|
||||||
This has been documented as deprecated for years, but only now we are actually emitting deprecation warnings.
|
|
||||||
|
``Config.warn`` and ``Node.warn``
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
*Removed in version 4.0.*
|
||||||
|
|
||||||
|
Those methods were part of the internal pytest warnings system, but since ``3.8`` pytest is using the builtin warning
|
||||||
|
system for its own warnings, so those two functions are now deprecated.
|
||||||
|
|
||||||
|
``Config.warn`` should be replaced by calls to the standard ``warnings.warn``, example:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
config.warn("C1", "some warning")
|
||||||
|
|
||||||
|
Becomes:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
warnings.warn(pytest.PytestWarning("some warning"))
|
||||||
|
|
||||||
|
``Node.warn`` now supports two signatures:
|
||||||
|
|
||||||
|
* ``node.warn(PytestWarning("some message"))``: is now the **recommended** way to call this function.
|
||||||
|
The warning instance must be a PytestWarning or subclass.
|
||||||
|
|
||||||
|
* ``node.warn("CI", "some message")``: this code/message form has been **removed** and should be converted to the warning instance form above.
|
||||||
|
|
||||||
``yield`` tests
|
``yield`` tests
|
||||||
~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~
|
||||||
|
|
|
@ -618,7 +618,6 @@ Session related reporting hooks:
|
||||||
.. autofunction:: pytest_terminal_summary
|
.. autofunction:: pytest_terminal_summary
|
||||||
.. autofunction:: pytest_fixture_setup
|
.. autofunction:: pytest_fixture_setup
|
||||||
.. autofunction:: pytest_fixture_post_finalizer
|
.. autofunction:: pytest_fixture_post_finalizer
|
||||||
.. autofunction:: pytest_logwarning
|
|
||||||
.. autofunction:: pytest_warning_captured
|
.. autofunction:: pytest_warning_captured
|
||||||
|
|
||||||
And here is the central hook for reporting about
|
And here is the central hook for reporting about
|
||||||
|
|
|
@ -278,11 +278,11 @@ class AssertionRewritingHook(object):
|
||||||
|
|
||||||
def _warn_already_imported(self, name):
|
def _warn_already_imported(self, name):
|
||||||
from _pytest.warning_types import PytestWarning
|
from _pytest.warning_types import PytestWarning
|
||||||
from _pytest.warnings import _issue_config_warning
|
from _pytest.warnings import _issue_warning_captured
|
||||||
|
|
||||||
_issue_config_warning(
|
_issue_warning_captured(
|
||||||
PytestWarning("Module already imported so cannot be rewritten: %s" % name),
|
PytestWarning("Module already imported so cannot be rewritten: %s" % name),
|
||||||
self.config,
|
self.config.hook,
|
||||||
stacklevel=5,
|
stacklevel=5,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -59,12 +59,12 @@ class Cache(object):
|
||||||
return resolve_from_str(config.getini("cache_dir"), config.rootdir)
|
return resolve_from_str(config.getini("cache_dir"), config.rootdir)
|
||||||
|
|
||||||
def warn(self, fmt, **args):
|
def warn(self, fmt, **args):
|
||||||
from _pytest.warnings import _issue_config_warning
|
from _pytest.warnings import _issue_warning_captured
|
||||||
from _pytest.warning_types import PytestWarning
|
from _pytest.warning_types import PytestWarning
|
||||||
|
|
||||||
_issue_config_warning(
|
_issue_warning_captured(
|
||||||
PytestWarning(fmt.format(**args) if args else fmt),
|
PytestWarning(fmt.format(**args) if args else fmt),
|
||||||
self._config,
|
self._config.hook,
|
||||||
stacklevel=3,
|
stacklevel=3,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -26,11 +26,13 @@ from .exceptions import PrintHelp
|
||||||
from .exceptions import UsageError
|
from .exceptions import UsageError
|
||||||
from .findpaths import determine_setup
|
from .findpaths import determine_setup
|
||||||
from .findpaths import exists
|
from .findpaths import exists
|
||||||
|
from _pytest import deprecated
|
||||||
from _pytest._code import ExceptionInfo
|
from _pytest._code import ExceptionInfo
|
||||||
from _pytest._code import filter_traceback
|
from _pytest._code import filter_traceback
|
||||||
from _pytest.compat import lru_cache
|
from _pytest.compat import lru_cache
|
||||||
from _pytest.compat import safe_str
|
from _pytest.compat import safe_str
|
||||||
from _pytest.outcomes import Skipped
|
from _pytest.outcomes import Skipped
|
||||||
|
from _pytest.warning_types import PytestWarning
|
||||||
|
|
||||||
hookimpl = HookimplMarker("pytest")
|
hookimpl = HookimplMarker("pytest")
|
||||||
hookspec = HookspecMarker("pytest")
|
hookspec = HookspecMarker("pytest")
|
||||||
|
@ -189,9 +191,9 @@ def _prepareconfig(args=None, plugins=None):
|
||||||
else:
|
else:
|
||||||
pluginmanager.register(plugin)
|
pluginmanager.register(plugin)
|
||||||
if warning:
|
if warning:
|
||||||
from _pytest.warnings import _issue_config_warning
|
from _pytest.warnings import _issue_warning_captured
|
||||||
|
|
||||||
_issue_config_warning(warning, config=config, stacklevel=4)
|
_issue_warning_captured(warning, hook=config.hook, stacklevel=4)
|
||||||
return pluginmanager.hook.pytest_cmdline_parse(
|
return pluginmanager.hook.pytest_cmdline_parse(
|
||||||
pluginmanager=pluginmanager, args=args
|
pluginmanager=pluginmanager, args=args
|
||||||
)
|
)
|
||||||
|
@ -245,14 +247,7 @@ class PytestPluginManager(PluginManager):
|
||||||
Use :py:meth:`pluggy.PluginManager.add_hookspecs <PluginManager.add_hookspecs>`
|
Use :py:meth:`pluggy.PluginManager.add_hookspecs <PluginManager.add_hookspecs>`
|
||||||
instead.
|
instead.
|
||||||
"""
|
"""
|
||||||
warning = dict(
|
warnings.warn(deprecated.PLUGIN_MANAGER_ADDHOOKS, stacklevel=2)
|
||||||
code="I2",
|
|
||||||
fslocation=_pytest._code.getfslineno(sys._getframe(1)),
|
|
||||||
nodeid=None,
|
|
||||||
message="use pluginmanager.add_hookspecs instead of "
|
|
||||||
"deprecated addhooks() method.",
|
|
||||||
)
|
|
||||||
self._warn(warning)
|
|
||||||
return self.add_hookspecs(module_or_class)
|
return self.add_hookspecs(module_or_class)
|
||||||
|
|
||||||
def parse_hookimpl_opts(self, plugin, name):
|
def parse_hookimpl_opts(self, plugin, name):
|
||||||
|
@ -296,10 +291,12 @@ class PytestPluginManager(PluginManager):
|
||||||
|
|
||||||
def register(self, plugin, name=None):
|
def register(self, plugin, name=None):
|
||||||
if name in ["pytest_catchlog", "pytest_capturelog"]:
|
if name in ["pytest_catchlog", "pytest_capturelog"]:
|
||||||
self._warn(
|
warnings.warn(
|
||||||
"{} plugin has been merged into the core, "
|
PytestWarning(
|
||||||
"please remove it from your requirements.".format(
|
"{} plugin has been merged into the core, "
|
||||||
name.replace("_", "-")
|
"please remove it from your requirements.".format(
|
||||||
|
name.replace("_", "-")
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
@ -336,14 +333,6 @@ class PytestPluginManager(PluginManager):
|
||||||
)
|
)
|
||||||
self._configured = True
|
self._configured = True
|
||||||
|
|
||||||
def _warn(self, message):
|
|
||||||
kwargs = (
|
|
||||||
message
|
|
||||||
if isinstance(message, dict)
|
|
||||||
else {"code": "I1", "message": message, "fslocation": None, "nodeid": None}
|
|
||||||
)
|
|
||||||
self.hook.pytest_logwarning.call_historic(kwargs=kwargs)
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# internal API for local conftest plugin handling
|
# internal API for local conftest plugin handling
|
||||||
#
|
#
|
||||||
|
@ -542,7 +531,13 @@ class PytestPluginManager(PluginManager):
|
||||||
six.reraise(new_exc_type, new_exc, sys.exc_info()[2])
|
six.reraise(new_exc_type, new_exc, sys.exc_info()[2])
|
||||||
|
|
||||||
except Skipped as e:
|
except Skipped as e:
|
||||||
self._warn("skipped plugin %r: %s" % ((modname, e.msg)))
|
from _pytest.warnings import _issue_warning_captured
|
||||||
|
|
||||||
|
_issue_warning_captured(
|
||||||
|
PytestWarning("skipped plugin %r: %s" % (modname, e.msg)),
|
||||||
|
self.hook,
|
||||||
|
stacklevel=1,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
mod = sys.modules[importspec]
|
mod = sys.modules[importspec]
|
||||||
self.register(mod, modname)
|
self.register(mod, modname)
|
||||||
|
@ -617,7 +612,6 @@ class Config(object):
|
||||||
self._override_ini = ()
|
self._override_ini = ()
|
||||||
self._opt2dest = {}
|
self._opt2dest = {}
|
||||||
self._cleanup = []
|
self._cleanup = []
|
||||||
self._warn = self.pluginmanager._warn
|
|
||||||
self.pluginmanager.register(self, "pytestconfig")
|
self.pluginmanager.register(self, "pytestconfig")
|
||||||
self._configured = False
|
self._configured = False
|
||||||
self.invocation_dir = py.path.local()
|
self.invocation_dir = py.path.local()
|
||||||
|
@ -642,36 +636,6 @@ class Config(object):
|
||||||
fin = self._cleanup.pop()
|
fin = self._cleanup.pop()
|
||||||
fin()
|
fin()
|
||||||
|
|
||||||
def warn(self, code, message, fslocation=None, nodeid=None):
|
|
||||||
"""
|
|
||||||
.. deprecated:: 3.8
|
|
||||||
|
|
||||||
Use :py:func:`warnings.warn` or :py:func:`warnings.warn_explicit` directly instead.
|
|
||||||
|
|
||||||
Generate a warning for this test session.
|
|
||||||
"""
|
|
||||||
from _pytest.warning_types import RemovedInPytest4Warning
|
|
||||||
|
|
||||||
if isinstance(fslocation, (tuple, list)) and len(fslocation) > 2:
|
|
||||||
filename, lineno = fslocation[:2]
|
|
||||||
else:
|
|
||||||
filename = "unknown file"
|
|
||||||
lineno = 0
|
|
||||||
msg = "config.warn has been deprecated, use warnings.warn instead"
|
|
||||||
if nodeid:
|
|
||||||
msg = "{}: {}".format(nodeid, msg)
|
|
||||||
warnings.warn_explicit(
|
|
||||||
RemovedInPytest4Warning(msg),
|
|
||||||
category=None,
|
|
||||||
filename=filename,
|
|
||||||
lineno=lineno,
|
|
||||||
)
|
|
||||||
self.hook.pytest_logwarning.call_historic(
|
|
||||||
kwargs=dict(
|
|
||||||
code=code, message=message, fslocation=fslocation, nodeid=nodeid
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_terminal_writer(self):
|
def get_terminal_writer(self):
|
||||||
return self.pluginmanager.get_plugin("terminalreporter")._tw
|
return self.pluginmanager.get_plugin("terminalreporter")._tw
|
||||||
|
|
||||||
|
@ -826,7 +790,15 @@ class Config(object):
|
||||||
if ns.help or ns.version:
|
if ns.help or ns.version:
|
||||||
# we don't want to prevent --help/--version to work
|
# we don't want to prevent --help/--version to work
|
||||||
# so just let is pass and print a warning at the end
|
# so just let is pass and print a warning at the end
|
||||||
self._warn("could not load initial conftests (%s)\n" % e.path)
|
from _pytest.warnings import _issue_warning_captured
|
||||||
|
|
||||||
|
_issue_warning_captured(
|
||||||
|
PytestWarning(
|
||||||
|
"could not load initial conftests: {}".format(e.path)
|
||||||
|
),
|
||||||
|
self.hook,
|
||||||
|
stacklevel=2,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
|
@ -34,14 +34,14 @@ def getcfg(args, config=None):
|
||||||
iniconfig = py.iniconfig.IniConfig(p)
|
iniconfig = py.iniconfig.IniConfig(p)
|
||||||
if "pytest" in iniconfig.sections:
|
if "pytest" in iniconfig.sections:
|
||||||
if inibasename == "setup.cfg" and config is not None:
|
if inibasename == "setup.cfg" and config is not None:
|
||||||
from _pytest.warnings import _issue_config_warning
|
from _pytest.warnings import _issue_warning_captured
|
||||||
from _pytest.warning_types import RemovedInPytest4Warning
|
from _pytest.warning_types import RemovedInPytest4Warning
|
||||||
|
|
||||||
_issue_config_warning(
|
_issue_warning_captured(
|
||||||
RemovedInPytest4Warning(
|
RemovedInPytest4Warning(
|
||||||
CFG_PYTEST_SECTION.format(filename=inibasename)
|
CFG_PYTEST_SECTION.format(filename=inibasename)
|
||||||
),
|
),
|
||||||
config=config,
|
hook=config.hook,
|
||||||
stacklevel=2,
|
stacklevel=2,
|
||||||
)
|
)
|
||||||
return base, p, iniconfig["pytest"]
|
return base, p, iniconfig["pytest"]
|
||||||
|
@ -112,13 +112,13 @@ def determine_setup(inifile, args, rootdir_cmd_arg=None, config=None):
|
||||||
inicfg = iniconfig[section]
|
inicfg = iniconfig[section]
|
||||||
if is_cfg_file and section == "pytest" and config is not None:
|
if is_cfg_file and section == "pytest" and config is not None:
|
||||||
from _pytest.deprecated import CFG_PYTEST_SECTION
|
from _pytest.deprecated import CFG_PYTEST_SECTION
|
||||||
from _pytest.warnings import _issue_config_warning
|
from _pytest.warnings import _issue_warning_captured
|
||||||
|
|
||||||
# TODO: [pytest] section in *.cfg files is deprecated. Need refactoring once
|
# TODO: [pytest] section in *.cfg files is deprecated. Need refactoring once
|
||||||
# the deprecation expires.
|
# the deprecation expires.
|
||||||
_issue_config_warning(
|
_issue_warning_captured(
|
||||||
CFG_PYTEST_SECTION.format(filename=str(inifile)),
|
CFG_PYTEST_SECTION.format(filename=str(inifile)),
|
||||||
config,
|
config.hook,
|
||||||
stacklevel=2,
|
stacklevel=2,
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
|
|
|
@ -75,10 +75,6 @@ MARK_PARAMETERSET_UNPACKING = RemovedInPytest4Warning(
|
||||||
"For more details, see: https://docs.pytest.org/en/latest/parametrize.html"
|
"For more details, see: https://docs.pytest.org/en/latest/parametrize.html"
|
||||||
)
|
)
|
||||||
|
|
||||||
NODE_WARN = RemovedInPytest4Warning(
|
|
||||||
"Node.warn(code, message) form has been deprecated, use Node.warn(warning_instance) instead."
|
|
||||||
)
|
|
||||||
|
|
||||||
RAISES_EXEC = PytestDeprecationWarning(
|
RAISES_EXEC = PytestDeprecationWarning(
|
||||||
"raises(..., 'code(as_a_string)') is deprecated, use the context manager form or use `exec()` directly\n\n"
|
"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"
|
"See https://docs.pytest.org/en/latest/deprecations.html#raises-warns-exec"
|
||||||
|
@ -94,6 +90,13 @@ RECORD_XML_PROPERTY = RemovedInPytest4Warning(
|
||||||
'"record_xml_property" is now deprecated.'
|
'"record_xml_property" is now deprecated.'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
PLUGIN_MANAGER_ADDHOOKS = PytestDeprecationWarning(
|
||||||
|
"use pluginmanager.add_hookspecs instead of deprecated addhooks() method."
|
||||||
|
)
|
||||||
|
|
||||||
|
COLLECTOR_MAKEITEM = RemovedInPytest4Warning(
|
||||||
|
"pycollector makeitem was removed as it is an accidentially leaked internal api"
|
||||||
|
)
|
||||||
|
|
||||||
PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST = RemovedInPytest4Warning(
|
PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST = RemovedInPytest4Warning(
|
||||||
"Defining pytest_plugins in a non-top-level conftest is deprecated, "
|
"Defining pytest_plugins in a non-top-level conftest is deprecated, "
|
||||||
|
@ -110,3 +113,8 @@ PYTEST_ENSURETEMP = RemovedInPytest4Warning(
|
||||||
"pytest/tmpdir_factory.ensuretemp is deprecated, \n"
|
"pytest/tmpdir_factory.ensuretemp is deprecated, \n"
|
||||||
"please use the tmp_path fixture or tmp_path_factory.mktemp"
|
"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"
|
||||||
|
)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
""" hook specifications for pytest plugins, invoked from main.py and builtin plugins. """
|
""" hook specifications for pytest plugins, invoked from main.py and builtin plugins. """
|
||||||
from pluggy import HookspecMarker
|
from pluggy import HookspecMarker
|
||||||
|
|
||||||
|
from _pytest.deprecated import PYTEST_LOGWARNING
|
||||||
|
|
||||||
|
|
||||||
hookspec = HookspecMarker("pytest")
|
hookspec = HookspecMarker("pytest")
|
||||||
|
|
||||||
|
@ -496,7 +498,7 @@ def pytest_terminal_summary(terminalreporter, exitstatus):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
@hookspec(historic=True)
|
@hookspec(historic=True, warn_on_impl=PYTEST_LOGWARNING)
|
||||||
def pytest_logwarning(message, code, nodeid, fslocation):
|
def pytest_logwarning(message, code, nodeid, fslocation):
|
||||||
"""
|
"""
|
||||||
.. deprecated:: 3.8
|
.. deprecated:: 3.8
|
||||||
|
|
|
@ -105,74 +105,7 @@ class Node(object):
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<%s %s>" % (self.__class__.__name__, getattr(self, "name", None))
|
return "<%s %s>" % (self.__class__.__name__, getattr(self, "name", None))
|
||||||
|
|
||||||
def warn(self, _code_or_warning=None, message=None, code=None):
|
def warn(self, warning):
|
||||||
"""Issue a warning for this item.
|
|
||||||
|
|
||||||
Warnings will be displayed after the test session, unless explicitly suppressed.
|
|
||||||
|
|
||||||
This can be called in two forms:
|
|
||||||
|
|
||||||
**Warning instance**
|
|
||||||
|
|
||||||
This was introduced in pytest 3.8 and uses the standard warning mechanism to issue warnings.
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
node.warn(PytestWarning("some message"))
|
|
||||||
|
|
||||||
The warning instance must be a subclass of :class:`pytest.PytestWarning`.
|
|
||||||
|
|
||||||
**code/message (deprecated)**
|
|
||||||
|
|
||||||
This form was used in pytest prior to 3.8 and is considered deprecated. Using this form will emit another
|
|
||||||
warning about the deprecation:
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
node.warn("CI", "some message")
|
|
||||||
|
|
||||||
:param Union[Warning,str] _code_or_warning:
|
|
||||||
warning instance or warning code (legacy). This parameter receives an underscore for backward
|
|
||||||
compatibility with the legacy code/message form, and will be replaced for something
|
|
||||||
more usual when the legacy form is removed.
|
|
||||||
|
|
||||||
:param Union[str,None] message: message to display when called in the legacy form.
|
|
||||||
:param str code: code for the warning, in legacy form when using keyword arguments.
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
if message is None:
|
|
||||||
if _code_or_warning is None:
|
|
||||||
raise ValueError("code_or_warning must be given")
|
|
||||||
self._std_warn(_code_or_warning)
|
|
||||||
else:
|
|
||||||
if _code_or_warning and code:
|
|
||||||
raise ValueError(
|
|
||||||
"code_or_warning and code cannot both be passed to this function"
|
|
||||||
)
|
|
||||||
code = _code_or_warning or code
|
|
||||||
self._legacy_warn(code, message)
|
|
||||||
|
|
||||||
def _legacy_warn(self, code, message):
|
|
||||||
"""
|
|
||||||
.. deprecated:: 3.8
|
|
||||||
|
|
||||||
Use :meth:`Node.std_warn <_pytest.nodes.Node.std_warn>` instead.
|
|
||||||
|
|
||||||
Generate a warning with the given code and message for this item.
|
|
||||||
"""
|
|
||||||
from _pytest.deprecated import NODE_WARN
|
|
||||||
|
|
||||||
self._std_warn(NODE_WARN)
|
|
||||||
|
|
||||||
assert isinstance(code, str)
|
|
||||||
fslocation = get_fslocation_from_item(self)
|
|
||||||
self.ihook.pytest_logwarning.call_historic(
|
|
||||||
kwargs=dict(
|
|
||||||
code=code, message=message, nodeid=self.nodeid, fslocation=fslocation
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def _std_warn(self, warning):
|
|
||||||
"""Issue a warning for this item.
|
"""Issue a warning for this item.
|
||||||
|
|
||||||
Warnings will be displayed after the test session, unless explicitly suppressed
|
Warnings will be displayed after the test session, unless explicitly suppressed
|
||||||
|
@ -180,6 +113,12 @@ class Node(object):
|
||||||
:param Warning warning: the warning instance to issue. Must be a subclass of PytestWarning.
|
:param Warning warning: the warning instance to issue. Must be a subclass of PytestWarning.
|
||||||
|
|
||||||
:raise ValueError: if ``warning`` instance is not a subclass of PytestWarning.
|
:raise ValueError: if ``warning`` instance is not a subclass of PytestWarning.
|
||||||
|
|
||||||
|
Example usage::
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
node.warn(PytestWarning("some message"))
|
||||||
"""
|
"""
|
||||||
from _pytest.warning_types import PytestWarning
|
from _pytest.warning_types import PytestWarning
|
||||||
|
|
||||||
|
|
|
@ -34,9 +34,9 @@ def pytest_configure(config):
|
||||||
config.pluginmanager.register(config._resultlog)
|
config.pluginmanager.register(config._resultlog)
|
||||||
|
|
||||||
from _pytest.deprecated import RESULT_LOG
|
from _pytest.deprecated import RESULT_LOG
|
||||||
from _pytest.warnings import _issue_config_warning
|
from _pytest.warnings import _issue_warning_captured
|
||||||
|
|
||||||
_issue_config_warning(RESULT_LOG, config, stacklevel=2)
|
_issue_warning_captured(RESULT_LOG, config.hook, stacklevel=2)
|
||||||
|
|
||||||
|
|
||||||
def pytest_unconfigure(config):
|
def pytest_unconfigure(config):
|
||||||
|
|
|
@ -186,20 +186,17 @@ def pytest_report_teststatus(report):
|
||||||
@attr.s
|
@attr.s
|
||||||
class WarningReport(object):
|
class WarningReport(object):
|
||||||
"""
|
"""
|
||||||
Simple structure to hold warnings information captured by ``pytest_logwarning`` and ``pytest_warning_captured``.
|
Simple structure to hold warnings information captured by ``pytest_warning_captured``.
|
||||||
|
|
||||||
:ivar str message: user friendly message about the warning
|
:ivar str message: user friendly message about the warning
|
||||||
:ivar str|None nodeid: node id that generated the warning (see ``get_location``).
|
:ivar str|None nodeid: node id that generated the warning (see ``get_location``).
|
||||||
:ivar tuple|py.path.local fslocation:
|
:ivar tuple|py.path.local fslocation:
|
||||||
file system location of the source of the warning (see ``get_location``).
|
file system location of the source of the warning (see ``get_location``).
|
||||||
|
|
||||||
:ivar bool legacy: if this warning report was generated from the deprecated ``pytest_logwarning`` hook.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
message = attr.ib()
|
message = attr.ib()
|
||||||
nodeid = attr.ib(default=None)
|
nodeid = attr.ib(default=None)
|
||||||
fslocation = attr.ib(default=None)
|
fslocation = attr.ib(default=None)
|
||||||
legacy = attr.ib(default=False)
|
|
||||||
|
|
||||||
def get_location(self, config):
|
def get_location(self, config):
|
||||||
"""
|
"""
|
||||||
|
@ -329,13 +326,6 @@ class TerminalReporter(object):
|
||||||
self.write_line("INTERNALERROR> " + line)
|
self.write_line("INTERNALERROR> " + line)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
def pytest_logwarning(self, fslocation, message, nodeid):
|
|
||||||
warnings = self.stats.setdefault("warnings", [])
|
|
||||||
warning = WarningReport(
|
|
||||||
fslocation=fslocation, message=message, nodeid=nodeid, legacy=True
|
|
||||||
)
|
|
||||||
warnings.append(warning)
|
|
||||||
|
|
||||||
def pytest_warning_captured(self, warning_message, item):
|
def pytest_warning_captured(self, warning_message, item):
|
||||||
# from _pytest.nodes import get_fslocation_from_item
|
# from _pytest.nodes import get_fslocation_from_item
|
||||||
from _pytest.warnings import warning_record_to_str
|
from _pytest.warnings import warning_record_to_str
|
||||||
|
|
|
@ -160,19 +160,19 @@ def pytest_terminal_summary(terminalreporter):
|
||||||
yield
|
yield
|
||||||
|
|
||||||
|
|
||||||
def _issue_config_warning(warning, config, stacklevel):
|
def _issue_warning_captured(warning, hook, stacklevel):
|
||||||
"""
|
"""
|
||||||
This function should be used instead of calling ``warnings.warn`` directly when we are in the "configure" stage:
|
This function should be used instead of calling ``warnings.warn`` directly when we are in the "configure" stage:
|
||||||
at this point the actual options might not have been set, so we manually trigger the pytest_warning_captured
|
at this point the actual options might not have been set, so we manually trigger the pytest_warning_captured
|
||||||
hook so we can display this warnings in the terminal. This is a hack until we can sort out #2891.
|
hook so we can display this warnings in the terminal. This is a hack until we can sort out #2891.
|
||||||
|
|
||||||
:param warning: the warning instance.
|
:param warning: the warning instance.
|
||||||
:param config:
|
:param hook: the hook caller
|
||||||
:param stacklevel: stacklevel forwarded to warnings.warn
|
:param stacklevel: stacklevel forwarded to warnings.warn
|
||||||
"""
|
"""
|
||||||
with warnings.catch_warnings(record=True) as records:
|
with warnings.catch_warnings(record=True) as records:
|
||||||
warnings.simplefilter("always", type(warning))
|
warnings.simplefilter("always", type(warning))
|
||||||
warnings.warn(warning, stacklevel=stacklevel)
|
warnings.warn(warning, stacklevel=stacklevel)
|
||||||
config.hook.pytest_warning_captured.call_historic(
|
hook.pytest_warning_captured.call_historic(
|
||||||
kwargs=dict(warning_message=records[0], when="config", item=None)
|
kwargs=dict(warning_message=records[0], when="config", item=None)
|
||||||
)
|
)
|
||||||
|
|
|
@ -146,6 +146,7 @@ class TestGeneralUsage(object):
|
||||||
assert result.ret
|
assert result.ret
|
||||||
result.stderr.fnmatch_lines(["*ERROR: not found:*{}".format(p2.basename)])
|
result.stderr.fnmatch_lines(["*ERROR: not found:*{}".format(p2.basename)])
|
||||||
|
|
||||||
|
@pytest.mark.filterwarnings("default")
|
||||||
def test_better_reporting_on_conftest_load_failure(self, testdir, request):
|
def test_better_reporting_on_conftest_load_failure(self, testdir, request):
|
||||||
"""Show a user-friendly traceback on conftest import failures (#486, #3332)"""
|
"""Show a user-friendly traceback on conftest import failures (#486, #3332)"""
|
||||||
testdir.makepyfile("")
|
testdir.makepyfile("")
|
||||||
|
|
|
@ -120,6 +120,7 @@ def test_terminal_reporter_writer_attr(pytestconfig):
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("plugin", ["catchlog", "capturelog"])
|
@pytest.mark.parametrize("plugin", ["catchlog", "capturelog"])
|
||||||
|
@pytest.mark.filterwarnings("default")
|
||||||
def test_pytest_catchlog_deprecated(testdir, plugin):
|
def test_pytest_catchlog_deprecated(testdir, plugin):
|
||||||
testdir.makepyfile(
|
testdir.makepyfile(
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -823,7 +823,9 @@ def test_rewritten():
|
||||||
testdir.makepyfile(test_remember_rewritten_modules="")
|
testdir.makepyfile(test_remember_rewritten_modules="")
|
||||||
warnings = []
|
warnings = []
|
||||||
hook = AssertionRewritingHook(pytestconfig)
|
hook = AssertionRewritingHook(pytestconfig)
|
||||||
monkeypatch.setattr(hook.config, "warn", lambda code, msg: warnings.append(msg))
|
monkeypatch.setattr(
|
||||||
|
hook, "_warn_already_imported", lambda code, msg: warnings.append(msg)
|
||||||
|
)
|
||||||
hook.find_module("test_remember_rewritten_modules")
|
hook.find_module("test_remember_rewritten_modules")
|
||||||
hook.load_module("test_remember_rewritten_modules")
|
hook.load_module("test_remember_rewritten_modules")
|
||||||
hook.mark_rewrite("test_remember_rewritten_modules")
|
hook.mark_rewrite("test_remember_rewritten_modules")
|
||||||
|
|
|
@ -12,7 +12,6 @@ from _pytest.config.findpaths import determine_setup
|
||||||
from _pytest.config.findpaths import get_common_ancestor
|
from _pytest.config.findpaths import get_common_ancestor
|
||||||
from _pytest.config.findpaths import getcfg
|
from _pytest.config.findpaths import getcfg
|
||||||
from _pytest.main import EXIT_NOTESTSCOLLECTED
|
from _pytest.main import EXIT_NOTESTSCOLLECTED
|
||||||
from _pytest.warnings import SHOW_PYTEST_WARNINGS_ARG
|
|
||||||
|
|
||||||
|
|
||||||
class TestParseIni(object):
|
class TestParseIni(object):
|
||||||
|
@ -790,66 +789,6 @@ def test_collect_pytest_prefix_bug(pytestconfig):
|
||||||
assert pm.parse_hookimpl_opts(Dummy(), "pytest_something") is None
|
assert pm.parse_hookimpl_opts(Dummy(), "pytest_something") is None
|
||||||
|
|
||||||
|
|
||||||
class TestLegacyWarning(object):
|
|
||||||
@pytest.mark.filterwarnings("default")
|
|
||||||
def test_warn_config(self, testdir):
|
|
||||||
testdir.makeconftest(
|
|
||||||
"""
|
|
||||||
values = []
|
|
||||||
def pytest_runtest_setup(item):
|
|
||||||
item.config.warn("C1", "hello")
|
|
||||||
def pytest_logwarning(code, message):
|
|
||||||
if message == "hello" and code == "C1":
|
|
||||||
values.append(1)
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
testdir.makepyfile(
|
|
||||||
"""
|
|
||||||
def test_proper(pytestconfig):
|
|
||||||
import conftest
|
|
||||||
assert conftest.values == [1]
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
result = testdir.runpytest(SHOW_PYTEST_WARNINGS_ARG)
|
|
||||||
result.stdout.fnmatch_lines(
|
|
||||||
["*hello", "*config.warn has been deprecated*", "*1 passed*"]
|
|
||||||
)
|
|
||||||
|
|
||||||
@pytest.mark.filterwarnings("default")
|
|
||||||
@pytest.mark.parametrize("use_kw", [True, False])
|
|
||||||
def test_warn_on_test_item_from_request(self, testdir, use_kw):
|
|
||||||
code_kw = "code=" if use_kw else ""
|
|
||||||
message_kw = "message=" if use_kw else ""
|
|
||||||
testdir.makepyfile(
|
|
||||||
"""
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def fix(request):
|
|
||||||
request.node.warn({code_kw}"T1", {message_kw}"hello")
|
|
||||||
|
|
||||||
def test_hello(fix):
|
|
||||||
pass
|
|
||||||
""".format(
|
|
||||||
code_kw=code_kw, message_kw=message_kw
|
|
||||||
)
|
|
||||||
)
|
|
||||||
result = testdir.runpytest(
|
|
||||||
"--disable-pytest-warnings", SHOW_PYTEST_WARNINGS_ARG
|
|
||||||
)
|
|
||||||
assert "hello" not in result.stdout.str()
|
|
||||||
|
|
||||||
result = testdir.runpytest(SHOW_PYTEST_WARNINGS_ARG)
|
|
||||||
result.stdout.fnmatch_lines(
|
|
||||||
"""
|
|
||||||
===*warnings summary*===
|
|
||||||
*test_warn_on_test_item_from_request.py::test_hello*
|
|
||||||
*hello*
|
|
||||||
*test_warn_on_test_item_from_request.py:7:*Node.warn(code, message) form has been deprecated*
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class TestRootdir(object):
|
class TestRootdir(object):
|
||||||
def test_simple_noini(self, tmpdir):
|
def test_simple_noini(self, tmpdir):
|
||||||
assert get_common_ancestor([tmpdir]) == tmpdir
|
assert get_common_ancestor([tmpdir]) == tmpdir
|
||||||
|
|
|
@ -32,7 +32,7 @@ class TestPytestPluginInteractions(object):
|
||||||
"""
|
"""
|
||||||
import newhooks
|
import newhooks
|
||||||
def pytest_addhooks(pluginmanager):
|
def pytest_addhooks(pluginmanager):
|
||||||
pluginmanager.addhooks(newhooks)
|
pluginmanager.add_hookspecs(newhooks)
|
||||||
def pytest_myhook(xyz):
|
def pytest_myhook(xyz):
|
||||||
return xyz + 1
|
return xyz + 1
|
||||||
"""
|
"""
|
||||||
|
@ -52,7 +52,7 @@ class TestPytestPluginInteractions(object):
|
||||||
"""
|
"""
|
||||||
import sys
|
import sys
|
||||||
def pytest_addhooks(pluginmanager):
|
def pytest_addhooks(pluginmanager):
|
||||||
pluginmanager.addhooks(sys)
|
pluginmanager.add_hookspecs(sys)
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
res = testdir.runpytest()
|
res = testdir.runpytest()
|
||||||
|
@ -141,23 +141,6 @@ class TestPytestPluginInteractions(object):
|
||||||
ihook_b = session.gethookproxy(testdir.tmpdir.join("tests"))
|
ihook_b = session.gethookproxy(testdir.tmpdir.join("tests"))
|
||||||
assert ihook_a is not ihook_b
|
assert ihook_a is not ihook_b
|
||||||
|
|
||||||
def test_warn_on_deprecated_addhooks(self, pytestpm):
|
|
||||||
warnings = []
|
|
||||||
|
|
||||||
class get_warnings(object):
|
|
||||||
def pytest_logwarning(self, code, fslocation, message, nodeid):
|
|
||||||
warnings.append(message)
|
|
||||||
|
|
||||||
class Plugin(object):
|
|
||||||
def pytest_testhook():
|
|
||||||
pass
|
|
||||||
|
|
||||||
pytestpm.register(get_warnings())
|
|
||||||
before = list(warnings)
|
|
||||||
pytestpm.addhooks(Plugin())
|
|
||||||
assert len(warnings) == len(before) + 1
|
|
||||||
assert "deprecated" in warnings[-1]
|
|
||||||
|
|
||||||
|
|
||||||
def test_default_markers(testdir):
|
def test_default_markers(testdir):
|
||||||
result = testdir.runpytest("--markers")
|
result = testdir.runpytest("--markers")
|
||||||
|
@ -240,11 +223,12 @@ class TestPytestPluginManager(object):
|
||||||
with pytest.raises(ImportError):
|
with pytest.raises(ImportError):
|
||||||
pytestpm.consider_env()
|
pytestpm.consider_env()
|
||||||
|
|
||||||
|
@pytest.mark.filterwarnings("always")
|
||||||
def test_plugin_skip(self, testdir, monkeypatch):
|
def test_plugin_skip(self, testdir, monkeypatch):
|
||||||
p = testdir.makepyfile(
|
p = testdir.makepyfile(
|
||||||
skipping1="""
|
skipping1="""
|
||||||
import pytest
|
import pytest
|
||||||
pytest.skip("hello")
|
pytest.skip("hello", allow_module_level=True)
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
p.copy(p.dirpath("skipping2.py"))
|
p.copy(p.dirpath("skipping2.py"))
|
||||||
|
|
|
@ -168,7 +168,7 @@ def make_holder():
|
||||||
@pytest.mark.parametrize("holder", make_holder())
|
@pytest.mark.parametrize("holder", make_holder())
|
||||||
def test_hookrecorder_basic(holder):
|
def test_hookrecorder_basic(holder):
|
||||||
pm = PytestPluginManager()
|
pm = PytestPluginManager()
|
||||||
pm.addhooks(holder)
|
pm.add_hookspecs(holder)
|
||||||
rec = HookRecorder(pm)
|
rec = HookRecorder(pm)
|
||||||
pm.hook.pytest_xyz(arg=123)
|
pm.hook.pytest_xyz(arg=123)
|
||||||
call = rec.popcall("pytest_xyz")
|
call = rec.popcall("pytest_xyz")
|
||||||
|
|
|
@ -308,9 +308,9 @@ def test_filterwarnings_mark_registration(testdir):
|
||||||
def test_warning_captured_hook(testdir):
|
def test_warning_captured_hook(testdir):
|
||||||
testdir.makeconftest(
|
testdir.makeconftest(
|
||||||
"""
|
"""
|
||||||
from _pytest.warnings import _issue_config_warning
|
from _pytest.warnings import _issue_warning_captured
|
||||||
def pytest_configure(config):
|
def pytest_configure(config):
|
||||||
_issue_config_warning(UserWarning("config warning"), config, stacklevel=2)
|
_issue_warning_captured(UserWarning("config warning"), config.hook, stacklevel=2)
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
testdir.makepyfile(
|
testdir.makepyfile(
|
||||||
|
|
Loading…
Reference in New Issue