Claude Code for dis: Python Bytecode Disassembly — Claude Skills 360 Blog
Blog / AI / Claude Code for dis: Python Bytecode Disassembly
AI

Claude Code for dis: Python Bytecode Disassembly

Published: August 15, 2028
Read time: 5 min read
By: Claude Skills 360

Python’s dis module disassembles bytecode of code objects, functions, methods, and strings. import dis. dis: dis.dis(x, file=None, depth=None) — print human-readable bytecode; x can be function, method, class, code object, string, generator, or coroutine. disassemble: dis.disassemble(code, lasti=-1, file=None) — same but takes a code object. get_instructions: dis.get_instructions(x, first_line=None) → iterator of Instruction named tuples. Instruction: .opname (str), .opcode (int), .arg (int or None), .argval (resolved value), .argrepr (str), .offset (int), .starts_line (int or None), .is_jump_target (bool). code_info: dis.code_info(x) → string summary (name, file, line, argcount, locals, consts, names, etc.). Bytecode: bc = dis.Bytecode(x) — iterable with .first_line, .exception_entries, .codeobj. findlinestarts: dis.findlinestarts(code)(offset, lineno) pairs. findlabels: dis.findlabels(code.co_code) → list of jump target offsets. stack_effect: dis.stack_effect(opcode, oparg=None, jump=None) → net stack change. opname: dis.opname[opcode] → name string. opmap: dis.opmap["LOAD_CONST"] → opcode int. HAVE_ARGUMENT: opcodes ≥ this value take an argument (CPython 3.6+ all take an arg). Claude Code generates bytecode analyzers, performance profilers, opcode frequency counters, and CPython internals explorers.

CLAUDE.md for dis

## dis Stack
- Stdlib: import dis
- Print:  dis.dis(my_function)            # human-readable bytecode
- Iter:   list(dis.get_instructions(fn))  # Instruction namedtuples
- Info:   dis.code_info(fn)               # code object summary
- Struct: bc = dis.Bytecode(fn)           # codeobj, first_line, iter
- Effect: dis.stack_effect(opcode)        # net push/pop delta

dis Bytecode Analysis Pipeline

# app/disutil.py — disassemble, opcode stats, compare, loop detector, perf
from __future__ import annotations

import dis
import io
import types
from collections import Counter
from dataclasses import dataclass
from typing import Any, Callable


# ─────────────────────────────────────────────────────────────────────────────
# 1. Core helpers
# ─────────────────────────────────────────────────────────────────────────────

def disasm_str(obj: Any) -> str:
    """
    Return the bytecode disassembly of obj as a string.

    Example:
        print(disasm_str(lambda x: x * 2))
    """
    buf = io.StringIO()
    dis.dis(obj, file=buf)
    return buf.getvalue()


def instructions(obj: Any) -> list[dis.Instruction]:
    """
    Return all Instruction objects for obj.

    Example:
        for instr in instructions(my_fn):
            print(instr.offset, instr.opname, instr.argrepr)
    """
    return list(dis.get_instructions(obj))


def code_summary(obj: Any) -> str:
    """
    Return dis.code_info() string for obj (name, file, argcount, locals, consts, etc.).

    Example:
        print(code_summary(my_function))
    """
    return dis.code_info(obj)


def first_line(obj: Any) -> int | None:
    """Return the first source line number of obj's bytecode."""
    return dis.Bytecode(obj).first_line


# ─────────────────────────────────────────────────────────────────────────────
# 2. Opcode statistics
# ─────────────────────────────────────────────────────────────────────────────

@dataclass
class OpcodeStats:
    opcode_counts:    dict[str, int]
    total_instrs:     int
    n_jumps:          int
    n_calls:          int
    n_loads:          int
    n_stores:         int
    n_const_loads:    int

    @classmethod
    def from_function(cls, fn: Any) -> "OpcodeStats":
        """
        Analyze a function's (or code object's) bytecode.

        Example:
            stats = OpcodeStats.from_function(my_heavy_fn)
            print(stats.top_opcodes(5))
        """
        instrs = list(dis.get_instructions(fn))
        counts: Counter[str] = Counter(i.opname for i in instrs)
        n_jumps = sum(1 for i in instrs if i.opname in dis.hasjabs or i.opname in dis.hasjrel)
        n_calls = sum(1 for i in instrs if "CALL" in i.opname)
        n_loads = sum(1 for i in instrs if "LOAD" in i.opname)
        n_stores = sum(1 for i in instrs if "STORE" in i.opname)
        n_const = counts.get("LOAD_CONST", 0)
        return cls(
            opcode_counts=dict(counts),
            total_instrs=len(instrs),
            n_jumps=n_jumps,
            n_calls=n_calls,
            n_loads=n_loads,
            n_stores=n_stores,
            n_const_loads=n_const,
        )

    def top_opcodes(self, n: int = 10) -> list[tuple[str, int]]:
        """Return the n most frequent opcodes."""
        return Counter(self.opcode_counts).most_common(n)

    def summary(self) -> str:
        lines = [
            f"Instructions: {self.total_instrs}",
            f"  Jumps:      {self.n_jumps}",
            f"  Calls:      {self.n_calls}",
            f"  Loads:      {self.n_loads}",
            f"  Stores:     {self.n_stores}",
            f"  LoadConst:  {self.n_const_loads}",
        ]
        lines.append("Top opcodes:")
        for op, cnt in self.top_opcodes(5):
            lines.append(f"  {op:30s} {cnt}")
        return "\n".join(lines)


