diff options
-rw-r--r-- | .gitlab-ci.yml | 27 | ||||
-rw-r--r-- | data/default_config.cfg | 4 | ||||
-rw-r--r-- | data/doap.xml | 2 | ||||
-rw-r--r-- | data/poezio_logo.svg | 2 | ||||
-rw-r--r-- | doc/source/configuration.rst | 4 | ||||
-rwxr-xr-x | launch.sh | 12 | ||||
-rw-r--r-- | plugins/reorder.py | 2 | ||||
-rw-r--r-- | poezio/config.py | 12 | ||||
-rw-r--r-- | poezio/contact.py | 16 | ||||
-rw-r--r-- | poezio/core/commands.py | 2 | ||||
-rw-r--r-- | poezio/core/core.py | 2 | ||||
-rw-r--r-- | poezio/core/handlers.py | 9 | ||||
-rw-r--r-- | poezio/fixes.py | 5 | ||||
-rw-r--r-- | poezio/logger.py | 2 | ||||
-rw-r--r-- | poezio/mam.py | 2 | ||||
-rw-r--r-- | poezio/plugin.py | 2 | ||||
-rw-r--r-- | poezio/plugin_e2ee.py | 18 | ||||
-rw-r--r-- | poezio/roster.py | 4 | ||||
-rw-r--r-- | poezio/size_manager.py | 12 | ||||
-rw-r--r-- | poezio/tabs/basetabs.py | 8 | ||||
-rw-r--r-- | poezio/tabs/conversationtab.py | 4 | ||||
-rw-r--r-- | poezio/tabs/muctab.py | 2 | ||||
-rw-r--r-- | poezio/tabs/privatetab.py | 6 | ||||
-rw-r--r-- | poezio/tabs/rostertab.py | 4 | ||||
-rwxr-xr-x | poezio/theming.py | 3 | ||||
-rw-r--r-- | poezio/windows/image.py | 5 | ||||
-rw-r--r-- | poezio/windows/roster_win.py | 2 |
27 files changed, 99 insertions, 74 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a908b86a..e8bd5415 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -79,7 +79,21 @@ pytest-3.9: pytest-3.10: stage: test - image: python:3.10-rc + image: python:3.10 + script: + - apt-get update && apt-get install -y libidn11-dev + - git clone https://lab.louiz.org/poezio/slixmpp.git + - pip3 install pytest pyasn1-modules cffi --upgrade + - cd slixmpp + - python3 setup.py install + - cd .. + - python3 setup.py install + - py.test -v test/ + +pytest-3.11: + stage: test + image: python:3.11-rc + allow_failure: true script: - apt-get update && apt-get install -y libidn11-dev - git clone https://lab.louiz.org/poezio/slixmpp.git @@ -102,10 +116,19 @@ pylint-plugins: - python3 setup.py install - pylint -E plugins -mypy: +mypy-fixed: stage: lint image: python:3 script: + - pip3 install mypy==0.971 types-setuptools + - mypy --ignore-missing-imports ./poezio + - mypy --ignore-missing-imports ./plugins + +mypy-latest: + stage: lint + image: python:3 + allow_failure: true + script: - pip3 install mypy types-setuptools - mypy --ignore-missing-imports ./poezio - mypy --ignore-missing-imports ./plugins diff --git a/data/default_config.cfg b/data/default_config.cfg index d91ff36a..8e926c0e 100644 --- a/data/default_config.cfg +++ b/data/default_config.cfg @@ -84,8 +84,8 @@ certificate = # keep the same for obvious reasons, so this is a good option if your server # does this, rather than skipping all verifications. # This is not affected by ignore_certificate -# Poezio attempts to guess this value automatically. Set to override this -# behaviour, to the empty string for example, or to another path. +# Poezio attempts to guess this value automatically if empty. To override this +# behaviour, set the value to another path. #ca_cert_path = # Auto-reconnects you when you get disconnected from the server diff --git a/data/doap.xml b/data/doap.xml index b9a42798..6a1330b7 100644 --- a/data/doap.xml +++ b/data/doap.xml @@ -20,7 +20,7 @@ <developer-forum rdf:resource="xmpp:poezio@muc.poez.io?join"/> <support-forum rdf:resource="xmpp:poezio@muc.poez.io?join"/> - <license rdf:resource="https://git.poez.io/poezio/plain/COPYING"/> + <license rdf:resource="https://lab.louiz.org/poezio/poezio/-/raw/main/COPYING"/> <!-- See https://github.com/ewilderj/doap/issues/49 --> <language>en</language> diff --git a/data/poezio_logo.svg b/data/poezio_logo.svg index 7848f8c6..30a93907 100644 --- a/data/poezio_logo.svg +++ b/data/poezio_logo.svg @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<svg width="162.31mm" height="155.71mm" version="1.1" viewBox="0 0 162.31 155.71" xmlns="http://www.w3.org/2000/svg"> +<svg version="1.1" viewBox="0 -3.3 162.31 162.31" xmlns="http://www.w3.org/2000/svg"> <defs> <linearGradient id="a" x1="-236.83" x2="-194.71" y1="320.77" y2="225.75" gradientTransform="matrix(3.7795 0 0 3.7795 -851.06 -1965.2)" gradientUnits="userSpaceOnUse"> <stop stop-color="#c6b8a3" offset="0"/> diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst index fe4d06cd..c28f38fa 100644 --- a/doc/source/configuration.rst +++ b/doc/source/configuration.rst @@ -10,8 +10,8 @@ or use the :term:`/set` command to edit some of its values directly from poezio. This file is also used to configure key bindings, but this is explained in the :ref:`keys-page` documentation file. -That file is read at each startup and the configuration is saved when poezio -is closed. +The configuration is read at each startup or when the `/reload` command is +issued, and it is updated after every `/set` command. This configuration file **requires** all global options to be in a section named [Poezio]. Some other options can be in optional sections and will @@ -1,20 +1,20 @@ #!/bin/sh -cd $(dirname "$(readlink -f "$0")") +proj_dir=$(dirname "$(readlink -f "$0")") if [ -z "$POEZIO_VENV" ] then POEZIO_VENV="poezio-venv" fi -if [ -e .git ] +if [ -e "$proj_dir/.git" ] then - args=$(git show --format='%h %ci' | head -n1) + args=$(git -C "$proj_dir" show --format='%h %ci' | head -n1) else args="0.14-dev" fi -if [ -e "$POEZIO_VENV" ] +if [ -e "$proj_dir/$POEZIO_VENV" ] then - PYTHON3="$POEZIO_VENV/bin/python3" + PYTHON3="$proj_dir/$POEZIO_VENV/bin/python3" else echo "" echo "WARNING: Not using the up-to-date launch format" @@ -25,5 +25,5 @@ else fi $PYTHON3 -c 'import sys;(print("Python 3.7 or newer is required") and exit(1)) if sys.version_info < (3, 7) else exit(0)' || exit 1 -exec "$PYTHON3" -m poezio --custom-version "$args" "$@" +PYTHONPATH="$proj_dir:$PYTHONPATH" exec "$PYTHON3" -m poezio --custom-version "$args" "$@" diff --git a/plugins/reorder.py b/plugins/reorder.py index 5769d560..158b89bb 100644 --- a/plugins/reorder.py +++ b/plugins/reorder.py @@ -118,7 +118,7 @@ def parse_runtime_tablist(tablist): i += 1 result = check_tab(tab) # Don't serialize gap tabs as they're recreated automatically - if result != 'empty' and isinstance(tab, TEXT_TO_TAB.values()): + if result != 'empty' and isinstance(tab, tuple(TEXT_TO_TAB.values())): props.append((i, '%s:%s' % (result, tab.jid.full))) return props diff --git a/poezio/config.py b/poezio/config.py index dbbed5ba..4eb43cad 100644 --- a/poezio/config.py +++ b/poezio/config.py @@ -30,16 +30,6 @@ ConfigDict = Dict[str, Dict[str, ConfigValue]] USE_DEFAULT_SECTION = '__DEFAULT SECTION PLACEHOLDER__' -CA_CERT_DEFAULT_PATHS = { - '/etc/ssl/cert.pem', - '/etc/ssl/certs/ca-certificates.crt', - '/etc/ssl/certs/ca-bundle.crt', - '/etc/pki/tls/certs/ca-bundle.crt', - '/etc/ssl/certs/ca-certificates.crt', - '/etc/ca-certificates/extracted/tls-ca-bundle.pem', - '/etc/pki/ca-trust/extracted/openssl/ca-bundle.trust.crt', -} - DEFAULT_CONFIG: ConfigDict = { 'Poezio': { 'ack_message_receipts': True, @@ -51,7 +41,7 @@ DEFAULT_CONFIG: ConfigDict = { 'autorejoin_delay': '5', 'autorejoin': False, 'beep_on': 'highlight private invite disconnect', - 'ca_cert_path': ':'.join(CA_CERT_DEFAULT_PATHS), + 'ca_cert_path': '', 'certificate': '', 'certfile': '', 'ciphers': 'HIGH+kEDH:HIGH+kEECDH:HIGH:!PSK:!SRP:!3DES:!aNULL', diff --git a/poezio/contact.py b/poezio/contact.py index 09948086..90f34c7e 100644 --- a/poezio/contact.py +++ b/poezio/contact.py @@ -21,6 +21,7 @@ from typing import ( ) from slixmpp import InvalidJID, JID +from slixmpp.roster import RosterItem log = logging.getLogger(__name__) @@ -74,7 +75,7 @@ class Contact: to get the resource with the highest priority, etc """ - def __init__(self, item): + def __init__(self, item: RosterItem): """ item: a slixmpp RosterItem pointing to that contact """ @@ -96,7 +97,7 @@ class Contact: return self.__item.jid @property - def name(self): + def name(self) -> str: """The name of the contact or an empty string.""" return self.__item['name'] or self._name or '' @@ -106,26 +107,27 @@ class Contact: self._name = value @property - def ask(self): + def ask(self) -> Optional[str]: if self.__item['pending_out']: return 'asked' + return None @property - def pending_in(self): + def pending_in(self) -> bool: """We received a subscribe stanza from this contact.""" return self.__item['pending_in'] @pending_in.setter - def pending_in(self, value): + def pending_in(self, value: bool): self.__item['pending_in'] = value @property - def pending_out(self): + def pending_out(self) -> bool: """We sent a subscribe stanza to this contact.""" return self.__item['pending_out'] @pending_out.setter - def pending_out(self, value): + def pending_out(self, value: bool): self.__item['pending_out'] = value @property diff --git a/poezio/core/commands.py b/poezio/core/commands.py index f4662021..fe91ca67 100644 --- a/poezio/core/commands.py +++ b/poezio/core/commands.py @@ -984,7 +984,7 @@ class CommandCore: bare = JID(jid).bare except InvalidJID: return self.core.information('Invalid JID for /impromptu: %s' % args[0], 'Error') - jids.add(bare) + jids.add(JID(bare)) asyncio.create_task(self.core.impromptu(jids)) diff --git a/poezio/core/core.py b/poezio/core/core.py index 7690e769..6582402d 100644 --- a/poezio/core/core.py +++ b/poezio/core/core.py @@ -276,7 +276,7 @@ class Core: self.key_func.try_execute = self.try_execute # Add handlers - xmpp_event_handlers = [ + xmpp_event_handlers: List[Tuple[str, Callable[..., Any]]] = [ ('attention', self.handler.on_attention), ('carbon_received', self.handler.on_carbon_received), ('carbon_sent', self.handler.on_carbon_sent), diff --git a/poezio/core/handlers.py b/poezio/core/handlers.py index a75ecc52..e92e4aac 100644 --- a/poezio/core/handlers.py +++ b/poezio/core/handlers.py @@ -1180,25 +1180,26 @@ class HandlerCore: We are sending a new stanza, write it in the xml buffer if needed. """ if self.core.xml_tab: + stanza_str = str(stanza) if PYGMENTS: - xhtml_text = highlight(str(stanza), LEXER, FORMATTER) + xhtml_text = highlight(stanza_str, LEXER, FORMATTER) poezio_colored = xhtml.xhtml_to_poezio_colors( xhtml_text, force=True).rstrip('\x19o').strip() else: - poezio_colored = str(stanza) + poezio_colored = stanza_str self.core.xml_buffer.add_message( XMLLog(txt=poezio_colored, incoming=False), ) try: if self.core.xml_tab.match_stanza( - ElementBase(ET.fromstring(stanza))): + ElementBase(ET.fromstring(stanza_str))): self.core.xml_tab.filtered_buffer.add_message( XMLLog(txt=poezio_colored, incoming=False), ) except: # Most of the time what gets logged is whitespace pings. Skip. # And also skip tab updates. - if stanza.strip() == '': + if stanza_str.strip() == '': return None log.debug('', exc_info=True) diff --git a/poezio/fixes.py b/poezio/fixes.py index 7383154f..c2db4332 100644 --- a/poezio/fixes.py +++ b/poezio/fixes.py @@ -5,14 +5,15 @@ upstream. TODO: Check that they are fixed and remove those hacks """ -from slixmpp import ClientXMPP, Message +from slixmpp import Message +from slixmpp.plugins.xep_0184 import XEP_0184 import logging log = logging.getLogger(__name__) -def _filter_add_receipt_request(self: ClientXMPP, stanza): +def _filter_add_receipt_request(self: XEP_0184, stanza): """ Auto add receipt requests to outgoing messages, if: diff --git a/poezio/logger.py b/poezio/logger.py index 882a34df..29eaad32 100644 --- a/poezio/logger.py +++ b/poezio/logger.py @@ -184,7 +184,7 @@ class Logger: self._check_and_create_log_dir(room) log.debug('Log handle for %s re-created', room) - def _check_and_create_log_dir(self, jid: str, + def _check_and_create_log_dir(self, jid: Union[str, JID], open_fd: bool = True) -> Optional[IO[str]]: """ Check that the directory where we want to log the messages diff --git a/poezio/mam.py b/poezio/mam.py index 59544b16..7cb1d369 100644 --- a/poezio/mam.py +++ b/poezio/mam.py @@ -130,7 +130,7 @@ def _parse_message(msg: SMessage) -> Dict: } -def _ignore_private_message(stanza: SMessage, filter_jid: JID) -> bool: +def _ignore_private_message(stanza: SMessage, filter_jid: Optional[JID]) -> bool: """Returns True if a MUC-PM should be ignored, as prosody returns all PMs within the same room. """ diff --git a/poezio/plugin.py b/poezio/plugin.py index 9101c6bb..f38e47e2 100644 --- a/poezio/plugin.py +++ b/poezio/plugin.py @@ -426,7 +426,7 @@ class BasePlugin(object, metaclass=SafetyMetaclass): self.init() @property - def name(self): + def name(self) -> str: """ Get the name (module name) of the plugin. """ diff --git a/poezio/plugin_e2ee.py b/poezio/plugin_e2ee.py index 9b6a9fe4..49f7b067 100644 --- a/poezio/plugin_e2ee.py +++ b/poezio/plugin_e2ee.py @@ -234,7 +234,8 @@ class E2EEPlugin(BasePlugin): for section in config.sections(): value = config.getstr('encryption', section=section) if value and value == self.encryption_short_name: - self._enabled_tabs[section] = self.encrypt + section_jid = JID(section) + self._enabled_tabs[section_jid] = self.encrypt def cleanup(self): ConversationTab.remove_information_element(self.encryption_short_name) @@ -278,7 +279,7 @@ class E2EEPlugin(BasePlugin): ) @staticmethod - def format_fingerprint(fingerprint: str, theme: Theme) -> str: + def format_fingerprint(fingerprint: str, own: bool, theme: Theme) -> str: return fingerprint async def _show_fingerprints(self, jid: JID) -> None: @@ -286,20 +287,21 @@ class E2EEPlugin(BasePlugin): theme = get_theme() fprs = await self.get_fingerprints(jid) if len(fprs) == 1: - fingerprint = self.format_fingerprint(fprs[0], theme) + fp, own = fprs[0] + fingerprint = self.format_fingerprint(fp, own, theme) self.api.information( f'Fingerprint for {jid}:\n{fingerprint}', 'Info', ) elif fprs: - fmt_fprs = map(lambda fp: self.format_fingerprint(fp, theme), fprs) + fmt_fprs = map(lambda fp: self.format_fingerprint(fp[0], fp[1], theme), fprs) self.api.information( 'Fingerprints for %s:\n%s' % (jid, '\n\n'.join(fmt_fprs)), 'Info', ) else: self.api.information( - 'No fingerprints to display', + f'{jid}: No fingerprints to display', 'Info', ) @@ -308,6 +310,8 @@ class E2EEPlugin(BasePlugin): tab = self.api.current_tab() if not args and isinstance(tab, self.supported_tab_types): jid = tab.jid + if isinstance(tab, MucTab): + jid = self.core.xmpp.boundjid.bare elif not args and isinstance(tab, RosterInfoTab): # Allow running the command without arguments in roster tab jid = self.core.xmpp.boundjid.bare @@ -489,7 +493,7 @@ class E2EEPlugin(BasePlugin): await func(message, jid, tab, passthrough=True) # type: ignore else: # pylint: disable=unexpected-keyword-arg - func(message, jid, tab) + func(message, jid, tab) # type: ignore log.debug('Decrypted %s message: %r', self.encryption_name, message['body']) return None @@ -671,7 +675,7 @@ class E2EEPlugin(BasePlugin): raise NotImplementedError - async def get_fingerprints(self, jid: JID) -> List[str]: + async def get_fingerprints(self, jid: JID) -> List[Tuple[str, bool]]: """Show fingerprint(s) for this encryption method and JID. To overload in plugins. diff --git a/poezio/roster.py b/poezio/roster.py index 20f5529d..a52ea23e 100644 --- a/poezio/roster.py +++ b/poezio/roster.py @@ -71,7 +71,7 @@ class Roster: self.last_modified = datetime.now() @property - def needs_rebuild(self): + def needs_rebuild(self) -> bool: return self.last_modified >= self.last_built def __getitem__(self, key): @@ -133,7 +133,7 @@ class Roster: return False @property - def jid(self): + def jid(self) -> JID: """Our JID""" return self.__node.jid diff --git a/poezio/size_manager.py b/poezio/size_manager.py index 3e80c357..c5312c9f 100644 --- a/poezio/size_manager.py +++ b/poezio/size_manager.py @@ -18,21 +18,25 @@ class SizeManager: self._core = core @property - def tab_degrade_x(self): + def tab_degrade_x(self) -> bool: + if base_wins.TAB_WIN is None: + raise ValueError _, x = base_wins.TAB_WIN.getmaxyx() return x < THRESHOLD_WIDTH_DEGRADE @property - def tab_degrade_y(self): + def tab_degrade_y(self) -> bool: + if base_wins.TAB_WIN is None: + raise ValueError y, x = base_wins.TAB_WIN.getmaxyx() return y < THRESHOLD_HEIGHT_DEGRADE @property - def core_degrade_x(self): + def core_degrade_x(self) -> bool: y, x = self._core.stdscr.getmaxyx() return x < FULL_WIDTH_DEGRADE @property - def core_degrade_y(self): + def core_degrade_y(self) -> bool: y, x = self._core.stdscr.getmaxyx() return y < FULL_HEIGHT_DEGRADE diff --git a/poezio/tabs/basetabs.py b/poezio/tabs/basetabs.py index de88a6d1..793eae62 100644 --- a/poezio/tabs/basetabs.py +++ b/poezio/tabs/basetabs.py @@ -170,15 +170,15 @@ class Tab: return 1 @property - def info_win(self): + def info_win(self) -> windows.TextWin: return self.core.information_win @property - def color(self): + def color(self) -> Union[Tuple[int, int], Tuple[int, int, 'str']]: return STATE_COLORS[self._state]() @property - def vertical_color(self): + def vertical_color(self) -> Union[Tuple[int, int], Tuple[int, int, 'str']]: return VERTICAL_STATE_COLORS[self._state]() @property @@ -492,7 +492,7 @@ class GapTab(Tab): return 0 @property - def name(self): + def name(self) -> str: return '' def refresh(self): diff --git a/poezio/tabs/conversationtab.py b/poezio/tabs/conversationtab.py index f6b5708b..de1f988a 100644 --- a/poezio/tabs/conversationtab.py +++ b/poezio/tabs/conversationtab.py @@ -83,8 +83,8 @@ class ConversationTab(OneToOneTab): self.update_keys() @property - def general_jid(self): - return self.jid.bare + def general_jid(self) -> JID: + return JID(self.jid.bare) def get_info_header(self): raise NotImplementedError diff --git a/poezio/tabs/muctab.py b/poezio/tabs/muctab.py index e2c27ab1..e2d546c9 100644 --- a/poezio/tabs/muctab.py +++ b/poezio/tabs/muctab.py @@ -556,7 +556,7 @@ class MucTab(ChatTab): """ replaced_id = message.message['replace']['id'] if replaced_id != '' and config.get_by_tabname( - 'group_corrections', message.room_from): + 'group_corrections', JID(message.room_from)): try: delayed_date = message.date or datetime.now() modify_hl = self.modify_message( diff --git a/poezio/tabs/privatetab.py b/poezio/tabs/privatetab.py index b6c06f2a..1909e3c1 100644 --- a/poezio/tabs/privatetab.py +++ b/poezio/tabs/privatetab.py @@ -85,14 +85,14 @@ class PrivateTab(OneToOneTab): return super().remote_user_color() @property - def general_jid(self): + def general_jid(self) -> JID: return self.jid - def get_dest_jid(self): + def get_dest_jid(self) -> JID: return self.jid @property - def nick(self): + def nick(self) -> str: return self.get_nick() def ack_message(self, msg_id: str, msg_jid: JID): diff --git a/poezio/tabs/rostertab.py b/poezio/tabs/rostertab.py index 66aff2b1..18334c20 100644 --- a/poezio/tabs/rostertab.py +++ b/poezio/tabs/rostertab.py @@ -14,7 +14,7 @@ import ssl from functools import partial from os import getenv, path from pathlib import Path -from typing import Dict, Callable +from typing import Dict, Callable, Union from slixmpp import JID, InvalidJID from slixmpp.exceptions import IqError, IqTimeout @@ -199,7 +199,7 @@ class RosterInfoTab(Tab): completion=self.completion_cert_fetch) @property - def selected_row(self): + def selected_row(self) -> Union[Contact, Resource]: return self.roster_win.get_selected_row() @command_args_parser.ignored diff --git a/poezio/theming.py b/poezio/theming.py index 712a44ab..187d07c5 100755 --- a/poezio/theming.py +++ b/poezio/theming.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python3 # Copyright 2010-2011 Florent Le Coz <louiz@louiz.org> # # This file is part of Poezio. @@ -391,7 +392,7 @@ class Theme: } @property - def ccg_palette(self): + def ccg_palette(self) -> Optional[Dict[float, int]]: prepare_ccolor_palette(self) return self.CCG_PALETTE diff --git a/poezio/windows/image.py b/poezio/windows/image.py index b721b859..2862d2d9 100644 --- a/poezio/windows/image.py +++ b/poezio/windows/image.py @@ -2,6 +2,8 @@ Defines a window which contains either an image or a border. """ +from __future__ import annotations + import curses from io import BytesIO @@ -9,9 +11,6 @@ try: from PIL import Image HAS_PIL = True except ImportError: - class Image: # type: ignore - class Image: - pass HAS_PIL = False try: diff --git a/poezio/windows/roster_win.py b/poezio/windows/roster_win.py index eed68429..dfdc9b9b 100644 --- a/poezio/windows/roster_win.py +++ b/poezio/windows/roster_win.py @@ -278,7 +278,7 @@ class RosterWin(Win): elif contact.name and contact.name != contact.bare_jid: display_name = '%s (%s)' % (contact.name, contact.bare_jid) else: - display_name = contact.bare_jid + display_name = str(contact.bare_jid) display_name = self.truncate_name(display_name, added) + nb |