summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormathieui <mathieui@mathieui.net>2018-07-22 14:23:39 +0200
committermathieui <mathieui@mathieui.net>2018-07-22 14:25:18 +0200
commite2414121af16474744d012cdb8466de6ae3136e4 (patch)
treee7d90b34da1d971e6e14cd8707837cab9200f0a3
parent3cb8e33f938db6bb6e86dd349f8b56676b83556f (diff)
downloadpoezio-e2414121af16474744d012cdb8466de6ae3136e4.tar.gz
poezio-e2414121af16474744d012cdb8466de6ae3136e4.tar.bz2
poezio-e2414121af16474744d012cdb8466de6ae3136e4.tar.xz
poezio-e2414121af16474744d012cdb8466de6ae3136e4.zip
Add type hints here and there
-rw-r--r--poezio/bookmarks.py21
-rw-r--r--poezio/common.py67
-rw-r--r--poezio/config.py68
-rw-r--r--poezio/contact.py57
-rw-r--r--poezio/events.py11
-rw-r--r--poezio/pep.py6
-rw-r--r--poezio/timed_events.py9
-rw-r--r--poezio/user.py52
-rw-r--r--poezio/xdg.py5
-rw-r--r--poezio/xhtml.py51
10 files changed, 187 insertions, 160 deletions
diff --git a/poezio/bookmarks.py b/poezio/bookmarks.py
index 3e3893f4..91f862c8 100644
--- a/poezio/bookmarks.py
+++ b/poezio/bookmarks.py
@@ -30,9 +30,10 @@ Adding a remote bookmark:
import functools
import logging
+from typing import Optional, List
-from slixmpp.plugins.xep_0048 import Bookmarks, Conference, URL
from slixmpp import JID
+from slixmpp.plugins.xep_0048 import Bookmarks, Conference, URL
from poezio.common import safeJID
from poezio.config import config
@@ -41,11 +42,11 @@ log = logging.getLogger(__name__)
class Bookmark:
def __init__(self,
- jid,
- name=None,
+ jid: JID,
+ name: Optional[str] = None,
autojoin=False,
- nick=None,
- password=None,
+ nick: Optional[str] = None,
+ password: Optional[str] = None,
method='local'):
self.jid = jid
self.name = name or jid
@@ -55,21 +56,21 @@ class Bookmark:
self._method = method
@property
- def method(self):
+ def method(self) -> str:
return self._method
@method.setter
- def method(self, value):
+ def method(self, value: str):
if value not in ('local', 'remote'):
log.debug('Could not set bookmark storing method: %s', value)
return
self._method = value
- def __repr__(self):
+ def __repr__(self) -> str:
return '<%s%s|%s>' % (self.jid, ('/' + self.nick)
if self.nick else '', self.method)
- def stanza(self):
+ def stanza(self) -> Conference:
"""
Generate a <conference/> stanza from the instance
"""
@@ -83,7 +84,7 @@ class Bookmark:
el['password'] = self.password
return el
- def local(self):
+ def local(self) -> str:
"""Generate a str for local storage"""
local = self.jid
if self.nick:
diff --git a/poezio/common.py b/poezio/common.py
index 3390b7b6..a021d898 100644
--- a/poezio/common.py
+++ b/poezio/common.py
@@ -10,19 +10,18 @@ Various useful functions.
from datetime import datetime, timedelta
from pathlib import Path
-from slixmpp import JID, InvalidJID
-from poezio.poezio_shlex import shlex
+from typing import Dict, List, Optional, Tuple, Union
-import base64
import os
-import mimetypes
-import hashlib
import subprocess
import time
import string
+from slixmpp import JID, InvalidJID, Message
+from poezio.poezio_shlex import shlex
+
-def _get_output_of_command(command):
+def _get_output_of_command(command: str) -> Optional[List[str]]:
"""
Runs a command and returns its output.
@@ -37,7 +36,7 @@ def _get_output_of_command(command):
return None
-def _is_in_path(command, return_abs_path=False):
+def _is_in_path(command: str, return_abs_path=False) -> Union[bool, str]:
"""
Check if *command* is in the $PATH or not.
@@ -53,8 +52,7 @@ def _is_in_path(command, return_abs_path=False):
if command in os.listdir(directory):
if return_abs_path:
return os.path.join(directory, command)
- else:
- return True
+ return True
except OSError:
# If the user has non directories in his path
pass
@@ -84,7 +82,7 @@ DISTRO_INFO = {
}
-def get_os_info():
+def get_os_info() -> str:
"""
Returns a detailed and well formatted string containing
information about the operating system
@@ -146,7 +144,7 @@ def get_os_info():
return os_info
-def _datetime_tuple(timestamp):
+def _datetime_tuple(timestamp: str) -> datetime:
"""
Convert a timestamp using strptime and the format: %Y%m%dT%H:%M:%S.
@@ -172,10 +170,10 @@ def _datetime_tuple(timestamp):
try:
if tz_msg and tz_msg != 'Z':
tz_mod = -1 if tz_msg[0] == '-' else 1
- tz_msg = time.strptime(tz_msg[1:], '%H%M')
- tz_msg = tz_msg.tm_hour * 3600 + tz_msg.tm_min * 60
- tz_msg = timedelta(seconds=tz_mod * tz_msg)
- ret -= tz_msg
+ tz_parsed = time.strptime(tz_msg[1:], '%H%M')
+ tz_seconds = tz_parsed.tm_hour * 3600 + tz_parsed.tm_min * 60
+ delta = timedelta(seconds=tz_mod * tz_seconds)
+ ret -= delta
except ValueError:
pass # ignore if we got a badly-formatted offset
# convert UTC to local time, with DST etc.
@@ -187,7 +185,7 @@ def _datetime_tuple(timestamp):
return ret
-def get_utc_time(local_time=None):
+def get_utc_time(local_time: Optional[datetime] = None) -> datetime:
"""
Get the current UTC time
@@ -210,7 +208,7 @@ def get_utc_time(local_time=None):
return utc_time
-def get_local_time(utc_time):
+def get_local_time(utc_time: datetime) -> datetime:
"""
Get the local time from an UTC time
"""
@@ -226,7 +224,7 @@ def get_local_time(utc_time):
return local_time
-def find_delayed_tag(message):
+def find_delayed_tag(message: Message) -> Tuple[bool, datetime]:
"""
Check if a message is delayed or not.
@@ -253,7 +251,7 @@ def find_delayed_tag(message):
return (delayed, date)
-def shell_split(st):
+def shell_split(st: str) -> List[str]:
"""
Split a string correctly according to the quotes
around the elements.
@@ -276,7 +274,7 @@ def shell_split(st):
return ret
-def find_argument(pos, text, quoted=True):
+def find_argument(pos: int, text: str, quoted=True) -> int:
"""
Split an input into a list of arguments, return the number of the
argument selected by pos.
@@ -293,11 +291,10 @@ def find_argument(pos, text, quoted=True):
"""
if quoted:
return _find_argument_quoted(pos, text)
- else:
- return _find_argument_unquoted(pos, text)
+ return _find_argument_unquoted(pos, text)
-def _find_argument_quoted(pos, text):
+def _find_argument_quoted(pos: int, text: str) -> int:
"""
Get the number of the argument at position pos in
a string with possibly quoted text.
@@ -314,7 +311,7 @@ def _find_argument_quoted(pos, text):
return count + 1
-def _find_argument_unquoted(pos, text):
+def _find_argument_unquoted(pos: int, text: str) -> int:
"""
Get the number of the argument at position pos in
a string without interpreting quotes.
@@ -332,7 +329,7 @@ def _find_argument_unquoted(pos, text):
return argnum + 1
-def parse_str_to_secs(duration=''):
+def parse_str_to_secs(duration='') -> int:
"""
Parse a string of with a number of d, h, m, s.
@@ -360,7 +357,7 @@ def parse_str_to_secs(duration=''):
return result
-def parse_secs_to_str(duration=0):
+def parse_secs_to_str(duration=0) -> str:
"""
Do the reverse operation of :py:func:`parse_str_to_secs`.
@@ -390,7 +387,7 @@ def parse_secs_to_str(duration=0):
return result
-def format_tune_string(infos):
+def format_tune_string(infos: Dict[str, str]) -> str:
"""
Contruct a string from a dict created from an "User tune" event.
@@ -417,18 +414,18 @@ def format_tune_string(infos):
rating = infos.get('rating')
if rating:
elems.append('[ ' + rating + '/10 ]')
- length = infos.get('length')
- if length:
- length = int(length)
+ length_str = infos.get('length')
+ if length_str:
+ length = int(length_str)
secs = length % 60
mins = length // 60
- secs = str(secs).zfill(2)
- mins = str(mins).zfill(2)
- elems.append('[' + mins + ':' + secs + ']')
+ secs_str = str(secs).zfill(2)
+ mins_str = str(mins).zfill(2)
+ elems.append('[' + mins_str + ':' + secs_str + ']')
return ' '.join(elems)
-def format_gaming_string(infos):
+def format_gaming_string(infos: Dict[str, str]) -> str:
"""
Construct a string from a dict containing "user gaming" information.
(for now, only use address and name)
@@ -447,7 +444,7 @@ def format_gaming_string(infos):
return name
-def safeJID(*args, **kwargs):
+def safeJID(*args, **kwargs) -> JID:
"""
Construct a :py:class:`slixmpp.JID` object from a string.
diff --git a/poezio/config.py b/poezio/config.py
index e6e5cbcc..8f35e19c 100644
--- a/poezio/config.py
+++ b/poezio/config.py
@@ -10,8 +10,6 @@ TODO: get http://bugs.python.org/issue1410680 fixed, one day, in order
to remove our ugly custom I/O methods.
"""
-DEFSECTION = "Poezio"
-
import logging.config
import os
import stat
@@ -19,12 +17,17 @@ import sys
import pkg_resources
from configparser import RawConfigParser, NoOptionError, NoSectionError
-from os import remove
-from shutil import copy2
from pathlib import Path
+from shutil import copy2
+from typing import Callable, Dict, List, Optional, Union, Tuple
+
from poezio.args import parse_args
from poezio import xdg
+ConfigValue = Union[str, int, float, bool]
+
+DEFSECTION = "Poezio"
+
DEFAULT_CONFIG = {
'Poezio': {
'ack_message_receipts': True,
@@ -159,7 +162,7 @@ class Config(RawConfigParser):
load/save the config to a file
"""
- def __init__(self, file_name, default=None):
+ def __init__(self, file_name: Path, default=None) -> None:
RawConfigParser.__init__(self, None)
# make the options case sensitive
self.optionxform = str
@@ -176,7 +179,10 @@ class Config(RawConfigParser):
if not self.has_section(section):
self.add_section(section)
- def get(self, option, default=None, section=DEFSECTION):
+ def get(self,
+ option: str,
+ default: Optional[ConfigValue] = None,
+ section=DEFSECTION) -> ConfigValue:
"""
get a value from the config but return
a default value if it is not found
@@ -190,12 +196,12 @@ class Config(RawConfigParser):
default = ''
try:
- if type(default) == int:
+ if isinstance(default, bool):
+ res = self.getboolean(option, section)
+ elif isinstance(default, int):
res = self.getint(option, section)
- elif type(default) == float:
+ elif isinstance(default, float):
res = self.getfloat(option, section)
- elif type(default) == bool:
- res = self.getboolean(option, section)
else:
res = self.getstr(option, section)
except (NoOptionError, NoSectionError, ValueError, AttributeError):
@@ -279,7 +285,8 @@ class Config(RawConfigParser):
"""
return RawConfigParser.getboolean(self, section, option)
- def write_in_file(self, section, option, value):
+ def write_in_file(self, section: str, option: str,
+ value: ConfigValue) -> bool:
"""
Our own way to save write the value in the file
Just find the right section, and then find the
@@ -305,7 +312,7 @@ class Config(RawConfigParser):
return self._write_file(result_lines)
- def remove_in_file(self, section, option):
+ def remove_in_file(self, section: str, option: str) -> bool:
"""
Our own way to remove an option from the file.
"""
@@ -334,7 +341,7 @@ class Config(RawConfigParser):
return self._write_file(result_lines)
- def _write_file(self, lines):
+ def _write_file(self, lines: List[str]) -> bool:
"""
Write the config file, write to a temporary file
before copying it to the final destination
@@ -360,7 +367,7 @@ class Config(RawConfigParser):
success = True
return success
- def _parse_file(self):
+ def _parse_file(self) -> Optional[Tuple[Dict[str, List[int]], List[str]]]:
"""
Parse the config file and return the list of sections with
their start and end positions, and the lines in the file.
@@ -372,17 +379,18 @@ class Config(RawConfigParser):
if file_ok(self.file_name):
try:
with self.file_name.open('r', encoding='utf-8') as df:
- lines_before = [line.strip() for line in df]
+ lines_before = [line.strip()
+ for line in df] # type: List[str]
except OSError:
log.error(
'Unable to read the config file %s',
self.file_name,
exc_info=True)
- return tuple()
+ return None
else:
lines_before = []
- sections = {}
+ sections = {} # type: Dict[str, List[int]]
duplicate_section = False
current_section = ''
current_line = 0
@@ -408,7 +416,8 @@ class Config(RawConfigParser):
return (sections, lines_before)
- def set_and_save(self, option, value, section=DEFSECTION):
+ def set_and_save(self, option: str, value: ConfigValue,
+ section=DEFSECTION) -> Tuple[str, str]:
"""
set the value in the configuration then save it
to the file
@@ -439,7 +448,8 @@ class Config(RawConfigParser):
return ('Unable to write in the config file', 'Error')
return ("%s=%s" % (option, value), 'Info')
- def remove_and_save(self, option, section=DEFSECTION):
+ def remove_and_save(self, option: str,
+ section=DEFSECTION) -> Tuple[str, str]:
"""
Remove an option and then save it the config file
"""
@@ -449,7 +459,7 @@ class Config(RawConfigParser):
return ('Unable to save the config file', 'Error')
return ('Option %s deleted' % option, 'Info')
- def silent_set(self, option, value, section=DEFSECTION):
+ def silent_set(self, option: str, value: ConfigValue, section=DEFSECTION):
"""
Set a value, save, and return True on success and False on failure
"""
@@ -460,7 +470,7 @@ class Config(RawConfigParser):
RawConfigParser.set(self, section, option, value)
return self.write_in_file(section, option, value)
- def set(self, option, value, section=DEFSECTION):
+ def set(self, option: str, value: ConfigValue, section=DEFSECTION):
"""
Set the value of an option temporarily
"""
@@ -469,11 +479,11 @@ class Config(RawConfigParser):
except NoSectionError:
pass
- def to_dict(self):
+ def to_dict(self) -> Dict[str, Dict[str, ConfigValue]]:
"""
Returns a dict of the form {section: {option: value, option: value}, …}
"""
- res = {}
+ res = {} # Dict[str, Dict[str, ConfigValue]]
for section in self.sections():
res[section] = {}
for option in self.options(section):
@@ -481,7 +491,7 @@ class Config(RawConfigParser):
return res
-def find_line(lines, start, end, option):
+def find_line(lines: List[str], start: int, end: int, option: str) -> int:
"""
Get the number of the line containing the option in the
relevant part of the config file.
@@ -497,7 +507,7 @@ def find_line(lines, start, end, option):
return -1
-def file_ok(filepath):
+def file_ok(filepath: Path) -> bool:
"""
Returns True if the file exists and is readable and writeable,
False otherwise.
@@ -507,7 +517,7 @@ def file_ok(filepath):
return bool(val)
-def get_image_cache():
+def get_image_cache() -> Path:
if not config.get('extract_inline_images'):
return None
tmp_dir = config.get('tmp_image_dir')
@@ -664,16 +674,16 @@ LOGGING_CONFIG = {
firstrun = False
# Global config object. Is setup in poezio.py
-config = None
+config = None # type: Optional[Config]
# The logger object for this module
-log = None
+log = None # type: Optional[logging.Logger]
# The command-line options
options = None
# delayed import from common.py
-safeJID = None
+safeJID = None # type: Optional[Callable]
# the global log dir
LOG_DIR = ''
diff --git a/poezio/contact.py b/poezio/contact.py
index b07be6f6..27b0598c 100644
--- a/poezio/contact.py
+++ b/poezio/contact.py
@@ -9,11 +9,14 @@ Defines the Resource and Contact classes, which are used in
the roster.
"""
+from collections import defaultdict
import logging
-log = logging.getLogger(__name__)
+from typing import Dict, Iterator, List, Optional, Union
from poezio.common import safeJID
-from collections import defaultdict
+from slixmpp import JID
+
+log = logging.getLogger(__name__)
class Resource:
@@ -26,29 +29,30 @@ class Resource:
"""
data: the dict to use as a source
"""
- self._jid = jid # Full jid
- self._data = data
+ # Full JID
+ self._jid = jid # type: str
+ self._data = data # type: Dict[str, Union[str, int]]
@property
- def jid(self):
+ def jid(self) -> str:
return self._jid
@property
- def priority(self):
+ def priority(self) -> int:
return self._data.get('priority') or 0
@property
- def presence(self):
+ def presence(self) -> str:
return self._data.get('show') or ''
@property
- def status(self):
+ def status(self) -> str:
return self._data.get('status') or ''
- def __repr__(self):
+ def __repr__(self) -> str:
return '<%s>' % self._jid
- def __eq__(self, value):
+ def __eq__(self, value: object) -> bool:
if not isinstance(value, Resource):
return False
return self.jid == value.jid and self._data == value._data
@@ -66,22 +70,22 @@ class Contact:
item: a slixmpp RosterItem pointing to that contact
"""
self.__item = item
- self.folded_states = defaultdict(lambda: True)
+ self.folded_states = defaultdict(lambda: True) # type: Dict[str, bool]
self._name = ''
self.avatar = None
self.error = None
- self.tune = {}
- self.gaming = {}
+ self.tune = {} # type: Dict[str, str]
+ self.gaming = {} # type: Dict[str, str]
self.mood = ''
self.activity = ''
@property
- def groups(self):
+ def groups(self) -> List[str]:
"""Name of the groups the contact is in"""
return self.__item['groups'] or ['none']
@property
- def bare_jid(self):
+ def bare_jid(self) -> JID:
"""The bare jid of the contact"""
return self.__item.jid
@@ -119,29 +123,29 @@ class Contact:
self.__item['pending_out'] = value
@property
- def resources(self):
+ def resources(self) -> Iterator[Resource]:
"""List of the available resources as Resource objects"""
return (Resource('%s%s' % (self.bare_jid, ('/' + key)
if key else ''), self.__item.resources[key])
for key in self.__item.resources.keys())
@property
- def subscription(self):
+ def subscription(self) -> str:
return self.__item['subscription']
def __contains__(self, value):
return value in self.__item.resources or safeJID(
value).resource in self.__item.resources
- def __len__(self):
+ def __len__(self) -> int:
"""Number of resources"""
return len(self.__item.resources)
- def __bool__(self):
- """This contacts exists even when he has no resources"""
+ def __bool__(self) -> bool:
+ """This contact exists even when he has no resources"""
return True
- def __getitem__(self, key):
+ def __getitem__(self, key) -> Optional[Resource]:
"""Return the corresponding Resource object, or None"""
res = safeJID(key).resource
resources = self.__item.resources
@@ -164,23 +168,24 @@ class Contact:
"""Unsubscribe from this JID"""
self.__item.unsubscribe()
- def get(self, key, default=None):
+ def get(self, key: str,
+ default: Optional[Resource] = None) -> Optional[Resource]:
"""Same as __getitem__, but with a configurable default"""
return self[key] or default
- def get_resources(self):
+ def get_resources(self) -> List[Resource]:
"""Return all resources, sorted by priority """
compare_resources = lambda x: x.priority
return sorted(self.resources, key=compare_resources, reverse=True)
- def get_highest_priority_resource(self):
+ def get_highest_priority_resource(self) -> Optional[Resource]:
"""Return the resource with the highest priority"""
resources = self.get_resources()
if resources:
return resources[0]
return None
- def folded(self, group_name='none'):
+ def folded(self, group_name='none') -> bool:
"""
Return the Folded state of a contact for this group
"""
@@ -192,7 +197,7 @@ class Contact:
"""
self.folded_states[group] = not self.folded_states[group]
- def __repr__(self):
+ def __repr__(self) -> str:
ret = '<Contact: %s' % self.bare_jid
for resource in self.resources:
ret += '\n\t\t%s' % resource
diff --git a/poezio/events.py b/poezio/events.py
index adf29c13..3bfe5156 100644
--- a/poezio/events.py
+++ b/poezio/events.py
@@ -9,6 +9,8 @@ The list of available events is here:
http://poezio.eu/doc/en/plugins.html#_poezio_events
"""
+from typing import Callable, Dict, List
+
class EventHandler:
"""
@@ -44,9 +46,10 @@ class EventHandler:
'send_normal_presence': [],
'ignored_private': [],
'tab_change': [],
- }
+ } # type: Dict[str, List[Callable]]
- def add_event_handler(self, name, callback, position=0):
+ def add_event_handler(self, name: str, callback: Callable,
+ position=0) -> bool:
"""
Add a callback to a given event.
Note that if that event name doesn’t exist, it just returns False.
@@ -64,7 +67,7 @@ class EventHandler:
return True
- def trigger(self, name, *args, **kwargs):
+ def trigger(self, name: str, *args, **kwargs):
"""
Call all the callbacks associated to the given event name.
"""
@@ -74,7 +77,7 @@ class EventHandler:
for callback in callbacks:
callback(*args, **kwargs)
- def del_event_handler(self, name, callback):
+ def del_event_handler(self, name: str, callback: Callable):
"""
Remove the callback from the list of callbacks of the given event
"""
diff --git a/poezio/pep.py b/poezio/pep.py
index a211b09b..52cc4cd5 100644
--- a/poezio/pep.py
+++ b/poezio/pep.py
@@ -3,6 +3,8 @@ Collection of mappings for PEP moods/activities
extracted directly from the XEP
"""
+from typing import Dict
+
MOODS = {
'afraid': 'Afraid',
'amazed': 'Amazed',
@@ -84,7 +86,7 @@ MOODS = {
'undefined': 'Undefined',
'weak': 'Weak',
'worried': 'Worried'
-}
+} # type: Dict[str, str]
ACTIVITIES = {
'doing_chores': {
@@ -202,4 +204,4 @@ ACTIVITIES = {
'studying': 'Studying',
'other': 'Other',
}
-}
+} # type: Dict[str, Dict[str, str]]
diff --git a/poezio/timed_events.py b/poezio/timed_events.py
index 5a5cadc2..3eeca2b3 100644
--- a/poezio/timed_events.py
+++ b/poezio/timed_events.py
@@ -12,7 +12,8 @@ Once created, they must be added to the list of checked events with
:py:func:`.PluginAPI.add_timed_event` (within a plugin).
"""
-import datetime
+from datetime import datetime
+from typing import Callable
class DelayedEvent:
@@ -21,7 +22,7 @@ class DelayedEvent:
Use it if you want an event to happen in, e.g. 6 seconds.
"""
- def __init__(self, delay, callback, *args):
+ def __init__(self, delay: int, callback: Callable, *args) -> None:
"""
Create a new DelayedEvent.
@@ -43,7 +44,7 @@ class TimedEvent(DelayedEvent):
The callback and its arguments should be passed as the lasts arguments.
"""
- def __init__(self, date, callback, *args):
+ def __init__(self, date: datetime, callback: Callable, *args) -> None:
"""
Create a new timed event.
@@ -51,6 +52,6 @@ class TimedEvent(DelayedEvent):
:param function callback: The handler that will be executed.
:param \*args: Optional arguments passed to the handler.
"""
- delta = date - datetime.datetime.now()
+ delta = date - datetime.now()
delay = delta.total_seconds()
DelayedEvent.__init__(self, delay, callback, *args)
diff --git a/poezio/user.py b/poezio/user.py
index a389e3f5..3792eca8 100644
--- a/poezio/user.py
+++ b/poezio/user.py
@@ -9,14 +9,16 @@ Define the user class.
An user is a MUC participant, not a roster contact (see contact.py)
"""
-from random import choice
+import logging
from datetime import timedelta, datetime
from hashlib import md5
-from poezio import xhtml, colors
+from random import choice
+from typing import Optional, Tuple
+from poezio import xhtml, colors
from poezio.theming import get_theme
+from slixmpp import JID
-import logging
log = logging.getLogger(__name__)
ROLE_DICT = {'': 0, 'none': 0, 'visitor': 1, 'participant': 2, 'moderator': 3}
@@ -30,19 +32,21 @@ class User:
'status', 'role', 'nick', 'color')
def __init__(self,
- nick,
- affiliation,
- show,
- status,
- role,
- jid,
+ nick: str,
+ affiliation: str,
+ show: str,
+ status: str,
+ role: str,
+ jid: JID,
deterministic=True,
color=''):
- self.last_talked = datetime(1, 1, 1) # The oldest possible time
+ # The oldest possible time
+ self.last_talked = datetime(1, 1, 1) # type: datetime
self.update(affiliation, show, status, role)
self.change_nick(nick)
- self.jid = jid
- self.chatstate = None
+ self.jid = jid # type: JID
+ self.chatstate = None # type: Optional[str]
+ self.color = (1, 1) # type: Tuple[int, int]
if color != '':
self.change_color(color, deterministic)
else:
@@ -63,7 +67,7 @@ class User:
16) % mod
self.color = theme.LIST_COLOR_NICKNAMES[nick_pos]
- def update(self, affiliation, show, status, role):
+ def update(self, affiliation: str, show: str, status: str, role: str):
self.affiliation = affiliation
self.show = show
self.status = status
@@ -71,12 +75,12 @@ class User:
role = ''
self.role = role
- def change_nick(self, nick):
+ def change_nick(self, nick: str):
self.nick = nick
- def change_color(self, color_name, deterministic=False):
+ def change_color(self, color_name: Optional[str], deterministic=False):
color = xhtml.colors.get(color_name)
- if color == None:
+ if color is None:
log.error('Unknown color "%s"', color_name)
if deterministic:
self.set_deterministic_color()
@@ -85,13 +89,13 @@ class User:
else:
self.color = (color, -1)
- def set_last_talked(self, time):
+ def set_last_talked(self, time: datetime):
"""
time: datetime object
"""
self.last_talked = time
- def has_talked_since(self, t):
+ def has_talked_since(self, t: int) -> bool:
"""
t: int
Return True if the user talked since the last s seconds
@@ -103,28 +107,28 @@ class User:
return False
return True
- def __repr__(self):
+ def __repr__(self) -> str:
return ">%s<" % (self.nick)
- def __eq__(self, b):
+ def __eq__(self, b) -> bool:
return self.role == b.role and self.nick == b.nick
- def __gt__(self, b):
+ def __gt__(self, b) -> bool:
if ROLE_DICT[self.role] == ROLE_DICT[b.role]:
return self.nick.lower() > b.nick.lower()
return ROLE_DICT[self.role] < ROLE_DICT[b.role]
- def __ge__(self, b):
+ def __ge__(self, b) -> bool:
if ROLE_DICT[self.role] == ROLE_DICT[b.role]:
return self.nick.lower() >= b.nick.lower()
return ROLE_DICT[self.role] <= ROLE_DICT[b.role]
- def __lt__(self, b):
+ def __lt__(self, b) -> bool:
if ROLE_DICT[self.role] == ROLE_DICT[b.role]:
return self.nick.lower() < b.nick.lower()
return ROLE_DICT[self.role] > ROLE_DICT[b.role]
- def __le__(self, b):
+ def __le__(self, b) -> bool:
if ROLE_DICT[self.role] == ROLE_DICT[b.role]:
return self.nick.lower() <= b.nick.lower()
return ROLE_DICT[self.role] >= ROLE_DICT[b.role]
diff --git a/poezio/xdg.py b/poezio/xdg.py
index e4b336a5..0b63998c 100644
--- a/poezio/xdg.py
+++ b/poezio/xdg.py
@@ -12,16 +12,17 @@ https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
from pathlib import Path
from os import environ
+from typing import Dict
# $HOME has already been checked to not be None in test_env().
DEFAULT_PATHS = {
'XDG_CONFIG_HOME': Path.home() / '.config',
'XDG_DATA_HOME': Path.home() / '.local' / 'share',
'XDG_CACHE_HOME': Path.home() / '.cache',
-}
+} # type: Dict[str, Path]
-def _get_directory(variable: str):
+def _get_directory(variable: str) -> Path:
"""
returns the default configuration directory path
"""
diff --git a/poezio/xhtml.py b/poezio/xhtml.py
index 7de048ae..9632855a 100644
--- a/poezio/xhtml.py
+++ b/poezio/xhtml.py
@@ -11,7 +11,6 @@ xhtml code to shell colors,
poezio colors to xhtml code
"""
-import curses
import hashlib
import re
from base64 import b64encode, b64decode
@@ -22,6 +21,7 @@ from pathlib import Path
from io import BytesIO
from xml import sax
from xml.sax import saxutils
+from typing import Dict, Optional, Tuple
from slixmpp.xmlstream import ET
from poezio.config import config
@@ -180,7 +180,7 @@ colors = {
'whitesmoke': 255,
'yellow': 226,
'yellowgreen': 149
-}
+} # type: Dict[str, int]
whitespace_re = re.compile(r'\s+')
@@ -194,7 +194,8 @@ xhtml_simple_attr_re = re.compile(r'\x19\d')
def get_body_from_message_stanza(message,
use_xhtml=False,
- extract_images_to=None):
+ extract_images_to: Optional[Path] = None
+ ) -> str:
"""
Returns a string with xhtml markups converted to
poezio colors if there's an xhtml_im element, or
@@ -213,12 +214,13 @@ def get_body_from_message_stanza(message,
return content or " "
-def rgb_to_html(rgb):
+def rgb_to_html(rgb: Tuple[float, float, float]) -> str:
+ """Get the RGB HTML value"""
r, g, b = rgb
return '#%02X%02X%02X' % (round(r * 255), round(g * 255), round(b * 255))
-def ncurses_color_to_html(color):
+def ncurses_color_to_html(color: int) -> str:
"""
Takes an int between 0 and 256 and returns
a string of the form #XXXXXX representing an
@@ -227,7 +229,7 @@ def ncurses_color_to_html(color):
return rgb_to_html(ncurses_color_to_rgb(color))
-def _parse_css_color(name):
+def _parse_css_color(name: str) -> int:
if name[0] == '#':
name = name[1:]
length = len(name)
@@ -254,7 +256,7 @@ def _parse_css_color(name):
return -1
-def _parse_css(css):
+def _parse_css(css: str) -> str:
shell = ''
rules = css.split(';')
for rule in rules:
@@ -285,7 +287,7 @@ def _parse_css(css):
return shell
-def _trim(string):
+def _trim(string: str) -> str:
return re.sub(whitespace_re, ' ', string)
@@ -297,11 +299,11 @@ def get_hash(data: bytes) -> str:
class XHTMLHandler(sax.ContentHandler):
- def __init__(self, force_ns=False, tmp_image_dir=None):
- self.builder = []
- self.formatting = []
- self.attrs = []
- self.list_state = []
+ def __init__(self, force_ns=False, tmp_image_dir: Optional[Path] = None):
+ self.builder = [] # type: List[str]
+ self.formatting = [] # type: List[str]
+ self.attrs = [] # type: List[Dict[str, str]]
+ self.list_state = [] # type: List[Union[str, int]]
self.is_pre = False
self.a_start = 0
# do not care about xhtml-in namespace
@@ -311,12 +313,12 @@ class XHTMLHandler(sax.ContentHandler):
self.enable_css_parsing = config.get('enable_css_parsing')
@property
- def result(self):
+ def result(self) -> str:
sanitized = re.sub(poezio_color_double, r'\1',
''.join(self.builder).strip())
return re.sub(poezio_format_trim, '\x19o', sanitized)
- def append_formatting(self, formatting):
+ def append_formatting(self, formatting: str):
self.formatting.append(formatting)
self.builder.append(formatting)
@@ -324,7 +326,7 @@ class XHTMLHandler(sax.ContentHandler):
self.formatting.pop()
self.builder.append('\x19o' + ''.join(self.formatting))
- def characters(self, characters):
+ def characters(self, characters: str):
self.builder.append(characters if self.is_pre else _trim(characters))
def startElementNS(self, name, _, attrs):
@@ -435,7 +437,8 @@ class XHTMLHandler(sax.ContentHandler):
builder.append(' [' + attrs['title'] + ']')
-def xhtml_to_poezio_colors(xml, force=False, tmp_dir=None):
+def xhtml_to_poezio_colors(xml, force=False,
+ tmp_dir: Optional[Path] = None) -> str:
if isinstance(xml, str):
xml = xml.encode('utf8')
elif not isinstance(xml, bytes):
@@ -449,7 +452,7 @@ def xhtml_to_poezio_colors(xml, force=False, tmp_dir=None):
return handler.result
-def clean_text(s):
+def clean_text(s: str) -> str:
"""
Remove all xhtml-im attributes (\x19etc) from the string with the
complete color format, i.e \x19xxx}
@@ -458,7 +461,7 @@ def clean_text(s):
return s
-def clean_text_simple(string):
+def clean_text_simple(string: str) -> str:
"""
Remove all \x19 from the string formatted with simple colors:
\x198
@@ -470,13 +473,13 @@ def clean_text_simple(string):
return string
-def convert_simple_to_full_colors(text):
+def convert_simple_to_full_colors(text: str) -> str:
"""
takes a \x19n formatted string and returns
a \x19n} formatted one.
"""
# TODO, have a single list of this. This is some sort of
- # dusplicate from windows.format_chars
+ # duplicate from windows.format_chars
mapping = str.maketrans({
'\x0E': '\x19b',
'\x0F': '\x19o',
@@ -508,14 +511,14 @@ number_to_color_names = {
5: 'violet',
6: 'turquoise',
7: 'white'
-}
+} # type: Dict[int, str]
-def format_inline_css(_dict):
+def format_inline_css(_dict: Dict[str, str]) -> str:
return ''.join(('%s: %s;' % (key, value) for key, value in _dict.items()))
-def poezio_colors_to_html(string):
+def poezio_colors_to_html(string: str) -> str:
"""
Convert poezio colors to html
(e.g. \x191}: <span style='color: red'>)