-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Issue 1619 conftest assert rewrite #1622
Changes from 3 commits
3a81d2e
e0cb046
9118c02
8b0fb47
819942e
573866b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,8 @@ | |
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 @@ def __init__(self, config, mode): | |
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,29 @@ 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 = rewrite.AssertionRewritingHook(early_config) # noqa | ||
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 +164,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 +177,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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the point of writing the captured data out again after resuming capturing? Wouldn't it be captured and double then? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not really, when There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are you sure it would get lost? Does There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, that's why it returns it. 😁 |
||
|
||
|
||
# Expose this plugin's implementation for the pytest_assertrepr_compare hook | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -44,20 +44,18 @@ | |
class AssertionRewritingHook(object): | ||
"""PEP302 Import hook which rewrites asserts.""" | ||
|
||
def __init__(self): | ||
def __init__(self, config): | ||
self.config = config | ||
self.fnpats = config.getini("python_files") | ||
self.session = None | ||
self.modules = {} | ||
self._register_with_pkg_resources() | ||
|
||
def set_session(self, session): | ||
self.fnpats = session.config.getini("python_files") | ||
self.session = session | ||
|
||
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 +84,11 @@ def find_module(self, name, path=None): | |
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this should be an explicit "return None" as the other returns are explicit too There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done, thanks |
||
|
||
# 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 +136,32 @@ def find_module(self, name, path=None): | |
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems superfluous as the function returns anyways, so There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i think the point is to remove There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't see a |
||
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, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this shouldn't be here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed, thanks!