Possibly read a pytest pyc containing rewritten code. Return rewritten code if successful or None if not.
(
source: Path, pyc: Path, trace: Callable[[str], None] = lambda x: None
)
| 366 | |
| 367 | |
| 368 | def _read_pyc( |
| 369 | source: Path, pyc: Path, trace: Callable[[str], None] = lambda x: None |
| 370 | ) -> Optional[types.CodeType]: |
| 371 | """Possibly read a pytest pyc containing rewritten code. |
| 372 | |
| 373 | Return rewritten code if successful or None if not. |
| 374 | """ |
| 375 | try: |
| 376 | fp = open(pyc, "rb") |
| 377 | except OSError: |
| 378 | return None |
| 379 | with fp: |
| 380 | # https://www.python.org/dev/peps/pep-0552/ |
| 381 | has_flags = sys.version_info >= (3, 7) |
| 382 | try: |
| 383 | stat_result = os.stat(source) |
| 384 | mtime = int(stat_result.st_mtime) |
| 385 | size = stat_result.st_size |
| 386 | data = fp.read(16 if has_flags else 12) |
| 387 | except OSError as e: |
| 388 | trace(f"_read_pyc({source}): OSError {e}") |
| 389 | return None |
| 390 | # Check for invalid or out of date pyc file. |
| 391 | if len(data) != (16 if has_flags else 12): |
| 392 | trace("_read_pyc(%s): invalid pyc (too short)" % source) |
| 393 | return None |
| 394 | if data[:4] != importlib.util.MAGIC_NUMBER: |
| 395 | trace("_read_pyc(%s): invalid pyc (bad magic number)" % source) |
| 396 | return None |
| 397 | if has_flags and data[4:8] != b"\x00\x00\x00\x00": |
| 398 | trace("_read_pyc(%s): invalid pyc (unsupported flags)" % source) |
| 399 | return None |
| 400 | mtime_data = data[8 if has_flags else 4 : 12 if has_flags else 8] |
| 401 | if int.from_bytes(mtime_data, "little") != mtime & 0xFFFFFFFF: |
| 402 | trace("_read_pyc(%s): out of date" % source) |
| 403 | return None |
| 404 | size_data = data[12 if has_flags else 8 : 16 if has_flags else 12] |
| 405 | if int.from_bytes(size_data, "little") != size & 0xFFFFFFFF: |
| 406 | trace("_read_pyc(%s): invalid pyc (incorrect size)" % source) |
| 407 | return None |
| 408 | try: |
| 409 | co = marshal.load(fp) |
| 410 | except Exception as e: |
| 411 | trace(f"_read_pyc({source}): marshal.load error {e}") |
| 412 | return None |
| 413 | if not isinstance(co, types.CodeType): |
| 414 | trace("_read_pyc(%s): not a code object" % source) |
| 415 | return None |
| 416 | return co |
| 417 | |
| 418 | |
| 419 | def rewrite_asserts( |