Daniel Brötzmann pushed to branch master at gajim / gajim

Commits:

8 changed files:

Changes:

  • gajim/common/app.py
    ... ... @@ -75,7 +75,7 @@
    75 75
     window = None # type: MainWindow
    
    76 76
     
    
    77 77
     ged = ged_module.GlobalEventsDispatcher() # Global Events Dispatcher
    
    78
    -plugin_manager = None # Plugins Manager
    
    78
    +plugin_manager = cast(types.PluginManagerT, None)  # Plugins Manager
    
    79 79
     
    
    80 80
     class Storage:
    
    81 81
         def __init__(self):
    

  • gajim/common/types.py
    ... ... @@ -45,6 +45,8 @@
    45 45
         from gajim.common.settings import Settings
    
    46 46
         from gajim.gtk.css_config import CSSConfig
    
    47 47
     
    
    48
    +    from gajim.plugins.pluginmanager import PluginManager
    
    49
    +
    
    48 50
         ContactT = Union[BareContact,
    
    49 51
                          ResourceContact,
    
    50 52
                          GroupchatContact,
    
    ... ... @@ -52,6 +54,7 @@
    52 54
     
    
    53 55
     
    
    54 56
     InterfaceT = Union['Interface']
    
    57
    +PluginManagerT = Union['PluginManager']
    
    55 58
     
    
    56 59
     ConnectionT = Union['Client']
    
    57 60
     CSSConfigT = Union['CSSConfig']
    

  • gajim/gtk/menus.py
    ... ... @@ -206,10 +206,12 @@ def get_account_menu(account: str) -> Gio.Menu:
    206 206
     
    
    207 207
     def build_accounts_menu() -> None:
    
    208 208
         menubar = app.app.get_menubar()
    
    209
    +    assert isinstance(menubar, Gio.Menu)
    
    209 210
         # Accounts Submenu
    
    210 211
         menu_position = 1
    
    211 212
     
    
    212 213
         acc_menu = menubar.get_item_link(menu_position, 'submenu')
    
    214
    +    assert isinstance(acc_menu, Gio.Menu)
    
    213 215
         acc_menu.remove_all()
    
    214 216
     
    
    215 217
         accounts_list = sorted(app.settings.get_active_accounts())
    
    ... ... @@ -261,7 +263,9 @@ def get_encryption_menu(control_id: str,
    261 263
         return menu
    
    262 264
     
    
    263 265
     
    
    264
    -def get_conv_action_context_menu(account: str, selected_text: str) -> Gtk.MenuItem:
    
    266
    +def get_conv_action_context_menu(account: str,
    
    267
    +                                 selected_text: str
    
    268
    +                                 ) -> Gtk.MenuItem:
    
    265 269
         selected_text_short = reduce_chars_newlines(selected_text, 10, 1)
    
    266 270
     
    
    267 271
         action_menu_item = Gtk.MenuItem.new_with_mnemonic(
    

  • gajim/gtk/preview.py
    ... ... @@ -198,8 +198,21 @@ def _on_open(self, _menu: Gtk.Menu) -> None:
    198 198
             open_file(self._preview.orig_path)
    
    199 199
     
    
    200 200
         def _on_save_as(self, _menu: Gtk.Menu) -> None:
    
    201
    -        def _on_ok(target_path):
    
    202
    -            dirname = Path(target_path).parent
    
    201
    +        assert self._preview is not None
    
    202
    +
    
    203
    +        def _on_ok(target: str) -> None:
    
    204
    +            assert self._preview is not None
    
    205
    +            assert self._preview.orig_path is not None
    
    206
    +
    
    207
    +            target_path = Path(target)
    
    208
    +            orig_ext = self._preview.orig_path.suffix
    
    209
    +            new_ext = target_path.suffix
    
    210
    +            if orig_ext != new_ext:
    
    211
    +                # Windows file chooser selects the full file name including
    
    212
    +                # extension. Starting to type will overwrite the extension
    
    213
    +                # as well. Restore the extension if it's lost.
    
    214
    +                target_path = target_path.with_suffix(orig_ext)
    
    215
    +            dirname = target_path.parent
    
    203 216
                 if not os.access(dirname, os.W_OK):
    
    204 217
                     ErrorDialog(
    
    205 218
                         _('Directory \'%s\' is not writable') % dirname,
    

  • gajim/plugins/gajimplugin.py
    ... ... @@ -21,18 +21,18 @@
    21 21
     :license: GPL
    
    22 22
     '''
    
    23 23
     
    
    24
    -from typing import List  # pylint: disable=W0611
    
    25
    -from typing import Tuple  # pylint: disable=W0611
    
    26
    -from typing import Dict  # pylint: disable=W0611
    
    27
    -from typing import Any  # pylint: disable=W0611
    
    24
    +from __future__ import annotations
    
    25
    +
    
    26
    +from typing import Any
    
    28 27
     
    
    29 28
     import os
    
    30
    -import locale
    
    31 29
     import logging
    
    32 30
     import pickle
    
    33 31
     
    
    34 32
     from gajim.common import configpaths
    
    35
    -from gajim.common.types import PluginExtensionPoints  # pylint: disable=W0611
    
    33
    +from gajim.common.events import ApplicationEvent
    
    34
    +from gajim.common.ged import HandlerFuncT
    
    35
    +from gajim.common.types import PluginExtensionPoints
    
    36 36
     
    
    37 37
     from gajim.plugins.helpers import log
    
    38 38
     from gajim.plugins.gui import GajimPluginConfigDialog
    
    ... ... @@ -45,13 +45,15 @@ class GajimPlugin:
    45 45
         '''
    
    46 46
         Base class for implementing Gajim plugins.
    
    47 47
         '''
    
    48
    +    __path__: str = ''
    
    49
    +
    
    50
    +
    
    48 51
         name = ''
    
    49 52
         '''
    
    50 53
         Name of plugin.
    
    51 54
     
    
    52 55
         Will be shown in plugins management GUI.
    
    53 56
     
    
    54
    -    :type: str
    
    55 57
         '''
    
    56 58
         short_name = ''
    
    57 59
         '''
    
    ... ... @@ -59,8 +61,6 @@ class GajimPlugin:
    59 61
     
    
    60 62
         Used for quick identification of plugin.
    
    61 63
     
    
    62
    -    :type: str
    
    63
    -
    
    64 64
         :todo: decide whether we really need this one, because class name (with
    
    65 65
                 module name) can act as such short name
    
    66 66
         '''
    
    ... ... @@ -71,15 +71,11 @@ class GajimPlugin:
    71 71
         The name that Gajim displays in the encryption menu.
    
    72 72
         Leave empty if the plugin is not an encryption plugin.
    
    73 73
     
    
    74
    -    :type: str
    
    75
    -
    
    76 74
         '''
    
    77 75
         version = ''
    
    78 76
         '''
    
    79 77
         Version of plugin.
    
    80 78
     
    
    81
    -    :type: str
    
    82
    -
    
    83 79
         :todo: decide how to compare version between each other (which one
    
    84 80
                 is higher). Also rethink: do we really need to compare versions
    
    85 81
                 of plugins between each other? This would be only useful if we detect
    
    ... ... @@ -90,16 +86,12 @@ class GajimPlugin:
    90 86
         '''
    
    91 87
         Plugin description.
    
    92 88
     
    
    93
    -    :type: str
    
    94
    -
    
    95 89
         :todo: should be allow rich text here (like HTML or reStructuredText)?
    
    96 90
         '''
    
    97
    -    authors = []  # type: List[str]
    
    91
    +    authors = ''
    
    98 92
         '''
    
    99 93
         Plugin authors.
    
    100 94
     
    
    101
    -    :type: [] of str
    
    102
    -
    
    103 95
         :todo: should we decide on any particular format of author strings?
    
    104 96
                 Especially: should we force format of giving author's e-mail?
    
    105 97
         '''
    
    ... ... @@ -112,7 +104,7 @@ class GajimPlugin:
    112 104
         :todo: should we check whether provided string is valid URI? (Maybe
    
    113 105
         using 'property')
    
    114 106
         '''
    
    115
    -    gui_extension_points = {}  # type: PluginExtensionPoints
    
    107
    +    gui_extension_points: PluginExtensionPoints = {}
    
    116 108
         '''
    
    117 109
         Extension points that plugin wants to connect with and handlers to be used.
    
    118 110
     
    
    ... ... @@ -126,7 +118,7 @@ class GajimPlugin:
    126 118
         or when extpoint is destroyed and plugin is activate (eg. chat window
    
    127 119
         closed).
    
    128 120
         '''
    
    129
    -    config_default_values = {}  # type: Dict[str, Tuple[Any, str]]
    
    121
    +    config_default_values: dict[str, tuple[Any, str]] = {}
    
    130 122
         '''
    
    131 123
         Default values for keys that should be stored in plug-in config.
    
    132 124
     
    
    ... ... @@ -140,7 +132,7 @@ class GajimPlugin:
    140 132
     
    
    141 133
         :type: {} of 2-element tuples
    
    142 134
         '''
    
    143
    -    events_handlers = {}
    
    135
    +    events_handlers: dict[str, tuple[int, HandlerFuncT]] = {}
    
    144 136
         '''
    
    145 137
         Dictionary with events handlers.
    
    146 138
     
    
    ... ... @@ -151,12 +143,10 @@ class GajimPlugin:
    151 143
     
    
    152 144
         :type: {} with 2-element tuples
    
    153 145
         '''
    
    154
    -    events = []
    
    146
    +    events: list[ApplicationEvent] = []
    
    155 147
         '''
    
    156 148
         New network event classes to be registered in Network Events Controller.
    
    157 149
     
    
    158
    -    :type: [] of `nec.NetworkIncomingEvent` or `nec.NetworkOutgoingEvent`
    
    159
    -    subclasses.
    
    160 150
         '''
    
    161 151
     
    
    162 152
         def __init__(self) -> None:
    
    ... ... @@ -169,6 +159,7 @@ def __init__(self) -> None:
    169 159
             :type: `plugins.plugin.GajimPluginConfig`
    
    170 160
             '''
    
    171 161
             self.activatable = True
    
    162
    +        self.active = False
    
    172 163
             self.available_text = ''
    
    173 164
             self.load_config()
    
    174 165
             self.config_dialog = GajimPluginConfigDialog(self)
    
    ... ... @@ -180,59 +171,59 @@ def save_config(self) -> None:
    180 171
         def load_config(self) -> None:
    
    181 172
             self.config.load()
    
    182 173
     
    
    183
    -    def __eq__(self, plugin):
    
    174
    +    def __eq__(self, plugin: object) -> bool:
    
    184 175
             if self.short_name == plugin.short_name:
    
    185 176
                 return True
    
    186 177
     
    
    187 178
             return False
    
    188 179
     
    
    189
    -    def __ne__(self, plugin):
    
    180
    +    def __ne__(self, plugin: object) -> bool:
    
    190 181
             if self.short_name != plugin.short_name:
    
    191 182
                 return True
    
    192 183
     
    
    193 184
             return False
    
    194 185
     
    
    195
    -    def local_file_path(self, file_name):
    
    186
    +    def local_file_path(self, file_name: str) -> str:
    
    196 187
             return os.path.join(self.__path__, file_name)
    
    197 188
     
    
    198
    -    def init(self):
    
    189
    +    def init(self) -> None:
    
    199 190
             pass
    
    200 191
     
    
    201
    -    def activate(self):
    
    192
    +    def activate(self) -> None:
    
    202 193
             pass
    
    203 194
     
    
    204
    -    def deactivate(self):
    
    195
    +    def deactivate(self) -> None:
    
    205 196
             pass
    
    206 197
     
    
    207 198
     
    
    208
    -class GajimPluginConfig():
    
    209
    -    def __init__(self, plugin):
    
    199
    +class GajimPluginConfig:
    
    200
    +    def __init__(self, plugin: GajimPlugin) -> None:
    
    210 201
             self.plugin = plugin
    
    211 202
             self.FILE_PATH = (configpaths.get('PLUGINS_CONFIG_DIR') /
    
    212 203
                               self.plugin.short_name)
    
    213
    -        self.data = {}
    
    204
    +        self.data: dict[str, Any] = {}
    
    214 205
     
    
    215
    -    def __getitem__(self, key):
    
    206
    +    def __getitem__(self, key: str) -> None:
    
    216 207
             if not key in self.data:
    
    217 208
                 self.data[key] = self.plugin.config_default_values[key][0]
    
    218 209
                 self.save()
    
    219 210
     
    
    220 211
             return self.data[key]
    
    221 212
     
    
    222
    -    def __setitem__(self, key, value):
    
    213
    +    def __setitem__(self, key: str, value: Any) -> None:
    
    223 214
             self.data[key] = value
    
    224 215
             self.save()
    
    225 216
     
    
    226
    -    def __delitem__(self, key):
    
    217
    +    def __delitem__(self, key: str) -> None:
    
    227 218
             del self.data[key]
    
    228 219
             self.save()
    
    229 220
     
    
    230
    -    def __contains__(self, key):
    
    221
    +    def __contains__(self, key: str) -> bool:
    
    231 222
             return key in self.data
    
    232 223
     
    
    233 224
         def __iter__(self):
    
    234
    -        for k in self.data.keys():
    
    235
    -            yield k
    
    225
    +        for key in self.data.keys():
    
    226
    +            yield key
    
    236 227
     
    
    237 228
         def keys(self):
    
    238 229
             return self.data.keys()
    
    ... ... @@ -240,11 +231,11 @@ def keys(self):
    240 231
         def items(self):
    
    241 232
             return self.data.items()
    
    242 233
     
    
    243
    -    def save(self):
    
    234
    +    def save(self) -> None:
    
    244 235
             with open(self.FILE_PATH, 'wb') as fd:
    
    245 236
                 pickle.dump(self.data, fd)
    
    246 237
     
    
    247
    -    def load(self):
    
    238
    +    def load(self) -> None:
    
    248 239
             if not self.FILE_PATH.is_file():
    
    249 240
                 self.data = {}
    
    250 241
                 self.save()
    
    ... ... @@ -255,23 +246,23 @@ def load(self):
    255 246
                 except Exception:
    
    256 247
                     try:
    
    257 248
                         import shelve
    
    258
    -                    s = shelve.open(self.FILE_PATH)
    
    259
    -                    for (k, v) in s.items():
    
    260
    -                        self.data[k] = v
    
    249
    +                    shelf = shelve.open(str(self.FILE_PATH))
    
    250
    +                    for (key, value) in shelf.items():
    
    251
    +                        self.data[key] = value
    
    261 252
                         if not isinstance(self.data, dict):
    
    262 253
                             raise GajimPluginException
    
    263
    -                    s.close()
    
    254
    +                    shelf.close()
    
    264 255
                         self.save()
    
    265 256
                     except Exception:
    
    266
    -                    enc = locale.getpreferredencoding()
    
    267
    -                    filename = self.FILE_PATH.decode(enc) + '.bak'
    
    257
    +                    filepath_bak = self.FILE_PATH.with_suffix('bak')
    
    268 258
                         log.warning(
    
    269 259
                             '%s plugin config file not readable. Saving it as '
    
    270 260
                             '%s and creating a new one',
    
    271
    -                        self.plugin.short_name, filename)
    
    272
    -                    if os.path.exists(self.FILE_PATH + '.bak'):
    
    273
    -                        os.remove(self.FILE_PATH + '.bak')
    
    274
    -                    os.rename(self.FILE_PATH, self.FILE_PATH + '.bak')
    
    261
    +                        self.plugin.short_name, filepath_bak)
    
    262
    +                    if filepath_bak.exists():
    
    263
    +                        filepath_bak.unlink()
    
    264
    +
    
    265
    +                    self.FILE_PATH.rename(f'{self.FILE_PATH}.bak')
    
    275 266
                         self.data = {}
    
    276 267
                         self.save()
    
    277 268
     
    
    ... ... @@ -279,5 +270,6 @@ def load(self):
    279 270
     class GajimPluginException(Exception):
    
    280 271
         pass
    
    281 272
     
    
    273
    +
    
    282 274
     class GajimPluginInitError(GajimPluginException):
    
    283 275
         pass

  • gajim/plugins/gui.py
    ... ... @@ -21,6 +21,12 @@
    21 21
     :license: GPL
    
    22 22
     '''
    
    23 23
     
    
    24
    +from __future__ import annotations
    
    25
    +
    
    26
    +from typing import Any
    
    27
    +from typing import Optional
    
    28
    +from typing import TYPE_CHECKING
    
    29
    +
    
    24 30
     import os
    
    25 31
     from enum import IntEnum
    
    26 32
     from enum import unique
    
    ... ... @@ -31,6 +37,8 @@
    31 37
     
    
    32 38
     from gajim.common import app
    
    33 39
     from gajim.common import ged
    
    40
    +from gajim.common.events import PluginAdded
    
    41
    +from gajim.common.events import PluginRemoved
    
    34 42
     from gajim.common.exceptions import PluginsystemError
    
    35 43
     from gajim.common.helpers import open_uri
    
    36 44
     from gajim.common.ged import EventHelper
    
    ... ... @@ -45,6 +53,9 @@
    45 53
     from gajim.gui.builder import get_builder
    
    46 54
     from gajim.gui.util import load_icon_pixbuf
    
    47 55
     
    
    56
    +if TYPE_CHECKING:
    
    57
    +    from gajim.plugins.gajimplugin import GajimPlugin
    
    58
    +
    
    48 59
     
    
    49 60
     @unique
    
    50 61
     class Column(IntEnum):
    
    ... ... @@ -56,7 +67,7 @@ class Column(IntEnum):
    56 67
     
    
    57 68
     
    
    58 69
     class PluginsWindow(Gtk.ApplicationWindow, EventHelper):
    
    59
    -    def __init__(self):
    
    70
    +    def __init__(self) -> None:
    
    60 71
             Gtk.ApplicationWindow.__init__(self)
    
    61 72
             EventHelper.__init__(self)
    
    62 73
     
    
    ... ... @@ -75,8 +86,13 @@ def __init__(self):
    75 86
                     _('Click to view Gajim\'s wiki page on how to install plugins '
    
    76 87
                       'in Flatpak.'))
    
    77 88
     
    
    78
    -        self.installed_plugins_model = Gtk.ListStore(object, str, bool, bool,
    
    79
    -                                                     GdkPixbuf.Pixbuf)
    
    89
    +        self.installed_plugins_model = Gtk.ListStore(
    
    90
    +            object,
    
    91
    +            str,
    
    92
    +            bool,
    
    93
    +            bool,
    
    94
    +            GdkPixbuf.Pixbuf
    
    95
    +        )
    
    80 96
             self._ui.installed_plugins_treeview.set_model(
    
    81 97
                 self.installed_plugins_model)
    
    82 98
     
    
    ... ... @@ -101,7 +117,7 @@ def __init__(self):
    101 117
             # connect signal for selection change
    
    102 118
             selection = self._ui.installed_plugins_treeview.get_selection()
    
    103 119
             selection.connect(
    
    104
    -            'changed', self._installed_plugins_treeview_selection_changed)
    
    120
    +            'changed', self._selection_changed)
    
    105 121
             selection.set_mode(Gtk.SelectionMode.SINGLE)
    
    106 122
     
    
    107 123
             self._clear_installed_plugin_info()
    
    ... ... @@ -128,19 +144,21 @@ def __init__(self):
    128 144
     
    
    129 145
             self.show_all()
    
    130 146
     
    
    131
    -    def get_notebook(self):
    
    147
    +    def get_notebook(self) -> Gtk.Notebook:
    
    132 148
             # Used by plugins
    
    133 149
             return self._ui.plugins_notebook
    
    134 150
     
    
    135
    -    def _on_key_press(self, _widget, event):
    
    151
    +    def _on_key_press(self, _widget: Gtk.Widget, event: Gdk.EventKey) -> None:
    
    136 152
             if event.keyval == Gdk.KEY_Escape:
    
    137 153
                 self.destroy()
    
    138 154
     
    
    139
    -    def _on_destroy(self, *args):
    
    155
    +    def _on_destroy(self, *args: Any) -> None:
    
    140 156
             self.unregister_events()
    
    141 157
             app.plugin_manager.remove_gui_extension_point('plugin_window', self)
    
    142 158
     
    
    143
    -    def _installed_plugins_treeview_selection_changed(self, treeview_selection):
    
    159
    +    def _selection_changed(self,
    
    160
    +                           treeview_selection: Gtk.TreeSelection
    
    161
    +                           ) -> None:
    
    144 162
             model, iter_ = treeview_selection.get_selected()
    
    145 163
             if iter_:
    
    146 164
                 plugin = model.get_value(iter_, Column.PLUGIN)
    
    ... ... @@ -148,11 +166,11 @@ def _installed_plugins_treeview_selection_changed(self, treeview_selection):
    148 166
             else:
    
    149 167
                 self._clear_installed_plugin_info()
    
    150 168
     
    
    151
    -    def _display_installed_plugin_info(self, plugin):
    
    169
    +    def _display_installed_plugin_info(self, plugin: GajimPlugin) -> None:
    
    152 170
             self._ui.plugin_name_label.set_text(plugin.name)
    
    153 171
             self._ui.plugin_version_label.set_text(plugin.version)
    
    154 172
             self._ui.plugin_authors_label.set_text(plugin.authors)
    
    155
    -        markup = '<a href="%s">%s</a>' % (plugin.homepage, plugin.homepage)
    
    173
    +        markup = f'<a href="{plugin.homepage}">{plugin.homepage}</a>'
    
    156 174
             self._ui.plugin_homepage_linkbutton.set_markup(markup)
    
    157 175
     
    
    158 176
             if plugin.available_text:
    
    ... ... @@ -170,7 +188,7 @@ def _display_installed_plugin_info(self, plugin):
    170 188
             self._ui.configure_plugin_button.set_sensitive(
    
    171 189
                 plugin.config_dialog is not None and plugin.active)
    
    172 190
     
    
    173
    -    def _clear_installed_plugin_info(self):
    
    191
    +    def _clear_installed_plugin_info(self) -> None:
    
    174 192
             self._ui.plugin_name_label.set_text('')
    
    175 193
             self._ui.plugin_version_label.set_text('')
    
    176 194
             self._ui.plugin_authors_label.set_text('')
    
    ... ... @@ -180,7 +198,7 @@ def _clear_installed_plugin_info(self):
    180 198
             self._ui.uninstall_plugin_button.set_sensitive(False)
    
    181 199
             self._ui.configure_plugin_button.set_sensitive(False)
    
    182 200
     
    
    183
    -    def _fill_installed_plugins_model(self):
    
    201
    +    def _fill_installed_plugins_model(self) -> None:
    
    184 202
             pm = app.plugin_manager
    
    185 203
             self.installed_plugins_model.clear()
    
    186 204
             self.installed_plugins_model.set_sort_column_id(1,
    
    ... ... @@ -195,15 +213,19 @@ def _fill_installed_plugins_model(self):
    195 213
                      plugin.activatable,
    
    196 214
                      icon])
    
    197 215
     
    
    198
    -    def _get_plugin_icon(self, plugin):
    
    199
    -        icon_file = os.path.join(plugin.__path__, os.path.split(
    
    200
    -                                 plugin.__path__)[1]) + '.png'
    
    216
    +    def _get_plugin_icon(self, plugin: GajimPlugin) -> GdkPixbuf.Pixbuf:
    
    217
    +        icon_file = os.path.join(
    
    218
    +            plugin.__path__, os.path.split(plugin.__path__)[1]) + '.png'
    
    219
    +        assert self.def_icon is not None
    
    201 220
             icon = self.def_icon
    
    202 221
             if os.path.isfile(icon_file):
    
    203 222
                 icon = GdkPixbuf.Pixbuf.new_from_file_at_size(icon_file, 16, 16)
    
    204 223
             return icon
    
    205 224
     
    
    206
    -    def _installed_plugin_toggled(self, _cell, path):
    
    225
    +    def _installed_plugin_toggled(self,
    
    226
    +                                  _cell: Gtk.CellRendererToggle,
    
    227
    +                                  path: str
    
    228
    +                                  ) -> None:
    
    207 229
             is_active = self.installed_plugins_model[path][Column.ACTIVE]
    
    208 230
             plugin = self.installed_plugins_model[path][Column.PLUGIN]
    
    209 231
     
    
    ... ... @@ -221,7 +243,7 @@ def _installed_plugin_toggled(self, _cell, path):
    221 243
                 plugin.config_dialog is not None and not is_active)
    
    222 244
             self.installed_plugins_model[path][Column.ACTIVE] = not is_active
    
    223 245
     
    
    224
    -    def _on_configure_plugin(self, _widget):
    
    246
    +    def _on_configure_plugin(self, _button: Gtk.Button) -> None:
    
    225 247
             selection = self._ui.installed_plugins_treeview.get_selection()
    
    226 248
             model, iter_ = selection.get_selected()
    
    227 249
             if iter_:
    
    ... ... @@ -238,25 +260,25 @@ def _on_configure_plugin(self, _widget):
    238 260
                 # XXX: maybe throw exception here?
    
    239 261
                 pass
    
    240 262
     
    
    241
    -    def _on_uninstall_plugin(self, _widget):
    
    263
    +    def _on_uninstall_plugin(self, _button: Gtk.ToolButton) -> None:
    
    242 264
             selection = self._ui.installed_plugins_treeview.get_selection()
    
    243 265
             model, iter_ = selection.get_selected()
    
    244 266
             if iter_:
    
    245 267
                 plugin = model.get_value(iter_, Column.PLUGIN)
    
    246 268
                 try:
    
    247 269
                     app.plugin_manager.uninstall_plugin(plugin)
    
    248
    -            except PluginsystemError as e:
    
    270
    +            except PluginsystemError as error:
    
    249 271
                     WarningDialog(_('Unable to properly remove the plugin'),
    
    250
    -                              str(e), self)
    
    272
    +                              str(error), self)
    
    251 273
                     return
    
    252 274
     
    
    253
    -    def _on_plugin_removed(self, event):
    
    275
    +    def _on_plugin_removed(self, event: PluginRemoved) -> None:
    
    254 276
             for row in self.installed_plugins_model:
    
    255 277
                 if row[Column.PLUGIN] == event.plugin:
    
    256 278
                     self.installed_plugins_model.remove(row.iter)
    
    257 279
                     break
    
    258 280
     
    
    259
    -    def _on_plugin_added(self, event):
    
    281
    +    def _on_plugin_added(self, event: PluginAdded) -> None:
    
    260 282
             icon = self._get_plugin_icon(event.plugin)
    
    261 283
             self.installed_plugins_model.append([event.plugin,
    
    262 284
                                                  event.plugin.name,
    
    ... ... @@ -264,18 +286,18 @@ def _on_plugin_added(self, event):
    264 286
                                                  event.plugin.activatable,
    
    265 287
                                                  icon])
    
    266 288
     
    
    267
    -    def _on_install_plugin(self, _widget):
    
    289
    +    def _on_install_plugin(self, _button: Gtk.ToolButton) -> None:
    
    268 290
             if app.is_flatpak():
    
    269 291
                 open_uri('https://dev.gajim.org/gajim/gajim/wikis/help/flathub')
    
    270 292
                 return
    
    271 293
     
    
    272
    -        def _show_warn_dialog():
    
    294
    +        def _show_warn_dialog() -> None:
    
    273 295
                 text = _('Archive is malformed')
    
    274 296
                 dialog = WarningDialog(text, '', transient_for=self)
    
    275 297
                 dialog.set_modal(False)
    
    276 298
                 dialog.popup()
    
    277 299
     
    
    278
    -        def _on_plugin_exists(zip_filename):
    
    300
    +        def _on_plugin_exists(zip_filename: str) -> None:
    
    279 301
                 def _on_yes():
    
    280 302
                     plugin = app.plugin_manager.install_from_zip(zip_filename,
    
    281 303
                                                                  overwrite=True)
    
    ... ... @@ -293,7 +315,7 @@ def _on_yes():
    293 315
                                        callback=_on_yes)],
    
    294 316
                     transient_for=self).show()
    
    295 317
     
    
    296
    -        def _try_install(zip_filename):
    
    318
    +        def _try_install(zip_filename: str) -> None:
    
    297 319
                 try:
    
    298 320
                     plugin = app.plugin_manager.install_from_zip(zip_filename)
    
    299 321
                 except PluginsystemError as er_type:
    
    ... ... @@ -302,7 +324,7 @@ def _try_install(zip_filename):
    302 324
                         _on_plugin_exists(zip_filename)
    
    303 325
                         return
    
    304 326
     
    
    305
    -                WarningDialog(error_text, '"%s"' % zip_filename, self)
    
    327
    +                WarningDialog(error_text, f'"{zip_filename}"', self)
    
    306 328
                     return
    
    307 329
                 if not plugin:
    
    308 330
                     _show_warn_dialog()
    
    ... ... @@ -312,9 +334,10 @@ def _try_install(zip_filename):
    312 334
     
    
    313 335
     
    
    314 336
     class GajimPluginConfigDialog(Gtk.Dialog):
    
    315
    -    def __init__(self, plugin, **kwargs):
    
    316
    -        Gtk.Dialog.__init__(self, title='%s %s' % (plugin.name,
    
    317
    -                            _('Configuration')), **kwargs)
    
    337
    +    def __init__(self, plugin: GajimPlugin, **kwargs: Any) -> None:
    
    338
    +        Gtk.Dialog.__init__(self,
    
    339
    +                            title='%s %s' % (plugin.name, _('Configuration')),
    
    340
    +                            **kwargs)
    
    318 341
             self.plugin = plugin
    
    319 342
             button = self.add_button('gtk-close', Gtk.ResponseType.CLOSE)
    
    320 343
             button.connect('clicked', self.on_close_button_clicked)
    
    ... ... @@ -323,14 +346,14 @@ def __init__(self, plugin, **kwargs):
    323 346
     
    
    324 347
             self.init()
    
    325 348
     
    
    326
    -    def on_close_dialog(self, widget, data):
    
    349
    +    def on_close_dialog(self, widget: Gtk.Widget, data: Any) -> bool:
    
    327 350
             self.hide()
    
    328 351
             return True
    
    329 352
     
    
    330
    -    def on_close_button_clicked(self, widget):
    
    353
    +    def on_close_button_clicked(self, widget: Gtk.Button) -> None:
    
    331 354
             self.hide()
    
    332 355
     
    
    333
    -    def run(self, parent=None):
    
    356
    +    def run(self, parent: Optional[PluginsWindow] = None):
    
    334 357
             self.set_transient_for(parent)
    
    335 358
             self.on_run()
    
    336 359
             self.show_all()
    
    ... ... @@ -338,8 +361,8 @@ def run(self, parent=None):
    338 361
             result = super(GajimPluginConfigDialog, self)
    
    339 362
             return result
    
    340 363
     
    
    341
    -    def init(self):
    
    364
    +    def init(self) -> None:
    
    342 365
             pass
    
    343 366
     
    
    344
    -    def on_run(self):
    
    367
    +    def on_run(self) -> None:
    
    345 368
             pass

  • gajim/plugins/helpers.py
    ... ... @@ -23,7 +23,8 @@
    23 23
     
    
    24 24
     __all__ = ['log', 'log_calls']
    
    25 25
     
    
    26
    -from typing import List
    
    26
    +from pathlib import Path
    
    27
    +from typing import Optional
    
    27 28
     
    
    28 29
     import logging
    
    29 30
     import functools
    
    ... ... @@ -56,7 +57,7 @@ class log_calls:
    56 57
         called, even though `log_calls` decorator is used.
    
    57 58
         '''
    
    58 59
     
    
    59
    -    def __init__(self, classname=''):
    
    60
    +    def __init__(self, classname: str = '') -> None:
    
    60 61
             '''
    
    61 62
             :Keywords:
    
    62 63
               classname : str
    
    ... ... @@ -117,14 +118,14 @@ def wrapper(*args, **kwargs):
    117 118
             return wrapper
    
    118 119
     
    
    119 120
     
    
    120
    -def get_builder(file_name: str, widgets: List[str] = None) -> Builder:
    
    121
    +def get_builder(file_name: str, widgets: Optional[list[str]] = None) -> Builder:
    
    121 122
         return Builder(file_name,
    
    122 123
                        widgets,
    
    123 124
                        domain=plugins_i18n.DOMAIN,
    
    124 125
                        gettext_=plugins_i18n._)
    
    125 126
     
    
    126 127
     
    
    127
    -def is_shipped_plugin(path):
    
    128
    +def is_shipped_plugin(path: Path) -> bool:
    
    128 129
         base = configpaths.get('PLUGINS_BASE')
    
    129 130
         if not base.exists():
    
    130 131
             return False
    

  • gajim/plugins/pluginmanager.py
    ... ... @@ -21,8 +21,14 @@
    21 21
     :license: GPL
    
    22 22
     '''
    
    23 23
     
    
    24
    +from __future__ import annotations
    
    25
    +
    
    24 26
     __all__ = ['PluginManager']
    
    25 27
     
    
    28
    +from typing import Any
    
    29
    +from typing import Optional
    
    30
    +from typing import Type
    
    31
    +
    
    26 32
     import os
    
    27 33
     import sys
    
    28 34
     import zipfile
    
    ... ... @@ -38,6 +44,7 @@
    38 44
     from gajim.common import app
    
    39 45
     from gajim.common import configpaths
    
    40 46
     from gajim.common import modules
    
    47
    +from gajim.common.client import Client
    
    41 48
     from gajim.common.events import PluginAdded
    
    42 49
     from gajim.common.events import PluginRemoved
    
    43 50
     from gajim.common.i18n import _
    
    ... ... @@ -48,7 +55,8 @@
    48 55
     from gajim.plugins.helpers import log
    
    49 56
     from gajim.plugins.helpers import GajimPluginActivateException
    
    50 57
     from gajim.plugins.helpers import is_shipped_plugin
    
    51
    -from gajim.plugins.gajimplugin import GajimPlugin, GajimPluginException
    
    58
    +from gajim.plugins.gajimplugin import GajimPlugin
    
    59
    +from gajim.plugins.gajimplugin import  GajimPluginException
    
    52 60
     
    
    53 61
     
    
    54 62
     FIELDS = ('name',
    
    ... ... @@ -75,7 +83,7 @@ class Plugin:
    75 83
         path: Path
    
    76 84
     
    
    77 85
         @classmethod
    
    78
    -    def from_manifest(cls, path):
    
    86
    +    def from_manifest(cls, path: Path) -> Plugin:
    
    79 87
             shipped = is_shipped_plugin(path)
    
    80 88
             manifest = path / 'manifest.ini'
    
    81 89
             if not manifest.exists() and not manifest.is_dir():
    
    ... ... @@ -129,7 +137,7 @@ def from_manifest(cls, path):
    129 137
                        shipped=shipped,
    
    130 138
                        path=path)
    
    131 139
     
    
    132
    -    def load_module(self):
    
    140
    +    def load_module(self) -> Optional[Type[GajimPlugin]]:
    
    133 141
             module_path = self.path / '__init__.py'
    
    134 142
             if not module_path.exists():
    
    135 143
                 # On Windows we only ship compiled files
    
    ... ... @@ -188,7 +196,7 @@ class PluginManager(metaclass=Singleton):
    188 196
         '''
    
    189 197
     
    
    190 198
         def __init__(self):
    
    191
    -        self.plugins = []
    
    199
    +        self.plugins: list[GajimPlugin] = []
    
    192 200
             '''
    
    193 201
             Detected plugin classes.
    
    194 202
     
    
    ... ... @@ -196,7 +204,7 @@ def __init__(self):
    196 204
     
    
    197 205
             :type: [] of class objects
    
    198 206
             '''
    
    199
    -        self.active_plugins = []
    
    207
    +        self.active_plugins: list[GajimPlugin] = []
    
    200 208
             '''
    
    201 209
             Instance objects of active plugins.
    
    202 210
     
    
    ... ... @@ -205,17 +213,17 @@ def __init__(self):
    205 213
     
    
    206 214
             :type: [] of `GajimPlugin` based objects
    
    207 215
             '''
    
    208
    -        self.gui_extension_points = {}
    
    216
    +        self.gui_extension_points: dict[str, Any] = {}
    
    209 217
             '''
    
    210 218
             Registered GUI extension points.
    
    211 219
             '''
    
    212 220
     
    
    213
    -        self.gui_extension_points_handlers = {}
    
    221
    +        self.gui_extension_points_handlers: dict[str, Any] = {}
    
    214 222
             '''
    
    215 223
             Registered handlers of GUI extension points.
    
    216 224
             '''
    
    217 225
     
    
    218
    -        self.encryption_plugins = {}
    
    226
    +        self.encryption_plugins: dict[str, GajimPlugin] = {}
    
    219 227
             '''
    
    220 228
             Registered names with instances of encryption Plugins.
    
    221 229
             '''
    
    ... ... @@ -223,20 +231,20 @@ def __init__(self):
    223 231
             self.update_plugins()
    
    224 232
             self._load_plugins()
    
    225 233
     
    
    226
    -    def update_plugins(self, replace=True, activate=False, plugin_name=None):
    
    234
    +    def update_plugins(self,
    
    235
    +                       replace: bool = True,
    
    236
    +                       activate: bool = False,
    
    237
    +                       plugin_name: Optional[str] = None
    
    238
    +                       ) -> list[str]:
    
    227 239
             '''
    
    228 240
             Move plugins from the downloaded folder to the user plugin folder
    
    229 241
     
    
    230 242
             :param replace: replace plugin files if they already exist.
    
    231
    -        :type replace: boolean
    
    232 243
             :param activate: load and activate the plugin
    
    233
    -        :type activate: boolean
    
    234 244
             :param plugin_name: if provided, update only this plugin
    
    235
    -        :type plugin_name: str
    
    236 245
             :return: list of updated plugins (files have been installed)
    
    237
    -        :rtype: [] of str
    
    238 246
             '''
    
    239
    -        updated_plugins = []
    
    247
    +        updated_plugins: list[str] = []
    
    240 248
             user_dir = configpaths.get('PLUGINS_USER')
    
    241 249
             dl_dir = configpaths.get('PLUGINS_DOWNLOAD')
    
    242 250
             to_update = [plugin_name] if plugin_name else next(os.walk(dl_dir))[1]
    
    ... ... @@ -264,7 +272,7 @@ def update_plugins(self, replace=True, activate=False, plugin_name=None):
    264 272
                     self.add_plugin(plugin, activate=True)
    
    265 273
             return updated_plugins
    
    266 274
     
    
    267
    -    def init_plugins(self):
    
    275
    +    def init_plugins(self) -> None:
    
    268 276
             for plugin in self.plugins:
    
    269 277
                 if not app.settings.get_plugin_setting(plugin.short_name, 'active'):
    
    270 278
                     continue
    
    ... ... @@ -276,14 +284,18 @@ def init_plugins(self):
    276 284
                 except GajimPluginActivateException:
    
    277 285
                     pass
    
    278 286
     
    
    279
    -    def add_plugin(self, plugin, activate=False):
    
    287
    +    def add_plugin(self,
    
    288
    +                   plugin: Plugin,
    
    289
    +                   activate: bool = False
    
    290
    +                   ) -> Optional[GajimPlugin]:
    
    280 291
             plugin_class = plugin.load_module()
    
    281 292
             if plugin_class is None:
    
    282 293
                 return None
    
    283 294
     
    
    284 295
             if plugin in self.plugins:
    
    285 296
                 log.info('Not loading plugin %s v %s. Plugin already loaded.',
    
    286
    -                     plugin.short_name, plugin.version)
    
    297
    +                     plugin.short_name,
    
    298
    +                     plugin.version)
    
    287 299
                 return None
    
    288 300
     
    
    289 301
             try:
    
    ... ... @@ -307,7 +319,7 @@ def add_plugin(self, plugin, activate=False):
    307 319
     
    
    308 320
             return plugin_obj
    
    309 321
     
    
    310
    -    def remove_plugin(self, plugin):
    
    322
    +    def remove_plugin(self, plugin: GajimPlugin) -> None:
    
    311 323
             '''
    
    312 324
             removes the plugin from the plugin list and deletes all loaded modules
    
    313 325
             from sys. This way we will have a fresh start when the plugin gets added
    
    ... ... @@ -332,19 +344,19 @@ def remove_plugin(self, plugin):
    332 344
             for module_to_remove in modules_to_remove:
    
    333 345
                 del sys.modules[module_to_remove]
    
    334 346
     
    
    335
    -    def get_active_plugin(self, plugin_name):
    
    347
    +    def get_active_plugin(self, plugin_name: str) -> Optional[GajimPlugin]:
    
    336 348
             for plugin in self.active_plugins:
    
    337 349
                 if plugin.short_name == plugin_name:
    
    338 350
                     return plugin
    
    339 351
             return None
    
    340 352
     
    
    341
    -    def get_plugin(self, short_name):
    
    353
    +    def get_plugin(self, short_name: str) -> Optional[GajimPlugin]:
    
    342 354
             for plugin in self.plugins:
    
    343 355
                 if plugin.short_name == short_name:
    
    344 356
                     return plugin
    
    345 357
             return None
    
    346 358
     
    
    347
    -    def extension_point(self, gui_extpoint_name, *args):
    
    359
    +    def extension_point(self, gui_extpoint_name: str, *args: Any) -> None:
    
    348 360
             '''
    
    349 361
             Invokes all handlers (from plugins) for a particular extension point, but
    
    350 362
             doesn't add it to collection for further processing.
    
    ... ... @@ -353,24 +365,22 @@ def extension_point(self, gui_extpoint_name, *args):
    353 365
             plugin. For example after an update.
    
    354 366
     
    
    355 367
             :param gui_extpoint_name: name of GUI extension point.
    
    356
    -        :type gui_extpoint_name: str
    
    357 368
             :param args: parameters to be passed to extension point handlers
    
    358 369
                     (typically and object that invokes `gui_extension_point`;
    
    359 370
                     however, this can be practically anything)
    
    360 371
             :type args: tuple
    
    361 372
             '''
    
    362 373
     
    
    363
    -        self._execute_all_handlers_of_gui_extension_point(gui_extpoint_name,
    
    364
    -            *args)
    
    374
    +        self._execute_all_handlers_of_gui_extension_point(
    
    375
    +            gui_extpoint_name, *args)
    
    365 376
     
    
    366
    -    def gui_extension_point(self, gui_extpoint_name, *args):
    
    377
    +    def gui_extension_point(self, gui_extpoint_name: str, *args: Any) -> None:
    
    367 378
             '''
    
    368 379
             Invokes all handlers (from plugins) for particular GUI extension point
    
    369 380
             and adds it to collection for further processing (eg. by plugins not
    
    370 381
             active yet).
    
    371 382
     
    
    372 383
             :param gui_extpoint_name: name of GUI extension point.
    
    373
    -        :type gui_extpoint_name: str
    
    374 384
             :param args: parameters to be passed to extension point handlers
    
    375 385
                     (typically and object that invokes `gui_extension_point`;
    
    376 386
                     however, this can be practically anything)
    
    ... ... @@ -392,7 +402,10 @@ def gui_extension_point(self, gui_extpoint_name, *args):
    392 402
             self._execute_all_handlers_of_gui_extension_point(gui_extpoint_name,
    
    393 403
                 *args)
    
    394 404
     
    
    395
    -    def remove_gui_extension_point(self, gui_extpoint_name, *args):
    
    405
    +    def remove_gui_extension_point(self,
    
    406
    +                                   gui_extpoint_name: str,
    
    407
    +                                   *args: Any
    
    408
    +                                   ) -> None:
    
    396 409
             '''
    
    397 410
             Removes GUI extension point from collection held by `PluginManager`.
    
    398 411
     
    
    ... ... @@ -420,7 +433,6 @@ def remove_gui_extension_point(self, gui_extpoint_name, *args):
    420 433
                     freedom, but is this necessary?
    
    421 434
     
    
    422 435
             :param gui_extpoint_name: name of GUI extension point.
    
    423
    -        :type gui_extpoint_name: str
    
    424 436
             :param args: arguments that `PluginManager.gui_extension_point` was
    
    425 437
                     called with for this extension point. This is used (along with
    
    426 438
                     extension point name) to identify element to be removed.
    
    ... ... @@ -441,7 +453,10 @@ def remove_gui_extension_point(self, gui_extpoint_name, *args):
    441 453
                 if disconnect_handler is not None:
    
    442 454
                     disconnect_handler(args[0])
    
    443 455
     
    
    444
    -    def _add_gui_extension_point_call_to_list(self, gui_extpoint_name, *args):
    
    456
    +    def _add_gui_extension_point_call_to_list(self,
    
    457
    +                                              gui_extpoint_name: str,
    
    458
    +                                              *args: Any
    
    459
    +                                              ) -> None:
    
    445 460
             '''
    
    446 461
             Adds GUI extension point call to list of calls.
    
    447 462
     
    
    ... ... @@ -453,8 +468,6 @@ def _add_gui_extension_point_call_to_list(self, gui_extpoint_name, *args):
    453 468
     
    
    454 469
             :param gui_extpoint_name: GUI extension point name used to identify it
    
    455 470
                     by plugins.
    
    456
    -        :type gui_extpoint_name: str
    
    457
    -
    
    458 471
             :param args: parameters to be passed to extension point handlers
    
    459 472
                     (typically and object that invokes `gui_extension_point`;
    
    460 473
                     however, this can be practically anything)
    
    ... ... @@ -466,8 +479,10 @@ def _add_gui_extension_point_call_to_list(self, gui_extpoint_name, *args):
    466 479
                 self.gui_extension_points.setdefault(gui_extpoint_name, []).append(
    
    467 480
                     args)
    
    468 481
     
    
    469
    -    def _execute_all_handlers_of_gui_extension_point(self, gui_extpoint_name,
    
    470
    -    *args):
    
    482
    +    def _execute_all_handlers_of_gui_extension_point(self,
    
    483
    +                                                     gui_extpoint_name: str,
    
    484
    +                                                     *args: Any
    
    485
    +                                                     ) -> None:
    
    471 486
             if gui_extpoint_name in self.gui_extension_points_handlers:
    
    472 487
                 for handlers in self.gui_extension_points_handlers[
    
    473 488
                 gui_extpoint_name]:
    
    ... ... @@ -477,47 +492,49 @@ def _execute_all_handlers_of_gui_extension_point(self, gui_extpoint_name,
    477 492
                         log.warning('Error executing %s',
    
    478 493
                                     handlers[0], exc_info=True)
    
    479 494
     
    
    480
    -    def _register_events_handlers_in_ged(self, plugin):
    
    495
    +    def _register_events_handlers_in_ged(self, plugin: GajimPlugin) -> None:
    
    481 496
             for event_name, handler in plugin.events_handlers.items():
    
    482 497
                 priority = handler[0]
    
    483 498
                 handler_function = handler[1]
    
    484
    -            app.ged.register_event_handler(event_name, priority,
    
    485
    -                handler_function)
    
    499
    +            app.ged.register_event_handler(
    
    500
    +                event_name, priority, handler_function)
    
    486 501
     
    
    487
    -    def _remove_events_handler_from_ged(self, plugin):
    
    502
    +    def _remove_events_handler_from_ged(self, plugin: GajimPlugin) -> None:
    
    488 503
             for event_name, handler in plugin.events_handlers.items():
    
    489 504
                 priority = handler[0]
    
    490 505
                 handler_function = handler[1]
    
    491
    -            app.ged.remove_event_handler(event_name, priority,
    
    492
    -                handler_function)
    
    506
    +            app.ged.remove_event_handler(
    
    507
    +                event_name, priority, handler_function)
    
    493 508
     
    
    494
    -    def _remove_name_from_encryption_plugins(self, plugin):
    
    509
    +    def _remove_name_from_encryption_plugins(self,
    
    510
    +                                             plugin: GajimPlugin
    
    511
    +                                             ) -> None:
    
    495 512
             if plugin.encryption_name:
    
    496 513
                 del self.encryption_plugins[plugin.encryption_name]
    
    497 514
     
    
    498
    -    def _register_modules_with_handlers(self, plugin):
    
    515
    +    def _register_modules_with_handlers(self, plugin: GajimPlugin) -> None:
    
    499 516
             if not hasattr(plugin, 'modules'):
    
    500 517
                 return
    
    501
    -        for con in app.connections.values():
    
    518
    +        for client in app.connections.values():
    
    502 519
                 for module in plugin.modules:
    
    503
    -                instance, name = module.get_instance(con)
    
    504
    -                modules.register_single_module(con, instance, name)
    
    520
    +                instance, name = module.get_instance(client)
    
    521
    +                modules.register_single_module(client, instance, name)
    
    505 522
     
    
    506 523
                     for handler in instance.handlers:
    
    507
    -                    con.connection.register_handler(handler)
    
    524
    +                    client.connection.register_handler(handler)
    
    508 525
     
    
    509
    -    def _unregister_modules_with_handlers(self, plugin):
    
    526
    +    def _unregister_modules_with_handlers(self, plugin: GajimPlugin) -> None:
    
    510 527
             if not hasattr(plugin, 'modules'):
    
    511 528
                 return
    
    512
    -        for con in app.connections.values():
    
    529
    +        for client in app.connections.values():
    
    513 530
                 for module in plugin.modules:
    
    514 531
                     instance = con.get_module(module.name)
    
    515
    -                modules.unregister_single_module(con, module.name)
    
    532
    +                modules.unregister_single_module(client, module.name)
    
    516 533
     
    
    517 534
                     for handler in instance.handlers:
    
    518
    -                    con.connection.unregister_handler(handler)
    
    535
    +                    client.connection.unregister_handler(handler)
    
    519 536
     
    
    520
    -    def activate_plugin(self, plugin):
    
    537
    +    def activate_plugin(self, plugin: GajimPlugin) -> None:
    
    521 538
             '''
    
    522 539
             :param plugin: plugin to be activated
    
    523 540
             :type plugin: class object of `GajimPlugin` subclass
    
    ... ... @@ -539,7 +556,7 @@ def activate_plugin(self, plugin):
    539 556
                 app.settings.set_plugin_setting(plugin.short_name, 'active', True)
    
    540 557
                 plugin.active = True
    
    541 558
     
    
    542
    -    def deactivate_plugin(self, plugin):
    
    559
    +    def deactivate_plugin(self, plugin: GajimPlugin) -> None:
    
    543 560
             # remove GUI extension points handlers (provided by plug-in) from
    
    544 561
             # handlers list
    
    545 562
             for gui_extpoint_name, gui_extpoint_handlers in \
    
    ... ... @@ -573,17 +590,21 @@ def deactivate_plugin(self, plugin):
    573 590
             app.settings.set_plugin_setting(plugin.short_name, 'active', False)
    
    574 591
             plugin.active = False
    
    575 592
     
    
    576
    -    def _add_gui_extension_points_handlers_from_plugin(self, plugin):
    
    593
    +    def _add_gui_extension_points_handlers_from_plugin(self,
    
    594
    +                                                       plugin: GajimPlugin
    
    595
    +                                                       ) -> None:
    
    577 596
             for gui_extpoint_name, gui_extpoint_handlers in \
    
    578 597
             plugin.gui_extension_points.items():
    
    579
    -            self.gui_extension_points_handlers.setdefault(gui_extpoint_name,
    
    580
    -                []).append(gui_extpoint_handlers)
    
    598
    +            self.gui_extension_points_handlers.setdefault(
    
    599
    +                gui_extpoint_name, []).append(gui_extpoint_handlers)
    
    581 600
     
    
    582
    -    def _add_encryption_name_from_plugin(self, plugin):
    
    601
    +    def _add_encryption_name_from_plugin(self, plugin: GajimPlugin) -> None:
    
    583 602
             if plugin.encryption_name:
    
    584 603
                 self.encryption_plugins[plugin.encryption_name] = plugin
    
    585 604
     
    
    586
    -    def _handle_all_gui_extension_points_with_plugin(self, plugin):
    
    605
    +    def _handle_all_gui_extension_points_with_plugin(self,
    
    606
    +                                                     plugin: GajimPlugin
    
    607
    +                                                     ) -> None:
    
    587 608
             for gui_extpoint_name, gui_extpoint_handlers in \
    
    588 609
             plugin.gui_extension_points.items():
    
    589 610
                 if gui_extpoint_name in self.gui_extension_points:
    
    ... ... @@ -597,7 +618,7 @@ def _handle_all_gui_extension_points_with_plugin(self, plugin):
    597 618
                                 log.warning('Error executing %s',
    
    598 619
                                             handler, exc_info=True)
    
    599 620
     
    
    600
    -    def register_modules_for_account(self, con):
    
    621
    +    def register_modules_for_account(self, client: Client) -> None:
    
    601 622
             '''
    
    602 623
             A new account has been added, register modules
    
    603 624
             of all active plugins
    
    ... ... @@ -610,21 +631,22 @@ def register_modules_for_account(self, con):
    610 631
                     continue
    
    611 632
     
    
    612 633
                 for module in plugin.modules:
    
    613
    -                instance, name = module.get_instance(con)
    
    614
    -                modules.register_single_module(con, instance, name)
    
    634
    +                instance, name = module.get_instance(client)
    
    635
    +                modules.register_single_module(client, instance, name)
    
    615 636
     
    
    616 637
                     for handler in instance.handlers:
    
    617
    -                    con.connection.register_handler(handler)
    
    638
    +                    client.connection.register_handler(handler)
    
    618 639
     
    
    619 640
         @staticmethod
    
    620
    -    def _load_plugin(plugin_path):
    
    641
    +    def _load_plugin(plugin_path: Path) -> Optional[Plugin]:
    
    621 642
             try:
    
    622 643
                 return Plugin.from_manifest(plugin_path)
    
    623 644
             except Exception as error:
    
    624 645
                 log.warning(error)
    
    646
    +            return None
    
    625 647
     
    
    626
    -    def _load_plugins(self):
    
    627
    -        plugins = {}
    
    648
    +    def _load_plugins(self) -> None:
    
    649
    +        plugins: dict[str, Plugin] = {}
    
    628 650
             for plugin_dir in configpaths.get_plugin_dirs():
    
    629 651
                 if not plugin_dir.is_dir():
    
    630 652
                     continue
    
    ... ... @@ -646,7 +668,10 @@ def _load_plugins(self):
    646 668
             for plugin in plugins.values():
    
    647 669
                 self.add_plugin(plugin)
    
    648 670
     
    
    649
    -    def install_from_zip(self, zip_filename, overwrite=None):
    
    671
    +    def install_from_zip(self,
    
    672
    +                         zip_filename: str,
    
    673
    +                         overwrite: bool = False
    
    674
    +                         ) -> Optional[GajimPlugin]:
    
    650 675
             '''
    
    651 676
             Install plugin from zip and return plugin
    
    652 677
             '''
    
    ... ... @@ -662,11 +687,11 @@ def install_from_zip(self, zip_filename, overwrite=None):
    662 687
                 # CRC error
    
    663 688
                 raise PluginsystemError(_('Archive corrupted'))
    
    664 689
     
    
    665
    -        dirs = []
    
    690
    +        dirs: list[str] = []
    
    666 691
             manifest = None
    
    667 692
             for filename in zip_file.namelist():
    
    668
    -            if filename.startswith('.') or filename.startswith('/') or \
    
    669
    -            ('/' not in filename):
    
    693
    +            if (filename.startswith('.') or filename.startswith('/') or
    
    694
    +                    ('/' not in filename)):
    
    670 695
                     # members not safe
    
    671 696
                     raise PluginsystemError(_('Archive is malformed'))
    
    672 697
                 if filename.endswith('/') and filename.find('/', 0, -1) < 0:
    
    ... ... @@ -686,7 +711,9 @@ def install_from_zip(self, zip_filename, overwrite=None):
    686 711
             # Plugin dir already exists
    
    687 712
                 if not overwrite:
    
    688 713
                     raise PluginsystemError(_('Plugin already exists'))
    
    689
    -            self.uninstall_plugin(self.get_plugin_by_path(str(plugin_path)))
    
    714
    +            plugin = self.get_plugin_by_path(str(plugin_path))
    
    715
    +            assert isinstance(plugin, GajimPlugin)
    
    716
    +            self.uninstall_plugin(plugin)
    
    690 717
     
    
    691 718
             zip_file.extractall(user_dir)
    
    692 719
             zip_file.close()
    
    ... ... @@ -699,7 +726,7 @@ def install_from_zip(self, zip_filename, overwrite=None):
    699 726
     
    
    700 727
             return self.add_plugin(plugin)
    
    701 728
     
    
    702
    -    def delete_plugin_files(self, plugin_path):
    
    729
    +    def delete_plugin_files(self, plugin_path: Path) -> None:
    
    703 730
             def on_error(func, path, error):
    
    704 731
                 if func is os.path.islink:
    
    705 732
                 # if symlink
    
    ... ... @@ -710,7 +737,7 @@ def on_error(func, path, error):
    710 737
     
    
    711 738
             rmtree(plugin_path, False, on_error)
    
    712 739
     
    
    713
    -    def uninstall_plugin(self, plugin):
    
    740
    +    def uninstall_plugin(self, plugin: GajimPlugin) -> None:
    
    714 741
             '''
    
    715 742
             Deactivate and remove plugin from `plugins` list
    
    716 743
             '''
    
    ... ... @@ -718,17 +745,17 @@ def uninstall_plugin(self, plugin):
    718 745
                 return
    
    719 746
     
    
    720 747
             self.remove_plugin(plugin)
    
    721
    -        self.delete_plugin_files(plugin.__path__)
    
    748
    +        self.delete_plugin_files(Path(plugin.__path__))
    
    722 749
             if not is_shipped_plugin(Path(plugin.__path__)):
    
    723 750
                 path = configpaths.get('PLUGINS_BASE') / plugin.short_name
    
    724 751
                 if path.exists():
    
    725
    -                self.delete_plugin_files(str(path))
    
    752
    +                self.delete_plugin_files(path)
    
    726 753
     
    
    727 754
             app.settings.remove_plugin(plugin.short_name)
    
    728 755
     
    
    729 756
             app.ged.raise_event(PluginRemoved(plugin=plugin))
    
    730 757
     
    
    731
    -    def get_plugin_by_path(self, plugin_dir):
    
    758
    +    def get_plugin_by_path(self, plugin_dir: str) -> Optional[GajimPlugin]:
    
    732 759
             for plugin in self.plugins:
    
    733 760
                 if plugin.__path__ in plugin_dir:
    
    734 761
                     return plugin