From d55cc5872503567775f0d7a7731d6f489bf2299b Mon Sep 17 00:00:00 2001 From: mathieui Date: Sun, 12 Nov 2017 15:03:09 +0100 Subject: yapf -ir --- poezio/__main__.py | 1 + poezio/args.py | 40 +- poezio/asyncio.py | 5 +- poezio/bookmarks.py | 53 +- poezio/common.py | 38 +- poezio/config.py | 87 +-- poezio/connection.py | 55 +- poezio/contact.py | 17 +- poezio/core/__init__.py | 1 - poezio/core/commands.py | 202 +++---- poezio/core/completions.py | 184 +++--- poezio/core/core.py | 816 ++++++++++++++------------ poezio/core/handlers.py | 659 +++++++++++++-------- poezio/core/structs.py | 14 +- poezio/daemon.py | 10 +- poezio/decorators.py | 23 +- poezio/events.py | 5 +- poezio/fifo.py | 7 +- poezio/fixes.py | 9 +- poezio/keyboard.py | 14 +- poezio/logger.py | 123 ++-- poezio/multiuserchat.py | 66 ++- poezio/pep.py | 408 +++++++------ poezio/plugin.py | 52 +- poezio/plugin_manager.py | 84 ++- poezio/poezio.py | 8 +- poezio/poezio_shlex.py | 24 +- poezio/poopt.py | 7 +- poezio/roster.py | 39 +- poezio/roster_sorting.py | 48 +- poezio/size_manager.py | 3 +- poezio/tabs/__init__.py | 12 +- poezio/tabs/adhoc_commands_list.py | 29 +- poezio/tabs/basetabs.py | 307 ++++++---- poezio/tabs/bookmarkstab.py | 45 +- poezio/tabs/confirmtab.py | 27 +- poezio/tabs/conversationtab.py | 197 ++++--- poezio/tabs/data_forms.py | 6 +- poezio/tabs/listtab.py | 50 +- poezio/tabs/muclisttab.py | 25 +- poezio/tabs/muctab.py | 1064 ++++++++++++++++++++-------------- poezio/tabs/privatetab.py | 199 ++++--- poezio/tabs/rostertab.py | 524 ++++++++++------- poezio/tabs/xmltab.py | 146 +++-- poezio/text_buffer.py | 115 +++- poezio/theming.py | 273 ++++++--- poezio/timed_events.py | 5 +- poezio/user.py | 27 +- poezio/windows/__init__.py | 18 +- poezio/windows/base_wins.py | 17 +- poezio/windows/bookmark_forms.py | 125 ++-- poezio/windows/confirm.py | 3 +- poezio/windows/data_forms.py | 159 +++-- poezio/windows/funcs.py | 14 +- poezio/windows/image.py | 16 +- poezio/windows/info_bar.py | 35 +- poezio/windows/info_wins.py | 78 ++- poezio/windows/input_placeholders.py | 5 +- poezio/windows/inputs.py | 111 ++-- poezio/windows/list.py | 42 +- poezio/windows/misc.py | 3 +- poezio/windows/muc.py | 35 +- poezio/windows/roster_win.py | 152 +++-- poezio/windows/text_win.py | 122 ++-- poezio/xhtml.py | 103 +++- 65 files changed, 4361 insertions(+), 2830 deletions(-) (limited to 'poezio') diff --git a/poezio/__main__.py b/poezio/__main__.py index b8696bb5..b0ba98e4 100644 --- a/poezio/__main__.py +++ b/poezio/__main__.py @@ -8,5 +8,6 @@ def run(): sys.exit(1) return 0 + if __name__ == '__main__': run() diff --git a/poezio/args.py b/poezio/args.py index 63e77927..71ae7488 100644 --- a/poezio/args.py +++ b/poezio/args.py @@ -6,23 +6,37 @@ There is a fallback to the deprecated optparse if argparse is not found from os import path from argparse import ArgumentParser, SUPPRESS + def parse_args(CONFIG_PATH=''): """ Parse the arguments from the command line """ parser = ArgumentParser('poezio') - parser.add_argument("-c", "--check-config", dest="check_config", - action='store_true', - help='Check the config file') - parser.add_argument("-d", "--debug", dest="debug", - help="The file where debug will be written", - metavar="DEBUG_FILE") - parser.add_argument("-f", "--file", dest="filename", - default=path.join(CONFIG_PATH, 'poezio.cfg'), - help="The config file you want to use", - metavar="CONFIG_FILE") - parser.add_argument("-v", "--version", dest="version", - help=SUPPRESS, metavar="VERSION", - default="1.0-dev") + parser.add_argument( + "-c", + "--check-config", + dest="check_config", + action='store_true', + help='Check the config file') + parser.add_argument( + "-d", + "--debug", + dest="debug", + help="The file where debug will be written", + metavar="DEBUG_FILE") + parser.add_argument( + "-f", + "--file", + dest="filename", + default=path.join(CONFIG_PATH, 'poezio.cfg'), + help="The config file you want to use", + metavar="CONFIG_FILE") + parser.add_argument( + "-v", + "--version", + dest="version", + help=SUPPRESS, + metavar="VERSION", + default="1.0-dev") options = parser.parse_args() return options diff --git a/poezio/asyncio.py b/poezio/asyncio.py index 2b02a91f..d333ffa6 100644 --- a/poezio/asyncio.py +++ b/poezio/asyncio.py @@ -28,6 +28,7 @@ def monkey_patch_asyncio_slixmpp(): if self._idle: handle = self._idle.popleft() handle._run() + cls = asyncio.get_event_loop().__class__ cls._idle = collections.deque() cls.idle_call = idle_call @@ -35,8 +36,8 @@ def monkey_patch_asyncio_slixmpp(): cls._run_once = my_run_once spawn_event = slixmpp.xmlstream.XMLStream._spawn_event + def patchy(self, xml): self.loop.idle_call(functools.partial(spawn_event, self, xml)) - slixmpp.xmlstream.XMLStream._spawn_event = patchy - + slixmpp.xmlstream.XMLStream._spawn_event = patchy diff --git a/poezio/bookmarks.py b/poezio/bookmarks.py index 81d006b7..e007ddd1 100644 --- a/poezio/bookmarks.py +++ b/poezio/bookmarks.py @@ -40,8 +40,13 @@ log = logging.getLogger(__name__) class Bookmark(object): - - def __init__(self, jid, name=None, autojoin=False, nick=None, password=None, method='local'): + def __init__(self, + jid, + name=None, + autojoin=False, + nick=None, + password=None, + method='local'): self.jid = jid self.name = name or jid self.autojoin = autojoin @@ -61,9 +66,8 @@ class Bookmark(object): self._method = value def __repr__(self): - return '<%s%s|%s>' % (self.jid, - ('/'+self.nick) if self.nick else '', - self.method) + return '<%s%s|%s>' % (self.jid, ('/' + self.nick) + if self.nick else '', self.method) def stanza(self): """ @@ -98,7 +102,8 @@ class Bookmark(object): """ jid = el.get('jid') name = el.get('name') - autojoin = True if el.get('autojoin', 'false').lower() in ('true', '1') else False + autojoin = True if el.get('autojoin', + 'false').lower() in ('true', '1') else False nick = None for n in el.iter('nick'): nick = n.text @@ -121,8 +126,8 @@ class Bookmark(object): name = el['name'] return Bookmark(jid, name, autojoin, nick, password, method='remote') -class BookmarkList(object): +class BookmarkList(object): def __init__(self): self.bookmarks = [] preferred = config.get('use_bookmarks_method').lower() @@ -191,17 +196,21 @@ class BookmarkList(object): method = 'xep_0049' if self.preferred == 'privatexml' else 'xep_0223' if method: - xmpp.plugin['xep_0048'].set_bookmarks(stanza_storage(self.bookmarks), - method=method, - callback=callback) + xmpp.plugin['xep_0048'].set_bookmarks( + stanza_storage(self.bookmarks), + method=method, + callback=callback) + def save_local(self): """Save the local bookmarks.""" - local = ''.join(bookmark.local() for bookmark in self if bookmark.method == 'local') + local = ''.join(bookmark.local() for bookmark in self + if bookmark.method == 'local') config.set_and_save('rooms', local) def save(self, xmpp, core=None, callback=None): """Save all the bookmarks.""" self.save_local() + def _cb(iq): if callback: callback(iq) @@ -209,14 +218,17 @@ class BookmarkList(object): core.information('Could not save remote bookmarks.', 'Error') elif core: core.information('Bookmarks saved', 'Info') + if config.get('use_remote_bookmarks'): self.save_remote(xmpp, _cb) def get_pep(self, xmpp, callback): """Add the remotely stored bookmarks via pep to the list.""" + def _cb(iq): if iq['type'] == 'result': - for conf in iq['pubsub']['items']['item']['bookmarks']['conferences']: + for conf in iq['pubsub']['items']['item']['bookmarks'][ + 'conferences']: if isinstance(conf, URL): continue b = Bookmark.parse(conf) @@ -230,6 +242,7 @@ class BookmarkList(object): """ Fetch the remote bookmarks stored via privatexml. """ + def _cb(iq): if iq['type'] == 'result': for conf in iq['private']['bookmarks']['conferences']: @@ -250,12 +263,15 @@ class BookmarkList(object): if force and not any(self.available_storage.values()): old_callback = callback method = 'pep' if self.preferred == 'pep' else 'privatexml' + def new_callback(result): if result['type'] != 'error': self.available_storage[method] = True old_callback(result) else: - information('No remote bookmark storage available', 'Warning') + information('No remote bookmark storage available', + 'Warning') + callback = new_callback if self.preferred == 'pep': @@ -277,10 +293,17 @@ class BookmarkList(object): nick = jid.resource else: nick = None - passwd = config.get_by_tabname('password', jid.bare, fallback=False) or None - b = Bookmark(jid.bare, autojoin=True, nick=nick, password=passwd, method='local') + passwd = config.get_by_tabname( + 'password', jid.bare, fallback=False) or None + b = Bookmark( + jid.bare, + autojoin=True, + nick=nick, + password=passwd, + method='local') self.append(b) + def stanza_storage(bookmarks): """Generate a stanza with the conference elements.""" storage = Bookmarks() diff --git a/poezio/common.py b/poezio/common.py index 7168fe18..0c6a77a8 100644 --- a/poezio/common.py +++ b/poezio/common.py @@ -4,7 +4,6 @@ # # Poezio is free software: you can redistribute it and/or modify # it under the terms of the zlib license. See the COPYING file. - """ Various useful functions. """ @@ -45,6 +44,7 @@ def get_base64_from_file(path): mime_type = mimetypes.guess_type(path)[0] return (encoded, mime_type, sha1) + def _get_output_of_command(command): """ Runs a command and returns its output. @@ -54,10 +54,12 @@ def _get_output_of_command(command): :rtype: :py:class:`str` """ try: - return subprocess.check_output(command.split()).decode('utf-8').split('\n') + return subprocess.check_output( + command.split()).decode('utf-8').split('\n') except subprocess.CalledProcessError: return None + def _is_in_path(command, return_abs_path=False): """ Check if *command* is in the $PATH or not. @@ -81,6 +83,7 @@ def _is_in_path(command, return_abs_path=False): pass return False + DISTRO_INFO = { 'Arch Linux': '/etc/arch-release', 'Aurox Linux': '/etc/aurox-release', @@ -103,6 +106,7 @@ DISTRO_INFO = { 'Redhat Linux': '/etc/redhat-release' } + def get_os_info(): """ Returns a detailed and well formated string containing @@ -116,10 +120,12 @@ def get_os_info(): full_path_to_executable = _is_in_path(executable, return_abs_path=True) if full_path_to_executable: command = executable + params - process = subprocess.Popen([command], shell=True, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - close_fds=True) + process = subprocess.Popen( + [command], + shell=True, + stdin=subprocess.PIPE, + 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 @@ -136,7 +142,7 @@ def get_os_info(): text = _get_output_of_command(path_to_file)[0] else: fdes = open(path_to_file, encoding='utf-8') - text = fdes.readline().strip() # get only first line + text = fdes.readline().strip() # get only first line fdes.close() if path_to_file.endswith('version'): # sourcemage_version and slackware-version files @@ -158,11 +164,12 @@ def get_os_info(): # our last chance, ask uname and strip it uname_output = _get_output_of_command('uname -sr') if uname_output is not None: - os_info = uname_output[0] # only first line + os_info = uname_output[0] # only first line return os_info os_info = 'N/A' return os_info + def _datetime_tuple(timestamp): """ Convert a timestamp using strptime and the format: %Y%m%dT%H:%M:%S. @@ -194,7 +201,7 @@ def _datetime_tuple(timestamp): tz_msg = timedelta(seconds=tz_mod * tz_msg) ret -= tz_msg except ValueError: - pass # ignore if we got a badly-formatted offset + pass # ignore if we got a badly-formatted offset # convert UTC to local time, with DST etc. if time.daylight and time.localtime().tm_isdst: tz = timedelta(seconds=-time.altzone) @@ -203,6 +210,7 @@ def _datetime_tuple(timestamp): ret += tz return ret + def get_utc_time(local_time=None): """ Get the current UTC time @@ -225,6 +233,7 @@ def get_utc_time(local_time=None): return utc_time + def get_local_time(utc_time): """ Get the local time from an UTC time @@ -240,6 +249,7 @@ def get_local_time(utc_time): return local_time + def find_delayed_tag(message): """ Check if a message is delayed or not. @@ -266,6 +276,7 @@ def find_delayed_tag(message): date = None return (delayed, date) + def shell_split(st): """ Split a string correctly according to the quotes @@ -288,6 +299,7 @@ def shell_split(st): w = sh.get_token() return ret + def find_argument(pos, text, quoted=True): """ Split an input into a list of arguments, return the number of the @@ -308,6 +320,7 @@ def find_argument(pos, text, quoted=True): else: return _find_argument_unquoted(pos, text) + def _find_argument_quoted(pos, text): """ Get the number of the argument at position pos in @@ -324,6 +337,7 @@ def _find_argument_quoted(pos, text): return count + 1 + def _find_argument_unquoted(pos, text): """ Get the number of the argument at position pos in @@ -341,6 +355,7 @@ def _find_argument_unquoted(pos, text): argnum = i return argnum + 1 + def parse_str_to_secs(duration=''): """ Parse a string of with a number of d, h, m, s. @@ -368,6 +383,7 @@ def parse_str_to_secs(duration=''): result += int(tmp) return result + def parse_secs_to_str(duration=0): """ Do the reverse operation of :py:func:`parse_str_to_secs`. @@ -397,6 +413,7 @@ def parse_secs_to_str(duration=0): result = '0s' return result + def format_tune_string(infos): """ Contruct a string from a dict created from an "User tune" event. @@ -434,6 +451,7 @@ def format_tune_string(infos): elems.append('[' + mins + ':' + secs + ']') return ' '.join(elems) + def format_gaming_string(infos): """ Construct a string from a dict containing "user gaming" information. @@ -452,6 +470,7 @@ def format_gaming_string(infos): return '%s on %s' % (name, server_address) return name + def safeJID(*args, **kwargs): """ Construct a :py:class:`slixmpp.JID` object from a string. @@ -463,4 +482,3 @@ def safeJID(*args, **kwargs): return JID(*args, **kwargs) except InvalidJID: return JID('') - diff --git a/poezio/config.py b/poezio/config.py index bef1c1a6..24e771fd 100644 --- a/poezio/config.py +++ b/poezio/config.py @@ -144,14 +144,15 @@ DEFAULT_CONFIG = { 'folded_roster_groups': '', 'info_win_height': 2 }, - 'muc_colors': { - } + 'muc_colors': {} } + class Config(RawConfigParser): """ load/save the config to a file """ + def __init__(self, file_name, default=None): RawConfigParser.__init__(self, None) # make the options case sensitive @@ -198,8 +199,12 @@ class Config(RawConfigParser): return default return res - def get_by_tabname(self, option, tabname, - fallback=True, fallback_server=True, default=''): + def get_by_tabname(self, + option, + tabname, + fallback=True, + fallback_server=True, + default=''): """ Try to get the value for the option. First we look in a section named `tabname`, if the option is not present @@ -232,7 +237,6 @@ class Config(RawConfigParser): return self.get(option, default) return default - def __get(self, option, section=DEFSECTION, **kwargs): """ facility for RawConfigParser.get @@ -331,11 +335,7 @@ class Config(RawConfigParser): prefix, file = path.split(self.file_name) filename = path.join(prefix, '.%s.tmp' % file) fd = os.fdopen( - os.open( - filename, - os.O_WRONLY | os.O_CREAT, - 0o600), - 'w') + os.open(filename, os.O_WRONLY | os.O_CREAT, 0o600), 'w') for line in lines: fd.write('%s\n' % line) fd.close() @@ -362,9 +362,10 @@ class Config(RawConfigParser): with open(self.file_name, 'r', encoding='utf-8') as df: lines_before = [line.strip() for line in df] except OSError: - log.error('Unable to read the config file %s', - self.file_name, - exc_info=True) + log.error( + 'Unable to read the config file %s', + self.file_name, + exc_info=True) return tuple() else: lines_before = [] @@ -415,8 +416,7 @@ class Config(RawConfigParser): else: return ('Could not toggle option: %s.' ' Current value is %s.' % - (option, current or "empty"), - 'Warning') + (option, current or "empty"), 'Warning') if self.has_section(section): RawConfigParser.set(self, section, option, value) else: @@ -477,12 +477,13 @@ def find_line(lines, start, end, option): """ current = start for line in lines[start:end]: - if (line.startswith('%s ' % option) or - line.startswith('%s=' % option)): + if (line.startswith('%s ' % option) + or line.startswith('%s=' % option)): return current current += 1 return -1 + def file_ok(filepath): """ Returns True if the file exists and is readable and writeable, @@ -492,6 +493,7 @@ def file_ok(filepath): val &= os.access(filepath, os.R_OK | os.W_OK) return bool(val) + def check_create_config_dir(): """ create the configuration directory if it doesn't exist @@ -507,6 +509,7 @@ def check_create_config_dir(): pass return CONFIG_PATH + def check_create_cache_dir(): """ create the cache directory if it doesn't exist @@ -525,6 +528,7 @@ def check_create_cache_dir(): except OSError: pass + def check_config(): """ Check the config file and print results @@ -533,7 +537,8 @@ def check_config(): for option in DEFAULT_CONFIG['Poezio']: value = config.get(option) if value != DEFAULT_CONFIG['Poezio'][option]: - result['changed'].append((option, value, DEFAULT_CONFIG['Poezio'][option])) + result['changed'].append((option, value, + DEFAULT_CONFIG['Poezio'][option])) else: value = config.get(option, default='') upper = value.upper() @@ -544,15 +549,19 @@ def check_config(): result['changed'].sort(key=lambda x: x[0]) result['missing'].sort() if result['changed']: - print('\033[1mOptions changed from the default configuration:\033[0m\n') + print( + '\033[1mOptions changed from the default configuration:\033[0m\n') for option, new_value, default in result['changed']: - print(' \033[1m%s\033[0m = \033[33m%s\033[0m (default: \033[32m%s\033[0m)' % (option, new_value, default)) + print( + ' \033[1m%s\033[0m = \033[33m%s\033[0m (default: \033[32m%s\033[0m)' + % (option, new_value, default)) if result['missing']: print('\n\033[1mMissing options:\033[0m (the defaults are used)\n') for option in result['missing']: print(' \033[31m%s\033[0m' % option) + def run_cmdline_args(CONFIG_PATH): "Parse the command line arguments" global options @@ -560,7 +569,8 @@ def run_cmdline_args(CONFIG_PATH): # Copy a default file if none exists if not path.isfile(options.filename): - default = path.join(path.dirname(__file__), '../data/default_config.cfg') + default = path.join( + path.dirname(__file__), '../data/default_config.cfg') other = pkg_resources.resource_filename('poezio', 'default_config.cfg') if path.isfile(default): copy2(default, options.filename) @@ -577,6 +587,7 @@ def run_cmdline_args(CONFIG_PATH): global firstrun firstrun = True + def create_global_config(): "Create the global config object, or crash" try: @@ -589,6 +600,7 @@ def create_global_config(): traceback.print_exc(limit=0) sys.exit(1) + def check_create_log_dir(): "Create the poezio logging directory if it doesn’t exist" global LOG_DIR @@ -610,29 +622,29 @@ def check_create_log_dir(): except: pass + def setup_logging(): "Change the logging config according to the cmdline options and config" if config.get('log_errors'): LOGGING_CONFIG['root']['handlers'].append('error') LOGGING_CONFIG['handlers']['error'] = { - 'level': 'ERROR', - 'class': 'logging.FileHandler', - 'filename': path.join(LOG_DIR, 'errors.log'), - 'formatter': 'simple', - } + 'level': 'ERROR', + 'class': 'logging.FileHandler', + 'filename': path.join(LOG_DIR, 'errors.log'), + 'formatter': 'simple', + } logging.disable(logging.WARNING) if options.debug: LOGGING_CONFIG['root']['handlers'].append('debug') LOGGING_CONFIG['handlers']['debug'] = { - 'level':'DEBUG', - 'class':'logging.FileHandler', - 'filename': options.debug, - 'formatter': 'simple', - } + 'level': 'DEBUG', + 'class': 'logging.FileHandler', + 'filename': options.debug, + 'formatter': 'simple', + } logging.disable(logging.NOTSET) - if LOGGING_CONFIG['root']['handlers']: logging.config.dictConfig(LOGGING_CONFIG) else: @@ -642,6 +654,7 @@ def setup_logging(): global log log = logging.getLogger(__name__) + def post_logging_setup(): # common imports slixmpp, which creates then its loggers, so # it needs to be after logger configuration @@ -649,6 +662,7 @@ def post_logging_setup(): global safeJID safeJID = JID + LOGGING_CONFIG = { 'version': 1, 'disable_existing_loggers': True, @@ -657,12 +671,11 @@ LOGGING_CONFIG = { 'format': '%(asctime)s %(levelname)s:%(module)s:%(message)s' } }, - 'handlers': { - }, + 'handlers': {}, 'root': { - 'handlers': [], - 'propagate': True, - 'level': 'DEBUG', + 'handlers': [], + 'propagate': True, + 'level': 'DEBUG', } } diff --git a/poezio/connection.py b/poezio/connection.py index d00f714f..99a19c11 100644 --- a/poezio/connection.py +++ b/poezio/connection.py @@ -4,7 +4,6 @@ # # Poezio is free software: you can redistribute it and/or modify # it under the terms of the zlib license. See the COPYING file. - """ Defines the Connection class """ @@ -12,7 +11,6 @@ Defines the Connection class import logging log = logging.getLogger(__name__) - import getpass import subprocess import sys @@ -25,12 +23,14 @@ from poezio import fixes from poezio.common import safeJID from poezio.config import config, options + class Connection(slixmpp.ClientXMPP): """ Receives everything from Jabber and emits the appropriate signals """ __init = False + def __init__(self): keyfile = config.get('keyfile') certfile = config.get('certfile') @@ -43,27 +43,35 @@ class Connection(slixmpp.ClientXMPP): jid = '%s' % config.get('jid') password = config.get('password') eval_password = config.get('eval_password') - if not password and not eval_password and not (keyfile and certfile): + if not password and not eval_password and not (keyfile + and certfile): password = getpass.getpass() elif not password and not (keyfile and certfile): - sys.stderr.write("No password or certificates provided, using the eval_password command.\n") - process = subprocess.Popen(['sh', '-c', eval_password], stdin=subprocess.PIPE, - stdout=subprocess.PIPE, close_fds=True) + sys.stderr.write( + "No password or certificates provided, using the eval_password command.\n" + ) + process = subprocess.Popen( + ['sh', '-c', eval_password], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + close_fds=True) code = process.wait() if code != 0: - sys.stderr.write('The eval_password command (%s) returned a ' - 'nonzero status code: %s.\n' % (eval_password, code)) + sys.stderr.write( + 'The eval_password command (%s) returned a ' + 'nonzero status code: %s.\n' % (eval_password, code)) sys.stderr.write('Poezio will now exit\n') sys.exit(code) - password = process.stdout.readline().decode('utf-8').strip('\n') - else: # anonymous auth + password = process.stdout.readline().decode('utf-8').strip( + '\n') + else: # anonymous auth self.anon = True jid = config.get('server') password = None jid = safeJID(jid) # TODO: use the system language - slixmpp.ClientXMPP.__init__(self, jid, password, - lang=config.get('lang')) + slixmpp.ClientXMPP.__init__( + self, jid, password, lang=config.get('lang')) force_encryption = config.get('force_encryption') if force_encryption: @@ -75,18 +83,19 @@ class Connection(slixmpp.ClientXMPP): self.keyfile = config.get('keyfile') self.certfile = config.get('certfile') if keyfile and not certfile: - log.error('keyfile is present in configuration file without certfile') + log.error( + 'keyfile is present in configuration file without certfile') elif certfile and not keyfile: - log.error('certfile is present in configuration file without keyfile') + log.error( + 'certfile is present in configuration file without keyfile') self.core = None self.auto_reconnect = config.get('auto_reconnect') self.auto_authorize = None # prosody defaults, lowest is AES128-SHA, it should be a minimum # for anything that came out after 2002 - self.ciphers = config.get('ciphers', - 'HIGH+kEDH:HIGH+kEECDH:HIGH:!PSK' - ':!SRP:!3DES:!aNULL') + self.ciphers = config.get('ciphers', 'HIGH+kEDH:HIGH+kEECDH:HIGH:!PSK' + ':!SRP:!3DES:!aNULL') self.ca_certs = config.get('ca_cert_path') or None interval = config.get('whitespace_interval') if int(interval) > 0: @@ -116,7 +125,8 @@ class Connection(slixmpp.ClientXMPP): XEP_0184._filter_add_receipt_request = fixes._filter_add_receipt_request self.register_plugin('xep_0184') self.plugin['xep_0184'].auto_ack = config.get('ack_message_receipts') - self.plugin['xep_0184'].auto_request = config.get('request_message_receipts') + self.plugin['xep_0184'].auto_request = config.get( + 'request_message_receipts') self.register_plugin('xep_0191') if config.get('enable_smacks'): @@ -139,16 +149,15 @@ class Connection(slixmpp.ClientXMPP): self.register_plugin('xep_0196') if config.get('send_poezio_info'): - info = {'name':'poezio', - 'version': options.version} + info = {'name': 'poezio', 'version': options.version} if config.get('send_os_info'): info['os'] = common.get_os_info() self.plugin['xep_0030'].set_identities( - identities={('client', 'console', None, 'Poezio')}) + identities={('client', 'console', None, 'Poezio')}) else: info = {'name': '', 'version': ''} self.plugin['xep_0030'].set_identities( - identities={('client', 'console', None, '')}) + identities={('client', 'console', None, '')}) self.register_plugin('xep_0092', pconfig=info) if config.get('send_time'): self.register_plugin('xep_0202') @@ -212,10 +221,12 @@ class Connection(slixmpp.ClientXMPP): self.core.handler.outgoing_stanza(data) slixmpp.ClientXMPP.send_raw(self, data) + class MatchAll(slixmpp.xmlstream.matcher.base.MatcherBase): """ Callback to retrieve all the stanzas for the XML tab """ + def match(self, xml): "match everything" return True diff --git a/poezio/contact.py b/poezio/contact.py index 090fed92..4b233088 100644 --- a/poezio/contact.py +++ b/poezio/contact.py @@ -4,7 +4,6 @@ # # Poezio is free software: you can redistribute it and/or modify # it under the terms of the zlib license. See the COPYING file. - """ Defines the Resource and Contact classes, which are used in the roster. @@ -16,16 +15,18 @@ log = logging.getLogger(__name__) from poezio.common import safeJID from collections import defaultdict + class Resource(object): """ Defines a roster item. It's a precise resource. """ + def __init__(self, jid, data): """ data: the dict to use as a source """ - self._jid = jid # Full jid + self._jid = jid # Full jid self._data = data @property @@ -52,12 +53,14 @@ class Resource(object): return False return self.jid == value.jid and self._data == value._data + class Contact(object): """ This a way to gather multiple resources from the same bare JID. This class contains zero or more Resource object and useful methods to get the resource with the highest priority, etc """ + def __init__(self, item): """ item: a slixmpp RosterItem pointing to that contact @@ -118,17 +121,17 @@ class Contact(object): @property def resources(self): """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()) + 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): return self.__item['subscription'] def __contains__(self, value): - return value in self.__item.resources or safeJID(value).resource in self.__item.resources + return value in self.__item.resources or safeJID( + value).resource in self.__item.resources def __len__(self): """Number of resources""" diff --git a/poezio/core/__init__.py b/poezio/core/__init__.py index 0c6d63d9..cfe4c179 100644 --- a/poezio/core/__init__.py +++ b/poezio/core/__init__.py @@ -5,4 +5,3 @@ __all__ = ['Core', 'Command', 'Status'] from poezio.core.core import Core from poezio.core.structs import Command, Status - diff --git a/poezio/core/commands.py b/poezio/core/commands.py index 5a28182b..ab0ced9a 100644 --- a/poezio/core/commands.py +++ b/poezio/core/commands.py @@ -46,10 +46,8 @@ class CommandCore: buff = ['Global commands:'] for name, command in self.core.commands.items(): if isinstance(command, Command): - acc.append(' \x19%s}%s\x19o - %s' % ( - color, - name, - command.short_desc)) + acc.append(' \x19%s}%s\x19o - %s' % (color, name, + command.short_desc)) else: acc.append(' \x19%s}%s\x19o' % (color, name)) acc = sorted(acc) @@ -59,10 +57,8 @@ class CommandCore: tab_commands = self.core.current_tab().commands for name, command in tab_commands.items(): if isinstance(command, Command): - acc.append(' \x19%s}%s\x19o - %s' % ( - color, - name, - command.short_desc)) + acc.append(' \x19%s}%s\x19o - %s' % (color, name, + command.short_desc)) else: acc.append(' \x19%s}%s\x19o' % (color, name)) acc = sorted(acc) @@ -93,11 +89,13 @@ class CommandCore: """ /runkey """ + def replace_line_breaks(key): "replace ^J with \n" if key == '^J': return '\n' return key + if args is None: return self.help('runkey') char = args[0] @@ -135,7 +133,8 @@ class CommandCore: current.send_chat_state('inactive') for tab in self.core.tabs: if isinstance(tab, tabs.MucTab) and tab.joined: - muc.change_show(self.core.xmpp, tab.name, tab.own_nick, show, msg) + muc.change_show(self.core.xmpp, tab.name, tab.own_nick, show, + msg) if hasattr(tab, 'directed_presence'): del tab.directed_presence self.core.set_status(show, msg) @@ -156,12 +155,14 @@ class CommandCore: if ptype == 'available': ptype = None try: - pres = self.core.xmpp.make_presence(pto=jid, ptype=ptype, pstatus=status) + pres = self.core.xmpp.make_presence( + pto=jid, ptype=ptype, pstatus=status) self.core.events.trigger('send_normal_presence', pres) pres.send() except (XMPPError, NotConnectedError): self.core.information('Could not send directed presence', 'Error') - log.debug('Could not send directed presence to %s', jid, exc_info=True) + log.debug( + 'Could not send directed presence to %s', jid, exc_info=True) return tab = self.core.get_tab_by_name(jid) if tab: @@ -184,7 +185,7 @@ class CommandCore: """/theme """ if args is None: return self.help('theme') - self.set('theme %s' % (args[0],)) + self.set('theme %s' % (args[0], )) @command_args_parser.quoted(1) def win(self, args): @@ -254,10 +255,12 @@ class CommandCore: if not old_tab and value == tab.name: old_tab = tab if not old_tab: - self.core.information("Tab %s does not exist" % args[0], "Error") + self.core.information("Tab %s does not exist" % args[0], + "Error") return None ref = old_tab.nb return ref + old = get_nb_from_value(args[0]) new = get_nb_from_value(args[1]) if new is None or old is None: @@ -281,19 +284,20 @@ class CommandCore: jid = safeJID(args[0]) else: if not isinstance(self.core.current_tab(), tabs.MucTab): - return self.core.information('Please provide a server', 'Error') + return self.core.information('Please provide a server', + 'Error') jid = safeJID(self.core.current_tab().name) list_tab = tabs.MucListTab(self.core, jid) self.core.add_tab(list_tab, True) cb = list_tab.on_muc_list_item_received - self.core.xmpp.plugin['xep_0030'].get_items(jid=jid, - callback=cb) + self.core.xmpp.plugin['xep_0030'].get_items(jid=jid, callback=cb) @command_args_parser.quoted(1) def version(self, args): """ /version """ + def callback(res): "Callback for /version" if not res: @@ -301,10 +305,9 @@ class CommandCore: ' version from %s' % jid, 'Warning') version = '%s is running %s version %s on %s' % ( - jid, - res.get('name') or 'an unknown software', - res.get('version') or 'unknown', - res.get('os') or 'an unknown platform') + jid, res.get('name') or 'an unknown software', + res.get('version') or 'unknown', + res.get('os') or 'an unknown platform') self.core.information(version, 'Info') if args is None: @@ -315,7 +318,8 @@ class CommandCore: fixes.get_version(self.core.xmpp, jid, callback=callback) elif jid in roster: for resource in roster[jid].resources: - fixes.get_version(self.core.xmpp, resource.jid, callback=callback) + fixes.get_version( + self.core.xmpp, resource.jid, callback=callback) def _empty_join(self): tab = self.core.current_tab() @@ -372,7 +376,7 @@ class CommandCore: else: room, nick = self._parse_join_jid(args[0]) if not room and not nick: - return # nothing was parsed + return # nothing was parsed room = room.lower() if nick == '': @@ -467,11 +471,13 @@ class CommandCore: bookmark.nick = nick if password: bookmark.password = password + def callback(iq): if iq["type"] != "error": self.core.information('Bookmark added.', 'Info') else: self.core.information("Could not add the bookmarks.", "Info") + self.core.bookmarks.save_local() self.core.bookmarks.save_remote(self.core.xmpp, callback) @@ -480,8 +486,7 @@ class CommandCore: for tab in self.core.get_tabs(tabs.MucTab): bookmark = self.core.bookmarks[tab.name] if not bookmark: - bookmark = Bookmark(tab.name, autojoin=True, - method=method) + bookmark = Bookmark(tab.name, autojoin=True, method=method) new_bookmarks.append(bookmark) else: bookmark.method = method @@ -489,11 +494,14 @@ class CommandCore: self.core.bookmarks.remove(bookmark) new_bookmarks.extend(self.core.bookmarks.bookmarks) self.core.bookmarks.set(new_bookmarks) + def _cb(iq): if iq["type"] != "error": self.core.information("Bookmarks saved.", "Info") else: - self.core.information("Could not save the remote bookmarks.", "Info") + self.core.information("Could not save the remote bookmarks.", + "Info") + self.core.bookmarks.save_local() self.core.bookmarks.save_remote(self.core.xmpp, _cb) @@ -520,7 +528,8 @@ class CommandCore: if success: self.core.information('Bookmark deleted', 'Info') else: - self.core.information('Error while deleting the bookmark', 'Error') + self.core.information('Error while deleting the bookmark', + 'Error') if not args: tab = self.core.current_tab() @@ -546,15 +555,17 @@ class CommandCore: lines = [] theme = get_theme() for section_name, section in config_dict.items(): - lines.append('\x19%(section_col)s}[%(section)s]\x19o' % - { - 'section': section_name, - 'section_col': dump_tuple(theme.COLOR_INFORMATION_TEXT), - }) + lines.append( + '\x19%(section_col)s}[%(section)s]\x19o' % { + 'section': section_name, + 'section_col': dump_tuple( + theme.COLOR_INFORMATION_TEXT), + }) for option_name, option_value in section.items(): - lines.append('%s\x19%s}=\x19o%s' % (option_name, - dump_tuple(theme.COLOR_REVISIONS_MESSAGE), - option_value)) + lines.append('%s\x19%s}=\x19o%s' % + (option_name, + dump_tuple(theme.COLOR_REVISIONS_MESSAGE), + option_value)) info = ('Current options:\n%s' % '\n'.join(lines), 'Info') elif len(args) == 1: option = args[0] @@ -573,7 +584,8 @@ class CommandCore: file_name = os.path.join(file_name, plugin_name + '.cfg') plugin_config = PluginConfig(file_name, plugin_name) else: - plugin_config = self.core.plugin_manager.plugins[plugin_name].config + plugin_config = self.core.plugin_manager.plugins[ + plugin_name].config value = plugin_config.get(option, default='', section=section) info = ('%s=%s' % (option, value), 'Info') else: @@ -600,14 +612,15 @@ class CommandCore: file_name = os.path.join(file_name, plugin_name + '.cfg') plugin_config = PluginConfig(file_name, plugin_name) else: - plugin_config = self.core.plugin_manager.plugins[plugin_name].config + plugin_config = self.core.plugin_manager.plugins[ + plugin_name].config info = plugin_config.set_and_save(option, value, section) else: if args[0] == '.': name = safeJID(self.core.current_tab().name).bare if not name: - self.core.information('Invalid tab to use the "." argument.', - 'Error') + self.core.information( + 'Invalid tab to use the "." argument.', 'Error') return section = name else: @@ -679,35 +692,35 @@ class CommandCore: """ /last_activity """ + def callback(iq): "Callback for the last activity" if iq['type'] != 'result': if iq['error']['type'] == 'auth': self.core.information('You are not allowed to see the ' - 'activity of this contact.', - 'Error') + 'activity of this contact.', 'Error') else: - self.core.information('Error retrieving the activity', 'Error') + self.core.information('Error retrieving the activity', + 'Error') return seconds = iq['last_activity']['seconds'] status = iq['last_activity']['status'] from_ = iq['from'] if not safeJID(from_).user: msg = 'The uptime of %s is %s.' % ( - from_, - common.parse_secs_to_str(seconds)) + from_, common.parse_secs_to_str(seconds)) else: msg = 'The last activity of %s was %s ago%s' % ( - from_, - common.parse_secs_to_str(seconds), - (' and his/her last status was %s' % status) if status else '') + from_, common.parse_secs_to_str(seconds), + (' and his/her last status was %s' % status) + if status else '') self.core.information(msg, 'Info') if args is None: return self.help('last_activity') jid = safeJID(args[0]) - self.core.xmpp.plugin['xep_0012'].get_last_activity(jid, - callback=callback) + self.core.xmpp.plugin['xep_0012'].get_last_activity( + jid, callback=callback) @command_args_parser.quoted(0, 2) def mood(self, args): @@ -719,15 +732,14 @@ class CommandCore: mood = args[0] if mood not in pep.MOODS: - return self.core.information('%s is not a correct value for a mood.' - % mood, - 'Error') + return self.core.information( + '%s is not a correct value for a mood.' % mood, 'Error') if len(args) == 2: text = args[1] else: text = None - self.core.xmpp.plugin['xep_0107'].publish_mood(mood, text, - callback=dumb_callback) + self.core.xmpp.plugin['xep_0107'].publish_mood( + mood, text, callback=dumb_callback) @command_args_parser.quoted(0, 3) def activity(self, args): @@ -740,9 +752,8 @@ class CommandCore: general = args[0] if general not in pep.ACTIVITIES: - return self.core.information('%s is not a correct value for an activity' - % general, - 'Error') + return self.core.information( + '%s is not a correct value for an activity' % general, 'Error') specific = None text = None if length == 2: @@ -755,10 +766,9 @@ class CommandCore: text = args[2] if specific and specific not in pep.ACTIVITIES[general]: return self.core.information('%s is not a correct value ' - 'for an activity' % specific, - 'Error') - self.core.xmpp.plugin['xep_0108'].publish_activity(general, specific, text, - callback=dumb_callback) + 'for an activity' % specific, 'Error') + self.core.xmpp.plugin['xep_0108'].publish_activity( + general, specific, text, callback=dumb_callback) @command_args_parser.quoted(0, 2) def gaming(self, args): @@ -773,9 +783,8 @@ class CommandCore: address = args[1] else: address = None - return self.core.xmpp.plugin['xep_0196'].publish_gaming(name=name, - server_address=address, - callback=dumb_callback) + return self.core.xmpp.plugin['xep_0196'].publish_gaming( + name=name, server_address=address, callback=dumb_callback) @command_args_parser.quoted(2, 1, [None]) def invite(self, args): @@ -800,9 +809,9 @@ class CommandCore: return reason = args[1] del self.core.pending_invites[jid.bare] - self.core.xmpp.plugin['xep_0045'].decline_invite(jid.bare, - self.core.pending_invites[jid.bare], - reason) + self.core.xmpp.plugin['xep_0045'].decline_invite( + jid.bare, self.core.pending_invites[jid.bare], reason) + ### Commands without a completion in this class ### @@ -811,8 +820,8 @@ class CommandCore: """/invitations""" build = "" for invite in self.core.pending_invites: - build += "%s by %s" % (invite, - safeJID(self.core.pending_invites[invite]).bare) + build += "%s by %s" % ( + invite, safeJID(self.core.pending_invites[invite]).bare) if self.core.pending_invites: build = "You are invited to the following rooms:\n" + build else: @@ -838,7 +847,8 @@ class CommandCore: self.core.save_config() self.core.plugin_manager.disable_plugins() self.core.disconnect(msg) - self.core.xmpp.add_event_handler("disconnected", self.core.exit, disposable=True) + self.core.xmpp.add_event_handler( + "disconnected", self.core.exit, disposable=True) @command_args_parser.quoted(0, 1, ['']) def destroy_room(self, args): @@ -849,7 +859,8 @@ class CommandCore: if room: muc.destroy_room(self.core.xmpp, room) elif isinstance(self.core.current_tab(), tabs.MucTab) and not args[0]: - muc.destroy_room(self.core.xmpp, self.core.current_tab().general_jid) + muc.destroy_room(self.core.xmpp, + self.core.current_tab().general_jid) else: self.core.information('Invalid JID: "%s"' % args[0], 'Error') @@ -862,12 +873,15 @@ class CommandCore: return self.help('bind') if not config.silent_set(args[0], args[1], section='bindings'): - self.core.information('Unable to write in the config file', 'Error') + self.core.information('Unable to write in the config file', + 'Error') if args[1]: - self.core.information('%s is now bound to %s' % (args[0], args[1]), 'Info') + self.core.information('%s is now bound to %s' % (args[0], args[1]), + 'Info') else: - self.core.information('%s is now reset to the default binding' % args[0], 'Info') + self.core.information( + '%s is now reset to the default binding' % args[0], 'Info') @command_args_parser.raw def rawxml(self, args): @@ -881,7 +895,8 @@ class CommandCore: stanza = args try: stanza = StanzaBase(self.core.xmpp, xml=ET.fromstring(stanza)) - if stanza.xml.tag == 'iq' and stanza.xml.attrib.get('type') in ('get', 'set'): + if stanza.xml.tag == 'iq' and stanza.xml.attrib.get('type') in ( + 'get', 'set'): iq_id = stanza.xml.attrib.get('id') if not iq_id: iq_id = self.core.xmpp.new_id() @@ -893,18 +908,15 @@ class CommandCore: self.core.xmpp.remove_handler('Iq %s' % iq_id) self.core.xmpp.register_handler( - Callback('Iq %s' % iq_id, - StanzaPath('iq@id=%s' % iq_id), - iqfunc - ) - ) + Callback('Iq %s' % iq_id, + StanzaPath('iq@id=%s' % iq_id), iqfunc)) stanza.send() except: self.core.information('Could not send custom stanza', 'Error') - log.debug('/rawxml: Could not send custom stanza (%s)', - repr(stanza), - exc_info=True) - + log.debug( + '/rawxml: Could not send custom stanza (%s)', + repr(stanza), + exc_info=True) @command_args_parser.quoted(1, 256) def load(self, args): @@ -928,9 +940,8 @@ class CommandCore: """ /plugins """ - self.core.information("Plugins currently in use: %s" % - repr(list(self.core.plugin_manager.plugins.keys())), - 'Info') + self.core.information("Plugins currently in use: %s" % repr( + list(self.core.plugin_manager.plugins.keys())), 'Info') @command_args_parser.quoted(1, 1) def message(self, args): @@ -942,7 +953,8 @@ class CommandCore: jid = safeJID(args[0]) if not jid.user and not jid.domain and not jid.resource: return self.core.information('Invalid JID.', 'Error') - tab = self.core.get_conversation_by_jid(jid.full, False, fallback_barejid=False) + tab = self.core.get_conversation_by_jid( + jid.full, False, fallback_barejid=False) muc = self.core.get_tab_by_name(jid.bare, typ=tabs.MucTab) if not tab and not muc: tab = self.core.open_conversation_window(jid.full, focus=True) @@ -977,8 +989,8 @@ class CommandCore: list_tab = tabs.AdhocCommandsListTab(self.core, jid) self.core.add_tab(list_tab, True) cb = list_tab.on_list_received - self.core.xmpp.plugin['xep_0050'].get_commands(jid=jid, local=False, - callback=cb) + self.core.xmpp.plugin['xep_0050'].get_commands( + jid=jid, local=False, callback=cb) @command_args_parser.ignored def self_(self): @@ -990,15 +1002,11 @@ class CommandCore: nick = self.core.own_nick jid = self.core.xmpp.boundjid.full info = ('Your JID is %s\nYour current status is "%s" (%s)' - '\nYour default nickname is %s\nYou are running poezio %s' % ( - jid, - message if message else '', - show if show else 'available', - nick, - config_opts.version)) + '\nYour default nickname is %s\nYou are running poezio %s' % + (jid, message if message else '', show + if show else 'available', nick, config_opts.version)) self.core.information(info, 'Info') - @command_args_parser.ignored def reload(self): """ @@ -1006,6 +1014,6 @@ class CommandCore: """ self.core.reload_config() + def dumb_callback(*args, **kwargs): "mock callback" - diff --git a/poezio/core/completions.py b/poezio/core/completions.py index 634ab6b3..5e7e510f 100644 --- a/poezio/core/completions.py +++ b/poezio/core/completions.py @@ -17,13 +17,15 @@ from poezio.roster import roster from poezio.core.structs import POSSIBLE_SHOW, Completion + class CompletionCore: def __init__(self, core): self.core = core def help(self, the_input): """Completion for /help.""" - commands = sorted(self.core.commands.keys()) + sorted(self.core.current_tab().commands.keys()) + commands = sorted(self.core.commands.keys()) + sorted( + self.core.current_tab().commands.keys()) return Completion(the_input.new_completion, commands, 1, quotify=False) def status(self, the_input): @@ -31,8 +33,11 @@ class CompletionCore: Completion of /status """ if the_input.get_argument_position() == 1: - return Completion(the_input.new_completion, [status for status in POSSIBLE_SHOW], 1, ' ', quotify=False) - + return Completion( + the_input.new_completion, [status for status in POSSIBLE_SHOW], + 1, + ' ', + quotify=False) def presence(self, the_input): """ @@ -40,29 +45,38 @@ class CompletionCore: """ arg = the_input.get_argument_position() if arg == 1: - return Completion(the_input.auto_completion, [jid for jid in roster.jids()], '', quotify=True) + return Completion( + the_input.auto_completion, [jid for jid in roster.jids()], + '', + quotify=True) elif arg == 2: - return Completion(the_input.auto_completion, [status for status in POSSIBLE_SHOW], '', quotify=True) - + return Completion( + the_input.auto_completion, + [status for status in POSSIBLE_SHOW], + '', + quotify=True) def theme(self, the_input): """ Completion for /theme""" themes_dir = config.get('themes_dir') - themes_dir = (themes_dir or - os.path.join(os.environ.get('XDG_DATA_HOME') or - os.path.join(os.environ.get('HOME'), '.local', 'share'), - 'poezio', 'themes')) + themes_dir = (themes_dir or os.path.join( + os.environ.get('XDG_DATA_HOME') + or os.path.join(os.environ.get('HOME'), '.local', 'share'), + 'poezio', 'themes')) themes_dir = os.path.expanduser(themes_dir) try: names = os.listdir(themes_dir) except OSError: log.error('Completion for /theme failed', exc_info=True) return False - theme_files = [name[:-3] for name in names if name.endswith('.py') and name != '__init__.py'] + theme_files = [ + name[:-3] for name in names + if name.endswith('.py') and name != '__init__.py' + ] if 'default' not in theme_files: theme_files.append('default') - return Completion(the_input.new_completion, theme_files, 1, '', quotify=False) - + return Completion( + the_input.new_completion, theme_files, 1, '', quotify=False) def win(self, the_input): """Completion for /win""" @@ -72,7 +86,6 @@ class CompletionCore: l = [i[1] for i in l] return Completion(the_input.new_completion, l, 1, '', quotify=False) - def join(self, the_input): """ Completion for /join @@ -97,7 +110,9 @@ class CompletionCore: relevant_rooms = [] relevant_rooms.extend(sorted(self.core.pending_invites.keys())) - bookmarks = [(str(elem.jid) if not elem.nick else '%s/%s' % (elem.jid, elem.nick)) for elem in self.core.bookmarks] + bookmarks = [(str(elem.jid) + if not elem.nick else '%s/%s' % (elem.jid, elem.nick)) + for elem in self.core.bookmarks] to_suggest = [] for bookmark in bookmarks: tab = self.core.get_tab_by_name(bookmark, tabs.MucTab) @@ -113,31 +128,39 @@ class CompletionCore: serv_list = [] for tab in self.core.get_tabs(tabs.MucTab): if tab.joined: - serv_list.append('%s@%s' % (jid.user, safeJID(tab.name).host)) + serv_list.append('%s@%s' % (jid.user, + safeJID(tab.name).host)) serv_list.extend(relevant_rooms) - return Completion(the_input.new_completion, serv_list, 1, quotify=True) + return Completion( + the_input.new_completion, serv_list, 1, quotify=True) elif args[1].startswith('/'): # we completing only a resource - return Completion(the_input.new_completion, ['/%s' % self.core.own_nick], 1, quotify=True) + return Completion( + the_input.new_completion, ['/%s' % self.core.own_nick], + 1, + quotify=True) else: - return Completion(the_input.new_completion, relevant_rooms, 1, quotify=True) - + return Completion( + the_input.new_completion, relevant_rooms, 1, quotify=True) def version(self, the_input): """Completion for /version""" - comp = reduce(lambda x, y: x + [i.jid for i in y], (roster[jid].resources for jid in roster.jids() if len(roster[jid])), []) - return Completion(the_input.new_completion, sorted(comp), 1, quotify=False) - + comp = reduce(lambda x, y: x + [i.jid for i in y], + (roster[jid].resources for jid in roster.jids() + if len(roster[jid])), []) + return Completion( + the_input.new_completion, sorted(comp), 1, quotify=False) def list(self, the_input): """Completion for /list""" muc_serv_list = [] - for tab in self.core.get_tabs(tabs.MucTab): # TODO, also from an history + for tab in self.core.get_tabs( + tabs.MucTab): # TODO, also from an history if tab.name not in muc_serv_list: muc_serv_list.append(safeJID(tab.name).server) if muc_serv_list: - return Completion(the_input.new_completion, muc_serv_list, 1, quotify=False) - + return Completion( + the_input.new_completion, muc_serv_list, 1, quotify=False) def move_tab(self, the_input): """Completion for /move_tab""" @@ -145,8 +168,8 @@ class CompletionCore: if n == 1: nodes = [tab.name for tab in self.core.tabs if tab] nodes.remove('Roster') - return Completion(the_input.new_completion, nodes, 1, ' ', quotify=True) - + return Completion( + the_input.new_completion, nodes, 1, ' ', quotify=True) def runkey(self, the_input): """ @@ -157,14 +180,14 @@ class CompletionCore: list_.extend(self.core.current_tab().key_func.keys()) return Completion(the_input.new_completion, list_, 1, quotify=False) - def bookmark(self, the_input): """Completion for /bookmark""" args = common.shell_split(the_input.text) n = the_input.get_argument_position(quoted=True) if n == 2: - return Completion(the_input.new_completion, ['true', 'false'], 2, quotify=True) + return Completion( + the_input.new_completion, ['true', 'false'], 2, quotify=True) if n >= 3: return False @@ -175,7 +198,8 @@ class CompletionCore: if jid.server and (jid.resource or jid.full.endswith('/')): tab = self.core.get_tab_by_name(jid.bare, tabs.MucTab) nicks = [tab.own_nick] if tab else [] - default = os.environ.get('USER') if os.environ.get('USER') else 'poezio' + default = os.environ.get('USER') if os.environ.get( + 'USER') else 'poezio' nick = config.get('default_nick') if not nick: if default not in nicks: @@ -184,29 +208,37 @@ class CompletionCore: if nick not in nicks: nicks.append(nick) jids_list = ['%s/%s' % (jid.bare, nick) for nick in nicks] - return Completion(the_input.new_completion, jids_list, 1, quotify=True) + return Completion( + the_input.new_completion, jids_list, 1, quotify=True) muc_list = [tab.name for tab in self.core.get_tabs(tabs.MucTab)] muc_list.sort() muc_list.append('*') return Completion(the_input.new_completion, muc_list, 1, quotify=True) - def remove_bookmark(self, the_input): """Completion for /remove_bookmark""" - return Completion(the_input.new_completion, [bm.jid for bm in self.core.bookmarks], 1, quotify=False) - + return Completion( + the_input.new_completion, [bm.jid for bm in self.core.bookmarks], + 1, + quotify=False) def decline(self, the_input): """Completion for /decline""" n = the_input.get_argument_position(quoted=True) if n == 1: - return Completion(the_input.auto_completion, sorted(self.core.pending_invites.keys()), 1, '', quotify=True) - + return Completion( + the_input.auto_completion, + sorted(self.core.pending_invites.keys()), + 1, + '', + quotify=True) def bind(self, the_input): n = the_input.get_argument_position() if n == 1: - args = [key for key in self.core.key_func if not key.startswith('_')] + args = [ + key for key in self.core.key_func if not key.startswith('_') + ] elif n == 2: args = [key for key in self.core.key_func] else: @@ -214,7 +246,6 @@ class CompletionCore: return Completion(the_input.new_completion, args, n, '', quotify=False) - def message(self, the_input): """Completion for /message""" n = the_input.get_argument_position(quoted=True) @@ -231,14 +262,17 @@ class CompletionCore: l.append(jid) return Completion(the_input.new_completion, l, 1, '', quotify=True) - def invite(self, the_input): """Completion for /invite""" n = the_input.get_argument_position(quoted=True) if n == 1: - comp = reduce(lambda x, y: x + [i.jid for i in y], (roster[jid].resources for jid in roster.jids() if len(roster[jid])), []) + comp = reduce(lambda x, y: x + [i.jid for i in y], + (roster[jid].resources for jid in roster.jids() + if len(roster[jid])), []) comp = sorted(comp) - bares = sorted(roster[contact].bare_jid for contact in roster.jids() if len(roster[contact])) + bares = sorted( + roster[contact].bare_jid for contact in roster.jids() + if len(roster[contact])) off = sorted(jid for jid in roster.jids() if jid not in bares) comp = comp + bares + off return Completion(the_input.new_completion, comp, n, quotify=True) @@ -248,15 +282,19 @@ class CompletionCore: if tab.joined: rooms.append(tab.name) rooms.sort() - return Completion(the_input.new_completion, rooms, n, '', quotify=True) - + return Completion( + the_input.new_completion, rooms, n, '', quotify=True) def activity(self, the_input): """Completion for /activity""" n = the_input.get_argument_position(quoted=True) args = common.shell_split(the_input.text) if n == 1: - return Completion(the_input.new_completion, sorted(pep.ACTIVITIES.keys()), n, quotify=True) + return Completion( + the_input.new_completion, + sorted(pep.ACTIVITIES.keys()), + n, + quotify=True) elif n == 2: if args[1] in pep.ACTIVITIES: l = list(pep.ACTIVITIES[args[1]]) @@ -264,13 +302,15 @@ class CompletionCore: l.sort() return Completion(the_input.new_completion, l, n, quotify=True) - def mood(self, the_input): """Completion for /mood""" n = the_input.get_argument_position(quoted=True) if n == 1: - return Completion(the_input.new_completion, sorted(pep.MOODS.keys()), 1, quotify=True) - + return Completion( + the_input.new_completion, + sorted(pep.MOODS.keys()), + 1, + quotify=True) def last_activity(self, the_input): """ @@ -279,9 +319,11 @@ class CompletionCore: n = the_input.get_argument_position(quoted=False) if n >= 2: return False - comp = reduce(lambda x, y: x + [i.jid for i in y], (roster[jid].resources for jid in roster.jids() if len(roster[jid])), []) - return Completion(the_input.new_completion, sorted(comp), 1, '', quotify=False) - + comp = reduce(lambda x, y: x + [i.jid for i in y], + (roster[jid].resources for jid in roster.jids() + if len(roster[jid])), []) + return Completion( + the_input.new_completion, sorted(comp), 1, '', quotify=False) def server_cycle(self, the_input): """Completion for /server_cycle""" @@ -291,7 +333,6 @@ class CompletionCore: serv_list.add(serv) return Completion(the_input.new_completion, sorted(serv_list), 1, ' ') - def set(self, the_input): """Completion for /set""" args = common.shell_split(the_input.text) @@ -302,9 +343,13 @@ class CompletionCore: if '|' in args[1]: plugin_name, section = args[1].split('|')[:2] if plugin_name not in self.core.plugin_manager.plugins: - return Completion(the_input.new_completion, [], n, quotify=True) + return Completion( + the_input.new_completion, [], n, quotify=True) plugin = self.core.plugin_manager.plugins[plugin_name] - end_list = ['%s|%s' % (plugin_name, section) for section in plugin.config.sections()] + end_list = [ + '%s|%s' % (plugin_name, section) + for section in plugin.config.sections() + ] else: end_list = set(config.options('Poezio')) end_list.update(config.default.get('Poezio', {})) @@ -314,11 +359,13 @@ class CompletionCore: if '|' in args[1]: plugin_name, section = args[1].split('|')[:2] if plugin_name not in self.core.plugin_manager.plugins: - return Completion(the_input.new_completion, [''], n, quotify=True) + return Completion( + the_input.new_completion, [''], n, quotify=True) plugin = self.core.plugin_manager.plugins[plugin_name] end_list = set(plugin.config.options(section or plugin_name)) if plugin.config.default: - end_list.update(plugin.config.default.get(section or plugin_name, {})) + end_list.update( + plugin.config.default.get(section or plugin_name, {})) end_list = list(end_list) end_list.sort() elif not config.has_option('Poezio', args[1]): @@ -333,9 +380,14 @@ class CompletionCore: if '|' in args[1]: plugin_name, section = args[1].split('|')[:2] if plugin_name not in self.core.plugin_manager.plugins: - return Completion(the_input.new_completion, [''], n, quotify=True) + return Completion( + the_input.new_completion, [''], n, quotify=True) plugin = self.core.plugin_manager.plugins[plugin_name] - end_list = [str(plugin.config.get(args[2], '', section or plugin_name)), ''] + end_list = [ + str( + plugin.config.get(args[2], '', section + or plugin_name)), '' + ] else: if not config.has_section(args[1]): end_list = [''] @@ -345,7 +397,6 @@ class CompletionCore: return False return Completion(the_input.new_completion, end_list, n, quotify=True) - def set_default(self, the_input): """ Completion for /set_default """ @@ -357,11 +408,13 @@ class CompletionCore: return Completion(self.set, the_input) return False - def toggle(self, the_input): "Completion for /toggle" - return Completion(the_input.new_completion, config.options('Poezio'), 1, quotify=False) - + return Completion( + the_input.new_completion, + config.options('Poezio'), + 1, + quotify=False) def bookmark_local(self, the_input): """Completion for /bookmark_local""" @@ -377,7 +430,8 @@ class CompletionCore: if jid.server and (jid.resource or jid.full.endswith('/')): tab = self.core.get_tab_by_name(jid.bare, tabs.MucTab) nicks = [tab.own_nick] if tab else [] - default = os.environ.get('USER') if os.environ.get('USER') else 'poezio' + default = os.environ.get('USER') if os.environ.get( + 'USER') else 'poezio' nick = config.get('default_nick') if not nick: if default not in nicks: @@ -386,8 +440,8 @@ class CompletionCore: if nick not in nicks: nicks.append(nick) jids_list = ['%s/%s' % (jid.bare, nick) for nick in nicks] - return Completion(the_input.new_completion, jids_list, 1, quotify=True) + return Completion( + the_input.new_completion, jids_list, 1, quotify=True) muc_list = [tab.name for tab in self.core.get_tabs(tabs.MucTab)] muc_list.append('*') return Completion(the_input.new_completion, muc_list, 1, quotify=True) - diff --git a/poezio/core/core.py b/poezio/core/core.py index 33bbbc54..7678c747 100644 --- a/poezio/core/core.py +++ b/poezio/core/core.py @@ -66,8 +66,7 @@ class Core(object): self.stdscr = None status = config.get('status') status = POSSIBLE_SHOW.get(status, None) - self.status = Status(show=status, - message=config.get('status_message')) + self.status = Status(show=status, message=config.get('status_message')) self.running = True self.xmpp = connection.Connection() self.xmpp.core = self @@ -81,7 +80,8 @@ class Core(object): # 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 = 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 @@ -164,7 +164,7 @@ class Core(object): 'M-D': self.scroll_info_up, 'M-C': self.scroll_info_down, 'M-k': self.escape_next_key, - ######## actions mappings ########## + ######## actions mappings ########## '_noop': lambda *args, **kwargs: None, '_bookmark': self.command.bookmark, '_bookmark_local': self.command.bookmark_local, @@ -188,25 +188,30 @@ class Core(object): '_show_plugins': self.command.plugins, '_show_xmltab': self.command.xml_tab, '_toggle_pane': self.toggle_left_pane, - ###### status actions ###### + ###### status actions ###### '_available': lambda: self.command.status('available'), '_away': lambda: self.command.status('away'), '_chat': lambda: self.command.status('chat'), '_dnd': lambda: self.command.status('dnd'), '_xa': lambda: self.command.status('xa'), - ##### Custom actions ######## + ##### Custom actions ######## '_exc_': self.try_execute, } self.key_func.update(key_func) # Add handlers self.xmpp.add_event_handler('connected', self.handler.on_connected) - self.xmpp.add_event_handler('connection_failed', self.handler.on_failed_connection) - self.xmpp.add_event_handler('disconnected', self.handler.on_disconnected) - self.xmpp.add_event_handler('stream_error', self.handler.on_stream_error) - self.xmpp.add_event_handler('failed_all_auth', self.handler.on_failed_all_auth) + self.xmpp.add_event_handler('connection_failed', + self.handler.on_failed_connection) + self.xmpp.add_event_handler('disconnected', + self.handler.on_disconnected) + self.xmpp.add_event_handler('stream_error', + self.handler.on_stream_error) + self.xmpp.add_event_handler('failed_all_auth', + self.handler.on_failed_all_auth) self.xmpp.add_event_handler('no_auth', self.handler.on_no_auth) - self.xmpp.add_event_handler("session_start", self.handler.on_session_start) + self.xmpp.add_event_handler("session_start", + self.handler.on_session_start) self.xmpp.add_event_handler("session_start", self.handler.on_session_start_features) self.xmpp.add_event_handler("groupchat_presence", @@ -215,8 +220,9 @@ class Core(object): self.handler.on_groupchat_message) self.xmpp.add_event_handler("groupchat_invite", self.handler.on_groupchat_invitation) - self.xmpp.add_event_handler("groupchat_direct_invite", - self.handler.on_groupchat_direct_invitation) + self.xmpp.add_event_handler( + "groupchat_direct_invite", + self.handler.on_groupchat_direct_invitation) self.xmpp.add_event_handler("groupchat_decline", self.handler.on_groupchat_decline) self.xmpp.add_event_handler("groupchat_config_status", @@ -224,13 +230,17 @@ class Core(object): self.xmpp.add_event_handler("groupchat_subject", self.handler.on_groupchat_subject) self.xmpp.add_event_handler("message", self.handler.on_message) - self.xmpp.add_event_handler("message_error", self.handler.on_error_message) - self.xmpp.add_event_handler("receipt_received", self.handler.on_receipt) + self.xmpp.add_event_handler("message_error", + self.handler.on_error_message) + self.xmpp.add_event_handler("receipt_received", + self.handler.on_receipt) self.xmpp.add_event_handler("got_online", self.handler.on_got_online) self.xmpp.add_event_handler("got_offline", self.handler.on_got_offline) - self.xmpp.add_event_handler("roster_update", self.handler.on_roster_update) + self.xmpp.add_event_handler("roster_update", + self.handler.on_roster_update) self.xmpp.add_event_handler("changed_status", self.handler.on_presence) - self.xmpp.add_event_handler("presence_error", self.handler.on_presence_error) + self.xmpp.add_event_handler("presence_error", + self.handler.on_presence_error) self.xmpp.add_event_handler("roster_subscription_request", self.handler.on_subscription_request) self.xmpp.add_event_handler("roster_subscription_authorized", @@ -252,8 +262,10 @@ class Core(object): self.handler.on_chatstate_inactive) self.xmpp.add_event_handler("attention", self.handler.on_attention) self.xmpp.add_event_handler("ssl_cert", self.handler.validate_ssl) - self.xmpp.add_event_handler("ssl_invalid_chain", self.handler.ssl_invalid_chain) - self.xmpp.add_event_handler('carbon_received', self.handler.on_carbon_received) + self.xmpp.add_event_handler("ssl_invalid_chain", + self.handler.ssl_invalid_chain) + self.xmpp.add_event_handler('carbon_received', + self.handler.on_carbon_received) self.xmpp.add_event_handler('carbon_sent', self.handler.on_carbon_sent) self.xmpp.add_event_handler('http_confirm', self.handler.http_confirm) @@ -315,14 +327,11 @@ class Core(object): self.xmpp.set_keepalive_values) self.add_configuration_handler("connection_check_interval", self.xmpp.set_keepalive_values) - self.add_configuration_handler("themes_dir", - theming.update_themes_dir) - self.add_configuration_handler("theme", - self.on_theme_config_change) + self.add_configuration_handler("themes_dir", theming.update_themes_dir) + self.add_configuration_handler("theme", self.on_theme_config_change) self.add_configuration_handler("use_bookmarks_method", self.on_bookmarks_method_config_change) - self.add_configuration_handler("password", - self.on_password_change) + self.add_configuration_handler("password", self.on_password_change) self.add_configuration_handler("enable_vertical_tab_list", self.on_vertical_tab_list_config_change) self.add_configuration_handler("vertical_tab_list_size", @@ -394,14 +403,15 @@ class Core(object): """ Called when the request_message_receipts option changes """ - self.xmpp.plugin['xep_0184'].auto_request = config.get(option, - default=True) + self.xmpp.plugin['xep_0184'].auto_request = config.get( + option, default=True) def on_ack_receipts_config_change(self, option, value): """ Called when the ack_message_receipts option changes """ - self.xmpp.plugin['xep_0184'].auto_ack = config.get(option, default=True) + self.xmpp.plugin['xep_0184'].auto_ack = config.get( + option, default=True) def on_plugins_dir_config_change(self, option, value): """ @@ -438,7 +448,6 @@ class Core(object): """ self.xmpp.password = value - def on_nick_determinism_changed(self, option, value): """If we change the value to true, we call /recolor on all the MucTabs, to make the current nick colors reflect their deterministic value. @@ -501,10 +510,10 @@ class Core(object): """ sig = args[0] signals = { - 1: 'SIGHUP', - 13: 'SIGPIPE', - 15: 'SIGTERM', - } + 1: 'SIGHUP', + 13: 'SIGPIPE', + 15: 'SIGTERM', + } log.error("%s received. Exiting…", signals[sig]) if config.get('enable_user_mood'): @@ -547,13 +556,12 @@ class Core(object): 'The online help is here http://doc.poez.io/\n' 'No room is joined by default, but you can join poezio’s' ' room (with /join poezio@muc.poez.io), where you can' - ' ask for help or tell us how great it is.', - 'Help') + ' ask for help or tell us how great it is.', 'Help') self.refresh_window() self.xmpp.plugin['xep_0012'].begin_idle(jid=self.xmpp.boundjid) def exit(self, event=None): - log.debug("exit(%s)", event) + log.debug("exit(%s)", event) asyncio.get_event_loop().stop() def on_exception(self, typ, value, trace): @@ -592,11 +600,13 @@ class Core(object): """ main loop waiting for the user to press a key """ + def replace_line_breaks(key): "replace ^J with \n" if key == '^J': return '\n' return key + def separate_chars_from_bindings(char_list): """ returns a list of lists. For example if you give @@ -638,7 +648,7 @@ class Core(object): log.debug("Input is readable.") big_char_list = [replace_key_with_bound(key)\ for key in self.read_keyboard()] - log.debug("Got from keyboard: %s", (big_char_list,)) + log.debug("Got from keyboard: %s", (big_char_list, )) # whether to refresh after ALL keys have been handled for char_list in separate_chars_from_bindings(big_char_list): @@ -651,7 +661,8 @@ class Core(object): except ValueError: pass else: - if self.current_tab().nb == nb and config.get('go_to_previous_tab_on_alt_number'): + if self.current_tab().nb == nb and config.get( + 'go_to_previous_tab_on_alt_number'): self.go_to_previous_tab() else: self.command.win('%d' % nb) @@ -673,12 +684,10 @@ class Core(object): """ ok = roster.save_to_config_file() ok = ok and config.silent_set('info_win_height', - self.information_win_size, - 'var') + self.information_win_size, 'var') if not ok: self.information('Unable to save runtime preferences' - ' in the config file', - 'Error') + ' in the config file', 'Error') def on_roster_enter_key(self, roster_row): """ @@ -690,9 +699,8 @@ class Core(object): else: self.focus_tab_named(roster_row.bare_jid) if isinstance(roster_row, Resource): - if not self.get_conversation_by_jid(roster_row.jid, - False, - fallback_barejid=False): + if not self.get_conversation_by_jid( + roster_row.jid, False, fallback_barejid=False): self.open_conversation_window(roster_row.jid) else: self.focus_tab_named(roster_row.jid) @@ -716,7 +724,6 @@ class Core(object): """ self.do_command(text, True) - ##################### Anything related to command execution ################### def execute(self, line): @@ -727,15 +734,14 @@ class Core(object): return if line.startswith('/'): command = line.strip().split()[0][1:] - arg = line[2+len(command):] # jump the '/' and the ' ' + arg = line[2 + len(command):] # jump the '/' and the ' ' # example. on "/link 0 open", command = "link" and arg = "0 open" if command in self.commands: func = self.commands[command].func func(arg) return else: - self.information("Unknown command (%s)" % (command), - 'Error') + self.information("Unknown command (%s)" % (command), 'Error') def exec_command(self, command): """ @@ -770,16 +776,15 @@ class Core(object): fifo_path = config.get('remote_fifo_path') if not self.remote_fifo: try: - self.remote_fifo = Fifo(os.path.join(fifo_path, - 'poezio.fifo'), - 'w') + self.remote_fifo = Fifo( + os.path.join(fifo_path, 'poezio.fifo'), 'w') except (OSError, IOError) as exc: - log.error('Could not open the fifo for writing (%s)', - os.path.join(fifo_path, './', 'poezio.fifo'), - exc_info=True) + log.error( + 'Could not open the fifo for writing (%s)', + os.path.join(fifo_path, './', 'poezio.fifo'), + exc_info=True) self.information('Could not open the fifo ' - 'file for writing: %s' % exc, - 'Error') + 'file for writing: %s' % exc, 'Error') return args = (pipes.quote(arg.replace('\n', ' ')) for arg in command) @@ -787,10 +792,11 @@ class Core(object): try: self.remote_fifo.write(command_str) except (IOError) as exc: - log.error('Could not write in the fifo (%s): %s', - os.path.join(fifo_path, './', 'poezio.fifo'), - repr(command), - exc_info=True) + log.error( + 'Could not write in the fifo (%s): %s', + os.path.join(fifo_path, './', 'poezio.fifo'), + repr(command), + exc_info=True) self.information('Could not execute %s: %s' % (command, exc), 'Error') self.remote_fifo = None @@ -799,12 +805,12 @@ class Core(object): try: executor.start() except ValueError as exc: - log.error('Could not execute command (%s)', - repr(command), - exc_info=True) + log.error( + 'Could not execute command (%s)', + repr(command), + exc_info=True) self.information('%s' % exc, 'Error') - def do_command(self, key, raw): """ Execute the action associated with a key @@ -824,7 +830,6 @@ class Core(object): else: self.current_tab().on_input(key, raw) - def try_execute(self, line): """ Try to execute a command in the current tab @@ -835,7 +840,6 @@ class Core(object): except: log.error('Execute failed (%s)', line, exc_info=True) - ########################## TImed Events ####################################### def remove_timed_event(self, event): @@ -844,9 +848,8 @@ class Core(object): def add_timed_event(self, event): """Add a new timed event""" - event.handler = asyncio.get_event_loop().call_later(event.delay, - event.callback, - *event.args) + event.handler = asyncio.get_event_loop().call_later( + event.delay, event.callback, *event.args) ####################### XMPP-related actions ################################## @@ -894,7 +897,10 @@ class Core(object): if reconnect: # Add a one-time event to reconnect as soon as we are # effectively disconnected - self.xmpp.add_event_handler('disconnected', lambda event: self.xmpp.connect(), disposable=True) + self.xmpp.add_event_handler( + 'disconnected', + lambda event: self.xmpp.connect(), + disposable=True) def send_message(self, msg): """ @@ -913,20 +919,19 @@ class Core(object): or a mediated one if it does not. TODO: allow passwords """ + def callback(iq): if not iq: return if 'jabber:x:conference' in iq['disco_info'].get_features(): self.xmpp.plugin['xep_0249'].send_invitation( - jid, - room, - reason=reason) - else: # fallback - self.xmpp.plugin['xep_0045'].invite(room, jid, - reason=reason or '') + jid, room, reason=reason) + else: # fallback + self.xmpp.plugin['xep_0045'].invite( + room, jid, reason=reason or '') - self.xmpp.plugin['xep_0030'].get_info(jid=jid, timeout=5, - callback=callback) + self.xmpp.plugin['xep_0030'].get_info( + jid=jid, timeout=5, callback=callback) def get_error_message(self, stanza, deprecated=False): """ @@ -951,16 +956,22 @@ class Core(object): body = condition or 'Unknown error' if code: message = '%(from)s: %(code)s - %(msg)s: %(body)s' % { - 'from': sender, 'msg': msg, 'body': body, 'code': code} + 'from': sender, + 'msg': msg, + 'body': body, + 'code': code + } else: message = '%(from)s: %(msg)s: %(body)s' % { - 'from': sender, 'msg': msg, 'body': body} + 'from': sender, + 'msg': msg, + 'body': body + } return message - ####################### Tab logic-related things ############################## - ### Tab getters ### +### Tab getters ### def get_tabs(self, cls=None): "Get all the tabs of a type" @@ -1002,8 +1013,8 @@ class Core(object): # We create a dynamic conversation with the bare Jid if # nothing was found (and we lock it to the resource # later) - conversation = self.open_conversation_window(jid.bare, - False) + conversation = self.open_conversation_window( + jid.bare, False) else: conversation = None return conversation @@ -1062,7 +1073,8 @@ class Core(object): if not target: 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) + self.tabs[new_pos], self.tabs[old_pos] = old_tab, tabs.GapTab( + self) else: self.tabs.append(self.tabs[old_pos]) self.tabs[old_pos] = tabs.GapTab(self) @@ -1139,6 +1151,7 @@ class Core(object): Read 2 more chars and go to the tab with the given number """ + def read_next_digit(digit): try: int(digit) @@ -1156,6 +1169,7 @@ class Core(object): else: # We need to read more digits keyboard.continuation_keys_callback = read_next_digit + keyboard.continuation_keys_callback = read_next_digit def go_to_roster(self): @@ -1164,7 +1178,7 @@ class Core(object): def go_to_previous_tab(self): "Go to the previous tab" - self.command.win('%s' % (self.previous_tab_nb,)) + self.command.win('%s' % (self.previous_tab_nb, )) def go_to_important_room(self): """ @@ -1183,15 +1197,14 @@ class Core(object): else: tab_refs[tab.state].append(tab) # sort the state by priority and remove those with negative priority - states = sorted(tab_refs.keys(), - key=(lambda x: priority.get(x, 0)), - reverse=True) + states = sorted( + tab_refs.keys(), key=(lambda x: priority.get(x, 0)), reverse=True) states = [state for state in states if priority.get(state, -1) >= 0] for state in states: for tab in tab_refs[state]: - if (tab.nb < self.current_tab_nb and - tab_refs[state][-1].nb > self.current_tab_nb): + if (tab.nb < self.current_tab_nb + and tab_refs[state][-1].nb > self.current_tab_nb): continue self.command.win('%s' % tab.nb) return @@ -1202,7 +1215,7 @@ class Core(object): for tab in self.tabs: if tab.name == tab_name: if (type_ and (isinstance(tab, type_))) or not type_: - self.command.win('%s' % (tab.nb,)) + self.command.win('%s' % (tab.nb, )) return True return False @@ -1249,7 +1262,7 @@ class Core(object): """ Open a Private conversation in a MUC and focus if needed. """ - complete_jid = room_name+'/'+user_nick + complete_jid = room_name + '/' + user_nick # if the room exists, focus it and return for tab in self.get_tabs(tabs.PrivateTab): if tab.name == complete_jid: @@ -1301,12 +1314,14 @@ class Core(object): if tab: tab.rename_user(old_nick, user) - def on_user_left_private_conversation(self, room_name, user, status_message): + def on_user_left_private_conversation(self, room_name, user, + status_message): """ The user left the MUC: add a message in the associated private conversation """ - tab = self.get_tab_by_name('%s/%s' % (room_name, user.nick), tabs.PrivateTab) + tab = self.get_tab_by_name('%s/%s' % (room_name, user.nick), + tabs.PrivateTab) if tab: tab.user_left(status_message, user) @@ -1315,7 +1330,8 @@ class Core(object): The user joined a MUC: add a message in the associated private conversation """ - tab = self.get_tab_by_name('%s/%s' % (room_name, nick), tabs.PrivateTab) + tab = self.get_tab_by_name('%s/%s' % (room_name, nick), + tabs.PrivateTab) if tab: tab.user_rejoined(nick) @@ -1352,10 +1368,10 @@ class Core(object): if tab is None: tab = self.current_tab() if isinstance(tab, tabs.RosterInfoTab): - return # The tab 0 should NEVER be closed + return # The tab 0 should NEVER be closed tab.on_close() - del tab.key_func # Remove self references - del tab.commands # and make the object collectable + del tab.key_func # Remove self references + del tab.commands # and make the object collectable nb = tab.nb if was_current: if self.previous_tab_nb != nb: @@ -1365,7 +1381,7 @@ class Core(object): if nb >= len(self.tabs) - 1: self.tabs.remove(tab) nb -= 1 - while not self.tabs[nb]: # remove the trailing gaps + while not self.tabs[nb]: # remove the trailing gaps self.tabs.pop() nb -= 1 else: @@ -1397,7 +1413,6 @@ class Core(object): if self.current_tab() is tab: self.refresh_window() - ####################### Curses and ui-related stuff ########################### def doupdate(self): @@ -1412,18 +1427,21 @@ class Core(object): """ filter_types = config.get('information_buffer_type_filter').split(':') if typ.lower() in filter_types: - log.debug('Did not show the message:\n\t%s> %s \n\tdue to information_popup_type_filter configuration', typ, msg) + log.debug( + 'Did not show the message:\n\t%s> %s \n\tdue to information_popup_type_filter configuration', + typ, msg) return False filter_messages = config.get('filter_info_messages').split(':') for words in filter_messages: if words and words in msg: - log.debug('Did not show the message:\n\t%s> %s \n\tdue to filter_info_messages configuration', typ, msg) + log.debug( + 'Did not show the message:\n\t%s> %s \n\tdue to filter_info_messages configuration', + typ, msg) return False colors = get_theme().INFO_COLORS color = colors.get(typ.lower(), colors.get('default', None)) - nb_lines = self.information_buffer.add_message(msg, - nickname=typ, - nick_color=color) + nb_lines = self.information_buffer.add_message( + msg, nickname=typ, nick_color=color) popup_on = config.get('information_buffer_popup_on').split() if isinstance(self.current_tab(), tabs.RosterInfoTab): self.refresh_window() @@ -1449,8 +1467,8 @@ class Core(object): curses.start_color() curses.use_default_colors() theming.reload_theme() - curses.ungetch(" ") # H4X: without this, the screen is - stdscr.getkey() # erased on the first "getkey()" + curses.ungetch(" ") # H4X: without this, the screen is + stdscr.getkey() # erased on the first "getkey()" def reset_curses(self): """ @@ -1604,9 +1622,8 @@ class Core(object): if time <= 0 or size <= 0: return result = self.grow_information_win(size) - timed_event = timed_events.DelayedEvent(time, - self.shrink_information_win, - result) + timed_event = timed_events.DelayedEvent( + time, self.shrink_information_win, result) self.add_timed_event(timed_event) self.refresh_window() @@ -1627,12 +1644,10 @@ class Core(object): self.information_win_size = tabs.Tab.height - 6 if tabs.Tab.height < 6: self.information_win_size = 0 - height = (tabs.Tab.height - 1 - self.information_win_size - - tabs.Tab.tab_win_height()) - self.information_win.resize(self.information_win_size, - tabs.Tab.width, - height, - 0) + height = (tabs.Tab.height - 1 - self.information_win_size - + tabs.Tab.tab_win_height()) + self.information_win.resize(self.information_win_size, tabs.Tab.width, + height, 0) def resize_global_info_bar(self): """ @@ -1645,16 +1660,15 @@ class Core(object): return try: height, _ = self.stdscr.getmaxyx() - truncated_win = self.stdscr.subwin(height, - config.get('vertical_tab_list_size'), - 0, 0) + truncated_win = self.stdscr.subwin( + height, config.get('vertical_tab_list_size'), 0, 0) except: log.error('Curses error on infobar resize', exc_info=True) return - self.left_tab_win = windows.VerticalGlobalInfoBar(self, truncated_win) + self.left_tab_win = windows.VerticalGlobalInfoBar( + self, truncated_win) elif not self.size.core_degrade_y: - self.tab_win.resize(1, tabs.Tab.width, - tabs.Tab.height - 2, 0) + self.tab_win.resize(1, tabs.Tab.width, tabs.Tab.height - 2, 0) self.left_tab_win = None def add_message_to_text_buffer(self, buff, txt, nickname=None): @@ -1663,7 +1677,8 @@ class Core(object): (in the Info tab of the info window in the RosterTab) """ if not buff: - self.information('Trying to add a message in no room: %s' % txt, 'Error') + self.information('Trying to add a message in no room: %s' % txt, + 'Error') return buff.add_message(txt, nickname=nickname) @@ -1683,11 +1698,11 @@ class Core(object): # the screen that they can occupy, and we draw the tab list on the # remaining space, on the left height, width = self.stdscr.getmaxyx() - if (config.get('enable_vertical_tab_list') and - not self.size.core_degrade_x): + if (config.get('enable_vertical_tab_list') + and not self.size.core_degrade_x): try: scr = self.stdscr.subwin(0, - config.get('vertical_tab_list_size')) + config.get('vertical_tab_list_size')) except: log.error('Curses error on resize', exc_info=True) return @@ -1739,227 +1754,300 @@ class Core(object): """ Register the commands when poezio starts """ - self.register_command('help', self.command.help, - usage='[command]', - shortdesc='\\_o< KOIN KOIN KOIN', - completion=self.completion.help) - self.register_command('join', self.command.join, - usage="[room_name][@server][/nick] [password]", - desc="Join the specified room. You can specify a nickname " - "after a slash (/). If no nickname is specified, you will" - " use the default_nick in the configuration file. You can" - " omit the room name: you will then join the room you\'re" - " looking at (useful if you were kicked). You can also " - "provide a room_name without specifying a server, the " - "server of the room you're currently in will be used. You" - " can also provide a password to join the room.\nExamples" - ":\n/join room@server.tld\n/join room@server.tld/John\n" - "/join room2\n/join /me_again\n/join\n/join room@server" - ".tld/my_nick password\n/join / password", - shortdesc='Join a room', - completion=self.completion.join) - self.register_command('exit', self.command.quit, - desc='Just disconnect from the server and exit poezio.', - shortdesc='Exit poezio.') - self.register_command('quit', self.command.quit, - desc='Just disconnect from the server and exit poezio.', - shortdesc='Exit poezio.') - self.register_command('next', self.rotate_rooms_right, - shortdesc='Go to the next room.') - self.register_command('prev', self.rotate_rooms_left, - shortdesc='Go to the previous room.') - self.register_command('win', self.command.win, - usage='', - shortdesc='Go to the specified room', - completion=self.completion.win) + self.register_command( + 'help', + self.command.help, + usage='[command]', + shortdesc='\\_o< KOIN KOIN KOIN', + completion=self.completion.help) + self.register_command( + 'join', + self.command.join, + usage="[room_name][@server][/nick] [password]", + desc="Join the specified room. You can specify a nickname " + "after a slash (/). If no nickname is specified, you will" + " use the default_nick in the configuration file. You can" + " omit the room name: you will then join the room you\'re" + " looking at (useful if you were kicked). You can also " + "provide a room_name without specifying a server, the " + "server of the room you're currently in will be used. You" + " can also provide a password to join the room.\nExamples" + ":\n/join room@server.tld\n/join room@server.tld/John\n" + "/join room2\n/join /me_again\n/join\n/join room@server" + ".tld/my_nick password\n/join / password", + shortdesc='Join a room', + completion=self.completion.join) + self.register_command( + 'exit', + self.command.quit, + desc='Just disconnect from the server and exit poezio.', + shortdesc='Exit poezio.') + self.register_command( + 'quit', + self.command.quit, + desc='Just disconnect from the server and exit poezio.', + shortdesc='Exit poezio.') + self.register_command( + 'next', self.rotate_rooms_right, shortdesc='Go to the next room.') + self.register_command( + 'prev', + self.rotate_rooms_left, + shortdesc='Go to the previous room.') + self.register_command( + 'win', + self.command.win, + usage='', + shortdesc='Go to the specified room', + completion=self.completion.win) self.commands['w'] = self.commands['win'] - self.register_command('move_tab', self.command.move_tab, - usage=' ', - desc="Insert the tab at the position of " - ". This will make the following tabs shift in" - " some cases (refer to the documentation). A tab can be " - "designated by its number or by the beginning of its " - "address. You can use \".\" as a shortcut for the current " - "tab.", - shortdesc='Move a tab.', - completion=self.completion.move_tab) - self.register_command('destroy_room', self.command.destroy_room, - usage='[room JID]', - desc='Try to destroy the room [room JID], or the current' - ' tab if it is a multi-user chat and [room JID] is ' - 'not given.', - shortdesc='Destroy a room.', - completion=None) - self.register_command('show', self.command.status, - usage=' [status message]', - desc="Sets your availability and (optionally) your status " - "message. The argument is one of \"available" - ", chat, away, afk, dnd, busy, xa\" and the optional " - "[status message] argument will be your status message.", - shortdesc='Change your availability.', - completion=self.completion.status) + self.register_command( + 'move_tab', + self.command.move_tab, + usage=' ', + desc="Insert the tab at the position of " + ". This will make the following tabs shift in" + " some cases (refer to the documentation). A tab can be " + "designated by its number or by the beginning of its " + "address. You can use \".\" as a shortcut for the current " + "tab.", + shortdesc='Move a tab.', + completion=self.completion.move_tab) + self.register_command( + 'destroy_room', + self.command.destroy_room, + usage='[room JID]', + desc='Try to destroy the room [room JID], or the current' + ' tab if it is a multi-user chat and [room JID] is ' + 'not given.', + shortdesc='Destroy a room.', + completion=None) + self.register_command( + 'show', + self.command.status, + usage=' [status message]', + desc="Sets your availability and (optionally) your status " + "message. The argument is one of \"available" + ", chat, away, afk, dnd, busy, xa\" and the optional " + "[status message] argument will be your status message.", + shortdesc='Change your availability.', + completion=self.completion.status) self.commands['status'] = self.commands['show'] - self.register_command('bookmark_local', self.command.bookmark_local, - usage="[roomname][/nick] [password]", - desc="Bookmark Local: Bookmark locally the specified room " - "(you will then auto-join it on each poezio start). This" - " commands uses almost the same syntaxe as /join. Type " - "/help join for syntax examples. Note that when typing " - "\"/bookmark\" on its own, the room will be bookmarked " - "with the nickname you\'re currently using in this room " - "(instead of default_nick)", - shortdesc='Bookmark a room locally.', - completion=self.completion.bookmark_local) - self.register_command('bookmark', self.command.bookmark, - usage="[roomname][/nick] [autojoin] [password]", - desc="Bookmark: Bookmark online the specified room (you " - "will then auto-join it on each poezio start if autojoin" - " is specified and is 'true'). This commands uses almost" - " the same syntax as /join. Type /help join for syntax " - "examples. Note that when typing \"/bookmark\" alone, the" - " room will be bookmarked with the nickname you\'re " - "currently using in this room (instead of default_nick).", - shortdesc="Bookmark a room online.", - completion=self.completion.bookmark) - self.register_command('set', self.command.set, - usage="[plugin|][section]