Use exceptions for command errors

This commit is contained in:
Florian Bruhin 2014-05-14 18:00:40 +02:00
parent ed61244964
commit a045511962
13 changed files with 87 additions and 89 deletions

1
TODO
View File

@ -102,7 +102,6 @@ Style
- rename commands
- reorder config #2
- rework exception hierarchy for config (common base exception)
- use exceptions for error messages in commands
Major features
==============

View File

@ -48,6 +48,7 @@ import qutebrowser.config.config as config
import qutebrowser.network.qutescheme as qutescheme
import qutebrowser.config.websettings as websettings
import qutebrowser.network.proxy as proxy
import qutebrowser.utils.message as message
from qutebrowser.network.networkmanager import NetworkManager
from qutebrowser.config.config import ConfigManager
from qutebrowser.keyinput.modeman import ModeManager
@ -56,6 +57,7 @@ from qutebrowser.widgets.crash import CrashDialog
from qutebrowser.keyinput.modeparsers import NormalKeyParser, HintKeyParser
from qutebrowser.keyinput.keyparser import PassthroughKeyParser
from qutebrowser.commands.managers import CommandManager, SearchManager
from qutebrowser.commands.exceptions import CommandError
from qutebrowser.config.iniparsers import ReadWriteConfigParser
from qutebrowser.config.lineparser import LineConfigParser
from qutebrowser.browser.cookies import CookieJar
@ -581,7 +583,10 @@ class QuteBrowser(QApplication):
else:
obj = dotted_getattr(self, instance)
handler = getattr(obj, func)
if count is not None:
handler(*args, count=count)
else:
handler(*args)
try:
if count is not None:
handler(*args, count=count)
else:
handler(*args)
except CommandError as e:
message.error(e)

View File

