mirror of
https://github.com/chatmail/core.git
synced 2026-04-25 01:16:29 +03:00
python: start integration of get_next_event
This commit is contained in:
@@ -17,54 +17,6 @@ except DistributionNotFound:
|
|||||||
__version__ = "0.0.0.dev0-unknown"
|
__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={}):
|
def get_dc_event_name(integer, _DC_EVENTNAME_MAP={}):
|
||||||
if not _DC_EVENTNAME_MAP:
|
if not _DC_EVENTNAME_MAP:
|
||||||
for name, val in vars(const).items():
|
for name, val in vars(const).items():
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ from .message import Message, map_system_message
|
|||||||
from .contact import Contact
|
from .contact import Contact
|
||||||
from .tracker import ImexTracker
|
from .tracker import ImexTracker
|
||||||
from . import hookspec, iothreads
|
from . import hookspec, iothreads
|
||||||
|
from .eventlogger import FFIEvent
|
||||||
|
|
||||||
|
|
||||||
class MissingCredentials(ValueError):
|
class MissingCredentials(ValueError):
|
||||||
@@ -63,6 +64,7 @@ class Account(object):
|
|||||||
self._configkeys = self.get_config("sys.config_keys").split()
|
self._configkeys = self.get_config("sys.config_keys").split()
|
||||||
atexit.register(self.shutdown)
|
atexit.register(self.shutdown)
|
||||||
hook.dc_account_init(account=self)
|
hook.dc_account_init(account=self)
|
||||||
|
self._threads.start()
|
||||||
|
|
||||||
def disable_logging(self):
|
def disable_logging(self):
|
||||||
""" disable logging. """
|
""" disable logging. """
|
||||||
@@ -75,8 +77,7 @@ class Account(object):
|
|||||||
@hookspec.account_hookimpl
|
@hookspec.account_hookimpl
|
||||||
def ac_process_ffi_event(self, ffi_event):
|
def ac_process_ffi_event(self, ffi_event):
|
||||||
for name, kwargs in self._map_ffi_event(ffi_event):
|
for name, kwargs in self._map_ffi_event(ffi_event):
|
||||||
ev = HookEvent(self, name=name, kwargs=kwargs)
|
yield HookEvent(self, name=name, kwargs=kwargs)
|
||||||
self._hook_event_queue.put(ev)
|
|
||||||
|
|
||||||
# def __del__(self):
|
# def __del__(self):
|
||||||
# self.shutdown()
|
# self.shutdown()
|
||||||
@@ -562,7 +563,7 @@ class Account(object):
|
|||||||
""" Stop ongoing securejoin, configuration or other core jobs. """
|
""" Stop ongoing securejoin, configuration or other core jobs. """
|
||||||
lib.dc_stop_ongoing_process(self._dc_context)
|
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.)
|
""" start this account (activate imap/smtp threads etc.)
|
||||||
and return immediately.
|
and return immediately.
|
||||||
|
|
||||||
@@ -580,7 +581,6 @@ class Account(object):
|
|||||||
if not self.get_config("addr") or not self.get_config("mail_pw"):
|
if not self.get_config("addr") or not self.get_config("mail_pw"):
|
||||||
raise MissingCredentials("addr or mail_pwd not set in config")
|
raise MissingCredentials("addr or mail_pwd not set in config")
|
||||||
lib.dc_configure(self._dc_context)
|
lib.dc_configure(self._dc_context)
|
||||||
self._threads.start(callback_thread=callback_thread)
|
|
||||||
|
|
||||||
def wait_shutdown(self):
|
def wait_shutdown(self):
|
||||||
""" wait until shutdown of this account has completed. """
|
""" wait until shutdown of this account has completed. """
|
||||||
@@ -588,7 +588,7 @@ class Account(object):
|
|||||||
|
|
||||||
def shutdown(self, wait=True):
|
def shutdown(self, wait=True):
|
||||||
""" shutdown account, stop threads and close and remove
|
""" shutdown account, stop threads and close and remove
|
||||||
underlying dc_context and callbacks. """
|
underlying dc_context."""
|
||||||
dc_context = self._dc_context
|
dc_context = self._dc_context
|
||||||
if dc_context is None:
|
if dc_context is None:
|
||||||
return
|
return
|
||||||
@@ -624,10 +624,34 @@ class Account(object):
|
|||||||
raise RuntimeError("can only call iter_events() from one thread")
|
raise RuntimeError("can only call iter_events() from one thread")
|
||||||
self._in_use_iter_events = True
|
self._in_use_iter_events = True
|
||||||
while 1:
|
while 1:
|
||||||
event = self._hook_event_queue.get(timeout=timeout)
|
event = lib.dc_get_next_event(self._dc_context)
|
||||||
if event is None:
|
if event == ffi.NULL:
|
||||||
break
|
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):
|
def _map_ffi_event(self, ffi_event):
|
||||||
name = ffi_event.name
|
name = ffi_event.name
|
||||||
@@ -660,12 +684,6 @@ class Account(object):
|
|||||||
def _destroy_dc_context(dc_context, dc_context_unref=lib.dc_context_unref):
|
def _destroy_dc_context(dc_context, dc_context_unref=lib.dc_context_unref):
|
||||||
# destructor for dc_context
|
# destructor for dc_context
|
||||||
dc_context_unref(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:
|
class ScannedQRCode:
|
||||||
|
|||||||
@@ -6,21 +6,13 @@ from queue import Queue, Empty
|
|||||||
from .hookspec import account_hookimpl, global_hookimpl
|
from .hookspec import account_hookimpl, global_hookimpl
|
||||||
|
|
||||||
|
|
||||||
@global_hookimpl
|
# @global_hookimpl
|
||||||
def dc_account_init(account):
|
# def dc_account_init(account):
|
||||||
# send all FFI events for this account to a plugin hook
|
# account._threads.start()
|
||||||
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_after_shutdown(dc_context):
|
||||||
|
|
||||||
@global_hookimpl
|
|
||||||
def dc_account_after_shutdown(dc_context):
|
|
||||||
deltachat.clear_context_callback(dc_context)
|
|
||||||
|
|
||||||
|
|
||||||
class FFIEvent:
|
class FFIEvent:
|
||||||
|
|||||||
@@ -4,8 +4,9 @@ import time
|
|||||||
|
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
|
||||||
from .capi import lib
|
from .capi import ffi, lib
|
||||||
|
import deltachat
|
||||||
|
from .eventlogger import FFIEvent
|
||||||
|
|
||||||
class IOThreads:
|
class IOThreads:
|
||||||
def __init__(self, account):
|
def __init__(self, account):
|
||||||
@@ -17,19 +18,10 @@ class IOThreads:
|
|||||||
def is_started(self):
|
def is_started(self):
|
||||||
return len(self._name2thread) > 0
|
return len(self._name2thread) > 0
|
||||||
|
|
||||||
def start(self, callback_thread):
|
def start(self):
|
||||||
assert not self.is_started()
|
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)
|
||||||
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)
|
|
||||||
|
|
||||||
def _start_one_thread(self, name, func):
|
def _start_one_thread(self, name, func):
|
||||||
self._name2thread[name] = t = threading.Thread(target=func, name=name)
|
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.
|
# not in between checking for quitflag and entering idle.
|
||||||
time.sleep(0.5)
|
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:
|
if wait:
|
||||||
for name, thread in self._name2thread.items():
|
for name, thread in self._name2thread.items():
|
||||||
if thread != threading.currentThread():
|
if thread != threading.currentThread():
|
||||||
@@ -68,39 +54,8 @@ class IOThreads:
|
|||||||
ev = next(it)
|
ev = next(it)
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
break
|
break
|
||||||
|
print("{}", ev)
|
||||||
self.account.ac_log_line("calling hook name={} kwargs={}".format(ev.name, ev.kwargs))
|
self.account.ac_log_line("calling hook name={} kwargs={}".format(ev.name, ev.kwargs))
|
||||||
ev.call_hook()
|
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)
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ from __future__ import print_function
|
|||||||
|
|
||||||
import threading
|
import threading
|
||||||
import time
|
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 import register_global_plugin
|
||||||
from deltachat.hookspec import global_hookimpl
|
from deltachat.hookspec import global_hookimpl
|
||||||
from deltachat.capi import ffi
|
from deltachat.capi import ffi
|
||||||
@@ -43,13 +43,6 @@ def test_empty_context():
|
|||||||
capi.lib.dc_close(ctx)
|
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):
|
def test_dc_close_events(tmpdir, acfactory):
|
||||||
ac1 = acfactory.get_unconfigured_account()
|
ac1 = acfactory.get_unconfigured_account()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user