Skip to content

Commit e766820

Browse files
committed
feat: add WIP interpreter internals and bytecode analysis tools
- Add bytecode inspector with disassembly analysis - Add interpreter internals documentation and code - Add lambda AST explorer and bytecode analysis - Include supporting documentation files These are work-in-progress tools for understanding Python's internal execution model and bytecode generation. Co-Authored-By: Claude <noreply@anthropic.com>
1 parent d591053 commit e766820

7 files changed

Lines changed: 3035 additions & 0 deletions

bytecode-inspector.org

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#+TITLE: Python Bytecode Inspector
2+
#+AUTHOR: jwalsh
3+
#+DATE: [2025-04-04]
4+
#+PROPERTY: header-args:python :tangle bytecode_inspector.py :mkdirp t
5+
6+
* Python Bytecode Inspector
7+
8+
This tool compiles Python files to bytecode and allows inspection of the generated bytecode.
9+
10+
** Implementation
11+
12+
#+begin_src python
13+
# bytecode_inspector.py code here...
14+
#+end_src
15+
16+
** Usage Examples
17+
18+
*** Compile and inspect closures.py
19+
#+begin_src shell :results output
20+
python bytecode_inspector.py closures.py
21+
#+end_src
22+
23+
*** Inspect existing bytecode without recompiling
24+
#+begin_src shell :results output
25+
python bytecode_inspector.py closures.py --no-compile
26+
#+end_src
27+
28+
*** Batch process multiple files
29+
#+begin_src shell :results output
30+
for file in *.py; do
31+
echo "Processing $file..."
32+
python bytecode_inspector.py "$file"
33+
done
34+
#+end_src
35+
36+
** Mermaid Diagram: Bytecode Inspection Process
37+
38+
#+begin_src mermaid :file bytecode_process.png
39+
flowchart TD
40+
A[Python Source File] -->|py_compile| B[.pyc File]
41+
B -->|importlib| C[Load Module]
42+
B -->|direct read| D[Read Raw Bytecode]
43+
C -->|dis.dis| E[Disassemble Functions]
44+
D -->|marshal.load| F[Load Code Object]
45+
F -->|dis.dis| G[Disassemble Raw Bytecode]
46+
E --> H[Bytecode Analysis]
47+
G --> H
48+
#+end_src
49+
50+
* Future Enhancements
51+
- Add comparison between different Python versions
52+
- Visualize bytecode execution flow
53+
- Analyze optimization opportunities

bytecode_inspector.py

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# bytecode_inspector.py
2+
3+
import py_compile
4+
import dis
5+
import os
6+
import marshal
7+
import importlib.util
8+
import argparse
9+
from pathlib import Path
10+
11+
def compile_to_bytecode(file_path):
12+
"""Compile a Python file to bytecode and return the path to the .pyc file"""
13+
try:
14+
py_compile.compile(file_path)
15+
print(f"Successfully compiled {file_path}")
16+
17+
# Find the compiled .pyc file
18+
cache_dir = '__pycache__'
19+
base_name = os.path.basename(file_path)
20+
module_name = os.path.splitext(base_name)[0]
21+
22+
if os.path.exists(cache_dir):
23+
pyc_files = [f for f in os.listdir(cache_dir) if f.startswith(f"{module_name}.")]
24+
if pyc_files:
25+
return os.path.join(cache_dir, pyc_files[0])
26+
except Exception as e:
27+
print(f"Error compiling {file_path}: {e}")
28+
29+
return None
30+
31+
def inspect_bytecode(pyc_path):
32+
"""Disassemble and inspect a .pyc file"""
33+
if not pyc_path or not os.path.exists(pyc_path):
34+
print("No valid .pyc file found")
35+
return
36+
37+
try:
38+
# Load the module from the .pyc file
39+
spec = importlib.util.spec_from_file_location("module.name", pyc_path)
40+
if spec:
41+
module = importlib.util.module_from_spec(spec)
42+
spec.loader.exec_module(module)
43+
44+
# Print all functions/classes and their bytecode
45+
for name, obj in module.__dict__.items():
46+
if callable(obj) and not name.startswith('__'):
47+
print(f"\n{'='*50}")
48+
print(f"Disassembly of {name}:")
49+
print(f"{'='*50}")
50+
dis.dis(obj)
51+
52+
# Directly examine the bytecode
53+
print(f"\n{'='*50}")
54+
print(f"Raw bytecode from {pyc_path}:")
55+
print(f"{'='*50}")
56+
with open(pyc_path, 'rb') as pyc_file:
57+
# Skip the magic number and timestamp/size
58+
pyc_file.read(16) # 4 bytes magic + 4 bytes timestamp + 4 bytes size + 4 bytes hash
59+
60+
# Load the code object
61+
try:
62+
code_obj = marshal.load(pyc_file)
63+
dis.dis(code_obj)
64+
except Exception as e:
65+
print(f"Error loading code object: {e}")
66+
67+
except Exception as e:
68+
print(f"Error inspecting {pyc_path}: {e}")
69+
70+
def main():
71+
parser = argparse.ArgumentParser(description='Compile Python files to bytecode and inspect them')
72+
parser.add_argument('file', help='Python file to compile and inspect')
73+
parser.add_argument('--no-compile', action='store_true',
74+
help='Inspect existing .pyc without recompiling')
75+
76+
args = parser.parse_args()
77+
78+
file_path = args.file
79+
if not os.path.exists(file_path):
80+
print(f"File not found: {file_path}")
81+
return
82+
83+
if args.no_compile:
84+
# Try to find existing .pyc file
85+
cache_dir = '__pycache__'
86+
base_name = os.path.basename(file_path)
87+
module_name = os.path.splitext(base_name)[0]
88+
89+
if os.path.exists(cache_dir):
90+
pyc_files = [f for f in os.listdir(cache_dir) if f.startswith(f"{module_name}.")]
91+
if pyc_files:
92+
pyc_path = os.path.join(cache_dir, pyc_files[0])
93+
print(f"Using existing .pyc file: {pyc_path}")
94+
inspect_bytecode(pyc_path)
95+
else:
96+
print(f"No existing .pyc file found for {file_path}")
97+
else:
98+
pyc_path = compile_to_bytecode(file_path)
99+
if pyc_path:
100+
print(f"Compiled to: {pyc_path}")
101+
inspect_bytecode(pyc_path)
102+
103+
if __name__ == "__main__":
104+
main()

0 commit comments

Comments
 (0)