mirror of
https://github.com/chatmail/core.git
synced 2026-04-17 21:46:35 +03:00
python: add mypy support and some type hints
`deltachat.const` module now defines `__getattr__` and `__dir__` as suggested by https://www.python.org/dev/peps/pep-0562/ mypy detects that `__getattr__` is defined and does not show errors for `DC_*` constants which cannot be detected statically. mypy is added to `tox.ini`, so type check can be run with `tox -e mypy`.
This commit is contained in:
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -150,4 +150,4 @@ jobs:
|
|||||||
DCC_RS_TARGET: debug
|
DCC_RS_TARGET: debug
|
||||||
DCC_RS_DEV: ${{ github.workspace }}
|
DCC_RS_DEV: ${{ github.workspace }}
|
||||||
working-directory: python
|
working-directory: python
|
||||||
run: tox -e lint,doc,py3
|
run: tox -e lint,mypy,doc,py3
|
||||||
|
|||||||
19
python/mypy.ini
Normal file
19
python/mypy.ini
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
[mypy]
|
||||||
|
|
||||||
|
[mypy-deltachat.capi.*]
|
||||||
|
ignore_missing_imports = True
|
||||||
|
|
||||||
|
[mypy-pluggy.*]
|
||||||
|
ignore_missing_imports = True
|
||||||
|
|
||||||
|
[mypy-cffi.*]
|
||||||
|
ignore_missing_imports = True
|
||||||
|
|
||||||
|
[mypy-imapclient.*]
|
||||||
|
ignore_missing_imports = True
|
||||||
|
|
||||||
|
[mypy-pytest.*]
|
||||||
|
ignore_missing_imports = True
|
||||||
|
|
||||||
|
[mypy-_pytest.*]
|
||||||
|
ignore_missing_imports = True
|
||||||
@@ -19,9 +19,9 @@ except DistributionNotFound:
|
|||||||
|
|
||||||
def get_dc_event_name(integer, _DC_EVENTNAME_MAP={}):
|
def get_dc_event_name(integer, _DC_EVENTNAME_MAP={}):
|
||||||
if not _DC_EVENTNAME_MAP:
|
if not _DC_EVENTNAME_MAP:
|
||||||
for name, val in vars(const).items():
|
for name in dir(const):
|
||||||
if name.startswith("DC_EVENT_"):
|
if name.startswith("DC_EVENT_"):
|
||||||
_DC_EVENTNAME_MAP[val] = name
|
_DC_EVENTNAME_MAP[getattr(const, name)] = name
|
||||||
return _DC_EVENTNAME_MAP[integer]
|
return _DC_EVENTNAME_MAP[integer]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ from .contact import Contact
|
|||||||
from .tracker import ImexTracker, ConfigureTracker
|
from .tracker import ImexTracker, ConfigureTracker
|
||||||
from . import hookspec
|
from . import hookspec
|
||||||
from .events import EventThread
|
from .events import EventThread
|
||||||
|
from typing import Union, Any, Dict, Optional, List, Generator
|
||||||
|
|
||||||
|
|
||||||
class MissingCredentials(ValueError):
|
class MissingCredentials(ValueError):
|
||||||
@@ -28,7 +29,7 @@ class Account(object):
|
|||||||
"""
|
"""
|
||||||
MissingCredentials = MissingCredentials
|
MissingCredentials = MissingCredentials
|
||||||
|
|
||||||
def __init__(self, db_path, os_name=None, logging=True):
|
def __init__(self, db_path, os_name=None, logging=True) -> None:
|
||||||
""" initialize account object.
|
""" initialize account object.
|
||||||
|
|
||||||
:param db_path: a path to the account database. The database
|
:param db_path: a path to the account database. The database
|
||||||
@@ -58,11 +59,11 @@ class Account(object):
|
|||||||
hook = hookspec.Global._get_plugin_manager().hook
|
hook = hookspec.Global._get_plugin_manager().hook
|
||||||
hook.dc_account_init(account=self)
|
hook.dc_account_init(account=self)
|
||||||
|
|
||||||
def disable_logging(self):
|
def disable_logging(self) -> None:
|
||||||
""" disable logging. """
|
""" disable logging. """
|
||||||
self._logging = False
|
self._logging = False
|
||||||
|
|
||||||
def enable_logging(self):
|
def enable_logging(self) -> None:
|
||||||
""" re-enable logging. """
|
""" re-enable logging. """
|
||||||
self._logging = True
|
self._logging = True
|
||||||
|
|
||||||
@@ -73,7 +74,7 @@ class Account(object):
|
|||||||
if self._logging:
|
if self._logging:
|
||||||
self._pm.hook.ac_log_line(message=msg)
|
self._pm.hook.ac_log_line(message=msg)
|
||||||
|
|
||||||
def _check_config_key(self, name):
|
def _check_config_key(self, name: str) -> None:
|
||||||
if name not in self._configkeys:
|
if name not in self._configkeys:
|
||||||
raise KeyError("{!r} not a valid config key, existing keys: {!r}".format(
|
raise KeyError("{!r} not a valid config key, existing keys: {!r}".format(
|
||||||
name, self._configkeys))
|
name, self._configkeys))
|
||||||
@@ -105,19 +106,19 @@ class Account(object):
|
|||||||
cursor += len(entry) + 1
|
cursor += len(entry) + 1
|
||||||
log("")
|
log("")
|
||||||
|
|
||||||
def set_stock_translation(self, id, string):
|
def set_stock_translation(self, id: int, string: str) -> None:
|
||||||
""" set stock translation string.
|
""" set stock translation string.
|
||||||
|
|
||||||
:param id: id of stock string (const.DC_STR_*)
|
:param id: id of stock string (const.DC_STR_*)
|
||||||
:param value: string to set as new transalation
|
:param value: string to set as new transalation
|
||||||
:returns: None
|
:returns: None
|
||||||
"""
|
"""
|
||||||
string = string.encode("utf8")
|
bytestring = string.encode("utf8")
|
||||||
res = lib.dc_set_stock_translation(self._dc_context, id, string)
|
res = lib.dc_set_stock_translation(self._dc_context, id, bytestring)
|
||||||
if res == 0:
|
if res == 0:
|
||||||
raise ValueError("could not set translation string")
|
raise ValueError("could not set translation string")
|
||||||
|
|
||||||
def set_config(self, name, value):
|
def set_config(self, name: str, value: Optional[str]) -> None:
|
||||||
""" set configuration values.
|
""" set configuration values.
|
||||||
|
|
||||||
:param name: config key name (unicode)
|
:param name: config key name (unicode)
|
||||||
@@ -125,16 +126,16 @@ class Account(object):
|
|||||||
:returns: None
|
:returns: None
|
||||||
"""
|
"""
|
||||||
self._check_config_key(name)
|
self._check_config_key(name)
|
||||||
name = name.encode("utf8")
|
namebytes = name.encode("utf8")
|
||||||
if name == b"addr" and self.is_configured():
|
if namebytes == b"addr" and self.is_configured():
|
||||||
raise ValueError("can not change 'addr' after account is configured.")
|
raise ValueError("can not change 'addr' after account is configured.")
|
||||||
if value is not None:
|
if value is not None:
|
||||||
value = value.encode("utf8")
|
valuebytes = value.encode("utf8")
|
||||||
else:
|
else:
|
||||||
value = ffi.NULL
|
valuebytes = ffi.NULL
|
||||||
lib.dc_set_config(self._dc_context, name, value)
|
lib.dc_set_config(self._dc_context, namebytes, valuebytes)
|
||||||
|
|
||||||
def get_config(self, name):
|
def get_config(self, name: str):
|
||||||
""" return unicode string value.
|
""" return unicode string value.
|
||||||
|
|
||||||
:param name: configuration key to lookup (eg "addr" or "mail_pw")
|
:param name: configuration key to lookup (eg "addr" or "mail_pw")
|
||||||
@@ -143,12 +144,12 @@ class Account(object):
|
|||||||
"""
|
"""
|
||||||
if name != "sys.config_keys":
|
if name != "sys.config_keys":
|
||||||
self._check_config_key(name)
|
self._check_config_key(name)
|
||||||
name = name.encode("utf8")
|
namebytes = name.encode("utf8")
|
||||||
res = lib.dc_get_config(self._dc_context, name)
|
res = lib.dc_get_config(self._dc_context, namebytes)
|
||||||
assert res != ffi.NULL, "config value not found for: {!r}".format(name)
|
assert res != ffi.NULL, "config value not found for: {!r}".format(name)
|
||||||
return from_dc_charpointer(res)
|
return from_dc_charpointer(res)
|
||||||
|
|
||||||
def _preconfigure_keypair(self, addr, public, secret):
|
def _preconfigure_keypair(self, addr: str, public: str, secret: str) -> None:
|
||||||
"""See dc_preconfigure_keypair() in deltachat.h.
|
"""See dc_preconfigure_keypair() in deltachat.h.
|
||||||
|
|
||||||
In other words, you don't need this.
|
In other words, you don't need this.
|
||||||
@@ -160,7 +161,7 @@ class Account(object):
|
|||||||
if res == 0:
|
if res == 0:
|
||||||
raise Exception("Failed to set key")
|
raise Exception("Failed to set key")
|
||||||
|
|
||||||
def update_config(self, kwargs):
|
def update_config(self, kwargs: Dict[str, Any]) -> None:
|
||||||
""" update config values.
|
""" update config values.
|
||||||
|
|
||||||
:param kwargs: name=value config settings for this account.
|
:param kwargs: name=value config settings for this account.
|
||||||
@@ -170,7 +171,7 @@ class Account(object):
|
|||||||
for key, value in kwargs.items():
|
for key, value in kwargs.items():
|
||||||
self.set_config(key, str(value))
|
self.set_config(key, str(value))
|
||||||
|
|
||||||
def is_configured(self):
|
def is_configured(self) -> bool:
|
||||||
""" determine if the account is configured already; an initial connection
|
""" determine if the account is configured already; an initial connection
|
||||||
to SMTP/IMAP has been verified.
|
to SMTP/IMAP has been verified.
|
||||||
|
|
||||||
@@ -178,7 +179,7 @@ class Account(object):
|
|||||||
"""
|
"""
|
||||||
return True if lib.dc_is_configured(self._dc_context) else False
|
return True if lib.dc_is_configured(self._dc_context) else False
|
||||||
|
|
||||||
def set_avatar(self, img_path):
|
def set_avatar(self, img_path: Optional[str]) -> None:
|
||||||
"""Set self avatar.
|
"""Set self avatar.
|
||||||
|
|
||||||
:raises ValueError: if profile image could not be set
|
:raises ValueError: if profile image could not be set
|
||||||
@@ -190,12 +191,12 @@ class Account(object):
|
|||||||
assert os.path.exists(img_path), img_path
|
assert os.path.exists(img_path), img_path
|
||||||
self.set_config("selfavatar", img_path)
|
self.set_config("selfavatar", img_path)
|
||||||
|
|
||||||
def check_is_configured(self):
|
def check_is_configured(self) -> None:
|
||||||
""" Raise ValueError if this account is not configured. """
|
""" Raise ValueError if this account is not configured. """
|
||||||
if not self.is_configured():
|
if not self.is_configured():
|
||||||
raise ValueError("need to configure first")
|
raise ValueError("need to configure first")
|
||||||
|
|
||||||
def get_latest_backupfile(self, backupdir):
|
def get_latest_backupfile(self, backupdir) -> Optional[str]:
|
||||||
""" return the latest backup file in a given directory.
|
""" return the latest backup file in a given directory.
|
||||||
"""
|
"""
|
||||||
res = lib.dc_imex_has_backup(self._dc_context, as_dc_charpointer(backupdir))
|
res = lib.dc_imex_has_backup(self._dc_context, as_dc_charpointer(backupdir))
|
||||||
@@ -203,7 +204,7 @@ class Account(object):
|
|||||||
return None
|
return None
|
||||||
return from_dc_charpointer(res)
|
return from_dc_charpointer(res)
|
||||||
|
|
||||||
def get_blobdir(self):
|
def get_blobdir(self) -> Optional[str]:
|
||||||
""" return the directory for files.
|
""" return the directory for files.
|
||||||
|
|
||||||
All sent files are copied to this directory if necessary.
|
All sent files are copied to this directory if necessary.
|
||||||
@@ -211,15 +212,15 @@ class Account(object):
|
|||||||
"""
|
"""
|
||||||
return from_dc_charpointer(lib.dc_get_blobdir(self._dc_context))
|
return from_dc_charpointer(lib.dc_get_blobdir(self._dc_context))
|
||||||
|
|
||||||
def get_self_contact(self):
|
def get_self_contact(self) -> Contact:
|
||||||
""" return this account's identity as a :class:`deltachat.contact.Contact`.
|
""" return this account's identity as a :class:`deltachat.contact.Contact`.
|
||||||
|
|
||||||
:returns: :class:`deltachat.contact.Contact`
|
:returns: :class:`deltachat.contact.Contact`
|
||||||
"""
|
"""
|
||||||
return Contact(self, const.DC_CONTACT_ID_SELF)
|
return Contact(self, const.DC_CONTACT_ID_SELF)
|
||||||
|
|
||||||
def create_contact(self, obj, name=None):
|
def create_contact(self, obj, name: Optional[str] = None) -> Contact:
|
||||||
""" create a (new) Contact or return an existing one.
|
"""create a (new) Contact or return an existing one.
|
||||||
|
|
||||||
Calling this method will always result in the same
|
Calling this method will always result in the same
|
||||||
underlying contact id. If there already is a Contact
|
underlying contact id. If there already is a Contact
|
||||||
@@ -236,13 +237,13 @@ class Account(object):
|
|||||||
contact_id = lib.dc_create_contact(self._dc_context, name, addr)
|
contact_id = lib.dc_create_contact(self._dc_context, name, addr)
|
||||||
return Contact(self, contact_id)
|
return Contact(self, contact_id)
|
||||||
|
|
||||||
def get_contact(self, obj):
|
def get_contact(self, obj) -> Optional[Contact]:
|
||||||
if isinstance(obj, Contact):
|
if isinstance(obj, Contact):
|
||||||
return obj
|
return obj
|
||||||
(_, addr) = self.get_contact_addr_and_name(obj)
|
(_, addr) = self.get_contact_addr_and_name(obj)
|
||||||
return self.get_contact_by_addr(addr)
|
return self.get_contact_by_addr(addr)
|
||||||
|
|
||||||
def get_contact_addr_and_name(self, obj, name=None):
|
def get_contact_addr_and_name(self, obj, name: Optional[str] = None):
|
||||||
if isinstance(obj, Account):
|
if isinstance(obj, Account):
|
||||||
if not obj.is_configured():
|
if not obj.is_configured():
|
||||||
raise ValueError("can only add addresses from configured accounts")
|
raise ValueError("can only add addresses from configured accounts")
|
||||||
@@ -260,7 +261,7 @@ class Account(object):
|
|||||||
name = displayname
|
name = displayname
|
||||||
return (name, addr)
|
return (name, addr)
|
||||||
|
|
||||||
def delete_contact(self, contact):
|
def delete_contact(self, contact: Contact) -> bool:
|
||||||
""" delete a Contact.
|
""" delete a Contact.
|
||||||
|
|
||||||
:param contact: contact object obtained
|
:param contact: contact object obtained
|
||||||
@@ -271,22 +272,23 @@ class Account(object):
|
|||||||
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))
|
||||||
|
|
||||||
def get_contact_by_addr(self, email):
|
def get_contact_by_addr(self, email: str) -> Optional[Contact]:
|
||||||
""" get a contact for the email address or None if it's blocked or doesn't exist. """
|
""" get a contact for the email address or None if it's blocked or doesn't exist. """
|
||||||
_, addr = parseaddr(email)
|
_, addr = parseaddr(email)
|
||||||
addr = as_dc_charpointer(addr)
|
addr = as_dc_charpointer(addr)
|
||||||
contact_id = lib.dc_lookup_contact_id_by_addr(self._dc_context, addr)
|
contact_id = lib.dc_lookup_contact_id_by_addr(self._dc_context, addr)
|
||||||
if contact_id:
|
if contact_id:
|
||||||
return self.get_contact_by_id(contact_id)
|
return self.get_contact_by_id(contact_id)
|
||||||
|
return None
|
||||||
|
|
||||||
def get_contact_by_id(self, contact_id):
|
def get_contact_by_id(self, contact_id: int) -> Contact:
|
||||||
""" return Contact instance or None.
|
""" return Contact instance or raise an exception.
|
||||||
:param contact_id: integer id of this contact.
|
:param contact_id: integer id of this contact.
|
||||||
:returns: None or :class:`deltachat.contact.Contact` instance.
|
:returns: :class:`deltachat.contact.Contact` instance.
|
||||||
"""
|
"""
|
||||||
return Contact(self, contact_id)
|
return Contact(self, contact_id)
|
||||||
|
|
||||||
def get_blocked_contacts(self):
|
def get_blocked_contacts(self) -> List[Contact]:
|
||||||
""" return a list of all blocked contacts.
|
""" return a list of all blocked contacts.
|
||||||
|
|
||||||
:returns: list of :class:`deltachat.contact.Contact` objects.
|
:returns: list of :class:`deltachat.contact.Contact` objects.
|
||||||
@@ -297,8 +299,13 @@ class Account(object):
|
|||||||
)
|
)
|
||||||
return list(iter_array(dc_array, lambda x: Contact(self, x)))
|
return list(iter_array(dc_array, lambda x: Contact(self, x)))
|
||||||
|
|
||||||
def get_contacts(self, query=None, with_self=False, only_verified=False):
|
def get_contacts(
|
||||||
""" get a (filtered) list of contacts.
|
self,
|
||||||
|
query: Optional[str] = None,
|
||||||
|
with_self: bool = False,
|
||||||
|
only_verified: bool = False,
|
||||||
|
) -> List[Contact]:
|
||||||
|
"""get a (filtered) list of contacts.
|
||||||
|
|
||||||
:param query: if a string is specified, only return contacts
|
:param query: if a string is specified, only return contacts
|
||||||
whose name or e-mail matches query.
|
whose name or e-mail matches query.
|
||||||
@@ -318,7 +325,7 @@ class Account(object):
|
|||||||
)
|
)
|
||||||
return list(iter_array(dc_array, lambda x: Contact(self, x)))
|
return list(iter_array(dc_array, lambda x: Contact(self, x)))
|
||||||
|
|
||||||
def get_fresh_messages(self):
|
def get_fresh_messages(self) -> Generator[Message, None, None]:
|
||||||
""" yield all fresh messages from all chats. """
|
""" yield all fresh messages from all chats. """
|
||||||
dc_array = ffi.gc(
|
dc_array = ffi.gc(
|
||||||
lib.dc_get_fresh_msgs(self._dc_context),
|
lib.dc_get_fresh_msgs(self._dc_context),
|
||||||
@@ -326,12 +333,17 @@ class Account(object):
|
|||||||
)
|
)
|
||||||
yield from iter_array(dc_array, lambda x: Message.from_db(self, x))
|
yield from iter_array(dc_array, lambda x: Message.from_db(self, x))
|
||||||
|
|
||||||
def create_chat(self, obj):
|
def create_chat(self, obj) -> Chat:
|
||||||
""" Create a 1:1 chat with Account, Contact or e-mail address. """
|
""" Create a 1:1 chat with Account, Contact or e-mail address. """
|
||||||
return self.create_contact(obj).create_chat()
|
return self.create_contact(obj).create_chat()
|
||||||
|
|
||||||
def create_group_chat(self, name, contacts=None, verified=False):
|
def create_group_chat(
|
||||||
""" create a new group chat object.
|
self,
|
||||||
|
name: str,
|
||||||
|
contacts: Optional[List[Contact]] = None,
|
||||||
|
verified: bool = False,
|
||||||
|
) -> Chat:
|
||||||
|
"""create a new group chat object.
|
||||||
|
|
||||||
Chats are unpromoted until the first message is sent.
|
Chats are unpromoted until the first message is sent.
|
||||||
|
|
||||||
@@ -347,7 +359,7 @@ class Account(object):
|
|||||||
chat.add_contact(contact)
|
chat.add_contact(contact)
|
||||||
return chat
|
return chat
|
||||||
|
|
||||||
def get_chats(self):
|
def get_chats(self) -> List[Chat]:
|
||||||
""" return list of chats.
|
""" return list of chats.
|
||||||
|
|
||||||
:returns: a list of :class:`deltachat.chat.Chat` objects.
|
:returns: a list of :class:`deltachat.chat.Chat` objects.
|
||||||
@@ -364,17 +376,17 @@ class Account(object):
|
|||||||
chatlist.append(Chat(self, chat_id))
|
chatlist.append(Chat(self, chat_id))
|
||||||
return chatlist
|
return chatlist
|
||||||
|
|
||||||
def get_device_chat(self):
|
def get_device_chat(self) -> Chat:
|
||||||
return Contact(self, const.DC_CONTACT_ID_DEVICE).create_chat()
|
return Contact(self, const.DC_CONTACT_ID_DEVICE).create_chat()
|
||||||
|
|
||||||
def get_message_by_id(self, msg_id):
|
def get_message_by_id(self, msg_id: int) -> Message:
|
||||||
""" return Message instance.
|
""" return Message instance.
|
||||||
:param msg_id: integer id of this message.
|
:param msg_id: integer id of this message.
|
||||||
:returns: :class:`deltachat.message.Message` instance.
|
:returns: :class:`deltachat.message.Message` instance.
|
||||||
"""
|
"""
|
||||||
return Message.from_db(self, msg_id)
|
return Message.from_db(self, msg_id)
|
||||||
|
|
||||||
def get_chat_by_id(self, chat_id):
|
def get_chat_by_id(self, chat_id: int) -> Chat:
|
||||||
""" return Chat instance.
|
""" return Chat instance.
|
||||||
:param chat_id: integer id of this chat.
|
:param chat_id: integer id of this chat.
|
||||||
:returns: :class:`deltachat.chat.Chat` instance.
|
:returns: :class:`deltachat.chat.Chat` instance.
|
||||||
@@ -386,19 +398,18 @@ class Account(object):
|
|||||||
lib.dc_chat_unref(res)
|
lib.dc_chat_unref(res)
|
||||||
return Chat(self, chat_id)
|
return Chat(self, chat_id)
|
||||||
|
|
||||||
def mark_seen_messages(self, messages):
|
def mark_seen_messages(self, messages: List[Union[int, Message]]) -> None:
|
||||||
""" mark the given set of messages as seen.
|
""" mark the given set of messages as seen.
|
||||||
|
|
||||||
:param messages: a list of message ids or Message instances.
|
:param messages: a list of message ids or Message instances.
|
||||||
"""
|
"""
|
||||||
arr = array("i")
|
arr = array("i")
|
||||||
for msg in messages:
|
for msg in messages:
|
||||||
msg = getattr(msg, "id", msg)
|
arr.append(getattr(msg, "id", 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, msg_ids, len(messages))
|
lib.dc_markseen_msgs(self._dc_context, msg_ids, len(messages))
|
||||||
|
|
||||||
def forward_messages(self, messages, chat):
|
def forward_messages(self, messages: List[Message], chat: Chat) -> None:
|
||||||
""" Forward list of messages to a chat.
|
""" Forward list of messages to a chat.
|
||||||
|
|
||||||
:param messages: list of :class:`deltachat.message.Message` object.
|
:param messages: list of :class:`deltachat.message.Message` object.
|
||||||
@@ -408,7 +419,7 @@ class Account(object):
|
|||||||
msg_ids = [msg.id for msg in messages]
|
msg_ids = [msg.id for msg in messages]
|
||||||
lib.dc_forward_msgs(self._dc_context, msg_ids, len(msg_ids), chat.id)
|
lib.dc_forward_msgs(self._dc_context, msg_ids, len(msg_ids), chat.id)
|
||||||
|
|
||||||
def delete_messages(self, messages):
|
def delete_messages(self, messages: List[Message]) -> None:
|
||||||
""" delete messages (local and remote).
|
""" delete messages (local and remote).
|
||||||
|
|
||||||
:param messages: list of :class:`deltachat.message.Message` object.
|
:param messages: list of :class:`deltachat.message.Message` object.
|
||||||
@@ -477,7 +488,7 @@ class Account(object):
|
|||||||
raise RuntimeError("could not send out autocrypt setup message")
|
raise RuntimeError("could not send out autocrypt setup message")
|
||||||
return from_dc_charpointer(res)
|
return from_dc_charpointer(res)
|
||||||
|
|
||||||
def get_setup_contact_qr(self):
|
def get_setup_contact_qr(self) -> Optional[str]:
|
||||||
""" get/create Setup-Contact QR Code as ascii-string.
|
""" get/create Setup-Contact QR Code as ascii-string.
|
||||||
|
|
||||||
this string needs to be transferred to another DC account
|
this string needs to be transferred to another DC account
|
||||||
@@ -527,7 +538,9 @@ class Account(object):
|
|||||||
raise ValueError("could not join group")
|
raise ValueError("could not join group")
|
||||||
return Chat(self, chat_id)
|
return Chat(self, chat_id)
|
||||||
|
|
||||||
def set_location(self, latitude=0.0, longitude=0.0, accuracy=0.0):
|
def set_location(
|
||||||
|
self, latitude: float = 0.0, longitude: float = 0.0, accuracy: float = 0.0
|
||||||
|
) -> None:
|
||||||
"""set a new location. It effects all chats where we currently
|
"""set a new location. It effects all chats where we currently
|
||||||
have enabled location streaming.
|
have enabled location streaming.
|
||||||
|
|
||||||
@@ -621,7 +634,7 @@ class Account(object):
|
|||||||
"""
|
"""
|
||||||
lib.dc_maybe_network(self._dc_context)
|
lib.dc_maybe_network(self._dc_context)
|
||||||
|
|
||||||
def configure(self, reconfigure=False):
|
def configure(self, reconfigure: bool = False) -> ConfigureTracker:
|
||||||
""" Start configuration process and return a Configtracker instance
|
""" Start configuration process and return a Configtracker instance
|
||||||
on which you can block with wait_finish() to get a True/False success
|
on which you can block with wait_finish() to get a True/False success
|
||||||
value for the configuration process.
|
value for the configuration process.
|
||||||
@@ -634,11 +647,11 @@ class Account(object):
|
|||||||
lib.dc_configure(self._dc_context)
|
lib.dc_configure(self._dc_context)
|
||||||
return configtracker
|
return configtracker
|
||||||
|
|
||||||
def wait_shutdown(self):
|
def wait_shutdown(self) -> None:
|
||||||
""" 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_io(self):
|
def stop_io(self) -> None:
|
||||||
""" stop core IO 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()
|
||||||
@@ -646,7 +659,7 @@ class Account(object):
|
|||||||
self.log("dc_stop_io (stop core IO scheduler)")
|
self.log("dc_stop_io (stop core IO scheduler)")
|
||||||
lib.dc_stop_io(self._dc_context)
|
lib.dc_stop_io(self._dc_context)
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self) -> None:
|
||||||
""" 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)."""
|
||||||
if self._dc_context is None:
|
if self._dc_context is None:
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ from .cutil import as_dc_charpointer, from_dc_charpointer, iter_array
|
|||||||
from .capi import lib, ffi
|
from .capi import lib, ffi
|
||||||
from . import const
|
from . import const
|
||||||
from .message import Message
|
from .message import Message
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
class Chat(object):
|
class Chat(object):
|
||||||
@@ -17,20 +18,20 @@ class Chat(object):
|
|||||||
You obtain instances of it through :class:`deltachat.account.Account`.
|
You obtain instances of it through :class:`deltachat.account.Account`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, account, id):
|
def __init__(self, account, id) -> None:
|
||||||
from .account import Account
|
from .account import Account
|
||||||
assert isinstance(account, Account), repr(account)
|
assert isinstance(account, Account), repr(account)
|
||||||
self.account = account
|
self.account = account
|
||||||
self.id = id
|
self.id = id
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other) -> bool:
|
||||||
return self.id == getattr(other, "id", None) and \
|
return self.id == getattr(other, "id", None) and \
|
||||||
self.account._dc_context == other.account._dc_context
|
self.account._dc_context == other.account._dc_context
|
||||||
|
|
||||||
def __ne__(self, other):
|
def __ne__(self, other) -> bool:
|
||||||
return not (self == other)
|
return not (self == other)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self) -> str:
|
||||||
return "<Chat id={} name={}>".format(self.id, self.get_name())
|
return "<Chat id={} name={}>".format(self.id, self.get_name())
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -40,7 +41,7 @@ class Chat(object):
|
|||||||
lib.dc_chat_unref
|
lib.dc_chat_unref
|
||||||
)
|
)
|
||||||
|
|
||||||
def delete(self):
|
def delete(self) -> None:
|
||||||
"""Delete this chat and all its messages.
|
"""Delete this chat and all its messages.
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
@@ -50,24 +51,24 @@ class Chat(object):
|
|||||||
"""
|
"""
|
||||||
lib.dc_delete_chat(self.account._dc_context, self.id)
|
lib.dc_delete_chat(self.account._dc_context, self.id)
|
||||||
|
|
||||||
def block(self):
|
def block(self) -> None:
|
||||||
"""Block this chat."""
|
"""Block this chat."""
|
||||||
lib.dc_block_chat(self.account._dc_context, self.id)
|
lib.dc_block_chat(self.account._dc_context, self.id)
|
||||||
|
|
||||||
def accept(self):
|
def accept(self) -> None:
|
||||||
"""Accept this contact request chat."""
|
"""Accept this contact request chat."""
|
||||||
lib.dc_accept_chat(self.account._dc_context, self.id)
|
lib.dc_accept_chat(self.account._dc_context, self.id)
|
||||||
|
|
||||||
# ------ chat status/metadata API ------------------------------
|
# ------ chat status/metadata API ------------------------------
|
||||||
|
|
||||||
def is_group(self):
|
def is_group(self) -> bool:
|
||||||
""" return true if this chat is a group chat.
|
""" return true if this chat is a group chat.
|
||||||
|
|
||||||
:returns: True if chat is a group-chat, false if it's a contact 1:1 chat.
|
:returns: True if chat is a group-chat, false if it's a contact 1:1 chat.
|
||||||
"""
|
"""
|
||||||
return lib.dc_chat_get_type(self._dc_chat) == const.DC_CHAT_TYPE_GROUP
|
return lib.dc_chat_get_type(self._dc_chat) == const.DC_CHAT_TYPE_GROUP
|
||||||
|
|
||||||
def is_muted(self):
|
def is_muted(self) -> bool:
|
||||||
""" return true if this chat is muted.
|
""" return true if this chat is muted.
|
||||||
|
|
||||||
:returns: True if chat is muted, False otherwise.
|
:returns: True if chat is muted, False otherwise.
|
||||||
@@ -90,7 +91,7 @@ class Chat(object):
|
|||||||
"""
|
"""
|
||||||
return not lib.dc_chat_is_unpromoted(self._dc_chat)
|
return not lib.dc_chat_is_unpromoted(self._dc_chat)
|
||||||
|
|
||||||
def can_send(self):
|
def can_send(self) -> bool:
|
||||||
"""Check if messages can be sent to a give chat.
|
"""Check if messages can be sent to a give chat.
|
||||||
This is not true eg. for the contact requests or for the device-talk
|
This is not true eg. for the contact requests or for the device-talk
|
||||||
|
|
||||||
@@ -98,30 +99,30 @@ class Chat(object):
|
|||||||
"""
|
"""
|
||||||
return lib.dc_chat_can_send(self._dc_chat)
|
return lib.dc_chat_can_send(self._dc_chat)
|
||||||
|
|
||||||
def is_protected(self):
|
def is_protected(self) -> bool:
|
||||||
""" return True if this chat is a protected chat.
|
""" return True if this chat is a protected chat.
|
||||||
|
|
||||||
:returns: True if chat is protected, False otherwise.
|
:returns: True if chat is protected, False otherwise.
|
||||||
"""
|
"""
|
||||||
return lib.dc_chat_is_protected(self._dc_chat)
|
return lib.dc_chat_is_protected(self._dc_chat)
|
||||||
|
|
||||||
def get_name(self):
|
def get_name(self) -> Optional[str]:
|
||||||
""" return name of this chat.
|
""" return name of this chat.
|
||||||
|
|
||||||
:returns: unicode name
|
:returns: unicode name
|
||||||
"""
|
"""
|
||||||
return from_dc_charpointer(lib.dc_chat_get_name(self._dc_chat))
|
return from_dc_charpointer(lib.dc_chat_get_name(self._dc_chat))
|
||||||
|
|
||||||
def set_name(self, name):
|
def set_name(self, name: str) -> bool:
|
||||||
""" set name of this chat.
|
""" set name of this chat.
|
||||||
|
|
||||||
:param name: as a unicode string.
|
:param name: as a unicode string.
|
||||||
:returns: None
|
:returns: True on success, False otherwise
|
||||||
"""
|
"""
|
||||||
name = as_dc_charpointer(name)
|
name = as_dc_charpointer(name)
|
||||||
return lib.dc_set_chat_name(self.account._dc_context, self.id, name)
|
return bool(lib.dc_set_chat_name(self.account._dc_context, self.id, name))
|
||||||
|
|
||||||
def mute(self, duration=None):
|
def mute(self, duration: Optional[int] = None) -> None:
|
||||||
""" mutes the chat
|
""" mutes the chat
|
||||||
|
|
||||||
:param duration: Number of seconds to mute the chat for. None to mute until unmuted again.
|
:param duration: Number of seconds to mute the chat for. None to mute until unmuted again.
|
||||||
@@ -135,7 +136,7 @@ class Chat(object):
|
|||||||
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")
|
||||||
|
|
||||||
def unmute(self):
|
def unmute(self) -> None:
|
||||||
""" unmutes the chat
|
""" unmutes the chat
|
||||||
|
|
||||||
:returns: None
|
:returns: None
|
||||||
@@ -144,7 +145,7 @@ class Chat(object):
|
|||||||
if not bool(ret):
|
if not bool(ret):
|
||||||
raise ValueError("Failed to unmute chat")
|
raise ValueError("Failed to unmute chat")
|
||||||
|
|
||||||
def get_mute_duration(self):
|
def get_mute_duration(self) -> int:
|
||||||
""" Returns the number of seconds until the mute of this chat is lifted.
|
""" Returns the number of seconds until the mute of this chat is lifted.
|
||||||
|
|
||||||
:param duration:
|
:param duration:
|
||||||
@@ -152,37 +153,37 @@ class Chat(object):
|
|||||||
"""
|
"""
|
||||||
return lib.dc_chat_get_remaining_mute_duration(self._dc_chat)
|
return lib.dc_chat_get_remaining_mute_duration(self._dc_chat)
|
||||||
|
|
||||||
def get_ephemeral_timer(self):
|
def get_ephemeral_timer(self) -> int:
|
||||||
""" get ephemeral timer.
|
""" get ephemeral timer.
|
||||||
|
|
||||||
:returns: ephemeral timer value in seconds
|
:returns: ephemeral timer value in seconds
|
||||||
"""
|
"""
|
||||||
return lib.dc_get_chat_ephemeral_timer(self.account._dc_context, self.id)
|
return lib.dc_get_chat_ephemeral_timer(self.account._dc_context, self.id)
|
||||||
|
|
||||||
def set_ephemeral_timer(self, timer):
|
def set_ephemeral_timer(self, timer: int) -> bool:
|
||||||
""" set ephemeral timer.
|
""" set ephemeral timer.
|
||||||
|
|
||||||
:param: timer value in seconds
|
:param: timer value in seconds
|
||||||
|
|
||||||
:returns: None
|
:returns: True on success, False otherwise
|
||||||
"""
|
"""
|
||||||
return lib.dc_set_chat_ephemeral_timer(self.account._dc_context, self.id, timer)
|
return bool(lib.dc_set_chat_ephemeral_timer(self.account._dc_context, self.id, timer))
|
||||||
|
|
||||||
def get_type(self):
|
def get_type(self) -> int:
|
||||||
""" (deprecated) return type of this chat.
|
""" (deprecated) return type of this chat.
|
||||||
|
|
||||||
:returns: one of const.DC_CHAT_TYPE_*
|
:returns: one of const.DC_CHAT_TYPE_*
|
||||||
"""
|
"""
|
||||||
return lib.dc_chat_get_type(self._dc_chat)
|
return lib.dc_chat_get_type(self._dc_chat)
|
||||||
|
|
||||||
def get_encryption_info(self):
|
def get_encryption_info(self) -> Optional[str]:
|
||||||
"""Return encryption info for this chat.
|
"""Return encryption info for this chat.
|
||||||
|
|
||||||
:returns: a string with encryption preferences of all chat members"""
|
:returns: a string with encryption preferences of all chat members"""
|
||||||
res = lib.dc_get_chat_encrinfo(self.account._dc_context, self.id)
|
res = lib.dc_get_chat_encrinfo(self.account._dc_context, self.id)
|
||||||
return from_dc_charpointer(res)
|
return from_dc_charpointer(res)
|
||||||
|
|
||||||
def get_join_qr(self):
|
def get_join_qr(self) -> Optional[str]:
|
||||||
""" get/create Join-Group QR Code as ascii-string.
|
""" get/create Join-Group QR Code as ascii-string.
|
||||||
|
|
||||||
this string needs to be transferred to another DC account
|
this string needs to be transferred to another DC account
|
||||||
@@ -194,7 +195,7 @@ class Chat(object):
|
|||||||
|
|
||||||
# ------ chat messaging API ------------------------------
|
# ------ chat messaging API ------------------------------
|
||||||
|
|
||||||
def send_msg(self, msg):
|
def send_msg(self, msg: Message) -> Message:
|
||||||
"""send a message by using a ready Message object.
|
"""send a message by using a ready Message object.
|
||||||
|
|
||||||
:param msg: a :class:`deltachat.message.Message` instance
|
:param msg: a :class:`deltachat.message.Message` instance
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
|
from typing import Any, List
|
||||||
|
|
||||||
from .capi import lib
|
from .capi import lib
|
||||||
|
|
||||||
|
|
||||||
for name in dir(lib):
|
def __getattr__(name: str) -> Any:
|
||||||
if name.startswith("DC_"):
|
if name.startswith("DC_"):
|
||||||
globals()[name] = getattr(lib, name)
|
return getattr(lib, name)
|
||||||
del name
|
return globals()[name]
|
||||||
|
|
||||||
|
|
||||||
|
def __dir__() -> List[str]:
|
||||||
|
return sorted(name for name in dir(lib) if name.startswith("DC_"))
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
from .capi import lib
|
from .capi import lib
|
||||||
from .capi import ffi
|
from .capi import ffi
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
|
from typing import Optional, TypeVar, Generator, Callable
|
||||||
|
|
||||||
|
T = TypeVar('T')
|
||||||
|
|
||||||
|
|
||||||
def as_dc_charpointer(obj):
|
def as_dc_charpointer(obj):
|
||||||
@@ -11,21 +14,22 @@ def as_dc_charpointer(obj):
|
|||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
|
||||||
def iter_array(dc_array_t, constructor):
|
def iter_array(dc_array_t, constructor: Callable[[int], T]) -> Generator[T, None, None]:
|
||||||
for i in range(0, lib.dc_array_get_cnt(dc_array_t)):
|
for i in range(0, lib.dc_array_get_cnt(dc_array_t)):
|
||||||
yield constructor(lib.dc_array_get_id(dc_array_t, i))
|
yield constructor(lib.dc_array_get_id(dc_array_t, i))
|
||||||
|
|
||||||
|
|
||||||
def from_dc_charpointer(obj):
|
def from_dc_charpointer(obj) -> Optional[str]:
|
||||||
if obj != ffi.NULL:
|
if obj != ffi.NULL:
|
||||||
return ffi.string(ffi.gc(obj, lib.dc_str_unref)).decode("utf8")
|
return ffi.string(ffi.gc(obj, lib.dc_str_unref)).decode("utf8")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class DCLot:
|
class DCLot:
|
||||||
def __init__(self, dc_lot):
|
def __init__(self, dc_lot) -> None:
|
||||||
self._dc_lot = dc_lot
|
self._dc_lot = dc_lot
|
||||||
|
|
||||||
def id(self):
|
def id(self) -> int:
|
||||||
return lib.dc_lot_get_id(self._dc_lot)
|
return lib.dc_lot_get_id(self._dc_lot)
|
||||||
|
|
||||||
def state(self):
|
def state(self):
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from imapclient import IMAPClient
|
|||||||
from imapclient.exceptions import IMAPClientError
|
from imapclient.exceptions import IMAPClientError
|
||||||
import imaplib
|
import imaplib
|
||||||
import deltachat
|
import deltachat
|
||||||
from deltachat import const
|
from deltachat import const, Account
|
||||||
|
|
||||||
|
|
||||||
SEEN = b'\\Seen'
|
SEEN = b'\\Seen'
|
||||||
@@ -62,7 +62,7 @@ def dc_account_after_shutdown(account):
|
|||||||
|
|
||||||
|
|
||||||
class DirectImap:
|
class DirectImap:
|
||||||
def __init__(self, account):
|
def __init__(self, account: Account) -> None:
|
||||||
self.account = account
|
self.account = account
|
||||||
self.logid = account.get_config("displayname") or id(account)
|
self.logid = account.get_config("displayname") or id(account)
|
||||||
self._idling = False
|
self._idling = False
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from .cutil import from_dc_charpointer
|
|||||||
|
|
||||||
|
|
||||||
class FFIEvent:
|
class FFIEvent:
|
||||||
def __init__(self, name, data1, data2):
|
def __init__(self, name: str, data1, data2):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.data1 = data1
|
self.data1 = data1
|
||||||
self.data2 = data2
|
self.data2 = data2
|
||||||
@@ -29,13 +29,13 @@ class FFIEventLogger:
|
|||||||
# to prevent garbled logging
|
# to prevent garbled logging
|
||||||
_loglock = threading.RLock()
|
_loglock = threading.RLock()
|
||||||
|
|
||||||
def __init__(self, account):
|
def __init__(self, account) -> None:
|
||||||
self.account = account
|
self.account = account
|
||||||
self.logid = self.account.get_config("displayname")
|
self.logid = self.account.get_config("displayname")
|
||||||
self.init_time = time.time()
|
self.init_time = time.time()
|
||||||
|
|
||||||
@account_hookimpl
|
@account_hookimpl
|
||||||
def ac_process_ffi_event(self, ffi_event):
|
def ac_process_ffi_event(self, ffi_event: FFIEvent) -> None:
|
||||||
self.account.log(str(ffi_event))
|
self.account.log(str(ffi_event))
|
||||||
|
|
||||||
@account_hookimpl
|
@account_hookimpl
|
||||||
@@ -69,7 +69,7 @@ class FFIEventTracker:
|
|||||||
self._event_queue = Queue()
|
self._event_queue = Queue()
|
||||||
|
|
||||||
@account_hookimpl
|
@account_hookimpl
|
||||||
def ac_process_ffi_event(self, ffi_event):
|
def ac_process_ffi_event(self, ffi_event: FFIEvent):
|
||||||
self._event_queue.put(ffi_event)
|
self._event_queue.put(ffi_event)
|
||||||
|
|
||||||
def set_timeout(self, timeout):
|
def set_timeout(self, timeout):
|
||||||
@@ -96,7 +96,7 @@ class FFIEventTracker:
|
|||||||
if rex.match(ev.name):
|
if rex.match(ev.name):
|
||||||
return ev
|
return ev
|
||||||
|
|
||||||
def get_info_contains(self, regex):
|
def get_info_contains(self, regex: str) -> FFIEvent:
|
||||||
rex = re.compile(regex)
|
rex = re.compile(regex)
|
||||||
while 1:
|
while 1:
|
||||||
ev = self.get_matching("DC_EVENT_INFO")
|
ev = self.get_matching("DC_EVENT_INFO")
|
||||||
@@ -176,6 +176,7 @@ class FFIEventTracker:
|
|||||||
ev = self.get_matching("DC_EVENT_MSGS_CHANGED")
|
ev = self.get_matching("DC_EVENT_MSGS_CHANGED")
|
||||||
if ev.data2 > 0:
|
if ev.data2 > 0:
|
||||||
return self.account.get_message_by_id(ev.data2)
|
return self.account.get_message_by_id(ev.data2)
|
||||||
|
return None
|
||||||
|
|
||||||
def wait_msg_delivered(self, msg):
|
def wait_msg_delivered(self, msg):
|
||||||
ev = self.get_matching("DC_EVENT_MSG_DELIVERED")
|
ev = self.get_matching("DC_EVENT_MSG_DELIVERED")
|
||||||
@@ -189,7 +190,7 @@ class EventThread(threading.Thread):
|
|||||||
|
|
||||||
With each Account init this callback thread is started.
|
With each Account init this callback thread is started.
|
||||||
"""
|
"""
|
||||||
def __init__(self, account):
|
def __init__(self, account) -> None:
|
||||||
self.account = account
|
self.account = account
|
||||||
super(EventThread, self).__init__(name="events")
|
super(EventThread, self).__init__(name="events")
|
||||||
self.setDaemon(True)
|
self.setDaemon(True)
|
||||||
@@ -202,17 +203,17 @@ class EventThread(threading.Thread):
|
|||||||
yield
|
yield
|
||||||
self.account.log(message + " FINISHED")
|
self.account.log(message + " FINISHED")
|
||||||
|
|
||||||
def mark_shutdown(self):
|
def mark_shutdown(self) -> None:
|
||||||
self._marked_for_shutdown = True
|
self._marked_for_shutdown = True
|
||||||
|
|
||||||
def wait(self, timeout=None):
|
def wait(self, timeout=None) -> None:
|
||||||
if self == threading.current_thread():
|
if self == threading.current_thread():
|
||||||
# we are in the callback thread and thus cannot
|
# we are in the callback thread and thus cannot
|
||||||
# wait for the thread-loop to finish.
|
# wait for the thread-loop to finish.
|
||||||
return
|
return
|
||||||
self.join(timeout=timeout)
|
self.join(timeout=timeout)
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
""" get and run events until shutdown. """
|
""" get and run events until shutdown. """
|
||||||
with self.log_execution("EVENT THREAD"):
|
with self.log_execution("EVENT THREAD"):
|
||||||
self._inner_run()
|
self._inner_run()
|
||||||
@@ -250,7 +251,7 @@ class EventThread(threading.Thread):
|
|||||||
if self.account._dc_context is not None:
|
if self.account._dc_context is not None:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def _map_ffi_event(self, ffi_event):
|
def _map_ffi_event(self, ffi_event: FFIEvent):
|
||||||
name = ffi_event.name
|
name = ffi_event.name
|
||||||
account = self.account
|
account = self.account
|
||||||
if name == "DC_EVENT_CONFIGURE_PROGRESS":
|
if name == "DC_EVENT_CONFIGURE_PROGRESS":
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class Provider(object):
|
|||||||
:param domain: The email to get the provider info for.
|
:param domain: The email to get the provider info for.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, account, addr):
|
def __init__(self, account, addr) -> None:
|
||||||
provider = ffi.gc(
|
provider = ffi.gc(
|
||||||
lib.dc_provider_new_from_email(account._dc_context, as_dc_charpointer(addr)),
|
lib.dc_provider_new_from_email(account._dc_context, as_dc_charpointer(addr)),
|
||||||
lib.dc_provider_unref,
|
lib.dc_provider_unref,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import fnmatch
|
|||||||
import time
|
import time
|
||||||
import weakref
|
import weakref
|
||||||
import tempfile
|
import tempfile
|
||||||
|
from typing import List, Dict, Callable
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import requests
|
import requests
|
||||||
@@ -126,7 +127,7 @@ def pytest_report_header(config, startdir):
|
|||||||
|
|
||||||
|
|
||||||
class SessionLiveConfigFromFile:
|
class SessionLiveConfigFromFile:
|
||||||
def __init__(self, fn):
|
def __init__(self, fn) -> None:
|
||||||
self.fn = fn
|
self.fn = fn
|
||||||
self.configlist = []
|
self.configlist = []
|
||||||
for line in open(fn):
|
for line in open(fn):
|
||||||
@@ -137,19 +138,21 @@ class SessionLiveConfigFromFile:
|
|||||||
d[name] = value
|
d[name] = value
|
||||||
self.configlist.append(d)
|
self.configlist.append(d)
|
||||||
|
|
||||||
def get(self, index):
|
def get(self, index: int):
|
||||||
return self.configlist[index]
|
return self.configlist[index]
|
||||||
|
|
||||||
def exists(self):
|
def exists(self) -> bool:
|
||||||
return bool(self.configlist)
|
return bool(self.configlist)
|
||||||
|
|
||||||
|
|
||||||
class SessionLiveConfigFromURL:
|
class SessionLiveConfigFromURL:
|
||||||
def __init__(self, url):
|
configlist: List[Dict[str, str]]
|
||||||
|
|
||||||
|
def __init__(self, url: str) -> None:
|
||||||
self.configlist = []
|
self.configlist = []
|
||||||
self.url = url
|
self.url = url
|
||||||
|
|
||||||
def get(self, index):
|
def get(self, index: int):
|
||||||
try:
|
try:
|
||||||
return self.configlist[index]
|
return self.configlist[index]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
@@ -162,7 +165,7 @@ class SessionLiveConfigFromURL:
|
|||||||
self.configlist.append(config)
|
self.configlist.append(config)
|
||||||
return config
|
return config
|
||||||
|
|
||||||
def exists(self):
|
def exists(self) -> bool:
|
||||||
return bool(self.configlist)
|
return bool(self.configlist)
|
||||||
|
|
||||||
|
|
||||||
@@ -179,7 +182,7 @@ def session_liveconfig(request):
|
|||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def data(request):
|
def data(request):
|
||||||
class Data:
|
class Data:
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
# trying to find test data heuristically
|
# trying to find test data heuristically
|
||||||
# because we are run from a dev-setup with pytest direct,
|
# because we are run from a dev-setup with pytest direct,
|
||||||
# through tox, and then maybe also from deltachat-binding
|
# through tox, and then maybe also from deltachat-binding
|
||||||
@@ -210,7 +213,10 @@ def data(request):
|
|||||||
def acfactory(pytestconfig, tmpdir, request, session_liveconfig, data):
|
def acfactory(pytestconfig, tmpdir, request, session_liveconfig, data):
|
||||||
|
|
||||||
class AccountMaker:
|
class AccountMaker:
|
||||||
def __init__(self):
|
_finalizers: List[Callable[[], None]]
|
||||||
|
_accounts: List[Account]
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
self.live_count = 0
|
self.live_count = 0
|
||||||
self.offline_count = 0
|
self.offline_count = 0
|
||||||
self._finalizers = []
|
self._finalizers = []
|
||||||
@@ -423,7 +429,7 @@ def acfactory(pytestconfig, tmpdir, request, session_liveconfig, data):
|
|||||||
pass
|
pass
|
||||||
imap.dump_imap_structures(tmpdir, logfile=logfile)
|
imap.dump_imap_structures(tmpdir, logfile=logfile)
|
||||||
|
|
||||||
def get_accepted_chat(self, ac1, ac2):
|
def get_accepted_chat(self, ac1: Account, ac2: Account):
|
||||||
ac2.create_chat(ac1)
|
ac2.create_chat(ac1)
|
||||||
return ac1.create_chat(ac2)
|
return ac1.create_chat(ac2)
|
||||||
|
|
||||||
@@ -451,7 +457,9 @@ def acfactory(pytestconfig, tmpdir, request, session_liveconfig, data):
|
|||||||
|
|
||||||
|
|
||||||
class BotProcess:
|
class BotProcess:
|
||||||
def __init__(self, popen, bot_cfg):
|
stdout_queue: queue.Queue
|
||||||
|
|
||||||
|
def __init__(self, popen, bot_cfg) -> None:
|
||||||
self.popen = popen
|
self.popen = popen
|
||||||
self.addr = bot_cfg["addr"]
|
self.addr = bot_cfg["addr"]
|
||||||
|
|
||||||
@@ -459,10 +467,10 @@ class BotProcess:
|
|||||||
# the (unicode) lines available for readers through a queue.
|
# the (unicode) lines available for readers through a queue.
|
||||||
self.stdout_queue = queue.Queue()
|
self.stdout_queue = queue.Queue()
|
||||||
self.stdout_thread = t = threading.Thread(target=self._run_stdout_thread, name="bot-stdout-thread")
|
self.stdout_thread = t = threading.Thread(target=self._run_stdout_thread, name="bot-stdout-thread")
|
||||||
t.setDaemon(1)
|
t.setDaemon(True)
|
||||||
t.start()
|
t.start()
|
||||||
|
|
||||||
def _run_stdout_thread(self):
|
def _run_stdout_thread(self) -> None:
|
||||||
try:
|
try:
|
||||||
while 1:
|
while 1:
|
||||||
line = self.popen.stdout.readline()
|
line = self.popen.stdout.readline()
|
||||||
@@ -474,10 +482,10 @@ class BotProcess:
|
|||||||
finally:
|
finally:
|
||||||
self.stdout_queue.put(None)
|
self.stdout_queue.put(None)
|
||||||
|
|
||||||
def kill(self):
|
def kill(self) -> None:
|
||||||
self.popen.kill()
|
self.popen.kill()
|
||||||
|
|
||||||
def wait(self, timeout=30):
|
def wait(self, timeout=30) -> None:
|
||||||
self.popen.wait(timeout=timeout)
|
self.popen.wait(timeout=timeout)
|
||||||
|
|
||||||
def fnmatch_lines(self, pattern_lines):
|
def fnmatch_lines(self, pattern_lines):
|
||||||
@@ -509,14 +517,14 @@ def tmp_db_path(tmpdir):
|
|||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def lp():
|
def lp():
|
||||||
class Printer:
|
class Printer:
|
||||||
def sec(self, msg):
|
def sec(self, msg: str) -> None:
|
||||||
print()
|
print()
|
||||||
print("=" * 10, msg, "=" * 10)
|
print("=" * 10, msg, "=" * 10)
|
||||||
|
|
||||||
def step(self, msg):
|
def step(self, msg: str) -> None:
|
||||||
print("-" * 5, "step " + msg, "-" * 5)
|
print("-" * 5, "step " + msg, "-" * 5)
|
||||||
|
|
||||||
def indent(self, msg):
|
def indent(self, msg: str) -> None:
|
||||||
print(" " + msg)
|
print(" " + msg)
|
||||||
|
|
||||||
return Printer()
|
return Printer()
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ isolated_build = true
|
|||||||
envlist =
|
envlist =
|
||||||
py3
|
py3
|
||||||
lint
|
lint
|
||||||
|
mypy
|
||||||
auditwheels
|
auditwheels
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
@@ -43,6 +44,15 @@ commands =
|
|||||||
flake8 tests/ examples/
|
flake8 tests/ examples/
|
||||||
rst-lint --encoding 'utf-8' README.rst
|
rst-lint --encoding 'utf-8' README.rst
|
||||||
|
|
||||||
|
[testenv:mypy]
|
||||||
|
deps =
|
||||||
|
mypy
|
||||||
|
typing
|
||||||
|
types-setuptools
|
||||||
|
types-requests
|
||||||
|
commands =
|
||||||
|
mypy --no-incremental src/
|
||||||
|
|
||||||
[testenv:doc]
|
[testenv:doc]
|
||||||
changedir=doc
|
changedir=doc
|
||||||
deps =
|
deps =
|
||||||
|
|||||||
Reference in New Issue
Block a user