From ab0714ef1d635ce11b8507ec557af4d186167b64 Mon Sep 17 00:00:00 2001 From: mjv761 Date: Sun, 6 Sep 2020 12:22:47 -0600 Subject: [PATCH] Add a mechanism for logging errors instead of swallowing exceptions --- pudb/debugger.py | 3 ++- pudb/lowlevel.py | 20 ++++++++++++++++++++ pudb/run.py | 16 +++++++++++++--- pudb/settings.py | 10 +++++----- pudb/var_view.py | 21 +++++++++++++++------ 5 files changed, 55 insertions(+), 15 deletions(-) diff --git a/pudb/debugger.py b/pudb/debugger.py index eaf5529..1ee524d 100644 --- a/pudb/debugger.py +++ b/pudb/debugger.py @@ -2613,7 +2613,8 @@ class DebuggerUI(FrameVarInfoKeeper): try: class_name = frame.f_locals["self"].__class__.__name__ except Exception: - pass + from pudb.lowlevel import ui_log + ui_log.exception('Failed to determine class name') return StackFrame(frame is self.debugger.curframe, code.co_name, class_name, diff --git a/pudb/lowlevel.py b/pudb/lowlevel.py index 9f5d5b3..37c8852 100644 --- a/pudb/lowlevel.py +++ b/pudb/lowlevel.py @@ -28,6 +28,26 @@ THE SOFTWARE. from pudb.py3compat import PY3, text_type +import logging + +ui_formatter = logging.Formatter( + fmt='\n*** Pudb UI Exception Encountered: %(message)s ***\n' +) +ui_handler = logging.StreamHandler() +ui_handler.setFormatter(ui_formatter) +ui_log = logging.getLogger('ui') +ui_log.setLevel(logging.CRITICAL) +ui_log.addHandler(ui_handler) + +settings_formatter = logging.Formatter( + fmt='\n*** Pudb Settings Exception Encountered: %(message)s ***\n' +) +settings_handler = logging.StreamHandler() +settings_handler.setFormatter(settings_formatter) +settings_log = logging.getLogger('settings') +settings_log.setLevel(logging.CRITICAL) +settings_log.addHandler(settings_handler) + # {{{ breakpoint validity diff --git a/pudb/run.py b/pudb/run.py index 8de6ca4..7b18f69 100644 --- a/pudb/run.py +++ b/pudb/run.py @@ -15,17 +15,27 @@ def main(): # python -m pudb -m http.server -h # where the -h will be passed to the http.server module parser.add_argument("-m", "--module", action='store_true', - help="Debug as module or package instead of as a script") + help="Debug as module or package instead of as a script") + parser.add_argument("-le", "--log-errors", action='store_true', + help="If an unexpected exception is encountered, " + "log the exception information to stderr", + default=False) parser.add_argument("--pre-run", metavar="COMMAND", - help="Run command before each program run", - default="") + help="Run command before each program run", + default="") parser.add_argument('script_args', nargs=argparse.REMAINDER, help="Arguments to pass to script or module") options = parser.parse_args() args = options.script_args + if options.log_errors: + import logging + from pudb.lowlevel import ui_log, settings_log + ui_log.setLevel(logging.ERROR) + settings_log.setLevel(logging.ERROR) + options_kwargs = { 'pre_run': options.pre_run, 'steal_output': options.steal_output, diff --git a/pudb/settings.py b/pudb/settings.py index 18583ed..fa83433 100644 --- a/pudb/settings.py +++ b/pudb/settings.py @@ -25,12 +25,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ - import os import sys from pudb.py3compat import ConfigParser -from pudb.lowlevel import lookup_module, get_breakpoint_invalid_reason +from pudb.lowlevel import (lookup_module, get_breakpoint_invalid_reason, + settings_log) # minor LGPL violation: stolen from python-xdg @@ -85,7 +85,7 @@ def load_config(): if cparser.has_section(CONF_SECTION): conf_dict.update(dict(cparser.items(CONF_SECTION))) except Exception: - pass + settings_log.exception('Failed to load config') conf_dict.setdefault("shell", "internal") conf_dict.setdefault("theme", "classic") @@ -121,7 +121,7 @@ def load_config(): else: conf_dict[name] = True except Exception: - pass + settings_log.exception('Failed to process config') normalize_bool_inplace("line_numbers") normalize_bool_inplace("wrap_variables") @@ -148,7 +148,7 @@ def save_config(conf_dict): cparser.write(outf) outf.close() except Exception: - pass + settings_log.exception('Failed to save config') def edit_config(ui, conf_dict): diff --git a/pudb/var_view.py b/pudb/var_view.py index 855018d..1322ce8 100644 --- a/pudb/var_view.py +++ b/pudb/var_view.py @@ -33,6 +33,8 @@ THE SOFTWARE. import urwid import inspect +from pudb.lowlevel import ui_log + try: import numpy HAVE_NUMPY = 1 @@ -285,7 +287,7 @@ def type_stringifier(value): try: return text_type(value) except Exception: - pass + ui_log.exception('string safe type stringifier failed') elif hasattr(type(value), "safely_stringify_for_pudb"): try: @@ -293,7 +295,7 @@ def type_stringifier(value): # and return nonsense. result = value.safely_stringify_for_pudb() except Exception: - pass + ui_log.exception('safely_stringify_for_pudb call failed') else: if isinstance(result, string_types): return text_type(result) @@ -371,6 +373,7 @@ class ValueWalker: # repr() on a random object. displayed_value = type_stringifier(value) \ + " (!! %s error !!)" % iinfo.display_type + ui_log.exception('stringifier failed') if iinfo.show_detail: if iinfo.access_level == "public": @@ -414,21 +417,27 @@ class ValueWalker: key_it = value.keys() else: key_it = value.iterkeys() - except Exception: + except AttributeError: + # keys or iterkeys doesn't exist, not worth mentioning! pass + except Exception: + ui_log.exception('Failed to obtain key iterator') if key_it is None: try: len_value = len(value) - except Exception: + except TypeError: + # no __len__ defined on the value, not worth mentioning! pass + except Exception: + ui_log.exception('Failed to determine container length') else: try: value[0] except IndexError: key_it = [] except Exception: - pass + ui_log.exception('Item is not iterable') else: key_it = xrange(len_value) @@ -456,7 +465,7 @@ class ValueWalker: try: key_its.append(dir(value)) except Exception: - pass + ui_log.exception('Failed to lookup attributes') keys = [key for ki in key_its