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.") @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.find_markers('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