# ─────────────────────────────────────────────────────────────────────────────
# 3. Code comparison
# ─────────────────────────────────────────────────────────────────────────────

def compare_bytecode(fn_a: Any, fn_b: Any) -> dict[str, Any]:
    """
    Compare the bytecode of two callables or code objects.

    Example:
        result = compare_bytecode(lambda x: x+1, lambda x: x+1)
        result["identical"]  # True
    """
    instrs_a = instructions(fn_a)
    instrs_b = instructions(fn_b)
    ops_a    = [(i.opname, i.argval) for i in instrs_a]
    ops_b    = [(i.opname, i.argval) for i in instrs_b]
    return {
        "identical":     ops_a == ops_b,
        "len_a":         len(instrs_a),
        "len_b":         len(instrs_b),
        "a_not_b":       [op for op in ops_a if op not in ops_b],
        "b_not_a":       [op for op in ops_b if op not in ops_a],
    }


def instruction_diff(fn_a: Any, fn_b: Any) -> list[str]:
    """
    Return a simple line-diff of bytecode between two functions.

    Example:
        for line in instruction_diff(old_fn, new_fn):
            print(line)
    """
    lines_a = disasm_str(fn_a).splitlines()
    lines_b = disasm_str(fn_b).splitlines()
    import difflib
    return list(difflib.unified_diff(lines_a, lines_b, fromfile="fn_a", tofile="fn_b", lineterm=""))


# ─────────────────────────────────────────────────────────────────────────────
# 4. Bytecode pattern detectors
# ─────────────────────────────────────────────────────────────────────────────

def has_global_access(fn: Any) -> bool:
    """
    Return True if fn reads any global variables (LOAD_GLOBAL instruction).
    Globals inside loops are a common performance anti-pattern.

    Example:
        has_global_access(my_fn)   # True if fn uses globals inside
    """
    return any(i.opname == "LOAD_GLOBAL" for i in dis.get_instructions(fn))


def count_function_calls(fn: Any) -> int:
    """
    Count the number of function call instructions in fn's bytecode.
    Higher = potentially more overhead per invocation.

    Example:
        count_function_calls(compute)   # 5
    """
    return sum(1 for i in dis.get_instructions(fn) if "CALL" in i.opname)


def find_constant_values(fn: Any) -> list[Any]:
    """
    Extract all LOAD_CONST values from a function's bytecode.

    Example:
        find_constant_values(lambda x: x * 2 + 1)  → [..., 2, 1]
    """
    return [
        i.argval for i in dis.get_instructions(fn)
        if i.opname == "LOAD_CONST" and i.argval is not None
    ]


def uses_comprehension(fn: Any) -> bool:
    """Return True if fn contains any BUILD_LIST/BUILD_SET/BUILD_MAP or comprehension code."""
    ops = {i.opname for i in dis.get_instructions(fn)}
    return bool(ops & {"BUILD_LIST", "BUILD_SET", "BUILD_MAP", "LIST_APPEND", "SET_ADD", "MAP_ADD"})


def global_writes(fn: Any) -> list[str]:
    """Return names of globals that fn writes to (STORE_GLOBAL)."""
    return [i.argval for i in dis.get_instructions(fn) if i.opname == "STORE_GLOBAL"]


# ─────────────────────────────────────────────────────────────────────────────
# 5. Recursive code object traversal
# ─────────────────────────────────────────────────────────────────────────────

def all_code_objects(root: Any) -> list[types.CodeType]:
    """
    Recursively collect all code objects nested inside root (lambdas,
    comprehensions, nested functions).

    Example:
        for co in all_code_objects(my_function):
            print(co.co_name, co.co_firstlineno)
    """
    try:
        code = root.__code__ if hasattr(root, "__code__") else root
    except AttributeError:
        return []
    result: list[types.CodeType] = [code]
    for const in code.co_consts:
        if isinstance(const, types.CodeType):
            result.extend(all_code_objects(const))
    return result


