|
|
|
@@ -1,5 +1,6 @@
|
|
|
|
|
import ast
|
|
|
|
|
import glob
|
|
|
|
|
import importlib
|
|
|
|
|
import os
|
|
|
|
|
import py_compile
|
|
|
|
|
import stat
|
|
|
|
@@ -117,6 +118,37 @@ class TestAssertionRewrite:
|
|
|
|
|
result = testdir.runpytest_subprocess()
|
|
|
|
|
assert "warnings" not in "".join(result.outlines)
|
|
|
|
|
|
|
|
|
|
def test_rewrites_plugin_as_a_package(self, testdir):
|
|
|
|
|
pkgdir = testdir.mkpydir("plugin")
|
|
|
|
|
pkgdir.join("__init__.py").write(
|
|
|
|
|
"import pytest\n"
|
|
|
|
|
"@pytest.fixture\n"
|
|
|
|
|
"def special_asserter():\n"
|
|
|
|
|
" def special_assert(x, y):\n"
|
|
|
|
|
" assert x == y\n"
|
|
|
|
|
" return special_assert\n"
|
|
|
|
|
)
|
|
|
|
|
testdir.makeconftest('pytest_plugins = ["plugin"]')
|
|
|
|
|
testdir.makepyfile("def test(special_asserter): special_asserter(1, 2)\n")
|
|
|
|
|
result = testdir.runpytest()
|
|
|
|
|
result.stdout.fnmatch_lines(["*assert 1 == 2*"])
|
|
|
|
|
|
|
|
|
|
def test_honors_pep_235(self, testdir, monkeypatch):
|
|
|
|
|
# note: couldn't make it fail on macos with a single `sys.path` entry
|
|
|
|
|
# note: these modules are named `test_*` to trigger rewriting
|
|
|
|
|
testdir.tmpdir.join("test_y.py").write("x = 1")
|
|
|
|
|
xdir = testdir.tmpdir.join("x").ensure_dir()
|
|
|
|
|
xdir.join("test_Y").ensure_dir().join("__init__.py").write("x = 2")
|
|
|
|
|
testdir.makepyfile(
|
|
|
|
|
"import test_y\n"
|
|
|
|
|
"import test_Y\n"
|
|
|
|
|
"def test():\n"
|
|
|
|
|
" assert test_y.x == 1\n"
|
|
|
|
|
" assert test_Y.x == 2\n"
|
|
|
|
|
)
|
|
|
|
|
monkeypatch.syspath_prepend(xdir)
|
|
|
|
|
testdir.runpytest().assert_outcomes(passed=1)
|
|
|
|
|
|
|
|
|
|
def test_name(self, request):
|
|
|
|
|
def f():
|
|
|
|
|
assert False
|
|
|
|
@@ -831,8 +863,9 @@ def test_rewritten():
|
|
|
|
|
monkeypatch.setattr(
|
|
|
|
|
hook, "_warn_already_imported", lambda code, msg: warnings.append(msg)
|
|
|
|
|
)
|
|
|
|
|
hook.find_module("test_remember_rewritten_modules")
|
|
|
|
|
hook.load_module("test_remember_rewritten_modules")
|
|
|
|
|
spec = hook.find_spec("test_remember_rewritten_modules")
|
|
|
|
|
module = importlib.util.module_from_spec(spec)
|
|
|
|
|
hook.exec_module(module)
|
|
|
|
|
hook.mark_rewrite("test_remember_rewritten_modules")
|
|
|
|
|
hook.mark_rewrite("test_remember_rewritten_modules")
|
|
|
|
|
assert warnings == []
|
|
|
|
@@ -872,33 +905,6 @@ def test_rewritten():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestAssertionRewriteHookDetails:
|
|
|
|
|
def test_loader_is_package_false_for_module(self, testdir):
|
|
|
|
|
testdir.makepyfile(
|
|
|
|
|
test_fun="""
|
|
|
|
|
def test_loader():
|
|
|
|
|
assert not __loader__.is_package(__name__)
|
|
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
result = testdir.runpytest()
|
|
|
|
|
result.stdout.fnmatch_lines(["* 1 passed*"])
|
|
|
|
|
|
|
|
|
|
def test_loader_is_package_true_for_package(self, testdir):
|
|
|
|
|
testdir.makepyfile(
|
|
|
|
|
test_fun="""
|
|
|
|
|
def test_loader():
|
|
|
|
|
assert not __loader__.is_package(__name__)
|
|
|
|
|
|
|
|
|
|
def test_fun():
|
|
|
|
|
assert __loader__.is_package('fun')
|
|
|
|
|
|
|
|
|
|
def test_missing():
|
|
|
|
|
assert not __loader__.is_package('pytest_not_there')
|
|
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
testdir.mkpydir("fun")
|
|
|
|
|
result = testdir.runpytest()
|
|
|
|
|
result.stdout.fnmatch_lines(["* 3 passed*"])
|
|
|
|
|
|
|
|
|
|
def test_sys_meta_path_munged(self, testdir):
|
|
|
|
|
testdir.makepyfile(
|
|
|
|
|
"""
|
|
|
|
@@ -917,7 +923,7 @@ class TestAssertionRewriteHookDetails:
|
|
|
|
|
state = AssertionState(config, "rewrite")
|
|
|
|
|
source_path = tmpdir.ensure("source.py")
|
|
|
|
|
pycpath = tmpdir.join("pyc").strpath
|
|
|
|
|
assert _write_pyc(state, [1], source_path.stat(), pycpath)
|
|
|
|
|
assert _write_pyc(state, [1], os.stat(source_path.strpath), pycpath)
|
|
|
|
|
|
|
|
|
|
@contextmanager
|
|
|
|
|
def atomic_write_failed(fn, mode="r", overwrite=False):
|
|
|
|
@@ -979,7 +985,7 @@ class TestAssertionRewriteHookDetails:
|
|
|
|
|
assert len(contents) > strip_bytes
|
|
|
|
|
pyc.write(contents[:strip_bytes], mode="wb")
|
|
|
|
|
|
|
|
|
|
assert _read_pyc(source, str(pyc)) is None # no error
|
|
|
|
|
assert _read_pyc(str(source), str(pyc)) is None # no error
|
|
|
|
|
|
|
|
|
|
def test_reload_is_same(self, testdir):
|
|
|
|
|
# A file that will be picked up during collecting.
|
|
|
|
@@ -1186,14 +1192,17 @@ def test_rewrite_infinite_recursion(testdir, pytestconfig, monkeypatch):
|
|
|
|
|
# make a note that we have called _write_pyc
|
|
|
|
|
write_pyc_called.append(True)
|
|
|
|
|
# try to import a module at this point: we should not try to rewrite this module
|
|
|
|
|
assert hook.find_module("test_bar") is None
|
|
|
|
|
assert hook.find_spec("test_bar") is None
|
|
|
|
|
return original_write_pyc(*args, **kwargs)
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(rewrite, "_write_pyc", spy_write_pyc)
|
|
|
|
|
monkeypatch.setattr(sys, "dont_write_bytecode", False)
|
|
|
|
|
|
|
|
|
|
hook = AssertionRewritingHook(pytestconfig)
|
|
|
|
|
assert hook.find_module("test_foo") is not None
|
|
|
|
|
spec = hook.find_spec("test_foo")
|
|
|
|
|
assert spec is not None
|
|
|
|
|
module = importlib.util.module_from_spec(spec)
|
|
|
|
|
hook.exec_module(module)
|
|
|
|
|
assert len(write_pyc_called) == 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1201,11 +1210,11 @@ class TestEarlyRewriteBailout:
|
|
|
|
|
@pytest.fixture
|
|
|
|
|
def hook(self, pytestconfig, monkeypatch, testdir):
|
|
|
|
|
"""Returns a patched AssertionRewritingHook instance so we can configure its initial paths and track
|
|
|
|
|
if imp.find_module has been called.
|
|
|
|
|
if PathFinder.find_spec has been called.
|
|
|
|
|
"""
|
|
|
|
|
import imp
|
|
|
|
|
import importlib.machinery
|
|
|
|
|
|
|
|
|
|
self.find_module_calls = []
|
|
|
|
|
self.find_spec_calls = []
|
|
|
|
|
self.initial_paths = set()
|
|
|
|
|
|
|
|
|
|
class StubSession:
|
|
|
|
@@ -1214,22 +1223,22 @@ class TestEarlyRewriteBailout:
|
|
|
|
|
def isinitpath(self, p):
|
|
|
|
|
return p in self._initialpaths
|
|
|
|
|
|
|
|
|
|
def spy_imp_find_module(name, path):
|
|
|
|
|
self.find_module_calls.append(name)
|
|
|
|
|
return imp.find_module(name, path)
|
|
|
|
|
def spy_find_spec(name, path):
|
|
|
|
|
self.find_spec_calls.append(name)
|
|
|
|
|
return importlib.machinery.PathFinder.find_spec(name, path)
|
|
|
|
|
|
|
|
|
|
hook = AssertionRewritingHook(pytestconfig)
|
|
|
|
|
# use default patterns, otherwise we inherit pytest's testing config
|
|
|
|
|
hook.fnpats[:] = ["test_*.py", "*_test.py"]
|
|
|
|
|
monkeypatch.setattr(hook, "_imp_find_module", spy_imp_find_module)
|
|
|
|
|
monkeypatch.setattr(hook, "_find_spec", spy_find_spec)
|
|
|
|
|
hook.set_session(StubSession())
|
|
|
|
|
testdir.syspathinsert()
|
|
|
|
|
return hook
|
|
|
|
|
|
|
|
|
|
def test_basic(self, testdir, hook):
|
|
|
|
|
"""
|
|
|
|
|
Ensure we avoid calling imp.find_module when we know for sure a certain module will not be rewritten
|
|
|
|
|
to optimize assertion rewriting (#3918).
|
|
|
|
|
Ensure we avoid calling PathFinder.find_spec when we know for sure a certain
|
|
|
|
|
module will not be rewritten to optimize assertion rewriting (#3918).
|
|
|
|
|
"""
|
|
|
|
|
testdir.makeconftest(
|
|
|
|
|
"""
|
|
|
|
@@ -1244,24 +1253,24 @@ class TestEarlyRewriteBailout:
|
|
|
|
|
self.initial_paths.add(foobar_path)
|
|
|
|
|
|
|
|
|
|
# conftest files should always be rewritten
|
|
|
|
|
assert hook.find_module("conftest") is not None
|
|
|
|
|
assert self.find_module_calls == ["conftest"]
|
|
|
|
|
assert hook.find_spec("conftest") is not None
|
|
|
|
|
assert self.find_spec_calls == ["conftest"]
|
|
|
|
|
|
|
|
|
|
# files matching "python_files" mask should always be rewritten
|
|
|
|
|
assert hook.find_module("test_foo") is not None
|
|
|
|
|
assert self.find_module_calls == ["conftest", "test_foo"]
|
|
|
|
|
assert hook.find_spec("test_foo") is not None
|
|
|
|
|
assert self.find_spec_calls == ["conftest", "test_foo"]
|
|
|
|
|
|
|
|
|
|
# file does not match "python_files": early bailout
|
|
|
|
|
assert hook.find_module("bar") is None
|
|
|
|
|
assert self.find_module_calls == ["conftest", "test_foo"]
|
|
|
|
|
assert hook.find_spec("bar") is None
|
|
|
|
|
assert self.find_spec_calls == ["conftest", "test_foo"]
|
|
|
|
|
|
|
|
|
|
# file is an initial path (passed on the command-line): should be rewritten
|
|
|
|
|
assert hook.find_module("foobar") is not None
|
|
|
|
|
assert self.find_module_calls == ["conftest", "test_foo", "foobar"]
|
|
|
|
|
assert hook.find_spec("foobar") is not None
|
|
|
|
|
assert self.find_spec_calls == ["conftest", "test_foo", "foobar"]
|
|
|
|
|
|
|
|
|
|
def test_pattern_contains_subdirectories(self, testdir, hook):
|
|
|
|
|
"""If one of the python_files patterns contain subdirectories ("tests/**.py") we can't bailout early
|
|
|
|
|
because we need to match with the full path, which can only be found by calling imp.find_module.
|
|
|
|
|
because we need to match with the full path, which can only be found by calling PathFinder.find_spec
|
|
|
|
|
"""
|
|
|
|
|
p = testdir.makepyfile(
|
|
|
|
|
**{
|
|
|
|
@@ -1273,8 +1282,8 @@ class TestEarlyRewriteBailout:
|
|
|
|
|
)
|
|
|
|
|
testdir.syspathinsert(p.dirpath())
|
|
|
|
|
hook.fnpats[:] = ["tests/**.py"]
|
|
|
|
|
assert hook.find_module("file") is not None
|
|
|
|
|
assert self.find_module_calls == ["file"]
|
|
|
|
|
assert hook.find_spec("file") is not None
|
|
|
|
|
assert self.find_spec_calls == ["file"]
|
|
|
|
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
|
|
|
sys.platform.startswith("win32"), reason="cannot remove cwd on Windows"
|
|
|
|
|