@ -35,6 +35,7 @@ import qutebrowser.utils.webelem as webelem
import qutebrowser.config.config as config
import qutebrowser.browser.hints as hints
from qutebrowser.utils.misc import shell_escape
from qutebrowser.commands.exceptions import CommandError
class CurCommandDispatcher(QObject):
@ -85,8 +86,7 @@ class CurCommandDispatcher(QObject):
widget = self._tabs.currentWidget()
frame = widget.page_.currentFrame()
if frame is None:
message.error("No frame focused!")
return
raise CommandError("No frame focused!")
widget.hintmanager.follow_prevnext(frame, widget.url(), prev, newtab)
@cmdutils.register(instance='mainwindow.tabs.cur', name='open',
@ -193,8 +193,7 @@ class CurCommandDispatcher(QObject):
count: How many pages to go back.
"""
for _ in range(count):
if not self._tabs.currentWidget().go_back():
break
self._tabs.currentWidget().go_back()
@cmdutils.register(instance='mainwindow.tabs.cur')
def forward(self, count=1):
@ -206,8 +205,7 @@ class CurCommandDispatcher(QObject):
count: How many pages to go forward.
"""
for _ in range(count):
if not self._tabs.currentWidget().go_forward():
break
self._tabs.currentWidget().go_forward()
@cmdutils.register(instance='mainwindow.tabs.cur')
def hint(self, groupstr='all', targetstr='normal'):
@ -222,18 +220,15 @@ class CurCommandDispatcher(QObject):
widget = self._tabs.currentWidget()
frame = widget.page_.mainFrame()
if frame is None:
message.error("No frame focused!")
return
raise CommandError("No frame focused!")
try:
group = getattr(webelem.Group, groupstr)
except AttributeError:
message.error("Unknown hinting group {}!".format(groupstr))
return
raise CommandError("Unknown hinting group {}!".format(groupstr))
try:
target = getattr(hints.Target, targetstr)
except AttributeError:
message.error("Unknown hinting target {}!".format(targetstr))
return
raise CommandError("Unknown hinting target {}!".format(targetstr))
widget.hintmanager.start(frame, widget.url(), group, target)
@cmdutils.register(instance='mainwindow.tabs.cur', hide=True)
@ -403,7 +398,7 @@ class CurCommandDispatcher(QObject):
try:
level = cmdutils.arg_or_count(zoom, count, default=100)
except ValueError as e:
message.error(e)
raise CommandError(e)
tab = self._tabs.currentWidget()
tab.zoom_perc(level)
@ -447,8 +442,7 @@ class CurCommandDispatcher(QObject):
elem = frame.findFirstElement(webelem.SELECTORS[
webelem.Group.editable_focused])
if elem.isNull():
message.error("No editable element focused!")
return
raise CommandError("No editable element focused!")
oshandle, filename = mkstemp(text=True)
text = elem.evaluateJavaScript('this.value')
if text:
@ -470,7 +464,7 @@ class CurCommandDispatcher(QObject):
try:
os.remove(filename)
except PermissionError:
message.error("Failed to delete tempfile...")
raise CommandError("Failed to delete tempfile...")
def on_editor_closed(self, elem, oshandle, filename, exitcode,
exitstatus):
@ -480,15 +474,13 @@ class CurCommandDispatcher(QObject):
"""
logging.debug("Editor closed")
if exitcode != 0:
message.error("Editor did quit abnormally (status {})!".format(
exitcode))
return
raise CommandError("Editor did quit abnormally (status "
"{})!".format(exitcode))
if exitstatus != QProcess.NormalExit:
# No error here, since we already handle this in on_editor_error
return
if elem.isNull():
message.error("Element vanished while editing!")
return
raise CommandError("Element vanished while editing!")
with open(filename, 'r', encoding='utf-8') as f:
text = ''.join(f.readlines())
text = webelem.javascript_escape(text)
@ -508,5 +500,6 @@ class CurCommandDispatcher(QObject):
"from the process."),
QProcess.UnknownError: "An unknown error occurred.",
}
message.error("Error while calling editor: {}".format(messages[error]))
self._editor_cleanup(oshandle, filename)
raise CommandError("Error while calling editor: {}".format(
messages[error]))

View File

@ -30,6 +30,7 @@ import qutebrowser.keyinput.modeman as modeman
import qutebrowser.utils.message as message
import qutebrowser.utils.url as urlutils
import qutebrowser.utils.webelem as webelem
from qutebrowser.commands.exceptions import CommandError
from qutebrowser.utils.usertypes import enum
@ -371,14 +372,12 @@ class HintManager(QObject):
"""
elem = self._find_prevnext(frame, prev)
if elem is None:
message.error("No {} links found!".format("prev" if prev
else "forward"))
return
raise CommandError("No {} links found!".format(
"prev" if prev else "forward"))
link = self._resolve_link(elem, baseurl)
if link is None:
message.error("No {} links found!".format("prev" if prev
else "forward"))
return
raise CommandError("No {} links found!".format(
"prev" if prev else "forward"))
self.openurl.emit(link, newtab)
def start(self, mainframe, baseurl, group=webelem.Group.all,
@ -406,8 +405,7 @@ class HintManager(QObject):
visible_elems = [e for e in elems if filterfunc(e) and
webelem.is_visible(e, mainframe)]
if not visible_elems:
message.error("No elements found.")
return
raise CommandError("No elements found.")
self._target = target
self._baseurl = baseurl
self._frames = webelem.get_child_frames(mainframe)
@ -498,8 +496,7 @@ class HintManager(QObject):
elif self._target in link_handlers:
link = self._resolve_link(elem)
if link is None:
message.error("No suitable link found for this element.")
return
raise CommandError("No suitable link found for this element.")
link_handlers[self._target](link)
else:
raise ValueError("No suitable handler found!")
@ -509,8 +506,7 @@ class HintManager(QObject):
def follow_hint(self):
"""Follow the currently selected hint."""
if not self._to_follow:
message.error("No hint to follow")
return
raise CommandError("No hint to follow")
self.fire(self._to_follow, force=True)
@pyqtSlot('QSize')

View File

@ -19,8 +19,8 @@
import logging
from qutebrowser.commands._exceptions import (ArgumentCountError,
PrerequisitesError)
from qutebrowser.commands.exceptions import (ArgumentCountError,
PrerequisitesError)
from PyQt5.QtCore import pyqtSignal, QObject, QCoreApplication
from PyQt5.QtWebKit import QWebSettings
@ -114,6 +114,8 @@ class Command(QObject):
def run(self, args=None, count=None):
"""Run the command.
Note we don't catch CommandError here as it might happen async.
Args:
args: Arguments to the command.
count: Command repetition count.

View File

@ -23,24 +23,31 @@ Defined here to avoid circular dependency hell.
class CommandError(Exception):
"""Common base class for all command exceptions."""
"""Raised when a command encounters a error while running."""
pass
class NoSuchCommandError(CommandError):
class CommandMetaError(Exception):
"""Common base class for exceptions occuring before a command is run."""
class NoSuchCommandError(CommandMetaError):
"""Raised when a command wasn't found."""
pass
class ArgumentCountError(CommandError):
class ArgumentCountError(CommandMetaError):
"""Raised when a command was called with an invalid count of arguments."""
pass
class PrerequisitesError(CommandError):
class PrerequisitesError(CommandMetaError):
"""Raised when a cmd can't be used because some prerequisites aren't met.

View File

@ -25,7 +25,8 @@ from PyQt5.QtWebKitWidgets import QWebPage
import qutebrowser.config.config as config
import qutebrowser.commands.utils as cmdutils
import qutebrowser.utils.message as message
from qutebrowser.commands._exceptions import NoSuchCommandError, CommandError
from qutebrowser.commands.exceptions import (NoSuchCommandError,
CommandMetaError, CommandError)
from qutebrowser.utils.misc import safe_shlex_split
@ -227,5 +228,5 @@ class CommandManager:
"""Run a command and display exceptions in the statusbar."""
try:
self.run(text, count)
except CommandError as e:
message.error(str(e))
except (CommandMetaError, CommandError) as e:
message.error(e)

View File

@ -37,6 +37,7 @@ import qutebrowser.commands.utils as cmdutils
import qutebrowser.utils.message as message
from qutebrowser.config.iniparsers import ReadConfigParser
from qutebrowser.config._conftypes import ValidationError
from qutebrowser.commands.exceptions import CommandError
def instance():
@ -258,7 +259,7 @@ class ConfigManager(QObject):
try:
val = self.get(sectname, optname, transformed=False)
except (NoOptionError, NoSectionError) as e:
message.error("get: {} - {}".format(e.__class__.__name__, e))
raise CommandError("get: {} - {}".format(e.__class__.__name__, e))
else:
message.info("{} {} = {}".format(sectname, optname, val))

View File

@ -23,7 +23,8 @@ from qutebrowser.keyinput._basekeyparser import BaseKeyParser
import qutebrowser.utils.message as message
from qutebrowser.commands.managers import CommandManager
from qutebrowser.commands._exceptions import ArgumentCountError, CommandError
from qutebrowser.commands.exceptions import (
ArgumentCountError, CommandMetaError, CommandError)
class CommandKeyParser(BaseKeyParser):
@ -52,8 +53,8 @@ class CommandKeyParser(BaseKeyParser):
logging.debug("Filling statusbar with partial command {}".format(
cmdstr))
message.set_cmd_text(':{} '.format(cmdstr))
except CommandError as e:
message.error(str(e))
except (CommandMetaError, CommandError) as e:
message.error(e)
def execute(self, cmdstr, _keytype, count=None):
self._run_or_fill(cmdstr, count)

View File

@ -63,6 +63,7 @@ class NetworkManager(QNetworkAccessManager):
if config.get('network', 'ssl-strict'):
return
for err in errors:
# FIXME we might want to use warn here (non-fatal error)
message.error('SSL error: {}'.format(err.errorString()))
reply.ignoreSslErrors()

View File

@ -29,18 +29,21 @@ def instance():
def error(message):
"""Display an error message in the statusbar."""
message = str(message)
logging.error(message)
instance().error.emit(message)
def info(message):
"""Display a temporary info message in the statusbar."""
message = str(message)
logging.info(message)
instance().info.emit(message)
def text(message):
"""Display a persistent message in the statusbar."""
message = str(message)
logging.debug(message)
instance().text.emit(message)

View File

@ -32,6 +32,7 @@ from qutebrowser.widgets._tabwidget import TabWidget, EmptyTabIcon
from qutebrowser.widgets.webview import WebView
from qutebrowser.browser.signalfilter import SignalFilter
from qutebrowser.browser.curcommand import CurCommandDispatcher
from qutebrowser.commands.exceptions import CommandError
class TabbedBrowser(TabWidget):
@ -337,7 +338,7 @@ class TabbedBrowser(TabWidget):
if self._url_stack:
self.tabopen(self._url_stack.pop())
else:
message.error("Nothing to undo!")
raise CommandError("Nothing to undo!")
@cmdutils.register(instance='mainwindow.tabs', name='tabprev')
def switch_prev(self, count=1):
@ -354,7 +355,7 @@ class TabbedBrowser(TabWidget):
elif config.get('tabbar', 'wrap'):
self.setCurrentIndex(newidx % self.count())
else:
message.error("First tab")
raise CommandError("First tab")
@cmdutils.register(instance='mainwindow.tabs', name='tabnext')
def switch_next(self, count=1):
@ -371,7 +372,7 @@ class TabbedBrowser(TabWidget):
elif config.get('tabbar', 'wrap'):
self.setCurrentIndex(newidx % self.count())
else:
message.error("Last tab")
raise CommandError("Last tab")
@cmdutils.register(instance='mainwindow.tabs', nargs=(0, 1))
def paste(self, sel=False, tab=False):
@ -387,8 +388,7 @@ class TabbedBrowser(TabWidget):
mode = QClipboard.Selection if sel else QClipboard.Clipboard
url = clip.text(mode)
if not url:
message.error("Clipboard is empty.")
return
raise CommandError("Clipboard is empty.")
logging.debug("Clipboard contained: '{}'".format(url))
if tab:
self.tabopen(url)
@ -417,13 +417,11 @@ class TabbedBrowser(TabWidget):
idx = cmdutils.arg_or_count(index, count, default=1,
countzero=self.count())
except ValueError as e:
message.error(e)
return
raise CommandError(e)
if 1 <= idx <= self.count():
self.setCurrentIndex(idx - 1)
else:
message.error("There's no tab with index {}!".format(idx))
return
raise CommandError("There's no tab with index {}!".format(idx))
@cmdutils.register(instance='mainwindow.tabs')
def tab_move(self, direction=None, count=None):
@ -440,14 +438,12 @@ class TabbedBrowser(TabWidget):
try:
new_idx = self._tab_move_relative(direction, count)
except ValueError:
message.error("Count must be given for relative moving!")
return
raise CommandError("Count must be given for relative moving!")
else:
message.error("Invalid direction '{}'!".format(direction))
return
raise CommandError("Invalid direction '{}'!".format(direction))
if not 0 <= new_idx < self.count():
message.error("Can't move tab to position {}!".format(new_idx))
return
raise CommandError("Can't move tab to position {}!".format(
new_idx))
tab = self.currentWidget()
cur_idx = self.currentIndex()
icon = self.tabIcon(cur_idx)
@ -461,8 +457,7 @@ class TabbedBrowser(TabWidget):
"""Select the tab which was last focused."""
idx = self.indexOf(self.last_focused)
if idx == -1:
message.error("Last focused tab vanished!")
return
raise CommandError("Last focused tab vanished!")
self.setCurrentIndex(idx)
@pyqtSlot(str, str)

View File

@ -34,6 +34,7 @@ from qutebrowser.browser.webpage import BrowserPage
from qutebrowser.browser.hints import HintManager
from qutebrowser.utils.signals import SignalCache
from qutebrowser.utils.usertypes import NeighborList, enum
from qutebrowser.commands.exceptions import CommandError
Target = enum('normal', 'tab', 'bgtab')
@ -165,8 +166,7 @@ class WebView(QWebView):
try:
u = urlutils.fuzzy_url(url)
except urlutils.SearchEngineError as e:
message.error(str(e))
return
raise CommandError(e)
logging.debug("New title: {}".format(urlutils.urlstring(u)))
self.titleChanged.emit(urlutils.urlstring(u))
self.urlChanged.emit(urlutils.qurl(u))
@ -194,30 +194,18 @@ class WebView(QWebView):
self.zoom_perc(level, fuzzyval=False)
def go_back(self):
"""Go back a page in the history.
Return:
True if going back succeeded, False otherwise.
"""
"""Go back a page in the history."""
if self.page_.history().canGoBack():
self.back()
return True
else:
message.error("At beginning of history.")
return False
raise CommandError("At beginning of history.")
def go_forward(self):
"""Go forward a page in the history.
Return:
True if going forward succeeded, False otherwise.
"""
"""Go forward a page in the history."""
if self.page_.history().canGoForward():
self.forward()
return True
else:
message.error("At end of history.")
return False
raise CommandError("At end of history.")
def shutdown(self, callback=None):
"""Shut down the tab cleanly and remove it.
@ -385,11 +373,17 @@ class WebView(QWebView):
"""
if e.button() == Qt.XButton1:
# Back button on mice which have it.
self.go_back()
try:
self.go_back()
except CommandError as ex:
message.error(ex)
return super().mousePressEvent(e)
elif e.button() == Qt.XButton2:
# Forward button on mice which have it.
self.go_forward()
try:
self.go_forward()
except CommandError as ex:
message.error(ex)
return super().mousePressEvent(e)
pos = e.pos()
frame = self.page_.frameAt(pos)