Claude Code for ctypes: Python C Library Bindings — Claude Skills 360 Blog
Blog / AI / Claude Code for ctypes: Python C Library Bindings
AI

Claude Code for ctypes: Python C Library Bindings

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

Python’s ctypes module is a foreign function interface (FFI) that loads shared libraries and calls C functions without writing a C extension. import ctypes. CDLL: lib = ctypes.CDLL("libm.so.6") (Linux), ctypes.CDLL("libSystem.dylib") (macOS); ctypes.WinDLL("kernel32.dll") (Windows). Type mapping: ctypes.c_int, ctypes.c_double, ctypes.c_float, ctypes.c_char_p (null-terminated bytes), ctypes.c_void_p, ctypes.c_bool, ctypes.c_size_t. argtypes / restype: lib.sqrt.argtypes = [ctypes.c_double]; lib.sqrt.restype = ctypes.c_double — essential for correctness. Structure: class Point(ctypes.Structure): _fields_ = [("x", ctypes.c_int), ("y", ctypes.c_int)]. Pointer: ctypes.POINTER(ctypes.c_int) — pointer type; ctypes.byref(obj) — pass like &obj; ctypes.pointer(obj) — full pointer object. Buffers: buf = ctypes.create_string_buffer(128) — mutable c_char array; buf.value = b"hello". cast: ctypes.cast(ptr, ctypes.POINTER(ctypes.c_uint8)) — reinterpret pointer. sizeof: ctypes.sizeof(ctypes.c_double) → 8. string_at: ctypes.string_at(addr) → bytes until null. memmove: ctypes.memmove(dst, src, n) — copies n bytes. ctypes.cdll.LoadLibrary(path) — alternative loader. Claude Code generates libc wrappers, high-performance buffer manipulators, platform API callers, and binary-format readers.

CLAUDE.md for ctypes

## ctypes Stack
- Stdlib: import ctypes
- Load:   lib = ctypes.CDLL(ctypes.util.find_library("m"))  # portable
- Types:  lib.fn.argtypes = [ctypes.c_double]; lib.fn.restype = ctypes.c_double
- Struct: class S(ctypes.Structure): _fields_ = [("x", ctypes.c_int), ("y", ctypes.c_int)]
- Buffer: buf = ctypes.create_string_buffer(256); ctypes.memmove(buf, src, n)
- Ptr:    ctypes.byref(obj)  # pass &obj to C function

ctypes C Bindings Pipeline

# app/ctypesutil.py — libc helpers, struct marshall, buffer ops, type tools
from __future__ import annotations

import ctypes
import ctypes.util
import struct
import sys
from dataclasses import dataclass
from typing import Any, Type


# ─────────────────────────────────────────────────────────────────────────────
# 1. Library loading helpers
# ─────────────────────────────────────────────────────────────────────────────

def load_library(name: str) -> ctypes.CDLL:
    """
    Load a shared library by short name (e.g. "m", "c", "z").
    Uses ctypes.util.find_library for portable path resolution.

    Example:
        libm = load_library("m")
        libm.sqrt.argtypes = [ctypes.c_double]
        libm.sqrt.restype = ctypes.c_double
        print(libm.sqrt(2.0))   # 1.4142...
    """
    path = ctypes.util.find_library(name)
    if not path:
        raise OSError(f"Library not found: {name}")
    return ctypes.CDLL(path)


def load_libc() -> ctypes.CDLL:
    """
    Load the C standard library portably.

    Example:
        libc = load_libc()
        libc.puts(b"hello from C")
    """
    if sys.platform == "win32":
        return ctypes.CDLL("msvcrt.dll")
    path = ctypes.util.find_library("c")
    return ctypes.CDLL(path)


# ─────────────────────────────────────────────────────────────────────────────
# 2. C type utilities
# ─────────────────────────────────────────────────────────────────────────────

_PY_TO_CTYPE: dict[type, type] = {
    int:   ctypes.c_long,
    float: ctypes.c_double,
    bool:  ctypes.c_bool,
    bytes: ctypes.c_char_p,
    str:   ctypes.c_wchar_p,
}


def py_to_ctype(py_type: type) -> type:
    """
    Map a Python type to a ctypes type.

    Example:
        py_to_ctype(float)   # ctypes.c_double
        py_to_ctype(int)     # ctypes.c_long
    """
    ct = _PY_TO_CTYPE.get(py_type)
    if ct is None:
        raise TypeError(f"No ctypes mapping for {py_type}")
    return ct


def declare_function(
    lib: ctypes.CDLL,
    name: str,
    restype: Any,
    *argtypes: Any,
) -> Any:
    """
    Declare a C function's types and return the callable.

    Example:
        sqrt_fn = declare_function(libm, "sqrt", ctypes.c_double, ctypes.c_double)
        print(sqrt_fn(2.0))
    """
    fn = getattr(lib, name)
    fn.restype = restype
    fn.argtypes = list(argtypes)
    return fn


def sizeof_all(*ctypes_types) -> dict[str, int]:
    """
    Return {type_name: sizeof} for a list of ctypes types.

    Example:
        sizes = sizeof_all(ctypes.c_int, ctypes.c_long, ctypes.c_double)
        print(sizes)
    """
    return {t.__name__: ctypes.sizeof(t) for t in ctypes_types}


# ─────────────────────────────────────────────────────────────────────────────
# 3. Structure and array helpers
# ─────────────────────────────────────────────────────────────────────────────

def make_struct(name: str, fields: list[tuple[str, Any]]) -> type:
    """
    Dynamically create a ctypes.Structure subclass.

    Example:
        Point = make_struct("Point", [("x", ctypes.c_int), ("y", ctypes.c_int)])
        p = Point(x=10, y=20)
        print(p.x, p.y)
    """
    return type(name, (ctypes.Structure,), {"_fields_": fields})


def struct_to_dict(obj: ctypes.Structure) -> dict[str, Any]:
    """
    Convert a ctypes.Structure instance to a plain Python dict.

    Example:
        Point = make_struct("Point", [("x", ctypes.c_int), ("y", ctypes.c_int)])
        p = Point(x=10, y=20)
        print(struct_to_dict(p))   # {"x": 10, "y": 20}
    """
    return {name: getattr(obj, name) for name, _ in obj._fields_}


def struct_to_bytes(obj: ctypes.Structure) -> bytes:
    """
    Serialize a ctypes.Structure to a bytes representation.

    Example:
        raw = struct_to_bytes(point_instance)
    """
    return bytes(obj)


def struct_from_bytes(struct_type: type, data: bytes):
    """
    Deserialize bytes into a ctypes.Structure instance.

    Example:
        point = struct_from_bytes(Point, raw_bytes)
    """
    return struct_type.from_buffer_copy(data)


def make_array(ctype: Any, values: list) -> ctypes.Array:
    """
    Create a ctypes array from a Python list.

    Example:
        arr = make_array(ctypes.c_int, [1, 2, 3, 4, 5])
        print(arr[2])   # 3
    """
    ArrayType = ctype * len(values)
    return ArrayType(*values)


# ─────────────────────────────────────────────────────────────────────────────
# 4. Buffer helpers
# ─────────────────────────────────────────────────────────────────────────────

def alloc_buffer(size: int, init: bytes | None = None) -> ctypes.Array:
    """
    Allocate a mutable byte buffer optionally pre-filled with init.

    Example:
        buf = alloc_buffer(256, b"hello\\x00")
        print(buf.value)   # b'hello'
    """
    buf = ctypes.create_string_buffer(size)
    if init:
        ctypes.memmove(buf, init, min(len(init), size))
    return buf


def buffer_to_bytes(buf: ctypes.Array) -> bytes:
    """Read the raw contents of a ctypes buffer (including null bytes)."""
    return bytes(buf)


def bytes_to_ptr(data: bytes) -> ctypes.c_char_p:
    """Wrap a bytes object as a c_char_p (const char*)."""
    return ctypes.c_char_p(data)


def copy_bytes_into_buffer(buf: ctypes.Array, data: bytes, offset: int = 0) -> None:
    """
    Copy bytes data into a ctypes buffer starting at offset.

    Example:
        buf = alloc_buffer(256)
        copy_bytes_into_buffer(buf, b"hello", offset=4)
    """
    ctypes.memmove(ctypes.addressof(buf) + offset, data, len(data))


# ─────────────────────────────────────────────────────────────────────────────
# 5. libc wrappers
# ─────────────────────────────────────────────────────────────────────────────

def libc_memset(buf: ctypes.Array, value: int, count: int) -> None:
    """
    Fill `count` bytes of `buf` with `value` using libc memset.

    Example:
        buf = alloc_buffer(64)
        libc_memset(buf, 0xFF, 64)
    """
    ctypes.memset(buf, value, count)


def libc_strlen(data: bytes) -> int:
    """
    Return the length of a null-terminated C string in bytes.

    Example:
        libc_strlen(b"hello\\x00extra")   # 5
    """
    libc = load_libc()
    libc.strlen.restype = ctypes.c_size_t
    libc.strlen.argtypes = [ctypes.c_char_p]
    return libc.strlen(data)


