A Python .pyc decompiler that reconstructs readable source code from compiled bytecode. Built to correctly handle .pyc files from Python 3.9 and higher, regardless of which Python version you run the decompiler under. It is currently being actively tested for Python 3.9, 3.12, and 3.14.
Using pycrefine to decompile a Python bytecode .pyc file is straightforward for any end-user:
python pycrefine.py path/to/compiled_file.pycOr save the output to a file:
python pycrefine.py path/to/compiled_file.pyc -o decompiled_output.pyYou can optionally enhance output readability by adjusting the formatting with --beautification-level. The core level (default) gracefully flattens nested else/if blocks into single elif statements, while the aggressive level is reserved as a stub for future styling logic:
python pycrefine.py path/to/compiled_file.pyc --beautification-level coreThe script is entirely self-contained with no third-party dependencies required. It needs Python 3.9 or later to execute.
You can also import pycrefine as a library to programmatically decompile .pyc files directly into a string format:
import pycrefine
# Path to the compiled Python file
pyc_file = "path/to/compiled_file.pyc"
# Seamlessly load the appropriate decompiler for the given file
decompiler = pycrefine.get_decompiler(pyc_file)
# Reconstruct the source code into a single string
source_code = decompiler.decompile(beautification_level="core")
print(source_code)- Automatic Version Navigation: Reads the magic number from the
.pycheader and seamlessly routes execution to the appropriate decompiler logic. - Cross-Version Parsing: You can run
pycrefineon newer Python versions (e.g., 3.12) and perfectly parse a.pyccompiled by an older version (e.g., 3.9), avoiding any native bytecode incompatibility issues. - PEP 552 Support: Correctly processes both timestamp-based and hash-based
.pycheaders introduced dynamically in Python 3.7. - Dispatch Table Architecture: The decompiler engine uses a modular opcode dispatch mapping designed to simplify extending and maintaining opcode handlers across newer Python versions.
Original Source (example.py):
import os
def process(items, threshold=0):
result = []
for item in items:
if item > threshold:
result.append(item)
return resultDecompiled Output:
import os
def process(items, threshold=0):
result = []
for item in items:
if item > threshold:
result.append(item)
return resultFor developers looking to contribute, improve, or maintain pycrefine, the project includes dedicated tooling to ensure decompilation accuracy and prevent regressions during architecture upgrades.
The core test suite is located in the tests/ directory. It uses pytest and contains comprehensive unit tests partitioned across specific domains (e.g., test_basic.py, test_exceptions.py, test_control_flow.py) covering everything from basic statements to complex edge cases.
To run the full test suite from the project root:
pytest tests/ -vThis test suite strictly verifies the core mechanics of pycrefine, asserting its ability to restructure assignments, variables, data structures, exception handling blocks, functions, loops, and conditional chains.
Located at debug/check_coherency.py, this tool is an advanced decompilation coherency checker. It works by compiling an arbitrary Python source file to .pyc, decompiling it backward with pycrefine, and scoring how faithfully the decompiler reproduced the semantic and syntactic structure of the original source.
It performs a multi-dimensional analysis with scoring based on:
- Line and Token fidelity/recall
- Keyword density mapping
- Emitted artefacts and "garbage" penalty checking
Run a test against its own source (self-scoring):
python debug/check_coherency.py pycrefine.pyScore the coherency of any other python file, enabling verbose output to view the per-dimension grading statistics:
python debug/check_coherency.py path/to/any_file.py --verboseFor Continuous Integration pipelines, you can format the scoring output as JSON by passing the --json flag. The script predictably returns an exit code of 0 for passing scores (>= 70%), 1 for failing scores, and 2 for compilation/configuration errors.
These constraints are an inherent part of the stack-machine approach parsing strategy in CPython bytecode:
match/case(3.10+) — structural pattern matching opcodes are not natively supported.async/await— concurrent execution coroutine opcodes are not completely reconstructed.