def full_stats(fn: Any) -> dict[str, Any]:
    """
    Aggregate stats across all nested code objects (including list comps, lambdas).

    Example:
        stats = full_stats(process_records)
        print(stats["total_instructions"])
    """
    all_instrs = []
    for co in all_code_objects(fn):
        all_instrs.extend(dis.get_instructions(co))
    opcode_counts = Counter(i.opname for i in all_instrs)
    return {
        "total_instructions": len(all_instrs),
        "n_nested_codes":     len(all_code_objects(fn)) - 1,
        "top_opcodes":        opcode_counts.most_common(5),
        "has_global_access":  any(i.opname == "LOAD_GLOBAL" for i in all_instrs),
        "n_calls":            sum(1 for i in all_instrs if "CALL" in i.opname),
    }


# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    print("=== dis demo ===")

    def multiply(x: int, y: int) -> int:
        return x * y

    def slow_sum(numbers):
        # Uses global 'sum' — each call does LOAD_GLOBAL
        total = 0
        for n in numbers:
            total += n
        return total

    def fast_sum(numbers):
        return sum(numbers)

    print("\n--- disasm_str ---")
    print(disasm_str(multiply))

    print("\n--- code_summary ---")
    print(code_summary(multiply))

    print("\n--- OpcodeStats ---")
    stats = OpcodeStats.from_function(slow_sum)
    print(stats.summary())

    print("\n--- compare_bytecode ---")
    fn1 = lambda x: x + 1
    fn2 = lambda x: x + 1
    fn3 = lambda x: x + 2
    r12 = compare_bytecode(fn1, fn2)
    r13 = compare_bytecode(fn1, fn3)
    print(f"  fn1 vs fn2 identical: {r12['identical']}")
    print(f"  fn1 vs fn3 identical: {r13['identical']}")
    print(f"  fn3 not in fn1: {r13['b_not_a']}")

    print("\n--- pattern detectors ---")
    print(f"  has_global_access(slow_sum):    {has_global_access(slow_sum)}")
    print(f"  has_global_access(multiply):    {has_global_access(multiply)}")
    print(f"  count_function_calls(fast_sum): {count_function_calls(fast_sum)}")
    print(f"  find_constant_values(lambda x: x*2+1): {find_constant_values(lambda x: x * 2 + 1)}")
    print(f"  uses_comprehension([x*2 for x in range(3)]): ", end="")
    print(uses_comprehension(lambda: [x * 2 for x in range(3)]))

    print("\n--- full_stats ---")
    def process(data):
        return [x * 2 for x in data if x > 0]

    fs = full_stats(process)
    print(f"  total_instructions: {fs['total_instructions']}")
    print(f"  n_nested_codes:     {fs['n_nested_codes']}")
    print(f"  has_global_access:  {fs['has_global_access']}")
    print(f"  top_opcodes:        {fs['top_opcodes']}")

    print("\n--- instruction_diff ---")
    diff = instruction_diff(slow_sum, fast_sum)
    for line in diff[:15]:
        print(f"  {line}")

    print("\n=== done ===")

For the bytecode PyPI alternative — the bytecode library (PyPI) provides a mutable bytecode representation with Bytecode, ConcreteInstructions, ControlFlowGraph, and a compiler backend; it enables programmatic bytecode modification and optimization without going through the AST layer; stdlib dis is read-only — use bytecode when you need to generate or mutate bytecode in a build pipeline or JIT system; use dis for profiling, debugging, performance investigation, and education where you only need to read and analyze bytecode. For the ast alternative — ast operates on source text and produces a structured tree that preserves variable names, operators, and expression structure; dis shows the compiled bytecode and reveals how the interpreter will actually execute the code (constant folding, short-circuit evaluation, comprehension code objects); use ast to analyze what the code says; use dis to analyze what the interpreter will do — for example, ast shows the comparison a and b, while dis shows the actual jump-if-false path the bytecode takes. The Claude Skills 360 bundle includes dis skill sets covering disasm_str()/instructions()/code_summary()/first_line() core helpers, OpcodeStats dataclass with total/jumps/calls/loads and top_opcodes() report, compare_bytecode()/instruction_diff() comparison utilities, has_global_access()/count_function_calls()/find_constant_values()/uses_comprehension()/global_writes() pattern detectors, and all_code_objects()/full_stats() recursive code object traversal. Start with the free tier to try bytecode analysis patterns and dis pipeline code generation.

Keep Reading

AI

Claude Code for email.contentmanager: Python Email Content Accessors

