merge PR192, streamline a bit.
This commit is contained in:
		
						commit
						d16fdb378c
					
				|  | @ -3,6 +3,10 @@ NEXT | ||||||
| 
 | 
 | ||||||
| - No longer show line numbers in the --verbose output, the output is now | - 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. |   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. | - fix issue547 capsys/capfd also work when output capturing ("-s") is disabled. | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -131,7 +131,7 @@ class AssertionRewritingHook(object): | ||||||
|         pyc = os.path.join(cache_dir, cache_name) |         pyc = os.path.join(cache_dir, cache_name) | ||||||
|         # Notice that even if we're in a read-only directory, I'm going |         # 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... |         # 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: |         if co is None: | ||||||
|             state.trace("rewriting %r" % (fn,)) |             state.trace("rewriting %r" % (fn,)) | ||||||
|             co = _rewrite_test(state, fn_pypath) |             co = _rewrite_test(state, fn_pypath) | ||||||
|  | @ -289,7 +289,7 @@ def _make_rewritten_pyc(state, fn, pyc, co): | ||||||
|         if _write_pyc(state, co, fn, proc_pyc): |         if _write_pyc(state, co, fn, proc_pyc): | ||||||
|             os.rename(proc_pyc, 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. |     """Possibly read a pytest pyc containing rewritten code. | ||||||
| 
 | 
 | ||||||
|     Return rewritten code if successful or None if not. |     Return rewritten code if successful or None if not. | ||||||
|  | @ -298,23 +298,27 @@ def _read_pyc(source, pyc): | ||||||
|         fp = open(pyc, "rb") |         fp = open(pyc, "rb") | ||||||
|     except IOError: |     except IOError: | ||||||
|         return None |         return None | ||||||
|     try: |     with fp: | ||||||
|         try: |         try: | ||||||
|             mtime = int(source.mtime()) |             mtime = int(source.mtime()) | ||||||
|             data = fp.read(8) |             data = fp.read(8) | ||||||
|         except EnvironmentError: |         except EnvironmentError as e: | ||||||
|  |             trace('_read_pyc(%s): EnvironmentError %s' % (source, e)) | ||||||
|             return None |             return None | ||||||
|         # Check for invalid or out of date pyc file. |         # Check for invalid or out of date pyc file. | ||||||
|         if (len(data) != 8 or data[:4] != imp.get_magic() or |         if (len(data) != 8 or data[:4] != imp.get_magic() or | ||||||
|                 struct.unpack("<l", data[4:])[0] != mtime): |                 struct.unpack("<l", data[4:])[0] != mtime): | ||||||
|  |             trace('_read_pyc(%s): invalid or out of date pyc' % source) | ||||||
|             return None |             return None | ||||||
|  |         try: | ||||||
|             co = marshal.load(fp) |             co = marshal.load(fp) | ||||||
|  |         except Exception as e: | ||||||
|  |             trace('_read_pyc(%s): marshal.load error %s' % (source, e)) | ||||||
|  |             return None | ||||||
|         if not isinstance(co, types.CodeType): |         if not isinstance(co, types.CodeType): | ||||||
|             # That's interesting.... |             trace('_read_pyc(%s): not a code object' % source) | ||||||
|             return None |             return None | ||||||
|         return co |         return co | ||||||
|     finally: |  | ||||||
|         fp.close() |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def rewrite_asserts(mod): | def rewrite_asserts(mod): | ||||||
|  |  | ||||||
|  | @ -539,3 +539,25 @@ class TestAssertionRewriteHookDetails(object): | ||||||
|         result.stdout.fnmatch_lines([ |         result.stdout.fnmatch_lines([ | ||||||
|             '* 1 passed*', |             '* 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 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue