Daniel Brötzmann pushed to branch mainwindow at gajim / gajim
Commits: a01f9cc5 by wurstsalat at 2021-11-06T21:24:54+01:00 URI: Use NamedTuple
- - - - - 4843bf14 by wurstsalat at 2021-11-06T21:25:04+01:00 Helpers: Typing
- - - - - 45d775ff by wurstsalat at 2021-11-06T21:54:57+01:00 Util: Typing
- - - - - f4fde9ce by wurstsalat at 2021-11-06T22:19:30+01:00 Menu Builder: Typing
- - - - -
4 changed files:
- gajim/common/helpers.py - gajim/common/structs.py - gajim/gtk/util.py - gajim/gui_menu_builder.py
Changes:
===================================== gajim/common/helpers.py ===================================== @@ -27,6 +27,10 @@
from typing import Any # pylint: disable=unused-import from typing import Dict # pylint: disable=unused-import +from typing import Optional +from typing import List +from typing import Tuple +from typing import Union
import sys import re @@ -62,6 +66,8 @@ from nbxmpp.const import Role from nbxmpp.const import ConnectionProtocol from nbxmpp.const import ConnectionType +from nbxmpp.const import Affiliation +from nbxmpp.const import Role from nbxmpp.structs import ProxyData from nbxmpp.protocol import JID from nbxmpp.protocol import InvalidJid @@ -163,12 +169,13 @@ def parse_resource(resource): except UnicodeError: raise InvalidFormat('Invalid character in resource.')
-def windowsify(word): +def windowsify(word: str) -> str: if os.name == 'nt': return word.capitalize() return word
-def get_uf_show(show, use_mnemonic=False): +def get_uf_show(show: Union[ShowConstant, str], + use_mnemonic: bool = False) -> str: """ Return a userfriendly string for dnd/xa/chat and make all strings translatable @@ -219,7 +226,7 @@ def get_uf_show(show, use_mnemonic=False): uf_show = Q_('?contact has status:Has errors') return uf_show
-def get_uf_sub(sub): +def get_uf_sub(sub: str) -> str: if sub == 'none': uf_sub = Q_('?Subscription we already have:None') elif sub == 'to': @@ -233,7 +240,7 @@ def get_uf_sub(sub):
return uf_sub
-def get_uf_ask(ask): +def get_uf_ask(ask: Union[str, None]) -> str: if ask is None: uf_ask = Q_('?Ask (for Subscription):None') elif ask == 'subscribe': @@ -243,7 +250,7 @@ def get_uf_ask(ask):
return uf_ask
-def get_uf_role(role, plural=False): +def get_uf_role(role: Union[Role, str], plural: bool = False) -> str: ''' plural determines if you get Moderators or Moderator''' if not isinstance(role, str): role = role.value @@ -267,7 +274,8 @@ def get_uf_role(role, plural=False): role_name = _('Visitor') return role_name
-def get_uf_affiliation(affiliation, plural=False): +def get_uf_affiliation(affiliation: Union[Affiliation, str], + plural: bool = False) -> str: '''Get a nice and translated affilition for muc''' if not isinstance(affiliation, str): affiliation = affiliation.value @@ -319,7 +327,7 @@ def get_sorted_keys(adict): keys = sorted(adict.keys()) return keys
-def to_one_line(msg): +def to_one_line(msg: str) -> str: msg = msg.replace('\', '\\') msg = msg.replace('\n', '\n') # s1 = 'test\ntest\ntest' @@ -329,7 +337,7 @@ def to_one_line(msg): # 'test\ntest\\ntest' return msg
-def from_one_line(msg): +def from_one_line(msg: str) -> str: # (?<!\) is a lookbehind assertion which asks anything but '' # to match the regexp that follows it
@@ -344,7 +352,7 @@ def from_one_line(msg): # 'test\ntest\ntest' return msg
-def get_uf_chatstate(chatstate): +def get_uf_chatstate(chatstate: str) -> str: """ Remove chatstate jargon and returns user friendly messages """ @@ -393,7 +401,7 @@ def get_file_path_from_dnd_dropped_uri(uri): path = path[5:] # 5 is len('file:') return path
-def sanitize_filename(filename): +def sanitize_filename(filename: str) -> str: """ Make sure the filename we will write does contain only acceptable and latin characters, and is not too long (in that case hash it) @@ -413,7 +421,8 @@ def sanitize_filename(filename):
return filename
-def reduce_chars_newlines(text, max_chars=0, max_lines=0): +def reduce_chars_newlines(text: str, max_chars: int = 0, + max_lines: int = 0) -> str: """ Cut the chars after 'max_chars' on each line and show only the first 'max_lines' @@ -454,7 +463,8 @@ def get_contact_dict_for_account(account): return contacts_dict
-def play_sound(sound_event, account, force=False): +def play_sound(sound_event: str, account: str, + force: bool = False) -> None: if sound_event is None: return if force or allow_sound_notification(account, sound_event): @@ -547,7 +557,7 @@ def play_sound_file(path_to_soundfile): except GLib.Error as error: log.error('Could not play sound: %s', error.message)
-def get_connection_status(account): +def get_connection_status(account: str) -> str: con = app.connections[account] if con.state.is_reconnect_scheduled: return 'error' @@ -559,7 +569,7 @@ def get_connection_status(account): return 'offline' return con.status
-def get_global_show(): +def get_global_show() -> str: maxi = 0 for account in app.connections: if not app.settings.get_account_setting(account, @@ -571,7 +581,7 @@ def get_global_show(): maxi = index return SHOW_LIST[maxi]
-def get_global_status_message(): +def get_global_status_message() -> str: maxi = 0 for account in app.connections: if not app.settings.get_account_setting(account, @@ -584,7 +594,7 @@ def get_global_status_message(): status_message = app.connections[account].status_message return status_message
-def statuses_unified(): +def statuses_unified() -> bool: """ Test if all statuses are the same """ @@ -628,7 +638,7 @@ def remove_invalid_xml_chars(string_): string_ = re.sub(INVALID_XML_CHARS_REGEX, '', string_) return string_
-def get_random_string(count=16): +def get_random_string(count: int = 16) -> str: """ Create random string of count length
@@ -638,7 +648,7 @@ def get_random_string(count=16): return ''.join(random.choice(allowed) for char in range(count))
@functools.lru_cache(maxsize=1) -def get_os_info(): +def get_os_info() -> str: info = 'N/A' if sys.platform in ('win32', 'darwin'): info = f'{platform.system()} {platform.release()}' @@ -680,7 +690,7 @@ def message_needs_highlight(text, nickname, own_jid): return False
-def allow_showing_notification(account): +def allow_showing_notification(account: str) -> bool: if not app.settings.get('show_notifications'): return False if app.settings.get('show_notifications_away'): @@ -690,7 +700,7 @@ def allow_showing_notification(account): return False
-def allow_sound_notification(account, sound_event): +def allow_sound_notification(account: str, sound_event: str) -> bool: if not app.settings.get('sounds_on'): return False client = app.get_client(account) @@ -730,23 +740,25 @@ def jid_is_blocked(account, jid): con = app.connections[account] return jid in con.get_module('Blocking').blocked
-def get_subscription_request_msg(account=None): - s = app.settings.get_account_setting(account, 'subscription_request_msg') - if s: - return s - s = _('I would like to add you to my contact list.') - if account: - s = _('Hello, I am $name.') + ' ' + s - s = Template(s).safe_substitute({'name': app.nicks[account]}) - return s - -def get_user_proxy(account): +def get_subscription_request_msg(account: Optional[str] = None) -> str: + message = _('I would like to add you to my contact list.') + if account is None: + return message + + message = app.settings.get_account_setting(account, 'subscription_request_msg') + if message: + return message + + message = _('Hello, I am $name. %s') % message + return Template(message).safe_substitute({'name': app.nicks[account]}) + +def get_user_proxy(account: str) -> Optional[ProxyData]: proxy_name = app.settings.get_account_setting(account, 'proxy') if not proxy_name: return None return get_proxy(proxy_name)
-def get_proxy(proxy_name): +def get_proxy(proxy_name: str) -> Optional[ProxyData]: try: settings = app.settings.get_proxy_settings(proxy_name) except ValueError: @@ -926,7 +938,7 @@ def func_wrapper(self, *args, **kwargs): return func_wrapper
-def parse_uri_actions(uri): +def parse_uri_actions(uri: str) -> Tuple[str, Dict[str, str]]: uri = uri[5:] if '?' not in uri: return 'message', {'jid': uri} @@ -948,7 +960,7 @@ def parse_uri_actions(uri): return action, data
-def parse_uri(uri): +def parse_uri(uri: str) -> URI: if uri.startswith('xmpp:'): action, data = parse_uri_actions(uri) try: @@ -990,7 +1002,7 @@ def parse_uri(uri):
@catch_exceptions -def open_uri(uri, account=None): +def open_uri(uri: Union[URI, str], account: Optional[str] = None) -> None: if not isinstance(uri, URI): uri = parse_uri(uri)
@@ -1023,13 +1035,17 @@ def open_uri(uri, account=None): log.warning('Account must be specified to open XMPP uri') return
+ if isinstance(uri.data, dict): + jid = uri.data['jid'] + message = uri.data.get('body') + else: + log.warning('Cant open URI: %s', uri) if uri.action == URIAction.JOIN: app.app.activate_action( 'groupchat-join', - GLib.Variant('as', [account, uri.data['jid']])) + GLib.Variant('as', [account, jid])) elif uri.action == URIAction.MESSAGE: - app.interface.start_chat_from_jid(account, uri.data['jid'], - message=uri.data.get('body')) + app.interface.start_chat_from_jid(account, jid, message=message) else: log.warning('Cant open URI: %s', uri)
@@ -1080,9 +1096,8 @@ def file_is_locked(path_to_file): return False
-def geo_provider_from_location(lat, lon): - return ('https://www.openstreetmap.org/?' - 'mlat=%s&mlon=%s&zoom=16') % (lat, lon) +def geo_provider_from_location(lat: str, lon: str) -> str: + return f'https://www.openstreetmap.org/?mlat=%7Blat%7D&mlon=%7Blon%7D&zoom=16'
def get_resource(account): @@ -1097,7 +1112,7 @@ def get_resource(account): return resource
-def get_default_muc_config(): +def get_default_muc_config() -> Dict[str, Union[bool, str]]: return { # XEP-0045 options 'muc#roomconfig_allowinvites': True, @@ -1182,10 +1197,10 @@ def is_role_change_allowed(self_contact, contact): return self_contact.affiliation >= contact.affiliation
-def get_tls_error_phrase(tls_error): +def get_tls_error_phrase(tls_error: Gio.TlsCertificateFlags) -> str: phrase = GIO_TLS_ERRORS.get(tls_error) if phrase is None: - return GIO_TLS_ERRORS.get(Gio.TlsCertificateFlags.GENERIC_ERROR) + return GIO_TLS_ERRORS[Gio.TlsCertificateFlags.GENERIC_ERROR] return phrase
@@ -1296,7 +1311,8 @@ def convert_gio_to_openssl_cert(cert): return cert
-def get_custom_host(account): +def get_custom_host(account: str + ) -> Optional[Tuple[str, ConnectionProtocol, str]]: if not app.settings.get_account_setting(account, 'use_custom_host'): return None host = app.settings.get_account_setting(account, 'custom_host') @@ -1312,7 +1328,9 @@ def get_custom_host(account): return (host, protocol, ConnectionType(type_))
-def warn_about_plain_connection(account, connection_types): +def warn_about_plain_connection(account: str, + connection_types: List[ConnectionType] + ) -> bool: warn = app.settings.get_account_setting( account, 'confirm_unencrypted_connection') for type_ in connection_types: @@ -1321,7 +1339,7 @@ def warn_about_plain_connection(account, connection_types): return False
-def get_idle_status_message(state, status_message): +def get_idle_status_message(state: str, status_message: str) -> str: message = app.settings.get(f'auto{state}_message') if not message: message = status_message @@ -1349,7 +1367,7 @@ def should_log(account, jid): return (account not in no_log_for) and (jid not in no_log_for)
-def ask_for_status_message(status, signin=False): +def ask_for_status_message(status: str, signin: bool = False) -> bool: if status is None: # We try to change the message return True
===================================== gajim/common/structs.py ===================================== @@ -12,8 +12,14 @@ # You should have received a copy of the GNU General Public License # along with Gajim. If not, see http://www.gnu.org/licenses/.
+from typing import Dict +from typing import NamedTuple +from typing import Optional +from typing import Tuple +from typing import Union +from typing import Any + import time -from collections import namedtuple from dataclasses import dataclass
from nbxmpp.protocol import JID @@ -23,9 +29,14 @@ from gajim.common.const import MUCJoinedState from gajim.common.const import KindConstant from gajim.common.const import PresenceShowExt +from gajim.common.const import URIType +from gajim.common.const import URIAction +
-URI = namedtuple('URI', 'type action data') -URI.__new__.__defaults__ = (None, None) # type: ignore +class URI(NamedTuple): + type: URIType + action: Optional[URIAction] = None + data: Optional[Union[Dict[str, str], str]] = None
class MUCData:
===================================== gajim/gtk/util.py ===================================== @@ -44,6 +44,7 @@ from gi.repository import Pango from gi.repository import GdkPixbuf import nbxmpp +from nbxmpp.structs import LocationData import cairo
from gajim.common import app @@ -263,7 +264,7 @@ def get_icon_name(name: str, return '%s-%s' % (iconset, name)
-def load_user_iconsets(): +def load_user_iconsets() -> None: iconsets_path = configpaths.get('MY_ICONSETS') if not iconsets_path.exists(): return @@ -275,7 +276,7 @@ def load_user_iconsets(): _icon_theme.append_search_path(str(path))
-def get_available_iconsets(): +def get_available_iconsets() -> List[str]: iconsets = [] for iconset in GajimIconSet: iconsets.append(iconset.value) @@ -421,7 +422,7 @@ def at_the_end(widget: Gtk.ScrolledWindow) -> bool:
def get_image_button(icon_name: str, tooltip: str, - toggle: Optional[bool] = False) -> Gtk.Button: + toggle: bool = False) -> Gtk.Button: if toggle: button = Gtk.ToggleButton() image = Gtk.Image.new_from_icon_name(icon_name, Gtk.IconSize.MENU) @@ -534,7 +535,7 @@ def func_wrapper(self, *args, **kwargs): return func_wrapper
-def format_mood(mood, text): +def format_mood(mood: str, text: str) -> str: if mood is None: return '' mood = MOODS[mood] @@ -544,13 +545,13 @@ def format_mood(mood, text): return markuptext
-def get_account_mood_icon_name(account): +def get_account_mood_icon_name(account: str) -> Optional[str]: client = app.get_client(account) mood = client.get_module('UserMood').get_current_mood() return f'mood-{mood.mood}' if mood is not None else mood
-def format_activity(activity, subactivity, text): +def format_activity(activity: str, subactivity: str, text: str) -> str: if subactivity in ACTIVITIES[activity]: subactivity = ACTIVITIES[activity][subactivity] activity = ACTIVITIES[activity]['category'] @@ -564,14 +565,15 @@ def format_activity(activity, subactivity, text): return markuptext
-def get_activity_icon_name(activity, subactivity=None): +def get_activity_icon_name(activity: str, + subactivity: Optional[str] = None) -> str: icon_name = 'activity-%s' % activity.replace('_', '-') if subactivity is not None: icon_name += '-%s' % subactivity.replace('_', '-') return icon_name
-def get_account_activity_icon_name(account): +def get_account_activity_icon_name(account: str) -> Optional[str]: client = app.get_client(account) activity = client.get_module('UserActivity').get_current_activity() if activity is None: @@ -579,7 +581,8 @@ def get_account_activity_icon_name(account): return get_activity_icon_name(activity.activity, activity.subactivity)
-def format_tune(artist, _length, _rating, source, title, _track, _uri): +def format_tune(artist: str, _length: str, _rating: str, source: str, + title: str, _track: str, _uri: str) -> str: artist = GLib.markup_escape_text(artist or _('Unknown Artist')) title = GLib.markup_escape_text(title or _('Unknown Title')) source = GLib.markup_escape_text(source or _('Unknown Source')) @@ -591,13 +594,13 @@ def format_tune(artist, _length, _rating, source, title, _track, _uri): return tune_string
-def get_account_tune_icon_name(account): +def get_account_tune_icon_name(account: str) -> Optional[str]: client = app.get_client(account) tune = client.get_module('UserTune').get_current_tune() return None if tune is None else 'audio-x-generic'
-def format_location(location): +def format_location(location: LocationData) -> str: location = location._asdict() location_string = '' for attr, value in location.items(): @@ -614,7 +617,7 @@ def format_location(location): return location_string.strip()
-def get_account_location_icon_name(account): +def get_account_location_icon_name(account: str) -> Optional[str]: client = app.get_client(account) location = client.get_module('UserLocation').get_current_location() return None if location is None else 'applications-internet' @@ -625,7 +628,7 @@ def format_eta(time_: Union[int, float]) -> str: time_ = int(time_) times['seconds'] = time_ % 60 if time_ >= 60: - time_ /= 60 + time_ = int(time_ / 60) times['minutes'] = round(time_ % 60) return _('%(minutes)s min %(seconds)s s') % times return _('%s s') % times['seconds'] @@ -705,7 +708,7 @@ def get_css_show_class(show: str) -> str: return '.gajim-status-offline'
-def add_css_to_widget(widget, css): +def add_css_to_widget(widget: Any, css: str) -> None: provider = Gtk.CssProvider() provider.load_from_data(bytes(css.encode())) context = widget.get_style_context() @@ -713,7 +716,7 @@ def add_css_to_widget(widget, css): Gtk.STYLE_PROVIDER_PRIORITY_USER)
-def get_pixbuf_from_data(file_data): +def get_pixbuf_from_data(file_data: bytes) -> Optional[GdkPixbuf.Pixbuf]: # TODO: This already exists in preview_helpery pixbuf_from_data """ Get image data and returns GdkPixbuf.Pixbuf @@ -738,7 +741,7 @@ def get_pixbuf_from_data(file_data): except Exception: log.warning('Could not use pillow to convert avatar image, ' 'image cannot be displayed', exc_info=True) - return + return None
return pixbuf
@@ -763,7 +766,7 @@ def scale_pixbuf(pixbuf: GdkPixbuf.Pixbuf, GdkPixbuf.InterpType.BILINEAR)
-def scale_pixbuf_from_data(data, size): +def scale_pixbuf_from_data(data: bytes, size: int) -> Optional[GdkPixbuf.Pixbuf]: pixbuf = get_pixbuf_from_data(data) return scale_pixbuf(pixbuf, size)
@@ -821,7 +824,7 @@ def get_thumbnail_size(pixbuf: GdkPixbuf.Pixbuf, size: int) -> Tuple[int, int]: return image_width, image_height
-def make_href_markup(string): +def make_href_markup(string: str) -> str: url_color = app.css_config.get_value('.gajim-url', StyleAttr.COLOR) color = convert_rgb_to_hex(url_color)
===================================== gajim/gui_menu_builder.py ===================================== @@ -14,13 +14,19 @@ # You should have received a copy of the GNU General Public License # along with Gajim. If not, see http://www.gnu.org/licenses/.
-from typing import Optional +from typing import Tuple +from typing import List +from typing import Union +from typing import Any + from urllib.parse import quote
from gi.repository import Gtk from gi.repository import Gio from gi.repository import GLib
+from nbxmpp import JID + from gajim.common import app from gajim.common.helpers import is_affiliation_change_allowed from gajim.common.helpers import is_role_change_allowed @@ -35,11 +41,12 @@ from gajim.gui.const import ControlType
-def get_singlechat_menu(control_id, account, jid, type_): +def get_singlechat_menu(control_id: str, account: str, jid: JID, + type_: ControlType) -> Gio.Menu: client = app.get_client(account) is_self_contact = jid.bare == client.get_own_jid().bare
- singlechat_menu = [ + singlechat_menu: List[Any] = [ (_('Send File'), [ ('win.send-file-httpupload-', _('Upload File…')), ('win.send-file-jingle-', _('Send File Directly…')) @@ -117,7 +124,7 @@ def build_menu(preset): return build_menu(singlechat_menu)
-def get_groupchat_menu(control_id, account, jid): +def get_groupchat_menu(control_id: str, account: str, jid: str) -> Gio.Menu: groupchat_menu = [ ('win.information-', _('Information')), ('win.groupchat-settings-', _('Settings…')), @@ -219,7 +226,7 @@ def build_menu(preset): return build_menu(account_menu)
-def build_accounts_menu(): +def build_accounts_menu() -> None: menubar = app.app.get_menubar() # Accounts Submenu menu_position = 1 @@ -259,7 +266,7 @@ def build_accounts_menu():
def get_encryption_menu(control_id: str, control_type: ControlType, - zeroconf: Optional[bool] = False) -> Gio.Menu: + zeroconf: bool = False) -> Gio.Menu: menu = Gio.Menu() menu.append( _('Disabled'), f'win.set-encryption-{control_id}::disabled') @@ -405,7 +412,10 @@ def get_conv_uri_context_menu(account: str, uri: URI) -> Gtk.Menu:
data = uri.data if uri.type == URIType.XMPP: - data = uri.data['jid'] + if isinstance(uri.data, dict): + data = uri.data['jid'] + else: + data = ''
if action in ('app.open-mail', 'app.copy-text'): value = GLib.Variant.new_string(data) @@ -417,7 +427,7 @@ def get_conv_uri_context_menu(account: str, uri: URI) -> Gtk.Menu: return menu
-def get_roster_menu(account, jid, gateway=False): +def get_roster_menu(account: str, jid: str, gateway: bool = False) -> Gio.Menu: if jid_is_blocked(account, jid): block_label = _('Unblock') else: @@ -436,14 +446,14 @@ def get_roster_menu(account, jid, gateway=False): action, label = item action = f'win.{action}-{account}' menuitem = Gio.MenuItem.new(label, action) - variant = GLib.Variant('s', str(jid)) + variant = GLib.Variant('s', jid) menuitem.set_action_and_target_value(action, variant) menu.append_item(menuitem)
return menu
-def get_subscription_menu(account, jid): +def get_subscription_menu(account: str, jid: str) -> Gio.Menu: menu = Gio.Menu()
action_name = 'win.add-chat' @@ -475,13 +485,15 @@ def get_subscription_menu(account, jid): return menu
-def get_chat_list_row_menu(workspace_id, account, jid, pinned): +def get_chat_list_row_menu(workspace_id: str, account: str, + jid: Union[JID, str], pinned: bool) -> Gio.Menu: + jid = str(jid) client = app.get_client(account) contact = client.get_module('Contacts').get_contact(jid)
toggle_label = _('Unpin Chat') if pinned else _('Pin Chat')
- menu_items = [ + menu_items: List[Any] = [ ('toggle-chat-pinned', toggle_label), ]
@@ -502,20 +514,21 @@ def get_chat_list_row_menu(workspace_id, account, jid, pinned): action = f'win.{action}' menuitem = Gio.MenuItem.new(label, action) variant_list = GLib.Variant( - 'as', [workspace_id, account, str(jid)]) + 'as', [workspace_id, account, jid]) menuitem.set_action_and_target_value(action, variant_list) menu.append_item(menuitem) else: # This is a submenu if len(workspaces) > 1: submenu = build_workspaces_submenu( - workspace_id, account, str(jid)) + workspace_id, account, jid) menu.append_submenu(item[0], submenu)
return menu
-def build_workspaces_submenu(current_workspace_id, account, jid): +def build_workspaces_submenu(current_workspace_id: str, account: str, + jid: str) -> Gio.Menu: submenu = Gio.Menu() for workspace_id in app.settings.get_workspaces(): if workspace_id == current_workspace_id:
View it on GitLab: https://dev.gajim.org/gajim/gajim/-/compare/e484fc87d0a11ec8bf83b31635b62674...