Read and write EmailMessage body content with Python's email.contentmanager module and Claude Code — email contentmanager ContentManager for the class that maps content types to get and set handler functions allowing EmailMessage to support get_content and set_content with type-specific behaviour, email contentmanager raw_data_manager for the ContentManager instance that handles raw bytes and str payloads without any conversion, email contentmanager content_manager for the standard ContentManager instance used by email.policy.default that intelligently handles text plain text html multipart and binary content types, email contentmanager get_content_text for the handler that returns the decoded text payload of a text-star message part as a str, email contentmanager get_content_binary for the handler that returns the raw decoded bytes payload of a non-text message part, email contentmanager get_data_manager for the get-handler lookup used by EmailMessage get_content to find the right reader function for the content type, email contentmanager set_content text for the handler that creates and sets a text part correctly choosing charset and transfer encoding, email contentmanager set_content bytes for the handler that creates and sets a binary part with base64 encoding and optional filename Content-Disposition, email contentmanager EmailMessage get_content for the method that reads the message body using the registered content manager handlers, email contentmanager EmailMessage set_content for the method that sets the message body and MIME headers in one call, email contentmanager EmailMessage make_alternative make_mixed make_related for the methods that convert a simple message into a multipart container, email contentmanager EmailMessage add_attachment for the method that attaches a file or bytes to a multipart message, and email contentmanager integration with email.message and email.policy and email.mime and io for building high-level email readers attachment extractors text body accessors HTML readers and policy-aware MIME construction pipelines.

5 min read Feb 12, 2029
AI

Claude Code for email.charset: Python Email Charset Encoding

Control header and body encoding for international email with Python's email.charset module and Claude Code — email charset Charset for the class that wraps a character set name with the encoding rules for header encoding and body encoding describing how to encode text for that charset in email messages, email charset Charset header_encoding for the attribute specifying whether headers using this charset should use QP quoted-printable encoding BASE64 encoding or no encoding, email charset Charset body_encoding for the attribute specifying the Content-Transfer-Encoding to use for message bodies in this charset such as QP or BASE64, email charset Charset output_codec for the attribute giving the Python codec name used to encode the string to bytes for the wire format, email charset Charset input_codec for the attribute giving the Python codec name used to decode incoming bytes to str, email charset Charset get_output_charset for returning the output charset name, email charset Charset header_encode for encoding a header string using the charset's header_encoding method, email charset Charset body_encode for encoding body content using the charset's body_encoding, email charset Charset convert for converting a string from the input_codec to the output_codec, email charset add_charset for registering a new charset with custom encoding rules in the global charset registry, email charset add_alias for adding an alias name that maps to an existing registered charset, email charset add_codec for registering a codec name mapping for use by the charset machinery, and email charset integration with email.message and email.mime and email.policy and email.encoders for building international email senders non-ASCII header encoders Content-Transfer-Encoding selectors charset-aware message constructors and MIME encoding pipelines.

5 min read Feb 11, 2029
AI

Claude Code for email.utils: Python Email Address and Header Utilities

Parse and format RFC 2822 email addresses and dates with Python's email.utils module and Claude Code — email utils parseaddr for splitting a display-name plus angle-bracket address string into a realname and email address tuple, email utils formataddr for combining a realname and address string into a properly quoted RFC 2822 address with angle brackets, email utils getaddresses for parsing a list of raw address header strings each potentially containing multiple comma-separated addresses into a list of realname address tuples, email utils parsedate for parsing an RFC 2822 date string into a nine-tuple compatible with time.mktime, email utils parsedate_tz for parsing an RFC 2822 date string into a ten-tuple that includes the UTC offset timezone in seconds, email utils parsedate_to_datetime for parsing an RFC 2822 date string into an aware datetime object with timezone, email utils formatdate for formatting a POSIX timestamp or the current time as an RFC 2822 date string with optional usegmt and localtime flags, email utils format_datetime for formatting a datetime object as an RFC 2822 date string, email utils make_msgid for generating a globally unique Message-ID string with optional idstring and domain components, email utils decode_rfc2231 for decoding an RFC 2231 encoded parameter value into a tuple of charset language and value, email utils encode_rfc2231 for encoding a string as an RFC 2231 encoded parameter value, email utils collapse_rfc2231_value for collapsing a decoded RFC 2231 tuple to a Unicode string, and email utils integration with email.message and email.headerregistry and datetime and time for building address parsers date formatters message-id generators header extractors and RFC-compliant email construction utilities.

5 min read Feb 10, 2029

Put these ideas into practice

Claude Skills 360 gives you production-ready skills for everything in this article — and 2,350+ more. Start free or go all-in.

Back to Blog

Get 360 skills free