Skip to content

Commit

Permalink
Switch to Python 3.9
Browse files Browse the repository at this point in the history
  • Loading branch information
icgood committed Feb 13, 2021
1 parent 0b80e87 commit 6fb862e
Show file tree
Hide file tree
Showing 115 changed files with 709 additions and 729 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
language: python
python:
- "3.8"
- "3.9"
dist: bionic # https://docs.travis-ci.com/user/languages/python/#python-37-and-higher
install:
- travis_retry pip install -U -r doc/requirements.txt
Expand Down
5 changes: 3 additions & 2 deletions pymap/admin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
import sys
from argparse import ArgumentParser, SUPPRESS
from asyncio import CancelledError
from collections.abc import Awaitable, Sequence
from datetime import datetime, timedelta, timezone
from ssl import SSLContext
from typing import Optional, Sequence, List, Awaitable
from typing import Optional

from grpclib.events import listen, RecvRequest
from grpclib.health.check import ServiceStatus
Expand Down Expand Up @@ -94,7 +95,7 @@ async def start(self) -> Awaitable:
path: Optional[str] = self.config.args.admin_path
host: Optional[str] = self.config.args.admin_host
port: Optional[int] = self.config.args.admin_port
server_handlers: List[Handler] = [self._get_health()]
server_handlers: list[Handler] = [self._get_health()]
server_handlers.extend(handler(backend) for _, handler in handlers)
ssl = self.config.ssl_context
servers = [await self._start_local(server_handlers, path),
Expand Down
6 changes: 3 additions & 3 deletions pymap/admin/handlers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
from __future__ import annotations

from abc import ABCMeta
from collections.abc import Mapping, AsyncGenerator
from contextlib import closing, asynccontextmanager, AsyncExitStack
from typing import Type, Mapping, AsyncGenerator
from typing_extensions import Final
from typing import Final

from pymap.context import connection_exit
from pymap.exceptions import InvalidAuth, ResponseError
Expand All @@ -20,7 +20,7 @@
__all__ = ['handlers', 'BaseHandler', 'LoginHandler']

#: Registers new admin handler plugins.
handlers: Plugin[Type[BaseHandler]] = Plugin('pymap.admin.handlers')
handlers: Plugin[type[BaseHandler]] = Plugin('pymap.admin.handlers')


class BaseHandler(Handler, metaclass=ABCMeta):
Expand Down
4 changes: 2 additions & 2 deletions pymap/admin/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
from __future__ import annotations

from abc import abstractmethod
from typing import Mapping
from typing_extensions import Protocol
from collections.abc import Mapping
from typing import Protocol

from grpclib.const import Handler as _Handler

Expand Down
4 changes: 1 addition & 3 deletions pymap/backend/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@

from __future__ import annotations

from typing import Type

from ..interfaces.backend import BackendInterface
from ..plugin import Plugin

__all__ = ['backends']

#: Registers new backend plugins.
backends: Plugin[Type[BackendInterface]] = Plugin('pymap.backend')
backends: Plugin[type[BackendInterface]] = Plugin('pymap.backend')
11 changes: 5 additions & 6 deletions pymap/backend/dict/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@
import os.path
import uuid
from argparse import ArgumentParser, Namespace
from collections.abc import Awaitable, Mapping, Sequence, AsyncIterator
from contextlib import closing, asynccontextmanager
from datetime import datetime, timezone
from secrets import token_bytes
from typing import Any, Optional, Tuple, Sequence, Mapping, Dict, Awaitable, \
AsyncIterator
from typing_extensions import Final
from typing import Any, Optional, Final

from pkg_resources import resource_listdir, resource_stream
from pysasl.creds import AuthenticationCredentials
Expand Down Expand Up @@ -71,7 +70,7 @@ def add_subparser(cls, name: str, subparsers: Any) -> ArgumentParser:

@classmethod
async def init(cls, args: Namespace, **overrides: Any) \
-> Tuple[DictBackend, Config]:
-> tuple[DictBackend, Config]:
config = Config.from_args(args, **overrides)
login = Login(config)
return cls(login, config), config
Expand All @@ -95,7 +94,7 @@ def __init__(self, args: Namespace, *, demo_data: bool,
self._demo_user = demo_user
self._demo_password = demo_password
self._demo_data_resource = demo_data_resource
self.set_cache: Dict[str, Tuple[MailboxSet, FilterSet]] = {}
self.set_cache: dict[str, tuple[MailboxSet, FilterSet]] = {}

@property
def backend_capability(self) -> BackendCapability:
Expand Down Expand Up @@ -166,7 +165,7 @@ def __init__(self, config: Config) -> None:
self.config = config
self.users_dict = {config.demo_user: UserMetadata(
config, password=config.demo_password)}
self.tokens_dict: Dict[str, Tuple[str, bytes]] = {}
self.tokens_dict: dict[str, tuple[str, bytes]] = {}
self._tokens = AllTokens()

@property
Expand Down
7 changes: 4 additions & 3 deletions pymap/backend/dict/filter.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@

from __future__ import annotations

from typing import Optional, Tuple, Sequence, Dict
from collections.abc import Sequence
from typing import Optional

from pymap.filter import PluginFilterSet

Expand All @@ -12,7 +13,7 @@ class FilterSet(PluginFilterSet[bytes]):

def __init__(self) -> None:
super().__init__('sieve', bytes)
self._filters: Dict[str, bytes] = {}
self._filters: dict[str, bytes] = {}
self._active: Optional[str] = None

async def put(self, name: str, value: bytes) -> None:
Expand Down Expand Up @@ -53,5 +54,5 @@ async def get_active(self) -> Optional[bytes]:
else:
return self._filters[self._active]

async def get_all(self) -> Tuple[Optional[str], Sequence[str]]:
async def get_all(self) -> tuple[Optional[str], Sequence[str]]:
return self._active, list(self._filters.keys())
39 changes: 19 additions & 20 deletions pymap/backend/dict/mailbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@

import hashlib
from bisect import bisect_left
from collections import OrderedDict
from collections.abc import Iterable, Sequence, AsyncIterable
from datetime import datetime
from itertools import islice
from typing import Tuple, Sequence, Dict, Optional, Iterable, \
AsyncIterable, List, Set, AbstractSet, FrozenSet
from typing import Optional
from weakref import finalize, WeakKeyDictionary, WeakValueDictionary

from pymap.bytes import HashStream
Expand Down Expand Up @@ -76,25 +75,25 @@ class _ModSequenceMapping:
def __init__(self) -> None:
super().__init__()
self._highest = 0
self._uids: Dict[int, int] = {}
self._updates: Dict[int, Set[int]] = {}
self._expunges: Dict[int, Set[int]] = {}
self._mod_seqs_order: List[int] = []
self._uids: dict[int, int] = {}
self._updates: dict[int, set[int]] = {}
self._expunges: dict[int, set[int]] = {}
self._mod_seqs_order: list[int] = []

@property
def highest(self) -> int:
return self._highest

def _remove_prev(self, uid: int, prev_mod_seq: int,
data: Dict[int, Set[int]]) -> None:
data: dict[int, set[int]]) -> None:
uid_set = data.get(prev_mod_seq, None)
if uid_set is not None:
uid_set.discard(uid)
if not uid_set:
del data[prev_mod_seq]
self._mod_seqs_order.remove(prev_mod_seq)

def _set(self, uids: Iterable[int], data: Dict[int, Set[int]]) -> None:
def _set(self, uids: Iterable[int], data: dict[int, set[int]]) -> None:
self._highest = mod_seq = self._highest + 1
self._mod_seqs_order.append(mod_seq)
new_uid_set = data.setdefault(mod_seq, set())
Expand All @@ -113,9 +112,9 @@ def expunge(self, uids: Iterable[int]) -> None:
return self._set(uids, self._expunges)

def find_updated(self, mod_seq: int) \
-> Tuple[AbstractSet[int], AbstractSet[int]]:
updates_ret: Set[int] = set()
expunges_ret: Set[int] = set()
-> tuple[frozenset[int], frozenset[int]]:
updates_ret: set[int] = set()
expunges_ret: set[int] = set()
updates = self._updates
expunges = self._expunges
mod_seqs_order = self._mod_seqs_order
Expand All @@ -128,15 +127,15 @@ def find_updated(self, mod_seq: int) \
updates_ret.update(updates_set)
if expunges_set is not None:
expunges_ret.update(expunges_set)
return updates_ret, expunges_ret
return frozenset(updates_ret), frozenset(expunges_ret)


class _ContentCache:

def __init__(self) -> None:
super().__init__()
self._email_ids: Dict[bytes, ObjectId] = {}
self._hashes: Dict[ObjectId, bytes] = {}
self._email_ids: dict[bytes, ObjectId] = {}
self._hashes: dict[ObjectId, bytes] = {}
self._content: WeakValueDictionary[ObjectId, MessageContent] = \
WeakValueDictionary()

Expand Down Expand Up @@ -200,7 +199,7 @@ def __init__(self, content_cache: _ContentCache,
self._uid_validity = MailboxSnapshot.new_uid_validity()
self._max_uid = 100
self._mod_sequences = _ModSequenceMapping()
self._messages: Dict[int, Message] = OrderedDict()
self._messages: dict[int, Message] = {}

@property
def mailbox_id(self) -> ObjectId:
Expand Down Expand Up @@ -299,7 +298,7 @@ async def get(self, uid: int, cached_msg: CachedMessage) -> Message:
return msg

async def update(self, uid: int, cached_msg: CachedMessage,
flag_set: FrozenSet[Flag], mode: FlagOp) -> Message:
flag_set: frozenset[Flag], mode: FlagOp) -> Message:
msg = await self.get(uid, cached_msg)
msg.permanent_flags = mode.apply(msg.permanent_flags, flag_set)
self._mod_sequences.update([uid])
Expand All @@ -317,7 +316,7 @@ async def delete(self, uids: Iterable[int]) -> None:
self._updated.set()

async def claim_recent(self, selected: SelectedMailbox) -> None:
uids: List[int] = []
uids: list[int] = []
async for msg in self.messages():
if msg.recent:
msg.recent = False
Expand Down Expand Up @@ -366,9 +365,9 @@ def __init__(self) -> None:
self._content_cache = _ContentCache()
self._thread_cache = _ThreadCache()
self._inbox = MailboxData(self._content_cache, self._thread_cache)
self._set: Dict[str, MailboxData] = OrderedDict()
self._set: dict[str, MailboxData] = {}
self._set_lock = subsystem.get().new_rwlock()
self._subscribed: Dict[str, bool] = {}
self._subscribed: dict[str, bool] = {}

@property
def delimiter(self) -> str:
Expand Down
13 changes: 6 additions & 7 deletions pymap/backend/mailbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@
from __future__ import annotations

from abc import abstractmethod
from typing import TypeVar, Optional, Tuple, Sequence, FrozenSet, \
Iterable, AsyncIterable
from typing_extensions import Protocol
from collections.abc import Iterable, Sequence, AsyncIterable
from typing import TypeVar, Optional, Protocol

from pymap.concurrent import Event
from pymap.flags import FlagOp
Expand Down Expand Up @@ -55,12 +54,12 @@ def uid_validity(self) -> int:
...

@property
def permanent_flags(self) -> FrozenSet[Flag]:
def permanent_flags(self) -> frozenset[Flag]:
"""The permanent flags allowed in the mailbox."""
return get_system_flags() - {Recent}

@property
def session_flags(self) -> FrozenSet[Flag]:
def session_flags(self) -> frozenset[Flag]:
"""The session flags allowed in the mailbox."""
return frozenset({Recent})

Expand Down Expand Up @@ -138,7 +137,7 @@ async def get(self, uid: int, cached_msg: CachedMessage) -> MessageT_co:

@abstractmethod
async def update(self, uid: int, cached_msg: CachedMessage,
flag_set: FrozenSet[Flag], mode: FlagOp) -> MessageT_co:
flag_set: frozenset[Flag], mode: FlagOp) -> MessageT_co:
"""Update the permanent flags of the message.
Args:
Expand Down Expand Up @@ -188,7 +187,7 @@ async def snapshot(self) -> MailboxSnapshot:
...

async def find(self, seq_set: SequenceSet, selected: SelectedMailbox) \
-> AsyncIterable[Tuple[int, MessageT_co]]:
-> AsyncIterable[tuple[int, MessageT_co]]:
"""Find the active message UID and message pairs in the mailbox that
are contained in the given sequences set. Message sequence numbers
are resolved by the selected mailbox session.
Expand Down
9 changes: 4 additions & 5 deletions pymap/backend/maildir/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@
import asyncio
import os.path
from argparse import ArgumentParser, Namespace
from collections.abc import Awaitable, Mapping, Sequence, AsyncIterator
from concurrent.futures import ThreadPoolExecutor
from contextlib import asynccontextmanager
from datetime import datetime
from typing import Any, Optional, Tuple, Sequence, Mapping, Awaitable, \
AsyncIterator
from typing_extensions import Final
from typing import Any, Optional, Final

