Skip to content

Commit d16fdb3

Browse files
committed
merge PR192, streamline a bit.
2 parents 3b8779a + cc092af commit d16fdb3

File tree

3 files changed

+38
-8
lines changed

3 files changed

+38
-8
lines changed

CHANGELOG

+4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ NEXT
33

44
- No longer show line numbers in the --verbose output, the output is now
55
purely the nodeid. The line number is still shown in failure reports.
6+
Thanks Floris Bruynooghe.
7+
8+
- fix issue437 where assertion rewriting could cause pytest-xdist slaves
9+
to collect different tests. Thanks Bruno Oliveira.
610

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

_pytest/assertion/rewrite.py

+12-8
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ def find_module(self, name, path=None):
131131
pyc = os.path.join(cache_dir, cache_name)
132132
# Notice that even if we're in a read-only directory, I'm going
133133
# to check for a cached pyc. This may not be optimal...
134-
co = _read_pyc(fn_pypath, pyc)
134+
co = _read_pyc(fn_pypath, pyc, state.trace)
135135
if co is None:
136136
state.trace("rewriting %r" % (fn,))
137137
co = _rewrite_test(state, fn_pypath)
@@ -289,7 +289,7 @@ def _make_rewritten_pyc(state, fn, pyc, co):
289289
if _write_pyc(state, co, fn, proc_pyc):
290290
os.rename(proc_pyc, pyc)
291291

292-
def _read_pyc(source, pyc):
292+
def _read_pyc(source, pyc, trace=lambda x: None):
293293
"""Possibly read a pytest pyc containing rewritten code.
294294
295295
Return rewritten code if successful or None if not.
@@ -298,23 +298,27 @@ def _read_pyc(source, pyc):
298298
fp = open(pyc, "rb")
299299
except IOError:
300300
return None
301-
try:
301+
with fp:
302302
try:
303303
mtime = int(source.mtime())
304304
data = fp.read(8)
305-
except EnvironmentError:
305+
except EnvironmentError as e:
306+
trace('_read_pyc(%s): EnvironmentError %s' % (source, e))
306307
return None
307308
# Check for invalid or out of date pyc file.
308309
if (len(data) != 8 or data[:4] != imp.get_magic() or
309310
struct.unpack("<l", data[4:])[0] != mtime):
311+
trace('_read_pyc(%s): invalid or out of date pyc' % source)
312+
return None
313+
try:
314+
co = marshal.load(fp)
315+
except Exception as e:
316+
trace('_read_pyc(%s): marshal.load error %s' % (source, e))
310317
return None
311-
co = marshal.load(fp)
312318
if not isinstance(co, types.CodeType):
313-
# That's interesting....
319+
trace('_read_pyc(%s): not a code object' % source)
314320
return None
315321
return co
316-
finally:
317-
fp.close()
318322

319323

320324
def rewrite_asserts(mod):

testing/test_assertrewrite.py

+22
Original file line numberDiff line numberDiff line change
@@ -539,3 +539,25 @@ def test_load_resource():
539539
result.stdout.fnmatch_lines([
540540
'* 1 passed*',
541541
])
542+
543+
def test_read_pyc(self, tmpdir):
544+
"""
545+
Ensure that the `_read_pyc` can properly deal with corrupted pyc files.
546+
In those circumstances it should just give up instead of generating
547+
an exception that is propagated to the caller.
548+
"""
549+
import py_compile
550+
from _pytest.assertion.rewrite import _read_pyc
551+
552+
source = tmpdir.join('source.py')
553+
pyc = source + 'c'
554+
555+
source.write('def test(): pass')
556+
py_compile.compile(str(source), str(pyc))
557+
558+
contents = pyc.read(mode='rb')
559+
strip_bytes = 20 # header is around 8 bytes, strip a little more
560+
assert len(contents) > strip_bytes
561+
pyc.write(contents[:strip_bytes], mode='wb')
562+
563+
assert _read_pyc(source, str(pyc)) is None # no error

0 commit comments

Comments
 (0)