util/ExceptionHandler.py
changeset 1792 4d1de8b0183f
child 1881 091005ec69c4
equal deleted inserted replaced
1791:3216ed1ba1f7 1792:4d1de8b0183f
       
     1 #!/usr/bin/env python
       
     2 # -*- coding: utf-8 -*-
       
     3 
       
     4 # This file is part of Beremiz, a Integrated Development Environment for
       
     5 # programming IEC 61131-3 automates supporting plcopen standard and CanFestival.
       
     6 #
       
     7 # Copyright (C) 2007:      Edouard TISSERANT and Laurent BESSARD
       
     8 # Copyright (C) 2016-2017: Andrey Skvortsov <andrej.skvortzov@gmail.com>
       
     9 #
       
    10 # See COPYING file for copyrights details.
       
    11 #
       
    12 # This program is free software; you can redistribute it and/or
       
    13 # modify it under the terms of the GNU General Public License
       
    14 # as published by the Free Software Foundation; either version 2
       
    15 # of the License, or (at your option) any later version.
       
    16 #
       
    17 # This program is distributed in the hope that it will be useful,
       
    18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       
    20 # GNU General Public License for more details.
       
    21 #
       
    22 # You should have received a copy of the GNU General Public License
       
    23 # along with this program; if not, write to the Free Software
       
    24 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
       
    25 
       
    26 import os
       
    27 import sys
       
    28 import time
       
    29 import tempfile
       
    30 import platform
       
    31 import traceback
       
    32 import threading
       
    33 import wx
       
    34 
       
    35 Max_Traceback_List_Size = 20
       
    36 
       
    37 
       
    38 def Display_Exception_Dialog(e_type, e_value, e_tb, bug_report_path):
       
    39     trcbck_lst = []
       
    40     for i, line in enumerate(traceback.extract_tb(e_tb)):
       
    41         trcbck = " " + str(i+1) + ". "
       
    42         if line[0].find(os.getcwd()) == -1:
       
    43             trcbck += "file : " + str(line[0]) + ",   "
       
    44         else:
       
    45             trcbck += "file : " + str(line[0][len(os.getcwd()):]) + ",   "
       
    46         trcbck += "line : " + str(line[1]) + ",   " + "function : " + str(line[2])
       
    47         trcbck_lst.append(trcbck)
       
    48 
       
    49     # Allow clicking....
       
    50     cap = wx.Window_GetCapture()
       
    51     if cap:
       
    52         cap.ReleaseMouse()
       
    53 
       
    54     dlg = wx.SingleChoiceDialog(
       
    55         None,
       
    56         _("""
       
    57 An unhandled exception (bug) occured. Bug report saved at :
       
    58 (%s)
       
    59 
       
    60 Please be kind enough to send this file to:
       
    61 beremiz-devel@lists.sourceforge.net
       
    62 
       
    63 You should now restart program.
       
    64 
       
    65 Traceback:
       
    66 """) % bug_report_path +
       
    67         repr(e_type) + " : " + repr(e_value),
       
    68         _("Error"),
       
    69         trcbck_lst)
       
    70     try:
       
    71         res = (dlg.ShowModal() == wx.ID_OK)
       
    72     finally:
       
    73         dlg.Destroy()
       
    74 
       
    75     return res
       
    76 
       
    77 
       
    78 def get_last_traceback(tb):
       
    79     while tb.tb_next:
       
    80         tb = tb.tb_next
       
    81     return tb
       
    82 
       
    83 
       
    84 def format_namespace(d, indent='    '):
       
    85     return '\n'.join(['%s%s: %s' % (indent, k, repr(v)[:10000]) for k, v in d.iteritems()])
       
    86 
       
    87 
       
    88 ignored_exceptions = []  # a problem with a line in a module is only reported once per session
       
    89 
       
    90 
       
    91 def AddExceptHook(app_version='[No version]'):
       
    92 
       
    93     def save_bug_report(e_type, e_value, e_traceback, bug_report_path, date):
       
    94         info = {
       
    95             'app-title': wx.GetApp().GetAppName(),
       
    96             'app-version': app_version,
       
    97             'wx-version': wx.VERSION_STRING,
       
    98             'wx-platform': wx.Platform,
       
    99             'python-version': platform.python_version(),
       
   100             'platform': platform.platform(),
       
   101             'e-type': e_type,
       
   102             'e-value': e_value,
       
   103             'date': date,
       
   104             'cwd': os.getcwd(),
       
   105         }
       
   106         if e_traceback:
       
   107             info['traceback'] = ''.join(traceback.format_tb(e_traceback)) + '%s: %s' % (e_type, e_value)
       
   108             last_tb = get_last_traceback(e_traceback)
       
   109             exception_locals = last_tb.tb_frame.f_locals  # the locals at the level of the stack trace where the exception actually occurred
       
   110             info['locals'] = format_namespace(exception_locals)
       
   111             if 'self' in exception_locals:
       
   112                 try:
       
   113                     info['self'] = format_namespace(exception_locals['self'].__dict__)
       
   114                 except Exception:
       
   115                     pass
       
   116         path = os.path.dirname(bug_report_path)
       
   117         if not os.path.exists(path):
       
   118             os.mkdir(path)
       
   119         output = open(bug_report_path, 'w')
       
   120         lst = info.keys()
       
   121         lst.sort()
       
   122         for a in lst:
       
   123             output.write(a + ":\n" + str(info[a]) + "\n\n")
       
   124         output.close()
       
   125 
       
   126     def handle_exception(e_type, e_value, e_traceback):
       
   127         traceback.print_exception(e_type, e_value, e_traceback)  # this is very helpful when there's an exception in the rest of this func
       
   128         last_tb = get_last_traceback(e_traceback)
       
   129         ex = (last_tb.tb_frame.f_code.co_filename, last_tb.tb_frame.f_lineno)
       
   130         if ex not in ignored_exceptions:
       
   131             ignored_exceptions.append(ex)
       
   132             date = time.ctime()
       
   133             path = tempfile.gettempdir()+os.sep+wx.GetApp().GetAppName()
       
   134             bug_report_path = path + os.sep + "bug_report_" + time.strftime("%Y_%m_%d__%H-%M-%S") + ".txt"
       
   135             save_bug_report(e_type, e_value, e_traceback, bug_report_path, date)
       
   136             Display_Exception_Dialog(e_type, e_value, e_traceback, bug_report_path)
       
   137     # sys.excepthook = lambda *args: wx.CallAfter(handle_exception, *args)
       
   138     sys.excepthook = handle_exception
       
   139 
       
   140     init_old = threading.Thread.__init__
       
   141 
       
   142     def init(self, *args, **kwargs):
       
   143         init_old(self, *args, **kwargs)
       
   144         run_old = self.run
       
   145 
       
   146         def run_with_except_hook(*args, **kw):
       
   147             try:
       
   148                 run_old(*args, **kw)
       
   149             except (KeyboardInterrupt, SystemExit):
       
   150                 raise
       
   151             except Exception:
       
   152                 sys.excepthook(*sys.exc_info())
       
   153         self.run = run_with_except_hook
       
   154     threading.Thread.__init__ = init