Philipp Hörist pushed to branch master at gajim / gajim
Commits: 72d8c294 by lovetox at 2021-12-27T17:54:17+01:00 Exceptions: Remove unused exceptions
- - - - - 86fb7962 by lovetox at 2021-12-27T19:02:42+01:00 Typing: Add AppKit stubs
- - - - - 5454a2d5 by lovetox at 2021-12-27T19:05:24+01:00 i18n: Refactor
- Make dedicated functions for getting the default langauge for every platform - Don’t assume get_default_lang() can fail - Add type annotations
- - - - - 2a6181d1 by lovetox at 2021-12-27T19:14:40+01:00 Regex: Add type annotations
- - - - - 1acccaad by lovetox at 2021-12-27T19:14:58+01:00 EmojiData: Add type annotations
- - - - - 6e8a16ca by lovetox at 2021-12-27T20:15:05+01:00 Typing: Add keyring stubs
- - - - - 6c1ec6bd by lovetox at 2021-12-27T20:15:40+01:00 Passwords: Add type annotations
- - - - - c903a813 by lovetox at 2021-12-27T20:15:50+01:00 Update pyrightconfig.json
- - - - -
11 changed files:
- gajim/common/exceptions.py - gajim/common/i18n.py - gajim/common/passwords.py - gajim/common/regex.py - gajim/gtk/emoji_data.pyi - pyrightconfig.json - + typings/AppKit/__init__.py - + typings/keyring/__init__.pyi - + typings/keyring/backend.pyi - + typings/keyring/core.pyi - + typings/keyring/errors.pyi
Changes:
===================================== gajim/common/exceptions.py ===================================== @@ -20,33 +20,6 @@ from gajim.common.i18n import _
-class PysqliteOperationalError(Exception): - """ - Sqlite2 raised pysqlite2.dbapi2.OperationalError - """ - - def __init__(self, text=''): - Exception.__init__(self) - self.text = text - - def __str__(self): - return self.text - -class DatabaseMalformed(Exception): - """ - The database can't be read - """ - - def __init__(self, path=''): - Exception.__init__(self) - self.path = path - - def __str__(self): - return _('The database file (%s) cannot be read. ' - 'Try to repair it (see ' - 'https://dev.gajim.org/gajim/gajim/wikis/help/DatabaseBackup)' - ' or remove it (all history will be lost).') % self.path - class ServiceNotAvailable(Exception): """ This exception is raised when we cannot use Gajim remotely' @@ -58,16 +31,6 @@ def __init__(self): def __str__(self): return _('Service not available: Gajim is not running, or remote_control is False')
-class DbusNotSupported(Exception): - """ - D-Bus is not installed or python bindings are missing - """ - - def __init__(self): - Exception.__init__(self) - - def __str__(self): - return _('D-Bus is not present on this machine or python module is missing')
class SessionBusNotPresent(Exception): """ @@ -81,39 +44,6 @@ def __str__(self): return _('Session bus is not available.\nTry reading %(url)s') % \ {'url': 'https://dev.gajim.org/gajim/gajim/wikis/help/GajimDBus%27%7D
-class SystemBusNotPresent(Exception): - """ - This exception indicates that there is no session daemon - """ - - def __init__(self): - Exception.__init__(self) - - def __str__(self): - return _('System bus is not available.\nTry reading %(url)s') % \ - {'url': 'https://dev.gajim.org/gajim/gajim/wikis/help/GajimDBus%27%7D - -class NegotiationError(Exception): - """ - A session negotiation failed - """ - -class Cancelled(Exception): - """ - The user cancelled an operation - """ - -class LatexError(Exception): - """ - LaTeX processing failed for some reason - """ - - def __init__(self, text=''): - Exception.__init__(self) - self.text = text - - def __str__(self): - return self.text
class GajimGeneralException(Exception): """ @@ -127,6 +57,7 @@ def __init__(self, text=''): def __str__(self): return self.text
+ class PluginsystemError(Exception): """ Error in the pluginsystem @@ -139,19 +70,6 @@ def __init__(self, text=''): def __str__(self): return self.text
-class StanzaMalformed(Exception): - """ - Malfromed Stanza - """ - def __init__(self, message, stanza=''): - Exception.__init__(self, message, stanza) - self._msg = '{}\n{}'.format(message, stanza) - def __str__(self): - return self._msg - -class SendMessageError(Exception): - pass -
class FileError(Exception): pass
===================================== gajim/common/i18n.py ===================================== @@ -18,6 +18,11 @@ # You should have received a copy of the GNU General Public License # along with Gajim. If not, see http://www.gnu.org/licenses/.
+from __future__ import annotations + +from typing import Iterator +from typing import Optional + import os import sys import locale @@ -27,13 +32,12 @@ from gi.repository import GLib
DOMAIN = 'gajim' -LANG = 'en' direction_mark = '\u200E' _translation = gettext.NullTranslations()
-def get_locale_dirs(): - if os.name == 'nt': +def get_locale_dirs() -> Optional[list[Path]]: + if sys.platform == 'win32': return
path = gettext.find(DOMAIN) @@ -50,7 +54,7 @@ def get_locale_dirs(): return [Path(dir_) for dir_ in data_dirs]
-def iter_locale_dirs(): +def iter_locale_dirs() -> Iterator[Optional[str]]: locale_dirs = get_locale_dirs() if locale_dirs is None: yield None @@ -59,7 +63,7 @@ def iter_locale_dirs(): # gettext fallback locale_dirs.append(Path(sys.base_prefix) / 'share')
- found_paths = [] + found_paths: list[Path] = [] for path in locale_dirs: locale_dir = path / 'locale' if locale_dir in found_paths: @@ -69,34 +73,42 @@ def iter_locale_dirs(): yield str(locale_dir)
-def get_default_lang(): - if os.name == "nt": - import ctypes - windll = ctypes.windll.kernel32 - return locale.windows_locale[windll.GetUserDefaultUILanguage()] +def get_win32_default_lang() -> str: + import ctypes + windll = ctypes.windll.kernel32 + return locale.windows_locale[windll.GetUserDefaultUILanguage()] + + +def get_darwin_default_lang() -> str: + from AppKit import NSLocale + # FIXME: This returns a two letter language code (en, de, fr) + # We need a way to get en_US, de_DE etc. + return NSLocale.currentLocale().languageCode() + + +def get_default_lang() -> str: + if sys.platform == "win32": + return get_win32_default_lang()
if sys.platform == "darwin": - from AppKit import NSLocale - # FIXME: This returns a two letter language code (en, de, fr) - # We need a way to get en_US, de_DE etc. - return NSLocale.currentLocale().languageCode() + return get_darwin_default_lang()
return locale.getdefaultlocale()[0] or 'en'
-def get_rfc5646_lang(lang=None): +def get_rfc5646_lang(lang: Optional[str] = None) -> str: if lang is None: lang = LANG return lang.replace('_', '-')
-def get_short_lang_code(lang=None): +def get_short_lang_code(lang: Optional[str] = None) -> str: if lang is None: lang = LANG return lang[:2]
-def initialize_direction_mark(): +def initialize_direction_mark() -> None: from gi.repository import Gtk
global direction_mark @@ -105,7 +117,7 @@ def initialize_direction_mark(): direction_mark = '\u200F'
-def paragraph_direction_mark(text): +def paragraph_direction_mark(text: str) -> str: """ Determine paragraph writing direction according to http://www.unicode.org/reports/tr9/#The_Paragraph_Level @@ -122,7 +134,7 @@ def paragraph_direction_mark(text): return '\u200E'
-def Q_(text): +def Q_(text: str) -> str: """ Translate the given text, optionally qualified with a special construction, which will help translators to disambiguate between @@ -143,7 +155,11 @@ def Q_(text): return text
-def ngettext(s_sing, s_plural, n, replace_sing=None, replace_plural=None): +def ngettext(s_sing: str, + s_plural: str, + n: int, + replace_sing: Optional[str] = None, + replace_plural: Optional[str] = None) -> str: """ Use as: i18n.ngettext( @@ -164,18 +180,13 @@ def ngettext(s_sing, s_plural, n, replace_sing=None, replace_plural=None): except locale.Error as error: print(error, file=sys.stderr)
-try: - LANG = get_default_lang() - if os.name == 'nt': - # Set the env var on Windows because gettext.find() uses it to - # find the translation - # Use LANGUAGE instead of LANG, LANG sets LC_ALL and thus - # doesn't retain other region settings like LC_TIME - os.environ['LANGUAGE'] = LANG -except Exception as error: - print('Failed to determine default language', file=sys.stderr) - import traceback - traceback.print_exc() +LANG = get_default_lang() +if sys.platform == 'win32': + # Set the env var on Windows because gettext.find() uses it to + # find the translation + # Use LANGUAGE instead of LANG, LANG sets LC_ALL and thus + # doesn't retain other region settings like LC_TIME + os.environ['LANGUAGE'] = LANG
# Search for the translation in all locale dirs for dir_ in iter_locale_dirs():
===================================== gajim/common/passwords.py ===================================== @@ -20,16 +20,24 @@ # You should have received a copy of the GNU General Public License # along with Gajim. If not, see http://www.gnu.org/licenses/.
+from __future__ import annotations + +from typing import Optional + import logging + import keyring
from gajim.common import app
-__all__ = ['get_password', 'save_password'] +__all__ = [ + 'get_password', + 'save_password', + 'delete_password' +]
log = logging.getLogger('gajim.password')
- backends = keyring.backend.get_all_keyring() for backend in backends: log.info('Found keyring backend: %s', backend) @@ -46,7 +54,7 @@ class SecretPasswordStorage: """
@staticmethod - def save_password(account_name, password): + def save_password(account_name: str, password: str) -> bool: if not KEYRING_AVAILABLE: log.warning('No recommended keyring backend available.' 'Passwords cannot be stored.') @@ -60,7 +68,7 @@ def save_password(account_name, password): return False
@staticmethod - def get_password(account_name): + def get_password(account_name: str) -> Optional[str]: log.info('Request password from keyring') if not KEYRING_AVAILABLE: return @@ -73,10 +81,11 @@ def get_password(account_name): return
@staticmethod - def delete_password(account_name): + def delete_password(account_name: str) -> None: log.info('Remove password from keyring') if not KEYRING_AVAILABLE: return + try: return keyring_backend.delete_password('gajim', account_name) except keyring.errors.PasswordDeleteError as error: @@ -91,27 +100,26 @@ class ConfigPasswordStorage: """
@staticmethod - def get_password(account_name): + def get_password(account_name: str) -> str: return app.settings.get_account_setting(account_name, 'password')
@staticmethod - def save_password(account_name, password): + def save_password(account_name: str, password: str) -> bool: app.settings.set_account_setting(account_name, 'password', password) return True
@staticmethod - def delete_password(account_name): + def delete_password(account_name: str) -> None: app.settings.set_account_setting(account_name, 'password', '') - return True
-def get_password(account_name): +def get_password(account_name: str) -> Optional[str]: if app.settings.get('use_keyring'): return SecretPasswordStorage.get_password(account_name) return ConfigPasswordStorage.get_password(account_name)
-def save_password(account_name, password): +def save_password(account_name: str, password: str) -> bool: if account_name in app.connections: app.connections[account_name].password = password
@@ -123,7 +131,7 @@ def save_password(account_name, password): return ConfigPasswordStorage.save_password(account_name, password)
-def delete_password(account_name): +def delete_password(account_name: str) -> None: if account_name in app.connections: app.connections[account_name].password = None
===================================== gajim/common/regex.py ===================================== @@ -54,7 +54,7 @@ def _get_basic_pattern(): r'(?<!\w)' r'_[^\s_]' r'([^_]*[^\s_])?' r'_(?!\w)' return basic_pattern + formatting
-def _get_emot_and_basic_pattern(use_ascii_formatting=True): +def _get_emot_and_basic_pattern(use_ascii_formatting: bool = True) -> str: from gajim.gui.emoji_data import emoji_data # because emoticons match later (in the string) they need to be after # basic matches that may occur earlier
===================================== gajim/gtk/emoji_data.pyi ===================================== @@ -24,7 +24,7 @@ def is_emoji(codepoints: str) -> bool: ... def get_emoji_pixbuf(codepoints: str) -> GdkPixbuf.Pixbuf: ...
class EmojiData(OrderedDict): - ... + def get_regex(self) -> str: ...
class EmojiPixbufs(dict): ...
===================================== pyrightconfig.json ===================================== @@ -14,6 +14,12 @@ "typeCheckingMode": "strict", "include": [ "gajim/common/cert_store.py", + "gajim/common/ged.py", + "gajim/common/i18n.py", + "gajim/common/nec.py", + "gajim/common/passwords.py", + "gajim/common/regex.py", + "gajim/common/sound.py", "gajim/gtk/app_side_bar.py", "gajim/gtk/chat_filter.py", "gajim/gtk/chat_page.py",
===================================== typings/AppKit/__init__.py ===================================== @@ -0,0 +1,16 @@ + +from __future__ import annotations + +from typing import Any + + +class NSLocale: + @classmethod + def currentLocale(cls) -> Any: ... + + +class NSSound: + @classmethod + def alloc(cls) -> NSSound: ... + def initWithContentsOfFile_byReference_(self, url: str, byref: bool) -> None: ... + def play(self) -> None: ...
===================================== typings/keyring/__init__.pyi ===================================== @@ -0,0 +1,7 @@ + + +from . import backend as backend +from . import core as core +from . import errors as errors +from .core import get_keyring as get_keyring +
===================================== typings/keyring/backend.pyi ===================================== @@ -0,0 +1,13 @@ + + +from typing import Optional + + +class KeyringBackend: + + def get_password(self, service: str, username: str) -> Optional[str]: ... + def set_password(self, service: str, username: str, password: str) -> None: ... + def delete_password(self, service: str, username: str) -> None: ... + + +def get_all_keyring() -> list[KeyringBackend]: ...
===================================== typings/keyring/core.pyi ===================================== @@ -0,0 +1,11 @@ + +from typing import Optional + +from keyring.backend import KeyringBackend + + +def get_password(service_name: str, username: str) -> Optional[str]: ... +def set_password(service_name: str, username: str, password: str) -> None: ... +def delete_password(service_name: str, username: str) -> None: ... +def recommended(backend: KeyringBackend) -> bool: ... +def get_keyring() -> KeyringBackend: ...
===================================== typings/keyring/errors.pyi ===================================== @@ -0,0 +1,8 @@ + + +class KeyringError(Exception): ... +class PasswordSetError(KeyringError): ... +class PasswordDeleteError(KeyringError): ... +class InitError(KeyringError): ... +class KeyringLocked(KeyringError): ... +class NoKeyringError(KeyringError): ...
View it on GitLab: https://dev.gajim.org/gajim/gajim/-/compare/92023bb14bde8bed9867409331acade1...