Tuesday, February 14, 2023

Dis This: Disassemble Python code online

When I was a lecturer at UC Berkeley teaching Python to thousands of students, I got asked all kinds of questions that got me digging deep into Python's innards. I soon discovered the dis module, which outputs the corresponding bytecode for a function or code segment. When students asked me the difference between various ways of writing the "same" code, I would often run the variations through the dis module to see if there was an underlying bytecode difference.

To see how dis works, consider this simple function:

def miles_to_km(miles):
    return miles * 1.609344

When we call dis.dis(miles_to_km), we see this output:

  1           0 RESUME                   0

  2           2 LOAD_FAST                0 (miles)
              4 LOAD_CONST               1 (1.609344)
              6 BINARY_OP                5 (*)
             10 RETURN_VALUE

The first (optional) number is the line number, then the offset, then the opcode name, then any opcode parameters, and optionally additional information to help interpret the parameters. I loved this output but found I was constantly trying to remember what each column represented and looking up the meaning of different op codes. I wanted to make disassembly more accessible for me and for others.

So I created dis-this.com, a website for disassembling Python code which includes an interactive hyperlinked output table plus the ability to permalink the output.

Screenshot of dis-this.com for miles_to_km example

The website uses Pyodide to execute the Python entirely in the browser, so that I don't have to run any backend or worry about executing arbitrary user code on a server. It also uses Lit for interactive elements, a library that wraps over Web Components, CodeMirror 6 for the editor, and RollUp for bundling up everything.

Since Pyodide has added support for 3.11 in its latest branch (not yet stable), the website optionally lets you enable the specializing adaptive interpreter. That's a new feature from the Faster CPython team that uses optimized bytecode operations for "hot" areas of code. If you check the box on dis-this.com, it will run the function 10 times and call dis.dis() with adaptive=True and show_caches=True.

For the example above, that results in a table with slightly different opcodes:

Screenshot of dis-this.com with miles_to_km example and specializing adaptive interpreter enabled

Try it out on some code and see for yourself! To learn more about the specializing adaptive interpreter, read the RealPython 3.11: New features article, PEP 659, or Python.org: What's New in Python 3.11.

No comments: