Source code for pymap.interfaces.message
from __future__ import annotations
from abc import abstractmethod
from collections.abc import Collection, Sequence
from datetime import datetime
from typing import TypeVar, Protocol
from ..bytes import Writeable
from ..flags import SessionFlags
from ..parsing.response.fetch import EnvelopeStructure, BodyStructure
from ..parsing.specials import Flag, ObjectId, FetchRequirement
__all__ = ['MessageT', 'MessageT_co', 'FlagsKey', 'CachedMessage',
'MessageInterface', 'LoadedMessageInterface']
#: Type variable with an upper bound of :class:`MessageInterface`.
MessageT = TypeVar('MessageT', bound='MessageInterface')
#: Covariant type variable with an upper bound of :class:`MessageInterface`.
MessageT_co = TypeVar('MessageT_co', bound='MessageInterface', covariant=True)
#: Type alias for the value used as a key in set comparisons detecting flag
#: updates.
FlagsKey = tuple[int, frozenset[Flag]]
[docs]
class CachedMessage(Protocol):
"""Cached message metadata used to track state changes. Used to produce
untagged FETCH responses when a message's flags have changed, or when a
FETCH command requests metadata of an expunged message before its untagged
EXPUNGE response has been sent.
This is intended to be compatible with :class:`MessageInterface`, and
should be implemented by the same classes in most cases.
"""
__slots__: Sequence[str] = []
@property
@abstractmethod
def uid(self) -> int:
"""The message's unique identifier in the mailbox."""
...
@property
@abstractmethod
def internal_date(self) -> datetime:
"""The message's internal date."""
...
@property
@abstractmethod
def permanent_flags(self) -> frozenset[Flag]:
"""The permanent flags for the message."""
...
@property
@abstractmethod
def email_id(self) -> ObjectId:
"""The message's email object ID."""
...
@property
@abstractmethod
def thread_id(self) -> ObjectId:
"""The message's thread object ID."""
...
@property
@abstractmethod
def flags_key(self) -> FlagsKey:
"""Hashable value that represents the current flags of this
message, used for detecting mailbox updates.
"""
...
[docs]
@abstractmethod
def get_flags(self, session_flags: SessionFlags) -> frozenset[Flag]:
"""Get the full set of permanent and session flags for the message.
Args:
session_flags: The current session flags.
"""
...
[docs]
class MessageInterface(Protocol):
"""Message data such as UID, permanent flags, and when the message was
added to the system.
"""
__slots__: Sequence[str] = []
@property
@abstractmethod
def uid(self) -> int:
"""The message's unique identifier in the mailbox."""
...
@property
@abstractmethod
def expunged(self) -> bool:
"""True if this message has been expunged from the mailbox."""
...
@property
@abstractmethod
def internal_date(self) -> datetime:
"""The message's internal date."""
...
@property
@abstractmethod
def permanent_flags(self) -> frozenset[Flag]:
"""The permanent flags for the message."""
...
@property
@abstractmethod
def email_id(self) -> ObjectId:
"""The message's email object ID, which can identify its content.
See Also:
`RFC 8474 5.1. <https://tools.ietf.org/html/rfc8474#section-5.1>`_
"""
...
@property
@abstractmethod
def thread_id(self) -> ObjectId:
"""The message's thread object ID, which groups messages together.
See Also:
`RFC 8474 5.2. <https://tools.ietf.org/html/rfc8474#section-5.2>`_
"""
...
[docs]
@abstractmethod
def get_flags(self, session_flags: SessionFlags) -> frozenset[Flag]:
"""Get the full set of permanent and session flags for the message.
Args:
session_flags: The current session flags.
"""
...
[docs]
@abstractmethod
async def load_content(self, requirement: FetchRequirement) \
-> LoadedMessageInterface:
"""Loads the content of the message.
Args:
requirement: The data required from the message content.
"""
...
[docs]
class LoadedMessageInterface(Protocol):
"""The loaded message content, which may include the header, the body,
both, or neither, depending on the requirements.
It is assumed that this object contains the entire content in-memory. As
such, when multiple :class:`MessageInterface` objects are being processed,
only one :class:`LoadedMessageInterface` should be in scope at a time.
"""
__slots__: Sequence[str] = []
@property
@abstractmethod
def requirement(self) -> FetchRequirement:
"""The :class:`~pymap.parsing.specials.FetchRequirement` used to load
the message content.
"""
...
@abstractmethod
def __bytes__(self) -> bytes:
...
[docs]
@abstractmethod
def get_message_text(self, section: Sequence[int] | None = None) \
-> Writeable:
"""Get the text of the message part, not including headers.
The ``section`` argument can index a nested sub-part of the message.
For example, ``[2, 3]`` would get the 2nd sub-part of the message and
then index it for its 3rd sub-part.
Args:
section: Optional nested list of sub-part indexes.
"""
...
[docs]
@abstractmethod
def get_body(self, section: Sequence[int] | None = None,
binary: bool = False) -> Writeable:
"""Get the full body of the message part, including headers.
The ``section`` argument can index a nested sub-part of the message.
For example, ``[2, 3]`` would get the 2nd sub-part of the message and
then index it for its 3rd sub-part.
Args:
section: Optional nested list of sub-part indexes.
binary: True if the result has decoded any
Content-Transfer-Encoding.
"""
...
[docs]
@abstractmethod
def get_size(self, section: Sequence[int] | None = None) -> int:
"""Return the size of the message, in octets.
Args:
section: Optional nested list of sub-part indexes.
"""
...
[docs]
@abstractmethod
def get_envelope_structure(self) -> EnvelopeStructure:
"""Build and return the envelope structure.
See Also:
`RFC 3501 2.3.5.
<https://tools.ietf.org/html/rfc3501#section-2.3.5>`_
"""
...
[docs]
@abstractmethod
def get_body_structure(self) -> BodyStructure:
"""Build and return the body structure.
See Also:
`RFC 3501 2.3.6
<https://tools.ietf.org/html/rfc3501#section-2.3.6>`_
"""
...
[docs]
@abstractmethod
def contains(self, value: bytes) -> bool:
"""Check the body of the message for a sub-string. This may be
optimized to only search headers and ``text/*`` MIME parts.
Args:
value: The sub-string to find.
"""
...