Claude Code for email.policy: Python Email Parsing and Generation Policies — Claude Skills 360 Blog
Blog / AI / Claude Code for email.policy: Python Email Parsing and Generation Policies
AI

Claude Code for email.policy: Python Email Parsing and Generation Policies

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

Python’s email.policy module controls how the email package parses and generates messages — header encoding, line folding, line endings, and strictness. from email import policy. Built-in policy objects: policy.default — the modern EmailPolicy; headers are str, max line 78, \n linesep; suitable for in-memory work. policy.SMTPEmailPolicy with linesep="\r\n" and max_line_length=998; for SMTP wire format. policy.SMTPUTF8 — like SMTP but utf8=True; allows non-ASCII headers without RFC 2047 encoding (requires SMTPUTF8-capable server). policy.HTTPlinesep="\n", max_line_length=None; for HTTP payloads. policy.compat32 — the legacy policy matching Python ≤3.2 behaviour; used by default in email.message.Message. Key attributes: pol.max_line_length (int, default 78); pol.linesep (str); pol.utf8 (bool); pol.raise_on_defect (bool, default False); pol.header_factory (callable). Clone and customise: my_pol = policy.SMTP.clone(max_line_length=200, raise_on_defect=True). Parse with policy: email.parser.BytesParser(policy=policy.default).parsebytes(raw). Generate with policy: msg.as_string(policy=policy.SMTP) or email.generator.BytesGenerator(fp, policy=policy.SMTP). Claude Code generates policy-aware message parsers, standards-compliant email generators, header validators, and RFC 5322 serializers.

CLAUDE.md for email.policy

## email.policy Stack
- Stdlib: from email import policy
-         from email.parser import BytesParser, Parser
-         from email.generator import BytesGenerator, Generator
- Policies:
-   policy.default   — modern str-header, \n linesep, max_line=78
-   policy.SMTP      — same + linesep="\r\n", max_line=998
-   policy.SMTPUTF8  — SMTP + utf8=True (no RFC 2047 encoding)
-   policy.HTTP      — linesep="\n", max_line=None
-   policy.compat32  — legacy (default for old Message class)
- Clone:  p = policy.SMTP.clone(raise_on_defect=True)
- Parse:  msg = BytesParser(policy=policy.default).parsebytes(raw)
- Gen:    msg.as_string(policy=policy.SMTP)
-         BytesGenerator(fp, policy=policy.SMTP).flatten(msg)

email.policy Message Policy Pipeline

# app/emailpolicyutil.py — parse, generate, validate, compare, header inspect
from __future__ import annotations

import io
import textwrap
from dataclasses import dataclass, field
from email import policy as _policy
from email.generator import BytesGenerator, Generator
from email.headerregistry import Address
from email.message import EmailMessage, Message
from email.parser import BytesParser, Parser
from typing import Any


# ─────────────────────────────────────────────────────────────────────────────
# 1. Policy-aware parser helpers
# ─────────────────────────────────────────────────────────────────────────────

def parse_bytes(raw: bytes,
                pol: Any = _policy.default) -> EmailMessage:
    """
    Parse raw RFC 5322 bytes into an EmailMessage using the given policy.

    Example:
        msg = parse_bytes(b"From: [email protected]\r\nSubject: Hi\r\n\r\nBody")
        print(msg["Subject"])
    """
    return BytesParser(policy=pol).parsebytes(raw)   # type: ignore[return-value]


def parse_str(text: str,
              pol: Any = _policy.default) -> EmailMessage:
    """
    Parse RFC 5322 text into an EmailMessage.

    Example:
        raw = "From: [email protected]\nSubject: Hi\n\nBody"
        msg = parse_str(raw)
    """
    return Parser(policy=pol).parsestr(text)   # type: ignore[return-value]


def safe_parse(raw: "bytes | str",
               pol: Any = _policy.default) -> "tuple[EmailMessage | None, list[str]]":
    """
    Parse with defect collection.  Returns (msg, defects).
    defects is a list of human-readable strings; empty if mail is clean.

    Example:
        msg, defects = safe_parse(raw_bytes)
        if defects:
            print("Parse warnings:", defects)
    """
    strict_pol = pol.clone(raise_on_defect=False)
    try:
        if isinstance(raw, bytes):
            msg = BytesParser(policy=strict_pol).parsebytes(raw)
        else:
            msg = Parser(policy=strict_pol).parsestr(raw)
    except Exception as e:
        return None, [f"fatal: {e}"]
    defects = [str(d) for d in msg.defects]
    for part in msg.walk():
        defects.extend(str(d) for d in part.defects)
    return msg, defects   # type: ignore[return-value]


