Add automatic backend selection in earlyinit

This commit is contained in:
Florian Bruhin 2017-02-05 17:09:04 +01:00
parent 7fe86b196d
commit b1a95a3930
20 changed files with 129 additions and 63 deletions

View File

@ -30,6 +30,7 @@ Changed
- New way of clicking hints with which solves various small issues
- When yanking a mailto: link via hints, the mailto: prefix is now stripped
- Zoom level messages are now not stacked on top of each other anymore.
- qutebrowser now automatically uses QtWebEngine if QtWebKit is unavailable
Fixed
-----

View File

@ -446,7 +446,7 @@ def _init_modules(args, crash_handler):
else:
os.environ.pop('QT_WAYLAND_DISABLE_WINDOWDECORATION', None)
# Init backend-specific stuff
browsertab.init(args)
browsertab.init()
def _init_late_modules(args):

View File

@ -28,7 +28,7 @@ from PyQt5.QtWidgets import QWidget, QApplication
from qutebrowser.keyinput import modeman
from qutebrowser.config import config
from qutebrowser.utils import utils, objreg, usertypes, log, qtutils
from qutebrowser.misc import miscwidgets
from qutebrowser.misc import miscwidgets, objects
from qutebrowser.browser import mouse, hints
@ -45,7 +45,7 @@ def create(win_id, parent=None):
# Importing modules here so we don't depend on QtWebEngine without the
# argument and to avoid circular imports.
mode_manager = modeman.instance(win_id)
if objreg.get('args').backend == 'webengine':
if objects.backend == usertypes.Backend.QtWebEngine:
from qutebrowser.browser.webengine import webenginetab
tab_class = webenginetab.WebEngineTab
else:
@ -54,9 +54,9 @@ def create(win_id, parent=None):
return tab_class(win_id=win_id, mode_manager=mode_manager, parent=parent)
def init(args):
def init():
"""Initialize backend-specific modules."""
if args.backend == 'webengine':
if objects.backend == usertypes.Backend.QtWebEngine:
from qutebrowser.browser.webengine import webenginetab
webenginetab.init()
else:

View File

@ -28,7 +28,7 @@ from qutebrowser.commands import cmdutils
from qutebrowser.utils import (utils, objreg, standarddir, log, qtutils,
usertypes)
from qutebrowser.config import config
from qutebrowser.misc import lineparser
from qutebrowser.misc import lineparser, objects
class Entry:
@ -293,7 +293,6 @@ def init(parent=None):
parent=parent)
objreg.register('web-history', history)
used_backend = usertypes.arg2backend[objreg.get('args').backend]
if used_backend == usertypes.Backend.QtWebKit:
if objects.backend == usertypes.Backend.QtWebKit:
from qutebrowser.browser.webkit import webkithistory
webkithistory.init(history)

View File

@ -24,8 +24,8 @@ import binascii
from PyQt5.QtWidgets import QWidget
from qutebrowser.utils import log, objreg
from qutebrowser.misc import miscwidgets
from qutebrowser.utils import log, objreg, usertypes
from qutebrowser.misc import miscwidgets, objects
def create(parent=None):
@ -36,7 +36,7 @@ def create(parent=None):
"""
# Importing modules here so we don't depend on QtWebEngine without the
# argument and to avoid circular imports.
if objreg.get('args').backend == 'webengine':
if objects.backend == usertypes.Backend.QtWebEngine:
from qutebrowser.browser.webengine import webengineinspector
return webengineinspector.WebEngineInspector(parent)
else:

View File

@ -28,7 +28,8 @@ import urllib.parse
import qutebrowser
from qutebrowser.utils import (version, utils, jinja, log, message, docutils,
objreg, usertypes)
objreg)
from qutebrowser.misc import objects
pyeval_output = ":pyeval was never called"
@ -89,8 +90,7 @@ class add_handler: # pylint: disable=invalid-name
return function
def wrapper(self, *args, **kwargs):
used_backend = usertypes.arg2backend[objreg.get('args').backend]
if self._backend is not None and used_backend != self._backend:
if self._backend is not None and objects.backend != self._backend:
return self.wrong_backend_handler(*args, **kwargs)
else:
return self._function(*args, **kwargs)

View File

@ -27,6 +27,7 @@ from qutebrowser.commands import cmdexc, argparser
from qutebrowser.utils import (log, utils, message, docutils, objreg,
usertypes, typing)
from qutebrowser.utils import debug as debug_utils
from qutebrowser.misc import objects
class ArgInfo:
@ -158,8 +159,7 @@ class Command:
window=win_id)
self.validate_mode(mode_manager.mode)
used_backend = usertypes.arg2backend[objreg.get('args').backend]
if self.backend is not None and used_backend != self.backend:
if self.backend is not None and objects.backend != self.backend:
raise cmdexc.PrerequisitesError(
"{}: Only available with {} "
"backend.".format(self.name, self.backend.name))

View File

@ -43,6 +43,7 @@ from qutebrowser.config.parsers import ini
from qutebrowser.commands import cmdexc, cmdutils
from qutebrowser.utils import (message, objreg, utils, standarddir, log,
qtutils, error, usertypes)
from qutebrowser.misc import objects
from qutebrowser.utils.usertypes import Completion
@ -882,10 +883,9 @@ class ConfigManager(QObject):
# Will be handled later in .setv()
pass
else:
backend = usertypes.arg2backend[objreg.get('args').backend]
if (allowed_backends is not None and
backend not in allowed_backends):
raise configexc.BackendError(backend)
objects.backend not in allowed_backends):
raise configexc.BackendError(objects.backend)
else:
interpolated = None

View File

@ -20,7 +20,8 @@
"""Bridge from QWeb(Engine)Settings to our own settings."""
from qutebrowser.config import config
from qutebrowser.utils import log, utils, debug, objreg
from qutebrowser.utils import log, utils, debug, objreg, usertypes
from qutebrowser.misc import objects
UNSET = object()
@ -261,7 +262,7 @@ def update_mappings(mappings, section, option):
def init(args):
"""Initialize all QWeb(Engine)Settings."""
if args.backend == 'webengine':
if objects.backend == usertypes.Backend.QtWebEngine:
from qutebrowser.browser.webengine import webenginesettings
webenginesettings.init(args)
else:
@ -271,7 +272,7 @@ def init(args):
def shutdown():
"""Shut down QWeb(Engine)Settings."""
if objreg.get('args').backend == 'webengine':
if objects.backend == usertypes.Backend.QtWebEngine:
from qutebrowser.browser.webengine import webenginesettings
webenginesettings.shutdown()
else:

View File

@ -28,6 +28,7 @@ from qutebrowser.keyinput import modeparsers, keyparser
from qutebrowser.config import config
from qutebrowser.commands import cmdexc, cmdutils
from qutebrowser.utils import usertypes, log, objreg, utils
from qutebrowser.misc import objects
class KeyEvent:
@ -266,13 +267,12 @@ class ModeManager(QObject):
except KeyError:
raise cmdexc.CommandError("Mode {} does not exist!".format(mode))
backend = usertypes.arg2backend[objreg.get('args').backend]
if m in [usertypes.KeyMode.hint, usertypes.KeyMode.command,
usertypes.KeyMode.yesno, usertypes.KeyMode.prompt]:
raise cmdexc.CommandError(
"Mode {} can't be entered manually!".format(mode))
elif (m == usertypes.KeyMode.caret and
backend == usertypes.Backend.QtWebEngine):
objects.backend == usertypes.Backend.QtWebEngine):
raise cmdexc.CommandError("Caret mode is not supported with "
"QtWebEngine yet.")

View File

@ -29,6 +29,7 @@ from PyQt5.QtGui import QIcon, QPalette, QColor
from qutebrowser.utils import qtutils, objreg, utils, usertypes, log
from qutebrowser.config import config
from qutebrowser.misc import objects
PixelMetrics = usertypes.enum('PixelMetrics', ['icon_padding'],
@ -124,7 +125,7 @@ class TabWidget(QTabWidget):
fields['title'] = page_title
fields['title_sep'] = ' - ' if page_title else ''
fields['perc_raw'] = tab.progress()
fields['backend'] = objreg.get('args').backend
fields['backend'] = objects.backend.name
if tab.load_status() == usertypes.LoadStatus.loading:
fields['perc'] = '[{}%] '.format(tab.progress())

View File

@ -239,7 +239,27 @@ def check_pyqt_core():
sys.exit(1)
def check_qt_version(args):
def get_backend(args):
"""Find out what backend to use based on available libraries.
Note this function returns the backend as a string so we don't have to
import qutebrowser.utils.usertypes yet.
"""
try:
import PyQt5.QtWebKit
webkit_available = True
except ImportError:
webkit_available = False
if args.backend is not None:
return args.backend
elif webkit_available:
return 'webkit'
else:
return 'webengine'
def check_qt_version(backend):
"""Check if the Qt version is recent enough."""
from PyQt5.QtCore import qVersion
from qutebrowser.utils import qtutils
@ -247,8 +267,8 @@ def check_qt_version(args):
text = ("Fatal error: Qt and PyQt >= 5.2.0 are required, but {} is "
"installed.".format(qVersion()))
_die(text)
elif args.backend == 'webengine' and qtutils.version_check('5.6.0',
operator.lt):
elif backend == 'webengine' and qtutils.version_check('5.6.0',
operator.lt):
text = ("Fatal error: Qt and PyQt >= 5.6.0 are required for "
"QtWebEngine support, but {} is installed.".format(qVersion()))
_die(text)
@ -267,7 +287,7 @@ def check_ssl_support():
_die(text)
def check_libraries(args):
def check_libraries(backend):
"""Check if all needed Python libraries are installed."""
modules = {
'pkg_resources':
@ -293,10 +313,11 @@ def check_libraries(args):
"or Install via pip.",
pip="PyYAML"),
}
if args.backend == 'webengine':
if backend == 'webengine':
modules['PyQt5.QtWebEngineWidgets'] = _missing_str("QtWebEngine",
webengine=True)
else:
assert backend == 'webkit'
modules['PyQt5.QtWebKit'] = _missing_str("PyQt5.QtWebKit")
from qutebrowser.utils import log
@ -345,6 +366,17 @@ def check_optimize_flag():
"unexpected behavior may occur.")
def set_backend(backend):
"""Set the objects.backend global to the given backend (as string)."""
from qutebrowser.misc import objects
from qutebrowser.utils import usertypes
backends = {
'webkit': usertypes.Backend.QtWebKit,
'webengine': usertypes.Backend.QtWebEngine,
}
objects.backend = backends[backend]
def earlyinit(args):
"""Do all needed early initialization.
@ -367,8 +399,10 @@ def earlyinit(args):
fix_harfbuzz(args)
# Now we can be sure QtCore is available, so we can print dialogs on
# errors, so people only using the GUI notice them as well.
check_qt_version(args)
backend = get_backend(args)
check_qt_version(backend)
remove_inputhook()
check_libraries(args)
check_libraries(backend)
check_ssl_support()
check_optimize_flag()
set_backend(backend)

View File

@ -0,0 +1,26 @@
# 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/>.
"""Various global objects."""
# NOTE: We need to be careful with imports here, as this is imported from
# earlyinit.
# A usertypes.Backend member
backend = None

View File

@ -65,7 +65,7 @@ def get_argparser():
"qutebrowser instance running.")
parser.add_argument('--backend', choices=['webkit', 'webengine'],
help="Which backend to use (webengine backend is "
"EXPERIMENTAL!).", default='webkit')
"EXPERIMENTAL!).")
parser.add_argument('--enable-webengine-inspector', action='store_true',
help="Enable the web inspector for QtWebEngine. Note "
"that this is a SECURITY RISK and you should not "

View File

@ -255,10 +255,6 @@ LoadStatus = enum('LoadStatus', ['none', 'success', 'success_https', 'error',
# Backend of a tab
Backend = enum('Backend', ['QtWebKit', 'QtWebEngine'])
arg2backend = {
'webkit': Backend.QtWebKit,
'webengine': Backend.QtWebEngine,
}
# JS world for QtWebEngine

View File

@ -155,11 +155,10 @@ def tab_registry(win_registry):
@pytest.fixture
def fake_web_tab(stubs, tab_registry, mode_manager, qapp, fake_args):
def fake_web_tab(stubs, tab_registry, mode_manager, qapp):
"""Fixture providing the FakeWebTab *class*."""
if PYQT_VERSION < 0x050600:
pytest.skip('Causes segfaults, see #1638')
fake_args.backend = 'webengine'
return stubs.FakeWebTab

View File

@ -22,6 +22,7 @@ import logging
from PyQt5.QtCore import QUrl
from qutebrowser.utils import usertypes
from qutebrowser.browser import pdfjs, qutescheme
# pylint: disable=unused-import
from qutebrowser.browser.webkit.network import webkitqutescheme
@ -42,8 +43,9 @@ class TestPDFJSHandler:
get_pdfjs_res)
@pytest.fixture(autouse=True)
def patch_args(self, fake_args):
fake_args.backend = 'webkit'
def patch_backend(self, monkeypatch):
monkeypatch.setattr(qutescheme.objects, 'backend',
usertypes.Backend.QtWebKit)
def test_existing_resource(self):
"""Test with a resource that exists."""

View File

@ -27,7 +27,7 @@ from hypothesis import strategies
from PyQt5.QtCore import QUrl
from qutebrowser.browser import history
from qutebrowser.utils import objreg, urlutils
from qutebrowser.utils import objreg, urlutils, usertypes
class FakeWebHistory:
@ -380,14 +380,14 @@ def test_history_interface(qtbot, webview, hist_interface):
webview.load(url)
@pytest.mark.parametrize('backend', ['webengine', 'webkit'])
def test_init(backend, qapp, tmpdir, monkeypatch, fake_save_manager,
fake_args):
@pytest.mark.parametrize('backend', [usertypes.Backend.QtWebEngine,
usertypes.Backend.QtWebKit])
def test_init(backend, qapp, tmpdir, monkeypatch, fake_save_manager):
if backend == 'webkit':
pytest.importorskip('PyQt5.QtWebKitWidgets')
fake_args.backend = backend
monkeypatch.setattr(history.standarddir, 'data', lambda: str(tmpdir))
monkeypatch.setattr(history.objects, 'backend', backend)
history.init(qapp)
hist = objreg.get('web-history')
assert hist.parent() is qapp
@ -397,11 +397,11 @@ def test_init(backend, qapp, tmpdir, monkeypatch, fake_save_manager,
except ImportError:
QWebHistoryInterface = None
if backend == 'webkit':
if backend == usertypes.Backend.QtWebKit:
default_interface = QWebHistoryInterface.defaultInterface()
assert default_interface._history is hist
else:
assert backend == 'webengine'
assert backend == usertypes.Backend.QtWebEngine
if QWebHistoryInterface is None:
default_interface = None
else:

View File

@ -446,19 +446,20 @@ class TestArgument:
class TestRun:
@pytest.fixture(autouse=True)
def patching(self, mode_manager, fake_args):
fake_args.backend = 'webkit'
def patch_backend(self, mode_manager, monkeypatch):
monkeypatch.setattr(command.objects, 'backend',
usertypes.Backend.QtWebKit)
@pytest.mark.parametrize('backend, used, ok', [
(usertypes.Backend.QtWebEngine, 'webengine', True),
(usertypes.Backend.QtWebEngine, 'webkit', False),
(usertypes.Backend.QtWebKit, 'webengine', False),
(usertypes.Backend.QtWebKit, 'webkit', True),
(None, 'webengine', True),
(None, 'webkit', True),
(usertypes.Backend.QtWebEngine, usertypes.Backend.QtWebEngine, True),
(usertypes.Backend.QtWebEngine, usertypes.Backend.QtWebKit, False),
(usertypes.Backend.QtWebKit, usertypes.Backend.QtWebEngine, False),
(usertypes.Backend.QtWebKit, usertypes.Backend.QtWebKit, True),
(None, usertypes.Backend.QtWebEngine, True),
(None, usertypes.Backend.QtWebKit, True),
])
def test_backend(self, fake_args, backend, used, ok):
fake_args.backend = used
def test_backend(self, monkeypatch, backend, used, ok):
monkeypatch.setattr(command.objects, 'backend', used)
cmd = _get_cmd(backend=backend)
if ok:
cmd.run(win_id=0)
@ -471,7 +472,7 @@ class TestRun:
cmd = _get_cmd()
cmd.run(win_id=0)
def test_instance_unavailable_with_backend(self, fake_args):
def test_instance_unavailable_with_backend(self, monkeypatch):
"""Test what happens when a backend doesn't have an objreg object.
For example, QtWebEngine doesn't have 'hintmanager' registered. We make
@ -484,7 +485,8 @@ class TestRun:
"""Blah."""
pass
fake_args.backend = 'webkit'
monkeypatch.setattr(command.objects, 'backend',
usertypes.Backend.QtWebKit)
cmd = cmdutils.cmd_dict['fun']
with pytest.raises(cmdexc.PrerequisitesError) as excinfo:
cmd.run(win_id=0)

View File

@ -21,11 +21,14 @@
import pytest
from qutebrowser.mainwindow import tabwidget
from qutebrowser.config import configtypes
from PyQt5.QtGui import QIcon, QPixmap, QFont, QColor
from PyQt5.QtCore import Qt
from qutebrowser.mainwindow import tabwidget
from qutebrowser.config import configtypes
from qutebrowser.misc import objects
from qutebrowser.utils import usertypes
class TestTabWidget:
@ -56,10 +59,12 @@ class TestTabWidget:
}
@pytest.fixture
def widget(self, qtbot, config_stub):
def widget(self, qtbot, monkeypatch, config_stub):
config_stub.data = self.CONFIG
w = tabwidget.TabWidget(0)
qtbot.addWidget(w)
monkeypatch.setattr(tabwidget.objects, 'backend',
usertypes.Backend.QtWebKit)
return w
def test_small_icon_doesnt_crash(self, widget, qtbot, fake_web_tab):