def system_pagesize() -> int:
    """
    Return the OS memory page size in bytes.

    Example:
        print(system_pagesize())   # typically 4096
    """
    if sys.platform == "win32":
        si = ctypes.create_string_buffer(64)
        ctypes.windll.kernel32.GetSystemInfo(si)  # type: ignore[attr-defined]
        return struct.unpack_from("I", bytes(si), 8)[0]  # dwPageSize at offset 8
    libc = load_libc()
    libc.getpagesize.restype = ctypes.c_int
    return libc.getpagesize()


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

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

    # ── sizeof ────────────────────────────────────────────────────────────────
    print("\n--- sizeof_all ---")
    sizes = sizeof_all(ctypes.c_char, ctypes.c_short, ctypes.c_int,
                       ctypes.c_long, ctypes.c_double, ctypes.c_void_p)
    for name, n in sizes.items():
        print(f"  sizeof({name}) = {n}")

    # ── math library via ctypes ───────────────────────────────────────────────
    print("\n--- load_library('m') ---")
    try:
        libm = load_library("m")
        sqrt_fn = declare_function(libm, "sqrt", ctypes.c_double, ctypes.c_double)
        pow_fn  = declare_function(libm, "pow",  ctypes.c_double, ctypes.c_double, ctypes.c_double)
        print(f"  sqrt(2.0) = {sqrt_fn(2.0):.6f}")
        print(f"  pow(2.0, 10) = {pow_fn(2.0, 10):.0f}")
    except OSError as e:
        print(f"  libm not found: {e}")

    # ── Structure ─────────────────────────────────────────────────────────────
    print("\n--- make_struct ---")
    Point = make_struct("Point", [("x", ctypes.c_int), ("y", ctypes.c_int)])
    p = Point(x=10, y=-5)
    print(f"  Point(x={p.x}, y={p.y})")
    print(f"  struct_to_dict: {struct_to_dict(p)}")
    raw = struct_to_bytes(p)
    print(f"  raw bytes ({len(raw)}): {raw.hex()}")
    p2 = struct_from_bytes(Point, raw)
    print(f"  round-tripped: x={p2.x}, y={p2.y}")

    # ── buffer ops ────────────────────────────────────────────────────────────
    print("\n--- buffer ops ---")
    buf = alloc_buffer(32, b"hello, ctypes!")
    print(f"  buf.value: {buf.value!r}")
    libc_memset(buf, 0x41, 5)   # overwrite first 5 bytes with 'A'
    print(f"  after memset('A', 5): {bytes(buf)[:10]!r}")

    # ── array ─────────────────────────────────────────────────────────────────
    print("\n--- make_array ---")
    arr = make_array(ctypes.c_int, [10, 20, 30, 40, 50])
    print(f"  arr[0]={arr[0]}  arr[4]={arr[4]}  len={len(arr)}")
    print(f"  sum: {sum(arr)}")

    # ── pagesize ──────────────────────────────────────────────────────────────
    print("\n--- system_pagesize ---")
    try:
        print(f"  page size: {system_pagesize()} bytes")
    except Exception as e:
        print(f"  error: {e}")

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

For the cffi alternative — cffi (PyPI) provides an alternative FFI with a C-declaration parser (ffi.cdef()), inline C compilation via ffi.verify(), and an API mode that generates a compiled C extension for maximum performance; it is the FFI used internally by PyPy — use cffi when you need to define complex C APIs by pasting header declarations, need API-mode performance comparable to a handwritten C extension, or are targeting PyPy compatibility; use ctypes for interactive prototyping, small-scale library wrapping, and when zero dependencies matter. For the struct alternative — struct.pack() / struct.unpack() work on Python bytes objects and are ideal for parsing binary file formats and network protocols without going through C function calls; ctypes.Structure maps a Python class directly onto C memory layout — use struct for pure Python binary parsing where you receive bytes from a file or socket; use ctypes.Structure when you need to pass a pointer to a C struct into a C library function, or when the binary layout must match a specific C ABI. The Claude Skills 360 bundle includes ctypes skill sets covering load_library()/load_libc() portable loaders, py_to_ctype()/declare_function()/sizeof_all() type utilities, make_struct()/struct_to_dict()/struct_to_bytes()/struct_from_bytes() struct helpers, make_array()/alloc_buffer()/copy_bytes_into_buffer()/buffer_to_bytes() buffer tools, and libc_memset()/libc_strlen()/system_pagesize() libc wrappers. Start with the free tier to try C FFI patterns and ctypes 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