apply isort and black formatters, add format checking to CI

This commit is contained in:
adbenitez
2022-05-29 21:11:49 -04:00
parent 62b50c87d4
commit 16e0f0e986
26 changed files with 899 additions and 575 deletions

View File

@@ -1,37 +1,48 @@
""" Account class implementation. """
from __future__ import print_function
import os
from array import array
from contextlib import contextmanager
from email.utils import parseaddr
from threading import Event
import os
from array import array
from . import const
from typing import Any, Dict, Generator, List, Optional, Union
from . import const, hookspec
from .capi import ffi, lib
from .cutil import as_dc_charpointer, from_dc_charpointer, from_optional_dc_charpointer, iter_array, DCLot
from .chat import Chat
from .message import Message
from .contact import Contact
from .tracker import ImexTracker, ConfigureTracker
from . import hookspec
from .cutil import (
DCLot,
as_dc_charpointer,
from_dc_charpointer,
from_optional_dc_charpointer,
iter_array,
)
from .events import EventThread
from typing import Union, Any, Dict, Optional, List, Generator
from .message import Message
from .tracker import ConfigureTracker, ImexTracker
class MissingCredentials(ValueError):
""" Account is missing `addr` and `mail_pw` config values. """
"""Account is missing `addr` and `mail_pw` config values."""
def get_core_info():
""" get some system info. """
"""get some system info."""
from tempfile import NamedTemporaryFile
with NamedTemporaryFile() as path:
path.close()
return get_dc_info_as_dict(ffi.gc(
lib.dc_context_new(as_dc_charpointer(""), as_dc_charpointer(path.name), ffi.NULL),
lib.dc_context_unref,
))
return get_dc_info_as_dict(
ffi.gc(
lib.dc_context_new(
as_dc_charpointer(""), as_dc_charpointer(path.name), ffi.NULL
),
lib.dc_context_unref,
)
)
def get_dc_info_as_dict(dc_context):
@@ -46,14 +57,15 @@ def get_dc_info_as_dict(dc_context):
class Account(object):
""" Each account is tied to a sqlite database file which is fully managed
"""Each account is tied to a sqlite database file which is fully managed
by the underlying deltachat core library. All public Account methods are
meant to be memory-safe and return memory-safe objects.
"""
MissingCredentials = MissingCredentials
def __init__(self, db_path, os_name=None, logging=True) -> None:
""" initialize account object.
"""initialize account object.
:param db_path: a path to the account database. The database
will be created if it doesn't exist.
@@ -83,11 +95,11 @@ class Account(object):
hook.dc_account_init(account=self)
def disable_logging(self) -> None:
""" disable logging. """
"""disable logging."""
self._logging = False
def enable_logging(self) -> None:
""" re-enable logging. """
"""re-enable logging."""
self._logging = True
def __repr__(self):
@@ -102,11 +114,14 @@ class Account(object):
def _check_config_key(self, name: str) -> None:
if name not in self._configkeys:
raise KeyError("{!r} not a valid config key, existing keys: {!r}".format(
name, self._configkeys))
raise KeyError(
"{!r} not a valid config key, existing keys: {!r}".format(
name, self._configkeys
)
)
def get_info(self) -> Dict[str, str]:
""" return dictionary of built config parameters. """
"""return dictionary of built config parameters."""
return get_dc_info_as_dict(self._dc_context)
def dump_account_info(self, logfile):
@@ -126,7 +141,7 @@ class Account(object):
log("")
def set_stock_translation(self, id: int, string: str) -> None:
""" set stock translation string.
"""set stock translation string.
:param id: id of stock string (const.DC_STR_*)
:param value: string to set as new transalation
@@ -138,7 +153,7 @@ class Account(object):
raise ValueError("could not set translation string")
def set_config(self, name: str, value: Optional[str]) -> None:
""" set configuration values.
"""set configuration values.
:param name: config key name (unicode)
:param value: value to set (unicode)
@@ -157,7 +172,7 @@ class Account(object):
lib.dc_set_config(self._dc_context, namebytes, valuebytes)
def get_config(self, name: str) -> str:
""" return unicode string value.
"""return unicode string value.
:param name: configuration key to lookup (eg "addr" or "mail_pw")
:returns: unicode value
@@ -175,15 +190,17 @@ class Account(object):
In other words, you don't need this.
"""
res = lib.dc_preconfigure_keypair(self._dc_context,
as_dc_charpointer(addr),
as_dc_charpointer(public),
as_dc_charpointer(secret))
res = lib.dc_preconfigure_keypair(
self._dc_context,
as_dc_charpointer(addr),
as_dc_charpointer(public),
as_dc_charpointer(secret),
)
if res == 0:
raise Exception("Failed to set key")
def update_config(self, kwargs: Dict[str, Any]) -> None:
""" update config values.
"""update config values.
:param kwargs: name=value config settings for this account.
values need to be unicode.
@@ -193,7 +210,7 @@ class Account(object):
self.set_config(key, value)
def is_configured(self) -> bool:
""" determine if the account is configured already; an initial connection
"""determine if the account is configured already; an initial connection
to SMTP/IMAP has been verified.
:returns: True if account is configured.
@@ -219,18 +236,17 @@ class Account(object):
self.set_config("selfavatar", img_path)
def check_is_configured(self) -> None:
""" Raise ValueError if this account is not configured. """
"""Raise ValueError if this account is not configured."""
if not self.is_configured():
raise ValueError("need to configure first")
def get_latest_backupfile(self, backupdir) -> Optional[str]:
""" return the latest backup file in a given directory.
"""
"""return the latest backup file in a given directory."""
res = lib.dc_imex_has_backup(self._dc_context, as_dc_charpointer(backupdir))
return from_optional_dc_charpointer(res)
def get_blobdir(self) -> str:
""" return the directory for files.
"""return the directory for files.
All sent files are copied to this directory if necessary.
Place files there directly to avoid copying.
@@ -238,7 +254,7 @@ class Account(object):
return from_dc_charpointer(lib.dc_get_blobdir(self._dc_context))
def get_self_contact(self) -> Contact:
""" return this account's identity as a :class:`deltachat.contact.Contact`.
"""return this account's identity as a :class:`deltachat.contact.Contact`.
:returns: :class:`deltachat.contact.Contact`
"""
@@ -280,14 +296,14 @@ class Account(object):
elif isinstance(obj, str):
displayname, addr = parseaddr(obj)
else:
raise TypeError("don't know how to create chat for %r" % (obj, ))
raise TypeError("don't know how to create chat for %r" % (obj,))
if name is None and displayname:
name = displayname
return (name, addr)
def delete_contact(self, contact: Contact) -> bool:
""" delete a Contact.
"""delete a Contact.
:param contact: contact object obtained
:returns: True if deletion succeeded (contact was deleted)
@@ -298,7 +314,7 @@ class Account(object):
return bool(lib.dc_delete_contact(self._dc_context, contact_id))
def get_contact_by_addr(self, email: str) -> Optional[Contact]:
""" get a contact for the email address or None if it's blocked or doesn't exist. """
"""get a contact for the email address or None if it's blocked or doesn't exist."""
_, addr = parseaddr(email)
addr = as_dc_charpointer(addr)
contact_id = lib.dc_lookup_contact_id_by_addr(self._dc_context, addr)
@@ -307,20 +323,19 @@ class Account(object):
return None
def get_contact_by_id(self, contact_id: int) -> Contact:
""" return Contact instance or raise an exception.
"""return Contact instance or raise an exception.
:param contact_id: integer id of this contact.
:returns: :class:`deltachat.contact.Contact` instance.
"""
return Contact(self, contact_id)
def get_blocked_contacts(self) -> List[Contact]:
""" return a list of all blocked contacts.
"""return a list of all blocked contacts.
:returns: list of :class:`deltachat.contact.Contact` objects.
"""
dc_array = ffi.gc(
lib.dc_get_blocked_contacts(self._dc_context),
lib.dc_array_unref
lib.dc_get_blocked_contacts(self._dc_context), lib.dc_array_unref
)
return list(iter_array(dc_array, lambda x: Contact(self, x)))
@@ -345,21 +360,17 @@ class Account(object):
if with_self:
flags |= const.DC_GCL_ADD_SELF
dc_array = ffi.gc(
lib.dc_get_contacts(self._dc_context, flags, query),
lib.dc_array_unref
lib.dc_get_contacts(self._dc_context, flags, query), lib.dc_array_unref
)
return list(iter_array(dc_array, lambda x: Contact(self, x)))
def get_fresh_messages(self) -> Generator[Message, None, None]:
""" yield all fresh messages from all chats. """
dc_array = ffi.gc(
lib.dc_get_fresh_msgs(self._dc_context),
lib.dc_array_unref
)
"""yield all fresh messages from all chats."""
dc_array = ffi.gc(lib.dc_get_fresh_msgs(self._dc_context), lib.dc_array_unref)
yield from iter_array(dc_array, lambda x: Message.from_db(self, x))
def create_chat(self, obj) -> Chat:
""" Create a 1:1 chat with Account, Contact or e-mail address. """
"""Create a 1:1 chat with Account, Contact or e-mail address."""
return self.create_contact(obj).create_chat()
def create_group_chat(
@@ -385,13 +396,12 @@ class Account(object):
return chat
def get_chats(self) -> List[Chat]:
""" return list of chats.
"""return list of chats.
:returns: a list of :class:`deltachat.chat.Chat` objects.
"""
dc_chatlist = ffi.gc(
lib.dc_get_chatlist(self._dc_context, 0, ffi.NULL, 0),
lib.dc_chatlist_unref
lib.dc_get_chatlist(self._dc_context, 0, ffi.NULL, 0), lib.dc_chatlist_unref
)
assert dc_chatlist != ffi.NULL
@@ -405,14 +415,14 @@ class Account(object):
return Contact(self, const.DC_CONTACT_ID_DEVICE).create_chat()
def get_message_by_id(self, msg_id: int) -> Message:
""" return Message instance.
"""return Message instance.
:param msg_id: integer id of this message.
:returns: :class:`deltachat.message.Message` instance.
"""
return Message.from_db(self, msg_id)
def get_chat_by_id(self, chat_id: int) -> Chat:
""" return Chat instance.
"""return Chat instance.
:param chat_id: integer id of this chat.
:returns: :class:`deltachat.chat.Chat` instance.
:raises: ValueError if chat does not exist.
@@ -424,7 +434,7 @@ class Account(object):
return Chat(self, chat_id)
def mark_seen_messages(self, messages: List[Union[int, Message]]) -> None:
""" mark the given set of messages as seen.
"""mark the given set of messages as seen.
:param messages: a list of message ids or Message instances.
"""
@@ -438,7 +448,7 @@ class Account(object):
lib.dc_markseen_msgs(self._dc_context, msg_ids, len(messages))
def forward_messages(self, messages: List[Message], chat: Chat) -> None:
""" Forward list of messages to a chat.
"""Forward list of messages to a chat.
:param messages: list of :class:`deltachat.message.Message` object.
:param chat: :class:`deltachat.chat.Chat` object.
@@ -448,7 +458,7 @@ class Account(object):
lib.dc_forward_msgs(self._dc_context, msg_ids, len(msg_ids), chat.id)
def delete_messages(self, messages: List[Message]) -> None:
""" delete messages (local and remote).
"""delete messages (local and remote).
:param messages: list of :class:`deltachat.message.Message` object.
:returns: None
@@ -457,7 +467,7 @@ class Account(object):
lib.dc_delete_msgs(self._dc_context, msg_ids, len(msg_ids))
def export_self_keys(self, path):
""" export public and private keys to the specified directory.
"""export public and private keys to the specified directory.
Note that the account does not have to be started.
"""
@@ -481,7 +491,7 @@ class Account(object):
return imex_tracker.wait_finish()
def import_self_keys(self, path):
""" Import private keys found in the `path` directory.
"""Import private keys found in the `path` directory.
The last imported key is made the default keys unless its name
contains the string legacy. Public keys are not imported.
@@ -517,7 +527,7 @@ class Account(object):
return from_dc_charpointer(res)
def get_setup_contact_qr(self) -> str:
""" get/create Setup-Contact QR Code as ascii-string.
"""get/create Setup-Contact QR Code as ascii-string.
this string needs to be transferred to another DC account
in a second channel (typically used by mobiles with QRcode-show + scan UX)
@@ -527,10 +537,9 @@ class Account(object):
return from_dc_charpointer(res)
def check_qr(self, qr):
""" check qr code and return :class:`ScannedQRCode` instance representing the result"""
"""check qr code and return :class:`ScannedQRCode` instance representing the result"""
res = ffi.gc(
lib.dc_check_qr(self._dc_context, as_dc_charpointer(qr)),
lib.dc_lot_unref
lib.dc_check_qr(self._dc_context, as_dc_charpointer(qr)), lib.dc_lot_unref
)
lot = DCLot(res)
if lot.state() == const.DC_QR_ERROR:
@@ -538,7 +547,7 @@ class Account(object):
return ScannedQRCode(lot)
def qr_setup_contact(self, qr):
""" setup contact and return a Chat after contact is established.
"""setup contact and return a Chat after contact is established.
Note that this function may block for a long time as messages are exchanged
with the emitter of the QR code. On success a :class:`deltachat.chat.Chat` instance
@@ -552,7 +561,7 @@ class Account(object):
return Chat(self, chat_id)
def qr_join_chat(self, qr):
""" join a chat group through a QR code.
"""join a chat group through a QR code.
Note that this function may block for a long time as messages are exchanged
with the emitter of the QR code. On success a :class:`deltachat.chat.Chat` instance
@@ -587,7 +596,7 @@ class Account(object):
#
def add_account_plugin(self, plugin, name=None):
""" add an account plugin which implements one or more of
"""add an account plugin which implements one or more of
the :class:`deltachat.hookspec.PerAccount` hooks.
"""
if name and self._pm.has_plugin(name=name):
@@ -597,18 +606,18 @@ class Account(object):
return plugin
def remove_account_plugin(self, plugin, name=None):
""" remove an account plugin. """
"""remove an account plugin."""
self._pm.unregister(plugin, name=name)
@contextmanager
def temp_plugin(self, plugin):
""" run a with-block with the given plugin temporarily registered. """
"""run a with-block with the given plugin temporarily registered."""
self._pm.register(plugin)
yield plugin
self._pm.unregister(plugin)
def stop_ongoing(self):
""" Stop ongoing securejoin, configuration or other core jobs. """
"""Stop ongoing securejoin, configuration or other core jobs."""
lib.dc_stop_ongoing_process(self._dc_context)
def get_connectivity(self):
@@ -621,7 +630,7 @@ class Account(object):
return lib.dc_all_work_done(self._dc_context)
def start_io(self):
""" start this account's IO scheduling (Rust-core async scheduler)
"""start this account's IO scheduling (Rust-core async scheduler)
If this account is not configured an Exception is raised.
You need to call account.configure() and account.wait_configure_finish()
@@ -665,7 +674,7 @@ class Account(object):
lib.dc_maybe_network(self._dc_context)
def configure(self, reconfigure: bool = False) -> ConfigureTracker:
""" Start configuration process and return a Configtracker instance
"""Start configuration process and return a Configtracker instance
on which you can block with wait_finish() to get a True/False success
value for the configuration process.
"""
@@ -678,11 +687,11 @@ class Account(object):
return configtracker
def wait_shutdown(self) -> None:
""" wait until shutdown of this account has completed. """
"""wait until shutdown of this account has completed."""
self._shutdown_event.wait()
def stop_io(self) -> None:
""" stop core IO scheduler if it is running. """
"""stop core IO scheduler if it is running."""
self.log("stop_ongoing")
self.stop_ongoing()
@@ -690,7 +699,7 @@ class Account(object):
lib.dc_stop_io(self._dc_context)
def shutdown(self) -> None:
""" shutdown and destroy account (stop callback thread, close and remove
"""shutdown and destroy account (stop callback thread, close and remove
underlying dc_context)."""
if self._dc_context is None:
return