Philipp Hörist pushed to branch master at gajim / gajim

Commits:

6 changed files:

Changes:

  • gajim/common/app.py
    ... ... @@ -26,10 +26,10 @@
    26 26
     
    
    27 27
     from __future__ import annotations
    
    28 28
     
    
    29
    -from typing import Any  # pylint: disable=unused-import
    
    30
    -from typing import Dict  # pylint: disable=unused-import
    
    31
    -from typing import List  # pylint: disable=unused-import
    
    32
    -from typing import Optional  # pylint: disable=unused-import
    
    29
    +from typing import Any
    
    30
    +from typing import NamedTuple
    
    31
    +from typing import Union
    
    32
    +from typing import Optional
    
    33 33
     from typing import cast
    
    34 34
     
    
    35 35
     import gc
    
    ... ... @@ -38,42 +38,37 @@
    38 38
     import logging
    
    39 39
     import weakref
    
    40 40
     import pprint
    
    41
    -from collections import namedtuple
    
    42 41
     from collections import defaultdict
    
    43 42
     
    
    44 43
     import nbxmpp
    
    45 44
     from nbxmpp.idlequeue import IdleQueue
    
    46 45
     from gi.repository import Gdk
    
    47 46
     from gi.repository import GLib
    
    47
    +from nbxmpp.protocol import JID
    
    48 48
     
    
    49 49
     import gajim
    
    50
    +from gajim.common import types
    
    50 51
     from gajim.common import config as c_config
    
    51 52
     from gajim.common import configpaths
    
    52 53
     from gajim.common import ged as ged_module
    
    53 54
     from gajim.common.i18n import LANG
    
    54 55
     from gajim.common.const import Display
    
    55
    -from gajim.common.types import NetworkEventsControllerT  # pylint: disable=unused-import
    
    56
    -from gajim.common.types import InterfaceT  # pylint: disable=unused-import
    
    57
    -from gajim.common.types import ConnectionT  # pylint: disable=unused-import
    
    58
    -from gajim.common.types import LegacyContactsAPIT  # pylint: disable=unused-import
    
    59
    -from gajim.common.types import SettingsT  # pylint: disable=unused-import
    
    60
    -from gajim.common.types import CSSConfigT  # pylint: disable=unused-import
    
    61 56
     
    
    62 57
     
    
    63
    -interface = cast(InterfaceT, None)
    
    58
    +interface = cast(types.InterfaceT, None)
    
    64 59
     thread_interface = lambda *args: None # Interface to run a thread and then a callback
    
    65 60
     config = c_config.Config()
    
    66
    -settings = cast(SettingsT, None)
    
    61
    +settings = cast(types.SettingsT, None)
    
    67 62
     version = gajim.__version__
    
    68
    -connections = {}  # type: Dict[str, ConnectionT]
    
    69
    -avatar_cache = {}  # type: Dict[str, Dict[str, Any]]
    
    70
    -bob_cache = {} # type: Dict[str, bytes]
    
    63
    +connections: dict[str, types.Client] = {}
    
    64
    +avatar_cache: dict[str, dict[str, Any]] = {}
    
    65
    +bob_cache: dict[str, bytes] = {}
    
    71 66
     ipython_window = None
    
    72 67
     app = None  # Gtk.Application
    
    73 68
     window = None # MainWindow
    
    74 69
     
    
    75 70
     ged = ged_module.GlobalEventsDispatcher() # Global Events Dispatcher
    
    76
    -nec = cast(NetworkEventsControllerT, None)
    
    71
    +nec = cast(types.NetworkEventsControllerT, None)
    
    77 72
     plugin_manager = None # Plugins Manager
    
    78 73
     
    
    79 74
     class Storage:
    
    ... ... @@ -83,41 +78,32 @@ def __init__(self):
    83 78
     
    
    84 79
     storage = Storage()
    
    85 80
     
    
    86
    -css_config = cast(CSSConfigT, None)
    
    81
    +css_config = cast(types.CSSConfigT, None)
    
    87 82
     
    
    88
    -transport_type = {}  # type: Dict[str, str]
    
    89
    -
    
    90
    -contacts = cast(LegacyContactsAPIT, None)
    
    91
    -
    
    92
    -# tell if we are connected to the room or not
    
    93
    -# {acct: {room_jid: True}}
    
    94
    -gc_connected = {}  # type: Dict[str, Dict[str, bool]]
    
    83
    +transport_type: dict[str, str] = {}
    
    95 84
     
    
    96 85
     # dict of rooms that must be automatically configured
    
    97 86
     # and for which we have a list of invities
    
    98 87
     # {account: {room_jid: {'invities': []}}}
    
    99
    -automatic_rooms = {}  # type: Dict[str, Dict[str, Dict[str, List[str]]]]
    
    100
    -
    
    101
    - # dict of groups, holds if they are expanded or not
    
    102
    -groups = {}  # type: Dict[str, Dict[str, Dict[str, bool]]]
    
    88
    +automatic_rooms: dict[str, dict[str, dict[str, list[str]]]] = {}
    
    103 89
     
    
    104 90
     # list of contacts that has just signed in
    
    105
    -newly_added = {}  # type: Dict[str, List[str]]
    
    91
    +newly_added: dict[str, list[str]] = {}
    
    106 92
     
    
    107 93
     # list of contacts that has just signed out
    
    108
    -to_be_removed = {}  # type: Dict[str, List[str]]
    
    94
    +to_be_removed: dict[str, list[str]] = {}
    
    109 95
     
    
    110 96
     notification = None
    
    111 97
     
    
    112 98
     # list of our nick names in each account
    
    113
    -nicks = {}  # type: Dict[str, str]
    
    99
    +nicks: dict[str, str] = {}
    
    114 100
     
    
    115 101
     # should we block 'contact signed in' notifications for this account?
    
    116 102
     # this is only for the first 30 seconds after we change our show
    
    117 103
     # to something else than offline
    
    118 104
     # can also contain account/transport_jid to block notifications for contacts
    
    119 105
     # from this transport
    
    120
    -block_signed_in_notifications = {}  # type: Dict[str, bool]
    
    106
    +block_signed_in_notifications: dict[str, bool] = {}
    
    121 107
     
    
    122 108
     proxy65_manager = None
    
    123 109
     
    
    ... ... @@ -151,32 +137,39 @@ def __init__(self):
    151 137
         'AYATANA_APPINDICATOR': False,
    
    152 138
     }
    
    153 139
     
    
    154
    -_tasks = defaultdict(list)  # type: Dict[int, List[Any]]
    
    140
    +_tasks: dict[int, list[Any]] = defaultdict(list)
    
    141
    +
    
    142
    +
    
    143
    +class RecentGroupchat(NamedTuple):
    
    144
    +    room: str
    
    145
    +    server: str
    
    146
    +    nickname: str
    
    155 147
     
    
    156
    -def print_version():
    
    148
    +
    
    149
    +def print_version() -> None:
    
    157 150
         log('gajim').info('Gajim Version: %s', gajim.__version__)
    
    158 151
     
    
    159 152
     
    
    160
    -def get_client(account):
    
    153
    +def get_client(account: str) -> types.Client:
    
    161 154
         return connections[account]
    
    162 155
     
    
    163 156
     
    
    164
    -def is_installed(dependency):
    
    157
    +def is_installed(dependency: str) -> bool:
    
    165 158
         if dependency == 'ZEROCONF':
    
    166 159
             # Alias for checking zeroconf libs
    
    167 160
             return _dependencies['AVAHI'] or _dependencies['PYBONJOUR']
    
    168 161
         return _dependencies[dependency]
    
    169 162
     
    
    170 163
     
    
    171
    -def is_flatpak():
    
    164
    +def is_flatpak() -> bool:
    
    172 165
         return gajim.IS_FLATPAK
    
    173 166
     
    
    174 167
     
    
    175
    -def is_portable():
    
    168
    +def is_portable() -> bool:
    
    176 169
         return gajim.IS_PORTABLE
    
    177 170
     
    
    178 171
     
    
    179
    -def is_display(display):
    
    172
    +def is_display(display: Display) -> bool:
    
    180 173
         # XWayland reports as Display X11, so try with env var
    
    181 174
         is_wayland = os.environ.get('XDG_SESSION_TYPE') == 'wayland'
    
    182 175
         if is_wayland and display == Display.WAYLAND:
    
    ... ... @@ -188,10 +181,12 @@ def is_display(display):
    188 181
             return False
    
    189 182
         return default.__class__.__name__ == display.value
    
    190 183
     
    
    191
    -def disable_dependency(dependency):
    
    184
    +
    
    185
    +def disable_dependency(dependency: str) -> None:
    
    192 186
         _dependencies[dependency] = False
    
    193 187
     
    
    194
    -def detect_dependencies():
    
    188
    +
    
    189
    +def detect_dependencies() -> None:
    
    195 190
         import gi
    
    196 191
     
    
    197 192
         # ZEROCONF
    
    ... ... @@ -310,7 +305,8 @@ def detect_dependencies():
    310 305
     
    
    311 306
         log('gajim').info('Used language: %s', LANG)
    
    312 307
     
    
    313
    -def detect_desktop_env():
    
    308
    +
    
    309
    +def detect_desktop_env() -> Optional[str]:
    
    314 310
         if sys.platform in ('win32', 'darwin'):
    
    315 311
             return sys.platform
    
    316 312
     
    
    ... ... @@ -325,12 +321,12 @@ def detect_desktop_env():
    325 321
     desktop_env = detect_desktop_env()
    
    326 322
     
    
    327 323
     
    
    328
    -def get_server_from_jid(jid):
    
    324
    +def get_server_from_jid(jid: str) -> str:
    
    329 325
         pos = jid.find('@') + 1 # after @
    
    330 326
         return jid[pos:]
    
    331 327
     
    
    332 328
     
    
    333
    -def get_room_and_nick_from_fjid(jid):
    
    329
    +def get_room_and_nick_from_fjid(jid: str) -> list[str]:
    
    334 330
         # fake jid is the jid for a contact in a room
    
    335 331
         # gaim@conference.jabber.no/nick/nick-continued
    
    336 332
         # return ('gaim@conference.jabber.no', 'nick/nick-continued')
    
    ... ... @@ -339,19 +335,22 @@ def get_room_and_nick_from_fjid(jid):
    339 335
             l.append('')
    
    340 336
         return l
    
    341 337
     
    
    342
    -def get_jid_without_resource(jid):
    
    338
    +
    
    339
    +def get_jid_without_resource(jid: str) -> str:
    
    343 340
         return jid.split('/')[0]
    
    344 341
     
    
    345 342
     
    
    346
    -def get_number_of_accounts():
    
    343
    +def get_number_of_accounts() -> int:
    
    347 344
         """
    
    348 345
         Return the number of ALL accounts
    
    349 346
         """
    
    350 347
         return len(connections.keys())
    
    351 348
     
    
    352
    -def get_number_of_connected_accounts(accounts_list=None):
    
    349
    +
    
    350
    +def get_number_of_connected_accounts(
    
    351
    +        accounts_list: Optional[list[str]] = None) -> int:
    
    353 352
         """
    
    354
    -    Returns the number of CONNECTED accounts. Uou can optionally pass an
    
    353
    +    Returns the number of connected accounts. You can optionally pass an
    
    355 354
         accounts_list and if you do those will be checked, else all will be checked
    
    356 355
         """
    
    357 356
         connected_accounts = 0
    
    ... ... @@ -364,18 +363,20 @@ def get_number_of_connected_accounts(accounts_list=None):
    364 363
                 connected_accounts = connected_accounts + 1
    
    365 364
         return connected_accounts
    
    366 365
     
    
    367
    -def get_available_clients():
    
    368
    -    clients = []
    
    366
    +
    
    367
    +def get_available_clients() -> list[types.Client]:
    
    368
    +    clients: list[types.Client] = []
    
    369 369
         for client in connections.values():
    
    370 370
             if client.state.is_available:
    
    371 371
                 clients.append(client)
    
    372 372
         return clients
    
    373 373
     
    
    374
    -def get_connected_accounts(exclude_local=False):
    
    374
    +
    
    375
    +def get_connected_accounts(exclude_local: bool = False) -> list[str]:
    
    375 376
         """
    
    376 377
         Returns a list of CONNECTED accounts
    
    377 378
         """
    
    378
    -    account_list = []
    
    379
    +    account_list: list[str] = []
    
    379 380
         for account in connections:
    
    380 381
             if account == 'Local' and exclude_local:
    
    381 382
                 continue
    
    ... ... @@ -383,7 +384,8 @@ def get_connected_accounts(exclude_local=False):
    383 384
                 account_list.append(account)
    
    384 385
         return account_list
    
    385 386
     
    
    386
    -def get_accounts_sorted():
    
    387
    +
    
    388
    +def get_accounts_sorted() -> list[str]:
    
    387 389
         '''
    
    388 390
         Get all accounts alphabetically sorted with Local first
    
    389 391
         '''
    
    ... ... @@ -394,13 +396,16 @@ def get_accounts_sorted():
    394 396
             account_list.insert(0, 'Local')
    
    395 397
         return account_list
    
    396 398
     
    
    397
    -def get_enabled_accounts_with_labels(exclude_local=True, connected_only=False,
    
    398
    -                                     private_storage_only=False):
    
    399
    +
    
    400
    +def get_enabled_accounts_with_labels(
    
    401
    +        exclude_local: bool = True,
    
    402
    +        connected_only: bool = False,
    
    403
    +        private_storage_only: bool = False) -> list[str]:
    
    399 404
         """
    
    400 405
         Returns a list with [account, account_label] entries.
    
    401 406
         Order by account_label
    
    402 407
         """
    
    403
    -    accounts = []
    
    408
    +    accounts: list[str] = []
    
    404 409
         for acc in connections:
    
    405 410
             if exclude_local and account_is_zeroconf(acc):
    
    406 411
                 continue
    
    ... ... @@ -414,42 +419,47 @@ def get_enabled_accounts_with_labels(exclude_local=True, connected_only=False,
    414 419
         accounts.sort(key=lambda xs: str.lower(xs[1]))
    
    415 420
         return accounts
    
    416 421
     
    
    417
    -def get_account_label(account):
    
    422
    +
    
    423
    +def get_account_label(account: str) -> str:
    
    418 424
         return settings.get_account_setting(account, 'account_label') or account
    
    419 425
     
    
    420
    -def account_is_zeroconf(account):
    
    426
    +
    
    427
    +def account_is_zeroconf(account: str) -> bool:
    
    421 428
         return connections[account].is_zeroconf
    
    422 429
     
    
    423
    -def account_supports_private_storage(account):
    
    430
    +
    
    431
    +def account_supports_private_storage(account: str) -> bool:
    
    424 432
         # If Delimiter module is not available we can assume
    
    425 433
         # Private Storage is not available
    
    426 434
         return connections[account].get_module('Delimiter').available
    
    427 435
     
    
    428
    -def account_is_connected(account):
    
    436
    +
    
    437
    +def account_is_connected(account: str) -> bool:
    
    429 438
         if account not in connections:
    
    430 439
             return False
    
    431 440
         return (connections[account].state.is_connected or
    
    432 441
                 connections[account].state.is_available)
    
    433 442
     
    
    434
    -def account_is_available(account):
    
    443
    +
    
    444
    +def account_is_available(account: str) -> bool:
    
    435 445
         if account not in connections:
    
    436 446
             return False
    
    437 447
         return connections[account].state.is_available
    
    438 448
     
    
    439
    -def account_is_disconnected(account):
    
    449
    +
    
    450
    +def account_is_disconnected(account: str) -> bool:
    
    440 451
         return not account_is_connected(account)
    
    441 452
     
    
    442
    -def zeroconf_is_connected():
    
    453
    +
    
    454
    +def zeroconf_is_connected() -> bool:
    
    443 455
         return account_is_connected(ZEROCONF_ACC_NAME) and \
    
    444 456
                 settings.get_account_setting(ZEROCONF_ACC_NAME, 'is_zeroconf')
    
    445 457
     
    
    446
    -def in_groupchat(account, room_jid):
    
    447
    -    room_jid = str(room_jid)
    
    448
    -    if room_jid not in gc_connected[account]:
    
    449
    -        return False
    
    450
    -    return gc_connected[account][room_jid]
    
    451 458
     
    
    452
    -def get_transport_name_from_jid(jid, use_config_setting=True):
    
    459
    +def get_transport_name_from_jid(
    
    460
    +        jid: str,
    
    461
    +        use_config_setting: bool = True) -> Optional[str]:
    
    462
    +
    
    453 463
         """
    
    454 464
         Returns 'gg', 'irc' etc
    
    455 465
     
    
    ... ... @@ -483,13 +493,14 @@ def get_transport_name_from_jid(jid, use_config_setting=True):
    483 493
             return 'facebook'
    
    484 494
         return None
    
    485 495
     
    
    486
    -def jid_is_transport(jid):
    
    496
    +def jid_is_transport(jid: str) -> bool:
    
    487 497
         # if not '@' or '@' starts the jid then it is transport
    
    488 498
         if jid.find('@') <= 0:
    
    489 499
             return True
    
    490 500
         return False
    
    491 501
     
    
    492
    -def get_jid_from_account(account_name):
    
    502
    +
    
    503
    +def get_jid_from_account(account_name: str) -> str:
    
    493 504
         """
    
    494 505
         Return the jid we use in the given account
    
    495 506
         """
    
    ... ... @@ -498,12 +509,14 @@ def get_jid_from_account(account_name):
    498 509
         jid = name + '@' + hostname
    
    499 510
         return jid
    
    500 511
     
    
    501
    -def get_account_from_jid(jid):
    
    512
    +
    
    513
    +def get_account_from_jid(jid: str) -> Optional[str]:
    
    502 514
         for account in settings.get_accounts():
    
    503 515
             if jid == get_jid_from_account(account):
    
    504 516
                 return account
    
    505 517
     
    
    506
    -def get_hostname_from_account(account_name, use_srv=False):
    
    518
    +
    
    519
    +def get_hostname_from_account(account_name: str, use_srv: bool = False) -> str:
    
    507 520
         """
    
    508 521
         Returns hostname (if custom hostname is used, that is returned)
    
    509 522
         """
    
    ... ... @@ -513,33 +526,32 @@ def get_hostname_from_account(account_name, use_srv=False):
    513 526
             return settings.get_account_setting(account_name, 'custom_host')
    
    514 527
         return settings.get_account_setting(account_name, 'hostname')
    
    515 528
     
    
    516
    -def get_notification_image_prefix(jid):
    
    529
    +
    
    530
    +def get_notification_image_prefix(jid: str) -> str:
    
    517 531
         """
    
    518 532
         Returns the prefix for the notification images
    
    519 533
         """
    
    520 534
         transport_name = get_transport_name_from_jid(jid)
    
    521
    -    if transport_name in ('icq', 'facebook'):
    
    535
    +    if transport_name in ['icq', 'facebook']:
    
    522 536
             prefix = transport_name
    
    523 537
         else:
    
    524 538
             prefix = 'jabber'
    
    525 539
         return prefix
    
    526 540
     
    
    527 541
     
    
    528
    -def get_recent_groupchats(account):
    
    542
    +def get_recent_groupchats(account: str) -> list[RecentGroupchat]:
    
    529 543
         recent_groupchats = settings.get_account_setting(
    
    530 544
             account, 'recent_groupchats').split()
    
    531 545
     
    
    532
    -    RecentGroupchat = namedtuple('RecentGroupchat',
    
    533
    -                                 ['room', 'server', 'nickname'])
    
    534
    -
    
    535
    -    recent_list = []
    
    546
    +    recent_list: list[RecentGroupchat] = []
    
    536 547
         for groupchat in recent_groupchats:
    
    537 548
             jid = nbxmpp.JID.from_string(groupchat)
    
    538 549
             recent = RecentGroupchat(jid.localpart, jid.domain, jid.resource)
    
    539 550
             recent_list.append(recent)
    
    540 551
         return recent_list
    
    541 552
     
    
    542
    -def add_recent_groupchat(account, room_jid, nickname):
    
    553
    +
    
    554
    +def add_recent_groupchat(account: str, room_jid: str, nickname: str) -> None:
    
    543 555
         recent = settings.get_account_setting(
    
    544 556
             account, 'recent_groupchats').split()
    
    545 557
         full_jid = room_jid + '/' + nickname
    
    ... ... @@ -551,7 +563,8 @@ def add_recent_groupchat(account, room_jid, nickname):
    551 563
         config_value = ' '.join(recent)
    
    552 564
         settings.set_account_setting(account, 'recent_groupchats', config_value)
    
    553 565
     
    
    554
    -def get_priority(account, show):
    
    566
    +
    
    567
    +def get_priority(account: str, show: str) -> int:
    
    555 568
         """
    
    556 569
         Return the priority an account must have
    
    557 570
         """
    
    ... ... @@ -569,23 +582,27 @@ def get_priority(account, show):
    569 582
             prio = 127
    
    570 583
         return prio
    
    571 584
     
    
    572
    -def log(domain):
    
    585
    +
    
    586
    +def log(domain: str) -> logging.Logger:
    
    573 587
         if domain != 'gajim':
    
    574 588
             domain = 'gajim.%s' % domain
    
    575 589
         return logging.getLogger(domain)
    
    576 590
     
    
    577
    -def prefers_app_menu():
    
    591
    +
    
    592
    +def prefers_app_menu() -> bool:
    
    578 593
         if sys.platform == 'darwin':
    
    579 594
             return True
    
    580 595
         if sys.platform == 'win32':
    
    581 596
             return False
    
    582 597
         return app.prefers_app_menu()
    
    583 598
     
    
    584
    -def load_css_config():
    
    599
    +
    
    600
    +def load_css_config() -> None:
    
    585 601
         global css_config
    
    586 602
         from gajim.gui.css_config import CSSConfig
    
    587 603
         css_config = CSSConfig()
    
    588 604
     
    
    605
    +
    
    589 606
     def set_debug_mode(enable: bool) -> None:
    
    590 607
         debug_folder = configpaths.get('DEBUG')
    
    591 608
         debug_enabled = debug_folder / 'debug-enabled'
    
    ... ... @@ -595,11 +612,13 @@ def set_debug_mode(enable: bool) -> None:
    595 612
             if debug_enabled.exists():
    
    596 613
                 debug_enabled.unlink()
    
    597 614
     
    
    615
    +
    
    598 616
     def get_debug_mode() -> bool:
    
    599 617
         debug_folder = configpaths.get('DEBUG')
    
    600 618
         debug_enabled = debug_folder / 'debug-enabled'
    
    601 619
         return debug_enabled.exists()
    
    602 620
     
    
    621
    +
    
    603 622
     def get_stored_bob_data(algo_hash: str) -> Optional[bytes]:
    
    604 623
         try:
    
    605 624
             return bob_cache[algo_hash]
    
    ... ... @@ -616,7 +635,7 @@ def register_task(self, task):
    616 635
         _tasks[id(self)].append(task)
    
    617 636
     
    
    618 637
     
    
    619
    -def remove_task(task, id_):
    
    638
    +def remove_task(task, id_) -> None:
    
    620 639
         try:
    
    621 640
             _tasks[id_].remove(task)
    
    622 641
         except Exception:
    
    ... ... @@ -626,7 +645,7 @@ def remove_task(task, id_):
    626 645
                 del _tasks[id_]
    
    627 646
     
    
    628 647
     
    
    629
    -def cancel_tasks(obj):
    
    648
    +def cancel_tasks(obj: Any) -> None:
    
    630 649
         id_ = id(obj)
    
    631 650
         if id_ not in _tasks:
    
    632 651
             return
    
    ... ... @@ -636,9 +655,10 @@ def cancel_tasks(obj):
    636 655
             task.cancel()
    
    637 656
     
    
    638 657
     
    
    639
    -def check_finalize(obj):
    
    658
    +def check_finalize(obj: Any) -> None:
    
    640 659
         if 'GAJIM_LEAK' not in os.environ:
    
    641 660
             return
    
    661
    +
    
    642 662
         name = obj.__class__.__name__
    
    643 663
         logger = logging.getLogger('gajim.leak')
    
    644 664
         finalizer = weakref.finalize(obj, logger.info, f'{name} has been finalized')
    

  • gajim/common/helpers.py
    ... ... @@ -25,8 +25,10 @@
    25 25
     # You should have received a copy of the GNU General Public License
    
    26 26
     # along with Gajim. If not, see <http://www.gnu.org/licenses/>.
    
    27 27
     
    
    28
    -from typing import Any  # pylint: disable=unused-import
    
    29
    -from typing import Dict  # pylint: disable=unused-import
    
    28
    +from __future__ import annotations
    
    29
    +
    
    30
    +from typing import Any
    
    31
    +from typing import Dict
    
    30 32
     from typing import Optional
    
    31 33
     from typing import List
    
    32 34
     from typing import Tuple
    
    ... ... @@ -91,6 +93,7 @@
    91 93
     from gajim.common.regex import INVALID_XML_CHARS_REGEX
    
    92 94
     from gajim.common.regex import STH_AT_STH_DOT_STH_REGEX
    
    93 95
     from gajim.common.structs import URI
    
    96
    +from gajim.common import types
    
    94 97
     
    
    95 98
     HAS_PYWIN32 = False
    
    96 99
     if os.name == 'nt':
    
    ... ... @@ -1221,53 +1224,56 @@ def get_tls_error_phrase(tls_error: Gio.TlsCertificateFlags) -> str:
    1221 1224
     
    
    1222 1225
     
    
    1223 1226
     class Observable:
    
    1224
    -    def __init__(self, log_=None):
    
    1227
    +    def __init__(self, log_: Optional[logging.Logger] = None):
    
    1225 1228
             self._log = log_
    
    1226
    -        self._callbacks = defaultdict(lambda: defaultdict(list))
    
    1229
    +        self._callbacks: types.ObservableCbDict = defaultdict(list)
    
    1227 1230
     
    
    1228
    -    def disconnect_signals(self):
    
    1229
    -        self._callbacks = defaultdict(lambda: defaultdict(list))
    
    1231
    +    def disconnect_signals(self) -> None:
    
    1232
    +        self._callbacks = defaultdict(list)
    
    1230 1233
     
    
    1231
    -    def disconnect_all_from_obj(self, object_):
    
    1232
    -        for qualifiers in self._callbacks.values():
    
    1233
    -            for handlers in qualifiers.values():
    
    1234
    -                for handler in list(handlers):
    
    1235
    -                    func = handler()
    
    1236
    -                    if func is None or func.__self__ is object_:
    
    1237
    -                        handlers.remove(handler)
    
    1234
    +    def disconnect_all_from_obj(self, obj: Any) -> None:
    
    1235
    +        for handlers in self._callbacks.values():
    
    1236
    +            for handler in list(handlers):
    
    1237
    +                func = handler()
    
    1238
    +                if func is None or func.__self__ is obj:
    
    1239
    +                    handlers.remove(handler)
    
    1238 1240
     
    
    1239
    -    def disconnect(self, *args):
    
    1240
    -        self.disconnect_all_from_obj(*args)
    
    1241
    +    def disconnect(self, obj: Any) -> None:
    
    1242
    +        self.disconnect_all_from_obj(obj)
    
    1241 1243
     
    
    1242
    -    def connect_signal(self, signal_name, func, qualifiers=None):
    
    1244
    +    def connect_signal(self,
    
    1245
    +                       signal_name: str,
    
    1246
    +                       func: types.AnyCallableT) -> None:
    
    1243 1247
             if not inspect.ismethod(func):
    
    1244 1248
                 raise ValueError('Only bound methods allowed')
    
    1245 1249
     
    
    1246 1250
             weak_func = weakref.WeakMethod(func)
    
    1247 1251
     
    
    1248
    -        self._callbacks[signal_name][qualifiers].append(weak_func)
    
    1252
    +        self._callbacks[signal_name].append(weak_func)
    
    1249 1253
     
    
    1250
    -    def connect(self, *args, **kwargs):
    
    1251
    -        self.connect_signal(*args, **kwargs)
    
    1254
    +    def connect(self,
    
    1255
    +                signal_name: str,
    
    1256
    +                func: types.AnyCallableT) -> None:
    
    1257
    +        self.connect_signal(signal_name, func)
    
    1252 1258
     
    
    1253
    -    def multi_connect(self, signal_dict):
    
    1259
    +    def multi_connect(self, signal_dict: dict[str, types.AnyCallableT]):
    
    1254 1260
             for signal_name, func in signal_dict.items():
    
    1255 1261
                 self.connect_signal(signal_name, func)
    
    1256 1262
     
    
    1257
    -    def notify(self, signal_name, *args, qualifiers=None, **kwargs):
    
    1263
    +    def notify(self, signal_name: str, *args: Any, **kwargs: Any):
    
    1258 1264
             signal_callbacks = self._callbacks.get(signal_name)
    
    1259
    -        if signal_callbacks is None:
    
    1265
    +        if not signal_callbacks:
    
    1260 1266
                 return
    
    1261 1267
     
    
    1262 1268
             if self._log is not None:
    
    1263 1269
                 self._log.info('Signal: %s', signal_name)
    
    1264 1270
     
    
    1265
    -        callbacks = signal_callbacks.get(qualifiers, [])
    
    1266
    -        for func in list(callbacks):
    
    1267
    -            if func() is None:
    
    1268
    -                self._callbacks[signal_name][qualifiers].remove(func)
    
    1271
    +        for weak_method in list(signal_callbacks):
    
    1272
    +            func = weak_method()
    
    1273
    +            if func is None:
    
    1274
    +                self._callbacks[signal_name].remove(weak_method)
    
    1269 1275
                     continue
    
    1270
    -            func()(self, signal_name, *args, **kwargs)
    
    1276
    +            func(self, signal_name, *args, **kwargs)
    
    1271 1277
     
    
    1272 1278
     
    
    1273 1279
     def write_file_async(path, data, callback, user_data=None):
    

  • gajim/common/modules/base.py
    ... ... @@ -12,9 +12,10 @@
    12 12
     # You should have received a copy of the GNU General Public License
    
    13 13
     # along with Gajim.  If not, see <http://www.gnu.org/licenses/>.
    
    14 14
     
    
    15
    +from __future__ import annotations
    
    16
    +
    
    15 17
     from typing import Any
    
    16
    -from typing import Dict
    
    17
    -from typing import List
    
    18
    +from typing import Optional
    
    18 19
     
    
    19 20
     import logging
    
    20 21
     from functools import partial
    
    ... ... @@ -23,6 +24,7 @@
    23 24
     from nbxmpp.namespaces import Namespace
    
    24 25
     from nbxmpp.structs import StanzaHandler
    
    25 26
     
    
    27
    +from gajim.common import types
    
    26 28
     from gajim.common import app
    
    27 29
     from gajim.common.nec import EventHelper
    
    28 30
     from gajim.common.modules.util import LogAdapter
    
    ... ... @@ -31,18 +33,23 @@
    31 33
     class BaseModule(EventHelper):
    
    32 34
     
    
    33 35
         _nbxmpp_extends = ''
    
    34
    -    _nbxmpp_methods = []  # type: List[str]
    
    36
    +    _nbxmpp_methods: list[str] = []
    
    37
    +
    
    38
    +    def __init__(self,
    
    39
    +                 con: types.Client,
    
    40
    +                 *args: Any,
    
    41
    +                 plugin: bool = False,
    
    42
    +                 **kwargs: Any):
    
    35 43
     
    
    36
    -    def __init__(self, con, *args, plugin=False, **kwargs):
    
    37 44
             EventHelper.__init__(self)
    
    38 45
             self._con = con
    
    39
    -        self._account = con.name
    
    46
    +        self._account = con.account
    
    40 47
             self._log = self._set_logger(plugin)
    
    41
    -        self._nbxmpp_callbacks: Dict[str, Any] = {}
    
    42
    -        self._stored_publish = None  # type: Callable
    
    43
    -        self.handlers: List[StanzaHandler] = []
    
    48
    +        self._nbxmpp_callbacks: dict[str, Any] = {}
    
    49
    +        self._stored_publish: Optional[types.AnyCallableT] = None
    
    50
    +        self.handlers: list[StanzaHandler] = []
    
    44 51
     
    
    45
    -    def _set_logger(self, plugin):
    
    52
    +    def _set_logger(self, plugin: bool) -> LogAdapter:
    
    46 53
             logger_name = 'gajim.c.m.%s'
    
    47 54
             if plugin:
    
    48 55
                 logger_name = 'gajim.p.%s'
    
    ... ... @@ -50,7 +57,7 @@ def _set_logger(self, plugin):
    50 57
             logger = logging.getLogger(logger_name)
    
    51 58
             return LogAdapter(logger, {'account': self._account})
    
    52 59
     
    
    53
    -    def __getattr__(self, key):
    
    60
    +    def __getattr__(self, key: str) -> Any:
    
    54 61
             if key not in self._nbxmpp_methods:
    
    55 62
                 raise AttributeError(
    
    56 63
                     "attribute '%s' is neither part of object '%s' "
    
    ... ... @@ -68,7 +75,7 @@ def __getattr__(self, key):
    68 75
                 return getattr(module, key)
    
    69 76
             return partial(getattr(module, key), callback=callback)
    
    70 77
     
    
    71
    -    def _nbxmpp(self, module_name=None):
    
    78
    +    def _nbxmpp(self, module_name: Optional[str] = None):
    
    72 79
             if not app.account_is_connected(self._account):
    
    73 80
                 self._log.warning('Account not connected, can’t use nbxmpp method')
    
    74 81
                 return Mock()
    
    ... ... @@ -80,22 +87,22 @@ def _nbxmpp(self, module_name=None):
    80 87
         def _register_callback(self, method, callback):
    
    81 88
             self._nbxmpp_callbacks[method] = callback
    
    82 89
     
    
    83
    -    def _register_pubsub_handler(self, callback):
    
    90
    +    def _register_pubsub_handler(self, callback: types.AnyCallableT):
    
    84 91
             handler = StanzaHandler(name='message',
    
    85 92
                                     callback=callback,
    
    86 93
                                     ns=Namespace.PUBSUB_EVENT,
    
    87 94
                                     priority=49)
    
    88 95
             self.handlers.append(handler)
    
    89 96
     
    
    90
    -    def send_stored_publish(self):
    
    97
    +    def send_stored_publish(self) -> None:
    
    91 98
             if self._stored_publish is None:
    
    92 99
                 return
    
    93 100
             self._log.info('Send stored publish')
    
    94 101
             self._stored_publish()  # pylint: disable=not-callable
    
    95 102
     
    
    96
    -    def _get_contact(self, jid, groupchat=False):
    
    103
    +    def _get_contact(self, jid, groupchat: bool = False):
    
    97 104
             return self._con.get_module('Contacts').get_contact(
    
    98 105
                 jid, groupchat=groupchat)
    
    99 106
     
    
    100
    -    def cleanup(self):
    
    107
    +    def cleanup(self) -> None:
    
    101 108
             self.unregister_events()

  • gajim/common/modules/muc.py
    ... ... @@ -814,7 +814,8 @@ def _on_invite_or_decline(self, _con, _stanza, properties):
    814 814
     
    
    815 815
                 self._log.info('Invite from: %s, to: %s', data.from_, data.muc)
    
    816 816
     
    
    817
    -            if app.in_groupchat(self._account, data.muc):
    
    817
    +            contact = self._get_contact(data.muc, groupchat=True)
    
    818
    +            if contact.is_joined:
    
    818 819
                     # We are already in groupchat. Ignore invitation
    
    819 820
                     self._log.info('We are already in this room')
    
    820 821
                     raise nbxmpp.NodeProcessed
    

  • gajim/common/types.py
    ... ... @@ -14,6 +14,7 @@
    14 14
     
    
    15 15
     # Types for typechecking
    
    16 16
     
    
    17
    +from typing import Any
    
    17 18
     from typing import Callable
    
    18 19
     from typing import Dict
    
    19 20
     from typing import List
    
    ... ... @@ -23,6 +24,7 @@
    23 24
     from typing import TYPE_CHECKING
    
    24 25
     
    
    25 26
     from pathlib import Path
    
    27
    +import weakref
    
    26 28
     
    
    27 29
     from gi.repository import GdkPixbuf
    
    28 30
     
    
    ... ... @@ -53,7 +55,6 @@
    53 55
     ConnectionT = Union['Client', 'ConnectionZeroconf']
    
    54 56
     ContactsT = Union['Contact', 'GC_Contact']
    
    55 57
     ContactT = Union['Contact']
    
    56
    -LegacyContactsAPIT = Union['LegacyContactsAPI']
    
    57 58
     CSSConfigT = Union['CSSConfig']
    
    58 59
     
    
    59 60
     # PEP
    
    ... ... @@ -75,3 +76,6 @@
    75 76
     BookmarksDict = Dict[JID, BookmarkData]
    
    76 77
     
    
    77 78
     GdkPixbufType = Union[GdkPixbuf.Pixbuf, GdkPixbuf.PixbufAnimation]
    
    79
    +
    
    80
    +AnyCallableT = Callable[..., Any]
    
    81
    +ObservableCbDict = dict[str, list[weakref.WeakMethod[AnyCallableT]]]

  • gajim/gui_interface.py
    ... ... @@ -159,8 +159,6 @@ def __init__(self):
    159 159
                     'infos': {},
    
    160 160
                     'disco': {}
    
    161 161
                 }
    
    162
    -            app.groups[acc] = {}
    
    163
    -            app.gc_connected[acc] = {}
    
    164 162
                 app.automatic_rooms[acc] = {}
    
    165 163
                 app.newly_added[acc] = []
    
    166 164
                 app.to_be_removed[acc] = []
    
    ... ... @@ -781,8 +779,6 @@ def enable_account(self, account: str) -> None:
    781 779
                 'disco': {}
    
    782 780
             }
    
    783 781
     
    
    784
    -        app.groups[account] = {}
    
    785
    -        app.gc_connected[account] = {}
    
    786 782
             app.automatic_rooms[account] = {}
    
    787 783
             app.newly_added[account] = []
    
    788 784
             app.to_be_removed[account] = []
    
    ... ... @@ -830,8 +826,6 @@ def disable_account(self, account: str) -> None:
    830 826
             del self.instances[account]
    
    831 827
             del app.nicks[account]
    
    832 828
             del app.block_signed_in_notifications[account]
    
    833
    -        del app.groups[account]
    
    834
    -        del app.gc_connected[account]
    
    835 829
             del app.automatic_rooms[account]
    
    836 830
             del app.to_be_removed[account]
    
    837 831
             del app.newly_added[account]