Skip to content

Commit

Permalink
Merge 3dd54fc into 012f754
Browse files Browse the repository at this point in the history
  • Loading branch information
icgood committed Sep 23, 2019
2 parents 012f754 + 3dd54fc commit f4bb18b
Show file tree
Hide file tree
Showing 65 changed files with 2,064 additions and 1,199 deletions.
3 changes: 1 addition & 2 deletions pymap/admin/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,8 @@ async def Append(self, stream) -> None:
return
else:
mailbox = new_mailbox
prepared_msg = await session.prepare_message(mailbox, append_msg)
append_uid, _ = await session.append_messages(
mailbox, [prepared_msg])
mailbox, [append_msg])
except ResponseError as exc:
resp = AppendResponse(result=ERROR_RESPONSE,
error_type=type(exc).__name__,
Expand Down
11 changes: 3 additions & 8 deletions pymap/backend/dict/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@
from pymap.exceptions import InvalidAuth
from pymap.interfaces.backend import BackendInterface
from pymap.interfaces.session import LoginProtocol
from pymap.parsing.message import AppendMessage, PreparedMessage
from pymap.parsing.specials import ExtensionOptions
from pymap.parsing.message import AppendMessage
from pymap.parsing.specials.flag import Flag, Recent

from .filter import FilterSet
Expand Down Expand Up @@ -220,9 +219,5 @@ async def _load_demo_mailbox(cls, resource: str, name: str,
msg_recent = True
else:
msg_recent = False
msg = AppendMessage(msg_data, msg_dt, frozenset(msg_flags),
ExtensionOptions.empty())
email_id, thread_id, ref = await mbx.save(msg_data)
prepared = PreparedMessage(msg_dt, msg.flag_set, email_id,
thread_id, msg.options, ref)
await mbx.add(prepared, recent=msg_recent)
append_msg = AppendMessage(msg_data, msg_dt, frozenset(msg_flags))
await mbx.append(append_msg, recent=msg_recent)
112 changes: 68 additions & 44 deletions pymap/backend/dict/mailbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,26 @@
from collections import OrderedDict
from datetime import datetime
from itertools import islice
from typing import Tuple, Sequence, Dict, Optional, Iterable, AsyncIterable, \
List, Set, AbstractSet, FrozenSet
from typing import Tuple, Sequence, Dict, Optional, Iterable, \
AsyncIterable, List, Set, AbstractSet, FrozenSet
from weakref import finalize, WeakKeyDictionary, WeakValueDictionary

from pymap.bytes import HashStream
from pymap.concurrent import ReadWriteLock
from pymap.context import subsystem
from pymap.exceptions import MailboxNotFound, MailboxConflict
from pymap.flags import FlagOp
from pymap.interfaces.message import PreparedMessage, CachedMessage
from pymap.interfaces.message import CachedMessage
from pymap.listtree import ListTree
from pymap.mailbox import MailboxSnapshot
from pymap.message import BaseMessage, BaseLoadedMessage
from pymap.mime import MessageContent
from pymap.parsing.message import AppendMessage
from pymap.parsing.specials import ObjectId, FetchRequirement
from pymap.parsing.specials.flag import Flag, Seen
from pymap.selected import SelectedSet, SelectedMailbox
from pymap.threads import ThreadKey

from ..mailbox import SavedMessage, MailboxDataInterface, MailboxSetInterface
from ..mailbox import MailboxDataInterface, MailboxSetInterface

__all__ = ['Message', 'MailboxData', 'MailboxSet']

Expand All @@ -45,10 +45,14 @@ def __init__(self, uid: int, internal_date: datetime,
self._content = content

@classmethod
def copy_expunged(cls, msg: Message) -> Message:
return cls(msg.uid, msg.internal_date, msg.permanent_flags,
expunged=True, email_id=msg.email_id,
thread_id=msg.thread_id, content=msg._content)
def copy(cls, msg: Message, *, uid: int = None, recent: bool = False,
expunged: bool = False) -> Message:
if uid is None:
uid = msg.uid
return cls(uid, msg.internal_date, msg.permanent_flags,
expunged=expunged, email_id=msg.email_id,
thread_id=msg.thread_id, recent=recent,
content=msg._content)

@property
def recent(self) -> bool:
Expand Down Expand Up @@ -231,38 +235,67 @@ async def update_selected(self, selected: SelectedMailbox) \
selected.add_updates(updated_messages, expunged)
return selected

async def save(self, message: bytes) -> SavedMessage:
content = MessageContent.parse(message)
async def append(self, append_msg: AppendMessage, *,
recent: bool = False) -> Message:
when = append_msg.when or datetime.now()
content = MessageContent.parse(append_msg.literal)
email_id = self._content_cache.add(content)
thread_id = self._thread_cache.add(content)
return SavedMessage(email_id, thread_id, content)

async def add(self, prepared: PreparedMessage, *,
recent: bool = False) -> Message:
when = prepared.when or datetime.now()
content = self._content_cache.get(prepared.email_id)
async with self.messages_lock.write_lock():
self._max_uid = new_uid = self._max_uid + 1
message = Message(new_uid, when, prepared.flag_set,
email_id=prepared.email_id,
thread_id=prepared.thread_id,
message = Message(new_uid, when, append_msg.flag_set,
email_id=email_id, thread_id=thread_id,
recent=recent, content=content)
self._messages[new_uid] = message
self._mod_sequences.update([new_uid])
return message

async def get(self, uid: int, cached_msg: CachedMessage = None) \
-> Optional[Message]:
async def copy(self, uid: int, destination: MailboxData, *,
recent: bool = False) -> Optional[int]:
async with self.messages_lock.read_lock():
try:
message = self._messages[uid]
except KeyError:
return None
async with destination.messages_lock.write_lock():
destination._max_uid = dest_uid = destination._max_uid + 1
new_msg = Message.copy(message, uid=dest_uid, recent=recent)
destination._messages[dest_uid] = new_msg
destination._mod_sequences.update([dest_uid])
return dest_uid

async def move(self, uid: int, destination: MailboxData, *,
recent: bool = False) -> Optional[int]:
async with self.messages_lock.write_lock():
try:
message = self._messages.pop(uid)
except KeyError:
return None
self._mod_sequences.expunge([uid])
async with destination.messages_lock.write_lock():
destination._max_uid = dest_uid = destination._max_uid + 1
new_msg = Message.copy(message, uid=dest_uid, recent=recent)
destination._messages[dest_uid] = new_msg
destination._mod_sequences.update([dest_uid])
return dest_uid

async def get(self, uid: int, cached_msg: CachedMessage) -> Message:
if uid < 1 or uid > self._max_uid:
raise IndexError(uid)
async with self.messages_lock.read_lock():
ret = self._messages.get(uid)
if ret is None and cached_msg is not None:
if not isinstance(cached_msg, Message):
raise TypeError(cached_msg)
return Message.copy_expunged(cached_msg)
else:
return ret
msg = self._messages.get(uid)
if msg is None:
if not isinstance(cached_msg, Message):
raise TypeError(cached_msg)
msg = Message.copy(cached_msg, expunged=True)
return msg

async def update(self, uid: int, cached_msg: CachedMessage,
flag_set: FrozenSet[Flag], mode: FlagOp) -> Message:
self._mod_sequences.update([uid])
msg = await self.get(uid, cached_msg)
msg.permanent_flags = mode.apply(msg.permanent_flags, flag_set)
return msg

async def delete(self, uids: Iterable[int]) -> None:
async with self.messages_lock.write_lock():
Expand All @@ -271,7 +304,7 @@ async def delete(self, uids: Iterable[int]) -> None:
del self._messages[uid]
except KeyError:
pass
self._mod_sequences.expunge(uids)
self._mod_sequences.expunge(uids)

async def claim_recent(self, selected: SelectedMailbox) -> None:
uids: List[int] = []
Expand All @@ -283,12 +316,6 @@ async def claim_recent(self, selected: SelectedMailbox) -> None:
uids.append(msg_uid)
self._mod_sequences.update(uids)

async def update_flags(self, messages: Sequence[Message],
flag_set: FrozenSet[Flag], mode: FlagOp) -> None:
self._mod_sequences.update(msg.uid for msg in messages)
for msg in messages:
msg.permanent_flags = mode.apply(msg.permanent_flags, flag_set)

async def cleanup(self) -> None:
pass

Expand Down Expand Up @@ -351,19 +378,16 @@ async def list_mailboxes(self) -> ListTree:
mailboxes = list(self._set.keys())
return ListTree(self.delimiter).update('INBOX', *mailboxes)

async def get_mailbox(self, name: str,
try_create: bool = False) -> MailboxData:
async def get_mailbox(self, name: str) -> MailboxData:
if name.upper() == 'INBOX':
return self._inbox
async with self._set_lock.read_lock():
if name not in self._set:
raise MailboxNotFound(name, try_create)
return self._set[name]

async def add_mailbox(self, name: str) -> ObjectId:
async with self._set_lock.read_lock():
if name in self._set:
raise MailboxConflict(name)
raise ValueError(name)
async with self._set_lock.write_lock():
self._set[name] = mbx = MailboxData(
self._content_cache, self._thread_cache)
Expand All @@ -372,7 +396,7 @@ async def add_mailbox(self, name: str) -> ObjectId:
async def delete_mailbox(self, name: str) -> None:
async with self._set_lock.read_lock():
if name not in self._set:
raise MailboxNotFound(name)
raise KeyError(name)
async with self._set_lock.write_lock():
del self._set[name]

Expand All @@ -382,9 +406,9 @@ async def rename_mailbox(self, before: str, after: str) -> None:
before_entry = tree.get(before)
after_entry = tree.get(after)
if before_entry is None:
raise MailboxNotFound(before)
raise KeyError(before)
elif after_entry is not None:
raise MailboxConflict(after)
raise ValueError(after)
async with self._set_lock.write_lock():
for before_name, after_name in tree.get_renames(before, after):
if before_name == 'INBOX':
Expand Down
Loading

0 comments on commit f4bb18b

Please sign in to comment.