mirror of
https://github.com/chatmail/core.git
synced 2026-04-17 21:46:35 +03:00
safer handling of calling account hooks, refined shutdown comment
This commit is contained in:
@@ -698,7 +698,8 @@ class Account(object):
|
|||||||
# mark the event thread for shutdown (latest on next incoming event)
|
# mark the event thread for shutdown (latest on next incoming event)
|
||||||
self._event_thread.mark_shutdown()
|
self._event_thread.mark_shutdown()
|
||||||
|
|
||||||
# stop_io also causes an info event that will terminate the event thread
|
# stop_io also causes an info event which will wake up
|
||||||
|
# the EventThread's inner loop and let it notice the shutdown marker.
|
||||||
self.stop_io()
|
self.stop_io()
|
||||||
|
|
||||||
self.log("wait for event thread to finish")
|
self.log("wait for event thread to finish")
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import threading
|
import threading
|
||||||
|
import sys
|
||||||
|
import traceback
|
||||||
import time
|
import time
|
||||||
|
import io
|
||||||
import re
|
import re
|
||||||
import os
|
import os
|
||||||
from queue import Queue, Empty
|
from queue import Queue, Empty
|
||||||
@@ -221,17 +224,13 @@ class EventThread(threading.Thread):
|
|||||||
self._inner_run()
|
self._inner_run()
|
||||||
|
|
||||||
def _inner_run(self):
|
def _inner_run(self):
|
||||||
if self._marked_for_shutdown or self.account._dc_context is None:
|
|
||||||
return
|
|
||||||
event_emitter = ffi.gc(
|
event_emitter = ffi.gc(
|
||||||
lib.dc_get_event_emitter(self.account._dc_context),
|
lib.dc_get_event_emitter(self.account._dc_context),
|
||||||
lib.dc_event_emitter_unref,
|
lib.dc_event_emitter_unref,
|
||||||
)
|
)
|
||||||
while not self._marked_for_shutdown:
|
while not self._marked_for_shutdown:
|
||||||
event = lib.dc_get_next_event(event_emitter)
|
event = lib.dc_get_next_event(event_emitter)
|
||||||
if event == ffi.NULL:
|
if event == ffi.NULL or self._marked_for_shutdown:
|
||||||
break
|
|
||||||
if self._marked_for_shutdown:
|
|
||||||
break
|
break
|
||||||
evt = lib.dc_event_get_id(event)
|
evt = lib.dc_event_get_id(event)
|
||||||
data1 = lib.dc_event_get_data1_int(event)
|
data1 = lib.dc_event_get_data1_int(event)
|
||||||
@@ -245,15 +244,22 @@ class EventThread(threading.Thread):
|
|||||||
|
|
||||||
lib.dc_event_unref(event)
|
lib.dc_event_unref(event)
|
||||||
ffi_event = FFIEvent(name=evt_name, data1=data1, data2=data2)
|
ffi_event = FFIEvent(name=evt_name, data1=data1, data2=data2)
|
||||||
try:
|
self.account._pm.hook.ac_process_ffi_event(account=self, ffi_event=ffi_event)
|
||||||
self.account._pm.hook.ac_process_ffi_event(account=self, ffi_event=ffi_event)
|
for name, kwargs in self._map_ffi_event(ffi_event):
|
||||||
for name, kwargs in self._map_ffi_event(ffi_event):
|
hook = getattr(self.account._pm.hook, name)
|
||||||
self.account.log("calling hook name={} kwargs={}".format(name, kwargs))
|
info = "call {} kwargs={} failed".format(name, kwargs)
|
||||||
hook = getattr(self.account._pm.hook, name)
|
with self.swallow_and_log_exception(info):
|
||||||
hook(**kwargs)
|
hook(**kwargs)
|
||||||
except Exception:
|
|
||||||
if not self._marked_for_shutdown and self.account._dc_context is not None:
|
@contextmanager
|
||||||
raise
|
def swallow_and_log_exception(self, info):
|
||||||
|
try:
|
||||||
|
yield
|
||||||
|
except Exception as ex:
|
||||||
|
logfile = io.StringIO()
|
||||||
|
traceback.print_exception(*sys.exc_info(), file=logfile)
|
||||||
|
self.account.log("{}\nException {}\nTraceback:\n{}"
|
||||||
|
.format(info, ex, logfile.getvalue()))
|
||||||
|
|
||||||
def _map_ffi_event(self, ffi_event: FFIEvent):
|
def _map_ffi_event(self, ffi_event: FFIEvent):
|
||||||
name = ffi_event.name
|
name = ffi_event.name
|
||||||
|
|||||||
@@ -140,3 +140,15 @@ def test_get_info_open(tmpdir):
|
|||||||
info = cutil.from_dc_charpointer(lib.dc_get_info(ctx))
|
info = cutil.from_dc_charpointer(lib.dc_get_info(ctx))
|
||||||
assert 'deltachat_core_version' in info
|
assert 'deltachat_core_version' in info
|
||||||
assert 'database_dir' in info
|
assert 'database_dir' in info
|
||||||
|
|
||||||
|
|
||||||
|
def test_logged_hook_failure(acfactory):
|
||||||
|
ac1 = acfactory.get_pseudo_configured_account()
|
||||||
|
cap = []
|
||||||
|
ac1.log = cap.append
|
||||||
|
with ac1._event_thread.swallow_and_log_exception("some"):
|
||||||
|
0/0
|
||||||
|
assert cap
|
||||||
|
assert "some" in str(cap)
|
||||||
|
assert "ZeroDivisionError" in str(cap)
|
||||||
|
assert "Traceback" in str(cap)
|
||||||
|
|||||||
Reference in New Issue
Block a user