# ─────────────────────────────────────────────────────────────────────────────
# 2. Policy-aware generator helpers
# ─────────────────────────────────────────────────────────────────────────────

def to_smtp_bytes(msg: "Message | EmailMessage") -> bytes:
    """
    Serialise a message to SMTP wire-format bytes (CRLF, max_line=998).

    Example:
        raw = to_smtp_bytes(msg)
        smtp.sendmail(from_addr, to_addrs, raw)
    """
    buf = io.BytesIO()
    BytesGenerator(buf, policy=_policy.SMTP).flatten(msg)
    return buf.getvalue()


def to_smtp_utf8_bytes(msg: "Message | EmailMessage") -> bytes:
    """
    Serialise with SMTPUTF8 policy (allows UTF-8 headers, no RFC 2047 encoding).
    Requires an SMTPUTF8-capable SMTP server.

    Example:
        raw = to_smtp_utf8_bytes(msg)
    """
    buf = io.BytesIO()
    BytesGenerator(buf, policy=_policy.SMTPUTF8).flatten(msg)
    return buf.getvalue()


def to_display_str(msg: "Message | EmailMessage",
                   max_line: int = 78) -> str:
    """
    Serialise to a human-readable string with configurable line length.

    Example:
        print(to_display_str(msg))
    """
    pol = _policy.default.clone(max_line_length=max_line)
    buf = io.StringIO()
    Generator(buf, policy=pol).flatten(msg)
    return buf.getvalue()


# ─────────────────────────────────────────────────────────────────────────────
# 3. Header inspection (EmailPolicy typed headers)
# ─────────────────────────────────────────────────────────────────────────────

@dataclass
class HeaderReport:
    name:         str
    raw_value:    str
    folded_value: str
    addresses:    list[str] = field(default_factory=list)


def inspect_headers(msg: EmailMessage,
                    names: "list[str] | None" = None) -> list[HeaderReport]:
    """
    Return a HeaderReport for each requested header (or all headers if names=None).
    For address headers, parses out display-name + email pairs.

    Example:
        msg = parse_bytes(raw, policy.default)
        for h in inspect_headers(msg, ["From", "To", "Subject"]):
            print(h.name, h.addresses or h.raw_value)
    """
    address_headers = {"from", "to", "cc", "bcc", "reply-to", "sender"}
    target = names if names else [k for k, _ in msg.items()]
    reports: list[HeaderReport] = []
    for name in target:
        raw = msg.get(name, "")
        folded = msg.get(name, "")
        addrs: list[str] = []
        if name.lower() in address_headers:
            try:
                header_obj = msg[name]
                if hasattr(header_obj, "addresses"):
                    addrs = [str(a) for a in header_obj.addresses]
            except Exception:
                pass
        reports.append(HeaderReport(name=name, raw_value=raw,
                                     folded_value=str(folded), addresses=addrs))
    return reports


# ─────────────────────────────────────────────────────────────────────────────
# 4. Policy comparison utility
# ─────────────────────────────────────────────────────────────────────────────

_POLICIES = {
    "default":   _policy.default,
    "SMTP":      _policy.SMTP,
    "SMTPUTF8":  _policy.SMTPUTF8,
    "HTTP":      _policy.HTTP,
    "compat32":  _policy.compat32,
}


def policy_info(name: str = "SMTP") -> dict[str, Any]:
    """
    Return a dict of key attributes for a named policy.

    Example:
        for pol_name in ["default", "SMTP", "SMTPUTF8", "HTTP", "compat32"]:
            print(pol_name, policy_info(pol_name))
    """
    pol = _POLICIES.get(name)
    if pol is None:
        return {"error": f"unknown policy {name!r}"}
    attrs: dict[str, Any] = {
        "name":            name,
        "max_line_length": getattr(pol, "max_line_length", None),
        "linesep":         repr(getattr(pol, "linesep", None)),
        "utf8":            getattr(pol, "utf8", None),
        "raise_on_defect": getattr(pol, "raise_on_defect", None),
        "cte_type":        getattr(pol, "cte_type", None),
        "class":           type(pol).__name__,
    }
    return attrs


# ─────────────────────────────────────────────────────────────────────────────
# 5. Build an EmailMessage with modern policy
# ─────────────────────────────────────────────────────────────────────────────

