Claude Code for asynchat: Python Async Protocol Handler — Claude Skills 360 Blog
Blog / AI / Claude Code for asynchat: Python Async Protocol Handler
AI

Claude Code for asynchat: Python Async Protocol Handler

Published: January 17, 2029
Read time: 5 min read
By: Claude Skills 360

Python’s asynchat module (deprecated Python 3.6, removed Python 3.12) builds on asyncore to add message-framing: it buffers incoming bytes and calls found_terminator() when a complete message arrives. import asynchat. Subclass: class Handler(asynchat.async_chat):; implement collect_incoming_data(data) (buffer bytes) and found_terminator() (process complete message). Set delimiter: self.set_terminator(b"\r\n") — byte string; self.set_terminator(4) — fixed length; self.set_terminator(None) — disable. Send: self.push(data) — queue bytes for async write. self.close_when_done() — autoclose after output drains. Server setup: create a dispatcher subclass, call asyncore.loop() to run the event loop. asynchat.simple_producer(data) wraps bytes as a producer object. The module is valuable for studying protocol handling patterns and legacy TCP proxy work, but all new code should use asyncio streams. Claude Code generates line-protocol servers, chat servers, HTTP-like parsers, SMTP relay handlers, and TCP protocol state machines.

CLAUDE.md for asynchat

## asynchat Stack
- Stdlib: import asynchat, asyncore, socket  (removed Python 3.12)
- Base:   class H(asynchat.async_chat):
-             def collect_incoming_data(self, data): self._buf += data
-             def found_terminator(self): process(self._buf); self._buf = b""
- Term:   self.set_terminator(b"\\r\\n")   # byte pattern
-         self.set_terminator(4)            # fixed length (int)
-         self.set_terminator(None)         # manual / disable
- Send:   self.push(data)                  # queue bytes
-         self.close_when_done()           # flush then close
- Loop:   asyncore.loop(timeout=1, count=n)
- Note:   Removed 3.12; use asyncio.StreamReader / StreamWriter instead

asynchat Protocol Handler Pipeline

# app/asynchatutil.py — line protocol, fixed-length, server, echo, graceful-close
from __future__ import annotations

import socket
import threading
import time
from dataclasses import dataclass, field
from typing import Callable

# asynchat removed in Python 3.12
_ASYNCHAT_AVAILABLE = False
try:
    import asynchat
    import asyncore
    _ASYNCHAT_AVAILABLE = True
except ImportError:
    pass


# ─────────────────────────────────────────────────────────────────────────────
# 1. Line-oriented echo handler
# ─────────────────────────────────────────────────────────────────────────────

if _ASYNCHAT_AVAILABLE:

    class LineHandler(asynchat.async_chat):
        """
        An async_chat subclass that collects lines terminated by b"\\r\\n"
        and echoes each back with a prefix.

        Example:
            server = LineServer(handler_class=LineHandler)
            server.start(port=0)
            # connect via telnet or socket
        """

        def __init__(self, sock: socket.socket,
                     on_line: Callable[[bytes], None] | None = None) -> None:
            super().__init__(sock=sock)
            self._buf: bytes = b""
            self._on_line = on_line
            self.set_terminator(b"\r\n")

        def collect_incoming_data(self, data: bytes) -> None:
            self._buf += data

        def found_terminator(self) -> None:
            line = self._buf
            self._buf = b""
            if self._on_line:
                self._on_line(line)
            else:
                self.push(b"ECHO: " + line + b"\r\n")

        def handle_close(self) -> None:
            self.close()


    class FixedLengthHandler(asynchat.async_chat):
        """
        Receives fixed-length 4-byte frames.  Each frame is treated as a
        big-endian uint32 and echoed back doubled.

        Example:
            # Sends 4 bytes, receives 4 bytes back with value * 2
        """

        FRAME = 4

        def __init__(self, sock: socket.socket) -> None:
            super().__init__(sock=sock)
            self._buf: bytes = b""
            self.set_terminator(self.FRAME)

        def collect_incoming_data(self, data: bytes) -> None:
            self._buf += data

        def found_terminator(self) -> None:
            frame = self._buf
            self._buf = b""
            if len(frame) == 4:
                import struct
                val = struct.unpack(">I", frame)[0]
                self.push(struct.pack(">I", val * 2))
            self.close_when_done()


    class SimpleServer(asyncore.dispatcher):
        """
        A minimal asyncore-based TCP server that creates a handler for
        each new connection.

        Example:
            server = SimpleServer(LineHandler, port=0)
            port = server.socket.getsockname()[1]
            asyncore.loop(timeout=0.1, count=20)
        """

        def __init__(self, handler_class: type,
                     host: str = "127.0.0.1",
                     port: int = 0) -> None:
            super().__init__()
            self.handler_class = handler_class
            self.create_socket()
            self.set_reuse_addr()
            self.bind((host, port))
            self.listen(5)
            self.port = self.socket.getsockname()[1]

        def handle_accepted(self, conn: socket.socket, addr) -> None:
            self.handler_class(conn)


# ─────────────────────────────────────────────────────────────────────────────
# 2. asyncio modern equivalent (always available)
# ─────────────────────────────────────────────────────────────────────────────

import asyncio


