Commit 34156198 authored by mathieui's avatar mathieui

80-columns wrapping and some docstrings

also bump version, and add some gettext wraps
parent b14aceaa
......@@ -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)
......
# 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))
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)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment