- simplify to offer start() and shutdown() as primary account methods, strike start_threads/stop_threads.

- introduce update_config(kwargs) method.
- group APIs a bit better
This commit is contained in:
holger krekel
2020-02-23 17:00:13 +01:00
parent c851f9d5a3
commit 5c8f558f60

View File

@@ -18,11 +18,17 @@ from .tracker import ImexTracker
from . import hookspec from . import hookspec
class MissingCredentials(ValueError):
""" Account is missing `addr` and `mail_pw` config values. """
class Account(object): 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 by the underlying deltachat core library. All public Account methods are
meant to be memory-safe and return memory-safe objects. meant to be memory-safe and return memory-safe objects.
""" """
MissingCredentials = MissingCredentials
def __init__(self, db_path, os_name=None): def __init__(self, db_path, os_name=None):
""" initialize account object. """ initialize account object.
@@ -80,12 +86,6 @@ class Account(object):
msg = self.get_message_by_id(ffi_event.data2) msg = self.get_message_by_id(ffi_event.data2)
self._pm.hook.process_message_delivered(message=msg) self._pm.hook.process_message_delivered(message=msg)
def add_account_plugin(self, plugin):
""" add an account plugin whose hookimpls are called. """
self._pm.register(plugin)
self._pm.check_pending()
return plugin
# def __del__(self): # def __del__(self):
# self.shutdown() # self.shutdown()
@@ -163,16 +163,14 @@ class Account(object):
if res == 0: if res == 0:
raise Exception("Failed to set key") raise Exception("Failed to set key")
def configure(self, **kwargs): def update_config(self, kwargs):
""" set config values and configure this account. """ update config values.
:param kwargs: name=value config settings for this account. :param kwargs: name=value config settings for this account.
values need to be unicode. values need to be unicode.
:returns: None :returns: None
""" """
for name, value in kwargs.items(): for key, value in kwargs.items():
self.set_config(name, value) self.set_config(key, str(value))
lib.dc_configure(self._dc_context)
def is_configured(self): def is_configured(self):
""" determine if the account is configured already; an initial connection """ determine if the account is configured already; an initial connection
@@ -180,7 +178,7 @@ class Account(object):
:returns: True if account is configured. :returns: True if account is configured.
""" """
return lib.dc_is_configured(self._dc_context) return bool(lib.dc_is_configured(self._dc_context))
def set_avatar(self, img_path): def set_avatar(self, img_path):
"""Set self avatar. """Set self avatar.
@@ -397,13 +395,18 @@ class Account(object):
lib.dc_delete_msgs(self._dc_context, msg_ids, len(msg_ids)) lib.dc_delete_msgs(self._dc_context, msg_ids, len(msg_ids))
def export_self_keys(self, path): 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.
"""
return self._export(path, imex_cmd=1) return self._export(path, imex_cmd=1)
def export_all(self, path): def export_all(self, path):
"""return new file containing a backup of all database state """return new file containing a backup of all database state
(chats, contacts, keys, media, ...). The file is created in the (chats, contacts, keys, media, ...). The file is created in the
the `path` directory. the `path` directory.
Note that the account does not have to be started.
""" """
export_files = self._export(path, 11) export_files = self._export(path, 11)
if len(export_files) != 1: if len(export_files) != 1:
@@ -421,6 +424,8 @@ class Account(object):
""" 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 The last imported key is made the default keys unless its name
contains the string legacy. Public keys are not imported. contains the string legacy. Public keys are not imported.
Note that the account does not have to be started.
""" """
self._import(path, imex_cmd=2) self._import(path, imex_cmd=2)
@@ -502,41 +507,6 @@ class Account(object):
raise ValueError("could not join group") raise ValueError("could not join group")
return Chat(self, chat_id) return Chat(self, chat_id)
def stop_ongoing(self):
lib.dc_stop_ongoing_process(self._dc_context)
#
# meta API for start/stop and event based processing
#
def start_threads(self):
""" start IMAP/SMTP threads (and configure account if it hasn't happened).
:raises: ValueError if 'addr' or 'mail_pw' are not configured.
:returns: None
"""
if not self.is_configured():
self.configure()
self._threads.start()
def stop_threads(self, wait=True):
""" stop IMAP/SMTP threads. """
if self._threads.is_started():
self.stop_ongoing()
self._threads.stop(wait=wait)
def shutdown(self, wait=True):
""" stop threads and close and remove underlying dc_context and callbacks. """
if hasattr(self, "_dc_context") and hasattr(self, "_threads"):
# print("SHUTDOWN", self)
self.stop_threads(wait=False)
lib.dc_close(self._dc_context)
self.stop_threads(wait=wait) # to wait for threads
deltachat.clear_context_callback(self._dc_context)
del self._dc_context
atexit.unregister(self.shutdown)
self._pm.hook.after_shutdown()
def set_location(self, latitude=0.0, longitude=0.0, accuracy=0.0): def set_location(self, latitude=0.0, longitude=0.0, accuracy=0.0):
"""set a new location. It effects all chats where we currently """set a new location. It effects all chats where we currently
have enabled location streaming. have enabled location streaming.
@@ -551,13 +521,58 @@ class Account(object):
if dc_res == 0: if dc_res == 0:
raise ValueError("no chat is streaming locations") raise ValueError("no chat is streaming locations")
#
# meta API for start/stop and event based processing
#
def add_account_plugin(self, plugin):
""" add an account plugin whose hookimpls are called. """
self._pm.register(plugin)
self._pm.check_pending()
return plugin
@contextmanager @contextmanager
def temp_plugin(self, plugin): def temp_plugin(self, plugin):
""" run a code block with the given plugin temporarily registered. """ """ run a with-block with the given plugin temporarily registered. """
self._pm.register(plugin) self._pm.register(plugin)
yield plugin yield plugin
self._pm.unregister(plugin) self._pm.unregister(plugin)
def stop_ongoing(self):
""" Stop ongoing securejoin, configuration or other core jobs. """
lib.dc_stop_ongoing_process(self._dc_context)
def start(self):
""" start this account (activate imap/smtp threads etc.)
and return immediately.
If this account is not configured, an internal configuration
job will be scheduled if config values are sufficiently specified.
:raises MissingCredentials: if `addr` and `mail_pw` values are not set.
:returns: None
"""
if not self.is_configured():
if not self.get_config("addr") or not self.get_config("mail_pwd"):
raise MissingCredentials("addr or mail_pwd not set in config")
lib.dc_configure(self._dc_context)
self._threads.start()
def shutdown(self, wait=True):
""" shutdown account, stop threads and close and remove
underlying dc_context and callbacks. """
if hasattr(self, "_dc_context") and hasattr(self, "_threads"):
if self._threads.is_started():
self.stop_ongoing()
self._threads.stop(wait=False)
lib.dc_close(self._dc_context)
self._threads.stop(wait=wait) # to wait for threads
deltachat.clear_context_callback(self._dc_context)
del self._dc_context
atexit.unregister(self.shutdown)
self._pm.hook.after_shutdown()
class IOThreads: class IOThreads:
def __init__(self, account): def __init__(self, account):