Source code for pymap.backend.maildir.flags


from __future__ import annotations

import os.path
from collections.abc import Iterable, Mapping
from typing import IO, Self

from pymap.parsing.specials.flag import Flag, Seen, Flagged, Deleted, Draft, \
    Answered

from .io import FileReadable

__all__ = ['MaildirFlags']


[docs] class MaildirFlags(FileReadable): """Maintains a set of IMAP keywords (non-standard flags) that are available for use on messages. This uses a custom file format to define keywords, which might look like this:: 0 $Junk 1 $NonJunk The lower-case letter codes that correspond to each keyword start with ``'a'`` for 0, ``'b'`` for 1, etc. and up to 26 are supported. Args: keywords: The list of keywords available for use on messages. See Also: `IMAP Keywords <https://wiki.dovecot.org/MailboxFormat/Maildir#line-40>`_ """ _from_sys: Mapping[Flag, str] = {Seen: 'S', Flagged: 'F', Deleted: 'T', Draft: 'D', Answered: 'R'} _to_sys: Mapping[str, Flag] = {'S': Seen, 'F': Flagged, 'T': Deleted, 'D': Draft, 'R': Answered} def __init__(self, path: str) -> None: super().__init__(path) self._keywords: frozenset[Flag] = frozenset() self._to_kwd: Mapping[str, Flag] = {} self._from_kwd: Mapping[Flag, str] = {} @property def empty(self) -> bool: return not self._keywords @property def permanent_flags(self) -> frozenset[Flag]: """Return the set of all permanent flags, system and keyword.""" return self.system_flags | self.keywords @property def system_flags(self) -> frozenset[Flag]: """Return the set of defined IMAP system flags.""" return frozenset(self._from_sys.keys()) @property def keywords(self) -> frozenset[Flag]: """Return the set of available IMAP keywords.""" return self._keywords
[docs] def to_maildir(self, flags: Iterable[bytes | Flag]) -> str: """Return the string of letter codes that are used to map to defined IMAP flags and keywords. Args: flags: The flags and keywords to map. """ codes = [] for flag in flags: if isinstance(flag, bytes): flag = Flag(flag) from_sys = self._from_sys.get(flag) if from_sys is not None: codes.append(from_sys) else: from_kwd = self._from_kwd.get(flag) if from_kwd is not None: codes.append(from_kwd) return ''.join(codes)
[docs] def from_maildir(self, codes: str) -> frozenset[Flag]: """Return the set of IMAP flags that correspond to the letter codes. Args: codes: The letter codes to map. """ flags = set() for code in codes: if code == ',': break to_sys = self._to_sys.get(code) if to_sys is not None: flags.add(to_sys) else: to_kwd = self._to_kwd.get(code) if to_kwd is not None: flags.add(to_kwd) return frozenset(flags)
@classmethod def get_file(cls, path: str) -> str: return os.path.join(path, 'dovecot-keywords') @classmethod def get_default(cls, path: str) -> Self: return cls(path) @classmethod def open(cls, path: str, fp: IO[str]) -> Self: return cls(path) def read(self, fp: IO[str]) -> None: to_kwd = {} from_kwd = {} for line in fp: i, kwd = line.split() if kwd.startswith('\\'): raise ValueError(kwd) code = chr(ord('a') + int(i)) flag = Flag(kwd) to_kwd[code] = flag from_kwd[flag] = code self._keywords = frozenset(from_kwd) self._to_kwd = to_kwd self._from_kwd = from_kwd