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

Commits:

4 changed files:

Changes:

  • gajim/common/helpers.py
    ... ... @@ -27,6 +27,10 @@
    27 27
     
    
    28 28
     from typing import Any  # pylint: disable=unused-import
    
    29 29
     from typing import Dict  # pylint: disable=unused-import
    
    30
    +from typing import Optional
    
    31
    +from typing import List
    
    32
    +from typing import Tuple
    
    33
    +from typing import Union
    
    30 34
     
    
    31 35
     import sys
    
    32 36
     import re
    
    ... ... @@ -62,6 +66,8 @@
    62 66
     from nbxmpp.const import Role
    
    63 67
     from nbxmpp.const import ConnectionProtocol
    
    64 68
     from nbxmpp.const import ConnectionType
    
    69
    +from nbxmpp.const import Affiliation
    
    70
    +from nbxmpp.const import Role
    
    65 71
     from nbxmpp.structs import ProxyData
    
    66 72
     from nbxmpp.protocol import JID
    
    67 73
     from nbxmpp.protocol import InvalidJid
    
    ... ... @@ -163,12 +169,13 @@ def parse_resource(resource):
    163 169
         except UnicodeError:
    
    164 170
             raise InvalidFormat('Invalid character in resource.')
    
    165 171
     
    
    166
    -def windowsify(word):
    
    172
    +def windowsify(word: str) -> str:
    
    167 173
         if os.name == 'nt':
    
    168 174
             return word.capitalize()
    
    169 175
         return word
    
    170 176
     
    
    171
    -def get_uf_show(show, use_mnemonic=False):
    
    177
    +def get_uf_show(show: Union[ShowConstant, str],
    
    178
    +                use_mnemonic: bool = False) -> str:
    
    172 179
         """
    
    173 180
         Return a userfriendly string for dnd/xa/chat and make all strings
    
    174 181
         translatable
    
    ... ... @@ -219,7 +226,7 @@ def get_uf_show(show, use_mnemonic=False):
    219 226
             uf_show = Q_('?contact has status:Has errors')
    
    220 227
         return uf_show
    
    221 228
     
    
    222
    -def get_uf_sub(sub):
    
    229
    +def get_uf_sub(sub: str) -> str:
    
    223 230
         if sub == 'none':
    
    224 231
             uf_sub = Q_('?Subscription we already have:None')
    
    225 232
         elif sub == 'to':
    
    ... ... @@ -233,7 +240,7 @@ def get_uf_sub(sub):
    233 240
     
    
    234 241
         return uf_sub
    
    235 242
     
    
    236
    -def get_uf_ask(ask):
    
    243
    +def get_uf_ask(ask: Union[str, None]) -> str:
    
    237 244
         if ask is None:
    
    238 245
             uf_ask = Q_('?Ask (for Subscription):None')
    
    239 246
         elif ask == 'subscribe':
    
    ... ... @@ -243,7 +250,7 @@ def get_uf_ask(ask):
    243 250
     
    
    244 251
         return uf_ask
    
    245 252
     
    
    246
    -def get_uf_role(role, plural=False):
    
    253
    +def get_uf_role(role: Union[Role, str], plural: bool = False) -> str:
    
    247 254
         ''' plural determines if you get Moderators or Moderator'''
    
    248 255
         if not isinstance(role, str):
    
    249 256
             role = role.value
    
    ... ... @@ -267,7 +274,8 @@ def get_uf_role(role, plural=False):
    267 274
                 role_name = _('Visitor')
    
    268 275
         return role_name
    
    269 276
     
    
    270
    -def get_uf_affiliation(affiliation, plural=False):
    
    277
    +def get_uf_affiliation(affiliation: Union[Affiliation, str],
    
    278
    +                       plural: bool = False) -> str:
    
    271 279
         '''Get a nice and translated affilition for muc'''
    
    272 280
         if not isinstance(affiliation, str):
    
    273 281
             affiliation = affiliation.value
    
    ... ... @@ -319,7 +327,7 @@ def get_sorted_keys(adict):
    319 327
         keys = sorted(adict.keys())
    
    320 328
         return keys
    
    321 329
     
    
    322
    -def to_one_line(msg):
    
    330
    +def to_one_line(msg: str) -> str:
    
    323 331
         msg = msg.replace('\\', '\\\\')
    
    324 332
         msg = msg.replace('\n', '\\n')
    
    325 333
         # s1 = 'test\ntest\\ntest'
    
    ... ... @@ -329,7 +337,7 @@ def to_one_line(msg):
    329 337
         # 'test\\ntest\\\\ntest'
    
    330 338
         return msg
    
    331 339
     
    
    332
    -def from_one_line(msg):
    
    340
    +def from_one_line(msg: str) -> str:
    
    333 341
         # (?<!\\) is a lookbehind assertion which asks anything but '\'
    
    334 342
         # to match the regexp that follows it
    
    335 343
     
    
    ... ... @@ -344,7 +352,7 @@ def from_one_line(msg):
    344 352
         # 'test\ntest\\ntest'
    
    345 353
         return msg
    
    346 354
     
    
    347
    -def get_uf_chatstate(chatstate):
    
    355
    +def get_uf_chatstate(chatstate: str) -> str:
    
    348 356
         """
    
    349 357
         Remove chatstate jargon and returns user friendly messages
    
    350 358
         """
    
    ... ... @@ -393,7 +401,7 @@ def get_file_path_from_dnd_dropped_uri(uri):
    393 401
             path = path[5:] # 5 is len('file:')
    
    394 402
         return path
    
    395 403
     
    
    396
    -def sanitize_filename(filename):
    
    404
    +def sanitize_filename(filename: str) -> str:
    
    397 405
         """
    
    398 406
         Make sure the filename we will write does contain only acceptable and latin
    
    399 407
         characters, and is not too long (in that case hash it)
    
    ... ... @@ -413,7 +421,8 @@ def sanitize_filename(filename):
    413 421
     
    
    414 422
         return filename
    
    415 423
     
    
    416
    -def reduce_chars_newlines(text, max_chars=0, max_lines=0):
    
    424
    +def reduce_chars_newlines(text: str, max_chars: int = 0,
    
    425
    +                          max_lines: int = 0) -> str:
    
    417 426
         """
    
    418 427
         Cut the chars after 'max_chars' on each line and show only the first
    
    419 428
         'max_lines'
    
    ... ... @@ -454,7 +463,8 @@ def get_contact_dict_for_account(account):
    454 463
         return contacts_dict
    
    455 464
     
    
    456 465
     
    
    457
    -def play_sound(sound_event, account, force=False):
    
    466
    +def play_sound(sound_event: str, account: str,
    
    467
    +               force: bool = False) -> None:
    
    458 468
         if sound_event is None:
    
    459 469
             return
    
    460 470
         if force or allow_sound_notification(account, sound_event):
    
    ... ... @@ -547,7 +557,7 @@ def play_sound_file(path_to_soundfile):
    547 557
             except GLib.Error as error:
    
    548 558
                 log.error('Could not play sound: %s', error.message)
    
    549 559
     
    
    550
    -def get_connection_status(account):
    
    560
    +def get_connection_status(account: str) -> str:
    
    551 561
         con = app.connections[account]
    
    552 562
         if con.state.is_reconnect_scheduled:
    
    553 563
             return 'error'
    
    ... ... @@ -559,7 +569,7 @@ def get_connection_status(account):
    559 569
             return 'offline'
    
    560 570
         return con.status
    
    561 571
     
    
    562
    -def get_global_show():
    
    572
    +def get_global_show() -> str:
    
    563 573
         maxi = 0
    
    564 574
         for account in app.connections:
    
    565 575
             if not app.settings.get_account_setting(account,
    
    ... ... @@ -571,7 +581,7 @@ def get_global_show():
    571 581
                 maxi = index
    
    572 582
         return SHOW_LIST[maxi]
    
    573 583
     
    
    574
    -def get_global_status_message():
    
    584
    +def get_global_status_message() -> str:
    
    575 585
         maxi = 0
    
    576 586
         for account in app.connections:
    
    577 587
             if not app.settings.get_account_setting(account,
    
    ... ... @@ -584,7 +594,7 @@ def get_global_status_message():
    584 594
                 status_message = app.connections[account].status_message
    
    585 595
         return status_message
    
    586 596
     
    
    587
    -def statuses_unified():
    
    597
    +def statuses_unified() -> bool:
    
    588 598
         """
    
    589 599
         Test if all statuses are the same
    
    590 600
         """
    
    ... ... @@ -628,7 +638,7 @@ def remove_invalid_xml_chars(string_):
    628 638
             string_ = re.sub(INVALID_XML_CHARS_REGEX, '', string_)
    
    629 639
         return string_
    
    630 640
     
    
    631
    -def get_random_string(count=16):
    
    641
    +def get_random_string(count: int = 16) -> str:
    
    632 642
         """
    
    633 643
         Create random string of count length
    
    634 644
     
    
    ... ... @@ -638,7 +648,7 @@ def get_random_string(count=16):
    638 648
         return ''.join(random.choice(allowed) for char in range(count))
    
    639 649
     
    
    640 650
     @functools.lru_cache(maxsize=1)
    
    641
    -def get_os_info():
    
    651
    +def get_os_info() -> str:
    
    642 652
         info = 'N/A'
    
    643 653
         if sys.platform in ('win32', 'darwin'):
    
    644 654
             info = f'{platform.system()} {platform.release()}'
    
    ... ... @@ -680,7 +690,7 @@ def message_needs_highlight(text, nickname, own_jid):
    680 690
         return False
    
    681 691
     
    
    682 692
     
    
    683
    -def allow_showing_notification(account):
    
    693
    +def allow_showing_notification(account: str) -> bool:
    
    684 694
         if not app.settings.get('show_notifications'):
    
    685 695
             return False
    
    686 696
         if app.settings.get('show_notifications_away'):
    
    ... ... @@ -690,7 +700,7 @@ def allow_showing_notification(account):
    690 700
         return False
    
    691 701
     
    
    692 702
     
    
    693
    -def allow_sound_notification(account, sound_event):
    
    703
    +def allow_sound_notification(account: str, sound_event: str) -> bool:
    
    694 704
         if not app.settings.get('sounds_on'):
    
    695 705
             return False
    
    696 706
         client = app.get_client(account)
    
    ... ... @@ -730,23 +740,25 @@ def jid_is_blocked(account, jid):
    730 740
         con = app.connections[account]
    
    731 741
         return jid in con.get_module('Blocking').blocked
    
    732 742
     
    
    733
    -def get_subscription_request_msg(account=None):
    
    734
    -    s = app.settings.get_account_setting(account, 'subscription_request_msg')
    
    735
    -    if s:
    
    736
    -        return s
    
    737
    -    s = _('I would like to add you to my contact list.')
    
    738
    -    if account:
    
    739
    -        s = _('Hello, I am $name.') + ' ' + s
    
    740
    -        s = Template(s).safe_substitute({'name': app.nicks[account]})
    
    741
    -        return s
    
    742
    -
    
    743
    -def get_user_proxy(account):
    
    743
    +def get_subscription_request_msg(account: Optional[str] = None) -> str:
    
    744
    +    message = _('I would like to add you to my contact list.')
    
    745
    +    if account is None:
    
    746
    +        return message
    
    747
    +    
    
    748
    +    message = app.settings.get_account_setting(account, 'subscription_request_msg')
    
    749
    +    if message:
    
    750
    +        return message
    
    751
    +
    
    752
    +    message = _('Hello, I am $name. %s') % message
    
    753
    +    return Template(message).safe_substitute({'name': app.nicks[account]})
    
    754
    +
    
    755
    +def get_user_proxy(account: str) -> Optional[ProxyData]:
    
    744 756
         proxy_name = app.settings.get_account_setting(account, 'proxy')
    
    745 757
         if not proxy_name:
    
    746 758
             return None
    
    747 759
         return get_proxy(proxy_name)
    
    748 760
     
    
    749
    -def get_proxy(proxy_name):
    
    761
    +def get_proxy(proxy_name: str) -> Optional[ProxyData]:
    
    750 762
         try:
    
    751 763
             settings = app.settings.get_proxy_settings(proxy_name)
    
    752 764
         except ValueError:
    
    ... ... @@ -926,7 +938,7 @@ def func_wrapper(self, *args, **kwargs):
    926 938
         return func_wrapper
    
    927 939
     
    
    928 940
     
    
    929
    -def parse_uri_actions(uri):
    
    941
    +def parse_uri_actions(uri: str) -> Tuple[str, Dict[str, str]]:
    
    930 942
         uri = uri[5:]
    
    931 943
         if '?' not in uri:
    
    932 944
             return 'message', {'jid': uri}
    
    ... ... @@ -948,7 +960,7 @@ def parse_uri_actions(uri):
    948 960
         return action, data
    
    949 961
     
    
    950 962
     
    
    951
    -def parse_uri(uri):
    
    963
    +def parse_uri(uri: str) -> URI:
    
    952 964
         if uri.startswith('xmpp:'):
    
    953 965
             action, data = parse_uri_actions(uri)
    
    954 966
             try:
    
    ... ... @@ -990,7 +1002,7 @@ def parse_uri(uri):
    990 1002
     
    
    991 1003
     
    
    992 1004
     @catch_exceptions
    
    993
    -def open_uri(uri, account=None):
    
    1005
    +def open_uri(uri: Union[URI, str], account: Optional[str] = None) -> None:
    
    994 1006
         if not isinstance(uri, URI):
    
    995 1007
             uri = parse_uri(uri)
    
    996 1008
     
    
    ... ... @@ -1023,13 +1035,17 @@ def open_uri(uri, account=None):
    1023 1035
                 log.warning('Account must be specified to open XMPP uri')
    
    1024 1036
                 return
    
    1025 1037
     
    
    1038
    +        if isinstance(uri.data, dict):
    
    1039
    +            jid = uri.data['jid']
    
    1040
    +            message = uri.data.get('body')
    
    1041
    +        else:
    
    1042
    +            log.warning('Cant open URI: %s', uri)
    
    1026 1043
             if uri.action == URIAction.JOIN:
    
    1027 1044
                 app.app.activate_action(
    
    1028 1045
                     'groupchat-join',
    
    1029
    -                GLib.Variant('as', [account, uri.data['jid']]))
    
    1046
    +                GLib.Variant('as', [account, jid]))
    
    1030 1047
             elif uri.action == URIAction.MESSAGE:
    
    1031
    -            app.interface.start_chat_from_jid(account, uri.data['jid'],
    
    1032
    -                                              message=uri.data.get('body'))
    
    1048
    +            app.interface.start_chat_from_jid(account, jid, message=message)
    
    1033 1049
             else:
    
    1034 1050
                 log.warning('Cant open URI: %s', uri)
    
    1035 1051
     
    
    ... ... @@ -1080,9 +1096,8 @@ def file_is_locked(path_to_file):
    1080 1096
             return False
    
    1081 1097
     
    
    1082 1098
     
    
    1083
    -def geo_provider_from_location(lat, lon):
    
    1084
    -    return ('https://www.openstreetmap.org/?'
    
    1085
    -            'mlat=%s&mlon=%s&zoom=16') % (lat, lon)
    
    1099
    +def geo_provider_from_location(lat: str, lon: str) -> str:
    
    1100
    +    return f'https://www.openstreetmap.org/?mlat={lat}&mlon={lon}&zoom=16'
    
    1086 1101
     
    
    1087 1102
     
    
    1088 1103
     def get_resource(account):
    
    ... ... @@ -1097,7 +1112,7 @@ def get_resource(account):
    1097 1112
         return resource
    
    1098 1113
     
    
    1099 1114
     
    
    1100
    -def get_default_muc_config():
    
    1115
    +def get_default_muc_config() -> Dict[str, Union[bool, str]]:
    
    1101 1116
         return {
    
    1102 1117
             # XEP-0045 options
    
    1103 1118
             'muc#roomconfig_allowinvites': True,
    
    ... ... @@ -1182,10 +1197,10 @@ def is_role_change_allowed(self_contact, contact):
    1182 1197
         return self_contact.affiliation >= contact.affiliation
    
    1183 1198
     
    
    1184 1199
     
    
    1185
    -def get_tls_error_phrase(tls_error):
    
    1200
    +def get_tls_error_phrase(tls_error: Gio.TlsCertificateFlags) -> str:
    
    1186 1201
         phrase = GIO_TLS_ERRORS.get(tls_error)
    
    1187 1202
         if phrase is None:
    
    1188
    -        return GIO_TLS_ERRORS.get(Gio.TlsCertificateFlags.GENERIC_ERROR)
    
    1203
    +        return GIO_TLS_ERRORS[Gio.TlsCertificateFlags.GENERIC_ERROR]
    
    1189 1204
         return phrase
    
    1190 1205
     
    
    1191 1206
     
    
    ... ... @@ -1296,7 +1311,8 @@ def convert_gio_to_openssl_cert(cert):
    1296 1311
         return cert
    
    1297 1312
     
    
    1298 1313
     
    
    1299
    -def get_custom_host(account):
    
    1314
    +def get_custom_host(account: str
    
    1315
    +                    ) -> Optional[Tuple[str, ConnectionProtocol, str]]:
    
    1300 1316
         if not app.settings.get_account_setting(account, 'use_custom_host'):
    
    1301 1317
             return None
    
    1302 1318
         host = app.settings.get_account_setting(account, 'custom_host')
    
    ... ... @@ -1312,7 +1328,9 @@ def get_custom_host(account):
    1312 1328
         return (host, protocol, ConnectionType(type_))
    
    1313 1329
     
    
    1314 1330
     
    
    1315
    -def warn_about_plain_connection(account, connection_types):
    
    1331
    +def warn_about_plain_connection(account: str,
    
    1332
    +                                connection_types: List[ConnectionType]
    
    1333
    +                                ) -> bool:
    
    1316 1334
         warn = app.settings.get_account_setting(
    
    1317 1335
             account, 'confirm_unencrypted_connection')
    
    1318 1336
         for type_ in connection_types:
    
    ... ... @@ -1321,7 +1339,7 @@ def warn_about_plain_connection(account, connection_types):
    1321 1339
         return False
    
    1322 1340
     
    
    1323 1341
     
    
    1324
    -def get_idle_status_message(state, status_message):
    
    1342
    +def get_idle_status_message(state: str, status_message: str) -> str:
    
    1325 1343
         message = app.settings.get(f'auto{state}_message')
    
    1326 1344
         if not message:
    
    1327 1345
             message = status_message
    
    ... ... @@ -1349,7 +1367,7 @@ def should_log(account, jid):
    1349 1367
         return (account not in no_log_for) and (jid not in no_log_for)
    
    1350 1368
     
    
    1351 1369
     
    
    1352
    -def ask_for_status_message(status, signin=False):
    
    1370
    +def ask_for_status_message(status: str, signin: bool = False) -> bool:
    
    1353 1371
         if status is None:
    
    1354 1372
             # We try to change the message
    
    1355 1373
             return True
    

  • gajim/common/structs.py
    ... ... @@ -12,8 +12,14 @@
    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 typing import Dict
    
    16
    +from typing import NamedTuple
    
    17
    +from typing import Optional
    
    18
    +from typing import Tuple
    
    19
    +from typing import Union
    
    20
    +from typing import Any
    
    21
    +
    
    15 22
     import time
    
    16
    -from collections import namedtuple
    
    17 23
     from dataclasses import dataclass
    
    18 24
     
    
    19 25
     from nbxmpp.protocol import JID
    
    ... ... @@ -23,9 +29,14 @@
    23 29
     from gajim.common.const import MUCJoinedState
    
    24 30
     from gajim.common.const import KindConstant
    
    25 31
     from gajim.common.const import PresenceShowExt
    
    32
    +from gajim.common.const import URIType
    
    33
    +from gajim.common.const import URIAction
    
    34
    +
    
    26 35
     
    
    27
    -URI = namedtuple('URI', 'type action data')
    
    28
    -URI.__new__.__defaults__ = (None, None)  # type: ignore
    
    36
    +class URI(NamedTuple):
    
    37
    +    type: URIType
    
    38
    +    action: Optional[URIAction] = None
    
    39
    +    data: Optional[Union[Dict[str, str], str]] = None
    
    29 40
     
    
    30 41
     
    
    31 42
     class MUCData:
    

  • gajim/gtk/util.py
    ... ... @@ -44,6 +44,7 @@
    44 44
     from gi.repository import Pango
    
    45 45
     from gi.repository import GdkPixbuf
    
    46 46
     import nbxmpp
    
    47
    +from nbxmpp.structs import LocationData
    
    47 48
     import cairo
    
    48 49
     
    
    49 50
     from gajim.common import app
    
    ... ... @@ -263,7 +264,7 @@ def get_icon_name(name: str,
    263 264
         return '%s-%s' % (iconset, name)
    
    264 265
     
    
    265 266
     
    
    266
    -def load_user_iconsets():
    
    267
    +def load_user_iconsets() -> None:
    
    267 268
         iconsets_path = configpaths.get('MY_ICONSETS')
    
    268 269
         if not iconsets_path.exists():
    
    269 270
             return
    
    ... ... @@ -275,7 +276,7 @@ def load_user_iconsets():
    275 276
             _icon_theme.append_search_path(str(path))
    
    276 277
     
    
    277 278
     
    
    278
    -def get_available_iconsets():
    
    279
    +def get_available_iconsets() -> List[str]:
    
    279 280
         iconsets = []
    
    280 281
         for iconset in GajimIconSet:
    
    281 282
             iconsets.append(iconset.value)
    
    ... ... @@ -421,7 +422,7 @@ def at_the_end(widget: Gtk.ScrolledWindow) -> bool:
    421 422
     
    
    422 423
     
    
    423 424
     def get_image_button(icon_name: str, tooltip: str,
    
    424
    -                     toggle: Optional[bool] = False) -> Gtk.Button:
    
    425
    +                     toggle: bool = False) -> Gtk.Button:
    
    425 426
         if toggle:
    
    426 427
             button = Gtk.ToggleButton()
    
    427 428
             image = Gtk.Image.new_from_icon_name(icon_name, Gtk.IconSize.MENU)
    
    ... ... @@ -534,7 +535,7 @@ def func_wrapper(self, *args, **kwargs):
    534 535
         return func_wrapper
    
    535 536
     
    
    536 537
     
    
    537
    -def format_mood(mood, text):
    
    538
    +def format_mood(mood: str, text: str) -> str:
    
    538 539
         if mood is None:
    
    539 540
             return ''
    
    540 541
         mood = MOODS[mood]
    
    ... ... @@ -544,13 +545,13 @@ def format_mood(mood, text):
    544 545
         return markuptext
    
    545 546
     
    
    546 547
     
    
    547
    -def get_account_mood_icon_name(account):
    
    548
    +def get_account_mood_icon_name(account: str) -> Optional[str]:
    
    548 549
         client = app.get_client(account)
    
    549 550
         mood = client.get_module('UserMood').get_current_mood()
    
    550 551
         return f'mood-{mood.mood}' if mood is not None else mood
    
    551 552
     
    
    552 553
     
    
    553
    -def format_activity(activity, subactivity, text):
    
    554
    +def format_activity(activity: str, subactivity: str, text: str) -> str:
    
    554 555
         if subactivity in ACTIVITIES[activity]:
    
    555 556
             subactivity = ACTIVITIES[activity][subactivity]
    
    556 557
         activity = ACTIVITIES[activity]['category']
    
    ... ... @@ -564,14 +565,15 @@ def format_activity(activity, subactivity, text):
    564 565
         return markuptext
    
    565 566
     
    
    566 567
     
    
    567
    -def get_activity_icon_name(activity, subactivity=None):
    
    568
    +def get_activity_icon_name(activity: str,
    
    569
    +                           subactivity: Optional[str] = None) -> str:
    
    568 570
         icon_name = 'activity-%s' % activity.replace('_', '-')
    
    569 571
         if subactivity is not None:
    
    570 572
             icon_name += '-%s' % subactivity.replace('_', '-')
    
    571 573
         return icon_name
    
    572 574
     
    
    573 575
     
    
    574
    -def get_account_activity_icon_name(account):
    
    576
    +def get_account_activity_icon_name(account: str) -> Optional[str]:
    
    575 577
         client = app.get_client(account)
    
    576 578
         activity = client.get_module('UserActivity').get_current_activity()
    
    577 579
         if activity is None:
    
    ... ... @@ -579,7 +581,8 @@ def get_account_activity_icon_name(account):
    579 581
         return get_activity_icon_name(activity.activity, activity.subactivity)
    
    580 582
     
    
    581 583
     
    
    582
    -def format_tune(artist, _length, _rating, source, title, _track, _uri):
    
    584
    +def format_tune(artist: str, _length: str, _rating: str, source: str,
    
    585
    +                title: str, _track: str, _uri: str) -> str:
    
    583 586
         artist = GLib.markup_escape_text(artist or _('Unknown Artist'))
    
    584 587
         title = GLib.markup_escape_text(title or _('Unknown Title'))
    
    585 588
         source = GLib.markup_escape_text(source or _('Unknown Source'))
    
    ... ... @@ -591,13 +594,13 @@ def format_tune(artist, _length, _rating, source, title, _track, _uri):
    591 594
         return tune_string
    
    592 595
     
    
    593 596
     
    
    594
    -def get_account_tune_icon_name(account):
    
    597
    +def get_account_tune_icon_name(account: str) -> Optional[str]:
    
    595 598
         client = app.get_client(account)
    
    596 599
         tune = client.get_module('UserTune').get_current_tune()
    
    597 600
         return None if tune is None else 'audio-x-generic'
    
    598 601
     
    
    599 602
     
    
    600
    -def format_location(location):
    
    603
    +def format_location(location: LocationData) -> str:
    
    601 604
         location = location._asdict()
    
    602 605
         location_string = ''
    
    603 606
         for attr, value in location.items():
    
    ... ... @@ -614,7 +617,7 @@ def format_location(location):
    614 617
         return location_string.strip()
    
    615 618
     
    
    616 619
     
    
    617
    -def get_account_location_icon_name(account):
    
    620
    +def get_account_location_icon_name(account: str) -> Optional[str]:
    
    618 621
         client = app.get_client(account)
    
    619 622
         location = client.get_module('UserLocation').get_current_location()
    
    620 623
         return None if location is None else 'applications-internet'
    
    ... ... @@ -625,7 +628,7 @@ def format_eta(time_: Union[int, float]) -> str:
    625 628
         time_ = int(time_)
    
    626 629
         times['seconds'] = time_ % 60
    
    627 630
         if time_ >= 60:
    
    628
    -        time_ /= 60
    
    631
    +        time_ = int(time_ / 60)
    
    629 632
             times['minutes'] = round(time_ % 60)
    
    630 633
             return _('%(minutes)s min %(seconds)s s') % times
    
    631 634
         return _('%s s') % times['seconds']
    
    ... ... @@ -705,7 +708,7 @@ def get_css_show_class(show: str) -> str:
    705 708
         return '.gajim-status-offline'
    
    706 709
     
    
    707 710
     
    
    708
    -def add_css_to_widget(widget, css):
    
    711
    +def add_css_to_widget(widget: Any, css: str) -> None:
    
    709 712
         provider = Gtk.CssProvider()
    
    710 713
         provider.load_from_data(bytes(css.encode()))
    
    711 714
         context = widget.get_style_context()
    
    ... ... @@ -713,7 +716,7 @@ def add_css_to_widget(widget, css):
    713 716
                              Gtk.STYLE_PROVIDER_PRIORITY_USER)
    
    714 717
     
    
    715 718
     
    
    716
    -def get_pixbuf_from_data(file_data):
    
    719
    +def get_pixbuf_from_data(file_data: bytes) -> Optional[GdkPixbuf.Pixbuf]:
    
    717 720
         # TODO: This already exists in preview_helpery pixbuf_from_data
    
    718 721
         """
    
    719 722
         Get image data and returns GdkPixbuf.Pixbuf
    
    ... ... @@ -738,7 +741,7 @@ def get_pixbuf_from_data(file_data):
    738 741
             except Exception:
    
    739 742
                 log.warning('Could not use pillow to convert avatar image, '
    
    740 743
                             'image cannot be displayed', exc_info=True)
    
    741
    -            return
    
    744
    +            return None
    
    742 745
     
    
    743 746
         return pixbuf
    
    744 747
     
    
    ... ... @@ -763,7 +766,7 @@ def scale_pixbuf(pixbuf: GdkPixbuf.Pixbuf,
    763 766
                                    GdkPixbuf.InterpType.BILINEAR)
    
    764 767
     
    
    765 768
     
    
    766
    -def scale_pixbuf_from_data(data, size):
    
    769
    +def scale_pixbuf_from_data(data: bytes, size: int) -> Optional[GdkPixbuf.Pixbuf]:
    
    767 770
         pixbuf = get_pixbuf_from_data(data)
    
    768 771
         return scale_pixbuf(pixbuf, size)
    
    769 772
     
    
    ... ... @@ -821,7 +824,7 @@ def get_thumbnail_size(pixbuf: GdkPixbuf.Pixbuf, size: int) -> Tuple[int, int]:
    821 824
         return image_width, image_height
    
    822 825
     
    
    823 826
     
    
    824
    -def make_href_markup(string):
    
    827
    +def make_href_markup(string: str) -> str:
    
    825 828
         url_color = app.css_config.get_value('.gajim-url', StyleAttr.COLOR)
    
    826 829
         color = convert_rgb_to_hex(url_color)
    
    827 830
     
    

  • gajim/gui_menu_builder.py
    ... ... @@ -14,13 +14,19 @@
    14 14
     # You should have received a copy of the GNU General Public License
    
    15 15
     # along with Gajim. If not, see <http://www.gnu.org/licenses/>.
    
    16 16
     
    
    17
    -from typing import Optional
    
    17
    +from typing import Tuple
    
    18
    +from typing import List
    
    19
    +from typing import Union
    
    20
    +from typing import Any
    
    21
    +
    
    18 22
     from urllib.parse import quote
    
    19 23
     
    
    20 24
     from gi.repository import Gtk
    
    21 25
     from gi.repository import Gio
    
    22 26
     from gi.repository import GLib
    
    23 27
     
    
    28
    +from nbxmpp import JID
    
    29
    +
    
    24 30
     from gajim.common import app
    
    25 31
     from gajim.common.helpers import is_affiliation_change_allowed
    
    26 32
     from gajim.common.helpers import is_role_change_allowed
    
    ... ... @@ -35,11 +41,12 @@
    35 41
     from gajim.gui.const import ControlType
    
    36 42
     
    
    37 43
     
    
    38
    -def get_singlechat_menu(control_id, account, jid, type_):
    
    44
    +def get_singlechat_menu(control_id: str, account: str, jid: JID,
    
    45
    +                        type_: ControlType) -> Gio.Menu:
    
    39 46
         client = app.get_client(account)
    
    40 47
         is_self_contact = jid.bare == client.get_own_jid().bare
    
    41 48
     
    
    42
    -    singlechat_menu = [
    
    49
    +    singlechat_menu: List[Any] = [
    
    43 50
             (_('Send File'), [
    
    44 51
                 ('win.send-file-httpupload-', _('Upload File…')),
    
    45 52
                 ('win.send-file-jingle-', _('Send File Directly…'))
    
    ... ... @@ -117,7 +124,7 @@ def build_menu(preset):
    117 124
         return build_menu(singlechat_menu)
    
    118 125
     
    
    119 126
     
    
    120
    -def get_groupchat_menu(control_id, account, jid):
    
    127
    +def get_groupchat_menu(control_id: str, account: str, jid: str) -> Gio.Menu:
    
    121 128
         groupchat_menu = [
    
    122 129
             ('win.information-', _('Information')),
    
    123 130
             ('win.groupchat-settings-', _('Settings…')),
    
    ... ... @@ -219,7 +226,7 @@ def build_menu(preset):
    219 226
         return build_menu(account_menu)
    
    220 227
     
    
    221 228
     
    
    222
    -def build_accounts_menu():
    
    229
    +def build_accounts_menu() -> None:
    
    223 230
         menubar = app.app.get_menubar()
    
    224 231
         # Accounts Submenu
    
    225 232
         menu_position = 1
    
    ... ... @@ -259,7 +266,7 @@ def build_accounts_menu():
    259 266
     
    
    260 267
     
    
    261 268
     def get_encryption_menu(control_id: str, control_type: ControlType,
    
    262
    -                        zeroconf: Optional[bool] = False) -> Gio.Menu:
    
    269
    +                        zeroconf: bool = False) -> Gio.Menu:
    
    263 270
         menu = Gio.Menu()
    
    264 271
         menu.append(
    
    265 272
             _('Disabled'), f'win.set-encryption-{control_id}::disabled')
    
    ... ... @@ -405,7 +412,10 @@ def get_conv_uri_context_menu(account: str, uri: URI) -> Gtk.Menu:
    405 412
     
    
    406 413
             data = uri.data
    
    407 414
             if uri.type == URIType.XMPP:
    
    408
    -            data = uri.data['jid']
    
    415
    +            if isinstance(uri.data, dict):
    
    416
    +                data = uri.data['jid']
    
    417
    +            else:
    
    418
    +                data = ''
    
    409 419
     
    
    410 420
             if action in ('app.open-mail', 'app.copy-text'):
    
    411 421
                 value = GLib.Variant.new_string(data)
    
    ... ... @@ -417,7 +427,7 @@ def get_conv_uri_context_menu(account: str, uri: URI) -> Gtk.Menu:
    417 427
         return menu
    
    418 428
     
    
    419 429
     
    
    420
    -def get_roster_menu(account, jid, gateway=False):
    
    430
    +def get_roster_menu(account: str, jid: str, gateway: bool = False) -> Gio.Menu:
    
    421 431
         if jid_is_blocked(account, jid):
    
    422 432
             block_label = _('Unblock')
    
    423 433
         else:
    
    ... ... @@ -436,14 +446,14 @@ def get_roster_menu(account, jid, gateway=False):
    436 446
             action, label = item
    
    437 447
             action = f'win.{action}-{account}'
    
    438 448
             menuitem = Gio.MenuItem.new(label, action)
    
    439
    -        variant = GLib.Variant('s', str(jid))
    
    449
    +        variant = GLib.Variant('s', jid)
    
    440 450
             menuitem.set_action_and_target_value(action, variant)
    
    441 451
             menu.append_item(menuitem)
    
    442 452
     
    
    443 453
         return menu
    
    444 454
     
    
    445 455
     
    
    446
    -def get_subscription_menu(account, jid):
    
    456
    +def get_subscription_menu(account: str, jid: str) -> Gio.Menu:
    
    447 457
         menu = Gio.Menu()
    
    448 458
     
    
    449 459
         action_name = 'win.add-chat'
    
    ... ... @@ -475,13 +485,15 @@ def get_subscription_menu(account, jid):
    475 485
         return menu
    
    476 486
     
    
    477 487
     
    
    478
    -def get_chat_list_row_menu(workspace_id, account, jid, pinned):
    
    488
    +def get_chat_list_row_menu(workspace_id: str, account: str,
    
    489
    +                           jid: Union[JID, str], pinned: bool) -> Gio.Menu:
    
    490
    +    jid = str(jid)
    
    479 491
         client = app.get_client(account)
    
    480 492
         contact = client.get_module('Contacts').get_contact(jid)
    
    481 493
     
    
    482 494
         toggle_label = _('Unpin Chat') if pinned else _('Pin Chat')
    
    483 495
     
    
    484
    -    menu_items = [
    
    496
    +    menu_items: List[Any] = [
    
    485 497
             ('toggle-chat-pinned', toggle_label),
    
    486 498
         ]
    
    487 499
     
    
    ... ... @@ -502,20 +514,21 @@ def get_chat_list_row_menu(workspace_id, account, jid, pinned):
    502 514
                 action = f'win.{action}'
    
    503 515
                 menuitem = Gio.MenuItem.new(label, action)
    
    504 516
                 variant_list = GLib.Variant(
    
    505
    -                'as', [workspace_id, account, str(jid)])
    
    517
    +                'as', [workspace_id, account, jid])
    
    506 518
                 menuitem.set_action_and_target_value(action, variant_list)
    
    507 519
                 menu.append_item(menuitem)
    
    508 520
             else:
    
    509 521
                 # This is a submenu
    
    510 522
                 if len(workspaces) > 1:
    
    511 523
                     submenu = build_workspaces_submenu(
    
    512
    -                    workspace_id, account, str(jid))
    
    524
    +                    workspace_id, account, jid)
    
    513 525
                     menu.append_submenu(item[0], submenu)
    
    514 526
     
    
    515 527
         return menu
    
    516 528
     
    
    517 529
     
    
    518
    -def build_workspaces_submenu(current_workspace_id, account, jid):
    
    530
    +def build_workspaces_submenu(current_workspace_id: str, account: str,
    
    531
    +                             jid: str) -> Gio.Menu:
    
    519 532
         submenu = Gio.Menu()
    
    520 533
         for workspace_id in app.settings.get_workspaces():
    
    521 534
             if workspace_id == current_workspace_id: