Skip to main content

Lib/imaplib.py (part 2)

Source:

cpython 3.14 @ ab2d84fe1023/Lib/imaplib.py

This annotation covers message retrieval and manipulation commands. See lib_imaplib_detail for IMAP4.__init__, login, select, logout, the response parser, and the continuation command mechanism.

Map

LinesSymbolRole
1-80IMAP4.fetchRetrieve message data (headers, body, flags)
81-200IMAP4.storeChange flags on messages (+FLAGS, -FLAGS, FLAGS)
201-320IMAP4.searchSearch messages by criteria (FROM, SUBJECT, SINCE, etc.)
321-440IMAP4.uidExecute a command using UIDs instead of sequence numbers
441-580IMAP4.copy / IMAP4.moveCopy/move messages to another mailbox
581-700IMAP4_SSLTLS subclass wrapping the socket
701-800IMAP4_streamSubclass using a subprocess stdin/stdout as transport

Reading

IMAP4.fetch

# CPython: Lib/imaplib.py:520 fetch
def fetch(self, message_set, message_parts):
"""Fetch (retrieve) parts of messages.

(typ, [data]) = <instance>.fetch(message_set, message_parts)

message_set -- '1', '1:5', '1,3,5', '*' (last message)
message_parts -- '(FLAGS)', '(RFC822)', '(BODY[TEXT])', etc.
"""
name = 'FETCH'
typ, dat = self._simple_command(name, message_set, message_parts)
return self._untagged_response(typ, dat, name)

fetch('1:10', '(RFC822)') retrieves the full RFC-822 message text for messages 1 through 10. (BODY.PEEK[HEADER]) retrieves headers without marking messages as seen.

IMAP4.search

# CPython: Lib/imaplib.py:620 search
def search(self, charset, *criteria):
"""Search mailbox for matching messages.

(typ, [data]) = <instance>.search(charset, criterion, ...)
data is a list of space-separated message numbers as bytes.

charset -- e.g. 'UTF-8', or None for ASCII
criteria -- search keys like 'UNSEEN', 'FROM "user@example.com"'
"""
name = 'SEARCH'
if charset:
typ, dat = self._simple_command(name, 'CHARSET', charset, *criteria)
else:
typ, dat = self._simple_command(name, *criteria)
return self._untagged_response(typ, dat, name)

search(None, 'UNSEEN') returns ('OK', [b'1 3 5']) — message numbers of all unread messages. The caller splits the bytes string by space to get individual sequence numbers.

IMAP4.uid

# CPython: Lib/imaplib.py:740 uid
def uid(self, command, *args):
"""Execute a command using UIDs (permanent message identifiers).

UID FETCH, UID STORE, UID SEARCH, UID COPY, UID MOVE.
UIDs are stable across reconnections; sequence numbers are not.
"""
name = 'UID'
typ, dat = self._simple_command(name, command, *args)
if command in ('SEARCH', 'SORT'):
name = command
return self._untagged_response(typ, dat, name)

uid('FETCH', '1234', '(RFC822)') retrieves message with UID 1234. UIDs persist until the mailbox is EXPUNGEd; sequence numbers shift when messages are deleted.

IMAP4_SSL

# CPython: Lib/imaplib.py:910 IMAP4_SSL
class IMAP4_SSL(IMAP4):
"""IMAP4 over TLS/SSL (port 993 by default)."""

def __init__(self, host='', port=IMAP4_SSL_PORT, *,
ssl_context=None, timeout=None):
if ssl_context is None:
ssl_context = ssl.create_default_context()
self.ssl_context = ssl_context
IMAP4.__init__(self, host, port, timeout=timeout)

def open(self, host='', port=IMAP4_SSL_PORT, timeout=None):
self.sock = socket.create_connection((host, port), timeout)
self.sock = self.ssl_context.wrap_socket(self.sock,
server_hostname=host)
self.file = self.sock.makefile('rb')

IMAP4_SSL wraps the raw socket with TLS before the IMAP greeting is received. The ssl_context can be customized to add client certificates or disable hostname verification.

gopy notes

imaplib is pure Python. IMAP4._simple_command and _untagged_response build on objects.Socket via module/socket. IMAP4_SSL uses module/ssl.SSLContext.WrapSocket. The IMAP response parser is a pure Python state machine in module/imaplib/module.go.