diff --git a/AUTHORS b/AUTHORS index 6de0a112b7d..8cc6824cd95 100644 --- a/AUTHORS +++ b/AUTHORS @@ -101,6 +101,7 @@ Michael Birtwell Michael Droettboom Michael Seifert Mike Lundy +Ned Batchelder Nicolas Delaby Oleg Pidsadnyi Oliver Bestwalter diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 60875f4c4ee..b87302e8cf6 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -5,12 +5,17 @@ * -* +* Cope gracefully with a .pyc file with no matching .py file (`#2038`_). Thanks + `@nedbat`_. * * +.. _@nedbat: https://github.com/nedbat + +.. _#2038: https://github.com/pytest-dev/pytest/issues/2038 + 3.0.4 ===== diff --git a/_pytest/assertion/rewrite.py b/_pytest/assertion/rewrite.py index 6b4c1f48357..9136c386741 100644 --- a/_pytest/assertion/rewrite.py +++ b/_pytest/assertion/rewrite.py @@ -80,7 +80,12 @@ def find_module(self, name, path=None): tp = desc[2] if tp == imp.PY_COMPILED: if hasattr(imp, "source_from_cache"): - fn = imp.source_from_cache(fn) + try: + fn = imp.source_from_cache(fn) + except ValueError: + # Python 3 doesn't like orphaned but still-importable + # .pyc files. + fn = fn[:-1] else: fn = fn[:-1] elif tp != imp.PY_SOURCE: diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index e72266a18d7..ded11cbb4b0 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -1,7 +1,10 @@ +import glob import os +import py_compile import stat import sys import zipfile + import py import pytest @@ -480,6 +483,31 @@ def test_no_bytecode(): monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", "1") assert testdir.runpytest_subprocess().ret == 0 + def test_orphaned_pyc_file(self, testdir): + if sys.version_info < (3, 0) and hasattr(sys, 'pypy_version_info'): + pytest.skip("pypy2 doesn't run orphaned pyc files") + + testdir.makepyfile(""" + import orphan + def test_it(): + assert orphan.value == 17 + """) + testdir.makepyfile(orphan=""" + value = 17 + """) + py_compile.compile("orphan.py") + os.remove("orphan.py") + + # Python 3 puts the .pyc files in a __pycache__ directory, and will + # not import from there without source. It will import a .pyc from + # the source location though. + if not os.path.exists("orphan.pyc"): + pycs = glob.glob("__pycache__/orphan.*.pyc") + assert len(pycs) == 1 + os.rename(pycs[0], "orphan.pyc") + + assert testdir.runpytest().ret == 0 + @pytest.mark.skipif('"__pypy__" in sys.modules') def test_pyc_vs_pyo(self, testdir, monkeypatch): testdir.makepyfile("""