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`.
This commit is contained in:
link2xt
2021-11-21 13:22:49 +00:00
parent 30a3eeece8
commit ddefd2cf09
6 changed files with 43 additions and 38 deletions

View File

@@ -8,7 +8,7 @@ import os
from array import array from array import array
from . import const from . import const
from .capi import ffi, lib 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 .chat import Chat
from .message import Message from .message import Message
from .contact import Contact from .contact import Contact
@@ -79,7 +79,7 @@ class Account(object):
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))
def get_info(self): def get_info(self) -> Dict[str, str]:
""" return dictionary of built config parameters. """ """ return dictionary of built config parameters. """
lines = from_dc_charpointer(lib.dc_get_info(self._dc_context)) lines = from_dc_charpointer(lib.dc_get_info(self._dc_context))
d = {} d = {}
@@ -135,7 +135,7 @@ class Account(object):
valuebytes = ffi.NULL valuebytes = ffi.NULL
lib.dc_set_config(self._dc_context, namebytes, valuebytes) 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. """ 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")
@@ -200,11 +200,9 @@ class Account(object):
""" 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))
if res == ffi.NULL: return from_optional_dc_charpointer(res)
return None
return from_dc_charpointer(res)
def get_blobdir(self) -> Optional[str]: def get_blobdir(self) -> 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.
@@ -477,7 +475,7 @@ class Account(object):
def imex(self, path, imex_cmd): def imex(self, path, imex_cmd):
lib.dc_imex(self._dc_context, imex_cmd, as_dc_charpointer(path), ffi.NULL) 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 """return setup code after a Autocrypt setup message
has been successfully sent to our own e-mail address ("self-sent message"). has been successfully sent to our own e-mail address ("self-sent message").
If sending out was unsuccessful, a RuntimeError is raised. 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") 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) -> Optional[str]: def get_setup_contact_qr(self) -> 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
@@ -584,7 +582,7 @@ class Account(object):
def get_connectivity(self): def get_connectivity(self):
return lib.dc_get_connectivity(self._dc_context) 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)) return from_dc_charpointer(lib.dc_get_connectivity_html(self._dc_context))
def all_work_done(self): def all_work_done(self):

View File

@@ -1,10 +1,11 @@
""" Contact object. """ """ Contact object. """
from . import props 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 .capi import lib, ffi
from .chat import Chat from .chat import Chat
from . import const from . import const
from typing import Optional
class Contact(object): class Contact(object):
@@ -35,12 +36,12 @@ class Contact(object):
) )
@props.with_doc @props.with_doc
def addr(self): def addr(self) -> str:
""" normalized e-mail address for this account. """ """ normalized e-mail address for this account. """
return from_dc_charpointer(lib.dc_contact_get_addr(self._dc_contact)) return from_dc_charpointer(lib.dc_contact_get_addr(self._dc_contact))
@props.with_doc @props.with_doc
def name(self): def name(self) -> str:
""" display name for this contact. """ """ display name for this contact. """
return from_dc_charpointer(lib.dc_contact_get_display_name(self._dc_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 True if the contact is verified. """
return lib.dc_contact_is_verified(self._dc_contact) 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. """Get contact profile image.
:returns: path to profile image, None if no profile image exists. :returns: path to profile image, None if no profile image exists.
""" """
dc_res = lib.dc_contact_get_profile_image(self._dc_contact) dc_res = lib.dc_contact_get_profile_image(self._dc_contact)
if dc_res == ffi.NULL: return from_optional_dc_charpointer(dc_res)
return None
return from_dc_charpointer(dc_res)
@property @property
def status(self): def status(self):

View File

@@ -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)) 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: 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 return None

View File

@@ -9,7 +9,7 @@ from .hookspec import account_hookimpl
from contextlib import contextmanager from contextlib import contextmanager
from .capi import ffi, lib from .capi import ffi, lib
from .message import map_system_message from .message import map_system_message
from .cutil import from_dc_charpointer from .cutil import from_optional_dc_charpointer
class FFIEvent: class FFIEvent:
@@ -235,7 +235,7 @@ class EventThread(threading.Thread):
# function which provides us signature info of an event call # function which provides us signature info of an event call
evt_name = deltachat.get_dc_event_name(evt) evt_name = deltachat.get_dc_event_name(evt)
if lib.dc_event_has_string_data(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: else:
data2 = lib.dc_event_get_data2_int(event) data2 = lib.dc_event_get_data2_int(event)

View File

@@ -3,10 +3,11 @@
import os import os
import re import re
from . import props 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 .capi import lib, ffi
from . import const from . import const
from datetime import datetime, timezone from datetime import datetime, timezone
from typing import Optional
class Message(object): class Message(object):
@@ -75,7 +76,7 @@ class Message(object):
return lib.dc_msg_get_id(self._dc_msg) return lib.dc_msg_get_id(self._dc_msg)
@props.with_doc @props.with_doc
def text(self): def text(self) -> str:
"""unicode text of this messages (might be empty if not a text message). """ """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)) 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)) lib.dc_msg_set_text(self._dc_msg, as_dc_charpointer(text))
@props.with_doc @props.with_doc
def html(self): def html(self) -> str:
"""html text of this messages (might be empty if not an html message). """ """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 "" lib.dc_get_msg_html(self.account._dc_context, self.id)) or ""
def has_html(self): def has_html(self):
@@ -113,12 +114,13 @@ class Message(object):
lib.dc_msg_set_file(self._dc_msg, as_dc_charpointer(path), mtype) lib.dc_msg_set_file(self._dc_msg, as_dc_charpointer(path), mtype)
@props.with_doc @props.with_doc
def basename(self): def basename(self) -> str:
"""basename of the attachment if it exists, otherwise empty string. """ """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)) return from_dc_charpointer(lib.dc_msg_get_filename(self._dc_msg))
@props.with_doc @props.with_doc
def filemime(self): def filemime(self) -> str:
"""mime type of the file (if it exists)""" """mime type of the file (if it exists)"""
return from_dc_charpointer(lib.dc_msg_get_filemime(self._dc_msg)) 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 True if this message is a setup message. """
return lib.dc_msg_is_setupmessage(self._dc_msg) 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 the first characters of a setup code in a setup message. """
return from_dc_charpointer(lib.dc_msg_get_setupcodebegin(self._dc_msg)) 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 True if this message was forwarded. """
return bool(lib.dc_msg_is_forwarded(self._dc_msg)) 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. """ Return informational text for a single message.
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.
@@ -203,11 +205,11 @@ class Message(object):
return datetime.fromtimestamp(ts, timezone.utc) return datetime.fromtimestamp(ts, timezone.utc)
@property @property
def quoted_text(self): def quoted_text(self) -> Optional[str]:
"""Text inside the quote """Text inside the quote
:returns: Quoted text""" :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 @property
def quote(self): def quote(self):
@@ -240,9 +242,9 @@ class Message(object):
return email.message_from_string(s) return email.message_from_string(s)
@property @property
def error(self): def error(self) -> Optional[str]:
"""Error message""" """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 @property
def chat(self): def chat(self):
@@ -255,12 +257,12 @@ class Message(object):
return Chat(self.account, chat_id) return Chat(self.account, chat_id)
@props.with_doc @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. """the name that should be shown over the message instead of the contact display name.
Usually used to impersonate someone else. 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)) lib.dc_msg_get_override_sender_name(self._dc_msg))
def set_override_sender_name(self, name): def set_override_sender_name(self, name):

View File

@@ -24,19 +24,19 @@ class Provider(object):
self._provider = provider self._provider = provider
@property @property
def overview_page(self): def overview_page(self) -> str:
"""URL to the overview page of the provider on providers.delta.chat.""" """URL to the overview page of the provider on providers.delta.chat."""
return from_dc_charpointer( return from_dc_charpointer(
lib.dc_provider_get_overview_page(self._provider)) lib.dc_provider_get_overview_page(self._provider))
@property @property
def get_before_login_hints(self): def get_before_login_hints(self) -> str:
"""Should be shown to the user on login.""" """Should be shown to the user on login."""
return from_dc_charpointer( return from_dc_charpointer(
lib.dc_provider_get_before_login_hints(self._provider)) lib.dc_provider_get_before_login_hint(self._provider))
@property @property
def status(self): def status(self) -> int:
"""The status of the provider information. """The status of the provider information.
This is one of the This is one of the