80-columns wrapping and some docstrings
also bump version, and add some gettext wraps
This commit is contained in:
parent
b14aceaa4f
commit
3415619895
4 changed files with 165 additions and 65 deletions
27
src/args.py
27
src/args.py
|
@ -3,6 +3,7 @@ Module related to the argument parsing
|
|||
|
||||
There is a fallback to the deprecated optparse if argparse is not found
|
||||
"""
|
||||
from gettext import gettext as _
|
||||
from os import path
|
||||
|
||||
def parse_args(CONFIG_PATH=''):
|
||||
|
@ -15,20 +16,28 @@ def parse_args(CONFIG_PATH=''):
|
|||
from optparse import OptionParser
|
||||
from optparse import SUPPRESS_HELP as SUPPRESS
|
||||
parser = OptionParser()
|
||||
parser.add_option("-f", "--file", dest="filename", default=path.join(CONFIG_PATH, 'poezio.cfg'),
|
||||
help="The config file you want to use", metavar="CONFIG_FILE")
|
||||
parser.add_option("-f", "--file", dest="filename",
|
||||
default=path.join(CONFIG_PATH, 'poezio.cfg'),
|
||||
help=_("The config file you want to use"),
|
||||
metavar="CONFIG_FILE")
|
||||
parser.add_option("-d", "--debug", dest="debug",
|
||||
help="The file where debug will be written", metavar="DEBUG_FILE")
|
||||
help=_("The file where debug will be written"),
|
||||
metavar="DEBUG_FILE")
|
||||
parser.add_option("-v", "--version", dest="version",
|
||||
help=SUPPRESS, metavar="VERSION", default="0.8.3-dev")
|
||||
(options, _) = parser.parse_args()
|
||||
help=SUPPRESS, metavar="VERSION",
|
||||
default="0.8.3-dev")
|
||||
(options, __) = parser.parse_args()
|
||||
else:
|
||||
parser = ArgumentParser()
|
||||
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("-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("-d", "--debug", dest="debug",
|
||||
help="The file where debug will be written", metavar="DEBUG_FILE")
|
||||
help=_("The file where debug will be written"),
|
||||
metavar="DEBUG_FILE")
|
||||
parser.add_argument("-v", "--version", dest="version",
|
||||
help=SUPPRESS, metavar="VERSION", default="0.8-dev")
|
||||
help=SUPPRESS, metavar="VERSION",
|
||||
default="0.8.3-dev")
|
||||
options = parser.parse_args()
|
||||
return options
|
||||
|
|
|
@ -29,9 +29,10 @@ class Connection(sleekxmpp.ClientXMPP):
|
|||
def __init__(self):
|
||||
resource = config.get('resource', '')
|
||||
if config.get('jid', ''):
|
||||
self.anon = False # Field used to know if we are anonymous or not.
|
||||
# many features will be handled diferently
|
||||
# Field used to know if we are anonymous or not.
|
||||
# many features will be handled differently
|
||||
# depending on this setting
|
||||
self.anon = False
|
||||
jid = '%s' % config.get('jid', '')
|
||||
if resource:
|
||||
jid = '%s/%s'% (jid, resource)
|
||||
|
@ -44,7 +45,8 @@ class Connection(sleekxmpp.ClientXMPP):
|
|||
password = None
|
||||
jid = safeJID(jid)
|
||||
# TODO: use the system language
|
||||
sleekxmpp.ClientXMPP.__init__(self, jid, password, lang=config.get('lang', 'en'))
|
||||
sleekxmpp.ClientXMPP.__init__(self, jid, password,
|
||||
lang=config.get('lang', 'en'))
|
||||
|
||||
force_encryption = config.get('force_encryption', True)
|
||||
if force_encryption:
|
||||
|
@ -59,7 +61,9 @@ class Connection(sleekxmpp.ClientXMPP):
|
|||
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', '300')
|
||||
if interval.isdecimal() and int(interval) > 0:
|
||||
|
@ -138,12 +142,15 @@ class Connection(sleekxmpp.ClientXMPP):
|
|||
self.plugin['xep_0199'].disable_keepalive()
|
||||
# If the ping_interval is 0 or less, we just disable the keepalive
|
||||
if ping_interval > 0:
|
||||
self.plugin['xep_0199'].enable_keepalive(ping_interval, timeout_delay)
|
||||
self.plugin['xep_0199'].enable_keepalive(ping_interval,
|
||||
timeout_delay)
|
||||
|
||||
def start(self):
|
||||
# TODO, try multiple servers
|
||||
# With anon auth.
|
||||
# (domain, config.get('port', 5222))
|
||||
"""
|
||||
Connect and process events.
|
||||
|
||||
TODO: try multiple servers with anon auth.
|
||||
"""
|
||||
custom_host = config.get('custom_host', '')
|
||||
custom_port = config.get('custom_port', 5222)
|
||||
if custom_port == -1:
|
||||
|
@ -151,7 +158,8 @@ class Connection(sleekxmpp.ClientXMPP):
|
|||
if custom_host:
|
||||
res = self.connect((custom_host, custom_port), reattempt=True)
|
||||
elif custom_port != 5222 and custom_port != -1:
|
||||
res = self.connect((self.boundjid.host, custom_port), reattempt=True)
|
||||
res = self.connect((self.boundjid.host, custom_port),
|
||||
reattempt=True)
|
||||
else:
|
||||
res = self.connect(reattempt=True)
|
||||
if not res:
|
||||
|
@ -172,4 +180,5 @@ class MatchAll(sleekxmpp.xmlstream.matcher.base.MatcherBase):
|
|||
Callback to retrieve all the stanzas for the XML tab
|
||||
"""
|
||||
def match(self, xml):
|
||||
"match everything"
|
||||
return True
|
||||
|
|
|
@ -27,13 +27,21 @@ class PluginManager(object):
|
|||
"""
|
||||
def __init__(self, core):
|
||||
self.core = core
|
||||
self.modules = {} # module name -> module object
|
||||
self.plugins = {} # module name -> plugin object
|
||||
self.commands = {} # module name -> dict of commands loaded for the module
|
||||
self.event_handlers = {} # module name -> list of event_name/handler pairs loaded for the module
|
||||
self.tab_commands = {} #module name -> dict of tab types; tab type -> commands loaded by the module
|
||||
self.keys = {} # module name → dict of keys/handlers loaded for the module
|
||||
self.tab_keys = {} #module name → dict of tab types; tab type → list of keybinds (tuples)
|
||||
# module name -> module object
|
||||
self.modules = {}
|
||||
# module name -> plugin object
|
||||
self.plugins = {}
|
||||
# module name -> dict of commands loaded for the module
|
||||
self.commands = {}
|
||||
# module name -> list of event_name/handler pairs loaded for the module
|
||||
self.event_handlers = {}
|
||||
# module name -> dict of tab types; tab type -> commands
|
||||
# loaded by the module
|
||||
self.tab_commands = {}
|
||||
# module name → dict of keys/handlers loaded for the module
|
||||
self.keys = {}
|
||||
# module name → dict of tab types; tab type → list of keybinds (tuples)
|
||||
self.tab_keys = {}
|
||||
self.roster_elements = {}
|
||||
|
||||
if version_info[1] >= 3: # 3.3 & >
|
||||
|
@ -67,7 +75,8 @@ class PluginManager(object):
|
|||
imp.acquire_lock()
|
||||
module = imp.reload(self.modules[name])
|
||||
else:
|
||||
file, filename, info = imp.find_module(name, self.load_path)
|
||||
file, filename, info = imp.find_module(name,
|
||||
self.load_path)
|
||||
imp.acquire_lock()
|
||||
module = imp.load_module(name, file, filename, info)
|
||||
else: # 3.3 & >
|
||||
|
@ -79,7 +88,8 @@ class PluginManager(object):
|
|||
|
||||
except Exception as e:
|
||||
log.debug("Could not load plugin %s", name, exc_info=True)
|
||||
self.core.information("Could not load plugin %s: %s" % (name, e), 'Error')
|
||||
self.core.information("Could not load plugin %s: %s" % (name, e),
|
||||
'Error')
|
||||
finally:
|
||||
if version_info[1] < 3 and imp.lock_held():
|
||||
imp.release_lock()
|
||||
|
@ -94,11 +104,14 @@ class PluginManager(object):
|
|||
self.event_handlers[name] = []
|
||||
try:
|
||||
self.plugins[name] = None
|
||||
self.plugins[name] = module.Plugin(self.plugin_api, self.core, self.plugins_conf_dir)
|
||||
self.plugins[name] = module.Plugin(self.plugin_api, self.core,
|
||||
self.plugins_conf_dir)
|
||||
except Exception as e:
|
||||
log.error('Error while loading the plugin %s', name, exc_info=True)
|
||||
if notify:
|
||||
self.core.information('Unable to load the plugin %s: %s' % (name, e), 'Error')
|
||||
self.core.information(_('Unable to load the plugin %s: %s') %
|
||||
(name, e),
|
||||
'Error')
|
||||
self.unload(name, notify=False)
|
||||
else:
|
||||
if notify:
|
||||
|
@ -113,7 +126,8 @@ class PluginManager(object):
|
|||
del self.core.key_func[key]
|
||||
for tab in list(self.tab_commands[name].keys()):
|
||||
for command in self.tab_commands[name][tab][:]:
|
||||
self.del_tab_command(name, getattr(tabs, tab), command[0])
|
||||
self.del_tab_command(name, getattr(tabs, tab),
|
||||
command[0])
|
||||
del self.tab_commands[name][tab]
|
||||
for tab in list(self.tab_keys[name].keys()):
|
||||
for key in self.tab_keys[name][tab][:]:
|
||||
|
@ -133,9 +147,12 @@ class PluginManager(object):
|
|||
self.core.information('Plugin %s unloaded' % name, 'Info')
|
||||
except Exception as e:
|
||||
log.debug("Could not unload plugin %s", name, exc_info=True)
|
||||
self.core.information("Could not unload plugin %s: %s" % (name, e), 'Error')
|
||||
self.core.information(_("Could not unload plugin %s: %s") %
|
||||
(name, e),
|
||||
'Error')
|
||||
|
||||
def add_command(self, module_name, name, handler, help, completion=None, short='', usage=''):
|
||||
def add_command(self, module_name, name, handler, help,
|
||||
completion=None, short='', usage=''):
|
||||
"""
|
||||
Add a global command.
|
||||
"""
|
||||
|
@ -155,7 +172,8 @@ class PluginManager(object):
|
|||
if name in self.core.commands:
|
||||
del self.core.commands[name]
|
||||
|
||||
def add_tab_command(self, module_name, tab_type, name, handler, help, completion=None, short='', usage=''):
|
||||
def add_tab_command(self, module_name, tab_type, name, handler, help,
|
||||
completion=None, short='', usage=''):
|
||||
"""
|
||||
Add a command only for a type of Tab.
|
||||
"""
|
||||
|
@ -166,7 +184,8 @@ class PluginManager(object):
|
|||
if not t in commands:
|
||||
commands[t] = []
|
||||
commands[t].append((name, handler, help, completion))
|
||||
tab_type.plugin_commands[name] = core.Command(handler, help, completion, short, usage)
|
||||
tab_type.plugin_commands[name] = core.Command(handler, help,
|
||||
completion, short, usage)
|
||||
for tab in self.core.tabs:
|
||||
if isinstance(tab, tab_type):
|
||||
tab.update_commands()
|
||||
|
@ -282,14 +301,16 @@ class PluginManager(object):
|
|||
and name != '__init__.py' and not name.startswith('.')]
|
||||
plugins_files.sort()
|
||||
position = the_input.get_argument_position(quoted=False)
|
||||
return the_input.new_completion(plugins_files, position, '', quotify=False)
|
||||
return the_input.new_completion(plugins_files, position, '',
|
||||
quotify=False)
|
||||
|
||||
def completion_unload(self, the_input):
|
||||
"""
|
||||
completion function that completes the name of the plugins that are loaded
|
||||
completion function that completes the name of loaded plugins
|
||||
"""
|
||||
position = the_input.get_argument_position(quoted=False)
|
||||
return the_input.new_completion(sorted(self.plugins.keys()), position, '', quotify=False)
|
||||
return the_input.new_completion(sorted(self.plugins.keys()), position,
|
||||
'', quotify=False)
|
||||
|
||||
def on_plugins_dir_change(self, new_value):
|
||||
self.plugins_dir = new_value
|
||||
|
@ -334,7 +355,8 @@ class PluginManager(object):
|
|||
plugins_dir = config.get('plugins_dir', '')
|
||||
plugins_dir = plugins_dir or\
|
||||
os.path.join(os.environ.get('XDG_DATA_HOME') or\
|
||||
os.path.join(os.environ.get('HOME'), '.local', 'share'),
|
||||
os.path.join(os.environ.get('HOME'),
|
||||
'.local', 'share'),
|
||||
'poezio', 'plugins')
|
||||
self.plugins_dir = os.path.expanduser(plugins_dir)
|
||||
self.check_create_plugins_dir()
|
||||
|
@ -360,7 +382,8 @@ class PluginManager(object):
|
|||
|
||||
self.load_path = []
|
||||
|
||||
default_plugin_path = path.join(path.dirname(path.dirname(__file__)), 'plugins')
|
||||
default_plugin_path = path.join(path.dirname(path.dirname(__file__)),
|
||||
'plugins')
|
||||
|
||||
if os.access(default_plugin_path, os.R_OK | os.X_OK):
|
||||
self.load_path.insert(0, default_plugin_path)
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
# Copyright 2010-2011 Florent Le Coz <louiz@louiz.org>
|
||||
#
|
||||
# This file is part of Poezio.
|
||||
#
|
||||
# Poezio is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the zlib license. See the COPYING file.
|
||||
|
||||
"""
|
||||
Define the TextBuffer class
|
||||
|
||||
A text buffer contains a list of intermediate representations of messages
|
||||
(not xml stanzas, but neither the Lines used in windows.py.
|
||||
|
||||
Each text buffer can be linked to multiple windows, that will be rendered
|
||||
independantly by their TextWins.
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
@ -18,13 +17,15 @@ from datetime import datetime
|
|||
from config import config
|
||||
from theming import get_theme, dump_tuple
|
||||
|
||||
message_fields = 'txt nick_color time str_time nickname user identifier highlight me old_message revisions jid'
|
||||
message_fields = ('txt nick_color time str_time nickname user identifier'
|
||||
' highlight me old_message revisions jid')
|
||||
Message = collections.namedtuple('Message', message_fields)
|
||||
|
||||
class CorrectionError(Exception):
|
||||
pass
|
||||
|
||||
def other_elems(self):
|
||||
"Helper for the repr_message function"
|
||||
acc = ['Message(']
|
||||
fields = message_fields.split()
|
||||
fields.remove('old_message')
|
||||
|
@ -33,6 +34,11 @@ def other_elems(self):
|
|||
return ', '.join(acc) + ', old_message='
|
||||
|
||||
def repr_message(self):
|
||||
"""
|
||||
repr() for the Message class, for debug purposes, since the default
|
||||
repr() is recursive, so it can stack overflow given too many revisions
|
||||
of a message
|
||||
"""
|
||||
init = other_elems(self)
|
||||
acc = [init]
|
||||
next_message = self.old_message
|
||||
|
@ -55,12 +61,17 @@ class TextBuffer(object):
|
|||
This class just keep trace of messages, in a list with various
|
||||
informations and attributes.
|
||||
"""
|
||||
def __init__(self, messages_nb_limit=config.get('max_messages_in_memory', 2048)):
|
||||
def __init__(self, messages_nb_limit=None):
|
||||
|
||||
if messages_nb_limit is None:
|
||||
messages_nb_limit = config.get('max_messages_in_memory', 2048)
|
||||
self.messages_nb_limit = messages_nb_limit
|
||||
self.messages = [] # Message objects
|
||||
self.windows = [] # we keep track of one or more windows
|
||||
# Message objects
|
||||
self.messages = []
|
||||
# we keep track of one or more windows
|
||||
# so we can pass the new messages to them, as they are added, so
|
||||
# they (the windows) can build the lines from the new message
|
||||
self.windows = []
|
||||
|
||||
def add_window(self, win):
|
||||
self.windows.append(win)
|
||||
|
@ -71,19 +82,35 @@ class TextBuffer(object):
|
|||
|
||||
|
||||
@staticmethod
|
||||
def make_message(txt, time, nickname, nick_color, history, user, identifier, str_time=None, highlight=False, old_message=None, revisions=0, jid=None):
|
||||
def make_message(txt, time, nickname, nick_color, history, user,
|
||||
identifier, str_time=None, highlight=False,
|
||||
old_message=None, revisions=0, jid=None):
|
||||
"""
|
||||
Create a new Message object with parameters, check for /me messages,
|
||||
and delayed messages
|
||||
"""
|
||||
time = time or datetime.now()
|
||||
me = False
|
||||
if txt.startswith('/me '):
|
||||
me = True
|
||||
txt = '\x19%(info_col)s}' % {'info_col': get_theme().COLOR_ME_MESSAGE[0]} + txt[4:]
|
||||
txt = '\x19%s}%s' % (dump_tuple(get_theme().COLOR_ME_MESSAGE),
|
||||
txt[4:])
|
||||
else:
|
||||
me = False
|
||||
if history:
|
||||
txt = txt.replace('\x19o', '\x19o\x19%s}' % dump_tuple(get_theme().COLOR_LOG_MSG))
|
||||
txt = txt.replace('\x19o', '\x19o\x19%s}' %
|
||||
dump_tuple(get_theme().COLOR_LOG_MSG))
|
||||
str_time = time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
else:
|
||||
if str_time is None:
|
||||
str_time = time.strftime("%H:%M:%S")
|
||||
else:
|
||||
str_time = ''
|
||||
|
||||
msg = Message(
|
||||
txt='%s\x19o'%(txt.replace('\t', ' '),),
|
||||
nick_color=nick_color,
|
||||
time=time,
|
||||
str_time=(time.strftime("%Y-%m-%d %H:%M:%S") if history else time.strftime("%H:%M:%S")) if str_time is None else '',
|
||||
str_time=str_time,
|
||||
nickname=nickname,
|
||||
user=user,
|
||||
identifier=identifier,
|
||||
|
@ -95,42 +122,74 @@ class TextBuffer(object):
|
|||
log.debug('Set message %s with %s.', identifier, msg)
|
||||
return msg
|
||||
|
||||
def add_message(self, txt, time=None, nickname=None, nick_color=None, history=None, user=None, highlight=False, identifier=None, str_time=None, jid=None):
|
||||
msg = self.make_message(txt, time, nickname, nick_color, history, user, identifier, str_time=str_time, highlight=highlight, jid=jid)
|
||||
def add_message(self, txt, time=None, nickname=None,
|
||||
nick_color=None, history=None, user=None, highlight=False,
|
||||
identifier=None, str_time=None, jid=None):
|
||||
"""
|
||||
Create a message and add it to the text buffer
|
||||
"""
|
||||
msg = self.make_message(txt, time, nickname, nick_color, history,
|
||||
user, identifier, str_time=str_time,
|
||||
highlight=highlight, jid=jid)
|
||||
self.messages.append(msg)
|
||||
|
||||
while len(self.messages) > self.messages_nb_limit:
|
||||
self.messages.pop(0)
|
||||
|
||||
ret_val = None
|
||||
show_timestamps = config.get('show_timestamps', True)
|
||||
for window in self.windows: # make the associated windows
|
||||
# build the lines from the new message
|
||||
nb = window.build_new_message(msg, history=history, highlight=highlight, timestamp=config.get("show_timestamps", True))
|
||||
# build the lines from the new message
|
||||
nb = window.build_new_message(msg, history=history,
|
||||
highlight=highlight,
|
||||
timestamp=show_timestamps)
|
||||
if ret_val is None:
|
||||
ret_val = nb
|
||||
if window.pos != 0:
|
||||
window.scroll_up(nb)
|
||||
|
||||
return ret_val or 1
|
||||
|
||||
def modify_message(self, txt, old_id, new_id, highlight=False, time=None, user=None, jid=None):
|
||||
def modify_message(self, txt, old_id, new_id, highlight=False,
|
||||
time=None, user=None, jid=None):
|
||||
"""
|
||||
Correct a message in a text buffer.
|
||||
"""
|
||||
|
||||
for i in range(len(self.messages) -1, -1, -1):
|
||||
msg = self.messages[i]
|
||||
|
||||
if msg.identifier == old_id:
|
||||
if msg.user and msg.user is not user:
|
||||
raise CorrectionError("Different users")
|
||||
elif len(msg.str_time) > 8: # ugly
|
||||
raise CorrectionError("Delayed message")
|
||||
elif not msg.user and (msg.jid is None or jid is None):
|
||||
raise CorrectionError('Could not check the identity of the sender')
|
||||
raise CorrectionError('Could not check the '
|
||||
'identity of the sender')
|
||||
elif not msg.user and msg.jid != jid:
|
||||
raise CorrectionError('Messages %s and %s have not been sent by the same fullJID' % (old_id, new_id))
|
||||
message = self.make_message(txt, time if time else msg.time, msg.nickname, msg.nick_color, None, msg.user, new_id, highlight=highlight, old_message=msg, revisions=msg.revisions + 1, jid=jid)
|
||||
raise CorrectionError('Messages %s and %s have not been '
|
||||
'sent by the same fullJID' %
|
||||
(old_id, new_id))
|
||||
|
||||
if not time:
|
||||
time = msg.time
|
||||
message = self.make_message(txt, time, msg.nickname,
|
||||
msg.nick_color, None, msg.user,
|
||||
new_id, highlight=highlight,
|
||||
old_message=msg,
|
||||
revisions=msg.revisions + 1,
|
||||
jid=jid)
|
||||
self.messages[i] = message
|
||||
log.debug('Replacing message %s with %s.', old_id, new_id)
|
||||
return message
|
||||
log.debug('Message %s not found in text_buffer, abort replacement.', old_id)
|
||||
log.debug('Message %s not found in text_buffer, abort replacement.',
|
||||
old_id)
|
||||
raise CorrectionError("nothing to replace")
|
||||
|
||||
def del_window(self, win):
|
||||
self.windows.remove(win)
|
||||
|
||||
def __del__(self):
|
||||
log.debug('** Deleting %s messages from textbuffer', len(self.messages))
|
||||
size = len(self.messages)
|
||||
log.debug('** Deleting %s messages from textbuffer', size)
|
||||
|
|
Loading…
Reference in a new issue