diff --git a/python/src/deltachat/account.py b/python/src/deltachat/account.py index 2aefb8c9..875b097c 100644 --- a/python/src/deltachat/account.py +++ b/python/src/deltachat/account.py @@ -15,7 +15,6 @@ from attr import validators as v import deltachat from .capi import ffi, lib from .cutil import as_dc_charpointer, from_dc_charpointer, iter_array_and_unref -from .types import DC_Context from .chatting import Contact, Chat, Message @@ -34,12 +33,13 @@ class Account(object): the default internal logging. """ - self._dc_context = DC_Context( - lib.dc_context_new(lib.py_dc_callback, ffi.NULL, ffi.NULL) + self._dc_context = ffi.gc( + lib.dc_context_new(lib.py_dc_callback, ffi.NULL, ffi.NULL), + lib.dc_context_unref, ) if hasattr(db_path, "encode"): 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)) self._evhandler = EventHandler(self._dc_context) self._evlogger = EventLogger(self._dc_context, logid) @@ -55,7 +55,7 @@ class Account(object): for name, value in kwargs.items(): name = name.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): """ return unicode string value. @@ -65,7 +65,7 @@ class Account(object): :raises: KeyError if no config value was found. """ 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) def is_configured(self): @@ -73,7 +73,7 @@ class Account(object): :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): """ Raise ValueError if this account is not configured. """ @@ -99,7 +99,7 @@ class Account(object): """ name = as_dc_charpointer(name) 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) 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 if with_self: flags |= lib.DC_GCL_ADD_SELF - dc_array_t = lib.dc_get_contacts(self._dc_context.p, flags, query) - return list(iter_array_and_unref(dc_array_t, lambda x: Contact(self._dc_context.p, x))) + 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, x))) def create_chat_by_contact(self, 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) assert isinstance(contact_id, int) 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) def create_chat_by_message(self, message): @@ -141,7 +141,7 @@ class Account(object): """ msg_id = getattr(message, "id", message) 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) def get_message_by_id(self, msg_id): @@ -158,22 +158,22 @@ class Account(object): msg = getattr(msg, "id", msg) arr.append(msg) 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): """ configure this account object, start receiving events, start IMAP/SMTP threads. """ - deltachat.set_context_callback(self._dc_context.p, self._process_event) - lib.dc_configure(self._dc_context.p) + deltachat.set_context_callback(self._dc_context, self._process_event) + lib.dc_configure(self._dc_context) self._threads.start() def shutdown(self): """ 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) 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) method = getattr(self._evhandler, evt_name.lower(), None) if method is not None: @@ -201,8 +201,8 @@ class IOThreads: def stop(self, wait=False): self._thread_quitflag = True - lib.dc_interrupt_imap_idle(self._dc_context.p) - lib.dc_interrupt_smtp_idle(self._dc_context.p) + lib.dc_interrupt_imap_idle(self._dc_context) + lib.dc_interrupt_smtp_idle(self._dc_context) if wait: for name, thread in self._name2thread.items(): thread.join() @@ -210,20 +210,20 @@ class IOThreads: def imap_thread_run(self): print ("starting imap thread") while not self._thread_quitflag: - lib.dc_perform_imap_jobs(self._dc_context.p) - lib.dc_perform_imap_fetch(self._dc_context.p) - lib.dc_perform_imap_idle(self._dc_context.p) + lib.dc_perform_imap_jobs(self._dc_context) + lib.dc_perform_imap_fetch(self._dc_context) + lib.dc_perform_imap_idle(self._dc_context) def smtp_thread_run(self): print ("starting smtp thread") while not self._thread_quitflag: - lib.dc_perform_smtp_jobs(self._dc_context.p) - lib.dc_perform_smtp_idle(self._dc_context.p) + lib.dc_perform_smtp_jobs(self._dc_context) + lib.dc_perform_smtp_idle(self._dc_context) @attr.s 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): try: @@ -251,7 +251,7 @@ class EventLogger: self._event_queue = Queue() self._debug = debug 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._timeout = None diff --git a/python/src/deltachat/chatting.py b/python/src/deltachat/chatting.py index 77138d15..dd2f46b1 100644 --- a/python/src/deltachat/chatting.py +++ b/python/src/deltachat/chatting.py @@ -1,9 +1,8 @@ """ chatting related objects: Contact, Chat, Message. """ 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 DC_Context, DC_Contact, DC_Chat, DC_Msg import attr from attr import validators as v @@ -14,30 +13,33 @@ class Contact(object): 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)) @cached_property 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 def addr(self): """ 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 def display_name(self): """ 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): """ 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): """ 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 @@ -46,12 +48,15 @@ class Chat(object): 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)) @cached_property 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): """ return true if this chat is a deaddrop chat. """ @@ -64,7 +69,7 @@ class Chat(object): :returns: the resulting :class:`Message` instance """ 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) def get_messages(self): @@ -72,7 +77,7 @@ class Chat(object): :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))) def count_fresh_messages(self): @@ -80,14 +85,14 @@ class Chat(object): :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): """ mark all messages in this chat as noticed. 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 @@ -97,12 +102,15 @@ class Message(object): You obtain instances of it through :class:`deltachat.account.Account` or :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)) @cached_property 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): try: @@ -120,7 +128,7 @@ class Message(object): @property_with_doc def text(self): """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 def chat(self): @@ -128,7 +136,7 @@ class Message(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) @@ -141,7 +149,7 @@ class MessageState(object): @property def _msgstate(self): 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): """ return True if Message is incoming fresh message (un-noticed). diff --git a/python/src/deltachat/types.py b/python/src/deltachat/types.py index 769781b9..fbd539a4 100644 --- a/python/src/deltachat/types.py +++ b/python/src/deltachat/types.py @@ -1,36 +1,8 @@ -from .capi import lib - def property_with_doc(f): 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 # https://github.com/devpi/devpi/blob/master/common/devpi_common/types.py