fixed #1988 Traceback handler

parent 133cda19
...@@ -26,6 +26,7 @@ import sys ...@@ -26,6 +26,7 @@ import sys
import shlex import shlex
import curses import curses
import threading import threading
import traceback
from datetime import datetime from datetime import datetime
...@@ -79,20 +80,22 @@ class Core(object): ...@@ -79,20 +80,22 @@ class Core(object):
User interface using ncurses User interface using ncurses
""" """
def __init__(self, xmpp): def __init__(self, xmpp):
# All uncaught exception are given to this callback, instead
# of being displayed on the screen and exiting the program.
sys.excepthook = self.on_exception
self.running = True self.running = True
self.stdscr = curses.initscr() self.stdscr = curses.initscr()
self.init_curses(self.stdscr) self.init_curses(self.stdscr)
self.xmpp = xmpp self.xmpp = xmpp
# a unique buffer used to store global informations
# that are displayed in almost all tabs, in an
# information window.
self.information_buffer = TextBuffer() self.information_buffer = TextBuffer()
self.information_win_size = config.get('info_win_height', 2, 'var') self.information_win_size = config.get('info_win_height', 2, 'var')
default_tab = tabs.InfoTab(self, "Info") if self.xmpp.anon\ default_tab = tabs.InfoTab(self, "Info") if self.xmpp.anon\
else tabs.RosterInfoTab(self) else tabs.RosterInfoTab(self)
default_tab.on_gain_focus() default_tab.on_gain_focus()
self.tabs = [default_tab] self.tabs = [default_tab]
# a unique buffer used to store global informations
# that are displayed in almost all tabs, in an
# information window.
self.resize_timer = None self.resize_timer = None
self.previous_tab_nb = 0 self.previous_tab_nb = 0
self.own_nick = config.get('own_nick', self.xmpp.boundjid.bare) self.own_nick = config.get('own_nick', self.xmpp.boundjid.bare)
...@@ -155,6 +158,20 @@ class Core(object): ...@@ -155,6 +158,20 @@ class Core(object):
self.xmpp.add_event_handler("roster_update", self.on_roster_update) self.xmpp.add_event_handler("roster_update", self.on_roster_update)
self.xmpp.add_event_handler("changed_status", self.on_presence) self.xmpp.add_event_handler("changed_status", self.on_presence)
def on_exception(self, typ, value, trace):
"""
When an exception in raised, open a special tab
displaying the traceback and some instructions to
make a bug report.
"""
try:
tb_tab = tabs.SimpleTextTab(self, "/!\ Oups, an error occured (this may not be fatal). /!\\\n\nPlease report this bug (by copying the present error message and explaining what you were doing) on the page http://codingteam.net/project/poezio/bugs/add\n\n%s\n\nIf Poezio does not respond anymore, kill it with Ctrl+\\, and sorry about that :(" % ''.join(traceback.format_exception(typ, value, trace)))
self.add_tab(tb_tab, focus=True)
except Exception: # If an exception is raised in this code, this is
# this is fatal, so we exit cleanly and display the traceback
curses.endwin()
raise
def grow_information_win(self): def grow_information_win(self):
""" """
""" """
......
...@@ -20,70 +20,9 @@ ...@@ -20,70 +20,9 @@
Starting point of poezio. Launches both the Connection and Gui Starting point of poezio. Launches both the Connection and Gui
""" """
import os
import curses
import sys import sys
import traceback import os
import threading
sys.path.append(os.path.dirname(os.path.abspath(__file__))) sys.path.append(os.path.dirname(os.path.abspath(__file__)))
def installThreadExcepthook():
"""
Workaround for sys.excepthook thread bug
See http://bugs.python.org/issue1230540
Python, you made me sad :(
"""
init_old = threading.Thread.__init__
def init(self, *args, **kwargs):
init_old(self, *args, **kwargs)
run_old = self.run
def run_with_except_hook(*args, **kw):
try:
run_old(*args, **kw)
except (KeyboardInterrupt, SystemExit):
raise
except:
sys.excepthook(*sys.exc_info())
self.run = run_with_except_hook
threading.Thread.__init__ = init
class MyStdErr(object):
def __init__(self, fd):
"""
Change sys.stderr to something like /dev/null
to disable any printout on the screen that would
mess everything
"""
self.old_stderr = sys.stderr
sys.stderr = fd
def restore(self):
"""
Restore the good ol' sys.stderr, because we need
it in order to print the tracebacks
"""
sys.stderr.close()
sys.stderr = self.old_stderr
# my_stderr = MyStdErr(open('/dev/null', 'a'))
def exception_handler(type_, value, trace):
"""
on any traceback: exit ncurses and print the traceback
then exit the program
"""
my_stderr.restore()
try:
curses.endwin()
curses.echo()
except: # if an exception is raised but initscr has never been called yet
pass
traceback.print_exception(type_, value, trace, None, sys.stderr)
import os # used to quit the program even from a thread
os.abort()
# sys.excepthook = exception_handler
import signal import signal
import logging import logging
...@@ -96,4 +35,7 @@ if __name__ == '__main__': ...@@ -96,4 +35,7 @@ if __name__ == '__main__':
if options.debug: if options.debug:
logging.basicConfig(filename=options.debug,level=logging.DEBUG) logging.basicConfig(filename=options.debug,level=logging.DEBUG)
connection.start() # Connect to remote server connection.start() # Connect to remote server
# Disable any display of non-wanted text on the terminal
# by redirecting stderr to /dev/null
# sys.stderr = open('/dev/null', 'a')
core.main_loop() # Refresh the screen, wait for user events etc core.main_loop() # Refresh the screen, wait for user events etc
...@@ -110,19 +110,19 @@ class Tab(object): ...@@ -110,19 +110,19 @@ class Tab(object):
""" """
returns the color that should be used in the GlobalInfoBar returns the color that should be used in the GlobalInfoBar
""" """
raise NotImplementedError return theme.COLOR_TAB_NORMAL
def set_color_state(self, color): def set_color_state(self, color):
""" """
set the color state set the color state
""" """
raise NotImplementedError pass
def get_name(self): def get_name(self):
""" """
get the name of the tab get the name of the tab
""" """
raise NotImplementedError return self.__class__.__name__
def get_text_window(self): def get_text_window(self):
""" """
...@@ -1039,14 +1039,51 @@ class MucListTab(Tab): ...@@ -1039,14 +1039,51 @@ class MucListTab(Tab):
self.core.execute(txt) self.core.execute(txt)
return self.reset_help_message() return self.reset_help_message()
def get_name(self):
return self.name
def on_input(self, key):
res = self.input.do_command(key)
if res:
return True
if key in self.key_func:
return self.key_func[key]()
def on_lose_focus(self):
self._color_state = theme.COLOR_TAB_NORMAL
def on_gain_focus(self):
self._color_state = theme.COLOR_TAB_CURRENT
curses.curs_set(0)
def get_color_state(self): def get_color_state(self):
return theme.COLOR_TAB_NORMAL return self._color_state
def set_color_state(self, color): class SimpleTextTab(Tab):
pass """
A very simple tab, with just a text displaying some
information or whatever.
For example used to display tracebacks
"""
def __init__(self, core, text):
Tab.__init__(self, core)
self._color_state = theme.COLOR_TAB_NORMAL
self.text_win = windows.SimpleTextWin(text)
self.tab_win = windows.GlobalInfoBar()
self.default_help_message = windows.HelpText("“Ctrl+q”: close")
self.input = self.default_help_message
self.key_func['^T'] = self.close
self.key_func["/"] = self.on_slash
self.resize()
def get_name(self): def on_slash(self):
return self.name """
'/' is pressed, activate the input
"""
curses.curs_set(1)
self.input = windows.CommandInput("", self.reset_help_message, self.execute_slash_command)
self.input.resize(1, self.width, self.height-1, 0, self.core.stdscr)
self.input.do_command("/") # we add the slash
def on_input(self, key): def on_input(self, key):
res = self.input.do_command(key) res = self.input.do_command(key)
...@@ -1055,6 +1092,19 @@ class MucListTab(Tab): ...@@ -1055,6 +1092,19 @@ class MucListTab(Tab):
if key in self.key_func: if key in self.key_func:
return self.key_func[key]() return self.key_func[key]()
def close(self):
self.core.close_tab()
def resize(self):
self.text_win.resize(self.height-2, self.width, 0, 0, self.core.stdscr)
self.tab_win.resize(1, self.width, self.height-2, 0, self.core.stdscr)
self.input.resize(1, self.width, self.height-1, 0, self.core.stdscr)
def refresh(self, tabs, information, roster):
self.text_win.refresh()
self.tab_win.refresh(tabs, tabs[0])
self.input.refresh()
def on_lose_focus(self): def on_lose_focus(self):
self._color_state = theme.COLOR_TAB_NORMAL self._color_state = theme.COLOR_TAB_NORMAL
...@@ -1065,18 +1115,6 @@ class MucListTab(Tab): ...@@ -1065,18 +1115,6 @@ class MucListTab(Tab):
def get_color_state(self): def get_color_state(self):
return self._color_state return self._color_state
# class SimpleTextTab(Tab):
# """
# A very simple tab, with just a text displaying some
# information or whatever
# """
# def __init__(self, core, text):
# Tab.__init__(self, core)
# self.text = text
# self.text_win =
# def resize(self):
# pass
def diffmatch(search, string): def diffmatch(search, string):
""" """
Use difflib and a loop to check if search_pattern can Use difflib and a loop to check if search_pattern can
......
...@@ -49,7 +49,7 @@ import theme ...@@ -49,7 +49,7 @@ import theme
g_lock = Lock() g_lock = Lock()
LINES_NB_LIMIT = 16384 LINES_NB_LIMIT = 4096
class Win(object): class Win(object):
def __init__(self): def __init__(self):
...@@ -1425,16 +1425,35 @@ class ColumnHeaderWin(Win): ...@@ -1425,16 +1425,35 @@ class ColumnHeaderWin(Win):
x += size x += size
self._refresh() self._refresh()
# class SimpleTextWin(Win): class SimpleTextWin(Win):
# def __init__(self, text): def __init__(self, text):
# self._text = text self._text = text
# self.built_lines = [] self.built_lines = []
# def resize(self, height, width, y, x, stdscr): def resize(self, height, width, y, x, stdscr):
# self._resize(height, width, y, x, stdscr) self._resize(height, width, y, x, stdscr)
# self.rebuild_text() self.rebuild_text()
# def rebuild_text(self): def rebuild_text(self):
"""
# def refresh(self): Transform the text in lines than can then be
# pass displayed without any calculation or anything
at refresh() time
It is basically called on each resize
"""
self.built_lines = []
for line in self._text.split('\n'):
while len(line) >= self.width:
limit = line[:self.width].rfind(' ')
if limit <= 0:
limit = self.width
self.built_lines.append(line[:limit])
line = line[limit:]
self.built_lines.append(line)
def refresh(self):
with g_lock:
self._win.erase()
for y, line in enumerate(self.built_lines):
self.addstr(y, 0, line)
self._refresh()
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