1
0
Fork 0
mirror of https://github.com/deltachat/deltachat-core.git synced 2025-10-04 02:09:17 +02:00

actually we can use ffi.gc(obj, destructor) instead of

the _UnrefStruct i invented -- this returns a gc-ed cdata
that will call the destructor when the object goes out of scope.
This commit is contained in:
holger krekel 2018-09-15 01:33:20 +02:00
parent 929d6da2ef
commit bf78f8e0aa
3 changed files with 53 additions and 73 deletions

View file

@ -15,7 +15,6 @@ from attr import validators as v
import deltachat import deltachat
from .capi import ffi, lib from .capi import ffi, lib
from .cutil import as_dc_charpointer, from_dc_charpointer, iter_array_and_unref from .cutil import as_dc_charpointer, from_dc_charpointer, iter_array_and_unref
from .types import DC_Context
from .chatting import Contact, Chat, Message from .chatting import Contact, Chat, Message
@ -34,12 +33,13 @@ class Account(object):
the default internal logging. the default internal logging.
""" """
self._dc_context = DC_Context( self._dc_context = ffi.gc(
lib.dc_context_new(lib.py_dc_callback, ffi.NULL, ffi.NULL) lib.dc_context_new(lib.py_dc_callback, ffi.NULL, ffi.NULL),
lib.dc_context_unref,
) )
if hasattr(db_path, "encode"): if hasattr(db_path, "encode"):
db_path = db_path.encode("utf8") db_path = db_path.encode("utf8")
if not lib.dc_open(self._dc_context.p, db_path, ffi.NULL): if not lib.dc_open(self._dc_context, db_path, ffi.NULL):
raise ValueError("Could not dc_open: {}".format(db_path)) raise ValueError("Could not dc_open: {}".format(db_path))
self._evhandler = EventHandler(self._dc_context) self._evhandler = EventHandler(self._dc_context)
self._evlogger = EventLogger(self._dc_context, logid) self._evlogger = EventLogger(self._dc_context, logid)
@ -55,7 +55,7 @@ class Account(object):
for name, value in kwargs.items(): for name, value in kwargs.items():
name = name.encode("utf8") name = name.encode("utf8")
value = value.encode("utf8") value = value.encode("utf8")
lib.dc_set_config(self._dc_context.p, name, value) lib.dc_set_config(self._dc_context, name, value)
def get_config(self, name): def get_config(self, name):
""" return unicode string value. """ return unicode string value.
@ -65,7 +65,7 @@ class Account(object):
:raises: KeyError if no config value was found. :raises: KeyError if no config value was found.
""" """
name = name.encode("utf8") name = name.encode("utf8")
res = lib.dc_get_config(self._dc_context.p, name, b'') res = lib.dc_get_config(self._dc_context, name, b'')
return from_dc_charpointer(res) return from_dc_charpointer(res)
def is_configured(self): def is_configured(self):
@ -73,7 +73,7 @@ class Account(object):
:returns: True if account is configured. :returns: True if account is configured.
""" """
return lib.dc_is_configured(self._dc_context.p) return lib.dc_is_configured(self._dc_context)
def check_is_configured(self): def check_is_configured(self):
""" Raise ValueError if this account is not configured. """ """ Raise ValueError if this account is not configured. """
@ -99,7 +99,7 @@ class Account(object):
""" """
name = as_dc_charpointer(name) name = as_dc_charpointer(name)
email = as_dc_charpointer(email) email = as_dc_charpointer(email)
contact_id = lib.dc_create_contact(self._dc_context.p, name, email) contact_id = lib.dc_create_contact(self._dc_context, name, email)
return Contact(self._dc_context, contact_id) return Contact(self._dc_context, contact_id)
def get_contacts(self, query=None, with_self=False, only_verified=False): def get_contacts(self, query=None, with_self=False, only_verified=False):
@ -117,8 +117,8 @@ class Account(object):
flags |= lib.DC_GCL_VERIFIED_ONLY flags |= lib.DC_GCL_VERIFIED_ONLY
if with_self: if with_self:
flags |= lib.DC_GCL_ADD_SELF flags |= lib.DC_GCL_ADD_SELF
dc_array_t = lib.dc_get_contacts(self._dc_context.p, flags, query) dc_array_t = lib.dc_get_contacts(self._dc_context, flags, query)
return list(iter_array_and_unref(dc_array_t, lambda x: Contact(self._dc_context.p, x))) return list(iter_array_and_unref(dc_array_t, lambda x: Contact(self._dc_context, x)))
def create_chat_by_contact(self, contact): def create_chat_by_contact(self, contact):
""" create or get an existing 1:1 chat object for the specified contact. """ create or get an existing 1:1 chat object for the specified contact.
@ -129,7 +129,7 @@ class Account(object):
contact_id = getattr(contact, "id", contact) contact_id = getattr(contact, "id", contact)
assert isinstance(contact_id, int) assert isinstance(contact_id, int)
chat_id = lib.dc_create_chat_by_contact_id( chat_id = lib.dc_create_chat_by_contact_id(
self._dc_context.p, contact_id) self._dc_context, contact_id)
return Chat(self._dc_context, chat_id) return Chat(self._dc_context, chat_id)
def create_chat_by_message(self, message): def create_chat_by_message(self, message):
@ -141,7 +141,7 @@ class Account(object):
""" """
msg_id = getattr(message, "id", message) msg_id = getattr(message, "id", message)
assert isinstance(msg_id, int) assert isinstance(msg_id, int)
chat_id = lib.dc_create_chat_by_msg_id(self._dc_context.p, msg_id) chat_id = lib.dc_create_chat_by_msg_id(self._dc_context, msg_id)
return Chat(self._dc_context, chat_id) return Chat(self._dc_context, chat_id)
def get_message_by_id(self, msg_id): def get_message_by_id(self, msg_id):
@ -158,22 +158,22 @@ class Account(object):
msg = getattr(msg, "id", msg) msg = getattr(msg, "id", msg)
arr.append(msg) arr.append(msg)
msg_ids = ffi.cast("uint32_t*", ffi.from_buffer(arr)) msg_ids = ffi.cast("uint32_t*", ffi.from_buffer(arr))
lib.dc_markseen_msgs(self._dc_context.p, msg_ids, len(messages)) lib.dc_markseen_msgs(self._dc_context, msg_ids, len(messages))
def start(self): def start(self):
""" configure this account object, start receiving events, """ configure this account object, start receiving events,
start IMAP/SMTP threads. """ start IMAP/SMTP threads. """
deltachat.set_context_callback(self._dc_context.p, self._process_event) deltachat.set_context_callback(self._dc_context, self._process_event)
lib.dc_configure(self._dc_context.p) lib.dc_configure(self._dc_context)
self._threads.start() self._threads.start()
def shutdown(self): def shutdown(self):
""" shutdown IMAP/SMTP threads and stop receiving events""" """ shutdown IMAP/SMTP threads and stop receiving events"""
deltachat.clear_context_callback(self._dc_context.p) deltachat.clear_context_callback(self._dc_context)
self._threads.stop(wait=True) self._threads.stop(wait=True)
def _process_event(self, ctx, evt_name, data1, data2): def _process_event(self, ctx, evt_name, data1, data2):
assert ctx == self._dc_context.p assert ctx == self._dc_context
self._evlogger(evt_name, data1, data2) self._evlogger(evt_name, data1, data2)
method = getattr(self._evhandler, evt_name.lower(), None) method = getattr(self._evhandler, evt_name.lower(), None)
if method is not None: if method is not None:
@ -201,8 +201,8 @@ class IOThreads:
def stop(self, wait=False): def stop(self, wait=False):
self._thread_quitflag = True self._thread_quitflag = True
lib.dc_interrupt_imap_idle(self._dc_context.p) lib.dc_interrupt_imap_idle(self._dc_context)
lib.dc_interrupt_smtp_idle(self._dc_context.p) lib.dc_interrupt_smtp_idle(self._dc_context)
if wait: if wait:
for name, thread in self._name2thread.items(): for name, thread in self._name2thread.items():
thread.join() thread.join()
@ -210,20 +210,20 @@ class IOThreads:
def imap_thread_run(self): def imap_thread_run(self):
print ("starting imap thread") print ("starting imap thread")
while not self._thread_quitflag: while not self._thread_quitflag:
lib.dc_perform_imap_jobs(self._dc_context.p) lib.dc_perform_imap_jobs(self._dc_context)
lib.dc_perform_imap_fetch(self._dc_context.p) lib.dc_perform_imap_fetch(self._dc_context)
lib.dc_perform_imap_idle(self._dc_context.p) lib.dc_perform_imap_idle(self._dc_context)
def smtp_thread_run(self): def smtp_thread_run(self):
print ("starting smtp thread") print ("starting smtp thread")
while not self._thread_quitflag: while not self._thread_quitflag:
lib.dc_perform_smtp_jobs(self._dc_context.p) lib.dc_perform_smtp_jobs(self._dc_context)
lib.dc_perform_smtp_idle(self._dc_context.p) lib.dc_perform_smtp_idle(self._dc_context)
@attr.s @attr.s
class EventHandler(object): class EventHandler(object):
_dc_context = attr.ib(validator=v.instance_of(DC_Context)) _dc_context = attr.ib(validator=v.instance_of(ffi.CData))
def read_url(self, url): def read_url(self, url):
try: try:
@ -251,7 +251,7 @@ class EventLogger:
self._event_queue = Queue() self._event_queue = Queue()
self._debug = debug self._debug = debug
if logid is None: if logid is None:
logid = str(self._dc_context.p).strip(">").split()[-1] logid = str(self._dc_context).strip(">").split()[-1]
self.logid = logid self.logid = logid
self._timeout = None self._timeout = None

