Skip to main content

Lib/email/headerregistry.py

cpython 3.14 @ ab2d84fe1023/Lib/email/headerregistry.py

Lib/email/headerregistry.py is the header-object layer that EmailPolicy calls into when reading or writing headers. Instead of returning raw strings, HeaderRegistry dispatches to a type-specific class depending on the header name. The result is that msg['From'] gives back an Address object rather than a plain string, and msg['Date'] gives back a datetime.

Map

LinesSymbolRole
1-80BaseHeaderMixin that all header classes inherit; stores name and value, provides fold()
81-145UniqueUnstructuredHeader, UniqueMessageIDHeaderUnstructured and message-ID header types
146-220DateHeader, AddressHeaderTyped headers that parse into datetime and Address objects
221-310MIMEVersionHeader, ParameterizedMIMEHeaderMIME-specific header types with parameter parsing
311-440Address, GroupStructured address and group types
441-500HeaderRegistryDispatch table; maps lowercase header names to classes

Reading

HeaderRegistry dispatch

HeaderRegistry.__call__ is the entry point. Given a header name and a raw value string it looks up the registered class for that name (falling back to UnstructuredHeader for unknown headers) and builds a new type dynamically via type(), merging BaseHeader with the registered class.

# CPython: Lib/email/headerregistry.py:441 HeaderRegistry.__call__
def __call__(self, name, value):
cls = self.registry.get(name.lower(), self.base_class)
return cls(name, value)

The dynamic type() merge allows any header class to pick up BaseHeader behaviour (folding, source tracking) without needing to inherit from it explicitly in the class body.

Address and structured parse

AddressHeader.parse calls email._parseaddr to turn a raw string like "Alice <alice@example.com>" into (display_name, addr_spec) tuples, then wraps each result in an Address object.

# CPython: Lib/email/headerregistry.py:195 AddressHeader.parse
@staticmethod
def parse(value, kwds):
kwds['parse_tree'] = parse_tree = parser.get_address_list(value)[0]
kwds['decoded'] = str(parse_tree)
kwds['groups'] = [Group(dn, [Address(dn, addr_spec=addr)])
for dn, addr in parse_tree.as_address_list()]
kwds['addresses'] = [address
for group in kwds['groups']
for address in group.addresses]

DateHeader.parse

DateHeader.parse calls utils.parsedate_to_datetime, which returns a timezone-aware datetime.datetime if the date string includes a timezone, or a naive datetime otherwise.

gopy notes

Not yet ported. The planned package path is module/email/headerregistry. The Go mirror will need a HeaderRegistry map from string to a factory function, and concrete types for Address, Group, and the date/MIME families.

CPython 3.14 changes

Python 3.14 added Address.display_name as a property alias. The HeaderRegistry default set is unchanged; extensions continue to register custom classes via map_to_type.