Daniel Brötzmann pushed to branch master at gajim / gajim
Commits:
-
231ef904
by wurstsalat at 2022-01-24T18:38:28+01:00
-
7807a8f8
by wurstsalat at 2022-01-24T18:38:28+01:00
-
9d1df09e
by wurstsalat at 2022-01-24T18:38:28+01:00
8 changed files:
- gajim/common/app.py
- gajim/common/types.py
- gajim/gtk/menus.py
- gajim/gtk/preview.py
- gajim/plugins/gajimplugin.py
- gajim/plugins/gui.py
- gajim/plugins/helpers.py
- gajim/plugins/pluginmanager.py
Changes:
| ... | ... | @@ -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):
|
| ... | ... | @@ -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']
|
| ... | ... | @@ -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(
|
| ... | ... | @@ -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,
|
| ... | ... | @@ -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
|
| ... | ... | @@ -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
|
| ... | ... | @@ -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
|
| ... | ... | @@ -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
|