from pysasl.creds import AuthenticationCredentials

Expand Down Expand Up @@ -73,7 +72,7 @@ def add_subparser(cls, name: str, subparsers: Any) -> ArgumentParser:

@classmethod
async def init(cls, args: Namespace, **overrides: Any) \
-> Tuple[MaildirBackend, Config]:
-> tuple[MaildirBackend, Config]:
config = Config.from_args(args)
login = Login(config)
return cls(login, config), config
Expand Down Expand Up @@ -271,7 +270,7 @@ async def new_session(self) -> AsyncIterator[Session]:
filter_set = FilterSet(layout.path)
yield Session(self.name, config, mailbox_set, filter_set)

def _load_maildir(self) -> Tuple[Maildir, MaildirLayout]:
def _load_maildir(self) -> tuple[Maildir, MaildirLayout]:
full_path = os.path.join(self.config.base_dir, self.mailbox_path)
layout = MaildirLayout.get(full_path, self.config.layout, Maildir)
create = not os.path.exists(full_path)
Expand Down
16 changes: 8 additions & 8 deletions pymap/backend/maildir/flags.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@

from __future__ import annotations

from typing import IO, TypeVar, Type, Iterable, Union, Mapping, FrozenSet, \
Sequence
from collections.abc import Iterable, Mapping, Sequence
from typing import IO, TypeVar, Union

from pymap.parsing.specials.flag import Flag, Seen, Flagged, Deleted, Draft, \
Answered
Expand Down Expand Up @@ -57,17 +57,17 @@ def __init__(self, keywords: Sequence[Flag]) -> None:
for i, kwd in enumerate(keywords)}

@property
def permanent_flags(self) -> FrozenSet[Flag]:
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]:
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]:
def keywords(self) -> frozenset[Flag]:
"""Return the set of available IMAP keywords."""
return self._keywords

Expand All @@ -92,7 +92,7 @@ def to_maildir(self, flags: Iterable[Union[bytes, Flag]]) -> str:
codes.append(from_kwd)
return ''.join(codes)

def from_maildir(self, codes: str) -> FrozenSet[Flag]:
def from_maildir(self, codes: str) -> frozenset[Flag]:
"""Return the set of IMAP flags that correspond to the letter codes.
Args:
Expand All @@ -117,11 +117,11 @@ def get_file(cls) -> str:
return 'dovecot-keywords'

@classmethod
def get_default(cls: Type[_MFT], base_dir: str) -> _MFT:
def get_default(cls: type[_MFT], base_dir: str) -> _MFT:
return cls([])

@classmethod
def open(cls: Type[_MFT], base_dir: str, fp: IO[str]) -> _MFT:
def open(cls: type[_MFT], base_dir: str, fp: IO[str]) -> _MFT:
ret = []
for line in fp:
i, kwd = line.split()
Expand Down

0 comments on commit 6fb862e

Please sign in to comment.