From 2cfd5754ca2f3b4f7f4209adf2d6b5b184e51466 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Tue, 19 May 2020 14:29:47 +0200 Subject: [PATCH] python: start integration of get_next_event --- python/src/deltachat/__init__.py | 48 ------------------------ python/src/deltachat/account.py | 48 ++++++++++++++++-------- python/src/deltachat/eventlogger.py | 18 +++------ python/src/deltachat/iothreads.py | 57 +++-------------------------- python/tests/test_lowlevel.py | 9 +---- 5 files changed, 45 insertions(+), 135 deletions(-) diff --git a/python/src/deltachat/__init__.py b/python/src/deltachat/__init__.py index 3d7e43e61..fbd20283d 100644 --- a/python/src/deltachat/__init__.py +++ b/python/src/deltachat/__init__.py @@ -17,54 +17,6 @@ except DistributionNotFound: __version__ = "0.0.0.dev0-unknown" -_DC_CALLBACK_MAP = {} - - -def py_dc_callback(ctx, evt, data1, data2): - """The global event handler. - - CFFI only allows us to set one global event handler, so this one - looks up the correct event handler for the given context. - """ - try: - callback = _DC_CALLBACK_MAP.get(ctx, lambda *a: 0) - except AttributeError: - # we are in a deep in GC-free/interpreter shutdown land - # nothing much better to do here than: - return 0 - - # the following code relates to the deltachat/_build.py's helper - # function which provides us signature info of an event call - evt_name = get_dc_event_name(evt) - event_sig_types = capi.lib.dc_get_event_signature_types(evt) - if data1 and event_sig_types & 1: - data1 = ffi.string(ffi.gc(ffi.cast('char*', data1), capi.lib.dc_str_unref)).decode("utf8") - if data2 and event_sig_types & 2: - data2 = ffi.string(ffi.gc(ffi.cast('char*', data2), capi.lib.dc_str_unref)).decode("utf8") - try: - if isinstance(data2, bytes): - data2 = data2.decode("utf8") - except UnicodeDecodeError: - # XXX ignoring the decode error is not quite correct but for now - # i don't want to hunt down encoding problems in the c lib - pass - try: - callback(ctx, evt_name, data1, data2) - except: # noqa - raise - - -def set_context_callback(dc_context, func): - _DC_CALLBACK_MAP[dc_context] = func - - -def clear_context_callback(dc_context): - try: - _DC_CALLBACK_MAP.pop(dc_context, None) - except AttributeError: - pass - - def get_dc_event_name(integer, _DC_EVENTNAME_MAP={}): if not _DC_EVENTNAME_MAP: for name, val in vars(const).items(): diff --git a/python/src/deltachat/account.py b/python/src/deltachat/account.py index c641a8e8f..03c9f12d7 100644 --- a/python/src/deltachat/account.py +++ b/python/src/deltachat/account.py @@ -17,6 +17,7 @@ from .message import Message, map_system_message from .contact import Contact from .tracker import ImexTracker from . import hookspec, iothreads +from .eventlogger import FFIEvent class MissingCredentials(ValueError): @@ -53,7 +54,7 @@ class Account(object): self._hook_event_queue = queue.Queue() self._in_use_iter_events = False self._shutdown_event = Event() - + # open database self.db_path = db_path if hasattr(db_path, "encode"): @@ -63,6 +64,7 @@ class Account(object): self._configkeys = self.get_config("sys.config_keys").split() atexit.register(self.shutdown) hook.dc_account_init(account=self) + self._threads.start() def disable_logging(self): """ disable logging. """ @@ -75,8 +77,7 @@ class Account(object): @hookspec.account_hookimpl def ac_process_ffi_event(self, ffi_event): for name, kwargs in self._map_ffi_event(ffi_event): - ev = HookEvent(self, name=name, kwargs=kwargs) - self._hook_event_queue.put(ev) + yield HookEvent(self, name=name, kwargs=kwargs) # def __del__(self): # self.shutdown() @@ -562,7 +563,7 @@ class Account(object): """ Stop ongoing securejoin, configuration or other core jobs. """ lib.dc_stop_ongoing_process(self._dc_context) - def start(self, callback_thread=True): + def start(self): """ start this account (activate imap/smtp threads etc.) and return immediately. @@ -580,7 +581,6 @@ class Account(object): if not self.get_config("addr") or not self.get_config("mail_pw"): raise MissingCredentials("addr or mail_pwd not set in config") lib.dc_configure(self._dc_context) - self._threads.start(callback_thread=callback_thread) def wait_shutdown(self): """ wait until shutdown of this account has completed. """ @@ -588,7 +588,7 @@ class Account(object): def shutdown(self, wait=True): """ shutdown account, stop threads and close and remove - underlying dc_context and callbacks. """ + underlying dc_context.""" dc_context = self._dc_context if dc_context is None: return @@ -624,10 +624,34 @@ class Account(object): raise RuntimeError("can only call iter_events() from one thread") self._in_use_iter_events = True while 1: - event = self._hook_event_queue.get(timeout=timeout) - if event is None: + event = lib.dc_get_next_event(self._dc_context) + if event == ffi.NULL: break - yield event + + ctx = self._dc_context + evt = lib.dc_event_get_id(event) + data1 = lib.dc_event_get_data1(event) + data2 = lib.dc_event_get_data2(event) + # the following code relates to the deltachat/_build.py's helper + # function which provides us signature info of an event call + evt_name = deltachat.get_dc_event_name(evt) + event_sig_types = lib.dc_get_event_signature_types(evt) + if data1 and event_sig_types & 1: + data1 = ffi.string(ffi.gc(ffi.cast('char*', data1), lib.dc_str_unref)).decode("utf8") + if data2 and event_sig_types & 2: + data2 = ffi.string(ffi.gc(ffi.cast('char*', data2), lib.dc_str_unref)).decode("utf8") + try: + if isinstance(data2, bytes): + data2 = data2.decode("utf8") + except UnicodeDecodeError: + # XXX ignoring the decode error is not quite correct but for now + # i don't want to hunt down encoding problems in the c lib + pass + + lib.dc_event_unref(event) + ffi_event = FFIEvent(name=evt_name, data1=data1, data2=data2) + for event in self._pm.hook.ac_process_ffi_event(account=self, ffi_event=ffi_event): + yield event def _map_ffi_event(self, ffi_event): name = ffi_event.name @@ -660,12 +684,6 @@ class Account(object): def _destroy_dc_context(dc_context, dc_context_unref=lib.dc_context_unref): # destructor for dc_context dc_context_unref(dc_context) - try: - deltachat.clear_context_callback(dc_context) - except (TypeError, AttributeError): - # we are deep into Python Interpreter shutdown, - # so no need to clear the callback context mapping. - pass class ScannedQRCode: diff --git a/python/src/deltachat/eventlogger.py b/python/src/deltachat/eventlogger.py index 5490e7e72..e225843e6 100644 --- a/python/src/deltachat/eventlogger.py +++ b/python/src/deltachat/eventlogger.py @@ -6,21 +6,13 @@ from queue import Queue, Empty from .hookspec import account_hookimpl, global_hookimpl -@global_hookimpl -def dc_account_init(account): - # send all FFI events for this account to a plugin hook - def _ll_event(ctx, evt_name, data1, data2): - assert ctx == account._dc_context - ffi_event = FFIEvent(name=evt_name, data1=data1, data2=data2) - account._pm.hook.ac_process_ffi_event( - account=account, ffi_event=ffi_event - ) - deltachat.set_context_callback(account._dc_context, _ll_event) +# @global_hookimpl +# def dc_account_init(account): + # account._threads.start() +# @global_hookimpl +# def dc_account_after_shutdown(dc_context): -@global_hookimpl -def dc_account_after_shutdown(dc_context): - deltachat.clear_context_callback(dc_context) class FFIEvent: diff --git a/python/src/deltachat/iothreads.py b/python/src/deltachat/iothreads.py index 1e9a864f1..901b6af7a 100644 --- a/python/src/deltachat/iothreads.py +++ b/python/src/deltachat/iothreads.py @@ -4,8 +4,9 @@ import time from contextlib import contextmanager -from .capi import lib - +from .capi import ffi, lib +import deltachat +from .eventlogger import FFIEvent class IOThreads: def __init__(self, account): @@ -17,19 +18,10 @@ class IOThreads: def is_started(self): return len(self._name2thread) > 0 - def start(self, callback_thread): + def start(self): assert not self.is_started() - self._start_one_thread("inbox", self.imap_thread_run) - self._start_one_thread("smtp", self.smtp_thread_run) - if callback_thread: - self._start_one_thread("cb", self.cb_thread_run) - - if int(self.account.get_config("mvbox_watch")): - self._start_one_thread("mvbox", self.mvbox_thread_run) - - if int(self.account.get_config("sentbox_watch")): - self._start_one_thread("sentbox", self.sentbox_thread_run) + self._start_one_thread("cb", self.cb_thread_run) def _start_one_thread(self, name, func): self._name2thread[name] = t = threading.Thread(target=func, name=name) @@ -49,12 +41,6 @@ class IOThreads: # not in between checking for quitflag and entering idle. time.sleep(0.5) - lib.dc_interrupt_imap_idle(self._dc_context) - lib.dc_interrupt_smtp_idle(self._dc_context) - if "mvbox" in self._name2thread: - lib.dc_interrupt_mvbox_idle(self._dc_context) - if "sentbox" in self._name2thread: - lib.dc_interrupt_sentbox_idle(self._dc_context) if wait: for name, thread in self._name2thread.items(): if thread != threading.currentThread(): @@ -68,39 +54,8 @@ class IOThreads: ev = next(it) except StopIteration: break + print("{}", ev) self.account.ac_log_line("calling hook name={} kwargs={}".format(ev.name, ev.kwargs)) ev.call_hook() - def imap_thread_run(self): - with self.log_execution("INBOX THREAD START"): - while not self._thread_quitflag: - lib.dc_perform_imap_jobs(self._dc_context) - if not self._thread_quitflag: - lib.dc_perform_imap_fetch(self._dc_context) - if not self._thread_quitflag: - lib.dc_perform_imap_idle(self._dc_context) - def mvbox_thread_run(self): - with self.log_execution("MVBOX THREAD"): - while not self._thread_quitflag: - lib.dc_perform_mvbox_jobs(self._dc_context) - if not self._thread_quitflag: - lib.dc_perform_mvbox_fetch(self._dc_context) - if not self._thread_quitflag: - lib.dc_perform_mvbox_idle(self._dc_context) - - def sentbox_thread_run(self): - with self.log_execution("SENTBOX THREAD"): - while not self._thread_quitflag: - lib.dc_perform_sentbox_jobs(self._dc_context) - if not self._thread_quitflag: - lib.dc_perform_sentbox_fetch(self._dc_context) - if not self._thread_quitflag: - lib.dc_perform_sentbox_idle(self._dc_context) - - def smtp_thread_run(self): - with self.log_execution("SMTP THREAD"): - while not self._thread_quitflag: - lib.dc_perform_smtp_jobs(self._dc_context) - if not self._thread_quitflag: - lib.dc_perform_smtp_idle(self._dc_context) diff --git a/python/tests/test_lowlevel.py b/python/tests/test_lowlevel.py index 6c62afb0f..9161f4dc1 100644 --- a/python/tests/test_lowlevel.py +++ b/python/tests/test_lowlevel.py @@ -2,7 +2,7 @@ from __future__ import print_function import threading import time -from deltachat import capi, cutil, const, set_context_callback, clear_context_callback +from deltachat import capi, cutil, const from deltachat import register_global_plugin from deltachat.hookspec import global_hookimpl from deltachat.capi import ffi @@ -43,13 +43,6 @@ def test_empty_context(): capi.lib.dc_close(ctx) -def test_callback_None2int(): - ctx = capi.lib.dc_context_new(ffi.NULL, ffi.NULL) - set_context_callback(ctx, lambda *args: None) - capi.lib.dc_close(ctx) - clear_context_callback(ctx) - - def test_dc_close_events(tmpdir, acfactory): ac1 = acfactory.get_unconfigured_account()