mirror of
https://github.com/chatmail/core.git
synced 2026-05-02 04:46:29 +03:00
fix python lifecycles so that termination works
This commit is contained in:
@@ -63,6 +63,10 @@ def run_cmdline(argv=None, account_plugins=None):
|
|||||||
log = events.FFIEventLogger(ac, "bot")
|
log = events.FFIEventLogger(ac, "bot")
|
||||||
ac.add_account_plugin(log)
|
ac.add_account_plugin(log)
|
||||||
|
|
||||||
|
for plugin in account_plugins or []:
|
||||||
|
print("adding plugin", plugin)
|
||||||
|
ac.add_account_plugin(plugin)
|
||||||
|
|
||||||
if not ac.is_configured():
|
if not ac.is_configured():
|
||||||
assert args.email and args.password, (
|
assert args.email and args.password, (
|
||||||
"you must specify --email and --password once to configure this database/account"
|
"you must specify --email and --password once to configure this database/account"
|
||||||
@@ -72,12 +76,11 @@ def run_cmdline(argv=None, account_plugins=None):
|
|||||||
ac.set_config("mvbox_move", "0")
|
ac.set_config("mvbox_move", "0")
|
||||||
ac.set_config("mvbox_watch", "0")
|
ac.set_config("mvbox_watch", "0")
|
||||||
ac.set_config("sentbox_watch", "0")
|
ac.set_config("sentbox_watch", "0")
|
||||||
|
ac.configure()
|
||||||
for plugin in account_plugins or []:
|
ac.wait_configure_finish()
|
||||||
ac.add_account_plugin(plugin)
|
|
||||||
|
|
||||||
# start IO threads and configure if neccessary
|
# start IO threads and configure if neccessary
|
||||||
ac.start()
|
ac.start_io()
|
||||||
|
|
||||||
print("{}: waiting for message".format(ac.get_config("addr")))
|
print("{}: waiting for message".format(ac.get_config("addr")))
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
""" Account class implementation. """
|
""" Account class implementation. """
|
||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
import atexit
|
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from email.utils import parseaddr
|
from email.utils import parseaddr
|
||||||
from threading import Event
|
from threading import Event
|
||||||
@@ -48,7 +47,7 @@ class Account(object):
|
|||||||
|
|
||||||
self._dc_context = ffi.gc(
|
self._dc_context = ffi.gc(
|
||||||
lib.dc_context_new(as_dc_charpointer(os_name), db_path, ffi.NULL),
|
lib.dc_context_new(as_dc_charpointer(os_name), db_path, ffi.NULL),
|
||||||
_destroy_dc_context,
|
lib.dc_context_unref,
|
||||||
)
|
)
|
||||||
if self._dc_context == ffi.NULL:
|
if self._dc_context == ffi.NULL:
|
||||||
raise ValueError("Could not dc_context_new: {} {}".format(os_name, db_path))
|
raise ValueError("Could not dc_context_new: {} {}".format(os_name, db_path))
|
||||||
@@ -58,7 +57,6 @@ class Account(object):
|
|||||||
self._shutdown_event = Event()
|
self._shutdown_event = Event()
|
||||||
self._event_thread = EventThread(self)
|
self._event_thread = EventThread(self)
|
||||||
self._configkeys = self.get_config("sys.config_keys").split()
|
self._configkeys = self.get_config("sys.config_keys").split()
|
||||||
atexit.register(self.shutdown)
|
|
||||||
hook.dc_account_init(account=self)
|
hook.dc_account_init(account=self)
|
||||||
|
|
||||||
def disable_logging(self):
|
def disable_logging(self):
|
||||||
@@ -69,8 +67,8 @@ class Account(object):
|
|||||||
""" re-enable logging. """
|
""" re-enable logging. """
|
||||||
self._logging = True
|
self._logging = True
|
||||||
|
|
||||||
# def __del__(self):
|
def __del__(self):
|
||||||
# self.shutdown()
|
self.shutdown()
|
||||||
|
|
||||||
def log(self, msg):
|
def log(self, msg):
|
||||||
if self._logging:
|
if self._logging:
|
||||||
@@ -241,7 +239,7 @@ class Account(object):
|
|||||||
:returns: True if deletion succeeded (contact was deleted)
|
:returns: True if deletion succeeded (contact was deleted)
|
||||||
"""
|
"""
|
||||||
contact_id = contact.id
|
contact_id = contact.id
|
||||||
assert contact._dc_context == self._dc_context
|
assert contact.account == self
|
||||||
assert contact_id > const.DC_CHAT_ID_LAST_SPECIAL
|
assert contact_id > const.DC_CHAT_ID_LAST_SPECIAL
|
||||||
return bool(lib.dc_delete_contact(self._dc_context, contact_id))
|
return bool(lib.dc_delete_contact(self._dc_context, contact_id))
|
||||||
|
|
||||||
@@ -289,7 +287,7 @@ class Account(object):
|
|||||||
:returns: a :class:`deltachat.chat.Chat` object.
|
:returns: a :class:`deltachat.chat.Chat` object.
|
||||||
"""
|
"""
|
||||||
if hasattr(contact, "id"):
|
if hasattr(contact, "id"):
|
||||||
if contact._dc_context != self._dc_context:
|
if contact.account != self:
|
||||||
raise ValueError("Contact belongs to a different Account")
|
raise ValueError("Contact belongs to a different Account")
|
||||||
contact_id = contact.id
|
contact_id = contact.id
|
||||||
else:
|
else:
|
||||||
@@ -309,7 +307,7 @@ class Account(object):
|
|||||||
:returns: a :class:`deltachat.chat.Chat` object.
|
:returns: a :class:`deltachat.chat.Chat` object.
|
||||||
"""
|
"""
|
||||||
if hasattr(message, "id"):
|
if hasattr(message, "id"):
|
||||||
if self._dc_context != message._dc_context:
|
if message.account != self:
|
||||||
raise ValueError("Message belongs to a different Account")
|
raise ValueError("Message belongs to a different Account")
|
||||||
msg_id = message.id
|
msg_id = message.id
|
||||||
else:
|
else:
|
||||||
@@ -557,13 +555,14 @@ 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):
|
def start_io(self):
|
||||||
""" start this account's IO scheduling (Rust-core async scheduler)
|
""" start this account's IO scheduling (Rust-core async scheduler)
|
||||||
|
|
||||||
If this account is not configured but "addr" and "mail_pw" config
|
If this account is not configured an Exception is raised.
|
||||||
values are set, dc_configure() will be called.
|
You need to call account.configure() and account.wait_configure_finish()
|
||||||
|
before.
|
||||||
|
|
||||||
You may call `wait_shutdown` or `shutdown` after the
|
You may call `stop_scheduler`, `wait_shutdown` or `shutdown` after the
|
||||||
account is started.
|
account is started.
|
||||||
|
|
||||||
:raises MissingCredentials: if `addr` and `mail_pw` values are not set.
|
:raises MissingCredentials: if `addr` and `mail_pw` values are not set.
|
||||||
@@ -572,8 +571,7 @@ class Account(object):
|
|||||||
:returns: None (account is configured and with io-scheduling running)
|
:returns: None (account is configured and with io-scheduling running)
|
||||||
"""
|
"""
|
||||||
if not self.is_configured():
|
if not self.is_configured():
|
||||||
self.configure()
|
raise ValueError("account not configured, cannot start io")
|
||||||
self.wait_configure_finish()
|
|
||||||
lib.dc_start_io(self._dc_context)
|
lib.dc_start_io(self._dc_context)
|
||||||
|
|
||||||
def configure(self):
|
def configure(self):
|
||||||
@@ -601,13 +599,13 @@ class Account(object):
|
|||||||
""" wait until shutdown of this account has completed. """
|
""" wait until shutdown of this account has completed. """
|
||||||
self._shutdown_event.wait()
|
self._shutdown_event.wait()
|
||||||
|
|
||||||
def stop_scheduler(self):
|
def stop_io(self):
|
||||||
""" stop core scheduler if it is running. """
|
""" stop core IO scheduler if it is running. """
|
||||||
self.log("stop_ongoing")
|
self.log("stop_ongoing")
|
||||||
self.stop_ongoing()
|
self.stop_ongoing()
|
||||||
|
|
||||||
if bool(lib.dc_is_io_running(self._dc_context)):
|
if bool(lib.dc_is_io_running(self._dc_context)):
|
||||||
self.log("context_shutdown (stop core scheduler)")
|
self.log("dc_stop_io (stop core IO scheduler)")
|
||||||
lib.dc_stop_io(self._dc_context)
|
lib.dc_stop_io(self._dc_context)
|
||||||
else:
|
else:
|
||||||
self.log("stop_scheduler called on non-running context")
|
self.log("stop_scheduler called on non-running context")
|
||||||
@@ -615,13 +613,12 @@ class Account(object):
|
|||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
""" shutdown and destroy account (stop callback thread, close and remove
|
""" shutdown and destroy account (stop callback thread, close and remove
|
||||||
underlying dc_context)."""
|
underlying dc_context)."""
|
||||||
dc_context = self._dc_context
|
if self._dc_context is None:
|
||||||
if dc_context is None:
|
|
||||||
return
|
return
|
||||||
|
|
||||||
self.stop_scheduler()
|
self.stop_io()
|
||||||
|
|
||||||
self.log("remove dc_context")
|
self.log("remove dc_context references")
|
||||||
# the dc_context_unref triggers get_next_event to return ffi.NULL
|
# the dc_context_unref triggers get_next_event to return ffi.NULL
|
||||||
# which in turns makes the event thread finish execution
|
# which in turns makes the event thread finish execution
|
||||||
self._dc_context = None
|
self._dc_context = None
|
||||||
@@ -629,18 +626,13 @@ class Account(object):
|
|||||||
self.log("wait for event thread to finish")
|
self.log("wait for event thread to finish")
|
||||||
self._event_thread.wait()
|
self._event_thread.wait()
|
||||||
|
|
||||||
atexit.unregister(self.shutdown)
|
|
||||||
self._shutdown_event.set()
|
self._shutdown_event.set()
|
||||||
|
|
||||||
hook = hookspec.Global._get_plugin_manager().hook
|
hook = hookspec.Global._get_plugin_manager().hook
|
||||||
hook.dc_account_after_shutdown(account=self, dc_context=dc_context)
|
hook.dc_account_after_shutdown(account=self)
|
||||||
self.log("shutdown finished")
|
self.log("shutdown finished")
|
||||||
|
|
||||||
|
|
||||||
def _destroy_dc_context(dc_context, dc_context_unref=lib.dc_context_unref):
|
|
||||||
# destructor for dc_context
|
|
||||||
dc_context_unref(dc_context)
|
|
||||||
|
|
||||||
|
|
||||||
class ScannedQRCode:
|
class ScannedQRCode:
|
||||||
def __init__(self, dc_lot):
|
def __init__(self, dc_lot):
|
||||||
self._dc_lot = dc_lot
|
self._dc_lot = dc_lot
|
||||||
|
|||||||
@@ -19,12 +19,11 @@ class Chat(object):
|
|||||||
|
|
||||||
def __init__(self, account, id):
|
def __init__(self, account, id):
|
||||||
self.account = account
|
self.account = account
|
||||||
self._dc_context = account._dc_context
|
|
||||||
self.id = id
|
self.id = id
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return self.id == getattr(other, "id", None) and \
|
return self.id == getattr(other, "id", None) and \
|
||||||
self._dc_context == getattr(other, "_dc_context", None)
|
self.account._dc_context == other.account._dc_context
|
||||||
|
|
||||||
def __ne__(self, other):
|
def __ne__(self, other):
|
||||||
return not (self == other)
|
return not (self == other)
|
||||||
@@ -35,7 +34,7 @@ class Chat(object):
|
|||||||
@property
|
@property
|
||||||
def _dc_chat(self):
|
def _dc_chat(self):
|
||||||
return ffi.gc(
|
return ffi.gc(
|
||||||
lib.dc_get_chat(self._dc_context, self.id),
|
lib.dc_get_chat(self.account._dc_context, self.id),
|
||||||
lib.dc_chat_unref
|
lib.dc_chat_unref
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -47,7 +46,7 @@ class Chat(object):
|
|||||||
- does not delete messages on server
|
- does not delete messages on server
|
||||||
- the chat or contact is not blocked, new message will arrive
|
- the chat or contact is not blocked, new message will arrive
|
||||||
"""
|
"""
|
||||||
lib.dc_delete_chat(self._dc_context, self.id)
|
lib.dc_delete_chat(self.account._dc_context, self.id)
|
||||||
|
|
||||||
# ------ chat status/metadata API ------------------------------
|
# ------ chat status/metadata API ------------------------------
|
||||||
|
|
||||||
@@ -105,7 +104,7 @@ class Chat(object):
|
|||||||
:returns: None
|
:returns: None
|
||||||
"""
|
"""
|
||||||
name = as_dc_charpointer(name)
|
name = as_dc_charpointer(name)
|
||||||
return lib.dc_set_chat_name(self._dc_context, self.id, name)
|
return lib.dc_set_chat_name(self.account._dc_context, self.id, name)
|
||||||
|
|
||||||
def mute(self, duration=None):
|
def mute(self, duration=None):
|
||||||
""" mutes the chat
|
""" mutes the chat
|
||||||
@@ -117,7 +116,7 @@ class Chat(object):
|
|||||||
mute_duration = -1
|
mute_duration = -1
|
||||||
else:
|
else:
|
||||||
mute_duration = duration
|
mute_duration = duration
|
||||||
ret = lib.dc_set_chat_mute_duration(self._dc_context, self.id, mute_duration)
|
ret = lib.dc_set_chat_mute_duration(self.account._dc_context, self.id, mute_duration)
|
||||||
if not bool(ret):
|
if not bool(ret):
|
||||||
raise ValueError("Call to dc_set_chat_mute_duration failed")
|
raise ValueError("Call to dc_set_chat_mute_duration failed")
|
||||||
|
|
||||||
@@ -126,7 +125,7 @@ class Chat(object):
|
|||||||
|
|
||||||
:returns: None
|
:returns: None
|
||||||
"""
|
"""
|
||||||
ret = lib.dc_set_chat_mute_duration(self._dc_context, self.id, 0)
|
ret = lib.dc_set_chat_mute_duration(self.account._dc_context, self.id, 0)
|
||||||
if not bool(ret):
|
if not bool(ret):
|
||||||
raise ValueError("Failed to unmute chat")
|
raise ValueError("Failed to unmute chat")
|
||||||
|
|
||||||
@@ -152,7 +151,7 @@ class Chat(object):
|
|||||||
in a second channel (typically used by mobiles with QRcode-show + scan UX)
|
in a second channel (typically used by mobiles with QRcode-show + scan UX)
|
||||||
where account.join_with_qrcode(qr) needs to be called.
|
where account.join_with_qrcode(qr) needs to be called.
|
||||||
"""
|
"""
|
||||||
res = lib.dc_get_securejoin_qr(self._dc_context, self.id)
|
res = lib.dc_get_securejoin_qr(self.account._dc_context, self.id)
|
||||||
return from_dc_charpointer(res)
|
return from_dc_charpointer(res)
|
||||||
|
|
||||||
# ------ chat messaging API ------------------------------
|
# ------ chat messaging API ------------------------------
|
||||||
@@ -174,7 +173,7 @@ class Chat(object):
|
|||||||
assert msg.id != 0
|
assert msg.id != 0
|
||||||
# get a fresh copy of dc_msg, the core needs it
|
# get a fresh copy of dc_msg, the core needs it
|
||||||
msg = Message.from_db(self.account, msg.id)
|
msg = Message.from_db(self.account, msg.id)
|
||||||
sent_id = lib.dc_send_msg(self._dc_context, self.id, msg._dc_msg)
|
sent_id = lib.dc_send_msg(self.account._dc_context, self.id, msg._dc_msg)
|
||||||
if sent_id == 0:
|
if sent_id == 0:
|
||||||
raise ValueError("message could not be sent")
|
raise ValueError("message could not be sent")
|
||||||
# modify message in place to avoid bad state for the caller
|
# modify message in place to avoid bad state for the caller
|
||||||
@@ -189,7 +188,7 @@ class Chat(object):
|
|||||||
:returns: the resulting :class:`deltachat.message.Message` instance
|
:returns: the resulting :class:`deltachat.message.Message` instance
|
||||||
"""
|
"""
|
||||||
msg = as_dc_charpointer(text)
|
msg = as_dc_charpointer(text)
|
||||||
msg_id = lib.dc_send_text_msg(self._dc_context, self.id, msg)
|
msg_id = lib.dc_send_text_msg(self.account._dc_context, self.id, msg)
|
||||||
if msg_id == 0:
|
if msg_id == 0:
|
||||||
raise ValueError("message could not be send, does chat exist?")
|
raise ValueError("message could not be send, does chat exist?")
|
||||||
return Message.from_db(self.account, msg_id)
|
return Message.from_db(self.account, msg_id)
|
||||||
@@ -204,7 +203,7 @@ class Chat(object):
|
|||||||
"""
|
"""
|
||||||
msg = Message.new_empty(self.account, view_type="file")
|
msg = Message.new_empty(self.account, view_type="file")
|
||||||
msg.set_file(path, mime_type)
|
msg.set_file(path, mime_type)
|
||||||
sent_id = lib.dc_send_msg(self._dc_context, self.id, msg._dc_msg)
|
sent_id = lib.dc_send_msg(self.account._dc_context, self.id, msg._dc_msg)
|
||||||
if sent_id == 0:
|
if sent_id == 0:
|
||||||
raise ValueError("message could not be sent")
|
raise ValueError("message could not be sent")
|
||||||
return Message.from_db(self.account, sent_id)
|
return Message.from_db(self.account, sent_id)
|
||||||
@@ -219,7 +218,7 @@ class Chat(object):
|
|||||||
mime_type = mimetypes.guess_type(path)[0]
|
mime_type = mimetypes.guess_type(path)[0]
|
||||||
msg = Message.new_empty(self.account, view_type="image")
|
msg = Message.new_empty(self.account, view_type="image")
|
||||||
msg.set_file(path, mime_type)
|
msg.set_file(path, mime_type)
|
||||||
sent_id = lib.dc_send_msg(self._dc_context, self.id, msg._dc_msg)
|
sent_id = lib.dc_send_msg(self.account._dc_context, self.id, msg._dc_msg)
|
||||||
if sent_id == 0:
|
if sent_id == 0:
|
||||||
raise ValueError("message could not be sent")
|
raise ValueError("message could not be sent")
|
||||||
return Message.from_db(self.account, sent_id)
|
return Message.from_db(self.account, sent_id)
|
||||||
@@ -230,7 +229,7 @@ class Chat(object):
|
|||||||
:param msg: the message to be prepared.
|
:param msg: the message to be prepared.
|
||||||
:returns: :class:`deltachat.message.Message` instance.
|
:returns: :class:`deltachat.message.Message` instance.
|
||||||
"""
|
"""
|
||||||
msg_id = lib.dc_prepare_msg(self._dc_context, self.id, msg._dc_msg)
|
msg_id = lib.dc_prepare_msg(self.account._dc_context, self.id, msg._dc_msg)
|
||||||
if msg_id == 0:
|
if msg_id == 0:
|
||||||
raise ValueError("message could not be prepared")
|
raise ValueError("message could not be prepared")
|
||||||
# invalidate passed in message which is not safe to use anymore
|
# invalidate passed in message which is not safe to use anymore
|
||||||
@@ -266,7 +265,7 @@ class Chat(object):
|
|||||||
msg = Message.from_db(self.account, message.id)
|
msg = Message.from_db(self.account, message.id)
|
||||||
|
|
||||||
# pass 0 as chat-id because core-docs say it's ok when out-preparing
|
# pass 0 as chat-id because core-docs say it's ok when out-preparing
|
||||||
sent_id = lib.dc_send_msg(self._dc_context, 0, msg._dc_msg)
|
sent_id = lib.dc_send_msg(self.account._dc_context, 0, msg._dc_msg)
|
||||||
if sent_id == 0:
|
if sent_id == 0:
|
||||||
raise ValueError("message could not be sent")
|
raise ValueError("message could not be sent")
|
||||||
assert sent_id == msg.id
|
assert sent_id == msg.id
|
||||||
@@ -280,9 +279,9 @@ class Chat(object):
|
|||||||
:returns: None
|
:returns: None
|
||||||
"""
|
"""
|
||||||
if message is None:
|
if message is None:
|
||||||
lib.dc_set_draft(self._dc_context, self.id, ffi.NULL)
|
lib.dc_set_draft(self.account._dc_context, self.id, ffi.NULL)
|
||||||
else:
|
else:
|
||||||
lib.dc_set_draft(self._dc_context, self.id, message._dc_msg)
|
lib.dc_set_draft(self.account._dc_context, self.id, message._dc_msg)
|
||||||
|
|
||||||
def get_draft(self):
|
def get_draft(self):
|
||||||
""" get draft message for this chat.
|
""" get draft message for this chat.
|
||||||
@@ -290,7 +289,7 @@ class Chat(object):
|
|||||||
:param message: a :class:`Message` instance
|
:param message: a :class:`Message` instance
|
||||||
:returns: Message object or None (if no draft available)
|
:returns: Message object or None (if no draft available)
|
||||||
"""
|
"""
|
||||||
x = lib.dc_get_draft(self._dc_context, self.id)
|
x = lib.dc_get_draft(self.account._dc_context, self.id)
|
||||||
if x == ffi.NULL:
|
if x == ffi.NULL:
|
||||||
return None
|
return None
|
||||||
dc_msg = ffi.gc(x, lib.dc_msg_unref)
|
dc_msg = ffi.gc(x, lib.dc_msg_unref)
|
||||||
@@ -302,7 +301,7 @@ class Chat(object):
|
|||||||
:returns: list of :class:`deltachat.message.Message` objects for this chat.
|
:returns: list of :class:`deltachat.message.Message` objects for this chat.
|
||||||
"""
|
"""
|
||||||
dc_array = ffi.gc(
|
dc_array = ffi.gc(
|
||||||
lib.dc_get_chat_msgs(self._dc_context, self.id, 0, 0),
|
lib.dc_get_chat_msgs(self.account._dc_context, self.id, 0, 0),
|
||||||
lib.dc_array_unref
|
lib.dc_array_unref
|
||||||
)
|
)
|
||||||
return list(iter_array(dc_array, lambda x: Message.from_db(self.account, x)))
|
return list(iter_array(dc_array, lambda x: Message.from_db(self.account, x)))
|
||||||
@@ -312,18 +311,18 @@ class Chat(object):
|
|||||||
|
|
||||||
:returns: number of fresh messages
|
:returns: number of fresh messages
|
||||||
"""
|
"""
|
||||||
return lib.dc_get_fresh_msg_cnt(self._dc_context, self.id)
|
return lib.dc_get_fresh_msg_cnt(self.account._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, self.id)
|
return lib.dc_marknoticed_chat(self.account._dc_context, self.id)
|
||||||
|
|
||||||
def get_summary(self):
|
def get_summary(self):
|
||||||
""" return dictionary with summary information. """
|
""" return dictionary with summary information. """
|
||||||
dc_res = lib.dc_chat_get_info_json(self._dc_context, self.id)
|
dc_res = lib.dc_chat_get_info_json(self.account._dc_context, self.id)
|
||||||
s = from_dc_charpointer(dc_res)
|
s = from_dc_charpointer(dc_res)
|
||||||
return json.loads(s)
|
return json.loads(s)
|
||||||
|
|
||||||
@@ -336,7 +335,7 @@ class Chat(object):
|
|||||||
:raises ValueError: if contact could not be added
|
:raises ValueError: if contact could not be added
|
||||||
:returns: None
|
:returns: None
|
||||||
"""
|
"""
|
||||||
ret = lib.dc_add_contact_to_chat(self._dc_context, self.id, contact.id)
|
ret = lib.dc_add_contact_to_chat(self.account._dc_context, self.id, contact.id)
|
||||||
if ret != 1:
|
if ret != 1:
|
||||||
raise ValueError("could not add contact {!r} to chat".format(contact))
|
raise ValueError("could not add contact {!r} to chat".format(contact))
|
||||||
|
|
||||||
@@ -347,7 +346,7 @@ class Chat(object):
|
|||||||
:raises ValueError: if contact could not be removed
|
:raises ValueError: if contact could not be removed
|
||||||
:returns: None
|
:returns: None
|
||||||
"""
|
"""
|
||||||
ret = lib.dc_remove_contact_from_chat(self._dc_context, self.id, contact.id)
|
ret = lib.dc_remove_contact_from_chat(self.account._dc_context, self.id, contact.id)
|
||||||
if ret != 1:
|
if ret != 1:
|
||||||
raise ValueError("could not remove contact {!r} from chat".format(contact))
|
raise ValueError("could not remove contact {!r} from chat".format(contact))
|
||||||
|
|
||||||
@@ -359,7 +358,7 @@ class Chat(object):
|
|||||||
"""
|
"""
|
||||||
from .contact import Contact
|
from .contact import Contact
|
||||||
dc_array = ffi.gc(
|
dc_array = ffi.gc(
|
||||||
lib.dc_get_chat_contacts(self._dc_context, self.id),
|
lib.dc_get_chat_contacts(self.account._dc_context, self.id),
|
||||||
lib.dc_array_unref
|
lib.dc_array_unref
|
||||||
)
|
)
|
||||||
return list(iter_array(
|
return list(iter_array(
|
||||||
@@ -378,7 +377,7 @@ class Chat(object):
|
|||||||
"""
|
"""
|
||||||
assert os.path.exists(img_path), img_path
|
assert os.path.exists(img_path), img_path
|
||||||
p = as_dc_charpointer(img_path)
|
p = as_dc_charpointer(img_path)
|
||||||
res = lib.dc_set_chat_profile_image(self._dc_context, self.id, p)
|
res = lib.dc_set_chat_profile_image(self.account._dc_context, self.id, p)
|
||||||
if res != 1:
|
if res != 1:
|
||||||
raise ValueError("Setting Profile Image {!r} failed".format(p))
|
raise ValueError("Setting Profile Image {!r} failed".format(p))
|
||||||
|
|
||||||
@@ -391,7 +390,7 @@ class Chat(object):
|
|||||||
:raises ValueError: if profile image could not be reset
|
:raises ValueError: if profile image could not be reset
|
||||||
:returns: None
|
:returns: None
|
||||||
"""
|
"""
|
||||||
res = lib.dc_set_chat_profile_image(self._dc_context, self.id, ffi.NULL)
|
res = lib.dc_set_chat_profile_image(self.account._dc_context, self.id, ffi.NULL)
|
||||||
if res != 1:
|
if res != 1:
|
||||||
raise ValueError("Removing Profile Image failed")
|
raise ValueError("Removing Profile Image failed")
|
||||||
|
|
||||||
@@ -421,7 +420,7 @@ class Chat(object):
|
|||||||
"""return True if this chat has location-sending enabled currently.
|
"""return True if this chat has location-sending enabled currently.
|
||||||
:returns: True if location sending is enabled.
|
:returns: True if location sending is enabled.
|
||||||
"""
|
"""
|
||||||
return lib.dc_is_sending_locations_to_chat(self._dc_context, self.id)
|
return lib.dc_is_sending_locations_to_chat(self.account._dc_context, self.id)
|
||||||
|
|
||||||
def is_archived(self):
|
def is_archived(self):
|
||||||
"""return True if this chat is archived.
|
"""return True if this chat is archived.
|
||||||
@@ -434,7 +433,7 @@ class Chat(object):
|
|||||||
|
|
||||||
all subsequent messages will carry a location with them.
|
all subsequent messages will carry a location with them.
|
||||||
"""
|
"""
|
||||||
lib.dc_send_locations_to_chat(self._dc_context, self.id, seconds)
|
lib.dc_send_locations_to_chat(self.account._dc_context, self.id, seconds)
|
||||||
|
|
||||||
def get_locations(self, contact=None, timestamp_from=None, timestamp_to=None):
|
def get_locations(self, contact=None, timestamp_from=None, timestamp_to=None):
|
||||||
"""return list of locations for the given contact in the given timespan.
|
"""return list of locations for the given contact in the given timespan.
|
||||||
@@ -458,7 +457,7 @@ class Chat(object):
|
|||||||
else:
|
else:
|
||||||
contact_id = contact.id
|
contact_id = contact.id
|
||||||
|
|
||||||
dc_array = lib.dc_get_locations(self._dc_context, self.id, contact_id, time_from, time_to)
|
dc_array = lib.dc_get_locations(self.account._dc_context, self.id, contact_id, time_from, time_to)
|
||||||
return [
|
return [
|
||||||
Location(
|
Location(
|
||||||
latitude=lib.dc_array_get_latitude(dc_array, i),
|
latitude=lib.dc_array_get_latitude(dc_array, i),
|
||||||
|
|||||||
@@ -12,22 +12,21 @@ class Contact(object):
|
|||||||
"""
|
"""
|
||||||
def __init__(self, account, id):
|
def __init__(self, account, id):
|
||||||
self.account = account
|
self.account = account
|
||||||
self._dc_context = account._dc_context
|
|
||||||
self.id = id
|
self.id = id
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return self._dc_context == other._dc_context and self.id == other.id
|
return self.account._dc_context == other.account._dc_context and self.id == other.id
|
||||||
|
|
||||||
def __ne__(self, other):
|
def __ne__(self, other):
|
||||||
return not (self == other)
|
return not (self == other)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<Contact id={} addr={} dc_context={}>".format(self.id, self.addr, self._dc_context)
|
return "<Contact id={} addr={} dc_context={}>".format(self.id, self.addr, self.account._dc_context)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _dc_contact(self):
|
def _dc_contact(self):
|
||||||
return ffi.gc(
|
return ffi.gc(
|
||||||
lib.dc_get_contact(self._dc_context, self.id),
|
lib.dc_get_contact(self.account._dc_context, self.id),
|
||||||
lib.dc_contact_unref
|
lib.dc_contact_unref
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -133,8 +133,8 @@ class EventThread(threading.Thread):
|
|||||||
"""
|
"""
|
||||||
def __init__(self, account):
|
def __init__(self, account):
|
||||||
self.account = account
|
self.account = account
|
||||||
self._dc_context = account._dc_context
|
|
||||||
super(EventThread, self).__init__(name="events")
|
super(EventThread, self).__init__(name="events")
|
||||||
|
self.setDaemon(True)
|
||||||
self.start()
|
self.start()
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
@@ -157,7 +157,7 @@ class EventThread(threading.Thread):
|
|||||||
|
|
||||||
def _inner_run(self):
|
def _inner_run(self):
|
||||||
event_emitter = ffi.gc(
|
event_emitter = ffi.gc(
|
||||||
lib.dc_get_event_emitter(self._dc_context),
|
lib.dc_get_event_emitter(self.account._dc_context),
|
||||||
lib.dc_event_emitter_unref,
|
lib.dc_event_emitter_unref,
|
||||||
)
|
)
|
||||||
while 1:
|
while 1:
|
||||||
@@ -176,11 +176,15 @@ 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)
|
||||||
self.account._pm.hook.ac_process_ffi_event(account=self, ffi_event=ffi_event)
|
try:
|
||||||
for name, kwargs in self._map_ffi_event(ffi_event):
|
self.account._pm.hook.ac_process_ffi_event(account=self, ffi_event=ffi_event)
|
||||||
# self.account.log("calling hook name={} kwargs={}".format(name, kwargs))
|
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))
|
||||||
hook(**kwargs)
|
hook = getattr(self.account._pm.hook, name)
|
||||||
|
hook(**kwargs)
|
||||||
|
except Exception:
|
||||||
|
if self.account._dc_context is not None:
|
||||||
|
raise
|
||||||
|
|
||||||
def _map_ffi_event(self, ffi_event):
|
def _map_ffi_event(self, ffi_event):
|
||||||
name = ffi_event.name
|
name = ffi_event.name
|
||||||
|
|||||||
@@ -89,5 +89,5 @@ class Global:
|
|||||||
""" called when `Account::__init__()` function starts executing. """
|
""" called when `Account::__init__()` function starts executing. """
|
||||||
|
|
||||||
@global_hookspec
|
@global_hookspec
|
||||||
def dc_account_after_shutdown(self, account, dc_context):
|
def dc_account_after_shutdown(self, account):
|
||||||
""" Called after the account has been shutdown. """
|
""" Called after the account has been shutdown. """
|
||||||
|
|||||||
@@ -16,8 +16,7 @@ class Message(object):
|
|||||||
"""
|
"""
|
||||||
def __init__(self, account, dc_msg):
|
def __init__(self, account, dc_msg):
|
||||||
self.account = account
|
self.account = account
|
||||||
self._dc_context = account._dc_context
|
assert isinstance(self.account._dc_context, ffi.CData)
|
||||||
assert isinstance(self._dc_context, ffi.CData)
|
|
||||||
assert isinstance(dc_msg, ffi.CData)
|
assert isinstance(dc_msg, ffi.CData)
|
||||||
assert dc_msg != ffi.NULL
|
assert dc_msg != ffi.NULL
|
||||||
self._dc_msg = dc_msg
|
self._dc_msg = dc_msg
|
||||||
@@ -58,7 +57,7 @@ class Message(object):
|
|||||||
"""
|
"""
|
||||||
self.account.create_chat_by_message(self)
|
self.account.create_chat_by_message(self)
|
||||||
self._dc_msg = ffi.gc(
|
self._dc_msg = ffi.gc(
|
||||||
lib.dc_get_msg(self._dc_context, self.id),
|
lib.dc_get_msg(self.account._dc_context, self.id),
|
||||||
lib.dc_msg_unref
|
lib.dc_msg_unref
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -118,12 +117,12 @@ class Message(object):
|
|||||||
|
|
||||||
The text is multiline and may contain eg. the raw text of the message.
|
The text is multiline and may contain eg. the raw text of the message.
|
||||||
"""
|
"""
|
||||||
return from_dc_charpointer(lib.dc_get_msg_info(self._dc_context, self.id))
|
return from_dc_charpointer(lib.dc_get_msg_info(self.account._dc_context, self.id))
|
||||||
|
|
||||||
def continue_key_transfer(self, setup_code):
|
def continue_key_transfer(self, setup_code):
|
||||||
""" extract key and use it as primary key for this account. """
|
""" extract key and use it as primary key for this account. """
|
||||||
res = lib.dc_continue_key_transfer(
|
res = lib.dc_continue_key_transfer(
|
||||||
self._dc_context,
|
self.account._dc_context,
|
||||||
self.id,
|
self.id,
|
||||||
as_dc_charpointer(setup_code)
|
as_dc_charpointer(setup_code)
|
||||||
)
|
)
|
||||||
@@ -158,7 +157,7 @@ class Message(object):
|
|||||||
:returns: email-mime message object (with headers only, no body).
|
:returns: email-mime message object (with headers only, no body).
|
||||||
"""
|
"""
|
||||||
import email.parser
|
import email.parser
|
||||||
mime_headers = lib.dc_get_mime_headers(self._dc_context, self.id)
|
mime_headers = lib.dc_get_mime_headers(self.account._dc_context, self.id)
|
||||||
if mime_headers:
|
if mime_headers:
|
||||||
s = ffi.string(ffi.gc(mime_headers, lib.dc_str_unref))
|
s = ffi.string(ffi.gc(mime_headers, lib.dc_str_unref))
|
||||||
if isinstance(s, bytes):
|
if isinstance(s, bytes):
|
||||||
@@ -201,7 +200,7 @@ class Message(object):
|
|||||||
else:
|
else:
|
||||||
# load message from db to get a fresh/current state
|
# load message from db to get a fresh/current state
|
||||||
dc_msg = ffi.gc(
|
dc_msg = ffi.gc(
|
||||||
lib.dc_get_msg(self._dc_context, self.id),
|
lib.dc_get_msg(self.account._dc_context, self.id),
|
||||||
lib.dc_msg_unref
|
lib.dc_msg_unref
|
||||||
)
|
)
|
||||||
return lib.dc_msg_get_state(dc_msg)
|
return lib.dc_msg_get_state(dc_msg)
|
||||||
|
|||||||
@@ -103,7 +103,6 @@ def pytest_report_header(config, startdir):
|
|||||||
t = tempfile.mktemp()
|
t = tempfile.mktemp()
|
||||||
m = MonkeyPatch()
|
m = MonkeyPatch()
|
||||||
try:
|
try:
|
||||||
m.setattr(sys.stdout, "write", lambda x: len(x))
|
|
||||||
ac = Account(t)
|
ac = Account(t)
|
||||||
info = ac.get_info()
|
info = ac.get_info()
|
||||||
ac.shutdown()
|
ac.shutdown()
|
||||||
@@ -310,16 +309,16 @@ def acfactory(pytestconfig, tmpdir, request, session_liveconfig, data):
|
|||||||
ac1 = self.get_online_configuring_account(
|
ac1 = self.get_online_configuring_account(
|
||||||
pre_generated_key=pre_generated_key, mvbox=mvbox, move=move)
|
pre_generated_key=pre_generated_key, mvbox=mvbox, move=move)
|
||||||
ac1.wait_configure_finish()
|
ac1.wait_configure_finish()
|
||||||
ac1.start()
|
ac1.start_io()
|
||||||
return ac1
|
return ac1
|
||||||
|
|
||||||
def get_two_online_accounts(self, move=False, quiet=False):
|
def get_two_online_accounts(self, move=False, quiet=False):
|
||||||
ac1 = self.get_online_configuring_account(move=True, quiet=quiet)
|
ac1 = self.get_online_configuring_account(move=True, quiet=quiet)
|
||||||
ac2 = self.get_online_configuring_account(quiet=quiet)
|
ac2 = self.get_online_configuring_account(quiet=quiet)
|
||||||
ac1.wait_configure_finish()
|
ac1.wait_configure_finish()
|
||||||
ac1.start()
|
ac1.start_io()
|
||||||
ac2.wait_configure_finish()
|
ac2.wait_configure_finish()
|
||||||
ac2.start()
|
ac2.start_io()
|
||||||
return ac1, ac2
|
return ac1, ac2
|
||||||
|
|
||||||
def clone_online_account(self, account, pre_generated_key=True):
|
def clone_online_account(self, account, pre_generated_key=True):
|
||||||
@@ -394,6 +393,7 @@ class BotProcess:
|
|||||||
break
|
break
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
self.stdout_queue.put(line)
|
self.stdout_queue.put(line)
|
||||||
|
print("bot-stdout: ", line)
|
||||||
finally:
|
finally:
|
||||||
self.stdout_queue.put(None)
|
self.stdout_queue.put(None)
|
||||||
|
|
||||||
|
|||||||
@@ -533,9 +533,9 @@ class TestOnlineAccount:
|
|||||||
# rsa key gen can be slow especially on CI, adjust timeout
|
# rsa key gen can be slow especially on CI, adjust timeout
|
||||||
ac1._evtracker.set_timeout(120)
|
ac1._evtracker.set_timeout(120)
|
||||||
ac1.wait_configure_finish()
|
ac1.wait_configure_finish()
|
||||||
ac1.start()
|
ac1.start_io()
|
||||||
ac2.wait_configure_finish()
|
ac2.wait_configure_finish()
|
||||||
ac2.start()
|
ac2.start_io()
|
||||||
chat = self.get_chat(ac1, ac2, both_created=True)
|
chat = self.get_chat(ac1, ac2, both_created=True)
|
||||||
|
|
||||||
lp.sec("ac1: send unencrypted message to ac2")
|
lp.sec("ac1: send unencrypted message to ac2")
|
||||||
@@ -591,11 +591,11 @@ class TestOnlineAccount:
|
|||||||
ac1_clone = acfactory.clone_online_account(ac1)
|
ac1_clone = acfactory.clone_online_account(ac1)
|
||||||
|
|
||||||
ac1.wait_configure_finish()
|
ac1.wait_configure_finish()
|
||||||
ac1.start()
|
ac1.start_io()
|
||||||
ac2.wait_configure_finish()
|
ac2.wait_configure_finish()
|
||||||
ac2.start()
|
ac2.start_io()
|
||||||
ac1_clone.wait_configure_finish()
|
ac1_clone.wait_configure_finish()
|
||||||
ac1_clone.start()
|
ac1_clone.start_io()
|
||||||
|
|
||||||
chat = self.get_chat(ac1, ac2)
|
chat = self.get_chat(ac1, ac2)
|
||||||
|
|
||||||
@@ -700,11 +700,11 @@ class TestOnlineAccount:
|
|||||||
|
|
||||||
lp.sec("ac2: waiting for configuration")
|
lp.sec("ac2: waiting for configuration")
|
||||||
ac2.wait_configure_finish()
|
ac2.wait_configure_finish()
|
||||||
ac2.start()
|
ac2.start_io()
|
||||||
|
|
||||||
lp.sec("ac1: waiting for configuration")
|
lp.sec("ac1: waiting for configuration")
|
||||||
ac1.wait_configure_finish()
|
ac1.wait_configure_finish()
|
||||||
ac1.start()
|
ac1.start_io()
|
||||||
|
|
||||||
lp.sec("ac1: send message and wait for ac2 to receive it")
|
lp.sec("ac1: send message and wait for ac2 to receive it")
|
||||||
chat = self.get_chat(ac1, ac2)
|
chat = self.get_chat(ac1, ac2)
|
||||||
@@ -717,9 +717,9 @@ class TestOnlineAccount:
|
|||||||
ac1 = acfactory.get_online_configuring_account()
|
ac1 = acfactory.get_online_configuring_account()
|
||||||
ac2 = acfactory.get_online_configuring_account(mvbox=True, move=True)
|
ac2 = acfactory.get_online_configuring_account(mvbox=True, move=True)
|
||||||
ac2.wait_configure_finish()
|
ac2.wait_configure_finish()
|
||||||
ac2.start()
|
ac2.start_io()
|
||||||
ac1.wait_configure_finish()
|
ac1.wait_configure_finish()
|
||||||
ac1.start()
|
ac1.start_io()
|
||||||
chat = self.get_chat(ac1, ac2)
|
chat = self.get_chat(ac1, ac2)
|
||||||
chat.send_text("message1")
|
chat.send_text("message1")
|
||||||
ev = ac2._evtracker.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
|
ev = ac2._evtracker.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
|
||||||
@@ -731,9 +731,9 @@ class TestOnlineAccount:
|
|||||||
ac1.set_config("bcc_self", "1")
|
ac1.set_config("bcc_self", "1")
|
||||||
ac2 = acfactory.get_online_configuring_account()
|
ac2 = acfactory.get_online_configuring_account()
|
||||||
ac2.wait_configure_finish()
|
ac2.wait_configure_finish()
|
||||||
ac2.start()
|
ac2.start_io()
|
||||||
ac1.wait_configure_finish()
|
ac1.wait_configure_finish()
|
||||||
ac1.start()
|
ac1.start_io()
|
||||||
|
|
||||||
chat = self.get_chat(ac1, ac2)
|
chat = self.get_chat(ac1, ac2)
|
||||||
chat.send_text("message1")
|
chat.send_text("message1")
|
||||||
@@ -1162,9 +1162,9 @@ class TestOnlineAccount:
|
|||||||
ac1 = acfactory.get_online_configuring_account()
|
ac1 = acfactory.get_online_configuring_account()
|
||||||
ac2 = acfactory.clone_online_account(ac1)
|
ac2 = acfactory.clone_online_account(ac1)
|
||||||
ac2.wait_configure_finish()
|
ac2.wait_configure_finish()
|
||||||
ac2.start()
|
ac2.start_io()
|
||||||
ac1.wait_configure_finish()
|
ac1.wait_configure_finish()
|
||||||
ac1.start()
|
ac1.start_io()
|
||||||
|
|
||||||
lp.sec("trigger ac setup message and return setupcode")
|
lp.sec("trigger ac setup message and return setupcode")
|
||||||
assert ac1.get_info()["fingerprint"] != ac2.get_info()["fingerprint"]
|
assert ac1.get_info()["fingerprint"] != ac2.get_info()["fingerprint"]
|
||||||
@@ -1188,9 +1188,9 @@ class TestOnlineAccount:
|
|||||||
ac2 = acfactory.clone_online_account(ac1)
|
ac2 = acfactory.clone_online_account(ac1)
|
||||||
ac2._evtracker.set_timeout(30)
|
ac2._evtracker.set_timeout(30)
|
||||||
ac2.wait_configure_finish()
|
ac2.wait_configure_finish()
|
||||||
ac2.start()
|
ac2.start_io()
|
||||||
ac1.wait_configure_finish()
|
ac1.wait_configure_finish()
|
||||||
ac1.start()
|
ac1.start_io()
|
||||||
|
|
||||||
lp.sec("trigger ac setup message but ignore")
|
lp.sec("trigger ac setup message but ignore")
|
||||||
assert ac1.get_info()["fingerprint"] != ac2.get_info()["fingerprint"]
|
assert ac1.get_info()["fingerprint"] != ac2.get_info()["fingerprint"]
|
||||||
@@ -1502,7 +1502,7 @@ class TestGroupStressTests:
|
|||||||
accounts = [acfactory.get_online_configuring_account() for i in range(5)]
|
accounts = [acfactory.get_online_configuring_account() for i in range(5)]
|
||||||
for acc in accounts:
|
for acc in accounts:
|
||||||
acc.wait_configure_finish()
|
acc.wait_configure_finish()
|
||||||
acc.start()
|
acc.start_io()
|
||||||
ac1 = accounts.pop()
|
ac1 = accounts.pop()
|
||||||
|
|
||||||
lp.sec("ac1: setting up contacts with 4 other members")
|
lp.sec("ac1: setting up contacts with 4 other members")
|
||||||
@@ -1610,7 +1610,7 @@ class TestGroupStressTests:
|
|||||||
accounts = [acfactory.get_online_configuring_account() for i in range(3)]
|
accounts = [acfactory.get_online_configuring_account() for i in range(3)]
|
||||||
for acc in accounts:
|
for acc in accounts:
|
||||||
acc.wait_configure_finish()
|
acc.wait_configure_finish()
|
||||||
acc.start()
|
acc.start_io()
|
||||||
ac1 = accounts.pop()
|
ac1 = accounts.pop()
|
||||||
|
|
||||||
lp.sec("ac1: setting up contacts with 2 other members")
|
lp.sec("ac1: setting up contacts with 2 other members")
|
||||||
|
|||||||
@@ -36,13 +36,14 @@ def test_wrong_db(tmpdir):
|
|||||||
# write an invalid database file
|
# write an invalid database file
|
||||||
p.write("x123" * 10)
|
p.write("x123" * 10)
|
||||||
|
|
||||||
assert ffi.NULL == lib.dc_context_new(ffi.NULL, ffi.NULL, p.strpath.encode("ascii"), ffi.NULL)
|
assert ffi.NULL == lib.dc_context_new(ffi.NULL, p.strpath.encode("ascii"), ffi.NULL)
|
||||||
|
|
||||||
|
|
||||||
def test_empty_blobdir(tmpdir):
|
def test_empty_blobdir(tmpdir):
|
||||||
db_fname = tmpdir.join("hello.db")
|
db_fname = tmpdir.join("hello.db")
|
||||||
# Apparently some client code expects this to be the same as passing NULL.
|
# Apparently some client code expects this to be the same as passing NULL.
|
||||||
ctx = ffi.gc(
|
ctx = ffi.gc(
|
||||||
lib.dc_context_new(ffi.NULL, ffi.NULL, db_fname.strpath.encode("ascii"), b""),
|
lib.dc_context_new(ffi.NULL, db_fname.strpath.encode("ascii"), b""),
|
||||||
lib.dc_context_unref,
|
lib.dc_context_unref,
|
||||||
)
|
)
|
||||||
assert ctx != ffi.NULL
|
assert ctx != ffi.NULL
|
||||||
@@ -86,26 +87,16 @@ def test_get_special_message_id_returns_empty_message(acfactory):
|
|||||||
|
|
||||||
def test_provider_info_none():
|
def test_provider_info_none():
|
||||||
ctx = ffi.gc(
|
ctx = ffi.gc(
|
||||||
lib.dc_context_new(ffi.NULL, ffi.NULL),
|
lib.dc_context_new(ffi.NULL, ffi.NULL, ffi.NULL),
|
||||||
lib.dc_context_unref,
|
lib.dc_context_unref,
|
||||||
)
|
)
|
||||||
assert lib.dc_provider_new_from_email(ctx, cutil.as_dc_charpointer("email@unexistent.no")) == ffi.NULL
|
assert lib.dc_provider_new_from_email(ctx, cutil.as_dc_charpointer("email@unexistent.no")) == ffi.NULL
|
||||||
|
|
||||||
|
|
||||||
def test_get_info_closed():
|
|
||||||
ctx = ffi.gc(
|
|
||||||
lib.dc_context_new(ffi.NULL, ffi.NULL),
|
|
||||||
lib.dc_context_unref,
|
|
||||||
)
|
|
||||||
info = cutil.from_dc_charpointer(lib.dc_get_info(ctx))
|
|
||||||
assert 'deltachat_core_version' in info
|
|
||||||
assert 'database_dir' not in info
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_info_open(tmpdir):
|
def test_get_info_open(tmpdir):
|
||||||
db_fname = tmpdir.join("test.db")
|
db_fname = tmpdir.join("test.db")
|
||||||
ctx = ffi.gc(
|
ctx = ffi.gc(
|
||||||
lib.dc_context_new(ffi.NULL, ffi.NULL, db_fname.strpath.encode("ascii"), ffi.NULL),
|
lib.dc_context_new(ffi.NULL, db_fname.strpath.encode("ascii"), ffi.NULL),
|
||||||
lib.dc_context_unref,
|
lib.dc_context_unref,
|
||||||
)
|
)
|
||||||
info = cutil.from_dc_charpointer(lib.dc_get_info(ctx))
|
info = cutil.from_dc_charpointer(lib.dc_get_info(ctx))
|
||||||
|
|||||||
Reference in New Issue
Block a user