diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 8cee8d45c..adf54c1b5 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -408,7 +408,7 @@ def _init_modules(args, crash_handler): objreg.register('readline-bridge', readline_bridge) log.init.debug("Initializing config...") - config.init(args, qApp) + config.init(qApp) save_manager.init_autosave() log.init.debug("Initializing sql...") diff --git a/qutebrowser/config/config.py b/qutebrowser/config/config.py index f8587e112..b05936473 100644 --- a/qutebrowser/config/config.py +++ b/qutebrowser/config/config.py @@ -24,10 +24,11 @@ import contextlib import functools from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QUrl +from PyQt5.QtWidgets import QApplication, QMessageBox from qutebrowser.config import configdata, configexc, configtypes, configfiles -from qutebrowser.utils import utils, objreg, message, log, usertypes, error -from qutebrowser.misc import objects +from qutebrowser.utils import utils, objreg, message, log, usertypes +from qutebrowser.misc import objects, msgbox from qutebrowser.commands import cmdexc, cmdutils from qutebrowser.completion.models import configmodel @@ -35,6 +36,7 @@ from qutebrowser.completion.models import configmodel val = None instance = None key_instance = None +_errbox = None # Keeping track of all change filters to validate them later. _change_filters = [] @@ -500,27 +502,28 @@ class ConfigContainer: Attributes: _config: The Config object. _prefix: The __getattr__ chain leading up to this object. - _confpy: If True, get values suitable for config.py and - do not raise exceptions. - _errors: If confpy=True is given, a list of configexc.ConfigErrorDesc. + _configapi: If given, get values suitable for config.py and + add errors to the given ConfigAPI object. """ - def __init__(self, config, confpy=False, prefix=''): + def __init__(self, config, configapi=None, prefix=''): self._config = config self._prefix = prefix - self._confpy = confpy - self._errors = [] + self._configapi = configapi def __repr__(self): return utils.get_repr(self, constructor=True, config=self._config, - confpy=self._confpy, prefix=self._prefix) + configapi=self._configapi, prefix=self._prefix) @contextlib.contextmanager def _handle_error(self, action, name): try: yield except configexc.Error as e: - self._errors.append(configexc.ConfigErrorDesc(action, name, e)) + if self._configapi is None: + raise + text = "While {} '{}'".format(action, name) + self._configapi.errors.append(configexc.ConfigErrorDesc(text, e)) def __getattr__(self, attr): """Get an option or a new ConfigContainer with the added prefix. @@ -536,14 +539,17 @@ class ConfigContainer: name = self._join(attr) if configdata.is_valid_prefix(name): - return ConfigContainer(config=self._config, confpy=self._confpy, + return ConfigContainer(config=self._config, + configapi=self._configapi, prefix=name) with self._handle_error('getting', name): - if self._confpy: - return self._config.get_obj(name) - else: + if self._configapi is None: + # access from Python code return self._config.get(name) + else: + # access from config.py + return self._config.get_obj(name) def __setattr__(self, attr, value): """Set the given option in the config.""" @@ -630,7 +636,7 @@ class StyleSheetObserver(QObject): instance.changed.connect(self._update_stylesheet) -def init(args, parent=None): +def init(parent=None): """Initialize the config. Args: @@ -655,9 +661,13 @@ def init(args, parent=None): try: config_api = configfiles.read_config_py() - except configexc.ConfigFileError as e: - error.handle_fatal_exc(e, args=args, - title="Error while loading config") + except configexc.ConfigFileErrors as e: + global _errbox + _errbox = msgbox.msgbox(parent=None, + title="Error while reading config", + text=e.to_html(), + icon=QMessageBox.Warning, + plain_text=False) config_api = None if getattr(config_api, 'load_autoconfig', True): diff --git a/qutebrowser/config/configexc.py b/qutebrowser/config/configexc.py index f83b4e371..57a32b4e5 100644 --- a/qutebrowser/config/configexc.py +++ b/qutebrowser/config/configexc.py @@ -20,7 +20,7 @@ """Exceptions related to config parsing.""" -import traceback +from qutebrowser.utils import jinja class Error(Exception): @@ -75,49 +75,46 @@ class NoOptionError(Error): self.option = option -class ConfigFileError(Error): - - """Raised when there was an error while loading a config file.""" - - def __init__(self, basename, msg): - super().__init__("Failed to load {}: {}".format(basename, msg)) - - -class ConfigFileUnhandledException(ConfigFileError): - - """Raised when there was an unhandled exception while loading config.py. - - Needs to be raised from an exception handler. - """ - - def __init__(self, basename): - super().__init__(basename, "Unhandled exception\n\n{}".format( - traceback.format_exc())) - - class ConfigErrorDesc: """A description of an error happening while reading the config. Attributes: - _action: What action has been taken, e.g 'set' - _name: The option which was set, or the key which was bound. - _exception: The exception which happened. + text: The text to show. + exception: The exception which happened. + traceback: The formatted traceback of the exception. """ - def __init__(self, action, name, exception): - self._action = action - self._exception = exception - self._name = name - - def __str__(self): - return "While {} {}: {}".format( - self._action, self._name, self._exception) + def __init__(self, text, exception, traceback=None): + self.text = text + self.exception = exception + self.traceback = traceback -class ConfigFileErrors(ConfigFileError): +class ConfigFileErrors(Error): """Raised when multiple errors occurred inside the config.""" def __init__(self, basename, errors): - super().__init__(basename, "\n\n".join(str(err) for err in errors)) + super().__init__("Errors occurred while reading {}".format(basename)) + self.basename = basename + self.errors = errors + + def to_html(self): + template = jinja.environment.from_string(""" + Errors occurred while reading {{ basename }}: + +
+ """.rstrip() + "\n{{ error.traceback }}" + """ ++ {% endif %} +