View file

@ -1,9 +1,8 @@
""" chatting related objects: Contact, Chat, Message. """ """ chatting related objects: Contact, Chat, Message. """
from .cutil import as_dc_charpointer, from_dc_charpointer, iter_array_and_unref from .cutil import as_dc_charpointer, from_dc_charpointer, iter_array_and_unref
from .capi import lib from .capi import lib, ffi
from .types import cached_property, property_with_doc from .types import cached_property, property_with_doc
from .types import DC_Context, DC_Contact, DC_Chat, DC_Msg
import attr import attr
from attr import validators as v from attr import validators as v
@ -14,30 +13,33 @@ class Contact(object):
You obtain instances of it through :class:`deltachat.account.Account`. You obtain instances of it through :class:`deltachat.account.Account`.
""" """
_dc_context = attr.ib(validator=v.instance_of(DC_Context)) _dc_context = attr.ib(validator=v.instance_of(ffi.CData))
id = attr.ib(validator=v.instance_of(int)) id = attr.ib(validator=v.instance_of(int))
@cached_property @cached_property
def _dc_contact(self): def _dc_contact(self):
return DC_Contact(lib.dc_get_contact(self._dc_context.p, self.id)) return ffi.gc(
lib.dc_get_contact(self._dc_context, self.id),
lib.dc_contact_unref
)
@property_with_doc @property_with_doc
def addr(self): def addr(self):
""" normalized e-mail address for this account. """ """ normalized e-mail address for this account. """
return from_dc_charpointer(lib.dc_contact_get_addr(self._dc_contact.p)) return from_dc_charpointer(lib.dc_contact_get_addr(self._dc_contact))
@property_with_doc @property_with_doc
def display_name(self): def display_name(self):
""" display name for this contact. """ """ display name for this contact. """
return from_dc_charpointer(lib.dc_contact_get_display_name(self._dc_contact.p)) return from_dc_charpointer(lib.dc_contact_get_display_name(self._dc_contact))
def is_blocked(self): def is_blocked(self):
""" Return True if the contact is blocked. """ """ Return True if the contact is blocked. """
return lib.dc_contact_is_blocked(self._dc_contact.p) return lib.dc_contact_is_blocked(self._dc_contact)
def is_verified(self): def is_verified(self):
""" Return True if the contact is verified. """ """ Return True if the contact is verified. """
return lib.dc_contact_is_verified(self._dc_contact.p) return lib.dc_contact_is_verified(self._dc_contact)
@attr.s @attr.s
@ -46,12 +48,15 @@ class Chat(object):
You obtain instances of it through :class:`deltachat.account.Account`. You obtain instances of it through :class:`deltachat.account.Account`.
""" """
_dc_context = attr.ib(validator=v.instance_of(DC_Context)) _dc_context = attr.ib(validator=v.instance_of(ffi.CData))
id = attr.ib(validator=v.instance_of(int)) id = attr.ib(validator=v.instance_of(int))
@cached_property @cached_property
def _dc_chat(self): def _dc_chat(self):
return DC_Chat(lib.dc_get_chat(self._dc_context.p, self.id)) return ffi.gc(
lib.dc_get_chat(self._dc_context, self.id),
lib.dc_chat_unref
)
def is_deaddrop(self): def is_deaddrop(self):
""" return true if this chat is a deaddrop chat. """ """ return true if this chat is a deaddrop chat. """
@ -64,7 +69,7 @@ class Chat(object):
:returns: the resulting :class:`Message` instance :returns: the resulting :class:`Message` instance
""" """
msg = as_dc_charpointer(msg) msg = as_dc_charpointer(msg)
msg_id = lib.dc_send_text_msg(self._dc_context.p, self.id, msg) msg_id = lib.dc_send_text_msg(self._dc_context, self.id, msg)
return Message(self._dc_context, msg_id) return Message(self._dc_context, msg_id)
def get_messages(self): def get_messages(self):
@ -72,7 +77,7 @@ class Chat(object):
:returns: list of :class:`Message` objects for this chat. :returns: list of :class:`Message` objects for this chat.
""" """
dc_array_t = lib.dc_get_chat_msgs(self._dc_context.p, self.id, 0, 0) dc_array_t = lib.dc_get_chat_msgs(self._dc_context, self.id, 0, 0)
return list(iter_array_and_unref(dc_array_t, lambda x: Message(self._dc_context, x))) return list(iter_array_and_unref(dc_array_t, lambda x: Message(self._dc_context, x)))
def count_fresh_messages(self): def count_fresh_messages(self):
@ -80,14 +85,14 @@ class Chat(object):
:returns: number of fresh messages :returns: number of fresh messages
""" """
return lib.dc_get_fresh_msg_cnt(self._dc_context.p, self.id) return lib.dc_get_fresh_msg_cnt(self._dc_context, self.id)
def mark_noticed(self): def mark_noticed(self):
""" mark all messages in this chat as noticed. """ mark all messages in this chat as noticed.
Noticed messages are no longer fresh. Noticed messages are no longer fresh.
""" """
return lib.dc_marknoticed_chat(self._dc_context.p, self.id) return lib.dc_marknoticed_chat(self._dc_context, self.id)
@attr.s @attr.s
@ -97,12 +102,15 @@ class Message(object):
You obtain instances of it through :class:`deltachat.account.Account` or You obtain instances of it through :class:`deltachat.account.Account` or
:class:`Chat`. :class:`Chat`.
""" """
_dc_context = attr.ib(validator=v.instance_of(DC_Context)) _dc_context = attr.ib(validator=v.instance_of(ffi.CData))
id = attr.ib(validator=v.instance_of(int)) id = attr.ib(validator=v.instance_of(int))
@cached_property @cached_property
def _dc_msg(self): def _dc_msg(self):
return DC_Msg(lib.dc_get_msg(self._dc_context.p, self.id)) return ffi.gc(
lib.dc_get_msg(self._dc_context, self.id),
lib.dc_msg_unref
)
def _refresh(self): def _refresh(self):
try: try:
@ -120,7 +128,7 @@ class Message(object):
@property_with_doc @property_with_doc
def text(self): def text(self):
"""unicode representation. """ """unicode representation. """
return from_dc_charpointer(lib.dc_msg_get_text(self._dc_msg.p)) return from_dc_charpointer(lib.dc_msg_get_text(self._dc_msg))
@property @property
def chat(self): def chat(self):
@ -128,7 +136,7 @@ class Message(object):
:returns: :class:`Chat` object :returns: :class:`Chat` object
""" """
chat_id = lib.dc_msg_get_chat_id(self._dc_msg.p) chat_id = lib.dc_msg_get_chat_id(self._dc_msg)
return Chat(self._dc_context, chat_id) return Chat(self._dc_context, chat_id)
@ -141,7 +149,7 @@ class MessageState(object):
@property @property
def _msgstate(self): def _msgstate(self):
self.message._refresh() self.message._refresh()
return lib.dc_msg_get_state(self.message._dc_msg.p) return lib.dc_msg_get_state(self.message._dc_msg)
def is_in_fresh(self): def is_in_fresh(self):
""" return True if Message is incoming fresh message (un-noticed). """ return True if Message is incoming fresh message (un-noticed).

View file

@ -1,36 +1,8 @@
from .capi import lib
def property_with_doc(f): def property_with_doc(f):
return property(f, None, None, f.__doc__) return property(f, None, None, f.__doc__)
class _UnrefStruct(object):
def __init__(self, c_obj):
self.p = c_obj
def __del__(self):
obj = self.__dict__.pop("p", None)
if lib is not None and obj is not None:
self._unref(obj)
class DC_Context(_UnrefStruct):
_unref = lib.dc_context_unref
class DC_Contact(_UnrefStruct):
_unref = lib.dc_contact_unref
class DC_Chat(_UnrefStruct):
_unref = lib.dc_chat_unref
class DC_Msg(_UnrefStruct):
_unref = lib.dc_msg_unref
# copied over unmodified from # copied over unmodified from
# https://github.com/devpi/devpi/blob/master/common/devpi_common/types.py # https://github.com/devpi/devpi/blob/master/common/devpi_common/types.py