def build_email_message(
    subject: str,
    body: str,
    from_addr: str,
    to_addrs: "list[str]",
    *,
    html_body: str | None = None,
    pol: Any = _policy.default,
) -> EmailMessage:
    """
    Build an EmailMessage using the modern email API (Python 3.6+).
    Uses EmailMessage.set_content() and make_alternative() for clean MIME structure.

    Example:
        msg = build_email_message(
            "Hello",
            "Plain text body.",
            "[email protected]",
            ["[email protected]"],
            html_body="<b>Hello!</b>",
        )
        raw = to_smtp_bytes(msg)
    """
    msg = EmailMessage(policy=pol)
    msg["Subject"] = subject
    msg["From"] = from_addr
    msg["To"] = ", ".join(to_addrs)
    msg.set_content(body)
    if html_body:
        msg.make_alternative()
        msg.get_payload()[0].set_content(     # type: ignore[union-attr]
            html_body, subtype="html")
    return msg


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

if __name__ == "__main__":
    print("=== email.policy demo ===")

    # ── policy comparison table ───────────────────────────────────────────
    print("\n--- policy_info comparison ---")
    for name in ["default", "SMTP", "SMTPUTF8", "HTTP", "compat32"]:
        info = policy_info(name)
        print(f"  {info['name']:12s}  max_line={str(info['max_line_length']):5s}  "
              f"linesep={info['linesep']:8s}  utf8={info['utf8']}  "
              f"class={info['class']}")

    # ── parse and detect defects ──────────────────────────────────────────
    print("\n--- safe_parse with defects ---")
    good_raw = b"From: [email protected]\r\nTo: [email protected]\r\nSubject: Test\r\n\r\nHello"
    bad_raw = b"From: alice\r\nTo:\r\n\r\nBody"   # missing domain, empty To

    msg_good, defects_good = safe_parse(good_raw)
    msg_bad, defects_bad = safe_parse(bad_raw)
    print(f"  good defects: {defects_good}")
    print(f"  bad  defects: {defects_bad}")

    # ── generate with different policies ─────────────────────────────────
    print("\n--- generate with SMTP vs default policy ---")
    if msg_good:
        smtp_bytes = to_smtp_bytes(msg_good)
        display_str = to_display_str(msg_good)
        print(f"  SMTP bytes linesep: {smtp_bytes[:80]!r}")
        print(f"  display str lines : {len(display_str.splitlines())}")

    # ── inspect headers ───────────────────────────────────────────────────
    print("\n--- inspect_headers ---")
    sample = b"From: Alice Smith <[email protected]>\r\nTo: Bob <[email protected]>, [email protected]\r\nSubject: Policy demo\r\n\r\n"
    parsed, _ = safe_parse(sample)
    if parsed:
        for h in inspect_headers(parsed, ["From", "To", "Subject"]):
            print(f"  {h.name:10s}: {h.raw_value!r}")
            if h.addresses:
                print(f"  {'':10s}  addresses: {h.addresses}")

    # ── clone and customise ───────────────────────────────────────────────
    print("\n--- clone policy ---")
    strict_smtp = _policy.SMTP.clone(
        raise_on_defect=True,
        max_line_length=200,
    )
    print(f"  cloned max_line_length: {strict_smtp.max_line_length}")
    print(f"  cloned raise_on_defect: {strict_smtp.raise_on_defect}")
    print(f"  cloned linesep        : {strict_smtp.linesep!r}")

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

For the email.message.EmailMessage stdlib companion — EmailMessage (Python 3.6+) is the modern message class that pairs naturally with email.policy.default; its set_content(), add_attachment(), make_alternative(), and make_related() methods build correct MIME structure automatically, replacing the older MIMEMultipart/MIMEText assembly — use EmailMessage + policy.SMTP for all new code; reserve email.mime.* classes for compatibility with code using legacy email.message.Message. For the flanker (PyPI) alternative — flanker.mime.create.text() / flanker.mime.create.multipart() provide high-level MIME construction with built-in address parsing and RFC compliance validation — use flanker in production email platforms (ESPs, inbox providers) that process high volumes of potentially malformed mail; use stdlib email.policy for general-purpose email tools that need zero dependencies. The Claude Skills 360 bundle includes email.policy skill sets covering parse_bytes()/parse_str()/safe_parse() policy-aware parsers, to_smtp_bytes()/to_smtp_utf8_bytes()/to_display_str() generators, HeaderReport/inspect_headers() typed header inspector, policy_info() attribute comparator, and build_email_message() modern EmailMessage builder. Start with the free tier to try email policy patterns and RFC 5322 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