Claude Code for memray: Memory Profiling in Python — Claude Skills 360 Blog
Blog / AI / Claude Code for memray: Memory Profiling in Python
AI

Claude Code for memray: Memory Profiling in Python

Published: April 30, 2028
Read time: 5 min read
By: Claude Skills 360

memray tracks every Python memory allocation. pip install memray. CLI: python -m memray run -o out.bin script.py. Flamegraph: python -m memray flamegraph out.bin → HTML. Table: python -m memray table out.bin. Tree: python -m memray tree out.bin. Stats: python -m memray stats out.bin. Live: python -m memray run --live script.py. Attach: python -m memray attach <pid>. Programmatic: import memray; with memray.Tracker("out.bin"): .... Native: memray run --native script.py — tracks C extensions too. Filter: memray flamegraph --filter-allocations 100 out.bin — only show ≥100 byte allocs. Peak: stats show peak RSS and heap. No-native for speed. Async: works with asyncio; each coroutine tracked. memray run --follow-fork for subprocess tracking. pytest plugin: pip install pytest-memray; pytest --memray --most-allocations 5. @pytest.mark.limit_memory("50MB") decorator. memray.Tracker(destination=memray.FileDestination("out.bin"), native_traces=True). Reader API: from memray._memray import FileReader; reader = FileReader("out.bin"). reader.get_high_watermark_allocation_records(). reader.get_leaked_allocation_records(top_k=10). Size filter: FileReader(...).get_leaked_allocation_records(top_k=5). memray transform speedscope out.bin -o out.speedscope.json. Claude Code generates memray Tracker context managers, pytest memory budgets, and leak detection pipelines.

CLAUDE.md for memray

## memray Stack
- Version: memray >= 1.8 | pip install memray
- CLI: python -m memray run -o out.bin script.py
- Profile: python -m memray flamegraph out.bin → interactive HTML report
- In-process: with memray.Tracker("out.bin"): ... → captures allocations
- pytest: pytest --memray | @pytest.mark.limit_memory("50 MB")
- Reader: FileReader("out.bin").get_high_watermark_allocation_records()

memray Memory Profiling Pipeline

# app/mem_profile.py — memray Tracker, FileReader, leak detector, and pytest helpers
from __future__ import annotations

import io
import subprocess
import sys
from contextlib import contextmanager
from pathlib import Path
from typing import Any, Generator


# ─────────────────────────────────────────────────────────────────────────────
# 1. Tracker context managers
# ─────────────────────────────────────────────────────────────────────────────

@contextmanager
def profile_memory(
    output: str | Path = "/tmp/memray_output.bin",
    native_traces: bool = False,
    follow_fork: bool = False,
) -> Generator[Path, None, None]:
    """
    Context manager that records all memory allocations to a .bin file.
    Yields the output path for use in reports.

    Usage:
        with profile_memory("/tmp/my_app.bin") as bin_path:
            run_expensive_function()
        generate_flamegraph(bin_path, "/tmp/flamegraph.html")

    native_traces=True: also track allocations from C extensions (slower).
    follow_fork=True: track child processes.
    """
    try:
        import memray
    except ImportError as e:
        raise ImportError("pip install memray") from e

    out_path = Path(output)
    out_path.unlink(missing_ok=True)

    dest = memray.FileDestination(str(out_path), overwrite=True)
    with memray.Tracker(destination=dest, native_traces=native_traces,
                        follow_fork=follow_fork):
        yield out_path


