summaryrefslogtreecommitdiff
path: root/src/_pytest/assertion
diff options
context:
space:
mode:
authorRan Benita <ran@unusedvar.com>2020-11-09 15:07:51 +0200
committerRan Benita <ran@unusedvar.com>2020-11-14 23:20:12 +0200
commit1d532da49ec7e25ff9d78509a61c3aa82a29b482 (patch)
treec96390e3a48501cf813fa040375cb473ff4ef9ea /src/_pytest/assertion
parente986d84466dfa98dbbc55cc1bf5fcb99075f4ac3 (diff)
downloadpytest-1d532da49ec7e25ff9d78509a61c3aa82a29b482.tar.gz
assertion/rewrite: write pyc's according to PEP-552 on Python>=3.7
Python 3.7 changes the pyc format by adding a flags byte. Even though it is not necessary for us to match it, it is nice to be able to read pyc files we emit for debugging the rewriter. Update our custom pyc files to use that format. We write flags==0 meaning we still use the mtime+size format rather the newer hash format.
Diffstat (limited to 'src/_pytest/assertion')
-rw-r--r--src/_pytest/assertion/rewrite.py34
1 files changed, 25 insertions, 9 deletions
diff --git a/src/_pytest/assertion/rewrite.py b/src/_pytest/assertion/rewrite.py
index 649726727..805d4c8b3 100644
--- a/src/_pytest/assertion/rewrite.py
+++ b/src/_pytest/assertion/rewrite.py
@@ -281,12 +281,16 @@ def _write_pyc_fp(
) -> None:
# Technically, we don't have to have the same pyc format as
# (C)Python, since these "pycs" should never be seen by builtin
- # import. However, there's little reason deviate.
+ # import. However, there's little reason to deviate.
fp.write(importlib.util.MAGIC_NUMBER)
+ # https://www.python.org/dev/peps/pep-0552/
+ if sys.version_info >= (3, 7):
+ flags = b"\x00\x00\x00\x00"
+ fp.write(flags)
# as of now, bytecode header expects 32-bit numbers for size and mtime (#4903)
mtime = int(source_stat.st_mtime) & 0xFFFFFFFF
size = source_stat.st_size & 0xFFFFFFFF
- # "<LL" stands for 2 unsigned longs, little-ending
+ # "<LL" stands for 2 unsigned longs, little-endian.
fp.write(struct.pack("<LL", mtime, size))
fp.write(marshal.dumps(co))
@@ -365,21 +369,33 @@ def _read_pyc(
except OSError:
return None
with fp:
+ # https://www.python.org/dev/peps/pep-0552/
+ has_flags = sys.version_info >= (3, 7)
try:
stat_result = os.stat(os.fspath(source))
mtime = int(stat_result.st_mtime)
size = stat_result.st_size
- data = fp.read(12)
+ data = fp.read(16 if has_flags else 12)
except OSError as e:
trace(f"_read_pyc({source}): OSError {e}")
return None
# Check for invalid or out of date pyc file.
- if (
- len(data) != 12
- or data[:4] != importlib.util.MAGIC_NUMBER
- or struct.unpack("<LL", data[4:]) != (mtime & 0xFFFFFFFF, size & 0xFFFFFFFF)
- ):
- trace("_read_pyc(%s): invalid or out of date pyc" % source)
+ if len(data) != (16 if has_flags else 12):
+ trace("_read_pyc(%s): invalid pyc (too short)" % source)
+ return None
+ if data[:4] != importlib.util.MAGIC_NUMBER:
+ trace("_read_pyc(%s): invalid pyc (bad magic number)" % source)
+ return None
+ if has_flags and data[4:8] != b"\x00\x00\x00\x00":
+ trace("_read_pyc(%s): invalid pyc (unsupported flags)" % source)
+ return None
+ mtime_data = data[8 if has_flags else 4 : 12 if has_flags else 8]
+ if int.from_bytes(mtime_data, "little") != mtime & 0xFFFFFFFF:
+ trace("_read_pyc(%s): out of date" % source)
+ return None
+ size_data = data[12 if has_flags else 8 : 16 if has_flags else 12]
+ if int.from_bytes(size_data, "little") != size & 0xFFFFFFFF:
+ trace("_read_pyc(%s): invalid pyc (incorrect size)" % source)
return None
try:
co = marshal.load(fp)