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) standarddir.init(args)
log.init.debug("Initializing config...") log.init.debug("Initializing config...")
config.early_init() config.early_init(args)
global qApp global qApp
qApp = Application(args) qApp = Application(args)

View File

@ -29,7 +29,7 @@ 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, usertypes, jinja
from qutebrowser.misc import objects, msgbox from qutebrowser.misc import objects, msgbox, earlyinit
from qutebrowser.commands import cmdexc, cmdutils, runners from qutebrowser.commands import cmdexc, cmdutils, runners
from qutebrowser.completion.models import configmodel from qutebrowser.completion.models import configmodel
@ -641,7 +641,7 @@ class StyleSheetObserver(QObject):
instance.changed.connect(self._update_stylesheet) instance.changed.connect(self._update_stylesheet)
def early_init(): def early_init(args):
"""Initialize the part of the config which works without a QApplication.""" """Initialize the part of the config which works without a QApplication."""
configdata.init() configdata.init()
@ -686,6 +686,30 @@ def early_init():
configfiles.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): def late_init(save_manager):
"""Initialize the rest of the config after the QApplication is created.""" """Initialize the rest of the config after the QApplication is created."""

View File

@ -163,26 +163,6 @@ def check_pyqt_core():
sys.exit(1) 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): def qt_version(qversion=None, qt_version_str=None):
"""Get a Qt version string based on the runtime/compiled versions.""" """Get a Qt version string based on the runtime/compiled versions."""
if qversion is None: if qversion is None:
@ -210,31 +190,52 @@ def check_qt_version():
_die(text) _die(text)
def check_ssl_support(backend): def check_ssl_support():
"""Check if SSL support is available.""" """Check if SSL support is available."""
from qutebrowser.utils import log
try: try:
from PyQt5.QtNetwork import QSslSocket from PyQt5.QtNetwork import QSslSocket
except ImportError: except ImportError:
_die("Fatal error: Your Qt is built without SSL support.") _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 " text = ("Could not initialize QtNetwork SSL support. If you use "
"OpenSSL 1.1 with a PyQt package from PyPI (e.g. on Archlinux " "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 " "or Debian Stretch), you need to set LD_LIBRARY_PATH to the path "
"of OpenSSL 1.0.") "of OpenSSL 1.0. This only affects downloads.")
if backend == 'webengine':
text += " This only affects downloads."
if not QSslSocket.supportsSsl(): if not QSslSocket.supportsSsl():
if backend == 'webkit': if backend == usertypes.Backend.QtWebKit:
_die("Could not initialize SSL support.") _die("Could not initialize SSL support.")
else: else:
assert backend == 'webengine' assert backend == usertypes.Backend.QtWebEngine
log.init.warning(text) 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.""" """Check if all needed Python libraries are installed."""
modules = { modules = {
'pkg_resources': 'pkg_resources':
@ -262,32 +263,29 @@ def check_libraries(backend):
'PyQt5.QtQml': _missing_str("PyQt5.QtQml"), 'PyQt5.QtQml': _missing_str("PyQt5.QtQml"),
'PyQt5.QtSql': _missing_str("PyQt5.QtSql"), 'PyQt5.QtSql': _missing_str("PyQt5.QtSql"),
} }
if backend == 'webengine': _check_modules(modules)
modules['PyQt5.QtWebEngineWidgets'] = _missing_str("QtWebEngine",
webengine=True)
modules['PyQt5.QtOpenGL'] = _missing_str("PyQt5.QtOpenGL") 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: else:
assert backend == 'webkit' assert backend == usertypes.Backend.QtWebKit, backend
modules['PyQt5.QtWebKit'] = _missing_str("PyQt5.QtWebKit") modules = {
modules['PyQt5.QtWebKitWidgets'] = _missing_str( 'PyQt5.QtWebKit': _missing_str("PyQt5.QtWebKit"),
"PyQt5.QtWebKitWidgets") 'PyQt5.QtWebKitWidgets': _missing_str("PyQt5.QtWebKitWidgets"),
}
from qutebrowser.utils import log _check_modules(modules)
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 remove_inputhook(): def remove_inputhook():
@ -318,18 +316,7 @@ def check_optimize_flag():
"unexpected behavior may occur.") "unexpected behavior may occur.")
def set_backend(backend): def early_init(args):
"""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. """Do all needed early initialization.
Note that it's vital the other earlyinit functions get called in the right Note that it's vital the other earlyinit functions get called in the right
@ -348,10 +335,20 @@ def earlyinit(args):
init_log(args) init_log(args)
# Now we can be sure QtCore is available, so we can print dialogs on # 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. # errors, so people only using the GUI notice them as well.
backend = get_backend(args) check_libraries()
check_libraries(backend)
check_qt_version() check_qt_version()
remove_inputhook() remove_inputhook()
check_ssl_support(backend) check_ssl_support()
check_optimize_flag() 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. # from json.
data = json.loads(args.json_args) data = json.loads(args.json_args)
args = argparse.Namespace(**data) 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 # We do this imports late as earlyinit needs to be run first (because of
# version checking and other early initialization) # version checking and other early initialization)
from qutebrowser import app from qutebrowser import app

View File

@ -415,8 +415,9 @@ def fake_save_manager():
@pytest.fixture @pytest.fixture
def fake_args(): def fake_args(request):
ns = types.SimpleNamespace() ns = types.SimpleNamespace()
ns.backend = 'webengine' if request.config.webengine else 'webkit'
objreg.register('args', ns) objreg.register('args', ns)
yield ns yield ns
objreg.delete('args') 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, 'key_instance', None)
monkeypatch.setattr(config, '_change_filters', []) monkeypatch.setattr(config, '_change_filters', [])
monkeypatch.setattr(config, '_init_errors', []) monkeypatch.setattr(config, '_init_errors', [])
# Make sure we get no SSL warning
monkeypatch.setattr(config.earlyinit, 'check_backend_ssl_support',
lambda _backend: None)
yield yield
try: try:
objreg.delete('config-commands') 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('config_py', [True, 'error', False])
@pytest.mark.parametrize('invalid_yaml', ['42', 'unknown', False]) @pytest.mark.parametrize('invalid_yaml', ['42', 'unknown', False])
# pylint: disable=too-many-branches # 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): load_autoconfig, config_py, invalid_yaml):
# Prepare files # Prepare files
autoconfig_file = config_tmpdir / 'autoconfig.yml' autoconfig_file = config_tmpdir / 'autoconfig.yml'
@ -914,7 +917,7 @@ def test_early_init(init_patch, config_tmpdir, caplog,
'utf-8', ensure=True) 'utf-8', ensure=True)
with caplog.at_level(logging.ERROR): with caplog.at_level(logging.ERROR):
config.early_init() config.early_init(fake_args)
# Check error messages # Check error messages
expected_errors = [] expected_errors = []
@ -954,15 +957,16 @@ def test_early_init(init_patch, config_tmpdir, caplog,
assert config.instance._values == {'colors.hints.fg': 'magenta'} 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') config.change_filter('foobar')
with pytest.raises(configexc.NoOptionError): with pytest.raises(configexc.NoOptionError):
config.early_init() config.early_init(fake_args)
@pytest.mark.parametrize('errors', [True, False]) @pytest.mark.parametrize('errors', [True, False])
def test_late_init(init_patch, monkeypatch, fake_save_manager, mocker, errors): def test_late_init(init_patch, monkeypatch, fake_save_manager, fake_args,
config.early_init() mocker, errors):
config.early_init(fake_args)
if errors: if errors:
err = configexc.ConfigErrorDesc("Error text", Exception("Exception")) err = configexc.ConfigErrorDesc("Error text", Exception("Exception"))
errs = configexc.ConfigFileErrors("config.py", [err]) errs = configexc.ConfigFileErrors("config.py", [err])