From d836e26107733e558a8e8553f6cc977d8aeac18c Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 3 Sep 2014 08:57:21 +0200 Subject: [PATCH] Nicer debug printing of functions. --- qutebrowser/commands/cmdutils.py | 9 +++-- qutebrowser/commands/command.py | 6 +-- qutebrowser/utils/debug.py | 38 ++++++++++++++++-- qutebrowser/utils/utilcmds.py | 68 +++++++++++++++++++++++++++++++- 4 files changed, 110 insertions(+), 11 deletions(-) diff --git a/qutebrowser/commands/cmdutils.py b/qutebrowser/commands/cmdutils.py index 4735bad53..7302fbfec 100644 --- a/qutebrowser/commands/cmdutils.py +++ b/qutebrowser/commands/cmdutils.py @@ -27,7 +27,7 @@ import enum import inspect import collections -from qutebrowser.utils import usertypes, qtutils, log +from qutebrowser.utils import usertypes, qtutils, log, debug from qutebrowser.commands import command, cmdexc, argparser cmd_dict = {} @@ -236,9 +236,10 @@ class register: # pylint: disable=invalid-name is_flag = typ == bool args += self._get_argparse_args(param, annotation_info, is_flag) - log.commands.vdebug('Adding argument {} of type {} -> ' - 'args {}, kwargs {}'.format( - param.name, typ, args, kwargs)) + callsig = debug.format_call(parser.add_argument, args, + kwargs, full=False) + log.commands.vdebug('Adding arg {} of type {} -> {}'.format( + param.name, typ, callsig)) parser.add_argument(*args, **kwargs) return has_count, desc, parser diff --git a/qutebrowser/commands/command.py b/qutebrowser/commands/command.py index 0780c3519..afb3b30e7 100644 --- a/qutebrowser/commands/command.py +++ b/qutebrowser/commands/command.py @@ -23,7 +23,7 @@ from PyQt5.QtCore import QCoreApplication from PyQt5.QtWebKit import QWebSettings from qutebrowser.commands import cmdexc, argparser -from qutebrowser.utils import log, utils, message +from qutebrowser.utils import log, utils, message, debug class Command: @@ -138,6 +138,6 @@ class Command: kwargs = {'count': count} self._check_prerequisites() - log.commands.debug('posargs: {}'.format(posargs)) - log.commands.debug('kwargs: {}'.format(kwargs)) + log.commands.debug('Calling {}'.format( + debug.format_call(self.handler, posargs, kwargs))) self.handler(*posargs, **kwargs) diff --git a/qutebrowser/utils/debug.py b/qutebrowser/utils/debug.py index aa7641143..1716604f8 100644 --- a/qutebrowser/utils/debug.py +++ b/qutebrowser/utils/debug.py @@ -208,6 +208,18 @@ def signal_name(sig): return m.group(1) +def _format_args(args=None, kwargs=None): + """Format a list of arguments/kwargs to a function-call like string.""" + if args is not None: + arglist = [utils.compact_text(repr(arg), 50) for arg in args] + else: + arglist = [] + if kwargs is not None: + for k, v in kwargs.items(): + arglist.append('{}={}'.format(k, utils.compact_text(repr(v), 50))) + return ', '.join(arglist) + + def dbg_signal(sig, args): """Get a string representation of a signal for debugging. @@ -218,6 +230,26 @@ def dbg_signal(sig, args): Return: A human-readable string representation of signal/args. """ - argstr = ', '.join([utils.elide(str(a).replace('\n', ' '), 20) - for a in args]) - return '{}({})'.format(signal_name(sig), argstr) + return '{}({})'.format(signal_name(sig), _format_args(args)) + + +def format_call(func, args=None, kwargs=None, full=True): + """Get a string representation of a function calls with the given args. + + Args: + func: The callable to print. + args: A list of positional arguments. + kwargs: A dict of named arguments. + full: Whether to print the full name + + Return: + A string with the function call. + """ + if full: + if func.__module__ is not None: + name = '.'.join([func.__module__, func.__qualname__]) + else: + name = func.__qualname__ + else: + name = func.__name__ + return '{}({})'.format(name, _format_args(args, kwargs)) diff --git a/qutebrowser/utils/utilcmds.py b/qutebrowser/utils/utilcmds.py index 82ef05117..933c14ad9 100644 --- a/qutebrowser/utils/utilcmds.py +++ b/qutebrowser/utils/utilcmds.py @@ -19,10 +19,14 @@ """Misc. utility commands exposed to the user.""" + +from PyQt5.QtCore import pyqtRemoveInputHook, QCoreApplication + from functools import partial -from qutebrowser.utils import usertypes +from qutebrowser.utils import usertypes, log from qutebrowser.commands import runners, cmdexc, cmdutils +from qutebrowser.config import config, style _timers = [] @@ -57,3 +61,65 @@ def later(ms : int, *command : {'nargs': '+'}): timer.timeout.connect(partial(_commandrunner.run_safely, cmdline)) timer.timeout.connect(lambda: _timers.remove(timer)) timer.start() + + +@cmdutils.register(debug=True, name='debug-set-trace') +def set_trace(): + """Break into the debugger in the shell. + + // + + Based on http://stackoverflow.com/a/1745965/2085149 + """ + if sys.stdout is not None: + sys.stdout.flush() + print() + print("When done debugging, remember to execute:") + print(" from PyQt5 import QtCore; QtCore.pyqtRestoreInputHook()") + print("before executing c(ontinue).") + pyqtRemoveInputHook() + pdb.set_trace() + + +@cmdutils.register(debug=True) +def debug_crash(typ : ('exception', 'segfault') = 'exception'): + """Crash for debugging purposes. + + Args: + typ: either 'exception' or 'segfault'. + + Raises: + raises Exception when typ is not segfault. + segfaults when typ is (you don't say...) + """ + if typ == 'segfault': + # From python's Lib/test/crashers/bogus_code_obj.py + co = types.CodeType(0, 0, 0, 0, 0, b'\x04\x71\x00\x00', (), (), (), + '', '', 1, b'') + exec(co) # pylint: disable=exec-used + raise Exception("Segfault failed (wat.)") + else: + raise Exception("Forced crash") + + +@cmdutils.register(debug=True) +def debug_all_widgets(): + """Print a list of all widgets to debug log.""" + s = QCoreApplication.instance().get_all_widgets() + log.misc.debug(s) + + +@cmdutils.register(debug=True) +def debug_all_objects(): + """Print a list of all objects to the debug log.""" + s = QCoreApplication.instance().get_all_objects() + log.misc.debug(s) + + +@cmdutils.register(debug=True) +def debug_cache_stats(): + """Print LRU cache stats.""" + config_info = config.instance().get.cache_info() + style_info = style.get_stylesheet.cache_info() + log.misc.debug('config: {}'.format(config_info)) + log.misc.debug('style: {}'.format(style_info))