Skip to main content
Reverse Engineering Module

Python Bytecode Disassembler (dis, in your browser)

Disassemble Python to CPython bytecode with the dis module — explore opcodes, code objects, jumps and constants. Runs in your browser; nothing uploaded.

Why use this tool?

Use the Bytecode Disassembler to see exactly what CPython turns your code into. Paste any Python — functions, classes, closures, comprehensions, f-strings — and it runs the standard-library dis module against the compiled code object and lays the instructions out interactively: colour-coded opcodes with plain-English explanations on hover, every nested code object as its own panel, followable jumps, and the constants/names/locals tables that reveal what a compiled file gives away.

It compiles your code but never runs it — compile(src, mode='exec') builds the code object, dis walks it statically, and everything happens in your browser through WebAssembly, so nothing is uploaded and no user code is executed.

Advertisement
Compiled, never run — nothing uploaded
Python source
def greet(names: list[str]) -> None:
    greeting = "Hello"
    for name in names:
        print(f"{greeting}, {name}!")


def make_counter(start=0):
    count = start
    def inc(step=1):
        nonlocal count
        count += step
        return count
    return inc


squares = [n * n for n in range(10) if n % 2 == 0]
Bytecode

Loading CPython 3.13 (WebAssembly)…

first load fetches the runtime; later runs are instant

Hover an instruction to highlight every op from the same source line; hover an opcode for what it does; click a jump to follow it. Switch the CPython version to watch the same code compile differently (list comprehensions were inlined in 3.12, RETURN_CONST arrived in 3.12, adaptive CACHE slots in 3.11).

The takeaway

Bytecode is not protection. Every string literal sits in co_consts in plain text and the logic is right there in the ops — which is exactly why a shipped .pyc gives your source away.

About Python Bytecode Disassembler

Every time you run a .py file, CPython compiles it to bytecode — a compact instruction set for its stack-based virtual machine — and it's that bytecode (cached as .pyc) the interpreter actually executes. The dis module is CPython's own disassembler: it turns those instructions back into a readable listing of opcodes, arguments, line numbers and jump targets. This tool runs dis in the browser via Pyodide, so you get the real CPython output for the version you pick (3.11, 3.12 or 3.13), not an approximation.

Because bytecode changes between versions, switching the CPython version shows the same source compiling differently — list and set comprehensions stopped creating a separate code object in 3.12, RETURN_CONST was added in 3.12, and 3.11 introduced the adaptive interpreter's inline CACHE slots (toggle 'Cache entries' to see them). Each function, lambda, comprehension and class body becomes its own code object, shown as a collapsible panel with its flags, stack size, constants, names and local variables.

The disassembly also makes a security point concretely: a code object stores every string and numeric literal in co_consts in plain text, and the control flow is right there in the opcodes. That's why shipping .pyc bytecode is not protection — the logic and secrets are trivially recoverable. If you need to protect code, obfuscate the source first with the AST obfuscator (and optionally compile to a native module), then the bytecode a reverse-engineer sees is meaningless. To go the other direction and rebuild source from a .pyc, use the .pyc Decompiler.

Frequently Asked Questions

It runs CPython's standard-library dis module on your code and shows the resulting bytecode: each opcode with its argument, source line and jump targets, grouped by code object (every function, lambda, comprehension and class body). It's the same output as running dis.dis() locally, but interactive — opcodes are colour-coded and explained on hover, and jumps are clickable.