Move backend initialization to config.py

This commit is contained in:
Florian Bruhin 2017-09-18 19:22:54 +02:00
parent 01da144a03
commit 5298d14084
6 changed files with 107 additions and 81 deletions

View File

@ -77,7 +77,7 @@ def run(args):
standarddir.init(args)
log.init.debug("Initializing config...")
config.early_init()
config.early_init(args)
global qApp
qApp = Application(args)

View File

@ -29,7 +29,7 @@ from PyQt5.QtWidgets import QMessageBox
from qutebrowser.config import configdata, configexc, configtypes, configfiles
from qutebrowser.utils import utils, objreg, message, log, usertypes, jinja
from qutebrowser.misc import objects, msgbox
from qutebrowser.misc import objects, msgbox, earlyinit
from qutebrowser.commands import cmdexc, cmdutils, runners
from qutebrowser.completion.models import configmodel
@ -641,7 +641,7 @@ class StyleSheetObserver(QObject):
instance.changed.connect(self._update_stylesheet)
def early_init():
def early_init(args):
"""Initialize the part of the config which works without a QApplication."""
configdata.init()
@ -686,6 +686,30 @@ def early_init():
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."""
from qutebrowser.utils import usertypes
try:
import PyQt5.QtWebKit # pylint: disable=unused-variable
webkit_available = True
except ImportError:
webkit_available = False
if args.backend is not None:
backends = {
'webkit': usertypes.Backend.QtWebKit,
'webengine': usertypes.Backend.QtWebEngine,
}
return backends[args.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."""

View File

@ -163,26 +163,6 @@ def check_pyqt_core():
sys.exit(1)
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 # pylint: disable=unused-variable
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 qt_version(qversion=None, qt_version_str=None):
"""Get a Qt version string based on the runtime/compiled versions."""
if qversion is None:
@ -210,31 +190,52 @@ def check_qt_version():
_die(text)
def check_ssl_support(backend):
def check_ssl_support():
"""Check if SSL support is available."""
from qutebrowser.utils import log
try:
from PyQt5.QtNetwork import QSslSocket
except ImportError:
_die("Fatal error: Your Qt is built without SSL support.")
def check_backend_ssl_support(backend):
"""Check for full SSL availability when we know the backend."""
from PyQt5.QtNetwork import QSslSocket
from qutebrowser.utils import log, usertypes
text = ("Could not initialize QtNetwork SSL support. If you use "
"OpenSSL 1.1 with a PyQt package from PyPI (e.g. on Archlinux "
"or Debian Stretch), you need to set LD_LIBRARY_PATH to the path "
"of OpenSSL 1.0.")
if backend == 'webengine':
text += " This only affects downloads."
"of OpenSSL 1.0. This only affects downloads.")
if not QSslSocket.supportsSsl():
if backend == 'webkit':
if backend == usertypes.Backend.QtWebKit:
_die("Could not initialize SSL support.")
else:
assert backend == 'webengine'
assert backend == usertypes.Backend.QtWebEngine
log.init.warning(text)
def check_libraries(backend):
def _check_modules(modules):
"""Make sure the given modules are available."""
from qutebrowser.utils import log
for name, text in modules.items():
try:
# https://github.com/pallets/jinja/pull/628
# https://bitbucket.org/birkenfeld/pygments-main/issues/1314/
# https://github.com/pallets/jinja/issues/646
# https://bitbucket.org/fdik/pypeg/commits/dd15ca462b532019c0a3be1d39b8ee2f3fa32f4e
messages = ['invalid escape sequence',
'Flags not at the start of the expression']
with log.ignore_py_warnings(
category=DeprecationWarning,
message=r'({})'.format('|'.join(messages))):
importlib.import_module(name)
except ImportError as e:
_die(text, e)
def check_libraries():
"""Check if all needed Python libraries are installed."""
modules = {
'pkg_resources':
@ -262,32 +263,29 @@ def check_libraries(backend):
'PyQt5.QtQml': _missing_str("PyQt5.QtQml"),
'PyQt5.QtSql': _missing_str("PyQt5.QtSql"),
}
if backend == 'webengine':
modules['PyQt5.QtWebEngineWidgets'] = _missing_str("QtWebEngine",
webengine=True)
modules['PyQt5.QtOpenGL'] = _missing_str("PyQt5.QtOpenGL")
_check_modules(modules)
def check_backend_libraries(backend):
"""Make sure the libraries needed by the given backend are available.
Args:
backend: The backend as usertypes.Backend member.
"""
from qutebrowser.utils import usertypes
if backend == usertypes.Backend.QtWebEngine:
modules = {
'PyQt5.QtWebEngineWidgets':
_missing_str("QtWebEngine", webengine=True),
'PyQt5.QtOpenGL': _missing_str("PyQt5.QtOpenGL"),
}
else:
assert backend == 'webkit'
modules['PyQt5.QtWebKit'] = _missing_str("PyQt5.QtWebKit")
modules['PyQt5.QtWebKitWidgets'] = _missing_str(
"PyQt5.QtWebKitWidgets")
from qutebrowser.utils import log
for name, text in modules.items():
try:
# https://github.com/pallets/jinja/pull/628
# https://bitbucket.org/birkenfeld/pygments-main/issues/1314/
# https://github.com/pallets/jinja/issues/646
# https://bitbucket.org/fdik/pypeg/commits/dd15ca462b532019c0a3be1d39b8ee2f3fa32f4e
messages = ['invalid escape sequence',
'Flags not at the start of the expression']
with log.ignore_py_warnings(
category=DeprecationWarning,
message=r'({})'.format('|'.join(messages))):
importlib.import_module(name)
except ImportError as e:
_die(text, e)
assert backend == usertypes.Backend.QtWebKit, backend
modules = {
'PyQt5.QtWebKit': _missing_str("PyQt5.QtWebKit"),
'PyQt5.QtWebKitWidgets': _missing_str("PyQt5.QtWebKitWidgets"),
}
_check_modules(modules)
def remove_inputhook():
@ -318,18 +316,7 @@ 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):
def early_init(args):
"""Do all needed early initialization.
Note that it's vital the other earlyinit functions get called in the right
@ -348,10 +335,20 @@ def earlyinit(args):
init_log(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.
backend = get_backend(args)
check_libraries(backend)
check_libraries()
check_qt_version()
remove_inputhook()
check_ssl_support(backend)
check_ssl_support()
check_optimize_flag()
set_backend(backend)
def init_with_backend(backend):
"""Do later stages of init when we know the backend.
Args:
backend: The backend as usertypes.Backend member.
"""
assert not isinstance(backend, str), backend
assert backend is not None
check_backend_libraries(backend)
check_backend_ssl_support(backend)

View File

@ -167,7 +167,7 @@ def main():
# from json.
data = json.loads(args.json_args)
args = argparse.Namespace(**data)
earlyinit.earlyinit(args)
earlyinit.early_init(args)
# We do this imports late as earlyinit needs to be run first (because of
# version checking and other early initialization)
from qutebrowser import app

View File

@ -415,8 +415,9 @@ def fake_save_manager():
@pytest.fixture
def fake_args():
def fake_args(request):
ns = types.SimpleNamespace()
ns.backend = 'webengine' if request.config.webengine else 'webkit'
objreg.register('args', ns)
yield ns
objreg.delete('args')

View File

@ -877,6 +877,9 @@ def init_patch(qapp, fake_save_manager, monkeypatch, config_tmpdir,
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')
@ -888,7 +891,7 @@ def init_patch(qapp, fake_save_manager, monkeypatch, config_tmpdir,
@pytest.mark.parametrize('config_py', [True, 'error', False])
@pytest.mark.parametrize('invalid_yaml', ['42', 'unknown', False])
# pylint: disable=too-many-branches
def test_early_init(init_patch, config_tmpdir, caplog,
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'
@ -914,7 +917,7 @@ def test_early_init(init_patch, config_tmpdir, caplog,
'utf-8', ensure=True)
with caplog.at_level(logging.ERROR):
config.early_init()
config.early_init(fake_args)
# Check error messages
expected_errors = []
@ -954,15 +957,16 @@ def test_early_init(init_patch, config_tmpdir, caplog,
assert config.instance._values == {'colors.hints.fg': 'magenta'}
def test_early_init_invalid_change_filter(init_patch):
def test_early_init_invalid_change_filter(init_patch, fake_args):
config.change_filter('foobar')
with pytest.raises(configexc.NoOptionError):
config.early_init()
config.early_init(fake_args)
@pytest.mark.parametrize('errors', [True, False])
def test_late_init(init_patch, monkeypatch, fake_save_manager, mocker, errors):
config.early_init()
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])