Skip to content

Commit

Permalink
merge PR192, streamline a bit.
Browse files Browse the repository at this point in the history
  • Loading branch information
hpk42 committed Aug 7, 2014
2 parents 3b8779a + cc092af commit d16fdb3
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 8 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ NEXT

- No longer show line numbers in the --verbose output, the output is now
purely the nodeid. The line number is still shown in failure reports.
Thanks Floris Bruynooghe.

- fix issue437 where assertion rewriting could cause pytest-xdist slaves
to collect different tests. Thanks Bruno Oliveira.

- fix issue547 capsys/capfd also work when output capturing ("-s") is disabled.

Expand Down
20 changes: 12 additions & 8 deletions _pytest/assertion/rewrite.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ def find_module(self, name, path=None):
pyc = os.path.join(cache_dir, cache_name)
# Notice that even if we're in a read-only directory, I'm going
# to check for a cached pyc. This may not be optimal...
co = _read_pyc(fn_pypath, pyc)
co = _read_pyc(fn_pypath, pyc, state.trace)
if co is None:
state.trace("rewriting %r" % (fn,))
co = _rewrite_test(state, fn_pypath)
Expand Down Expand Up @@ -289,7 +289,7 @@ def _make_rewritten_pyc(state, fn, pyc, co):
if _write_pyc(state, co, fn, proc_pyc):
os.rename(proc_pyc, pyc)

def _read_pyc(source, pyc):
def _read_pyc(source, pyc, trace=lambda x: None):
"""Possibly read a pytest pyc containing rewritten code.
Return rewritten code if successful or None if not.
Expand All @@ -298,23 +298,27 @@ def _read_pyc(source, pyc):
fp = open(pyc, "rb")
except IOError:
return None
try:
with fp:
try:
mtime = int(source.mtime())
data = fp.read(8)
except EnvironmentError:
except EnvironmentError as e:
trace('_read_pyc(%s): EnvironmentError %s' % (source, e))
return None
# Check for invalid or out of date pyc file.
if (len(data) != 8 or data[:4] != imp.get_magic() or
struct.unpack("<l", data[4:])[0] != mtime):
trace('_read_pyc(%s): invalid or out of date pyc' % source)
return None
try:
co = marshal.load(fp)
except Exception as e:
trace('_read_pyc(%s): marshal.load error %s' % (source, e))
return None
co = marshal.load(fp)
if not isinstance(co, types.CodeType):
# That's interesting....
trace('_read_pyc(%s): not a code object' % source)
return None
return co
finally:
fp.close()


def rewrite_asserts(mod):
Expand Down
22 changes: 22 additions & 0 deletions testing/test_assertrewrite.py
Original file line number Diff line number Diff line change
Expand Up @@ -539,3 +539,25 @@ def test_load_resource():
result.stdout.fnmatch_lines([
'* 1 passed*',
])

def test_read_pyc(self, tmpdir):
"""
Ensure that the `_read_pyc` can properly deal with corrupted pyc files.
In those circumstances it should just give up instead of generating
an exception that is propagated to the caller.
"""
import py_compile
from _pytest.assertion.rewrite import _read_pyc

source = tmpdir.join('source.py')
pyc = source + 'c'

source.write('def test(): pass')
py_compile.compile(str(source), str(pyc))

contents = pyc.read(mode='rb')
strip_bytes = 20 # header is around 8 bytes, strip a little more
assert len(contents) > strip_bytes
pyc.write(contents[:strip_bytes], mode='wb')

assert _read_pyc(source, str(pyc)) is None # no error

0 comments on commit d16fdb3

Please sign in to comment.