conftest files now use assertion rewriting

Fix #1619
This commit is contained in:
Bruno Oliveira
2016-06-20 14:45:13 +02:00
parent 54872e94b4
commit 3a81d2e012
4 changed files with 112 additions and 40 deletions

View File

@@ -4,6 +4,8 @@ support for presenting detailed information in failing assertions.
import py
import os
import sys
from _pytest.config import hookimpl
from _pytest.monkeypatch import monkeypatch
from _pytest.assertion import util
@@ -42,9 +44,13 @@ class AssertionState:
self.trace = config.trace.root.get("assertion")
def pytest_configure(config):
mode = config.getvalue("assertmode")
if config.getvalue("noassert") or config.getvalue("nomagic"):
@hookimpl(tryfirst=True)
def pytest_load_initial_conftests(early_config, parser, args):
ns, ns_unknown_args = parser.parse_known_and_unknown_args(args)
mode = ns.assertmode
no_assert = ns.noassert
no_magic = ns.nomagic
if no_assert or no_magic:
mode = "plain"
if mode == "rewrite":
try:
@@ -57,25 +63,30 @@ def pytest_configure(config):
if (sys.platform.startswith('java') or
sys.version_info[:3] == (2, 6, 0)):
mode = "reinterp"
early_config._assertstate = AssertionState(early_config, mode)
warn_about_missing_assertion(mode, early_config.pluginmanager)
if mode != "plain":
_load_modules(mode)
m = monkeypatch()
config._cleanup.append(m.undo)
early_config._cleanup.append(m.undo)
m.setattr(py.builtin.builtins, 'AssertionError',
reinterpret.AssertionError) # noqa
hook = None
if mode == "rewrite":
hook = rewrite.AssertionRewritingHook() # noqa
hook.set_config(early_config)
sys.meta_path.insert(0, hook)
warn_about_missing_assertion(mode)
config._assertstate = AssertionState(config, mode)
config._assertstate.hook = hook
config._assertstate.trace("configured with mode set to %r" % (mode,))
early_config._assertstate.hook = hook
early_config._assertstate.trace("configured with mode set to %r" % (mode,))
def undo():
hook = config._assertstate.hook
hook = early_config._assertstate.hook
if hook is not None and hook in sys.meta_path:
sys.meta_path.remove(hook)
config.add_cleanup(undo)
early_config.add_cleanup(undo)
def pytest_collection(session):
@@ -154,7 +165,8 @@ def _load_modules(mode):
from _pytest.assertion import rewrite # noqa
def warn_about_missing_assertion(mode):
def warn_about_missing_assertion(mode, pluginmanager):
print('got here')
try:
assert False
except AssertionError:
@@ -166,10 +178,18 @@ def warn_about_missing_assertion(mode):
else:
specifically = "failing tests may report as passing"
sys.stderr.write("WARNING: " + specifically +
" because assert statements are not executed "
"by the underlying Python interpreter "
"(are you using python -O?)\n")
# temporarily disable capture so we can print our warning
capman = pluginmanager.getplugin('capturemanager')
try:
out, err = capman.suspendcapture()
sys.stderr.write("WARNING: " + specifically +
" because assert statements are not executed "
"by the underlying Python interpreter "
"(are you using python -O?)\n")
finally:
capman.resumecapture()
sys.stdout.write(out)
sys.stderr.write(err)
# Expose this plugin's implementation for the pytest_assertrepr_compare hook

View File

@@ -50,14 +50,14 @@ class AssertionRewritingHook(object):
self._register_with_pkg_resources()
def set_session(self, session):
self.fnpats = session.config.getini("python_files")
self.session = session
def set_config(self, config):
self.config = config
self.fnpats = config.getini("python_files")
def find_module(self, name, path=None):
if self.session is None:
return None
sess = self.session
state = sess.config._assertstate
state = self.config._assertstate
state.trace("find_module called for: %s" % name)
names = name.rsplit(".", 1)
lastname = names[-1]
@@ -86,24 +86,11 @@ class AssertionRewritingHook(object):
return None
else:
fn = os.path.join(pth, name.rpartition(".")[2] + ".py")
fn_pypath = py.path.local(fn)
# Is this a test file?
if not sess.isinitpath(fn):
# We have to be very careful here because imports in this code can
# trigger a cycle.
self.session = None
try:
for pat in self.fnpats:
if fn_pypath.fnmatch(pat):
state.trace("matched test file %r" % (fn,))
break
else:
return None
finally:
self.session = sess
else:
state.trace("matched test file (was specified on cmdline): %r" %
(fn,))
if not self._should_rewrite(fn_pypath, state):
return
# The requested module looks like a test file, so rewrite it. This is
# the most magical part of the process: load the source, rewrite the
# asserts, and load the rewritten source. We also cache the rewritten
@@ -151,6 +138,32 @@ class AssertionRewritingHook(object):
self.modules[name] = co, pyc
return self
def _should_rewrite(self, fn_pypath, state):
# always rewrite conftest files
fn = str(fn_pypath)
if fn_pypath.basename == 'conftest.py':
state.trace("rewriting conftest file: %r" % (fn,))
return True
elif self.session is not None:
if self.session.isinitpath(fn):
state.trace("matched test file (was specified on cmdline): %r" %
(fn,))
return True
else:
# modules not passed explicitly on the command line are only
# rewritten if they match the naming convention for test files
session = self.session # avoid a cycle here
self.session = None
try:
for pat in self.fnpats:
if fn_pypath.fnmatch(pat):
state.trace("matched test file %r" % (fn,))
return True
finally:
self.session = session
del session
return False
def load_module(self, name):
# If there is an existing module object named 'fullname' in
# sys.modules, the loader must use that existing module. (Otherwise,