async def asyncio_echo_server(host: str = "127.0.0.1",
                               port: int = 0) -> asyncio.Server:
    """
    asyncio equivalent of asynchat echo server.
    Reads lines terminated by '\\n' and echoes them back.

    Example:
        server = asyncio.run(asyncio_echo_server(port=9000))
        # connect: echo "hello" | nc 127.0.0.1 9000
    """
    async def handle(reader: asyncio.StreamReader,
                     writer: asyncio.StreamWriter) -> None:
        try:
            while True:
                line = await reader.readline()
                if not line:
                    break
                writer.write(b"ECHO: " + line)
                await writer.drain()
        except (asyncio.IncompleteReadError, ConnectionResetError):
            pass
        finally:
            writer.close()

    server = await asyncio.start_server(handle, host, port)
    return server


async def asyncio_fixed_length_server(frame_size: int = 4,
                                       host: str = "127.0.0.1",
                                       port: int = 0) -> asyncio.Server:
    """
    asyncio equivalent of FixedLengthHandler — reads fixed-size frames.

    Example:
        server = asyncio.run(asyncio_fixed_length_server(frame_size=4))
    """
    import struct

    async def handle(reader: asyncio.StreamReader,
                     writer: asyncio.StreamWriter) -> None:
        try:
            frame = await reader.readexactly(frame_size)
            val = struct.unpack(">I", frame)[0]
            writer.write(struct.pack(">I", val * 2))
            await writer.drain()
        except asyncio.IncompleteReadError:
            pass
        finally:
            writer.close()

    server = await asyncio.start_server(handle, host, port)
    return server


# ─────────────────────────────────────────────────────────────────────────────
# 3. Protocol state machine helper (asyncio-based, modern)
# ─────────────────────────────────────────────────────────────────────────────

@dataclass
class ProtocolStats:
    connections: int = 0
    messages:    int = 0
    bytes_recv:  int = 0
    bytes_sent:  int = 0


async def run_stats_server(host: str = "127.0.0.1",
                            port: int = 0,
                            stats: ProtocolStats | None = None,
                            max_connections: int = 5) -> asyncio.Server:
    """
    A stats-collecting echo server using asyncio streams.

    Example:
        stats = ProtocolStats()
        server = asyncio.run(run_stats_server(port=9001, stats=stats))
    """
    if stats is None:
        stats = ProtocolStats()

    sem = asyncio.Semaphore(max_connections)

    async def handle(reader: asyncio.StreamReader,
                     writer: asyncio.StreamWriter) -> None:
        async with sem:
            stats.connections += 1
            try:
                while True:
                    line = await reader.readline()
                    if not line:
                        break
                    stats.bytes_recv += len(line)
                    stats.messages += 1
                    response = b"ACK: " + line
                    writer.write(response)
                    await writer.drain()
                    stats.bytes_sent += len(response)
            except (asyncio.IncompleteReadError, ConnectionResetError):
                pass
            finally:
                writer.close()

    return await asyncio.start_server(handle, host, port)


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

if __name__ == "__main__":
    print("=== asynchat demo ===")
    print(f"  asynchat available: {_ASYNCHAT_AVAILABLE}")

    if _ASYNCHAT_AVAILABLE:
        # ── asyncore + asynchat line-echo server ──────────────────────────────
        print("\n--- asyncore/asynchat line-echo server ---")
        server = SimpleServer(LineHandler)
        port = server.port
        print(f"  server listening on port {port}")

        received: list[bytes] = []

        def bg_loop():
            asyncore.loop(timeout=0.05, count=30)

        t = threading.Thread(target=bg_loop, daemon=True)
        t.start()

        with socket.create_connection(("127.0.0.1", port)) as s:
            s.sendall(b"hello\r\n")
            time.sleep(0.2)
            s.settimeout(0.3)
            try:
                data = s.recv(256)
                received.append(data)
            except Exception:
                pass

        server.close()
        t.join(timeout=1.0)
        print(f"  received: {received}")

    # ── asyncio echo server ──────────────────────────────────────────────────
    print("\n--- asyncio echo server (modern equivalent) ---")

    async def demo_asyncio():
        server = await asyncio_echo_server()
        port = server.sockets[0].getsockname()[1]
        print(f"  asyncio server on port {port}")
        reader, writer = await asyncio.open_connection("127.0.0.1", port)
        writer.write(b"world\n")
        await writer.drain()
        response = await asyncio.wait_for(reader.readline(), timeout=1.0)
        print(f"  response: {response!r}")
        writer.close()
        server.close()
        await server.wait_closed()

    asyncio.run(demo_asyncio())

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

For the asyncio stdlib replacement — asyncio.StreamReader / StreamReader.readline() / StreamReader.readexactly(n) plus asyncio.StreamWriter.write() provide coroutine-based, non-blocking protocol handling without the single-threaded polling loop — use asyncio streams for all new network protocol code; asynchat and asyncore were removed in Python 3.12. For the twisted (PyPI) alternative — twisted.internet.protocol.Protocol and LineReceiver provide the same collect/found pattern as asynchat but on a reactor that supports TLS, HTTP, SSH, DNS, and thousands of concurrent connections — use Twisted for production line-protocol servers that need battle-tested TLS and connection management; use asyncio for new greenfield async servers. The Claude Skills 360 bundle includes asynchat skill sets covering LineHandler/FixedLengthHandler async_chat subclasses, SimpleServer asyncore dispatcher, asyncio_echo_server()/asyncio_fixed_length_server() modern equivalents, and ProtocolStats/run_stats_server() stats-collecting asyncio server. Start with the free tier to try async protocol patterns and asynchat 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