diff --git a/python/src/deltachat/account.py b/python/src/deltachat/account.py index f11a4f99d..045dd4de3 100644 --- a/python/src/deltachat/account.py +++ b/python/src/deltachat/account.py @@ -7,8 +7,6 @@ from contextlib import contextmanager import os import time from array import array -from queue import Queue - import deltachat from . import const from .capi import ffi, lib @@ -17,6 +15,7 @@ from .chat import Chat from .message import Message from .contact import Contact from .eventlogger import EventLogger +from .tracker import ImexTracker from .hookspec import AccountHookSpecs, account_hookimpl @@ -45,13 +44,14 @@ class Account(object): self._threads = IOThreads(self._dc_context, self._evlogger._log_event) # initialize per-account plugin system - self.plugin_manager = AccountHookSpecs._make_plugin_manager() - self.plugin_manager.register(self._evlogger) + self._pm = AccountHookSpecs._make_plugin_manager() + self.add_account_plugin(self) + self.add_account_plugin(self._evlogger) # send all FFI events for this account to a plugin hook def _ll_event(ctx, evt_name, data1, data2): assert ctx == self._dc_context - self.plugin_manager.hook.process_low_level_event( + self._pm.hook.process_low_level_event( account=self, event_name=evt_name, data1=data1, data2=data2 ) deltachat.set_context_callback(self._dc_context, _ll_event) @@ -64,6 +64,19 @@ class Account(object): self._configkeys = self.get_config("sys.config_keys").split() atexit.register(self.shutdown) + @account_hookimpl + def process_low_level_event(self, event_name, data1, data2): + if event_name == "DC_EVENT_CONFIGURE_PROGRESS": + if data1 == 0 or data1 == 1000: + success = data1 == 1000 + self._pm.hook.configure_completed(success=success) + + 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): # self.shutdown() @@ -519,7 +532,7 @@ class Account(object): deltachat.clear_context_callback(self._dc_context) del self._dc_context atexit.unregister(self.shutdown) - self.plugin_manager.unregister(self._evlogger) + self._pm.unregister(self._evlogger) def set_location(self, latitude=0.0, longitude=0.0, accuracy=0.0): """set a new location. It effects all chats where we currently @@ -538,33 +551,9 @@ class Account(object): @contextmanager def temp_plugin(self, plugin): """ run a code block with the given plugin temporarily registered. """ - self.plugin_manager.register(plugin) + self._pm.register(plugin) yield plugin - self.plugin_manager.unregister(plugin) - - -class ImexTracker: - def __init__(self): - self._imex_events = Queue() - - @account_hookimpl - def process_low_level_event(self, event_name, data1, data2): - if event_name == "DC_EVENT_IMEX_PROGRESS": - self._imex_events.put(data1) - elif event_name == "DC_EVENT_IMEX_FILE_WRITTEN": - self._imex_events.put(data1) - - def wait_finish(self, progress_timeout=60): - """ Return list of written files, raise ValueError if ExportFailed. """ - files_written = [] - while True: - ev = self._imex_events.get(timeout=progress_timeout) - if isinstance(ev, str): - files_written.append(ev) - elif ev == 0: - raise ValueError("export failed, exp-files: {}".format(files_written)) - elif ev == 1000: - return files_written + self._pm.unregister(plugin) class IOThreads: diff --git a/python/src/deltachat/eventlogger.py b/python/src/deltachat/eventlogger.py index 006459feb..95831088e 100644 --- a/python/src/deltachat/eventlogger.py +++ b/python/src/deltachat/eventlogger.py @@ -1,4 +1,3 @@ -import threading import time from .hookspec import account_hookimpl diff --git a/python/src/deltachat/hookspec.py b/python/src/deltachat/hookspec.py index 6a65d40c9..c960b4a82 100644 --- a/python/src/deltachat/hookspec.py +++ b/python/src/deltachat/hookspec.py @@ -23,3 +23,7 @@ class AccountHookSpecs: @account_hookspec def process_low_level_event(self, event_name, data1, data2): """ process a CFFI low level events for a given account. """ + + @account_hookspec + def configure_completed(self, success): + """ Called when a configure process completed. """ diff --git a/python/src/deltachat/tracker.py b/python/src/deltachat/tracker.py new file mode 100644 index 000000000..5646a759a --- /dev/null +++ b/python/src/deltachat/tracker.py @@ -0,0 +1,74 @@ + +from queue import Queue +from threading import Event + +from .hookspec import account_hookimpl + + +class ImexFailed(RuntimeError): + """ Exception for signalling that import/export operations failed.""" + + +class ImexTracker: + def __init__(self): + self._imex_events = Queue() + + @account_hookimpl + def process_low_level_event(self, event_name, data1, data2): + if event_name == "DC_EVENT_IMEX_PROGRESS": + self._imex_events.put(data1) + elif event_name == "DC_EVENT_IMEX_FILE_WRITTEN": + self._imex_events.put(data1) + + def wait_finish(self, progress_timeout=60): + """ Return list of written files, raise ValueError if ExportFailed. """ + files_written = [] + while True: + ev = self._imex_events.get(timeout=progress_timeout) + if isinstance(ev, str): + files_written.append(ev) + elif ev == 0: + raise ImexFailed("export failed, exp-files: {}".format(files_written)) + elif ev == 1000: + return files_written + + +class ConfigureFailed(RuntimeError): + """ Exception for signalling that configuration failed.""" + + +class ConfigureTracker: + def __init__(self): + self._configure_events = Queue() + self._smtp_finished = Event() + self._imap_finished = Event() + self._ffi_events = [] + + @account_hookimpl + def process_low_level_event(self, event_name, data1, data2): + self._ffi_events.append((event_name, data1, data2)) + if event_name == "DC_EVENT_SMTP_CONNECTED": + self._smtp_finished.set() + elif event_name == "DC_EVENT_IMAP_CONNECTED": + self._imap_finished.set() + + @account_hookimpl + def configure_completed(self, success): + self._configure_events.put(success) + + def wait_smtp_connected(self): + """ wait until smtp is configured. """ + self._smtp_finished.wait() + + def wait_imap_connected(self): + """ wait until smtp is configured. """ + self._imap_finished.wait() + + def wait_finish(self): + """ wait until configure is completed. + + Raise Exception if Configure failed + """ + if not self._configure_events.get(): + content = "\n".join("{}: {} {}".format(*args) for args in self._ffi_events) + raise ConfigureFailed(content) diff --git a/python/tests/conftest.py b/python/tests/conftest.py index 5e0141354..1e6423f3f 100644 --- a/python/tests/conftest.py +++ b/python/tests/conftest.py @@ -6,6 +6,7 @@ import pytest import requests import time from deltachat import Account +from deltachat.tracker import ConfigureTracker from deltachat import const from deltachat.capi import lib from _pytest.monkeypatch import MonkeyPatch @@ -164,8 +165,8 @@ def acfactory(pytestconfig, tmpdir, request, session_liveconfig, datadir): def make_account(self, path, logid): ac = Account(path, logid=logid) - ac._evtracker = FFIEventTracker(ac) - ac.plugin_manager.register(ac._evtracker) + ac._evtracker = ac.add_account_plugin(FFIEventTracker(ac)) + ac._configtracker = ac.add_account_plugin(ConfigureTracker()) self._finalizers.append(ac.shutdown) return ac @@ -232,17 +233,16 @@ def acfactory(pytestconfig, tmpdir, request, session_liveconfig, datadir): def get_one_online_account(self, pre_generated_key=True): ac1 = self.get_online_configuring_account( pre_generated_key=pre_generated_key) - wait_successful_IMAP_SMTP_connection(ac1) - wait_configuration_progress(ac1, 1000) + ac1._configtracker.wait_imap_connected() + ac1._configtracker.wait_smtp_connected() + ac1._configtracker.wait_finish() return ac1 def get_two_online_accounts(self): ac1 = self.get_online_configuring_account() ac2 = self.get_online_configuring_account() - wait_successful_IMAP_SMTP_connection(ac1) - wait_configuration_progress(ac1, 1000) - wait_successful_IMAP_SMTP_connection(ac2) - wait_configuration_progress(ac2, 1000) + ac1._configtracker.wait_finish() + ac2._configtracker.wait_finish() return ac1, ac2 def clone_online_account(self, account, pre_generated_key=True):