Philipp Hörist pushed to branch master at gajim / gajim
Commits: 13c8d5bc by lovetox at 2021-12-29T01:21:26+01:00 Settings: Add type annotations
- - - - - 4f05b70f by lovetox at 2021-12-29T13:36:42+01:00 Contacts: Add type annotations
- - - - - fb1654b5 by lovetox at 2021-12-29T13:36:47+01:00 Application: Add type annotations
- - - - - 1ab09d45 by lovetox at 2021-12-29T13:36:47+01:00 AppActions: Add type annotations
- - - - - 932b79c7 by lovetox at 2021-12-29T13:36:47+01:00 ConversationView: Add type annotations
- - - - -
7 changed files:
- gajim/app_actions.py - gajim/application.py - gajim/common/modules/contacts.py - gajim/common/setting_values.py - gajim/common/settings.py - gajim/gtk/conversation/view.py - gajim/gui_interface.py
Changes:
===================================== gajim/app_actions.py ===================================== @@ -14,8 +14,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 __future__ import annotations + +from typing import Optional + from gi.repository import Gtk from gi.repository import Gdk +from gi.repository import GLib +from gi.repository import Gio
from nbxmpp import JID
@@ -33,7 +39,8 @@ # General Actions
-def on_add_contact_jid(_action, param): +def on_add_contact_jid(_action: Gio.SimpleAction, + param: GLib.Variant) -> None: jid = param.get_string() or None if jid is not None: jid = JID.from_string(jid) @@ -43,35 +50,42 @@ def on_add_contact_jid(_action, param): # Application Menu Actions
-def on_preferences(_action, _param): +def on_preferences(_action: Gio.SimpleAction, + _param: Optional[GLib.Variant]) -> None: open_window('Preferences')
-def on_plugins(_action, _param): +def on_plugins(_action: Gio.SimpleAction, + _param: Optional[GLib.Variant]) -> None: open_window('PluginsWindow')
-def on_accounts(_action, param): +def on_accounts(_action: Gio.SimpleAction, + param: GLib.Variant) -> None: window = open_window('AccountsWindow') account = param.get_string() if account: window.select_account(account)
-def on_history_manager(_action, _param): +def on_history_manager(_action: Gio.SimpleAction, + _param: Optional[GLib.Variant]) -> None: open_window('HistoryManager')
-def on_bookmarks(_action, param): +def on_bookmarks(_action: Gio.SimpleAction, + param: GLib.Variant) -> None: account = param.get_string() open_window('Bookmarks', account=account)
-def on_quit(_action, _param): +def on_quit(_action: Gio.SimpleAction, + _param: Optional[GLib.Variant]) -> None: app.window.quit()
-def on_new_chat(_action, param): +def on_new_chat(_action: Gio.SimpleAction, + param: GLib.Variant) -> None: window = open_window('StartChatDialog') search_text = param.get_string() if search_text: @@ -81,19 +95,22 @@ def on_new_chat(_action, param): # Accounts Actions
-def on_profile(_action, param): +def on_profile(_action: Gio.SimpleAction, + param: GLib.Variant) -> None: account = param.get_string() open_window('ProfileWindow', account=account)
-def on_send_server_message(_action, param): +def on_send_server_message(_action: Gio.SimpleAction, + param: GLib.Variant) -> None: account = param.get_string() server = app.settings.get_account_setting(account, 'hostname') server += '/announce/online' open_window('SingleMessageWindow', account=account, recipients=server)
-def on_service_disco(_action, param): +def on_service_disco(_action: Gio.SimpleAction, + param: GLib.Variant) -> None: account = param.get_string() server_jid = app.settings.get_account_setting(account, 'hostname') if server_jid in interface.instances[account]['disco']: @@ -107,114 +124,135 @@ def on_service_disco(_action, param): pass
-def on_create_gc(_action, param): +def on_create_gc(_action: Gio.SimpleAction, + param: GLib.Variant) -> None: account = param.get_string() open_window('CreateGroupchatWindow', account=account or None)
-def on_add_contact(_action, param): +def on_add_contact(_action: Gio.SimpleAction, + param: GLib.Variant) -> None: account, jid = param.get_strv() if jid is not None: jid = JID.from_string(jid) open_window('AddContact', account=account or None, jid=jid or None)
-def on_single_message(_action, param): +def on_single_message(_action: Gio.SimpleAction, + param: GLib.Variant) -> None: account = param.get_string() open_window('SingleMessageWindow', account=account)
-def on_add_account(_action, _param): +def on_add_account(_action: Gio.SimpleAction, + _param: Optional[GLib.Variant]) -> None: open_window('AccountWizard')
-def on_import_contacts(_action, param): +def on_import_contacts(_action: Gio.SimpleAction, + param: Optional[GLib.Variant]) -> None: open_window('SynchronizeAccounts', account=param.get_string())
# Advanced Actions
-def on_pep_config(_action, param): +def on_pep_config(_action: Gio.SimpleAction, + param: GLib.Variant) -> None: account = param.get_string() open_window('PEPConfig', account=account)
-def on_mam_preferences(_action, param): +def on_mam_preferences(_action: Gio.SimpleAction, + param: GLib.Variant) -> None: account = param.get_string() open_window('MamPreferences', account=account)
-def on_blocking_list(_action, param): +def on_blocking_list(_action: Gio.SimpleAction, + param: GLib.Variant) -> None: account = param.get_string() open_window('BlockingList', account=account)
-def on_history_sync(_action, param): +def on_history_sync(_action: Gio.SimpleAction, + param: GLib.Variant) -> None: account = param.get_string() open_window('HistorySyncAssistant', account=account)
-def on_server_info(_action, param): +def on_server_info(_action: Gio.SimpleAction, + param: GLib.Variant) -> None: account = param.get_string() open_window('ServerInfo', account=account)
-def on_xml_console(_action, _param): +def on_xml_console(_action: Gio.SimpleAction, + _param: Optional[GLib.Variant]) -> None: open_window('XMLConsoleWindow')
-def on_manage_proxies(_action, _param): +def on_manage_proxies(_action: Gio.SimpleAction, + _param: Optional[GLib.Variant]) -> None: open_window('ManageProxies')
# Admin Actions
-def on_set_motd(_action, param): +def on_set_motd(_action: Gio.SimpleAction, + param: GLib.Variant) -> None: account = param.get_string() server = app.settings.get_account_setting(account, 'hostname') server += '/announce/motd' open_window('SingleMessageWindow', account=account, recipients=server)
-def on_update_motd(_action, param): +def on_update_motd(_action: Gio.SimpleAction, + param: GLib.Variant) -> None: account = param.get_string() server = app.settings.get_account_setting(account, 'hostname') server += '/announce/motd/update' open_window('SingleMessageWindow', account=account, recipients=server)
-def on_delete_motd(_action, param): +def on_delete_motd(_action: Gio.SimpleAction, + param: GLib.Variant) -> None: account = param.get_string() app.connections[account].get_module('Announce').delete_motd()
# Help Actions
-def on_contents(_action, _param): +def on_contents(_action: Gio.SimpleAction, + _param: Optional[GLib.Variant]) -> None: helpers.open_uri('https://dev.gajim.org/gajim/gajim/wikis')
-def on_faq(_action, _param): +def on_faq(_action: Gio.SimpleAction, + _param: Optional[GLib.Variant]) -> None: helpers.open_uri('https://dev.gajim.org/gajim/gajim/wikis/help/gajimfaq')
-def on_keyboard_shortcuts(_action, _param): +def on_keyboard_shortcuts(_action: Gio.SimpleAction, + _param: Optional[GLib.Variant]) -> None: ShortcutsWindow()
-def on_features(_action, _param): +def on_features(_action: Gio.SimpleAction, + _param: Optional[GLib.Variant]) -> None: open_window('Features')
-def on_about(_action, _param): +def on_about(_action: Gio.SimpleAction, + _param: Optional[GLib.Variant]) -> None: AboutDialog()
# View Actions
-def on_file_transfers(_action, _param): +def on_file_transfers(_action: Gio.SimpleAction, + _param: Optional[GLib.Variant]) -> None: if interface.instances['file_transfers']. \ window.get_property('visible'): interface.instances['file_transfers'].window.present() @@ -222,13 +260,15 @@ def on_file_transfers(_action, _param): interface.instances['file_transfers'].window.show_all()
-def on_open_event(_action, param): +def on_open_event(_action: Gio.SimpleAction, + param: GLib.Variant) -> None: dict_ = param.unpack() app.interface.handle_event( dict_['account'], dict_['jid'], dict_['notif_detail'])
-def on_mark_as_read(_action, param): +def on_mark_as_read(_action: Gio.SimpleAction, + param: GLib.Variant) -> None: dict_ = param.unpack() account, jid = dict_['account'], dict_['jid'] app.window.mark_as_read(account, JID.from_string(jid)) @@ -236,7 +276,8 @@ def on_mark_as_read(_action, param): # Other Actions
-def toggle_ipython(_action, _param): +def toggle_ipython(_action: Gio.SimpleAction, + _param: Optional[GLib.Variant]) -> None: """ Show/hide the ipython window """ @@ -247,29 +288,34 @@ def toggle_ipython(_action, _param): app.interface.create_ipython_window()
-def open_mail(_action, param): +def open_mail(_action: Gio.SimpleAction, + param: GLib.Variant) -> None: uri = param.get_string() if not uri.startswith('mailto:'): uri = 'mailto:%s' % uri helpers.open_uri(uri)
-def open_link(_action, param): +def open_link(_action: Gio.SimpleAction, + param: GLib.Variant) -> None: account, uri = param.get_strv() helpers.open_uri(uri, account=account)
-def copy_text(_action, param): +def copy_text(_action: Gio.SimpleAction, + param: GLib.Variant) -> None: text = param.get_string() clip = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) clip.set_text(text, -1)
-def start_chat(_action, param): +def start_chat(_action: Gio.SimpleAction, + param: GLib.Variant) -> None: account, jid = param.get_strv() app.interface.start_chat_from_jid(account, jid)
-def forget_groupchat(_action, param): +def forget_groupchat(_action: Gio.SimpleAction, + param: GLib.Variant) -> None: account, jid = param.unpack() room_jid = JID.from_string(jid) window = get_app_window('StartChatDialog') @@ -282,6 +328,7 @@ def forget_groupchat(_action, param): app.storage.archive.remove_chat_history( account, str(room_jid), groupchat=True)
-def on_groupchat_join(_action, param): +def on_groupchat_join(_action: Gio.SimpleAction, + param: GLib.Variant) -> None: account, jid = param.get_strv() open_window('GroupchatJoin', account=account, jid=jid)
===================================== gajim/application.py ===================================== @@ -33,6 +33,16 @@ # You should have received a copy of the GNU General Public License # along with Gajim. If not, see http://www.gnu.org/licenses/.
+from __future__ import annotations + +from typing import Any +from typing import Callable +from typing import Optional +from typing import TextIO +from typing import Type +from typing import Union +from typing import cast + import os import sys from urllib.parse import unquote @@ -49,7 +59,9 @@ from gajim.common import ged from gajim.common import configpaths from gajim.common import logging_helpers +from gajim.common.client import Client from gajim.common.i18n import _ +from gajim.common.nec import NetworkEvent from gajim.common.task_manager import TaskManager from gajim.common.storage.cache import CacheStorage from gajim.common.storage.archive import MessageArchiveStorage @@ -58,6 +70,11 @@ from gajim.common.helpers import load_json
+ActionListT = list[tuple[str, + Optional[str], + Callable[[Gio.SimpleAction, GLib.Variant], Any]]] + + class GajimApplication(Gtk.Application): '''Main class handling activation and command line.'''
@@ -175,7 +192,7 @@ def _get_remaining_entry(): option.short_name = 0 return [option]
- def _startup(self, _application): + def _startup(self, _application: GajimApplication) -> None: # Create and initialize Application Paths & Databases app.print_version() app.detect_dependencies() @@ -203,8 +220,7 @@ def _startup(self, _application): app.app = self from gajim.gui.builder import get_builder builder = get_builder('application_menu.ui') - menubar = builder.get_object("menubar") - self.set_menubar(menubar) + self.set_menubar(cast(Gio.Menu, builder.get_object('menubar')))
from gajim.gui_interface import Interface self.interface = Interface() @@ -219,7 +235,7 @@ def _startup(self, _application): ged.CORE, self._on_feature_discovered)
- def _open_uris(self, uris): + def _open_uris(self, uris: list[str]) -> None: accounts = list(app.connections.keys()) if not accounts: return @@ -272,14 +288,16 @@ def _open_uris(self, uris):
app.interface.start_chat_from_jid(accounts[0], jid, message)
- def do_shutdown(self, *args): + def do_shutdown(self, *args: Any) -> None: Gtk.Application.do_shutdown(self)
# Commit any outstanding SQL transactions app.storage.cache.shutdown() app.storage.archive.shutdown()
- def _command_line(self, _application, command_line): + def _command_line(self, + _application: GajimApplication, + command_line: Gio.ApplicationCommandLine) -> int: options = command_line.get_options_dict()
remote_commands = [ @@ -308,18 +326,23 @@ def _handle_local_options(self, if options.contains('version'): print(gajim.__version__) return 0 - if options.contains('profile'): + + profile = options.lookup_value('profile') + if profile is not None: # Incorporate profile name into application id # to have a single app instance for each profile. - profile = options.lookup_value('profile').get_string() + profile = profile.get_string() app_id = '%s.%s' % (self.get_application_id(), profile) self.set_application_id(app_id) configpaths.set_profile(profile) + if options.contains('separate'): configpaths.set_separation(True) - if options.contains('config-path'): - path = options.lookup_value('config-path').get_string() - configpaths.set_config_root(path) + + config_path = options.lookup_value('config-path') + if config_path is not None: + config_path = config_path.get_string() + configpaths.set_config_root(config_path)
configpaths.init()
@@ -330,23 +353,32 @@ def _handle_local_options(self,
if options.contains('quiet'): logging_helpers.set_quiet() + if options.contains('verbose'): logging_helpers.set_verbose() - if options.contains('loglevel'): - loglevel = options.lookup_value('loglevel').get_string() + + loglevel = options.lookup_value('loglevel') + if loglevel is not None: + loglevel = loglevel.get_string() logging_helpers.set_loglevels(loglevel) + if options.contains('warnings'): self.show_warnings()
return -1
@staticmethod - def show_warnings(): + def show_warnings() -> None: import traceback import warnings
- def warn_with_traceback(message, category, filename, lineno, - _file=None, line=None): + def warn_with_traceback(message: Union[Warning, str], + category: Type[Warning], + filename: str, + lineno: int, + _file: Optional[TextIO] = None, + line: Optional[str] = None) -> None: + traceback.print_stack(file=sys.stderr) sys.stderr.write(warnings.formatwarning(message, category, filename, lineno, line)) @@ -354,27 +386,27 @@ def warn_with_traceback(message, category, filename, lineno, warnings.showwarning = warn_with_traceback warnings.filterwarnings(action="always")
- def add_actions(self): + def add_actions(self) -> None: ''' Build Application Actions ''' from gajim import app_actions
# General Stateful Actions - actions = [ - ('quit', app_actions.on_quit), - ('add-account', app_actions.on_add_account), - ('add-contact', app_actions.on_add_contact), - ('manage-proxies', app_actions.on_manage_proxies), - ('history-manager', app_actions.on_history_manager), - ('preferences', app_actions.on_preferences), - ('plugins', app_actions.on_plugins), - ('xml-console', app_actions.on_xml_console), - ('file-transfer', app_actions.on_file_transfers), - ('shortcuts', app_actions.on_keyboard_shortcuts), - ('features', app_actions.on_features), - ('content', app_actions.on_contents), - ('about', app_actions.on_about), - ('faq', app_actions.on_faq), - ('ipython', app_actions.toggle_ipython), + actions: ActionListT = [ + ('quit', None, app_actions.on_quit), + ('add-account', None, app_actions.on_add_account), + ('add-contact', None, app_actions.on_add_contact), + ('manage-proxies', None, app_actions.on_manage_proxies), + ('history-manager', None, app_actions.on_history_manager), + ('preferences', None, app_actions.on_preferences), + ('plugins', None, app_actions.on_plugins), + ('xml-console', None, app_actions.on_xml_console), + ('file-transfer', None, app_actions.on_file_transfers), + ('shortcuts', None, app_actions.on_keyboard_shortcuts), + ('features', None, app_actions.on_features), + ('content', None, app_actions.on_contents), + ('about', None, app_actions.on_about), + ('faq', None, app_actions.on_faq), + ('ipython', None, app_actions.toggle_ipython), ('start-chat', 's', app_actions.on_new_chat), ('accounts', 's', app_actions.on_accounts), ('add-contact', 's', app_actions.on_add_contact_jid), @@ -387,11 +419,8 @@ def add_actions(self): ]
for action in actions: - if len(action) == 2: - action_name, func = action - variant = None - else: - action_name, variant, func = action # pylint: disable=unbalanced-tuple-unpacking + action_name, variant, func = action + if variant is not None: variant = GLib.VariantType.new(variant)
act = Gio.SimpleAction.new(action_name, variant) @@ -408,7 +437,7 @@ def add_actions(self): self.add_account_actions(accounts_list[0])
@staticmethod - def _get_account_actions(account): + def _get_account_actions(account: str) -> list[tuple[str, Any, str, str]]: from gajim import app_actions as a
if account == 'Local': @@ -435,11 +464,11 @@ def _get_account_actions(account): ('-import-contacts', a.on_import_contacts, 'online', 's'), ]
- def add_account_actions(self, account): + def add_account_actions(self, account: str) -> None: for action in self._get_account_actions(account): action_name, func, state, type_ = action action_name = account + action_name - if self.lookup_action(action_name): + if self.lookup_action(action_name) is not None: # We already added this action continue act = Gio.SimpleAction.new( @@ -449,29 +478,37 @@ def add_account_actions(self, account): act.set_enabled(False) self.add_action(act)
- def remove_account_actions(self, account): + def remove_account_actions(self, account: str) -> None: for action in self._get_account_actions(account): action_name = account + action[0] self.remove_action(action_name)
- def set_account_actions_state(self, account, new_state=False): + def set_action_state(self, action_name: str, state: bool) -> None: + action = self.lookup_action(action_name) + if action is None: + raise ValueError('Action %s does not exist' % action_name) + action.set_enabled(state) + + def set_account_actions_state(self, + account: str, + new_state: bool = False) -> None: for action in self._get_account_actions(account): action_name, _, state, _ = action if not new_state and state in ('online', 'feature'): # We go offline - self.lookup_action(account + action_name).set_enabled(False) + self.set_action_state(account + action_name, False) elif new_state and state == 'online': # We go online - self.lookup_action(account + action_name).set_enabled(True) + self.set_action_state(account + action_name, True)
- def update_app_actions_state(self): + def update_app_actions_state(self) -> None: active_accounts = bool(app.get_connected_accounts(exclude_local=True)) - self.lookup_action('create-groupchat').set_enabled(active_accounts) + self.set_action_state('create-groupchat', active_accounts)
- enabled_accounts = app.settings.get_active_accounts() - self.lookup_action('start-chat').set_enabled(enabled_accounts) + enabled_accounts = bool(app.settings.get_active_accounts()) + self.set_action_state('start-chat', enabled_accounts)
- def _load_shortcuts(self): + def _load_shortcuts(self) -> None: default_path = configpaths.get('DATA') / 'other' / 'shortcuts.json' shortcuts = load_json(default_path) assert shortcuts is not None @@ -487,16 +524,16 @@ def _load_shortcuts(self): for action, accels in shortcuts.items(): self.set_accels_for_action(action, accels)
- def _on_feature_discovered(self, event): + def _on_feature_discovered(self, event: NetworkEvent) -> None: if event.feature == Namespace.MAM_2: action = '%s-archive' % event.account - self.lookup_action(action).set_enabled(True) + self.set_action_state(action, True) elif event.feature == Namespace.BLOCKING: action = '%s-blocking' % event.account - self.lookup_action(action).set_enabled(True) + self.set_action_state(action, True)
- def start_shutdown(self, *args, **kwargs): - accounts_to_disconnect = {} + def start_shutdown(self, *args: Any, **kwargs: Any) -> None: + accounts_to_disconnect: dict[str, Client] = {}
for account, client in app.connections.items(): if app.account_is_available(account): @@ -506,7 +543,7 @@ def start_shutdown(self, *args, **kwargs): self.quit() return
- def _on_disconnect(event): + def _on_disconnect(event: NetworkEvent) -> None: accounts_to_disconnect.pop(event.account) if not accounts_to_disconnect: self.quit()
===================================== gajim/common/modules/contacts.py ===================================== @@ -18,6 +18,7 @@ from typing import Iterator from typing import Optional from typing import Union +from typing import overload
import logging from functools import partial @@ -31,6 +32,9 @@ from gajim.common import types from gajim.common.const import PresenceShowExt from gajim.common.const import SimpleClientState +from gajim.common.setting_values import BoolGroupChatSettings +from gajim.common.setting_values import IntGroupChatSettings +from gajim.common.setting_values import StringGroupChatSettings from gajim.common.structs import UNKNOWN_PRESENCE from gajim.common.structs import PresenceData from gajim.common.structs import UNKNOWN_MUC_PRESENCE @@ -41,15 +45,35 @@
class ContactSettings: - def __init__(self, account, jid): + def __init__(self, account: str, jid: str) -> None: self.get = partial(app.settings.get_contact_setting, account, jid) self.set = partial(app.settings.set_contact_setting, account, jid)
class GroupChatSettings: - def __init__(self, account, jid): - self.get = partial(app.settings.get_group_chat_setting, account, jid) - self.set = partial(app.settings.set_group_chat_setting, account, jid) + def __init__(self, account: str, jid: str) -> None: + self._account = account + self._jid = jid + + @overload + def get(self, setting: StringGroupChatSettings) -> str: ... + @overload + def get(self, setting: BoolGroupChatSettings) -> bool: ... + @overload + def get(self, setting: IntGroupChatSettings) -> int: ... + def get(self, setting: Any) -> Any: + return app.settings.get_group_chat_setting( + self._account, self._jid, setting) + + @overload + def set(self, setting: StringGroupChatSettings, value: str) -> None: ... + @overload + def set(self, setting: BoolGroupChatSettings, value: bool) -> None: ... + @overload + def set(self, setting: IntGroupChatSettings, value: int) -> None: ... + def set(self, setting: Any, value: Any) -> None: + app.settings.set_group_chat_setting( + self._account, str(self._jid), setting, value)
class Contacts(BaseModule):
===================================== gajim/common/setting_values.py ===================================== @@ -361,6 +361,31 @@ class _ACCOUNT_DEFAULT: IntAccountSettings, StringAccountSettings]
+ +BoolGroupChatSettings = Literal[ + 'notify_on_all_messages', + 'print_status', + 'print_join_left', + 'send_chatstate', + 'send_marker', +] + +StringGroupChatSettings = Literal[ + 'speller_language', + 'encryption', +] + +IntGroupChatSettings = Literal[ + 'sync_threshold', +] + + +AllGroupChatSettings = Literal[BoolGroupChatSettings, + IntGroupChatSettings, + StringGroupChatSettings] + +AllGroupChatSettingsT = Union[str, int, bool] + ACCOUNT_SETTINGS = { 'account': { 'name': '',
===================================== gajim/common/settings.py ===================================== @@ -12,12 +12,16 @@ # 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 Any, Literal +from typing import Any +from typing import Callable +from typing import Literal +from typing import NamedTuple from typing import Optional from typing import Dict from typing import List from typing import Union from typing import overload +from typing import cast
import sys import json @@ -41,6 +45,11 @@ from gajim.common.storage.base import Encoder from gajim.common.storage.base import json_decoder from gajim.common.setting_values import APP_SETTINGS +from gajim.common.setting_values import AllGroupChatSettings +from gajim.common.setting_values import AllGroupChatSettingsT +from gajim.common.setting_values import BoolGroupChatSettings +from gajim.common.setting_values import IntGroupChatSettings +from gajim.common.setting_values import StringGroupChatSettings from gajim.common.setting_values import AllWorkspaceSettings from gajim.common.setting_values import AllWorkspaceSettingsT from gajim.common.setting_values import OpenChatSettingT @@ -93,27 +102,35 @@ json.dumps(INITAL_WORKSPACE))
+_SignalCallable = Callable[[Any, str, Optional[str], Optional[JID]], Any] +_CallbackDict = dict[tuple[str, Optional[str], Optional[JID]], + list[weakref.WeakMethod[_SignalCallable]]] + class Settings: def __init__(self): - self._con = None + self._con = cast(sqlite3.Connection, None) self._commit_scheduled = None
self._settings = {} self._account_settings = {}
- self._callbacks = defaultdict(list) + self._callbacks: _CallbackDict = defaultdict(list)
- def connect_signal(self, setting, func, account=None, jid=None): + def connect_signal(self, + setting: str, + func: _SignalCallable, + account: Optional[str] = None, + jid: Optional[JID] = None) -> None: if not inspect.ismethod(func): # static methods are not bound to an object so we can’t easily # remove the func once it should not be called anymore raise ValueError('Only bound methods can be connected')
- func = weakref.WeakMethod(func) - self._callbacks[(setting, account, jid)].append(func) + weak_func = weakref.WeakMethod(func) + self._callbacks[(setting, account, jid)].append(weak_func)
- def disconnect_signals(self, object_): + def disconnect_signals(self, object_: object) -> Any: for _, handlers in self._callbacks.items(): for handler in list(handlers): if isinstance(handler, tuple): @@ -140,7 +157,12 @@ def _on_destroy(*args):
widget.connect('destroy', _on_destroy)
- def _notify(self, value, setting, account=None, jid=None): + def _notify(self, + value: Any, + setting: str, + account: Optional[str] = None, + jid: Optional[JID] = None) -> None: + log.info('Signal: %s changed', setting)
callbacks = self._callbacks[(setting, account, jid)] @@ -190,9 +212,10 @@ def _setup_installation_defaults() -> None: APP_SETTINGS['use_keyring'] = False
@staticmethod - def _namedtuple_factory(cursor: Any, row: Any) -> Any: + def _namedtuple_factory(cursor: sqlite3.Cursor, + row: tuple[Any, ...]) -> NamedTuple: fields = [col[0] for col in cursor.description] - return namedtuple("Row", fields)(*row) + return namedtuple("Row", fields)(*row) # type: ignore
def _connect_database(self) -> None: path = configpaths.get('SETTINGS') @@ -374,7 +397,8 @@ def _migrate_encryption_settings(self) -> None: jid]['encryption'] = encryption self._commit_account_settings(account)
- def _split_encryption_config_key(self, key: str) -> Any: + def _split_encryption_config_key(self, key: str) -> tuple[Optional[str], + Optional[str]]: for account in self._account_settings: if not key.startswith(account): continue @@ -448,7 +472,7 @@ def close(self) -> None: log.info('Close settings') self._con.commit() self._con.close() - self._con = None + self._con = cast(sqlite3.Connection, None)
def _load_settings(self) -> None: settings = self._con.execute('SELECT * FROM settings').fetchall() @@ -678,10 +702,32 @@ def set_account_setting(self, self._commit_account_settings(account) self._notify(value, setting, account)
+ @overload + def get_group_chat_setting(self, + account: str, + jid: Union[str, JID], + setting: IntGroupChatSettings + ) -> int: ... + + @overload + def get_group_chat_setting(self, + account: str, + jid: Union[str, JID], + setting: BoolGroupChatSettings + ) -> bool: ... + + @overload + def get_group_chat_setting(self, + account: str, + jid: Union[str, JID], + setting: StringGroupChatSettings + ) -> str: ... + def get_group_chat_setting(self, account: str, jid: Union[str, JID], - setting: str) -> SETTING_TYPE: + setting: AllGroupChatSettings + ) -> AllGroupChatSettingsT:
if account not in self._account_settings: raise ValueError(f'Account missing: {account}') @@ -717,11 +763,32 @@ def get_group_chat_setting(self,
return default
+ @overload def set_group_chat_setting(self, account: str, jid: str, - setting: str, - value: SETTING_TYPE) -> None: + setting: StringGroupChatSettings, + value: str) -> None: ... + + @overload + def set_group_chat_setting(self, + account: str, + jid: str, + setting: IntGroupChatSettings, + value: int) -> None: ... + + @overload + def set_group_chat_setting(self, + account: str, + jid: str, + setting: BoolGroupChatSettings, + value: bool) -> None: ... + + def set_group_chat_setting(self, + account: str, + jid: str, + setting: AllGroupChatSettings, + value: AllGroupChatSettingsT) -> None:
if account not in self._account_settings: raise ValueError(f'Account missing: {account}')
===================================== gajim/gtk/conversation/view.py ===================================== @@ -14,10 +14,11 @@
from __future__ import annotations
-from typing import Dict -from typing import Set +from typing import Any from typing import Optional +from typing import Union from typing import Generator +from typing import cast
import logging import time @@ -38,6 +39,8 @@ from gajim.common.helpers import to_user_string from gajim.common.helpers import get_start_of_day from gajim.common.modules.contacts import GroupchatParticipant +from gajim.common.modules.contacts import GroupchatContact +from gajim.common.modules.contacts import BareContact from gajim.common.modules.httpupload import HTTPFileTransfer
from .rows.base import BaseRow @@ -58,6 +61,8 @@
log = logging.getLogger('gajim.gui.conversation_view')
+ContactT = Union[BareContact, GroupchatContact, GroupchatParticipant] +
class ConversationView(Gtk.ListBox):
@@ -84,7 +89,7 @@ class ConversationView(Gtk.ListBox): ), }
- def __init__(self, account, contact): + def __init__(self, account: str, contact: ContactT) -> None: Gtk.ListBox.__init__(self) self.set_selection_mode(Gtk.SelectionMode.NONE) self.set_sort_func(self._sort_func) @@ -104,10 +109,10 @@ def __init__(self, account, contact): self._max_row_count: int = 100
# Keeps track of date rows we have added to the list - self._active_date_rows: Set[datetime] = set() + self._active_date_rows: set[datetime] = set()
# message_id -> row mapping - self._message_id_row_map: Dict[str, MessageRow] = {} + self._message_id_row_map: dict[str, MessageRow] = {}
app.settings.connect_signal('print_join_left', self._on_contact_setting_changed, @@ -127,7 +132,10 @@ def __init__(self, account, contact): self.add(self._scroll_hint_row) self.connect('destroy', self._on_destroy)
- def _on_destroy(self, *args): + def get_row_at_index(self, index: int) -> BaseRow: + return cast(BaseRow, Gtk.ListBox.get_row_at_index(self, index)) + + def _on_destroy(self, *args: Any) -> None: self.set_filter_func(None) app.check_finalize(self)
@@ -169,12 +177,15 @@ def set_history_complete(self, complete: bool) -> None: self._scroll_hint_row.set_history_complete(complete)
@staticmethod - def _sort_func(row1, row2): + def _sort_func(row1: BaseRow, row2: BaseRow) -> int: if row1.timestamp == row2.timestamp: return 0 return -1 if row1.timestamp < row2.timestamp else 1
- def _filter_func(self, row): + def _filter_func(self, row: BaseRow) -> bool: + if not isinstance(self._contact, GroupchatContact): + return True + if row.type in ('muc-user-joined', 'muc-user-left'): return self._contact.settings.get('print_join_left') if row.type == 'muc-user-status': @@ -186,8 +197,11 @@ def add_muc_subject(self, text: str, nick: str, date: str) -> None: subject = MUCSubject(self._account, text, nick, date) self._insert_message(subject)
- def add_muc_user_left(self, nick: str, reason: str, + def add_muc_user_left(self, + nick: str, + reason: str, error: bool = False) -> None: + join_left = MUCJoinLeft('muc-user-left', self._account, nick, @@ -273,7 +287,7 @@ def add_message(self,
self._insert_message(message)
- def _insert_message(self, message: MessageRow) -> None: + def _insert_message(self, message: BaseRow) -> None: self.add(message) self._add_date_row(message.timestamp) self._check_for_merge(message) @@ -337,7 +351,7 @@ def _find_ancestor(self, message: MessageRow) -> Optional[MessageRow]: return row return None
- def _update_descendants(self, message: MessageRow) -> None: + def _update_descendants(self, message: BaseRow) -> None: index = message.get_index() while True: index += 1 @@ -508,7 +522,7 @@ def accept_call(self, event): def decline_call(self, event): self.emit('decline-call', event)
- def _on_contact_setting_changed(self, *args): + def _on_contact_setting_changed(self, *args: Any) -> None: self.invalidate_filter()
def remove(self, widget: Gtk.Widget) -> None:
===================================== gajim/gui_interface.py ===================================== @@ -31,6 +31,8 @@ # You should have received a copy of the GNU General Public License # along with Gajim. If not, see http://www.gnu.org/licenses/.
+from __future__ import annotations + from typing import Dict from typing import Optional from typing import Union @@ -1111,7 +1113,7 @@ def _on_update_checked(self, _session, message): else: log.info('Gajim is up to date')
- def run(self, _application): + def run(self, _application: Gtk.Application) -> None: if app.settings.get('trayicon') != 'never': self.show_systray()
View it on GitLab: https://dev.gajim.org/gajim/gajim/-/compare/4ec3783e175777df23e5fe387ad9ecbf...