Python’s opcode module exposes the CPython bytecode opcode table — the mapping between mnemonic names and their numeric byte values. import opcode. Lookup: opcode.opmap["LOAD_FAST"] → int; opcode.opname[116] → "LOAD_FAST". Argument groups: opcode.hasconst, opcode.haslocal, opcode.hasfree, opcode.hasname, opcode.hasjrel, opcode.hasjabs, opcode.hascompare, opcode.hasjump — each is a list of opcode ints. Has-argument threshold: opcode.HAVE_ARGUMENT (= 90 in CPython 3.11+; opcodes ≥ this value take a 1-byte argument). Stack effect: opcode.stack_effect(op_int, arg=None) → net stack delta (positive = push, negative = pop). Comparison operators: opcode.cmp_op[i] — the string operator name for a COMPARE_OP argument. Extended args: opcode.EXTENDED_ARG is the opcode that prefixes multi-byte arguments; each EXTENDED_ARG shifts the real arg left by 8 bits. Claude Code generates bytecode analyzers, opcode frequency profilers, dead code detectors, stack depth validators, and custom Python-to-Python compilers.
CLAUDE.md for opcode
## opcode Stack
- Stdlib: import opcode, dis, types
- Lookup: opcode.opmap["LOAD_CONST"] # name → int
- opcode.opname[100] # int → name
- Groups: opcode.hasconst / haslocal / hasfree / hasname
- opcode.hasjrel / hasjabs / hascompare
- Effect: opcode.stack_effect(op, arg) # net stack delta
- Arg?: op >= opcode.HAVE_ARGUMENT # True if op takes an arg
- Cmp: opcode.cmp_op[i] # e.g. "<", "==", "in"
- ExtArg: opcode.EXTENDED_ARG # multi-byte arg prefix opcode
opcode Bytecode Analysis Pipeline
# app/opcodeutil.py — loader, freqs, effects, dead-code, stack validator, disassembler
from __future__ import annotations
import dis
import opcode
import types
from collections import Counter
from dataclasses import dataclass, field
from typing import Any
# ─────────────────────────────────────────────────────────────────────────────
# 1. Basic opcode info helpers
# ─────────────────────────────────────────────────────────────────────────────
def opcode_name(op: int) -> str:
"""
Return the mnemonic name for a numeric opcode.
Example:
opcode_name(100) # "LOAD_CONST"
opcode_name(116) # "LOAD_FAST" (varies by Python version)
"""
return opcode.opname[op] if 0 <= op < len(opcode.opname) else f"<{op}>"
def opcode_int(name: str) -> int | None:
"""
Return the numeric opcode for a mnemonic name, or None if unknown.
Example:
opcode_int("LOAD_FAST") # 124
opcode_int("BOGUS") # None
"""
return opcode.opmap.get(name)
def takes_arg(op: int) -> bool:
"""Return True if this opcode takes an inline argument."""
return op >= opcode.HAVE_ARGUMENT
def stack_delta(op: int, arg: int | None = None) -> int:
"""
Return the net stack depth change for opcode op with the given arg.
Example:
stack_delta(opcode.opmap["LOAD_CONST"]) # +1
stack_delta(opcode.opmap["POP_TOP"]) # -1
"""
return opcode.stack_effect(op, arg)
def classify_opcode(op: int) -> list[str]:
"""
Return a list of classification tags for an opcode.
Tags: 'const', 'local', 'free', 'name', 'jrel', 'jabs', 'compare', 'jump'.
Example:
classify_opcode(opcode.opmap["LOAD_FAST"]) # ['local']
classify_opcode(opcode.opmap["FOR_ITER"]) # ['jrel', 'jump']
"""
tags: list[str] = []
if op in opcode.hasconst: tags.append("const")
if op in opcode.haslocal: tags.append("local")
if op in opcode.hasfree: tags.append("free")
if op in opcode.hasname: tags.append("name")
if op in opcode.hasjrel: tags.append("jrel")
if op in opcode.hasjabs: tags.append("jabs")
if op in opcode.hascompare: tags.append("compare")
if hasattr(opcode, "hasjump") and op in opcode.hasjump:
tags.append("jump")
return tags
# ─────────────────────────────────────────────────────────────────────────────
# 2. Instruction stream decoder
# ─────────────────────────────────────────────────────────────────────────────
@dataclass
class Instruction:
offset: int
op: int
name: str
arg: int | None
arg_repr: str = ""
tags: list[str] = field(default_factory=list)
stack_effect: int = 0
def decode_bytecode(code: types.CodeType) -> list[Instruction]:
"""
Decode a code object into a list of Instruction records.
Handles EXTENDED_ARG prefixes for multi-byte arguments.
Example:
import dis, types
co = compile("x = 1 + 2", "<string>", "exec")
instrs = decode_bytecode(co)
for i in instrs:
print(i.offset, i.name, i.arg)
"""
instructions: list[Instruction] = []
raw = code.co_code if hasattr(code, "co_code") else bytes(code.co_code)
extended_arg = 0
i = 0
while i < len(raw):
op = raw[i]
if op == opcode.EXTENDED_ARG:
arg_byte = raw[i + 1] if i + 1 < len(raw) else 0
extended_arg = (extended_arg | arg_byte) << 8
i += 2
continue
if op >= opcode.HAVE_ARGUMENT:
arg_byte = raw[i + 1] if i + 1 < len(raw) else 0
arg = extended_arg | arg_byte
else:
arg = None
extended_arg = 0
name = opcode.opname[op]
# Resolve arg to human-readable repr
arg_repr = ""
if op in opcode.hasconst and arg is not None:
try:
arg_repr = repr(code.co_consts[arg])
except IndexError:
arg_repr = f"const[{arg}]"
elif op in opcode.haslocal and arg is not None:
try:
arg_repr = code.co_varnames[arg]
except IndexError:
arg_repr = f"var[{arg}]"
elif op in opcode.hasname and arg is not None:
try:
arg_repr = code.co_names[arg]
except IndexError:
arg_repr = f"name[{arg}]"
elif op in opcode.hasfree and arg is not None:
free_vars = (getattr(code, "co_cellvars", ()) +
getattr(code, "co_freevars", ()))
try:
arg_repr = free_vars[arg]
except IndexError:
arg_repr = f"free[{arg}]"
elif op in opcode.hascompare and arg is not None:
try:
arg_repr = opcode.cmp_op[arg]
except IndexError:
arg_repr = str(arg)
elif arg is not None:
arg_repr = str(arg)
try:
effect = opcode.stack_effect(op, arg)
except (TypeError, ValueError):
effect = 0
instructions.append(Instruction(
offset=i,
op=op,
name=name,
arg=arg,
arg_repr=arg_repr,
tags=classify_opcode(op),
stack_effect=effect,
))
i += 2
return instructions
# ─────────────────────────────────────────────────────────────────────────────
# 3. Opcode frequency profiler
# ─────────────────────────────────────────────────────────────────────────────
def opcode_frequencies(code: types.CodeType,
recursive: bool = True) -> Counter[str]:
"""
Count how many times each opcode name appears in a code object.
If recursive=True, also counts opcodes in nested code objects
(functions, comprehensions, classes).
Example:
co = compile(open("script.py").read(), "script.py", "exec")
freq = opcode_frequencies(co)
for name, count in freq.most_common(10):
print(f" {name:25s} {count}")
"""
freq: Counter[str] = Counter()
instrs = decode_bytecode(code)
for instr in instrs:
freq[instr.name] += 1
if recursive:
for const in code.co_consts:
if isinstance(const, types.CodeType):
freq.update(opcode_frequencies(const, recursive=True))
return freq
# ─────────────────────────────────────────────────────────────────────────────
# 4. Stack depth validator
# ─────────────────────────────────────────────────────────────────────────────
@dataclass
class StackReport:
max_depth: int
min_depth: int
underflows: list[int] # offsets where depth would go negative
ok: bool
def validate_stack_depth(code: types.CodeType) -> StackReport:
"""
Walk the instruction stream linearly and track net stack depth.
Flags offsets where the stack would underflow.
Note: this is a linear (not control-flow-aware) approximation.
Example:
co = compile("x = [i*i for i in range(10)]", "<s>", "exec")
report = validate_stack_depth(co)
print(report.max_depth, report.ok)
"""
instrs = decode_bytecode(code)
depth = 0
max_depth = 0
min_depth = 0
underflows: list[int] = []
for instr in instrs:
depth += instr.stack_effect
if depth < 0:
underflows.append(instr.offset)
max_depth = max(max_depth, depth)
min_depth = min(min_depth, depth)
return StackReport(
max_depth=max_depth,
min_depth=min_depth,
underflows=underflows,
ok=len(underflows) == 0,
)
# ─────────────────────────────────────────────────────────────────────────────
# 5. All-opcodes table printer
# ─────────────────────────────────────────────────────────────────────────────
def print_opcode_table(filter_tag: str | None = None) -> None:
"""
Print a table of all known opcodes with their numeric value and tags.
Pass filter_tag to narrow to e.g. 'local', 'const', 'jump'.
Example:
print_opcode_table()
print_opcode_table("const")
"""
print(f"{'Op':>4} {'Name':30s} {'Tags'}")
print("-" * 60)
for name, op in sorted(opcode.opmap.items(), key=lambda x: x[1]):
tags = classify_opcode(op)
if filter_tag and filter_tag not in tags:
continue
tag_str = ", ".join(tags) if tags else "—"
print(f"{op:4d} {name:30s} {tag_str}")
# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────
if __name__ == "__main__":
print("=== opcode demo ===")
# ── basic lookups ─────────────────────────────────────────────────────────
print("\n--- basic lookups ---")
for name in ["LOAD_CONST", "LOAD_FAST", "CALL_FUNCTION",
"RETURN_VALUE", "POP_TOP"]:
op = opcode.opmap.get(name)
if op is None:
print(f" {name}: not in this Python version")
continue
delta = stack_delta(op)
has_arg = takes_arg(op)
tags = classify_opcode(op)
print(f" {name:25s} op={op:3d} arg={has_arg} "
f"Δstack={delta:+d} tags={tags}")
# ── decode a simple code object ───────────────────────────────────────────
print("\n--- decode compile('x = a + b', ...) ---")
src = "x = a + b"
co = compile(src, "<demo>", "exec")
instrs = decode_bytecode(co)
for instr in instrs:
arg_s = f"{instr.arg_repr}" if instr.arg_repr else ""
print(f" {instr.offset:4d} {instr.name:25s} {arg_s}")
# ── opcode frequency ──────────────────────────────────────────────────────
print("\n--- opcode_frequencies (list comp) ---")
co2 = compile("[x*x for x in range(100)]", "<demo>", "eval")
freq = opcode_frequencies(co2)
for name, count in freq.most_common(8):
print(f" {name:25s} {count}")
# ── stack depth validator ─────────────────────────────────────────────────
print("\n--- stack depth validator ---")
co3 = compile("y = [i**2 for i in range(10) if i % 2 == 0]", "<s>", "exec")
report = validate_stack_depth(co3)
print(f" max_depth={report.max_depth} min_depth={report.min_depth} "
f"ok={report.ok} underflows={report.underflows}")
# ── compare ops ───────────────────────────────────────────────────────────
print("\n--- cmp_op table ---")
for i, name in enumerate(opcode.cmp_op):
print(f" [{i}] {name}")
print("\n=== done ===")
For the dis stdlib alternative — dis.get_instructions(code) returns dis.Instruction named-tuples with opname, opcode, arg, argval, argrepr, offset, starts_line, and is_jump_target — use dis.get_instructions() when you need a high-level, human-readable instruction stream with jump analysis; use opcode directly when you need the raw numeric constants, stack effect computation, or opcode classification tables to build your own bytecode tools. For the bytecode (PyPI) alternative — the bytecode library provides Bytecode, ConcreteInstr, and ControlFlowGraph to read, transform, and rewrite CPython bytecode at a high level — use bytecode when you are writing a bytecode optimizer or code transformer that needs control flow graph operations; use opcode + dis for read-only analysis and introspection. The Claude Skills 360 bundle includes opcode skill sets covering opcode_name()/opcode_int()/takes_arg()/stack_delta() primitives, classify_opcode() tag classifier, Instruction dataclass + decode_bytecode() decoder, opcode_frequencies() recursive counter, validate_stack_depth()/StackReport linear validator, and print_opcode_table() filter printer. Start with the free tier to try CPython bytecode patterns and opcode pipeline code generation.