From c294b608c6ac8a4b6f23cf482bcb3896fd54d5fb Mon Sep 17 00:00:00 2001 From: Harshad Sharma Date: Fri, 16 Jun 2017 18:57:48 +0530 Subject: [PATCH] Add: display traceback within the browser if userscript crashes. Use click instead of argparse. --- setup.py | 1 + src/qutescript/cli.py | 35 ++++++++++++---------- src/qutescript/decorator.py | 59 +++++++++++++++++++++++++++---------- 3 files changed, 63 insertions(+), 32 deletions(-) diff --git a/setup.py b/setup.py index 6ecb45e..02d758f 100644 --- a/setup.py +++ b/setup.py @@ -67,6 +67,7 @@ setup( ], install_requires=[ # eg: 'aspectlib==1.1.1', 'six>=1.7', + 'click', ], extras_require={ # eg: diff --git a/src/qutescript/cli.py b/src/qutescript/cli.py index 5b046f6..7e0968a 100755 --- a/src/qutescript/cli.py +++ b/src/qutescript/cli.py @@ -14,34 +14,37 @@ Why does this file exist, and why not put this in __main__? Also see (1) from http://click.pocoo.org/5/setuptools/#setuptools-integration """ -import argparse +import sys +import click import os -import sys -parser = argparse.ArgumentParser(description='Command description.') -parser.add_argument('names', metavar='NAME', nargs=argparse.ZERO_OR_MORE, - help="A name of something.") +# --- -def main(args=None): - args = parser.parse_args(args=args) - print(args.names) +# script_cli_parser = argparse.ArgumentParser(description='Qutebrowser userscript.') +# script_cli_parser.add_argument('--install', action='store_true', help='Setup permissions and show install +# instructions.') +class NoSubCommands(Exception): + pass -# --- -script_cli_parser = argparse.ArgumentParser(description='Qutebrowser userscript.') -script_cli_parser.add_argument('--install', action='store_true', help='Setup permissions and show install instructions.') +@click.group(invoke_without_command=True) +@click.pass_context +def userscript(ctx): + """ + Qutebrowser Userscript + """ + if ctx.invoked_subcommand is None: + raise NoSubCommands() -def script_cli(args=None): - args = script_cli_parser.parse_args(args=args) - if not args.install: - return +@userscript.command(name='install') +def userscript_install(): from .installer import install userscript_path = os.path.abspath(sys.argv[0]) path = os.path.abspath(userscript_path) name = os.path.basename(userscript_path) - print(install(path, name=name)) + click.echo_via_pager(install(path, name=name)) sys.exit(0) diff --git a/src/qutescript/decorator.py b/src/qutescript/decorator.py index b2cd812..5ab5fea 100644 --- a/src/qutescript/decorator.py +++ b/src/qutescript/decorator.py @@ -3,9 +3,10 @@ import sys import traceback +import tempfile import os -from .cli import script_cli +from .cli import NoSubCommands, userscript from .request import build_request log_file_path = './qutescript.log' @@ -15,38 +16,64 @@ def write_log(message, file_path=None): print('***', message) file_path = file_path or log_file_path file_path = os.path.abspath(os.path.expanduser(file_path)) - record = [message, '\n', '\n'] + record = ['***' + message, '\n', '\n'] with open(file_path, 'a') as logfile: logfile.writelines(record) +def send_traceback_to_browser(trace, *messages): + """ + Write trace and messages to a temporary file, + Attempt to open the file through FIFO in the browser. + """ + write_log(trace) + [write_log(msg) for msg in messages] + fifo = os.getenv('QUTE_FIFO') + out_lines = ['
', trace] + ['

{}

'.format(m or ' ') for m in messages] + if not fifo: + return + with tempfile.NamedTemporaryFile(mode='w', suffix='.html', delete=False) as trace_file: + trace_file.writelines(out_lines) + print('***', trace_file.name) + with open(fifo, 'w') as fifo_file: + fifo_file.write('open -t file://{}'.format( + os.path.abspath(trace_file.name))) + + def qutescript(func): def wrapper(): - script_cli(args=sys.argv[1:]) try: - request = build_request() + userscript() + except NoSubCommands as e: + pass except Exception as e: - write_log(traceback.format_exc()) - write_log('Cannot build request.') + send_traceback_to_browser(traceback.format_exc(), 'Cannot execute cli handler') sys.exit(1) + try: + request = build_request() + except Exception as e: + send_traceback_to_browser(traceback.format_exc(), 'Cannot build request.') + sys.exit(5) try: command = func(request) if not command: return except Exception as e: - write_log(traceback.format_exc()) - write_log('Userscript error.') - sys.exit(2) + send_traceback_to_browser(traceback.format_exc(), 'Userscript error.') + sys.exit(10) if not request.fifo: - write_log('ERROR: userscript returned command: {}, ' - 'but QUTE_FIFO was not found in passed environment.\n' - 'Try: :spawn --userscript /path/to/script ?') - sys.exit(3) + message = ('ERROR: userscript returned command: {}, ' + 'but QUTE_FIFO was not found in passed environment.\n' + 'Try: :spawn --userscript /path/to/script ?') + send_traceback_to_browser(traceback.format_exc(), message) + sys.exit(20) try: with open(request.fifo, 'w') as fifo: fifo.write('{}\n'.format(command)) except Exception as e: - write_log(traceback.format_exc()) - write_log('Cannot write to FIFO: {!r}'.format(request.fifo)) - sys.exit(4) + send_traceback_to_browser( + traceback.format_exc(), + 'Cannot write to FIFO: {!r}'.format(request.fifo)) + sys.exit(30) + return wrapper