parent
d1a4a028cd
commit
d5a1fff637
@ -43,7 +43,8 @@ import qutebrowser
|
|||||||
import qutebrowser.resources
|
import qutebrowser.resources
|
||||||
from qutebrowser.completion.models import miscmodels
|
from qutebrowser.completion.models import miscmodels
|
||||||
from qutebrowser.commands import cmdutils, runners, cmdexc
|
from qutebrowser.commands import cmdutils, runners, cmdexc
|
||||||
from qutebrowser.config import config, websettings, configexc, configfiles
|
from qutebrowser.config import (config, websettings, configexc, configfiles,
|
||||||
|
configinit)
|
||||||
from qutebrowser.browser import (urlmarks, adblock, history, browsertab,
|
from qutebrowser.browser import (urlmarks, adblock, history, browsertab,
|
||||||
downloads)
|
downloads)
|
||||||
from qutebrowser.browser.network import proxy
|
from qutebrowser.browser.network import proxy
|
||||||
@ -77,7 +78,7 @@ def run(args):
|
|||||||
standarddir.init(args)
|
standarddir.init(args)
|
||||||
|
|
||||||
log.init.debug("Initializing config...")
|
log.init.debug("Initializing config...")
|
||||||
config.early_init(args)
|
configinit.early_init(args)
|
||||||
|
|
||||||
global qApp
|
global qApp
|
||||||
qApp = Application(args)
|
qApp = Application(args)
|
||||||
@ -393,7 +394,7 @@ def _init_modules(args, crash_handler):
|
|||||||
log.init.debug("Initializing save manager...")
|
log.init.debug("Initializing save manager...")
|
||||||
save_manager = savemanager.SaveManager(qApp)
|
save_manager = savemanager.SaveManager(qApp)
|
||||||
objreg.register('save-manager', save_manager)
|
objreg.register('save-manager', save_manager)
|
||||||
config.late_init(save_manager)
|
configinit.late_init(save_manager)
|
||||||
|
|
||||||
log.init.debug("Initializing network...")
|
log.init.debug("Initializing network...")
|
||||||
networkmanager.init()
|
networkmanager.init()
|
||||||
@ -762,7 +763,7 @@ class Application(QApplication):
|
|||||||
"""
|
"""
|
||||||
self._last_focus_object = None
|
self._last_focus_object = None
|
||||||
|
|
||||||
qt_args = config.qt_args(args)
|
qt_args = configinit.qt_args(args)
|
||||||
log.init.debug("Qt arguments: {}, based on {}".format(qt_args, args))
|
log.init.debug("Qt arguments: {}, based on {}".format(qt_args, args))
|
||||||
super().__init__(qt_args)
|
super().__init__(qt_args)
|
||||||
|
|
||||||
|
@ -19,18 +19,15 @@
|
|||||||
|
|
||||||
"""Configuration storage and config-related utilities."""
|
"""Configuration storage and config-related utilities."""
|
||||||
|
|
||||||
import sys
|
|
||||||
import copy
|
import copy
|
||||||
import contextlib
|
import contextlib
|
||||||
import functools
|
import functools
|
||||||
|
|
||||||
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QUrl
|
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QUrl
|
||||||
from PyQt5.QtWidgets import QMessageBox
|
|
||||||
|
|
||||||
from qutebrowser.config import configdata, configexc, configtypes, configfiles
|
from qutebrowser.config import configdata, configexc, configtypes, configfiles
|
||||||
from qutebrowser.utils import (utils, objreg, message, log, usertypes, jinja,
|
from qutebrowser.utils import utils, objreg, message, log, jinja, qtutils
|
||||||
qtutils)
|
from qutebrowser.misc import objects
|
||||||
from qutebrowser.misc import objects, msgbox, earlyinit
|
|
||||||
from qutebrowser.commands import cmdexc, cmdutils
|
from qutebrowser.commands import cmdexc, cmdutils
|
||||||
from qutebrowser.completion.models import configmodel
|
from qutebrowser.completion.models import configmodel
|
||||||
|
|
||||||
@ -40,9 +37,7 @@ instance = None
|
|||||||
key_instance = None
|
key_instance = None
|
||||||
|
|
||||||
# Keeping track of all change filters to validate them later.
|
# Keeping track of all change filters to validate them later.
|
||||||
_change_filters = []
|
change_filters = []
|
||||||
# Errors which happened during init, so we can show a message box.
|
|
||||||
_init_errors = []
|
|
||||||
|
|
||||||
|
|
||||||
class change_filter: # pylint: disable=invalid-name
|
class change_filter: # pylint: disable=invalid-name
|
||||||
@ -68,7 +63,7 @@ class change_filter: # pylint: disable=invalid-name
|
|||||||
"""
|
"""
|
||||||
self._option = option
|
self._option = option
|
||||||
self._function = function
|
self._function = function
|
||||||
_change_filters.append(self)
|
change_filters.append(self)
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
"""Make sure the configured option or prefix exists.
|
"""Make sure the configured option or prefix exists.
|
||||||
@ -634,114 +629,3 @@ class StyleSheetObserver(QObject):
|
|||||||
self._obj.setStyleSheet(qss)
|
self._obj.setStyleSheet(qss)
|
||||||
if update:
|
if update:
|
||||||
instance.changed.connect(self._update_stylesheet)
|
instance.changed.connect(self._update_stylesheet)
|
||||||
|
|
||||||
|
|
||||||
def early_init(args):
|
|
||||||
"""Initialize the part of the config which works without a QApplication."""
|
|
||||||
configdata.init()
|
|
||||||
|
|
||||||
yaml_config = configfiles.YamlConfig()
|
|
||||||
|
|
||||||
global val, instance, key_instance
|
|
||||||
instance = Config(yaml_config=yaml_config)
|
|
||||||
val = ConfigContainer(instance)
|
|
||||||
key_instance = KeyConfig(instance)
|
|
||||||
|
|
||||||
for cf in _change_filters:
|
|
||||||
cf.validate()
|
|
||||||
|
|
||||||
configtypes.Font.monospace_fonts = val.fonts.monospace
|
|
||||||
|
|
||||||
config_commands = ConfigCommands(instance, key_instance)
|
|
||||||
objreg.register('config-commands', config_commands)
|
|
||||||
|
|
||||||
config_api = None
|
|
||||||
|
|
||||||
try:
|
|
||||||
config_api = configfiles.read_config_py()
|
|
||||||
# Raised here so we get the config_api back.
|
|
||||||
if config_api.errors:
|
|
||||||
raise configexc.ConfigFileErrors('config.py', config_api.errors)
|
|
||||||
except configexc.ConfigFileErrors as e:
|
|
||||||
log.config.exception("Error while loading config.py")
|
|
||||||
_init_errors.append(e)
|
|
||||||
|
|
||||||
try:
|
|
||||||
if getattr(config_api, 'load_autoconfig', True):
|
|
||||||
try:
|
|
||||||
instance.read_yaml()
|
|
||||||
except configexc.ConfigFileErrors as e:
|
|
||||||
raise # caught in outer block
|
|
||||||
except configexc.Error as e:
|
|
||||||
desc = configexc.ConfigErrorDesc("Error", e)
|
|
||||||
raise configexc.ConfigFileErrors('autoconfig.yml', [desc])
|
|
||||||
except configexc.ConfigFileErrors as e:
|
|
||||||
log.config.exception("Error while loading config.py")
|
|
||||||
_init_errors.append(e)
|
|
||||||
|
|
||||||
configfiles.init()
|
|
||||||
|
|
||||||
objects.backend = get_backend(args)
|
|
||||||
earlyinit.init_with_backend(objects.backend)
|
|
||||||
|
|
||||||
|
|
||||||
def get_backend(args):
|
|
||||||
"""Find out what backend to use based on available libraries."""
|
|
||||||
try:
|
|
||||||
import PyQt5.QtWebKit # pylint: disable=unused-variable
|
|
||||||
except ImportError:
|
|
||||||
webkit_available = False
|
|
||||||
else:
|
|
||||||
webkit_available = qtutils.is_new_qtwebkit()
|
|
||||||
|
|
||||||
str_to_backend = {
|
|
||||||
'webkit': usertypes.Backend.QtWebKit,
|
|
||||||
'webengine': usertypes.Backend.QtWebEngine,
|
|
||||||
}
|
|
||||||
|
|
||||||
if args.backend is not None:
|
|
||||||
return str_to_backend[args.backend]
|
|
||||||
elif val.backend != 'auto':
|
|
||||||
return str_to_backend[val.backend]
|
|
||||||
elif webkit_available:
|
|
||||||
return usertypes.Backend.QtWebKit
|
|
||||||
else:
|
|
||||||
return usertypes.Backend.QtWebEngine
|
|
||||||
|
|
||||||
|
|
||||||
def late_init(save_manager):
|
|
||||||
"""Initialize the rest of the config after the QApplication is created."""
|
|
||||||
global _init_errors
|
|
||||||
for err in _init_errors:
|
|
||||||
errbox = msgbox.msgbox(parent=None,
|
|
||||||
title="Error while reading config",
|
|
||||||
text=err.to_html(),
|
|
||||||
icon=QMessageBox.Warning,
|
|
||||||
plain_text=False)
|
|
||||||
errbox.exec_()
|
|
||||||
_init_errors = []
|
|
||||||
|
|
||||||
instance.init_save_manager(save_manager)
|
|
||||||
configfiles.state.init_save_manager(save_manager)
|
|
||||||
|
|
||||||
|
|
||||||
def qt_args(namespace):
|
|
||||||
"""Get the Qt QApplication arguments based on an argparse namespace.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
namespace: The argparse namespace.
|
|
||||||
|
|
||||||
Return:
|
|
||||||
The argv list to be passed to Qt.
|
|
||||||
"""
|
|
||||||
argv = [sys.argv[0]]
|
|
||||||
|
|
||||||
if namespace.qt_flag is not None:
|
|
||||||
argv += ['--' + flag[0] for flag in namespace.qt_flag]
|
|
||||||
|
|
||||||
if namespace.qt_arg is not None:
|
|
||||||
for name, value in namespace.qt_arg:
|
|
||||||
argv += ['--' + name, value]
|
|
||||||
|
|
||||||
argv += ['--' + arg for arg in val.qt_args]
|
|
||||||
return argv
|
|
||||||
|
144
qutebrowser/config/configinit.py
Normal file
144
qutebrowser/config/configinit.py
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||||
|
|
||||||
|
# Copyright 2017 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||||
|
#
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
"""Initialization of the configuration."""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import QMessageBox
|
||||||
|
|
||||||
|
from qutebrowser.config import (config, configdata, configfiles, configtypes,
|
||||||
|
configexc)
|
||||||
|
from qutebrowser.utils import objreg, qtutils, usertypes, log
|
||||||
|
from qutebrowser.misc import earlyinit, msgbox, objects
|
||||||
|
|
||||||
|
|
||||||
|
# Errors which happened during init, so we can show a message box.
|
||||||
|
_init_errors = []
|
||||||
|
|
||||||
|
|
||||||
|
def early_init(args):
|
||||||
|
"""Initialize the part of the config which works without a QApplication."""
|
||||||
|
configdata.init()
|
||||||
|
|
||||||
|
yaml_config = configfiles.YamlConfig()
|
||||||
|
|
||||||
|
config.instance = config.Config(yaml_config=yaml_config)
|
||||||
|
config.val = config.ConfigContainer(config.instance)
|
||||||
|
config.key_instance = config.KeyConfig(config.instance)
|
||||||
|
|
||||||
|
for cf in config.change_filters:
|
||||||
|
cf.validate()
|
||||||
|
|
||||||
|
configtypes.Font.monospace_fonts = config.val.fonts.monospace
|
||||||
|
|
||||||
|
config_commands = config.ConfigCommands(config.instance,
|
||||||
|
config.key_instance)
|
||||||
|
objreg.register('config-commands', config_commands)
|
||||||
|
|
||||||
|
config_api = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
config_api = configfiles.read_config_py()
|
||||||
|
# Raised here so we get the config_api back.
|
||||||
|
if config_api.errors:
|
||||||
|
raise configexc.ConfigFileErrors('config.py', config_api.errors)
|
||||||
|
except configexc.ConfigFileErrors as e:
|
||||||
|
log.config.exception("Error while loading config.py")
|
||||||
|
_init_errors.append(e)
|
||||||
|
|
||||||
|
try:
|
||||||
|
if getattr(config_api, 'load_autoconfig', True):
|
||||||
|
try:
|
||||||
|
config.instance.read_yaml()
|
||||||
|
except configexc.ConfigFileErrors as e:
|
||||||
|
raise # caught in outer block
|
||||||
|
except configexc.Error as e:
|
||||||
|
desc = configexc.ConfigErrorDesc("Error", e)
|
||||||
|
raise configexc.ConfigFileErrors('autoconfig.yml', [desc])
|
||||||
|
except configexc.ConfigFileErrors as e:
|
||||||
|
log.config.exception("Error while loading config.py")
|
||||||
|
_init_errors.append(e)
|
||||||
|
|
||||||
|
configfiles.init()
|
||||||
|
|
||||||
|
objects.backend = get_backend(args)
|
||||||
|
earlyinit.init_with_backend(objects.backend)
|
||||||
|
|
||||||
|
|
||||||
|
def get_backend(args):
|
||||||
|
"""Find out what backend to use based on available libraries."""
|
||||||
|
try:
|
||||||
|
import PyQt5.QtWebKit # pylint: disable=unused-variable
|
||||||
|
except ImportError:
|
||||||
|
webkit_available = False
|
||||||
|
else:
|
||||||
|
webkit_available = qtutils.is_new_qtwebkit()
|
||||||
|
|
||||||
|
str_to_backend = {
|
||||||
|
'webkit': usertypes.Backend.QtWebKit,
|
||||||
|
'webengine': usertypes.Backend.QtWebEngine,
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.backend is not None:
|
||||||
|
return str_to_backend[args.backend]
|
||||||
|
elif config.val.backend != 'auto':
|
||||||
|
return str_to_backend[config.val.backend]
|
||||||
|
elif webkit_available:
|
||||||
|
return usertypes.Backend.QtWebKit
|
||||||
|
else:
|
||||||
|
return usertypes.Backend.QtWebEngine
|
||||||
|
|
||||||
|
|
||||||
|
def late_init(save_manager):
|
||||||
|
"""Initialize the rest of the config after the QApplication is created."""
|
||||||
|
global _init_errors
|
||||||
|
for err in _init_errors:
|
||||||
|
errbox = msgbox.msgbox(parent=None,
|
||||||
|
title="Error while reading config",
|
||||||
|
text=err.to_html(),
|
||||||
|
icon=QMessageBox.Warning,
|
||||||
|
plain_text=False)
|
||||||
|
errbox.exec_()
|
||||||
|
_init_errors = []
|
||||||
|
|
||||||
|
config.instance.init_save_manager(save_manager)
|
||||||
|
configfiles.state.init_save_manager(save_manager)
|
||||||
|
|
||||||
|
|
||||||
|
def qt_args(namespace):
|
||||||
|
"""Get the Qt QApplication arguments based on an argparse namespace.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
namespace: The argparse namespace.
|
||||||
|
|
||||||
|
Return:
|
||||||
|
The argv list to be passed to Qt.
|
||||||
|
"""
|
||||||
|
argv = [sys.argv[0]]
|
||||||
|
|
||||||
|
if namespace.qt_flag is not None:
|
||||||
|
argv += ['--' + flag[0] for flag in namespace.qt_flag]
|
||||||
|
|
||||||
|
if namespace.qt_arg is not None:
|
||||||
|
for name, value in namespace.qt_arg:
|
||||||
|
argv += ['--' + name, value]
|
||||||
|
|
||||||
|
argv += ['--' + arg for arg in config.val.qt_args]
|
||||||
|
return argv
|
@ -18,19 +18,15 @@
|
|||||||
|
|
||||||
"""Tests for qutebrowser.config.config."""
|
"""Tests for qutebrowser.config.config."""
|
||||||
|
|
||||||
import sys
|
|
||||||
import copy
|
import copy
|
||||||
import types
|
import types
|
||||||
import logging
|
|
||||||
import unittest.mock
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from PyQt5.QtCore import QObject, QUrl
|
from PyQt5.QtCore import QObject, QUrl
|
||||||
from PyQt5.QtGui import QColor
|
from PyQt5.QtGui import QColor
|
||||||
|
|
||||||
from qutebrowser import qutebrowser
|
|
||||||
from qutebrowser.commands import cmdexc
|
from qutebrowser.commands import cmdexc
|
||||||
from qutebrowser.config import config, configdata, configexc, configfiles
|
from qutebrowser.config import config, configdata, configexc
|
||||||
from qutebrowser.utils import objreg, usertypes
|
from qutebrowser.utils import objreg, usertypes
|
||||||
from qutebrowser.misc import objects
|
from qutebrowser.misc import objects
|
||||||
|
|
||||||
@ -52,8 +48,8 @@ class TestChangeFilter:
|
|||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def cleanup_globals(self, monkeypatch):
|
def cleanup_globals(self, monkeypatch):
|
||||||
"""Make sure config._change_filters is cleaned up."""
|
"""Make sure config.change_filters is cleaned up."""
|
||||||
monkeypatch.setattr(config, '_change_filters', [])
|
monkeypatch.setattr(config, 'change_filters', [])
|
||||||
|
|
||||||
@pytest.mark.parametrize('option', ['foobar', 'tab', 'tabss', 'tabs.'])
|
@pytest.mark.parametrize('option', ['foobar', 'tab', 'tabss', 'tabs.'])
|
||||||
def test_unknown_option(self, option):
|
def test_unknown_option(self, option):
|
||||||
@ -65,7 +61,7 @@ class TestChangeFilter:
|
|||||||
def test_validate(self, option):
|
def test_validate(self, option):
|
||||||
cf = config.change_filter(option)
|
cf = config.change_filter(option)
|
||||||
cf.validate()
|
cf.validate()
|
||||||
assert cf in config._change_filters
|
assert cf in config.change_filters
|
||||||
|
|
||||||
@pytest.mark.parametrize('method', [True, False])
|
@pytest.mark.parametrize('method', [True, False])
|
||||||
@pytest.mark.parametrize('option, changed, matches', [
|
@pytest.mark.parametrize('option, changed, matches', [
|
||||||
@ -864,207 +860,3 @@ def test_set_register_stylesheet(delete, stylesheet_param, update, qtbot,
|
|||||||
expected = 'yellow'
|
expected = 'yellow'
|
||||||
|
|
||||||
assert obj.rendered_stylesheet == expected
|
assert obj.rendered_stylesheet == expected
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def init_patch(qapp, fake_save_manager, monkeypatch, config_tmpdir,
|
|
||||||
data_tmpdir):
|
|
||||||
monkeypatch.setattr(configdata, 'DATA', None)
|
|
||||||
monkeypatch.setattr(configfiles, 'state', None)
|
|
||||||
monkeypatch.setattr(config, 'instance', None)
|
|
||||||
monkeypatch.setattr(config, 'key_instance', None)
|
|
||||||
monkeypatch.setattr(config, '_change_filters', [])
|
|
||||||
monkeypatch.setattr(config, '_init_errors', [])
|
|
||||||
# Make sure we get no SSL warning
|
|
||||||
monkeypatch.setattr(config.earlyinit, 'check_backend_ssl_support',
|
|
||||||
lambda _backend: None)
|
|
||||||
yield
|
|
||||||
try:
|
|
||||||
objreg.delete('config-commands')
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('load_autoconfig', [True, False]) # noqa
|
|
||||||
@pytest.mark.parametrize('config_py', [True, 'error', False])
|
|
||||||
@pytest.mark.parametrize('invalid_yaml',
|
|
||||||
['42', 'unknown', 'wrong-type', False])
|
|
||||||
# pylint: disable=too-many-branches
|
|
||||||
def test_early_init(init_patch, config_tmpdir, caplog, fake_args,
|
|
||||||
load_autoconfig, config_py, invalid_yaml):
|
|
||||||
# Prepare files
|
|
||||||
autoconfig_file = config_tmpdir / 'autoconfig.yml'
|
|
||||||
config_py_file = config_tmpdir / 'config.py'
|
|
||||||
|
|
||||||
if invalid_yaml == '42':
|
|
||||||
text = '42'
|
|
||||||
elif invalid_yaml == 'unknown':
|
|
||||||
text = 'global:\n colors.foobar: magenta\n'
|
|
||||||
elif invalid_yaml == 'wrong-type':
|
|
||||||
text = 'global:\n tabs.position: true\n'
|
|
||||||
else:
|
|
||||||
assert not invalid_yaml
|
|
||||||
text = 'global:\n colors.hints.fg: magenta\n'
|
|
||||||
autoconfig_file.write_text(text, 'utf-8', ensure=True)
|
|
||||||
|
|
||||||
if config_py:
|
|
||||||
config_py_lines = ['c.colors.hints.bg = "red"']
|
|
||||||
if not load_autoconfig:
|
|
||||||
config_py_lines.append('config.load_autoconfig = False')
|
|
||||||
if config_py == 'error':
|
|
||||||
config_py_lines.append('c.foo = 42')
|
|
||||||
config_py_file.write_text('\n'.join(config_py_lines),
|
|
||||||
'utf-8', ensure=True)
|
|
||||||
|
|
||||||
with caplog.at_level(logging.ERROR):
|
|
||||||
config.early_init(fake_args)
|
|
||||||
|
|
||||||
# Check error messages
|
|
||||||
expected_errors = []
|
|
||||||
if config_py == 'error':
|
|
||||||
expected_errors.append(
|
|
||||||
"Errors occurred while reading config.py:\n"
|
|
||||||
" While setting 'foo': No option 'foo'")
|
|
||||||
if load_autoconfig or not config_py:
|
|
||||||
error = "Errors occurred while reading autoconfig.yml:\n"
|
|
||||||
if invalid_yaml == '42':
|
|
||||||
error += " While loading data: Toplevel object is not a dict"
|
|
||||||
expected_errors.append(error)
|
|
||||||
elif invalid_yaml == 'wrong-type':
|
|
||||||
error += (" Error: Invalid value 'True' - expected a value of "
|
|
||||||
"type str but got bool.")
|
|
||||||
expected_errors.append(error)
|
|
||||||
|
|
||||||
actual_errors = [str(err) for err in config._init_errors]
|
|
||||||
assert actual_errors == expected_errors
|
|
||||||
|
|
||||||
# Make sure things have been init'ed
|
|
||||||
objreg.get('config-commands')
|
|
||||||
assert isinstance(config.instance, config.Config)
|
|
||||||
assert isinstance(config.key_instance, config.KeyConfig)
|
|
||||||
|
|
||||||
# Check config values
|
|
||||||
if config_py and load_autoconfig and not invalid_yaml:
|
|
||||||
assert config.instance._values == {
|
|
||||||
'colors.hints.bg': 'red',
|
|
||||||
'colors.hints.fg': 'magenta',
|
|
||||||
}
|
|
||||||
elif config_py:
|
|
||||||
assert config.instance._values == {'colors.hints.bg': 'red'}
|
|
||||||
elif invalid_yaml:
|
|
||||||
assert config.instance._values == {}
|
|
||||||
else:
|
|
||||||
assert config.instance._values == {'colors.hints.fg': 'magenta'}
|
|
||||||
|
|
||||||
|
|
||||||
def test_early_init_invalid_change_filter(init_patch, fake_args):
|
|
||||||
config.change_filter('foobar')
|
|
||||||
with pytest.raises(configexc.NoOptionError):
|
|
||||||
config.early_init(fake_args)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('errors', [True, False])
|
|
||||||
def test_late_init(init_patch, monkeypatch, fake_save_manager, fake_args,
|
|
||||||
mocker, errors):
|
|
||||||
config.early_init(fake_args)
|
|
||||||
if errors:
|
|
||||||
err = configexc.ConfigErrorDesc("Error text", Exception("Exception"))
|
|
||||||
errs = configexc.ConfigFileErrors("config.py", [err])
|
|
||||||
monkeypatch.setattr(config, '_init_errors', [errs])
|
|
||||||
msgbox_mock = mocker.patch('qutebrowser.config.config.msgbox.msgbox',
|
|
||||||
autospec=True)
|
|
||||||
|
|
||||||
config.late_init(fake_save_manager)
|
|
||||||
|
|
||||||
fake_save_manager.add_saveable.assert_any_call(
|
|
||||||
'state-config', unittest.mock.ANY)
|
|
||||||
fake_save_manager.add_saveable.assert_any_call(
|
|
||||||
'yaml-config', unittest.mock.ANY)
|
|
||||||
if errors:
|
|
||||||
assert len(msgbox_mock.call_args_list) == 1
|
|
||||||
_call_posargs, call_kwargs = msgbox_mock.call_args_list[0]
|
|
||||||
text = call_kwargs['text'].strip()
|
|
||||||
assert text.startswith('Errors occurred while reading config.py:')
|
|
||||||
assert '<b>Error text</b>: Exception' in text
|
|
||||||
else:
|
|
||||||
assert not msgbox_mock.called
|
|
||||||
|
|
||||||
|
|
||||||
class TestQtArgs:
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def parser(self, mocker):
|
|
||||||
"""Fixture to provide an argparser.
|
|
||||||
|
|
||||||
Monkey-patches .exit() of the argparser so it doesn't exit on errors.
|
|
||||||
"""
|
|
||||||
parser = qutebrowser.get_argparser()
|
|
||||||
mocker.patch.object(parser, 'exit', side_effect=Exception)
|
|
||||||
return parser
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('args, expected', [
|
|
||||||
# No Qt arguments
|
|
||||||
(['--debug'], [sys.argv[0]]),
|
|
||||||
# Qt flag
|
|
||||||
(['--debug', '--qt-flag', 'reverse'], [sys.argv[0], '--reverse']),
|
|
||||||
# Qt argument with value
|
|
||||||
(['--qt-arg', 'stylesheet', 'foo'],
|
|
||||||
[sys.argv[0], '--stylesheet', 'foo']),
|
|
||||||
# --qt-arg given twice
|
|
||||||
(['--qt-arg', 'stylesheet', 'foo', '--qt-arg', 'geometry', 'bar'],
|
|
||||||
[sys.argv[0], '--stylesheet', 'foo', '--geometry', 'bar']),
|
|
||||||
# --qt-flag given twice
|
|
||||||
(['--qt-flag', 'foo', '--qt-flag', 'bar'],
|
|
||||||
[sys.argv[0], '--foo', '--bar']),
|
|
||||||
])
|
|
||||||
def test_qt_args(self, config_stub, args, expected, parser):
|
|
||||||
"""Test commandline with no Qt arguments given."""
|
|
||||||
parsed = parser.parse_args(args)
|
|
||||||
assert config.qt_args(parsed) == expected
|
|
||||||
|
|
||||||
def test_qt_both(self, config_stub, parser):
|
|
||||||
"""Test commandline with a Qt argument and flag."""
|
|
||||||
args = parser.parse_args(['--qt-arg', 'stylesheet', 'foobar',
|
|
||||||
'--qt-flag', 'reverse'])
|
|
||||||
qt_args = config.qt_args(args)
|
|
||||||
assert qt_args[0] == sys.argv[0]
|
|
||||||
assert '--reverse' in qt_args
|
|
||||||
assert '--stylesheet' in qt_args
|
|
||||||
assert 'foobar' in qt_args
|
|
||||||
|
|
||||||
def test_with_settings(self, config_stub, parser):
|
|
||||||
parsed = parser.parse_args(['--qt-flag', 'foo'])
|
|
||||||
config_stub.val.qt_args = ['bar']
|
|
||||||
assert config.qt_args(parsed) == [sys.argv[0], '--foo', '--bar']
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('arg, confval, can_import, is_new_webkit, used', [
|
|
||||||
# overridden by commandline arg
|
|
||||||
('webkit', 'auto', False, False, usertypes.Backend.QtWebKit),
|
|
||||||
# overridden by config
|
|
||||||
(None, 'webkit', False, False, usertypes.Backend.QtWebKit),
|
|
||||||
# WebKit available but too old
|
|
||||||
(None, 'auto', True, False, usertypes.Backend.QtWebEngine),
|
|
||||||
# WebKit available and new
|
|
||||||
(None, 'auto', True, True, usertypes.Backend.QtWebKit),
|
|
||||||
# WebKit unavailable
|
|
||||||
(None, 'auto', False, False, usertypes.Backend.QtWebEngine),
|
|
||||||
])
|
|
||||||
def test_get_backend(monkeypatch, fake_args, config_stub,
|
|
||||||
arg, confval, can_import, is_new_webkit, used):
|
|
||||||
real_import = __import__
|
|
||||||
|
|
||||||
def fake_import(name, *args, **kwargs):
|
|
||||||
if name != 'PyQt5.QtWebKit':
|
|
||||||
return real_import(name, *args, **kwargs)
|
|
||||||
if can_import:
|
|
||||||
return None
|
|
||||||
raise ImportError
|
|
||||||
|
|
||||||
fake_args.backend = arg
|
|
||||||
config_stub.val.backend = confval
|
|
||||||
monkeypatch.setattr(config.qtutils, 'is_new_qtwebkit',
|
|
||||||
lambda: is_new_webkit)
|
|
||||||
monkeypatch.setattr('builtins.__import__', fake_import)
|
|
||||||
|
|
||||||
assert config.get_backend(fake_args) == used
|
|
||||||
|
234
tests/unit/config/test_configinit.py
Normal file
234
tests/unit/config/test_configinit.py
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||||
|
# Copyright 2017 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||||
|
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
"""Tests for qutebrowser.config.configinit."""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import logging
|
||||||
|
import unittest.mock
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from qutebrowser import qutebrowser
|
||||||
|
from qutebrowser.config import (config, configdata, configexc, configfiles,
|
||||||
|
configinit)
|
||||||
|
from qutebrowser.utils import objreg, usertypes
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def init_patch(qapp, fake_save_manager, monkeypatch, config_tmpdir,
|
||||||
|
data_tmpdir):
|
||||||
|
monkeypatch.setattr(configdata, 'DATA', None)
|
||||||
|
monkeypatch.setattr(configfiles, 'state', None)
|
||||||
|
monkeypatch.setattr(config, 'instance', None)
|
||||||
|
monkeypatch.setattr(config, 'key_instance', None)
|
||||||
|
monkeypatch.setattr(config, 'change_filters', [])
|
||||||
|
monkeypatch.setattr(configinit, '_init_errors', [])
|
||||||
|
# Make sure we get no SSL warning
|
||||||
|
monkeypatch.setattr(configinit.earlyinit, 'check_backend_ssl_support',
|
||||||
|
lambda _backend: None)
|
||||||
|
yield
|
||||||
|
try:
|
||||||
|
objreg.delete('config-commands')
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('load_autoconfig', [True, False]) # noqa
|
||||||
|
@pytest.mark.parametrize('config_py', [True, 'error', False])
|
||||||
|
@pytest.mark.parametrize('invalid_yaml',
|
||||||
|
['42', 'unknown', 'wrong-type', False])
|
||||||
|
# pylint: disable=too-many-branches
|
||||||
|
def test_early_init(init_patch, config_tmpdir, caplog, fake_args,
|
||||||
|
load_autoconfig, config_py, invalid_yaml):
|
||||||
|
# Prepare files
|
||||||
|
autoconfig_file = config_tmpdir / 'autoconfig.yml'
|
||||||
|
config_py_file = config_tmpdir / 'config.py'
|
||||||
|
|
||||||
|
if invalid_yaml == '42':
|
||||||
|
text = '42'
|
||||||
|
elif invalid_yaml == 'unknown':
|
||||||
|
text = 'global:\n colors.foobar: magenta\n'
|
||||||
|
elif invalid_yaml == 'wrong-type':
|
||||||
|
text = 'global:\n tabs.position: true\n'
|
||||||
|
else:
|
||||||
|
assert not invalid_yaml
|
||||||
|
text = 'global:\n colors.hints.fg: magenta\n'
|
||||||
|
autoconfig_file.write_text(text, 'utf-8', ensure=True)
|
||||||
|
|
||||||
|
if config_py:
|
||||||
|
config_py_lines = ['c.colors.hints.bg = "red"']
|
||||||
|
if not load_autoconfig:
|
||||||
|
config_py_lines.append('config.load_autoconfig = False')
|
||||||
|
if config_py == 'error':
|
||||||
|
config_py_lines.append('c.foo = 42')
|
||||||
|
config_py_file.write_text('\n'.join(config_py_lines),
|
||||||
|
'utf-8', ensure=True)
|
||||||
|
|
||||||
|
with caplog.at_level(logging.ERROR):
|
||||||
|
configinit.early_init(fake_args)
|
||||||
|
|
||||||
|
# Check error messages
|
||||||
|
expected_errors = []
|
||||||
|
if config_py == 'error':
|
||||||
|
expected_errors.append(
|
||||||
|
"Errors occurred while reading config.py:\n"
|
||||||
|
" While setting 'foo': No option 'foo'")
|
||||||
|
if load_autoconfig or not config_py:
|
||||||
|
error = "Errors occurred while reading autoconfig.yml:\n"
|
||||||
|
if invalid_yaml == '42':
|
||||||
|
error += " While loading data: Toplevel object is not a dict"
|
||||||
|
expected_errors.append(error)
|
||||||
|
elif invalid_yaml == 'wrong-type':
|
||||||
|
error += (" Error: Invalid value 'True' - expected a value of "
|
||||||
|
"type str but got bool.")
|
||||||
|
expected_errors.append(error)
|
||||||
|
|
||||||
|
actual_errors = [str(err) for err in configinit._init_errors]
|
||||||
|
assert actual_errors == expected_errors
|
||||||
|
|
||||||
|
# Make sure things have been init'ed
|
||||||
|
objreg.get('config-commands')
|
||||||
|
assert isinstance(config.instance, config.Config)
|
||||||
|
assert isinstance(config.key_instance, config.KeyConfig)
|
||||||
|
|
||||||
|
# Check config values
|
||||||
|
if config_py and load_autoconfig and not invalid_yaml:
|
||||||
|
assert config.instance._values == {
|
||||||
|
'colors.hints.bg': 'red',
|
||||||
|
'colors.hints.fg': 'magenta',
|
||||||
|
}
|
||||||
|
elif config_py:
|
||||||
|
assert config.instance._values == {'colors.hints.bg': 'red'}
|
||||||
|
elif invalid_yaml:
|
||||||
|
assert config.instance._values == {}
|
||||||
|
else:
|
||||||
|
assert config.instance._values == {'colors.hints.fg': 'magenta'}
|
||||||
|
|
||||||
|
|
||||||
|
def test_early_init_invalid_change_filter(init_patch, fake_args):
|
||||||
|
config.change_filter('foobar')
|
||||||
|
with pytest.raises(configexc.NoOptionError):
|
||||||
|
configinit.early_init(fake_args)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('errors', [True, False])
|
||||||
|
def test_late_init(init_patch, monkeypatch, fake_save_manager, fake_args,
|
||||||
|
mocker, errors):
|
||||||
|
configinit.early_init(fake_args)
|
||||||
|
if errors:
|
||||||
|
err = configexc.ConfigErrorDesc("Error text", Exception("Exception"))
|
||||||
|
errs = configexc.ConfigFileErrors("config.py", [err])
|
||||||
|
monkeypatch.setattr(configinit, '_init_errors', [errs])
|
||||||
|
msgbox_mock = mocker.patch('qutebrowser.config.configinit.msgbox.msgbox',
|
||||||
|
autospec=True)
|
||||||
|
|
||||||
|
configinit.late_init(fake_save_manager)
|
||||||
|
|
||||||
|
fake_save_manager.add_saveable.assert_any_call(
|
||||||
|
'state-config', unittest.mock.ANY)
|
||||||
|
fake_save_manager.add_saveable.assert_any_call(
|
||||||
|
'yaml-config', unittest.mock.ANY)
|
||||||
|
if errors:
|
||||||
|
assert len(msgbox_mock.call_args_list) == 1
|
||||||
|
_call_posargs, call_kwargs = msgbox_mock.call_args_list[0]
|
||||||
|
text = call_kwargs['text'].strip()
|
||||||
|
assert text.startswith('Errors occurred while reading config.py:')
|
||||||
|
assert '<b>Error text</b>: Exception' in text
|
||||||
|
else:
|
||||||
|
assert not msgbox_mock.called
|
||||||
|
|
||||||
|
|
||||||
|
class TestQtArgs:
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def parser(self, mocker):
|
||||||
|
"""Fixture to provide an argparser.
|
||||||
|
|
||||||
|
Monkey-patches .exit() of the argparser so it doesn't exit on errors.
|
||||||
|
"""
|
||||||
|
parser = qutebrowser.get_argparser()
|
||||||
|
mocker.patch.object(parser, 'exit', side_effect=Exception)
|
||||||
|
return parser
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('args, expected', [
|
||||||
|
# No Qt arguments
|
||||||
|
(['--debug'], [sys.argv[0]]),
|
||||||
|
# Qt flag
|
||||||
|
(['--debug', '--qt-flag', 'reverse'], [sys.argv[0], '--reverse']),
|
||||||
|
# Qt argument with value
|
||||||
|
(['--qt-arg', 'stylesheet', 'foo'],
|
||||||
|
[sys.argv[0], '--stylesheet', 'foo']),
|
||||||
|
# --qt-arg given twice
|
||||||
|
(['--qt-arg', 'stylesheet', 'foo', '--qt-arg', 'geometry', 'bar'],
|
||||||
|
[sys.argv[0], '--stylesheet', 'foo', '--geometry', 'bar']),
|
||||||
|
# --qt-flag given twice
|
||||||
|
(['--qt-flag', 'foo', '--qt-flag', 'bar'],
|
||||||
|
[sys.argv[0], '--foo', '--bar']),
|
||||||
|
])
|
||||||
|
def test_qt_args(self, config_stub, args, expected, parser):
|
||||||
|
"""Test commandline with no Qt arguments given."""
|
||||||
|
parsed = parser.parse_args(args)
|
||||||
|
assert configinit.qt_args(parsed) == expected
|
||||||
|
|
||||||
|
def test_qt_both(self, config_stub, parser):
|
||||||
|
"""Test commandline with a Qt argument and flag."""
|
||||||
|
args = parser.parse_args(['--qt-arg', 'stylesheet', 'foobar',
|
||||||
|
'--qt-flag', 'reverse'])
|
||||||
|
qt_args = configinit.qt_args(args)
|
||||||
|
assert qt_args[0] == sys.argv[0]
|
||||||
|
assert '--reverse' in qt_args
|
||||||
|
assert '--stylesheet' in qt_args
|
||||||
|
assert 'foobar' in qt_args
|
||||||
|
|
||||||
|
def test_with_settings(self, config_stub, parser):
|
||||||
|
parsed = parser.parse_args(['--qt-flag', 'foo'])
|
||||||
|
config_stub.val.qt_args = ['bar']
|
||||||
|
assert configinit.qt_args(parsed) == [sys.argv[0], '--foo', '--bar']
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('arg, confval, can_import, is_new_webkit, used', [
|
||||||
|
# overridden by commandline arg
|
||||||
|
('webkit', 'auto', False, False, usertypes.Backend.QtWebKit),
|
||||||
|
# overridden by config
|
||||||
|
(None, 'webkit', False, False, usertypes.Backend.QtWebKit),
|
||||||
|
# WebKit available but too old
|
||||||
|
(None, 'auto', True, False, usertypes.Backend.QtWebEngine),
|
||||||
|
# WebKit available and new
|
||||||
|
(None, 'auto', True, True, usertypes.Backend.QtWebKit),
|
||||||
|
# WebKit unavailable
|
||||||
|
(None, 'auto', False, False, usertypes.Backend.QtWebEngine),
|
||||||
|
])
|
||||||
|
def test_get_backend(monkeypatch, fake_args, config_stub,
|
||||||
|
arg, confval, can_import, is_new_webkit, used):
|
||||||
|
real_import = __import__
|
||||||
|
|
||||||
|
def fake_import(name, *args, **kwargs):
|
||||||
|
if name != 'PyQt5.QtWebKit':
|
||||||
|
return real_import(name, *args, **kwargs)
|
||||||
|
if can_import:
|
||||||
|
return None
|
||||||
|
raise ImportError
|
||||||
|
|
||||||
|
fake_args.backend = arg
|
||||||
|
config_stub.val.backend = confval
|
||||||
|
monkeypatch.setattr(config.qtutils, 'is_new_qtwebkit',
|
||||||
|
lambda: is_new_webkit)
|
||||||
|
monkeypatch.setattr('builtins.__import__', fake_import)
|
||||||
|
|
||||||
|
assert configinit.get_backend(fake_args) == used
|
Loading…
Reference in New Issue
Block a user