mirror of
https://github.com/deltachat/deltachat-core.git
synced 2025-10-05 02:29:28 +02:00
address #273 -- introduce attr for contact/chat/message/EventHandler
also refine the GC-handling of contact and message pointers. free-ing dc_context pointers currently does not work because the threads are keeping a reference and not shutting down. help on this shutdown issue from @r10s or @flub welcome. I think fixing the context GC/shutdown issue should not hold up merging this branch.
This commit is contained in:
parent
64a8d85be5
commit
75f8169072
5 changed files with 94 additions and 36 deletions
|
@ -11,7 +11,7 @@ def main():
|
||||||
long_description = long_description,
|
long_description = long_description,
|
||||||
author='Delta Chat contributors',
|
author='Delta Chat contributors',
|
||||||
setup_requires=['cffi>=1.0.0'],
|
setup_requires=['cffi>=1.0.0'],
|
||||||
install_requires=['cffi>=1.0.0', 'requests'],
|
install_requires=['cffi>=1.0.0', 'requests', 'attr'],
|
||||||
packages=setuptools.find_packages('src'),
|
packages=setuptools.find_packages('src'),
|
||||||
package_dir={'': 'src'},
|
package_dir={'': 'src'},
|
||||||
cffi_modules=['src/deltachat/_build.py:ffibuilder'],
|
cffi_modules=['src/deltachat/_build.py:ffibuilder'],
|
||||||
|
|
|
@ -7,14 +7,15 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from Queue import Queue
|
from Queue import Queue
|
||||||
|
|
||||||
|
import deltachat
|
||||||
from . import capi
|
from . import capi
|
||||||
from .capi import ffi
|
from .capi import ffi
|
||||||
import deltachat
|
from .myattr import attrib_CData, attrs, attrib_int, cached_property
|
||||||
|
|
||||||
|
|
||||||
class EventHandler:
|
@attrs
|
||||||
def __init__(self, dc_context):
|
class EventHandler(object):
|
||||||
self.dc_context = dc_context
|
dc_context = attrib_CData()
|
||||||
|
|
||||||
def read_url(self, url):
|
def read_url(self, url):
|
||||||
try:
|
try:
|
||||||
|
@ -75,14 +76,17 @@ class EventLogger:
|
||||||
tname, self.logid, evt_name, data1, data2))
|
tname, self.logid, evt_name, data1, data2))
|
||||||
|
|
||||||
|
|
||||||
class Contact:
|
@attrs
|
||||||
def __init__(self, dc_context, contact_id):
|
class Contact(object):
|
||||||
self.dc_context = dc_context
|
dc_context = attrib_CData()
|
||||||
self.id = contact_id
|
id = attrib_int()
|
||||||
self.dc_contact_t = capi.lib.dc_get_contact(self.dc_context, contact_id)
|
|
||||||
|
@cached_property # only get it once because we only free it once
|
||||||
|
def dc_contact_t(self):
|
||||||
|
return capi.lib.dc_get_contact(self.dc_context, self.id)
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
capi.lib.free(self.dc_contact_t)
|
capi.lib.dc_contact_unref(self.dc_contact_t)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def addr(self):
|
def addr(self):
|
||||||
|
@ -101,33 +105,37 @@ class Contact:
|
||||||
return capi.lib.dc_contact_is_verified(self.dc_contact_t)
|
return capi.lib.dc_contact_is_verified(self.dc_contact_t)
|
||||||
|
|
||||||
|
|
||||||
class Chat:
|
@attrs
|
||||||
def __init__(self, dc_context, chat_id):
|
class Chat(object):
|
||||||
self.dc_context = dc_context
|
dc_context = attrib_CData()
|
||||||
self.id = chat_id
|
id = attrib_int()
|
||||||
|
|
||||||
def __eq__(self, other_chat):
|
@cached_property
|
||||||
return (
|
def dc_chat_t(self):
|
||||||
self.dc_context == getattr(other_chat, "dc_context", None) and
|
return capi.lib.get_chat(self.id)
|
||||||
self.id == getattr(other_chat, "id", None)
|
|
||||||
)
|
|
||||||
|
|
||||||
def __ne__(self, other_chat):
|
def __del__(self):
|
||||||
return not self == other_chat
|
capi.lib.dc_chat_unref(self.dc_chat_t)
|
||||||
|
|
||||||
def send_text_message(self, msg):
|
def send_text_message(self, msg):
|
||||||
""" return ID of the message in this chat.
|
""" send a text message and return Message instance. """
|
||||||
'msg' should be unicode"""
|
|
||||||
msg = convert_to_bytes_utf8(msg)
|
msg = convert_to_bytes_utf8(msg)
|
||||||
print ("chat id", self.id)
|
print ("chat id", self.id)
|
||||||
return capi.lib.dc_send_text_msg(self.dc_context, self.id, msg)
|
msg_id = capi.lib.dc_send_text_msg(self.dc_context, self.id, msg)
|
||||||
|
return Message(self.dc_context, msg_id)
|
||||||
|
|
||||||
|
|
||||||
class Message:
|
@attrs
|
||||||
def __init__(self, dc_context, msg_id):
|
class Message(object):
|
||||||
self.dc_context = dc_context
|
dc_context = attrib_CData()
|
||||||
self.id = msg_id
|
id = attrib_int()
|
||||||
self.dc_msg = capi.lib.dc_get_msg(self.dc_context, msg_id)
|
|
||||||
|
@cached_property
|
||||||
|
def dc_msg(self):
|
||||||
|
return capi.lib.dc_get_msg(self.dc_context, self.id)
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
capi.lib.dc_msg_unref(self.dc_msg_t)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def text(self):
|
def text(self):
|
||||||
|
@ -139,7 +147,7 @@ class Message:
|
||||||
return Chat(self.dc_context, chat_id)
|
return Chat(self.dc_context, chat_id)
|
||||||
|
|
||||||
|
|
||||||
class Account:
|
class Account(object):
|
||||||
def __init__(self, db_path, logid=None):
|
def __init__(self, db_path, logid=None):
|
||||||
self.dc_context = ctx = capi.lib.dc_context_new(
|
self.dc_context = ctx = capi.lib.dc_context_new(
|
||||||
capi.lib.py_dc_callback,
|
capi.lib.py_dc_callback,
|
||||||
|
@ -151,6 +159,15 @@ class Account:
|
||||||
self._evlogger = EventLogger(self.dc_context, logid)
|
self._evlogger = EventLogger(self.dc_context, logid)
|
||||||
self._threads = IOThreads(self.dc_context)
|
self._threads = IOThreads(self.dc_context)
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
# XXX this causes currently a segfault because
|
||||||
|
# the threads still have a reference to dc_context
|
||||||
|
#
|
||||||
|
# capi.lib.dc_context_unref(self.dc_context)
|
||||||
|
#
|
||||||
|
# let's for now leak memory instead of causing segfaults
|
||||||
|
pass
|
||||||
|
|
||||||
def set_config(self, **kwargs):
|
def set_config(self, **kwargs):
|
||||||
for name, value in kwargs.items():
|
for name, value in kwargs.items():
|
||||||
name = name.encode("utf8")
|
name = name.encode("utf8")
|
||||||
|
@ -184,7 +201,7 @@ class Account:
|
||||||
self.dc_context, contact_id)
|
self.dc_context, contact_id)
|
||||||
return Chat(self.dc_context, chat_id)
|
return Chat(self.dc_context, chat_id)
|
||||||
|
|
||||||
def get_message(self, msg_id):
|
def get_message_by_id(self, msg_id):
|
||||||
return Message(self.dc_context, msg_id)
|
return Message(self.dc_context, msg_id)
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
|
|
33
python/src/deltachat/myattr.py
Normal file
33
python/src/deltachat/myattr.py
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
from attr import attrs, attrib # noqa
|
||||||
|
from attr import validators as v
|
||||||
|
|
||||||
|
|
||||||
|
def attrib_int():
|
||||||
|
return attrib(validator=v.instance_of(int))
|
||||||
|
|
||||||
|
|
||||||
|
def attrib_CData():
|
||||||
|
from deltachat.capi import ffi
|
||||||
|
return attrib(validator=v.instance_of(ffi.CData))
|
||||||
|
|
||||||
|
|
||||||
|
# copied over unmodified from
|
||||||
|
# https://github.com/devpi/devpi/blob/master/common/devpi_common/types.py
|
||||||
|
# where it's also tested
|
||||||
|
|
||||||
|
def cached_property(f):
|
||||||
|
"""returns a cached property that is calculated by function f"""
|
||||||
|
def get(self):
|
||||||
|
try:
|
||||||
|
return self._property_cache[f]
|
||||||
|
except AttributeError:
|
||||||
|
self._property_cache = {}
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
x = self._property_cache[f] = f(self)
|
||||||
|
return x
|
||||||
|
|
||||||
|
def set(self, val):
|
||||||
|
propcache = self.__dict__.setdefault("_property_cache", {})
|
||||||
|
propcache[f] = val
|
||||||
|
return property(get, set)
|
|
@ -29,6 +29,13 @@ class TestOfflineAccount:
|
||||||
assert chat == chat2
|
assert chat == chat2
|
||||||
assert not (chat != chat2)
|
assert not (chat != chat2)
|
||||||
|
|
||||||
|
def test_message(self, acfactory):
|
||||||
|
ac1 = acfactory.get_offline_account()
|
||||||
|
contact1 = ac1.create_contact("some1@hello.com", name="some1")
|
||||||
|
chat = ac1.create_chat_by_contact(contact1)
|
||||||
|
msg = chat.send_text_message("msg1")
|
||||||
|
assert msg
|
||||||
|
|
||||||
|
|
||||||
class TestOnlineAccount:
|
class TestOnlineAccount:
|
||||||
def wait_successful_IMAP_SMTP_connection(self, account):
|
def wait_successful_IMAP_SMTP_connection(self, account):
|
||||||
|
@ -74,14 +81,14 @@ class TestOnlineAccount:
|
||||||
self.wait_successful_IMAP_SMTP_connection(ac2)
|
self.wait_successful_IMAP_SMTP_connection(ac2)
|
||||||
self.wait_configuration_progress(ac1, 1000)
|
self.wait_configuration_progress(ac1, 1000)
|
||||||
self.wait_configuration_progress(ac2, 1000)
|
self.wait_configuration_progress(ac2, 1000)
|
||||||
msg_id = chat.send_text_message("msg1")
|
msg = chat.send_text_message("msg1")
|
||||||
ev = ac1._evlogger.get_matching("DC_EVENT_MSG_DELIVERED")
|
ev = ac1._evlogger.get_matching("DC_EVENT_MSG_DELIVERED")
|
||||||
evt_name, data1, data2 = ev
|
evt_name, data1, data2 = ev
|
||||||
assert data1 == chat.id
|
assert data1 == chat.id
|
||||||
assert data2 == msg_id
|
assert data2 == msg.id
|
||||||
ev = ac2._evlogger.get_matching("DC_EVENT_MSGS_CHANGED")
|
ev = ac2._evlogger.get_matching("DC_EVENT_MSGS_CHANGED")
|
||||||
assert ev[2] == msg_id
|
assert ev[2] == msg.id
|
||||||
msg = ac2.get_message(msg_id)
|
msg = ac2.get_message_by_id(msg.id)
|
||||||
assert msg.text == "msg1"
|
assert msg.text == "msg1"
|
||||||
# note that ev[1] aka data1 contains a bogus channel id
|
# note that ev[1] aka data1 contains a bogus channel id
|
||||||
# probably should just not get passed from the core
|
# probably should just not get passed from the core
|
||||||
|
|
|
@ -12,6 +12,7 @@ commands = pytest {posargs:tests}
|
||||||
usedevelop = True
|
usedevelop = True
|
||||||
deps =
|
deps =
|
||||||
pytest
|
pytest
|
||||||
|
pytest-faulthandler
|
||||||
pdbpp
|
pdbpp
|
||||||
|
|
||||||
[testenv:lint]
|
[testenv:lint]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue