Source code for imapclient.response_types

# Copyright (c) 2014, Menno Smits
# Released subject to the New BSD License
# Please see http://en.wikipedia.org/wiki/BSD_licenses

import dataclasses
import datetime
from email.utils import formataddr
from typing import Any, List, Optional, Tuple, TYPE_CHECKING, Union

from .typing_imapclient import _Atom
from .util import to_unicode


[docs]@dataclasses.dataclass class Envelope: r"""Represents envelope structures of messages. Returned when parsing ENVELOPE responses. :ivar date: A datetime instance that represents the "Date" header. :ivar subject: A string that contains the "Subject" header. :ivar from\_: A tuple of Address objects that represent one or more addresses from the "From" header, or None if header does not exist. :ivar sender: As for from\_ but represents the "Sender" header. :ivar reply_to: As for from\_ but represents the "Reply-To" header. :ivar to: As for from\_ but represents the "To" header. :ivar cc: As for from\_ but represents the "Cc" header. :ivar bcc: As for from\_ but represents the "Bcc" recipients. :ivar in_reply_to: A string that contains the "In-Reply-To" header. :ivar message_id: A string that contains the "Message-Id" header. A particular issue to watch out for is IMAP's handling of "group syntax" in address fields. This is often encountered as a recipient header of the form:: undisclosed-recipients:; but can also be expressed per this more general example:: A group: a@example.com, B <b@example.org>; This example would yield the following Address tuples:: Address(name=None, route=None, mailbox=u'A group', host=None) Address(name=None, route=None, mailbox=u'a', host=u'example.com') Address(name=u'B', route=None, mailbox=u'b', host=u'example.org') Address(name=None, route=None, mailbox=None, host=None) The first Address, where ``host`` is ``None``, indicates the start of the group. The ``mailbox`` field contains the group name. The final Address, where both ``mailbox`` and ``host`` are ``None``, indicates the end of the group. See :rfc:`3501#section-7.4.2` and :rfc:`2822` for further details. """ date: Optional[datetime.datetime] subject: bytes from_: Optional[Tuple["Address", ...]] sender: Optional[Tuple["Address", ...]] reply_to: Optional[Tuple["Address", ...]] to: Optional[Tuple["Address", ...]] cc: Optional[Tuple["Address", ...]] bcc: Optional[Tuple["Address", ...]] in_reply_to: bytes message_id: bytes
[docs]@dataclasses.dataclass class Address: """Represents electronic mail addresses. Used to store addresses in :py:class:`Envelope`. :ivar name: The address "personal name". :ivar route: SMTP source route (rarely used). :ivar mailbox: Mailbox name (what comes just before the @ sign). :ivar host: The host/domain name. As an example, an address header that looks like:: Mary Smith <mary@foo.com> would be represented as:: Address(name=u'Mary Smith', route=None, mailbox=u'mary', host=u'foo.com') See :rfc:`2822` for more detail. See also :py:class:`Envelope` for information about handling of "group syntax". """ name: bytes route: bytes mailbox: bytes host: bytes def __str__(self) -> str: if self.mailbox and self.host: address = to_unicode(self.mailbox) + "@" + to_unicode(self.host) else: address = to_unicode(self.mailbox or self.host) return formataddr((to_unicode(self.name), address))
[docs]class SearchIds(List[int]): """ Contains a list of message ids as returned by IMAPClient.search(). The *modseq* attribute will contain the MODSEQ value returned by the server (only if the SEARCH command sent involved the MODSEQ criteria). See :rfc:`4551` for more details. """ def __init__(self, *args: Any): super().__init__(*args) self.modseq: Optional[int] = None
_BodyDataType = Tuple[Union[bytes, int, "BodyData"], "_BodyDataType"]
[docs]class BodyData(_BodyDataType): """ Returned when parsing BODY and BODYSTRUCTURE responses. """ @classmethod def create(cls, response: Tuple[_Atom, ...]) -> "BodyData": # In case of multipart messages we will see at least 2 tuples # at the start. Nest these in to a list so that the returned # response tuple always has a consistent number of elements # regardless of whether the message is multipart or not. if isinstance(response[0], tuple): # Multipart, find where the message part tuples stop parts = [] for i, part in enumerate(response): if isinstance(part, bytes): break if TYPE_CHECKING: assert isinstance(part, tuple) parts.append(part) return cls(([cls.create(part) for part in parts],) + response[i:]) return cls(response) @property def is_multipart(self) -> bool: return isinstance(self[0], list)