From 91b960b797bbf17f6c4b33279f2d406c5a2c93b2 Mon Sep 17 00:00:00 2001 From: mathieui Date: Fri, 5 Apr 2013 23:57:53 +0200 Subject: Handle I/O errors better MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Do not crash because of low disk space - Notify the user whenever it happens - A few functions now return a boolean instead of nothing - Config.silent_set is Config.set_and_save without toggle and returning strings. It is used whenever we don’t need set_and_save - Config.set_and_save now returns a tuple (that can be passed directly to core.information()) TODO: display the precise error to the user (instead of “unable to…”) --- src/config.py | 33 ++++++++++++++++++++++++++------- src/core.py | 36 +++++++++++++++++++++++------------- src/logger.py | 27 +++++++++++++++++++-------- src/roster.py | 2 +- src/tabs.py | 18 ++++++++++++------ 5 files changed, 81 insertions(+), 35 deletions(-) (limited to 'src') diff --git a/src/config.py b/src/config.py index e3c93f29..c94bb89a 100644 --- a/src/config.py +++ b/src/config.py @@ -12,6 +12,8 @@ from/to the config file DEFSECTION = "Poezio" +from gettext import gettext as _ + from configparser import RawConfigParser, NoOptionError, NoSectionError from os import environ, makedirs, path from shutil import copy2 @@ -171,10 +173,16 @@ class Config(RawConfigParser): result_lines.append('%s = %s' % (option, value)) - df = open(self.file_name, 'w', encoding='utf-8') - for line in result_lines: - df.write('%s\n' % line) - df.close() + try: + df = open(self.file_name, 'w', encoding='utf-8') + for line in result_lines: + df.write('%s\n' % line) + df.close() + except: + success = False + else: + success = True + return success def set_and_save(self, option, value, section=DEFSECTION): """ @@ -191,15 +199,26 @@ class Config(RawConfigParser): elif current.lower() == "true": value = "false" else: - return "Could not toggle option: %s. Current value is %s." % (option, current or "empty") + return (_("Could not toggle option: %s. Current value is %s.") % (option, current or _("empty")), 'Warning') if self.has_section(section): RawConfigParser.set(self, section, option, value) else: self.add_section(section) RawConfigParser.set(self, section, option, value) - self.write_in_file(section, option, value) - return "%s=%s" % (option, value) + if not self.write_in_file(section, option, value): + return (_('Unable to write in the config file'), 'Error') + return ("%s=%s" % (option, value), 'Info') + def silent_set(self, option, value, section=DEFSECTION): + """ + Set a value, save, and return True on success and False on failure + """ + if self.has_section(section): + RawConfigParser.set(self, section, option, value) + else: + self.add_section(section) + RawConfigParser.set(self, section, option, value) + return self.write_in_file(section, option, value) def set(self, option, value, section=DEFSECTION): """ diff --git a/src/core.py b/src/core.py index 97470b48..68c7f241 100644 --- a/src/core.py +++ b/src/core.py @@ -432,8 +432,9 @@ class Core(object): """ Save config in the file just before exit """ - roster.save_to_config_file() - config.set_and_save('info_win_height', self.information_win_size, 'var') + if not roster.save_to_config_file() or \ + not config.silent_set('info_win_height', self.information_win_size, 'var'): + self.information(_('Unable to write in the config file'), 'Error') def on_roster_enter_key(self, roster_row): """ @@ -595,8 +596,9 @@ class Core(object): """ self.status = Status(show=pres, message=msg) if config.get('save_status', 'true').lower() != 'false': - config.set_and_save('status', pres if pres else '') - config.set_and_save('status_message', msg.replace('\n', '|') if msg else '') + if not config.silent_set('status', pres if pres else '') or \ + not config.silent_set('status_message', msg.replace('\n', '|') if msg else ''): + self.information(_('Unable to write in the config file'), 'Error') def get_bookmark_nickname(self, room_name): """ @@ -1238,7 +1240,8 @@ class Core(object): Enable/disable the left panel. """ enabled = config.get('enable_vertical_tab_list', 'false') - config.set_and_save('enable_vertical_tab_list', 'false' if enabled == 'true' else 'true') + if not config.silent_set('enable_vertical_tab_list', 'false' if enabled == 'true' else 'true'): + self.information(_('Unable to write in the config file'), 'Error') self.call_for_resize() def resize_global_information_win(self): @@ -2051,7 +2054,7 @@ class Core(object): path = os.path.expanduser(value) self.plugin_manager.on_plugins_conf_dir_change(path) self.call_for_resize() - self.information(info, "Info") + self.information(*info) def completion_set(self, the_input): """Completion for /set""" @@ -2336,7 +2339,8 @@ class Core(object): return self.command_help('bind') elif len(args) < 2: args.append("") - config.set_and_save(args[0], args[1], section='bindings') + if not config.silent_set(args[0], args[1], section='bindings'): + self.information(_('Unable to write in the config file'), 'Error') if args[1]: self.information('%s is now bound to %s' % (args[0], args[1]), 'Info') else: @@ -2716,7 +2720,8 @@ class Core(object): conversation.remote_wants_chatstates = True else: conversation.remote_wants_chatstates = False - logger.log_message(jid.bare, remote_nick, body) + if not logger.log_message(jid.bare, remote_nick, body): + self.information(_('Unable to write in the log file'), 'Error') if 'private' in config.get('beep_on', 'highlight private').split(): if config.get_by_tabname('disable_beep', 'false', jid.bare, False).lower() != 'true': curses.beep() @@ -2967,7 +2972,8 @@ class Core(object): if 'private' in config.get('beep_on', 'highlight private').split(): if config.get_by_tabname('disable_beep', 'false', jid.full, False).lower() != 'true': curses.beep() - logger.log_message(jid.full.replace('/', '\\'), nick_from, body) + if not logger.log_message(jid.full.replace('/', '\\'), nick_from, body): + self.information(_('Unable to write in the log file'), 'Error') if tab is self.current_tab(): self.refresh_window() else: @@ -3147,7 +3153,8 @@ class Core(object): if presence.match('presence/muc') or presence.xml.find('{http://jabber.org/protocol/muc#user}x'): return jid = presence['from'] - logger.log_roster_change(jid.bare, 'got offline') + if not logger.log_roster_change(jid.bare, 'got offline'): + self.information(_('Unable to write in the log file'), 'Error') # If a resource got offline, display the message in the conversation with this # precise resource. if jid.resource: @@ -3168,7 +3175,8 @@ class Core(object): if contact is None: # Todo, handle presence coming from contacts not in roster return - logger.log_roster_change(jid.bare, 'got online') + if not logger.log_roster_change(jid.bare, 'got online'): + self.information(_('Unable to write in the log file'), 'Error') resource = Resource(jid.full, { 'priority': presence.get_priority() or 0, 'status': presence['status'], @@ -3430,13 +3438,15 @@ class Core(object): if input.value: self.information('Setting new certificate: old: %s, new: %s' % (cert, found_cert), 'Info') log.debug('Setting certificate to %s', found_cert) - config.set_and_save('certificate', found_cert) + if not config.silent_set('certificate', found_cert): + self.information(_('Unable to write in the config file'), 'Error') else: self.information('You refused to validate the certificate. You are now disconnected', 'Info') self.xmpp.disconnect() else: log.debug('First time. Setting certificate to %s', found_cert) - config.set_and_save('certificate', found_cert) + if not config.silent_set('certificate', found_cert): + self.information(_('Unable to write in the config file'), 'Error') diff --git a/src/logger.py b/src/logger.py index 8b4b1d3c..a696137f 100644 --- a/src/logger.py +++ b/src/logger.py @@ -32,7 +32,10 @@ class Logger(object): def __del__(self): for opened_file in self.fds.values(): if opened_file: - opened_file.close() + try: + opened_file.close() + except: # Can't close? too bad + pass def reload_all(self): """Close and reload all the file handles (on SIGHUP)""" @@ -106,7 +109,7 @@ class Logger(object): else: fd = self.check_and_create_log_dir(jid) if not fd: - return + return True try: msg = clean_text(msg) if date is None: @@ -117,18 +120,26 @@ class Logger(object): fd.write(''.join((str_time, nick, ': ', msg, '\n'))) else: fd.write(''.join((str_time, '* ', msg, '\n'))) - except IOError: - pass + except: + return False else: - fd.flush() # TODO do something better here? + try: + fd.flush() # TODO do something better here? + except: + return False + return True def log_roster_change(self, jid, message): if not self.roster_logfile: try: self.roster_logfile = open(os.path.join(DATA_HOME, 'logs', 'roster.log'), 'a') except IOError: - return - self.roster_logfile.write('%s %s %s\n' % (datetime.now().strftime('%d-%m-%y [%H:%M:%S]'), jid, message)) - self.roster_logfile.flush() + return False + try: + self.roster_logfile.write('%s %s %s\n' % (datetime.now().strftime('%d-%m-%y [%H:%M:%S]'), jid, message)) + self.roster_logfile.flush() + except: + return False + return True logger = Logger() diff --git a/src/roster.py b/src/roster.py index 67e81da9..b0f3cf65 100644 --- a/src/roster.py +++ b/src/roster.py @@ -141,7 +141,7 @@ class Roster(object): folded_groups = ':'.join([group.name for group in self.groups.values()\ if group.folded]) log.debug('folded:%s\n' %folded_groups) - config.set_and_save('folded_roster_groups', folded_groups, 'var') + return config.silent_set('folded_roster_groups', folded_groups, 'var') def get_nb_connected_contacts(self): """ diff --git a/src/tabs.py b/src/tabs.py index bbae6e3d..ef31da67 100644 --- a/src/tabs.py +++ b/src/tabs.py @@ -481,7 +481,8 @@ class ChatTab(Tab): """ Log the messages in the archives. """ - logger.log_message(self.name, nickname, txt, date=time) + if not logger.log_message(self.name, nickname, txt, date=time): + self.core.information(_('Unable to write in the log file'), 'Error') def add_message(self, txt, time=None, nickname=None, forced_user=None, nick_color=None, identifier=None): self._text_buffer.add_message(txt, time=time, @@ -1696,7 +1697,8 @@ class MucTab(ChatTab): to be """ if time is None and self.joined: # don't log the history messages - logger.log_message(self.name, nickname, txt) + if not logger.log_message(self.name, nickname, txt): + self.core.information(_('Unable to write in the log file'), 'Error') def do_highlight(self, txt, time, nickname): """ @@ -1844,7 +1846,8 @@ class PrivateTab(ChatTab): msg = self.core.xmpp.make_message(self.get_name()) msg['type'] = 'chat' msg['body'] = line - logger.log_message(self.get_name().replace('/', '\\'), self.own_nick, line) + if not logger.log_message(self.get_name().replace('/', '\\'), self.own_nick, line): + self.core.information(_('Unable to write in the log file'), 'Error') # trigger the event BEFORE looking for colors. # This lets a plugin insert \x19xxx} colors, that will # be converted in xhtml. @@ -2722,9 +2725,11 @@ class RosterInfoTab(Tab): """ option = 'roster_show_offline' if config.get(option, 'false') == 'false': - config.set_and_save(option, 'true') + success = config.silent_set(option, 'true') else: - config.set_and_save(option, 'false') + success = config.silent_set(option, 'false') + if not success: + self.information(_('Unable to write in the config file'), 'Error') return True def on_slash(self): @@ -3036,7 +3041,8 @@ class ConversationTab(ChatTab): self.core.events.trigger('conversation_say_after', msg, self) self.last_sent_message = msg msg.send() - logger.log_message(safeJID(self.get_dest_jid()).bare, self.core.own_nick, line) + if not logger.log_message(safeJID(self.get_dest_jid()).bare, self.core.own_nick, line): + self.core.information(_('Unable to write in the log file'), 'Error') self.cancel_paused_delay() self.text_win.refresh() self.input.refresh() -- cgit v1.2.3