Files
rea/docs/python-pyc.md
T

7.5 KiB
Raw Blame History

CPython pyc inspection

inspect_python_pyc / inspect-python-pyc reports the supported CPython format and version declared by one selected local .pyc file's magic, how its 16-byte header records source identity, and which nested code objects its decoded marshal graph contains. It reads the file without running bytecode and requires no Python installation, active binary target, Hopper, Ghidra, or IDA.

rea inspect-python-pyc /absolute/path/to/module.pyc --json

The CLI resolves relative paths against the caller's working directory. MCP requires an absolute filesystem path and preserves that exact selection:

{
  "name": "inspect_python_pyc",
  "arguments": { "path": "/artifacts/module.pyc" }
}

Observations

The inline Evidence result contains the artifact path, SHA-256, and byte length. header contains:

  • magic, the 16-bit little-endian field;
  • python_version and code_layout, mapped from the pinned CPython 3.7–3.13 table (py37, py38, or py311); 3.11 alpha magics before 3494 used other code layouts and are unsupported. The 3.7.0a1/a2 magics 3390/3391 used 12-byte headers and are outside this reader's supported format;
  • flags, hash_based, and check_source from PEP 552;
  • source_hash_hex for hash-based pycs, or source_mtime and source_size for timestamp pycs.

code_objects lists each code object reachable in the decoded marshal graph once, the top-level module code first. This follows CPython container behavior; it is not an inventory of raw serialized code-object records. nested_code holds the indexes of the distinct code objects reachable from that object's constants, including code nested in tuples and other containers and the owner itself when a constant container reaches it. A function shared through a marshal reference is named by index once per owner, even when that owner reaches it again through the same container. Relationships are collected after the marshal graph is complete, so a container that a nested code object also references is not summarized while its items are still missing. Each object reports name, optional qualname, filename, the signed 32-bit co_firstlineno value, argument counts, flags, names, locals (or localsplusnames on 3.11+), const summaries, and bytecode length plus SHA-256.

Code-object equality used by sets and dictionaries follows the selected CPython format: 3.7–3.10 derive CO_NOFREE from the free/cell tables for equality, while 3.11+ compare raw flags; raw co_flags remain in reported facts. The older layouts ignore line tables, while 3.11+ include line and exception tables and skip inline-cache code units according to the cache table for the exact magic. Reported bytecode length and SHA-256 still describe the original code bytes. This does not emulate CPython's deoptimization of specialized or instrumented opcode bytes in handcrafted pyc files. The artifact SHA-256 identifies the exact selected .pyc bytes independently of this decoded object graph. Reference slots follow each tag's CPython behavior: singleton values and TYP