From af0b6901bee7a248059317dd8bbcce559cace339 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 3 Mar 2014 21:06:10 +0100 Subject: [PATCH] First kinda working version --- qutebrowser/app.py | 42 ++++++++++++++-- .../commands/{template.py => command.py} | 18 +++++-- qutebrowser/commands/commands.py | 32 ------------- qutebrowser/commands/exceptions.py | 2 +- qutebrowser/commands/utils.py | 12 ++--- qutebrowser/utils/misc.py | 48 ++++--------------- 6 files changed, 68 insertions(+), 86 deletions(-) rename qutebrowser/commands/{template.py => command.py} (81%) delete mode 100644 qutebrowser/commands/commands.py diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 372b0b4e7..27cc96d9c 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -54,7 +54,8 @@ from qutebrowser.widgets.mainwindow import MainWindow from qutebrowser.widgets.crash import CrashDialog from qutebrowser.commands.keys import KeyParser from qutebrowser.utils.appdirs import AppDirs -from qutebrowser.utils.misc import set_trace +from qutebrowser.utils.misc import dotted_getattr +from qutebrowser.utils.debug import set_trace class QuteBrowser(QApplication): @@ -180,6 +181,14 @@ class QuteBrowser(QApplication): Registers all commands, connects its signals, and sets up keyparser. """ + for key, cmd in sorted(cmdutils.cmd_dict.items()): + cmd.signal.connect(self.command_handler) + if cmd.instance is not None: + func = '.'.join([cmd.instance if cmd.instance else 'app', + cmd.handler.__name__]) + else: + func = cmd.handler.__name__ + logging.debug("Registered command: {} -> {}".format(key, func)) self.keyparser.from_config_sect(config.config['keybind']) def _process_init_args(self): @@ -322,7 +331,7 @@ class QuteBrowser(QApplication): logging.debug("maybe_quit quitting.") self.quit() - @cmdutils.register(instance='app', split_args=False) + @cmdutils.register(instance='', split_args=False) def pyeval(self, s): """Evaluate a python string and display the results as a webpage. @@ -340,7 +349,7 @@ class QuteBrowser(QApplication): qutescheme.pyeval_output = out self.mainwindow.tabs.cur.openurl('qute:pyeval') - @cmdutils.register(instance='app', hide=True) + @cmdutils.register(instance='', hide=True) def crash(self): """Crash for debugging purposes. @@ -353,7 +362,7 @@ class QuteBrowser(QApplication): raise Exception("Forced crash") @pyqtSlot() - @cmdutils.register(instance='app', name=['q', 'quit'], nargs=0) + @cmdutils.register(instance='', name=['q', 'quit'], nargs=0) def shutdown(self, do_quit=True): """Try to shutdown everything cleanly. @@ -399,3 +408,28 @@ class QuteBrowser(QApplication): """ logging.debug("Shutdown complete, quitting.") self.quit() + + @pyqtSlot(tuple) + def command_handler(self, tpl): + """Handle commands which need an instance.. + + Args: + tpl: An (instance, func, count, args) tuple. + instance: How to get the current instance of the target object + from app.py, as a dotted string, e.g. + 'mainwindow.tabs.cur'. + func: The function name to be called (as string). + count: The count given to the command, or None. + args: A list of arguments given to the command. + + """ + (instance, func, count, args) = tpl + if instance == '': + obj = self + else: + obj = dotted_getattr(self, instance) + handler = getattr(obj, func) + if count is not None: + handler(*args, count=count) + else: + handler(*args) diff --git a/qutebrowser/commands/template.py b/qutebrowser/commands/command.py similarity index 81% rename from qutebrowser/commands/template.py rename to qutebrowser/commands/command.py index ca9f4efaf..d45812387 100644 --- a/qutebrowser/commands/template.py +++ b/qutebrowser/commands/command.py @@ -21,8 +21,10 @@ import logging from qutebrowser.commands.exceptions import ArgumentCountError +from PyQt5.QtCore import pyqtSignal, QObject -class Command: + +class Command(QObject): """Base skeleton for a command. @@ -31,11 +33,14 @@ class Command: """ + signal = pyqtSignal(tuple) + # FIXME: # we should probably have some kind of typing / argument casting for args # this might be combined with help texts or so as well - def __init__(self, name, split_args, hide, nargs, count, desc, handler): + def __init__(self, name, split_args, hide, nargs, count, desc, instance, + handler): super().__init__() self.name = name self.split_args = split_args @@ -43,6 +48,7 @@ class Command: self.nargs = nargs self.count = count self.desc = desc + self.instance = instance self.handler = handler def check(self, args): @@ -78,7 +84,13 @@ class Command: dbgout.append("(count={})".format(count)) logging.debug(' '.join(dbgout)) - if count is not None and self.count: + if self.instance is not None and self.count and count is not None: + self.signal.emit((self.instance, self.handler.__name__, count, + args)) + elif self.instance is not None: + self.signal.emit((self.instance, self.handler.__name__, None, + args)) + elif count is not None and self.count: return self.handler(*args, count=count) else: return self.handler(*args) diff --git a/qutebrowser/commands/commands.py b/qutebrowser/commands/commands.py deleted file mode 100644 index 61a767575..000000000 --- a/qutebrowser/commands/commands.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright 2014 Florian Bruhin (The Compiler) -# -# This file is part of qutebrowser. -# -# qutebrowser is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# qutebrowser is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with qutebrowser. If not, see . - -"""All command classes. - -These are automatically propagated from commands.utils -via inspect. - -A command class can set the following properties: - - nargs -- Number of arguments. Either a number, '?' (0 or 1), '+' (1 or - more), or '*' (any). Default: 0 - name -- The name of the command, or a list of aliases. - split_args -- If arguments should be split or not. Default: True - count -- If the command supports a count. Default: False - hide -- If the command should be hidden in tab completion. Default: False - -""" diff --git a/qutebrowser/commands/exceptions.py b/qutebrowser/commands/exceptions.py index 8a2d37719..83251f457 100644 --- a/qutebrowser/commands/exceptions.py +++ b/qutebrowser/commands/exceptions.py @@ -15,7 +15,7 @@ # You should have received a copy of the GNU General Public License # along with qutebrowser. If not, see . -"""Exception classes for commands.utils and commands.template. +"""Exception classes for commands.utils and commands.command. Defined here to avoid circular dependency hell. diff --git a/qutebrowser/commands/utils.py b/qutebrowser/commands/utils.py index c6c94fad4..f75a60c81 100644 --- a/qutebrowser/commands/utils.py +++ b/qutebrowser/commands/utils.py @@ -19,6 +19,7 @@ import shlex import inspect +import logging import functools from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject @@ -26,7 +27,7 @@ from PyQt5.QtWidgets import QApplication from PyQt5.QtWebKitWidgets import QWebPage import qutebrowser.config.config as config -from qutebrowser.commands.template import Command +from qutebrowser.commands.command import Command from qutebrowser.commands.exceptions import (ArgumentCountError, NoSuchCommandError) @@ -62,12 +63,9 @@ class register: names += name count, nargs = self._get_nargs_count(func) desc = func.__doc__.splitlines()[0].strip().rstrip('.') - if self.instance is not None: - handler = functools.partial(func, instance]) - else: - handler = func - cmd = Command(mainname, self.split_args, self.hide, nargs, count, desc, - handler=handler) + cmd = Command(name=mainname, split_args=self.split_args, + hide=self.hide, nargs=nargs, count=count, desc=desc, + instance=self.instance, handler=func) for name in names: cmd_dict[name] = cmd return func diff --git a/qutebrowser/utils/misc.py b/qutebrowser/utils/misc.py index 2c85236a4..177e7c383 100644 --- a/qutebrowser/utils/misc.py +++ b/qutebrowser/utils/misc.py @@ -17,37 +17,11 @@ """Other utilities which don't fit anywhere else.""" -import sys import os.path - -from PyQt5.QtCore import pyqtRemoveInputHook - -#import qutebrowser.commands.utils as cmdutils - -try: - # pylint: disable=import-error - from ipdb import set_trace as pdb_set_trace -except ImportError: - from pdb import set_trace as pdb_set_trace +from functools import reduce import qutebrowser -# FIXME we can';t do this because of circular imports -#@cmdutils.register(name='settrace', hide=True) -def set_trace(): - """Set a tracepoint in the Python debugger that works with Qt. - - Based on http://stackoverflow.com/a/1745965/2085149 - - """ - print() - print("When done debugging, remember to execute:") - print(" from PyQt5 import QtCore; QtCore.pyqtRestoreInputHook()") - print("before executing c(ontinue).") - pyqtRemoveInputHook() - return pdb_set_trace() - - def read_file(filename): """Get the contents of a file contained with qutebrowser. @@ -63,19 +37,15 @@ def read_file(filename): return f.read() -def trace_lines(do_trace): - """Turn on/off printing each executed line. +def dotted_getattr(obj, path): + """getattr supporting the dot notation. Args: - do_trace: Whether to start tracing (True) or stop it (False). + obj: The object where to start. + path: A dotted object path as a string. + + Return: + The object at path. """ - def trace(frame, event, _): - """Trace function passed to sys.settrace.""" - print("{}, {}:{}".format(event, frame.f_code.co_filename, - frame.f_lineno)) - return trace - if do_trace: - sys.settrace(trace) - else: - sys.settrace(None) + return reduce(getattr, path.split('.'), obj)