Merge branch 'more-typing' into 'master'
More typing See merge request poezio/poezio!135
This commit is contained in:
commit
291233bbbd
16 changed files with 489 additions and 316 deletions
|
@ -14,7 +14,7 @@ from datetime import (
|
|||
timezone,
|
||||
)
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional, Tuple, Union
|
||||
from typing import Dict, List, Optional, Tuple, Union, Any
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
|
@ -44,7 +44,7 @@ def _get_output_of_command(command: str) -> Optional[List[str]]:
|
|||
return None
|
||||
|
||||
|
||||
def _is_in_path(command: str, return_abs_path=False) -> Union[bool, str]:
|
||||
def _is_in_path(command: str, return_abs_path: bool = False) -> Union[bool, str]:
|
||||
"""
|
||||
Check if *command* is in the $PATH or not.
|
||||
|
||||
|
@ -111,10 +111,12 @@ def get_os_info() -> str:
|
|||
stdout=subprocess.PIPE,
|
||||
close_fds=True)
|
||||
process.wait()
|
||||
output = process.stdout.readline().decode('utf-8').strip()
|
||||
# some distros put n/a in places, so remove those
|
||||
output = output.replace('n/a', '').replace('N/A', '')
|
||||
return output
|
||||
if process.stdout is not None:
|
||||
out = process.stdout.readline().decode('utf-8').strip()
|
||||
# some distros put n/a in places, so remove those
|
||||
out = out.replace('n/a', '').replace('N/A', '')
|
||||
return out
|
||||
return ''
|
||||
|
||||
# lsb_release executable not available, so parse files
|
||||
for distro_name in DISTRO_INFO:
|
||||
|
@ -287,7 +289,7 @@ def shell_split(st: str) -> List[str]:
|
|||
return ret
|
||||
|
||||
|
||||
def find_argument(pos: int, text: str, quoted=True) -> int:
|
||||
def find_argument(pos: int, text: str, quoted: bool = True) -> int:
|
||||
"""
|
||||
Split an input into a list of arguments, return the number of the
|
||||
argument selected by pos.
|
||||
|
@ -342,7 +344,7 @@ def _find_argument_unquoted(pos: int, text: str) -> int:
|
|||
return argnum + 1
|
||||
|
||||
|
||||
def parse_str_to_secs(duration='') -> int:
|
||||
def parse_str_to_secs(duration: str = '') -> int:
|
||||
"""
|
||||
Parse a string of with a number of d, h, m, s.
|
||||
|
||||
|
@ -370,7 +372,7 @@ def parse_str_to_secs(duration='') -> int:
|
|||
return result
|
||||
|
||||
|
||||
def parse_secs_to_str(duration=0) -> str:
|
||||
def parse_secs_to_str(duration: int = 0) -> str:
|
||||
"""
|
||||
Do the reverse operation of :py:func:`parse_str_to_secs`.
|
||||
|
||||
|
@ -457,7 +459,7 @@ def format_gaming_string(infos: Dict[str, str]) -> str:
|
|||
return name
|
||||
|
||||
|
||||
def safeJID(*args, **kwargs) -> JID:
|
||||
def safeJID(*args: Any, **kwargs: Any) -> JID:
|
||||
"""
|
||||
Construct a :py:class:`slixmpp.JID` object from a string.
|
||||
|
||||
|
|
|
@ -15,7 +15,17 @@ import shutil
|
|||
import time
|
||||
import uuid
|
||||
from collections import defaultdict
|
||||
from typing import Callable, Dict, List, Optional, Set, Tuple, Type
|
||||
from typing import (
|
||||
cast,
|
||||
Callable,
|
||||
Dict,
|
||||
List,
|
||||
Optional,
|
||||
Set,
|
||||
Tuple,
|
||||
Type,
|
||||
TypeVar,
|
||||
)
|
||||
from xml.etree import ElementTree as ET
|
||||
from functools import partial
|
||||
|
||||
|
@ -65,6 +75,7 @@ from poezio.ui.types import Message, InfoMessage
|
|||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
T = TypeVar('T', bound=tabs.Tab)
|
||||
|
||||
class Core:
|
||||
"""
|
||||
|
@ -99,8 +110,10 @@ class Core:
|
|||
# that are displayed in almost all tabs, in an
|
||||
# information window.
|
||||
self.information_buffer = TextBuffer()
|
||||
self.information_win_size = config.get(
|
||||
'info_win_height', section='var')
|
||||
self.information_win_size = cast(
|
||||
int,
|
||||
config.get('info_win_height', section='var'),
|
||||
)
|
||||
self.information_win = windows.TextWin(300)
|
||||
self.information_buffer.add_window(self.information_win)
|
||||
self.left_tab_win = None
|
||||
|
@ -813,7 +826,7 @@ class Core:
|
|||
|
||||
####################### XMPP-related actions ##################################
|
||||
|
||||
def get_status(self) -> str:
|
||||
def get_status(self) -> Status:
|
||||
"""
|
||||
Get the last status that was previously set
|
||||
"""
|
||||
|
@ -1016,7 +1029,7 @@ class Core:
|
|||
|
||||
### Tab getters ###
|
||||
|
||||
def get_tabs(self, cls: Type[tabs.Tab] = None) -> List[tabs.Tab]:
|
||||
def get_tabs(self, cls: Type[T] = None) -> List[T]:
|
||||
"Get all the tabs of a type"
|
||||
if cls is None:
|
||||
return self.tabs.get_tabs()
|
||||
|
@ -1324,7 +1337,7 @@ class Core:
|
|||
if tab.name.startswith(room_name):
|
||||
tab.activate(reason=reason)
|
||||
|
||||
def on_user_changed_status_in_private(self, jid: JID, status: str) -> None:
|
||||
def on_user_changed_status_in_private(self, jid: JID, status: Status) -> None:
|
||||
tab = self.tabs.by_name_and_class(jid, tabs.ChatTab)
|
||||
if tab is not None: # display the message in private
|
||||
tab.update_status(status)
|
||||
|
@ -1652,7 +1665,7 @@ class Core:
|
|||
return
|
||||
else:
|
||||
scr = self.stdscr
|
||||
tabs.Tab.resize(scr)
|
||||
tabs.Tab.initial_resize(scr)
|
||||
self.resize_global_info_bar()
|
||||
self.resize_global_information_win()
|
||||
for tab in self.tabs:
|
||||
|
@ -2105,7 +2118,7 @@ class Core:
|
|||
self.bookmarks.get_remote(self.xmpp, self.information,
|
||||
_join_remote_only)
|
||||
|
||||
def room_error(self, error, room_name):
|
||||
def room_error(self, error: IqError, room_name: str) -> None:
|
||||
"""
|
||||
Display the error in the tab
|
||||
"""
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
"""
|
||||
Module defining structures useful to the core class and related methods
|
||||
"""
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Callable, List, Dict
|
||||
|
||||
__all__ = [
|
||||
'ERROR_AND_STATUS_CODES', 'DEPRECATED_ERRORS', 'POSSIBLE_SHOW', 'Status',
|
||||
|
@ -51,23 +53,11 @@ POSSIBLE_SHOW = {
|
|||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class Status:
|
||||
__slots__ = ('show', 'message')
|
||||
|
||||
def __init__(self, show, message):
|
||||
self.show = show
|
||||
self.message = message
|
||||
|
||||
|
||||
class Command:
|
||||
__slots__ = ('func', 'desc', 'comp', 'short_desc', 'usage')
|
||||
|
||||
def __init__(self, func, desc, comp, short_desc, usage):
|
||||
self.func = func
|
||||
self.desc = desc
|
||||
self.comp = comp
|
||||
self.short_desc = short_desc
|
||||
self.usage = usage
|
||||
show: str
|
||||
message: str
|
||||
|
||||
|
||||
class Completion:
|
||||
|
@ -75,8 +65,13 @@ class Completion:
|
|||
A completion result essentially currying the input completion call.
|
||||
"""
|
||||
__slots__ = ['func', 'args', 'kwargs', 'comp_list']
|
||||
|
||||
def __init__(self, func, comp_list, *args, **kwargs):
|
||||
def __init__(
|
||||
self,
|
||||
func: Callable[..., Any],
|
||||
comp_list: List[str],
|
||||
*args: Any,
|
||||
**kwargs: Any
|
||||
) -> None:
|
||||
self.func = func
|
||||
self.comp_list = comp_list
|
||||
self.args = args
|
||||
|
@ -84,3 +79,12 @@ class Completion:
|
|||
|
||||
def run(self):
|
||||
return self.func(self.comp_list, *self.args, **self.kwargs)
|
||||
|
||||
@dataclass
|
||||
class Command:
|
||||
__slots__ = ('func', 'desc', 'comp', 'short_desc', 'usage')
|
||||
func: Callable[..., Any]
|
||||
desc: str
|
||||
comp: Callable[['windows.Input'], Completion]
|
||||
short_desc: str
|
||||
usage: str
|
||||
|
|
|
@ -347,16 +347,16 @@ class Tabs:
|
|||
if new_pos < len(self._tabs):
|
||||
old_tab = self._tabs[old_pos]
|
||||
self._tabs[new_pos], self._tabs[
|
||||
old_pos] = old_tab, tabs.GapTab(self)
|
||||
old_pos] = old_tab, tabs.GapTab(None)
|
||||
else:
|
||||
self._tabs.append(self._tabs[old_pos])
|
||||
self._tabs[old_pos] = tabs.GapTab(self)
|
||||
self._tabs[old_pos] = tabs.GapTab(None)
|
||||
else:
|
||||
if new_pos > old_pos:
|
||||
self._tabs.insert(new_pos, tab)
|
||||
self._tabs[old_pos] = tabs.GapTab(self)
|
||||
self._tabs[old_pos] = tabs.GapTab(None)
|
||||
elif new_pos < old_pos:
|
||||
self._tabs[old_pos] = tabs.GapTab(self)
|
||||
self._tabs[old_pos] = tabs.GapTab(None)
|
||||
self._tabs.insert(new_pos, tab)
|
||||
else:
|
||||
return False
|
||||
|
|
|
@ -1,54 +1,68 @@
|
|||
"""
|
||||
Module containing various decorators
|
||||
"""
|
||||
from typing import Any, Callable, List, Optional
|
||||
from typing import (
|
||||
cast,
|
||||
Any,
|
||||
Callable,
|
||||
List,
|
||||
Optional,
|
||||
TypeVar,
|
||||
TYPE_CHECKING,
|
||||
)
|
||||
|
||||
from poezio import common
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from poezio.tabs import RosterInfoTab
|
||||
|
||||
T = TypeVar('T', bound=Callable[..., Any])
|
||||
|
||||
|
||||
|
||||
class RefreshWrapper:
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
self.core = None
|
||||
|
||||
def conditional(self, func: Callable) -> Callable:
|
||||
def conditional(self, func: T) -> T:
|
||||
"""
|
||||
Decorator to refresh the UI if the wrapped function
|
||||
returns True
|
||||
"""
|
||||
|
||||
def wrap(*args, **kwargs):
|
||||
def wrap(*args: Any, **kwargs: Any) -> Any:
|
||||
ret = func(*args, **kwargs)
|
||||
if self.core and ret:
|
||||
self.core.refresh_window()
|
||||
return ret
|
||||
|
||||
return wrap
|
||||
return cast(T, wrap)
|
||||
|
||||
def always(self, func: Callable) -> Callable:
|
||||
def always(self, func: T) -> T:
|
||||
"""
|
||||
Decorator that refreshs the UI no matter what after the function
|
||||
"""
|
||||
|
||||
def wrap(*args, **kwargs):
|
||||
def wrap(*args: Any, **kwargs: Any) -> Any:
|
||||
ret = func(*args, **kwargs)
|
||||
if self.core:
|
||||
self.core.refresh_window()
|
||||
return ret
|
||||
|
||||
return wrap
|
||||
return cast(T, wrap)
|
||||
|
||||
def update(self, func: Callable) -> Callable:
|
||||
def update(self, func: T) -> T:
|
||||
"""
|
||||
Decorator that only updates the screen
|
||||
"""
|
||||
|
||||
def wrap(*args, **kwargs):
|
||||
def wrap(*args: Any, **kwargs: Any) -> Any:
|
||||
ret = func(*args, **kwargs)
|
||||
if self.core:
|
||||
self.core.doupdate()
|
||||
return ret
|
||||
|
||||
return wrap
|
||||
return cast(T, wrap)
|
||||
|
||||
|
||||
refresh_wrapper = RefreshWrapper()
|
||||
|
@ -61,32 +75,32 @@ class CommandArgParser:
|
|||
"""
|
||||
|
||||
@staticmethod
|
||||
def raw(func: Callable) -> Callable:
|
||||
def raw(func: T) -> T:
|
||||
"""Just call the function with a single string, which is the original string
|
||||
untouched
|
||||
"""
|
||||
|
||||
def wrap(self, args, *a, **kw):
|
||||
def wrap(self: Any, args: Any, *a: Any, **kw: Any) -> Any:
|
||||
return func(self, args, *a, **kw)
|
||||
|
||||
return wrap
|
||||
return cast(T, wrap)
|
||||
|
||||
@staticmethod
|
||||
def ignored(func: Callable) -> Callable:
|
||||
def ignored(func: T) -> T:
|
||||
"""
|
||||
Call the function without any argument
|
||||
"""
|
||||
|
||||
def wrap(self, args=None, *a, **kw):
|
||||
def wrap(self: Any, args: Any = None, *a: Any, **kw: Any) -> Any:
|
||||
return func(self, *a, **kw)
|
||||
|
||||
return wrap
|
||||
return cast(T, wrap)
|
||||
|
||||
@staticmethod
|
||||
def quoted(mandatory: int,
|
||||
optional=0,
|
||||
optional: int = 0,
|
||||
defaults: Optional[List[Any]] = None,
|
||||
ignore_trailing_arguments=False):
|
||||
ignore_trailing_arguments: bool = False) -> Callable[[T], T]:
|
||||
"""The function receives a list with a number of arguments that is between
|
||||
the numbers `mandatory` and `optional`.
|
||||
|
||||
|
@ -131,8 +145,8 @@ class CommandArgParser:
|
|||
"""
|
||||
default_args_outer = defaults or []
|
||||
|
||||
def first(func: Callable):
|
||||
def second(self, args: str, *a, **kw):
|
||||
def first(func: T) -> T:
|
||||
def second(self: Any, args: str, *a: Any, **kw: Any) -> Any:
|
||||
default_args = default_args_outer
|
||||
if args and args.strip():
|
||||
split_args = common.shell_split(args)
|
||||
|
@ -156,8 +170,7 @@ class CommandArgParser:
|
|||
res[-1] += " " + " ".join(split_args)
|
||||
return func(self, res, *a, **kw)
|
||||
|
||||
return second
|
||||
|
||||
return cast(T, second)
|
||||
return first
|
||||
|
||||
|
||||
|
@ -166,11 +179,11 @@ command_args_parser = CommandArgParser()
|
|||
|
||||
def deny_anonymous(func: Callable) -> Callable:
|
||||
"""Decorator to disable commands when using an anonymous account."""
|
||||
def wrap(self: 'RosterInfoTab', *args, **kwargs):
|
||||
def wrap(self: 'RosterInfoTab', *args: Any, **kwargs: Any) -> Any:
|
||||
if self.core.xmpp.anon:
|
||||
return self.core.information(
|
||||
'This command is not available for anonymous accounts.',
|
||||
'Info'
|
||||
)
|
||||
return func(self, *args, **kwargs)
|
||||
return wrap
|
||||
return cast(T, wrap)
|
||||
|
|
|
@ -5,7 +5,8 @@ upstream.
|
|||
TODO: Check that they are fixed and remove those hacks
|
||||
"""
|
||||
|
||||
from slixmpp.stanza import Message
|
||||
from typing import Callable, Any
|
||||
from slixmpp import Message, Iq, ClientXMPP
|
||||
from slixmpp.xmlstream import ET
|
||||
|
||||
import logging
|
||||
|
@ -25,7 +26,7 @@ def has_identity(xmpp, jid, identity, on_true=None, on_false=None):
|
|||
xmpp.plugin['xep_0030'].get_info(jid=jid, callback=_cb)
|
||||
|
||||
|
||||
def get_room_form(xmpp, room, callback):
|
||||
def get_room_form(xmpp: ClientXMPP, room: str, callback: Callable[[Iq], Any]):
|
||||
def _cb(result):
|
||||
if result["type"] == "error":
|
||||
return callback(None)
|
||||
|
|
|
@ -11,18 +11,39 @@ slix plugin
|
|||
"""
|
||||
|
||||
from xml.etree import ElementTree as ET
|
||||
from typing import (
|
||||
Callable,
|
||||
Optional,
|
||||
TYPE_CHECKING,
|
||||
)
|
||||
|
||||
from poezio.common import safeJID
|
||||
from slixmpp import JID
|
||||
from slixmpp.exceptions import IqError, IqTimeout
|
||||
from slixmpp import (
|
||||
JID,
|
||||
ClientXMPP,
|
||||
Iq,
|
||||
)
|
||||
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from poezio.core import Core
|
||||
from poezio.tabs import Tab
|
||||
from slixmpp.plugins.xep_0004 import Form
|
||||
|
||||
|
||||
NS_MUC_ADMIN = 'http://jabber.org/protocol/muc#admin'
|
||||
NS_MUC_OWNER = 'http://jabber.org/protocol/muc#owner'
|
||||
|
||||
|
||||
def destroy_room(xmpp, room, reason='', altroom=''):
|
||||
def destroy_room(
|
||||
xmpp: ClientXMPP,
|
||||
room: str,
|
||||
reason: str = '',
|
||||
altroom: str = ''
|
||||
) -> bool:
|
||||
"""
|
||||
destroy a room
|
||||
"""
|
||||
|
@ -42,7 +63,7 @@ def destroy_room(xmpp, room, reason='', altroom=''):
|
|||
query.append(destroy)
|
||||
iq.append(query)
|
||||
|
||||
def callback(iq):
|
||||
def callback(iq: Iq) -> None:
|
||||
if not iq or iq['type'] == 'error':
|
||||
xmpp.core.information('Unable to destroy room %s' % room, 'Info')
|
||||
else:
|
||||
|
@ -52,23 +73,13 @@ def destroy_room(xmpp, room, reason='', altroom=''):
|
|||
return True
|
||||
|
||||
|
||||
def send_private_message(xmpp, jid, line):
|
||||
"""
|
||||
Send a private message
|
||||
"""
|
||||
jid = safeJID(jid)
|
||||
xmpp.send_message(mto=jid, mbody=line, mtype='chat')
|
||||
|
||||
|
||||
def send_groupchat_message(xmpp, jid, line):
|
||||
"""
|
||||
Send a message to the groupchat
|
||||
"""
|
||||
jid = safeJID(jid)
|
||||
xmpp.send_message(mto=jid, mbody=line, mtype='groupchat')
|
||||
|
||||
|
||||
def change_show(xmpp, jid: JID, own_nick: str, show, status):
|
||||
def change_show(
|
||||
xmpp: ClientXMPP,
|
||||
jid: JID,
|
||||
own_nick: str,
|
||||
show: str,
|
||||
status: Optional[str]
|
||||
) -> None:
|
||||
"""
|
||||
Change our 'Show'
|
||||
"""
|
||||
|
@ -81,7 +92,7 @@ def change_show(xmpp, jid: JID, own_nick: str, show, status):
|
|||
pres.send()
|
||||
|
||||
|
||||
def change_subject(xmpp, jid, subject):
|
||||
def change_subject(xmpp: ClientXMPP, jid: JID, subject: str) -> None:
|
||||
"""
|
||||
Change the room subject
|
||||
"""
|
||||
|
@ -92,7 +103,13 @@ def change_subject(xmpp, jid, subject):
|
|||
msg.send()
|
||||
|
||||
|
||||
def change_nick(core, jid, nick, status=None, show=None):
|
||||
def change_nick(
|
||||
core: 'Core',
|
||||
jid: JID,
|
||||
nick: str,
|
||||
status: Optional[str] = None,
|
||||
show: Optional[str] = None
|
||||
) -> None:
|
||||
"""
|
||||
Change our own nick in a room
|
||||
"""
|
||||
|
@ -103,14 +120,16 @@ def change_nick(core, jid, nick, status=None, show=None):
|
|||
presence.send()
|
||||
|
||||
|
||||
def join_groupchat(core,
|
||||
jid,
|
||||
nick,
|
||||
passwd='',
|
||||
status=None,
|
||||
show=None,
|
||||
seconds=None,
|
||||
tab=None):
|
||||
def join_groupchat(
|
||||
core: 'Core',
|
||||
jid: JID,
|
||||
nick: str,
|
||||
passwd: str = '',
|
||||
status: Optional[str] = None,
|
||||
show: Optional[str] = None,
|
||||
seconds: Optional[int] = None,
|
||||
tab: Optional['Tab'] = None
|
||||
) -> None:
|
||||
xmpp = core.xmpp
|
||||
stanza = xmpp.make_presence(
|
||||
pto='%s/%s' % (jid, nick), pstatus=status, pshow=show)
|
||||
|
@ -119,8 +138,10 @@ def join_groupchat(core,
|
|||
passelement = ET.Element('password')
|
||||
passelement.text = passwd
|
||||
x.append(passelement)
|
||||
def on_disco(iq):
|
||||
if 'urn:xmpp:mam:2' in iq['disco_info'].get_features() or (tab and tab._text_buffer.last_message):
|
||||
|
||||
def on_disco(iq: Iq) -> None:
|
||||
if ('urn:xmpp:mam:2' in iq['disco_info'].get_features()
|
||||
or (tab and tab._text_buffer.last_message)):
|
||||
history = ET.Element('{http://jabber.org/protocol/muc}history')
|
||||
history.attrib['seconds'] = str(0)
|
||||
x.append(history)
|
||||
|
@ -136,13 +157,15 @@ def join_groupchat(core,
|
|||
xmpp.plugin['xep_0045'].rooms[jid] = {}
|
||||
xmpp.plugin['xep_0045'].our_nicks[jid] = to.resource
|
||||
|
||||
try:
|
||||
xmpp.plugin['xep_0030'].get_info(jid=jid, callback=on_disco)
|
||||
except (IqError, IqTimeout):
|
||||
return core.information('Failed to retrieve messages', 'Error')
|
||||
xmpp.plugin['xep_0030'].get_info(jid=jid, callback=on_disco)
|
||||
|
||||
|
||||
def leave_groupchat(xmpp, jid, own_nick, msg):
|
||||
def leave_groupchat(
|
||||
xmpp: ClientXMPP,
|
||||
jid: JID,
|
||||
own_nick: str,
|
||||
msg: str
|
||||
) -> None:
|
||||
"""
|
||||
Leave the groupchat
|
||||
"""
|
||||
|
@ -156,7 +179,14 @@ def leave_groupchat(xmpp, jid, own_nick, msg):
|
|||
exc_info=True)
|
||||
|
||||
|
||||
def set_user_role(xmpp, jid, nick, reason, role, callback=None):
|
||||
def set_user_role(
|
||||
xmpp: ClientXMPP,
|
||||
jid: JID,
|
||||
nick: str,
|
||||
reason: str,
|
||||
role: str,
|
||||
callback: Callable[[Iq], None]
|
||||
) -> None:
|
||||
"""
|
||||
(try to) Set the role of a MUC user
|
||||
(role = 'none': eject user)
|
||||
|
@ -172,21 +202,18 @@ def set_user_role(xmpp, jid, nick, reason, role, callback=None):
|
|||
query.append(item)
|
||||
iq.append(query)
|
||||
iq['to'] = jid
|
||||
if callback:
|
||||
return iq.send(callback=callback)
|
||||
try:
|
||||
return iq.send()
|
||||
except (IqError, IqTimeout) as e:
|
||||
return e.iq
|
||||
iq.send(callback=callback)
|
||||
|
||||
|
||||
def set_user_affiliation(xmpp,
|
||||
muc_jid,
|
||||
affiliation,
|
||||
nick=None,
|
||||
jid=None,
|
||||
reason=None,
|
||||
callback=None):
|
||||
def set_user_affiliation(
|
||||
xmpp: ClientXMPP,
|
||||
muc_jid: JID,
|
||||
affiliation: str,
|
||||
callback: Callable[[Iq], None],
|
||||
nick: Optional[str] = None,
|
||||
jid: Optional[JID] = None,
|
||||
reason: Optional[str] = None
|
||||
) -> None:
|
||||
"""
|
||||
(try to) Set the affiliation of a MUC user
|
||||
"""
|
||||
|
@ -212,18 +239,10 @@ def set_user_affiliation(xmpp,
|
|||
query.append(item)
|
||||
iq = xmpp.make_iq_set(query)
|
||||
iq['to'] = muc_jid
|
||||
if callback:
|
||||
return iq.send(callback=callback)
|
||||
try:
|
||||
return xmpp.plugin['xep_0045'].set_affiliation(
|
||||
str(muc_jid),
|
||||
str(jid) if jid else None, nick, affiliation)
|
||||
except:
|
||||
log.debug('Error setting the affiliation: %s', exc_info=True)
|
||||
return False
|
||||
iq.send(callback=callback)
|
||||
|
||||
|
||||
def cancel_config(xmpp, room):
|
||||
def cancel_config(xmpp: ClientXMPP, room: str) -> None:
|
||||
query = ET.Element('{http://jabber.org/protocol/muc#owner}query')
|
||||
x = ET.Element('{jabber:x:data}x', type='cancel')
|
||||
query.append(x)
|
||||
|
@ -232,7 +251,7 @@ def cancel_config(xmpp, room):
|
|||
iq.send()
|
||||
|
||||
|
||||
def configure_room(xmpp, room, form):
|
||||
def configure_room(xmpp: ClientXMPP, room: str, form: 'Form') -> None:
|
||||
if form is None:
|
||||
return
|
||||
iq = xmpp.make_iq_set()
|
||||
|
|
|
@ -104,7 +104,7 @@ def main():
|
|||
logger.create_logger()
|
||||
|
||||
from poezio import roster
|
||||
roster.create_roster()
|
||||
roster.roster.reset()
|
||||
|
||||
from poezio.core.core import Core
|
||||
|
||||
|
|
45
poezio/poezio_shlex.pyi
Normal file
45
poezio/poezio_shlex.pyi
Normal file
|
@ -0,0 +1,45 @@
|
|||
from typing import List, Tuple, Any, TextIO, Union, Optional, Iterable, TypeVar
|
||||
import sys
|
||||
|
||||
def split(s: str, comments: bool = ..., posix: bool = ...) -> List[str]: ...
|
||||
if sys.version_info >= (3, 8):
|
||||
def join(split_command: Iterable[str]) -> str: ...
|
||||
def quote(s: str) -> str: ...
|
||||
|
||||
_SLT = TypeVar('_SLT', bound=shlex)
|
||||
|
||||
class shlex(Iterable[str]):
|
||||
commenters: str
|
||||
wordchars: str
|
||||
whitespace: str
|
||||
escape: str
|
||||
quotes: str
|
||||
escapedquotes: str
|
||||
whitespace_split: bool
|
||||
infile: str
|
||||
instream: TextIO
|
||||
source: str
|
||||
debug: int
|
||||
lineno: int
|
||||
token: str
|
||||
eof: str
|
||||
if sys.version_info >= (3, 6):
|
||||
punctuation_chars: str
|
||||
|
||||
if sys.version_info >= (3, 6):
|
||||
def __init__(self, instream: Union[str, TextIO] = ..., infile: Optional[str] = ...,
|
||||
posix: bool = ..., punctuation_chars: Union[bool, str] = ...) -> None: ...
|
||||
else:
|
||||
def __init__(self, instream: Union[str, TextIO] = ..., infile: Optional[str] = ...,
|
||||
posix: bool = ...) -> None: ...
|
||||
def get_token(self) -> Tuple[int, int, str]: ...
|
||||
def push_token(self, tok: str) -> None: ...
|
||||
def read_token(self) -> str: ...
|
||||
def sourcehook(self, filename: str) -> Tuple[str, TextIO]: ...
|
||||
# TODO argument types
|
||||
def push_source(self, newstream: Any, newfile: Any = ...) -> None: ...
|
||||
def pop_source(self) -> None: ...
|
||||
def error_leader(self, infile: str = ...,
|
||||
lineno: int = ...) -> None: ...
|
||||
def __iter__(self: _SLT) -> _SLT: ...
|
||||
def __next__(self) -> str: ...
|
|
@ -10,6 +10,8 @@ Defines the Roster and RosterGroup classes
|
|||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
from typing import List
|
||||
|
||||
from poezio.config import config
|
||||
from poezio.contact import Contact
|
||||
from poezio.roster_sorting import SORTING_METHODS, GROUP_SORTING_METHODS
|
||||
|
@ -18,6 +20,7 @@ from os import path as p
|
|||
from datetime import datetime
|
||||
from poezio.common import safeJID
|
||||
from slixmpp.exceptions import IqError, IqTimeout
|
||||
from slixmpp import JID
|
||||
|
||||
|
||||
class Roster:
|
||||
|
@ -29,6 +32,22 @@ class Roster:
|
|||
DEFAULT_FILTER = (lambda x, y: None, None)
|
||||
|
||||
def __init__(self):
|
||||
self.__node = None
|
||||
|
||||
# A tuple(function, *args) function to filter contacts
|
||||
# on search, for example
|
||||
self.contact_filter = self.DEFAULT_FILTER
|
||||
self.groups = {}
|
||||
self.contacts = {}
|
||||
self.length = 0
|
||||
self.connected = 0
|
||||
self.folded_groups = []
|
||||
|
||||
# Used for caching roster infos
|
||||
self.last_built = datetime.now()
|
||||
self.last_modified = datetime.now()
|
||||
|
||||
def reset(self):
|
||||
"""
|
||||
node: the RosterSingle from slixmpp
|
||||
"""
|
||||
|
@ -143,7 +162,7 @@ class Roster:
|
|||
"""Subscribe to a jid"""
|
||||
self.__node.subscribe(jid)
|
||||
|
||||
def jids(self):
|
||||
def jids(self) -> List[JID]:
|
||||
"""List of the contact JIDS"""
|
||||
l = []
|
||||
for key in self.__node.keys():
|
||||
|
@ -335,11 +354,6 @@ class RosterGroup:
|
|||
return len([1 for contact in self.contacts if len(contact)])
|
||||
|
||||
|
||||
def create_roster():
|
||||
"Create the global roster object"
|
||||
global roster
|
||||
roster = Roster()
|
||||
|
||||
|
||||
# Shared roster object
|
||||
roster = None
|
||||
roster = Roster()
|
||||
|
|
|
@ -28,6 +28,7 @@ from typing import (
|
|||
List,
|
||||
Optional,
|
||||
Union,
|
||||
Tuple,
|
||||
TYPE_CHECKING,
|
||||
)
|
||||
|
||||
|
@ -52,6 +53,8 @@ from slixmpp import JID, InvalidJID, Message as SMessage
|
|||
|
||||
if TYPE_CHECKING:
|
||||
from _curses import _CursesWindow # pylint: disable=E0611
|
||||
from poezio.size_manager import SizeManager
|
||||
from poezio.core.core import Core
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -117,7 +120,7 @@ class Tab:
|
|||
height = 1
|
||||
width = 1
|
||||
|
||||
def __init__(self, core):
|
||||
def __init__(self, core: 'Core'):
|
||||
self.core = core
|
||||
self.nb = 0
|
||||
if not hasattr(self, 'name'):
|
||||
|
@ -133,7 +136,7 @@ class Tab:
|
|||
self.commands = {} # and their own commands
|
||||
|
||||
@property
|
||||
def size(self) -> int:
|
||||
def size(self) -> 'SizeManager':
|
||||
return self.core.size
|
||||
|
||||
@staticmethod
|
||||
|
@ -196,7 +199,7 @@ class Tab:
|
|||
self._state = 'normal'
|
||||
|
||||
@staticmethod
|
||||
def resize(scr: '_CursesWindow'):
|
||||
def initial_resize(scr: '_CursesWindow'):
|
||||
Tab.height, Tab.width = scr.getmaxyx()
|
||||
windows.base_wins.TAB_WIN = scr
|
||||
|
||||
|
@ -327,7 +330,7 @@ class Tab:
|
|||
else:
|
||||
return False
|
||||
|
||||
def refresh_tab_win(self):
|
||||
def refresh_tab_win(self) -> None:
|
||||
if config.get('enable_vertical_tab_list'):
|
||||
left_tab_win = self.core.left_tab_win
|
||||
if left_tab_win and not self.size.core_degrade_x:
|
||||
|
@ -371,12 +374,12 @@ class Tab:
|
|||
"""
|
||||
pass
|
||||
|
||||
def update_commands(self):
|
||||
def update_commands(self) -> None:
|
||||
for c in self.plugin_commands:
|
||||
if c not in self.commands:
|
||||
self.commands[c] = self.plugin_commands[c]
|
||||
|
||||
def update_keys(self):
|
||||
def update_keys(self) -> None:
|
||||
for k in self.plugin_keys:
|
||||
if k not in self.key_func:
|
||||
self.key_func[k] = self.plugin_keys[k]
|
||||
|
@ -435,7 +438,7 @@ class Tab:
|
|||
"""
|
||||
pass
|
||||
|
||||
def on_close(self):
|
||||
def on_close(self) -> None:
|
||||
"""
|
||||
Called when the tab is to be closed
|
||||
"""
|
||||
|
@ -443,7 +446,7 @@ class Tab:
|
|||
self.input.on_delete()
|
||||
self.closed = True
|
||||
|
||||
def matching_names(self) -> List[str]:
|
||||
def matching_names(self) -> List[Tuple[int, str]]:
|
||||
"""
|
||||
Returns a list of strings that are used to name a tab with the /win
|
||||
command. For example you could switch to a tab that returns
|
||||
|
@ -532,7 +535,7 @@ class ChatTab(Tab):
|
|||
desc='Fix the last message with whatever you want.',
|
||||
shortdesc='Correct the last message.',
|
||||
completion=self.completion_correct)
|
||||
self.chat_state = None
|
||||
self.chat_state = None # type: Optional[str]
|
||||
self.update_commands()
|
||||
self.update_keys()
|
||||
|
||||
|
@ -667,11 +670,11 @@ class ChatTab(Tab):
|
|||
self._text_buffer.messages = []
|
||||
self.text_win.rebuild_everything(self._text_buffer)
|
||||
|
||||
def check_send_chat_state(self):
|
||||
def check_send_chat_state(self) -> bool:
|
||||
"If we should send a chat state"
|
||||
return True
|
||||
|
||||
def send_chat_state(self, state, always_send=False):
|
||||
def send_chat_state(self, state: str, always_send: bool = False) -> None:
|
||||
"""
|
||||
Send an empty chatstate message
|
||||
"""
|
||||
|
@ -691,9 +694,8 @@ class ChatTab(Tab):
|
|||
x = ET.Element('{%s}x' % NS_MUC_USER)
|
||||
msg.append(x)
|
||||
msg.send()
|
||||
return True
|
||||
|
||||
def send_composing_chat_state(self, empty_after):
|
||||
def send_composing_chat_state(self, empty_after: bool) -> None:
|
||||
"""
|
||||
Send the "active" or "composing" chatstate, depending
|
||||
on the the current status of the input
|
||||
|
@ -729,7 +731,7 @@ class ChatTab(Tab):
|
|||
self.core.add_timed_event(new_event)
|
||||
self.timed_event_not_paused = new_event
|
||||
|
||||
def cancel_paused_delay(self):
|
||||
def cancel_paused_delay(self) -> None:
|
||||
"""
|
||||
Remove that event from the list and set it to None.
|
||||
Called for example when the input is emptied, or when the message
|
||||
|
@ -741,7 +743,7 @@ class ChatTab(Tab):
|
|||
self.core.remove_timed_event(self.timed_event_not_paused)
|
||||
self.timed_event_not_paused = None
|
||||
|
||||
def set_last_sent_message(self, msg, correct=False):
|
||||
def set_last_sent_message(self, msg: SMessage, correct: bool = False) -> None:
|
||||
"""Ensure last_sent_message is set with the correct attributes"""
|
||||
if correct:
|
||||
# XXX: Is the copy needed. Is the object passed here reused
|
||||
|
@ -751,7 +753,7 @@ class ChatTab(Tab):
|
|||
self.last_sent_message = msg
|
||||
|
||||
@command_args_parser.raw
|
||||
def command_correct(self, line):
|
||||
def command_correct(self, line: str) -> None:
|
||||
"""
|
||||
/correct <fixed message>
|
||||
"""
|
||||
|
@ -777,7 +779,7 @@ class ChatTab(Tab):
|
|||
return self.core.status.show in ('xa', 'away') or\
|
||||
(hasattr(self, 'directed_presence') and not self.directed_presence)
|
||||
|
||||
def move_separator(self):
|
||||
def move_separator(self) -> None:
|
||||
self.text_win.remove_line_separator()
|
||||
self.text_win.add_line_separator(self._text_buffer)
|
||||
self.text_win.refresh()
|
||||
|
@ -786,7 +788,7 @@ class ChatTab(Tab):
|
|||
def get_conversation_messages(self):
|
||||
return self._text_buffer.messages
|
||||
|
||||
def check_scrolled(self):
|
||||
def check_scrolled(self) -> None:
|
||||
if self.text_win.pos != 0:
|
||||
self.state = 'scrolled'
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -145,7 +145,7 @@ class PrivateTab(OneToOneTab):
|
|||
|
||||
@refresh_wrapper.always
|
||||
@command_args_parser.raw
|
||||
def command_say(self, line, attention=False, correct=False):
|
||||
def command_say(self, line: str, attention: bool = False, correct: bool = False) -> None:
|
||||
if not self.on:
|
||||
return
|
||||
our_jid = JID(self.jid.bare)
|
||||
|
|
|
@ -32,6 +32,7 @@ from poezio.ui.types import (
|
|||
|
||||
if TYPE_CHECKING:
|
||||
from poezio.windows.text_win import TextWin
|
||||
from poezio.user import User
|
||||
|
||||
|
||||
class CorrectionError(Exception):
|
||||
|
@ -249,7 +250,7 @@ class TextBuffer:
|
|||
new_id: str,
|
||||
highlight: bool = False,
|
||||
time: Optional[datetime] = None,
|
||||
user: Optional[str] = None,
|
||||
user: Optional['User'] = None,
|
||||
jid: Optional[str] = None) -> Message:
|
||||
"""
|
||||
Correct a message in a text buffer.
|
||||
|
|
|
@ -55,7 +55,7 @@ class User:
|
|||
else:
|
||||
self.color = choice(get_theme().LIST_COLOR_NICKNAMES)
|
||||
|
||||
def set_deterministic_color(self):
|
||||
def set_deterministic_color(self) -> None:
|
||||
theme = get_theme()
|
||||
if theme.ccg_palette:
|
||||
# use XEP-0392 CCG
|
||||
|
|
|
@ -3,6 +3,8 @@ Module defining all the "info wins", ie the bar which is on top of the
|
|||
info buffer in normal tabs
|
||||
"""
|
||||
|
||||
from typing import Optional, Dict, TYPE_CHECKING, Any
|
||||
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -13,6 +15,11 @@ from poezio.windows.base_wins import Win
|
|||
from poezio.ui.funcs import truncate_nick
|
||||
from poezio.theming import get_theme, to_curses_attr
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from poezio.user import User
|
||||
from poezio.tabs import MucTab
|
||||
from poezio.windows import TextWin
|
||||
|
||||
|
||||
class InfoWin(Win):
|
||||
"""
|
||||
|
@ -260,10 +267,16 @@ class MucInfoWin(InfoWin):
|
|||
|
||||
__slots__ = ()
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
InfoWin.__init__(self)
|
||||
|
||||
def refresh(self, room, window=None, user=None, information=None):
|
||||
def refresh(
|
||||
self,
|
||||
room: 'MucTab',
|
||||
window: Optional['TextWin'] = None,
|
||||
user: Optional['User'] = None,
|
||||
information: Optional[Dict[str, Any]] = None
|
||||
) -> None:
|
||||
log.debug('Refresh: %s', self.__class__.__name__)
|
||||
self._win.erase()
|
||||
self.write_room_name(room)
|
||||
|
|
Loading…
Reference in a new issue