@contextmanager
def profile_memory_string(
    native_traces: bool = False,
) -> Generator[io.BytesIO, None, None]:
    """
    Context manager that records allocations to an in-memory buffer.
    Yields the BytesIO buffer (readable after the context exits).

    Usage:
        with profile_memory_string() as buf:
            run_function()
        buf.seek(0)
        # pass buf to FileReader
    """
    try:
        import memray
    except ImportError as e:
        raise ImportError("pip install memray") from e

    buf = io.BytesIO()
    dest = memray.SocketDestination  # fallback: use file if no socket
    # Use file-based approach for compatibility
    import tempfile, os
    with tempfile.NamedTemporaryFile(suffix=".bin", delete=False) as tmp:
        tmp_path = tmp.name
    try:
        dest_f = memray.FileDestination(tmp_path, overwrite=True)
        with memray.Tracker(destination=dest_f, native_traces=native_traces):
            yield buf
        # Copy to buffer after context
        buf.write(Path(tmp_path).read_bytes())
        buf.seek(0)
    finally:
        Path(tmp_path).unlink(missing_ok=True)


# ─────────────────────────────────────────────────────────────────────────────
# 2. Report generators (shell out to memray CLI)
# ─────────────────────────────────────────────────────────────────────────────

def generate_flamegraph(
    bin_path: str | Path,
    output_html: str | Path | None = None,
    leaks: bool = False,
    show_memory_leaks: bool = False,
) -> Path:
    """
    Generate an HTML flamegraph from a memray .bin file.
    Returns path to the generated HTML file.

    leaks=True: include --leaks flag to focus on unfreed allocations.
    """
    bin_p  = Path(bin_path)
    out_p  = Path(output_html) if output_html else bin_p.with_suffix(".html")
    cmd    = [sys.executable, "-m", "memray", "flamegraph", str(bin_p),
              "-o", str(out_p), "--force"]
    if leaks or show_memory_leaks:
        cmd.append("--leaks")
    subprocess.run(cmd, check=True, capture_output=True)
    return out_p


def generate_table_report(bin_path: str | Path) -> str:
    """
    Run `memray table` and return the text output.
    """
    result = subprocess.run(
        [sys.executable, "-m", "memray", "table", str(bin_path)],
        capture_output=True, text=True, check=False,
    )
    return result.stdout or result.stderr


def get_stats(bin_path: str | Path) -> dict[str, Any]:
    """
    Run `memray stats` and return a dict with key metrics.
    Parses: total allocations, peak memory (bytes), number of leaked objects.
    """
    result = subprocess.run(
        [sys.executable, "-m", "memray", "stats", str(bin_path), "--json"],
        capture_output=True, text=True, check=False,
    )
    if result.returncode == 0 and result.stdout.strip():
        import json
        try:
            return json.loads(result.stdout)
        except json.JSONDecodeError:
            pass
    # Fallback: return raw output
    return {"raw": result.stdout + result.stderr}


# ─────────────────────────────────────────────────────────────────────────────
# 3. FileReader API (programmatic analysis)
# ─────────────────────────────────────────────────────────────────────────────

def analyze_allocations(
    bin_path: str | Path,
    top_k: int = 10,
    leaks_only: bool = False,
) -> list[dict[str, Any]]:
    """
    Read a memray .bin file and return the top allocation records.
    Returns list of {size, n_allocations, stack_trace} dicts.

    leaks_only=True: return only allocations not freed at end of run.
    """
    try:
        from memray._memray import FileReader
    except ImportError as e:
        raise ImportError("pip install memray") from e

    reader  = FileReader(str(bin_path))
    records = (
        reader.get_leaked_allocation_records(top_k=top_k)
        if leaks_only
        else reader.get_high_watermark_allocation_records(top_k=top_k)
    )

    results = []
    for record in records:
        results.append({
            "size":          record.size,
            "n_allocations": record.n_allocations,
            "stack":         [str(frame) for frame in record.stack_trace()],
        })
    return results


def peak_memory_bytes(bin_path: str | Path) -> int:
    """Return peak heap memory from a memray bin file in bytes."""
    try:
        from memray._memray import FileReader
        reader = FileReader(str(bin_path))
        records = reader.get_high_watermark_allocation_records(top_k=1)
        # Sum total size at high watermark
        total = sum(r.size for r in
                    reader.get_high_watermark_allocation_records(top_k=10000))
        return total
    except Exception:
        return -1


# ─────────────────────────────────────────────────────────────────────────────
# 4. Memory budget checker (for tests / CI)
# ─────────────────────────────────────────────────────────────────────────────

class MemoryBudget:
    """
    Assert that a code block does not exceed a peak memory budget.

    Usage:
        with MemoryBudget(max_mb=50) as budget:
            process_large_file("data.csv")
        budget.assert_ok()   # raises if peak > 50 MB
    """

    def __init__(self, max_mb: float, tmp_dir: str | Path = "/tmp"):
        self.max_mb   = max_mb
        self._bin     = Path(tmp_dir) / "budget_check.bin"
        self._peak_mb: float | None = None
        self._ctx     = None

    def __enter__(self):
        self._bin.unlink(missing_ok=True)
        try:
            import memray
            dest       = memray.FileDestination(str(self._bin), overwrite=True)
            self._ctx  = memray.Tracker(destination=dest).__enter__()
        except ImportError:
            pass
        return self

    def __exit__(self, *args):
        if self._ctx is not None:
            import memray
            memray.Tracker.__exit__(self._ctx, *args)  # type: ignore[arg-type]

    @property
    def peak_mb(self) -> float:
        if self._peak_mb is None and self._bin.exists():
            raw = peak_memory_bytes(self._bin)
            self._peak_mb = raw / 1_048_576 if raw > 0 else 0.0
        return self._peak_mb or 0.0

    def assert_ok(self, msg: str = "") -> None:
        peak = self.peak_mb
        if peak > self.max_mb:
            raise AssertionError(
                f"Memory budget exceeded: {peak:.1f} MB > {self.max_mb:.1f} MB"
                + (f" — {msg}" if msg else "")
            )


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

if __name__ == "__main__":
    BIN = Path("/tmp/memray_demo.bin")

    with profile_memory(BIN) as path:
        # Allocate ~10 MB of data
        big = [bytearray(1024) for _ in range(10_000)]
        del big

    print(f"Profiling bin: {path}")

    print("\n=== Stats ===")
    stats = get_stats(BIN)
    print(stats)

    print("\n=== Top allocations ===")
    allocs = analyze_allocations(BIN, top_k=3)
    for a in allocs:
        print(f"  {a['size']:,} bytes × {a['n_allocations']:,} allocs")
        if a["stack"]:
            print(f"    → {a['stack'][0]}")

    print("\n=== Flamegraph ===")
    html = generate_flamegraph(BIN, "/tmp/memray_demo.html")
    print(f"  Flamegraph: {html}")

    print("\n=== Memory budget ===")
    with MemoryBudget(max_mb=200) as budget:
        moderate = [bytearray(1024) for _ in range(50_000)]
        del moderate
    print(f"  Peak: {budget.peak_mb:.1f} MB (budget: 200 MB)")
    budget.assert_ok("moderate allocation")
    print("  Budget OK")

For the tracemalloc stdlib alternative — tracemalloc (built in to Python 3.4+) records allocation snapshots and shows top memory consumers, but it only captures Python-level allocations at fixed points in time; memray continuously records every allocation including C extension calls, produces interactive flamegraph HTML, and integrates with pytest for per-test memory budgets with @pytest.mark.limit_memory(). For the memory_profiler (@profile decorator) alternative — memory_profiler instruments individual functions line-by-line and reports RSS growth per line, which is useful for finding which line in a function allocates; memray tracks all allocations across the entire process in a single pass and provides flamegraph visualization — use memory_profiler for per-line granularity, memray for full-call-graph memory profiling. The Claude Skills 360 bundle includes memray skill sets covering profile_memory() context manager, profile_memory_string() in-memory variant, generate_flamegraph()/generate_table_report()/get_stats() CLI wrappers, analyze_allocations()/peak_memory_bytes() FileReader API, MemoryBudget context manager for CI assertions, pytest —memray integration notes, and native_traces + follow_fork options. Start with the free tier to try memory profiling 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