From ddefd2cf09a406768c6581397d5a64ac693b018f Mon Sep 17 00:00:00 2001 From: link2xt Date: Sun, 21 Nov 2021 13:22:49 +0000 Subject: [PATCH] python: add cutil.from_optional_dc_charpointer() `cutil.from_dc_charpointer()` is guaranteed to return `str`, while `cutil.from_optional_dc_charpointer()` may return `None` if C function returns `NULL`. --- python/src/deltachat/account.py | 18 ++++++++---------- python/src/deltachat/contact.py | 13 ++++++------- python/src/deltachat/cutil.py | 8 +++++++- python/src/deltachat/events.py | 4 ++-- python/src/deltachat/message.py | 30 ++++++++++++++++-------------- python/src/deltachat/provider.py | 8 ++++---- 6 files changed, 43 insertions(+), 38 deletions(-) diff --git a/python/src/deltachat/account.py b/python/src/deltachat/account.py index c20ed8b75..db75d30d1 100644 --- a/python/src/deltachat/account.py +++ b/python/src/deltachat/account.py @@ -8,7 +8,7 @@ import os from array import array from . import const from .capi import ffi, lib -from .cutil import as_dc_charpointer, from_dc_charpointer, iter_array, DCLot +from .cutil import as_dc_charpointer, from_dc_charpointer, from_optional_dc_charpointer, iter_array, DCLot from .chat import Chat from .message import Message from .contact import Contact @@ -79,7 +79,7 @@ class Account(object): raise KeyError("{!r} not a valid config key, existing keys: {!r}".format( name, self._configkeys)) - def get_info(self): + def get_info(self) -> Dict[str, str]: """ return dictionary of built config parameters. """ lines = from_dc_charpointer(lib.dc_get_info(self._dc_context)) d = {} @@ -135,7 +135,7 @@ class Account(object): valuebytes = ffi.NULL lib.dc_set_config(self._dc_context, namebytes, valuebytes) - def get_config(self, name: str): + def get_config(self, name: str) -> str: """ return unicode string value. :param name: configuration key to lookup (eg "addr" or "mail_pw") @@ -200,11 +200,9 @@ class Account(object): """ return the latest backup file in a given directory. """ res = lib.dc_imex_has_backup(self._dc_context, as_dc_charpointer(backupdir)) - if res == ffi.NULL: - return None - return from_dc_charpointer(res) + return from_optional_dc_charpointer(res) - def get_blobdir(self) -> Optional[str]: + def get_blobdir(self) -> str: """ return the directory for files. All sent files are copied to this directory if necessary. @@ -477,7 +475,7 @@ class Account(object): def imex(self, path, imex_cmd): lib.dc_imex(self._dc_context, imex_cmd, as_dc_charpointer(path), ffi.NULL) - def initiate_key_transfer(self): + def initiate_key_transfer(self) -> str: """return setup code after a Autocrypt setup message has been successfully sent to our own e-mail address ("self-sent message"). If sending out was unsuccessful, a RuntimeError is raised. @@ -488,7 +486,7 @@ class Account(object): raise RuntimeError("could not send out autocrypt setup message") return from_dc_charpointer(res) - def get_setup_contact_qr(self) -> Optional[str]: + def get_setup_contact_qr(self) -> str: """ get/create Setup-Contact QR Code as ascii-string. this string needs to be transferred to another DC account @@ -584,7 +582,7 @@ class Account(object): def get_connectivity(self): return lib.dc_get_connectivity(self._dc_context) - def get_connectivity_html(self): + def get_connectivity_html(self) -> str: return from_dc_charpointer(lib.dc_get_connectivity_html(self._dc_context)) def all_work_done(self): diff --git a/python/src/deltachat/contact.py b/python/src/deltachat/contact.py index 477effb35..f29f1a096 100644 --- a/python/src/deltachat/contact.py +++ b/python/src/deltachat/contact.py @@ -1,10 +1,11 @@ """ Contact object. """ from . import props -from .cutil import from_dc_charpointer +from .cutil import from_dc_charpointer, from_optional_dc_charpointer from .capi import lib, ffi from .chat import Chat from . import const +from typing import Optional class Contact(object): @@ -35,12 +36,12 @@ class Contact(object): ) @props.with_doc - def addr(self): + def addr(self) -> str: """ normalized e-mail address for this account. """ return from_dc_charpointer(lib.dc_contact_get_addr(self._dc_contact)) @props.with_doc - def name(self): + def name(self) -> str: """ display name for this contact. """ return from_dc_charpointer(lib.dc_contact_get_display_name(self._dc_contact)) @@ -67,15 +68,13 @@ class Contact(object): """ Return True if the contact is verified. """ return lib.dc_contact_is_verified(self._dc_contact) - def get_profile_image(self): + def get_profile_image(self) -> Optional[str]: """Get contact profile image. :returns: path to profile image, None if no profile image exists. """ dc_res = lib.dc_contact_get_profile_image(self._dc_contact) - if dc_res == ffi.NULL: - return None - return from_dc_charpointer(dc_res) + return from_optional_dc_charpointer(dc_res) @property def status(self): diff --git a/python/src/deltachat/cutil.py b/python/src/deltachat/cutil.py index da98212a5..1a91e6a8e 100644 --- a/python/src/deltachat/cutil.py +++ b/python/src/deltachat/cutil.py @@ -19,7 +19,13 @@ def iter_array(dc_array_t, constructor: Callable[[int], T]) -> Generator[T, None yield constructor(lib.dc_array_get_id(dc_array_t, i)) -def from_dc_charpointer(obj) -> Optional[str]: +def from_dc_charpointer(obj) -> str: + if obj != ffi.NULL: + return ffi.string(ffi.gc(obj, lib.dc_str_unref)).decode("utf8") + raise ValueError + + +def from_optional_dc_charpointer(obj) -> Optional[str]: if obj != ffi.NULL: return ffi.string(ffi.gc(obj, lib.dc_str_unref)).decode("utf8") return None diff --git a/python/src/deltachat/events.py b/python/src/deltachat/events.py index 8fe60a261..e0e1c3348 100644 --- a/python/src/deltachat/events.py +++ b/python/src/deltachat/events.py @@ -9,7 +9,7 @@ from .hookspec import account_hookimpl from contextlib import contextmanager from .capi import ffi, lib from .message import map_system_message -from .cutil import from_dc_charpointer +from .cutil import from_optional_dc_charpointer class FFIEvent: @@ -235,7 +235,7 @@ class EventThread(threading.Thread): # function which provides us signature info of an event call evt_name = deltachat.get_dc_event_name(evt) if lib.dc_event_has_string_data(evt): - data2 = from_dc_charpointer(lib.dc_event_get_data2_str(event)) + data2 = from_optional_dc_charpointer(lib.dc_event_get_data2_str(event)) else: data2 = lib.dc_event_get_data2_int(event) diff --git a/python/src/deltachat/message.py b/python/src/deltachat/message.py index 8bb06252a..2ac2c65d8 100644 --- a/python/src/deltachat/message.py +++ b/python/src/deltachat/message.py @@ -3,10 +3,11 @@ import os import re from . import props -from .cutil import from_dc_charpointer, as_dc_charpointer +from .cutil import from_dc_charpointer, from_optional_dc_charpointer, as_dc_charpointer from .capi import lib, ffi from . import const from datetime import datetime, timezone +from typing import Optional class Message(object): @@ -75,7 +76,7 @@ class Message(object): return lib.dc_msg_get_id(self._dc_msg) @props.with_doc - def text(self): + def text(self) -> str: """unicode text of this messages (might be empty if not a text message). """ return from_dc_charpointer(lib.dc_msg_get_text(self._dc_msg)) @@ -84,9 +85,9 @@ class Message(object): lib.dc_msg_set_text(self._dc_msg, as_dc_charpointer(text)) @props.with_doc - def html(self): + def html(self) -> str: """html text of this messages (might be empty if not an html message). """ - return from_dc_charpointer( + return from_optional_dc_charpointer( lib.dc_get_msg_html(self.account._dc_context, self.id)) or "" def has_html(self): @@ -113,12 +114,13 @@ class Message(object): lib.dc_msg_set_file(self._dc_msg, as_dc_charpointer(path), mtype) @props.with_doc - def basename(self): + def basename(self) -> str: """basename of the attachment if it exists, otherwise empty string. """ + # FIXME, it does not return basename return from_dc_charpointer(lib.dc_msg_get_filename(self._dc_msg)) @props.with_doc - def filemime(self): + def filemime(self) -> str: """mime type of the file (if it exists)""" return from_dc_charpointer(lib.dc_msg_get_filemime(self._dc_msg)) @@ -130,7 +132,7 @@ class Message(object): """ return True if this message is a setup message. """ return lib.dc_msg_is_setupmessage(self._dc_msg) - def get_setupcodebegin(self): + def get_setupcodebegin(self) -> str: """ return the first characters of a setup code in a setup message. """ return from_dc_charpointer(lib.dc_msg_get_setupcodebegin(self._dc_msg)) @@ -146,7 +148,7 @@ class Message(object): """ return True if this message was forwarded. """ return bool(lib.dc_msg_is_forwarded(self._dc_msg)) - def get_message_info(self): + def get_message_info(self) -> str: """ Return informational text for a single message. The text is multiline and may contain eg. the raw text of the message. @@ -203,11 +205,11 @@ class Message(object): return datetime.fromtimestamp(ts, timezone.utc) @property - def quoted_text(self): + def quoted_text(self) -> Optional[str]: """Text inside the quote :returns: Quoted text""" - return from_dc_charpointer(lib.dc_msg_get_quoted_text(self._dc_msg)) + return from_optional_dc_charpointer(lib.dc_msg_get_quoted_text(self._dc_msg)) @property def quote(self): @@ -240,9 +242,9 @@ class Message(object): return email.message_from_string(s) @property - def error(self): + def error(self) -> Optional[str]: """Error message""" - return from_dc_charpointer(lib.dc_msg_get_error(self._dc_msg)) + return from_optional_dc_charpointer(lib.dc_msg_get_error(self._dc_msg)) @property def chat(self): @@ -255,12 +257,12 @@ class Message(object): return Chat(self.account, chat_id) @props.with_doc - def override_sender_name(self): + def override_sender_name(self) -> Optional[str]: """the name that should be shown over the message instead of the contact display name. Usually used to impersonate someone else. """ - return from_dc_charpointer( + return from_optional_dc_charpointer( lib.dc_msg_get_override_sender_name(self._dc_msg)) def set_override_sender_name(self, name): diff --git a/python/src/deltachat/provider.py b/python/src/deltachat/provider.py index d4b7c7934..ed90784b4 100644 --- a/python/src/deltachat/provider.py +++ b/python/src/deltachat/provider.py @@ -24,19 +24,19 @@ class Provider(object): self._provider = provider @property - def overview_page(self): + def overview_page(self) -> str: """URL to the overview page of the provider on providers.delta.chat.""" return from_dc_charpointer( lib.dc_provider_get_overview_page(self._provider)) @property - def get_before_login_hints(self): + def get_before_login_hints(self) -> str: """Should be shown to the user on login.""" return from_dc_charpointer( - lib.dc_provider_get_before_login_hints(self._provider)) + lib.dc_provider_get_before_login_hint(self._provider)) @property - def status(self): + def status(self) -> int: """The status of the provider information. This is one of the