123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119 |
- from __future__ import absolute_import, division, print_function
- import warnings
- from contextlib import contextmanager
- import pytest
- from _pytest import compat
- def _setoption(wmod, arg):
- """
- Copy of the warning._setoption function but does not escape arguments.
- """
- parts = arg.split(":")
- if len(parts) > 5:
- raise wmod._OptionError("too many fields (max 5): %r" % (arg,))
- while len(parts) < 5:
- parts.append("")
- action, message, category, module, lineno = [s.strip() for s in parts]
- action = wmod._getaction(action)
- category = wmod._getcategory(category)
- if lineno:
- try:
- lineno = int(lineno)
- if lineno < 0:
- raise ValueError
- except (ValueError, OverflowError):
- raise wmod._OptionError("invalid lineno %r" % (lineno,))
- else:
- lineno = 0
- wmod.filterwarnings(action, message, category, module, lineno)
- def pytest_addoption(parser):
- group = parser.getgroup("pytest-warnings")
- group.addoption(
- "-W",
- "--pythonwarnings",
- action="append",
- help="set which warnings to report, see -W option of python itself.",
- )
- parser.addini(
- "filterwarnings",
- type="linelist",
- help="Each line specifies a pattern for "
- "warnings.filterwarnings. "
- "Processed after -W and --pythonwarnings.",
- )
- def pytest_configure(config):
- config.addinivalue_line(
- "markers",
- "filterwarnings(warning): add a warning filter to the given test. "
- "see https://docs.pytest.org/en/latest/warnings.html#pytest-mark-filterwarnings ",
- )
- @contextmanager
- def catch_warnings_for_item(item):
- """
- catches the warnings generated during setup/call/teardown execution
- of the given item and after it is done posts them as warnings to this
- item.
- """
- args = item.config.getoption("pythonwarnings") or []
- inifilters = item.config.getini("filterwarnings")
- with warnings.catch_warnings(record=True) as log:
- for arg in args:
- warnings._setoption(arg)
- for arg in inifilters:
- _setoption(warnings, arg)
- for mark in item.iter_markers(name="filterwarnings"):
- for arg in mark.args:
- warnings._setoption(arg)
- yield
- for warning in log:
- warn_msg = warning.message
- unicode_warning = False
- if compat._PY2 and any(
- isinstance(m, compat.UNICODE_TYPES) for m in warn_msg.args
- ):
- new_args = []
- for m in warn_msg.args:
- new_args.append(
- compat.ascii_escaped(m)
- if isinstance(m, compat.UNICODE_TYPES)
- else m
- )
- unicode_warning = list(warn_msg.args) != new_args
- warn_msg.args = new_args
- msg = warnings.formatwarning(
- warn_msg,
- warning.category,
- warning.filename,
- warning.lineno,
- warning.line,
- )
- item.warn("unused", msg)
- if unicode_warning:
- warnings.warn(
- "Warning is using unicode non convertible to ascii, "
- "converting to a safe representation:\n %s" % msg,
- UnicodeWarning,
- )
- @pytest.hookimpl(hookwrapper=True)
- def pytest_runtest_protocol(item):
- with catch_warnings_for_item(item):
- yield
|