Merge branch 'webengine-by-default'
This commit is contained in:
commit
4e57b79e91
@ -179,6 +179,7 @@
|
||||
|<<fonts.web.size.default_fixed,fonts.web.size.default_fixed>>|The default font size for fixed-pitch text.
|
||||
|<<fonts.web.size.minimum,fonts.web.size.minimum>>|The hard minimum font size.
|
||||
|<<fonts.web.size.minimum_logical,fonts.web.size.minimum_logical>>|The minimum logical font size that is applied when zooming out.
|
||||
|<<force_software_rendering,force_software_rendering>>|Force software rendering for QtWebEngine.
|
||||
|<<hints.auto_follow,hints.auto_follow>>|Controls when a hint can be automatically followed without pressing Enter.
|
||||
|<<hints.auto_follow_timeout,hints.auto_follow_timeout>>|A timeout (in milliseconds) to ignore normal-mode key bindings after a successful auto-follow.
|
||||
|<<hints.border,hints.border>>|CSS border value for hints.
|
||||
@ -291,18 +292,17 @@ Default: +pass:[false]+
|
||||
=== backend
|
||||
The backend to use to display websites.
|
||||
qutebrowser supports two different web rendering engines / backends, QtWebKit and QtWebEngine.
|
||||
QtWebKit is based on WebKit (similar to Safari). It was discontinued by the Qt project with Qt 5.6, but picked up as a well maintained fork: https://github.com/annulen/webkit/wiki - qutebrowser only supports the fork.
|
||||
QtWebEngine is Qt's official successor to QtWebKit and based on the Chromium project. It's slightly more resource hungry that QtWebKit and has a couple of missing features in qutebrowser, but is generally the preferred choice.
|
||||
QtWebKit was discontinued by the Qt project with Qt 5.6, but picked up as a well maintained fork: https://github.com/annulen/webkit/wiki - qutebrowser only supports the fork.
|
||||
QtWebEngine is Qt's official successor to QtWebKit. It's slightly more resource hungry that QtWebKit and has a couple of missing features in qutebrowser, but is generally the preferred choice.
|
||||
|
||||
Type: <<types,String>>
|
||||
|
||||
Valid values:
|
||||
|
||||
* +auto+: Automatically select either QtWebEngine or QtWebKit
|
||||
* +webkit+: Force QtWebKit
|
||||
* +webengine+: Force QtWebEngine
|
||||
* +webengine+: Use QtWebEngine (based on Chromium)
|
||||
* +webkit+: Use QtWebKit (based on WebKit, similar to Safari)
|
||||
|
||||
Default: +pass:[auto]+
|
||||
Default: +pass:[webengine]+
|
||||
|
||||
[[bindings.commands]]
|
||||
=== bindings.commands
|
||||
@ -2263,6 +2263,22 @@ Type: <<types,Int>>
|
||||
|
||||
Default: +pass:[6]+
|
||||
|
||||
[[force_software_rendering]]
|
||||
=== force_software_rendering
|
||||
Force software rendering for QtWebEngine.
|
||||
This is needed for QtWebEngine to work with Nouveau drivers.
|
||||
|
||||
Type: <<types,Bool>>
|
||||
|
||||
Valid values:
|
||||
|
||||
* +true+
|
||||
* +false+
|
||||
|
||||
Default: +pass:[false]+
|
||||
|
||||
This setting is only available with the QtWebEngine backend.
|
||||
|
||||
[[hints.auto_follow]]
|
||||
=== hints.auto_follow
|
||||
Controls when a hint can be automatically followed without pressing Enter.
|
||||
|
@ -17,7 +17,25 @@
|
||||
# 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 qutebrowser and application-wide things."""
|
||||
"""Initialization of qutebrowser and application-wide things.
|
||||
|
||||
The run() function will get called once early initialization (in
|
||||
qutebrowser.py/earlyinit.py) is done. See the qutebrowser.py docstring for
|
||||
details about early initialization.
|
||||
|
||||
As we need to access the config before the QApplication is created, we
|
||||
initialize everything the config needs before the QApplication is created, and
|
||||
then leave it in a partially initialized state (no saving, no config errors
|
||||
shown yet).
|
||||
|
||||
We then set up the QApplication object and initialize a few more low-level
|
||||
things.
|
||||
|
||||
After that, init() and _init_modules() take over and initialize the rest.
|
||||
|
||||
After all initialization is done, the qt_mainloop() function is called, which
|
||||
blocks and spins the Qt mainloop.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
@ -44,8 +62,7 @@ import qutebrowser.resources
|
||||
from qutebrowser.completion import completiondelegate
|
||||
from qutebrowser.completion.models import miscmodels
|
||||
from qutebrowser.commands import cmdutils, runners, cmdexc
|
||||
from qutebrowser.config import (config, websettings, configexc, configfiles,
|
||||
configinit)
|
||||
from qutebrowser.config import config, websettings, configfiles, configinit
|
||||
from qutebrowser.browser import (urlmarks, adblock, history, browsertab,
|
||||
downloads)
|
||||
from qutebrowser.browser.network import proxy
|
||||
@ -54,7 +71,8 @@ from qutebrowser.browser.webkit.network import networkmanager
|
||||
from qutebrowser.keyinput import macros
|
||||
from qutebrowser.mainwindow import mainwindow, prompt
|
||||
from qutebrowser.misc import (readline, ipc, savemanager, sessions,
|
||||
crashsignal, earlyinit, sql, cmdhistory)
|
||||
crashsignal, earlyinit, sql, cmdhistory,
|
||||
backendproblem)
|
||||
from qutebrowser.utils import (log, version, message, utils, urlutils, objreg,
|
||||
usertypes, standarddir, error)
|
||||
# pylint: disable=unused-import
|
||||
@ -188,12 +206,6 @@ def _init_icon():
|
||||
|
||||
def _process_args(args):
|
||||
"""Open startpage etc. and process commandline args."""
|
||||
for opt, val in args.temp_settings:
|
||||
try:
|
||||
config.instance.set_str(opt, val)
|
||||
except configexc.Error as e:
|
||||
message.error("set: {} - {}".format(e.__class__.__name__, e))
|
||||
|
||||
if not args.override_restore:
|
||||
_load_session(args.session)
|
||||
session_manager = objreg.get('session-manager')
|
||||
@ -389,14 +401,17 @@ def _init_modules(args, crash_handler):
|
||||
crash_handler: The CrashHandler instance.
|
||||
"""
|
||||
# pylint: disable=too-many-statements
|
||||
log.init.debug("Initializing prompts...")
|
||||
prompt.init()
|
||||
|
||||
log.init.debug("Initializing save manager...")
|
||||
save_manager = savemanager.SaveManager(qApp)
|
||||
objreg.register('save-manager', save_manager)
|
||||
configinit.late_init(save_manager)
|
||||
|
||||
log.init.debug("Checking backend requirements...")
|
||||
backendproblem.init()
|
||||
|
||||
log.init.debug("Initializing prompts...")
|
||||
prompt.init()
|
||||
|
||||
log.init.debug("Initializing network...")
|
||||
networkmanager.init()
|
||||
|
||||
@ -509,12 +524,13 @@ class Quitter:
|
||||
with tokenize.open(os.path.join(dirpath, fn)) as f:
|
||||
compile(f.read(), fn, 'exec')
|
||||
|
||||
def _get_restart_args(self, pages=(), session=None):
|
||||
def _get_restart_args(self, pages=(), session=None, override_args=None):
|
||||
"""Get the current working directory and args to relaunch qutebrowser.
|
||||
|
||||
Args:
|
||||
pages: The pages to re-open.
|
||||
session: The session to load, or None.
|
||||
override_args: Argument overrides as a dict.
|
||||
|
||||
Return:
|
||||
An (args, cwd) tuple.
|
||||
@ -565,6 +581,9 @@ class Quitter:
|
||||
argdict['temp_basedir'] = False
|
||||
argdict['temp_basedir_restarted'] = True
|
||||
|
||||
if override_args is not None:
|
||||
argdict.update(override_args)
|
||||
|
||||
# Dump the data
|
||||
data = json.dumps(argdict)
|
||||
args += ['--json-args', data]
|
||||
@ -589,7 +608,7 @@ class Quitter:
|
||||
if ok:
|
||||
self.shutdown(restart=True)
|
||||
|
||||
def restart(self, pages=(), session=None):
|
||||
def restart(self, pages=(), session=None, override_args=None):
|
||||
"""Inner logic to restart qutebrowser.
|
||||
|
||||
The "better" way to restart is to pass a session (_restart usually) as
|
||||
@ -602,6 +621,7 @@ class Quitter:
|
||||
Args:
|
||||
pages: A list of URLs to open.
|
||||
session: The session to load, or None.
|
||||
override_args: Argument overrides as a dict.
|
||||
|
||||
Return:
|
||||
True if the restart succeeded, False otherwise.
|
||||
@ -611,13 +631,19 @@ class Quitter:
|
||||
log.destroy.debug("sys.path: {}".format(sys.path))
|
||||
log.destroy.debug("sys.argv: {}".format(sys.argv))
|
||||
log.destroy.debug("frozen: {}".format(hasattr(sys, 'frozen')))
|
||||
|
||||
# Save the session if one is given.
|
||||
if session is not None:
|
||||
session_manager = objreg.get('session-manager')
|
||||
session_manager.save(session, with_private=True)
|
||||
|
||||
# Make sure we're not accepting a connection from the new process
|
||||
# before we fully exited.
|
||||
ipc.server.shutdown()
|
||||
|
||||
# Open a new process and immediately shutdown the existing one
|
||||
try:
|
||||
args, cwd = self._get_restart_args(pages, session)
|
||||
args, cwd = self._get_restart_args(pages, session, override_args)
|
||||
if cwd is None:
|
||||
subprocess.Popen(args)
|
||||
else:
|
||||
@ -705,7 +731,7 @@ class Quitter:
|
||||
QApplication.closeAllWindows()
|
||||
# Shut down IPC
|
||||
try:
|
||||
objreg.get('ipc-server').shutdown()
|
||||
ipc.server.shutdown()
|
||||
except KeyError:
|
||||
pass
|
||||
# Save everything
|
||||
|
@ -19,7 +19,6 @@
|
||||
|
||||
"""Wrapper over a QWebEngineView."""
|
||||
|
||||
import os
|
||||
import math
|
||||
import functools
|
||||
import html as html_utils
|
||||
@ -38,7 +37,7 @@ from qutebrowser.browser.webengine import (webview, webengineelem, tabhistory,
|
||||
webenginesettings)
|
||||
from qutebrowser.misc import miscwidgets
|
||||
from qutebrowser.utils import (usertypes, qtutils, log, javascript, utils,
|
||||
message, objreg, jinja, debug, version)
|
||||
message, objreg, jinja, debug)
|
||||
|
||||
|
||||
_qute_scheme_handler = None
|
||||
@ -50,16 +49,8 @@ def init():
|
||||
# won't work...
|
||||
# https://www.riverbankcomputing.com/pipermail/pyqt/2016-September/038075.html
|
||||
global _qute_scheme_handler
|
||||
|
||||
app = QApplication.instance()
|
||||
|
||||
software_rendering = (os.environ.get('LIBGL_ALWAYS_SOFTWARE') == '1' or
|
||||
'QT_XCB_FORCE_SOFTWARE_OPENGL' in os.environ)
|
||||
if version.opengl_vendor() == 'nouveau' and not software_rendering:
|
||||
# FIXME:qtwebengine display something more sophisticated here
|
||||
raise browsertab.WebTabError(
|
||||
"QtWebEngine is not supported with Nouveau graphics (unless "
|
||||
"QT_XCB_FORCE_SOFTWARE_OPENGL is set as environment variable).")
|
||||
|
||||
log.init.debug("Initializing qute://* handler...")
|
||||
_qute_scheme_handler = webenginequtescheme.QuteSchemeHandler(parent=app)
|
||||
_qute_scheme_handler.install(webenginesettings.default_profile)
|
||||
|
@ -101,27 +101,35 @@ qt_args:
|
||||
https://peter.sh/experiments/chromium-command-line-switches/ for a list)
|
||||
will work.
|
||||
|
||||
force_software_rendering:
|
||||
type: Bool
|
||||
default: false
|
||||
backend: QtWebEngine
|
||||
desc: >-
|
||||
Force software rendering for QtWebEngine.
|
||||
|
||||
This is needed for QtWebEngine to work with Nouveau drivers.
|
||||
|
||||
backend:
|
||||
type:
|
||||
name: String
|
||||
valid_values:
|
||||
- auto: Automatically select either QtWebEngine or QtWebKit
|
||||
- webkit: Force QtWebKit
|
||||
- webengine: Force QtWebEngine
|
||||
default: auto
|
||||
- webengine: Use QtWebEngine (based on Chromium)
|
||||
- webkit: Use QtWebKit (based on WebKit, similar to Safari)
|
||||
default: webengine
|
||||
desc: >-
|
||||
The backend to use to display websites.
|
||||
|
||||
qutebrowser supports two different web rendering engines / backends,
|
||||
QtWebKit and QtWebEngine.
|
||||
|
||||
QtWebKit is based on WebKit (similar to Safari). It was discontinued by the
|
||||
Qt project with Qt 5.6, but picked up as a well maintained fork:
|
||||
https://github.com/annulen/webkit/wiki - qutebrowser only supports the fork.
|
||||
QtWebKit was discontinued by the Qt project with Qt 5.6, but picked up as a
|
||||
well maintained fork: https://github.com/annulen/webkit/wiki - qutebrowser
|
||||
only supports the fork.
|
||||
|
||||
QtWebEngine is Qt's official successor to QtWebKit and based on the Chromium
|
||||
project. It's slightly more resource hungry that QtWebKit and has a couple
|
||||
of missing features in qutebrowser, but is generally the preferred choice.
|
||||
QtWebEngine is Qt's official successor to QtWebKit. It's slightly more
|
||||
resource hungry that QtWebKit and has a couple of missing features in
|
||||
qutebrowser, but is generally the preferred choice.
|
||||
|
||||
## auto_save
|
||||
|
||||
|
@ -26,8 +26,8 @@ from PyQt5.QtWidgets import QMessageBox
|
||||
|
||||
from qutebrowser.config import (config, configdata, configfiles, configtypes,
|
||||
configexc)
|
||||
from qutebrowser.utils import objreg, qtutils, usertypes, log, standarddir
|
||||
from qutebrowser.misc import earlyinit, msgbox, objects
|
||||
from qutebrowser.utils import objreg, usertypes, log, standarddir, message
|
||||
from qutebrowser.misc import msgbox, objects
|
||||
|
||||
|
||||
# Error which happened during init, so we can show a message box.
|
||||
@ -69,18 +69,20 @@ def early_init(args):
|
||||
configfiles.init()
|
||||
|
||||
objects.backend = get_backend(args)
|
||||
earlyinit.init_with_backend(objects.backend)
|
||||
|
||||
for opt, val in args.temp_settings:
|
||||
try:
|
||||
config.instance.set_str(opt, val)
|
||||
except configexc.Error as e:
|
||||
message.error("set: {} - {}".format(e.__class__.__name__, e))
|
||||
|
||||
if (objects.backend == usertypes.Backend.QtWebEngine and
|
||||
config.val.force_software_rendering):
|
||||
os.environ['QT_XCB_FORCE_SOFTWARE_OPENGL'] = '1'
|
||||
|
||||
|
||||
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,
|
||||
@ -88,12 +90,8 @@ def get_backend(args):
|
||||
|
||||
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
|
||||
return str_to_backend[config.val.backend]
|
||||
|
||||
|
||||
def late_init(save_manager):
|
||||
|
318
qutebrowser/misc/backendproblem.py
Normal file
318
qutebrowser/misc/backendproblem.py
Normal file
@ -0,0 +1,318 @@
|
||||
# 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/>.
|
||||
|
||||
"""Dialogs shown when there was a problem with a backend choice."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import functools
|
||||
import html
|
||||
|
||||
import attr
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import (QApplication, QDialog, QPushButton, QHBoxLayout,
|
||||
QVBoxLayout, QLabel, QMessageBox)
|
||||
from PyQt5.QtNetwork import QSslSocket
|
||||
|
||||
from qutebrowser.config import config
|
||||
from qutebrowser.utils import usertypes, objreg, version, qtutils, log
|
||||
from qutebrowser.misc import objects, msgbox
|
||||
|
||||
|
||||
_Result = usertypes.enum(
|
||||
'_Result',
|
||||
['quit', 'restart', 'restart_webkit', 'restart_webengine'],
|
||||
is_int=True, start=QDialog.Accepted + 1)
|
||||
|
||||
|
||||
@attr.s
|
||||
class _Button:
|
||||
|
||||
"""A button passed to BackendProblemDialog."""
|
||||
|
||||
text = attr.ib()
|
||||
setting = attr.ib()
|
||||
value = attr.ib()
|
||||
default = attr.ib(default=False)
|
||||
|
||||
|
||||
class _Dialog(QDialog):
|
||||
|
||||
"""A dialog which gets shown if there are issues with the backend."""
|
||||
|
||||
def __init__(self, because, text, backend, buttons=None, parent=None):
|
||||
super().__init__(parent)
|
||||
vbox = QVBoxLayout(self)
|
||||
|
||||
other_backend = {
|
||||
usertypes.Backend.QtWebKit: usertypes.Backend.QtWebEngine,
|
||||
usertypes.Backend.QtWebEngine: usertypes.Backend.QtWebKit,
|
||||
}[backend]
|
||||
other_setting = other_backend.name.lower()[2:]
|
||||
|
||||
label = QLabel(
|
||||
"<b>Failed to start with the {backend} backend!</b>"
|
||||
"<p>qutebrowser tried to start with the {backend} backend but "
|
||||
"failed because {because}.</p>{text}"
|
||||
"<p><b>Forcing the {other_backend.name} backend</b></p>"
|
||||
"<p>This forces usage of the {other_backend.name} backend by "
|
||||
"setting the <i>backend = '{other_setting}'</i> option "
|
||||
"(if you have a <i>config.py</i> file, you'll need to set "
|
||||
"this manually).</p>".format(
|
||||
backend=backend.name, because=because, text=text,
|
||||
other_backend=other_backend, other_setting=other_setting),
|
||||
wordWrap=True)
|
||||
label.setTextFormat(Qt.RichText)
|
||||
vbox.addWidget(label)
|
||||
|
||||
hbox = QHBoxLayout()
|
||||
buttons = [] if buttons is None else buttons
|
||||
|
||||
quit_button = QPushButton("Quit")
|
||||
quit_button.clicked.connect(lambda: self.done(_Result.quit))
|
||||
hbox.addWidget(quit_button)
|
||||
|
||||
backend_button = QPushButton("Force {} backend".format(
|
||||
other_backend.name))
|
||||
backend_button.clicked.connect(functools.partial(
|
||||
self._change_setting, 'backend', other_setting))
|
||||
hbox.addWidget(backend_button)
|
||||
|
||||
for button in buttons:
|
||||
btn = QPushButton(button.text, default=button.default)
|
||||
btn.clicked.connect(functools.partial(
|
||||
self._change_setting, button.setting, button.value))
|
||||
hbox.addWidget(btn)
|
||||
|
||||
vbox.addLayout(hbox)
|
||||
|
||||
def _change_setting(self, setting, value):
|
||||
"""Change the given setting and restart."""
|
||||
config.instance.set_obj(setting, value, save_yaml=True)
|
||||
save_manager = objreg.get('save-manager')
|
||||
save_manager.save_all(is_exit=True)
|
||||
|
||||
if setting == 'backend' and value == 'webkit':
|
||||
self.done(_Result.restart_webkit)
|
||||
elif setting == 'backend' and value == 'webengine':
|
||||
self.done(_Result.restart_webengine)
|
||||
else:
|
||||
self.done(_Result.restart)
|
||||
|
||||
|
||||
def _show_dialog(*args, **kwargs):
|
||||
"""Show a dialog for a backend problem."""
|
||||
dialog = _Dialog(*args, **kwargs)
|
||||
|
||||
status = dialog.exec_()
|
||||
quitter = objreg.get('quitter')
|
||||
|
||||
if status in [_Result.quit, QDialog.Rejected]:
|
||||
pass
|
||||
elif status == _Result.restart_webkit:
|
||||
quitter.restart(override_args={'backend': 'webkit'})
|
||||
elif status == _Result.restart_webengine:
|
||||
quitter.restart(override_args={'backend': 'webengine'})
|
||||
elif status == _Result.restart:
|
||||
quitter.restart()
|
||||
else:
|
||||
assert False, status
|
||||
|
||||
sys.exit(usertypes.Exit.err_init)
|
||||
|
||||
|
||||
def _handle_nouveau_graphics():
|
||||
assert objects.backend == usertypes.Backend.QtWebEngine, objects.backend
|
||||
|
||||
if version.opengl_vendor() != 'nouveau':
|
||||
return
|
||||
|
||||
if (os.environ.get('LIBGL_ALWAYS_SOFTWARE') == '1' or
|
||||
'QT_XCB_FORCE_SOFTWARE_OPENGL' in os.environ):
|
||||
return
|
||||
|
||||
button = _Button("Force software rendering", 'force_software_rendering',
|
||||
True)
|
||||
_show_dialog(
|
||||
backend=usertypes.Backend.QtWebEngine,
|
||||
because="you're using Nouveau graphics",
|
||||
text="<p>There are two ways to fix this:</p>"
|
||||
"<p><b>Forcing software rendering</b></p>"
|
||||
"<p>This allows you to use the newer QtWebEngine backend (based "
|
||||
"on Chromium) but could have noticable performance impact "
|
||||
"(depending on your hardware). "
|
||||
"This sets the <i>force_software_rendering = True</i> option "
|
||||
"(if you have a <i>config.py</i> file, you'll need to set this "
|
||||
"manually).</p>",
|
||||
buttons=[button],
|
||||
)
|
||||
|
||||
# Should never be reached
|
||||
assert False
|
||||
|
||||
|
||||
def _handle_wayland():
|
||||
assert objects.backend == usertypes.Backend.QtWebEngine, objects.backend
|
||||
|
||||
platform = QApplication.instance().platformName()
|
||||
if platform not in ['wayland', 'wayland-egl']:
|
||||
return
|
||||
|
||||
_show_dialog(
|
||||
backend=usertypes.Backend.QtWebEngine,
|
||||
because="you're using Wayland",
|
||||
text="<p>There are two ways to fix this:</p>"
|
||||
"<p><b>Set up XWayland</b></p>"
|
||||
"<p>This allows you to use the newer QtWebEngine backend (based "
|
||||
"on Chromium). "
|
||||
)
|
||||
|
||||
# Should never be reached
|
||||
assert False
|
||||
|
||||
|
||||
@attr.s
|
||||
class BackendImports:
|
||||
|
||||
"""Whether backend modules could be imported."""
|
||||
|
||||
webkit_available = attr.ib(default=None)
|
||||
webengine_available = attr.ib(default=None)
|
||||
webkit_error = attr.ib(default=None)
|
||||
webengine_error = attr.ib(default=None)
|
||||
|
||||
|
||||
def _try_import_backends():
|
||||
"""Check whether backends can be imported and return BackendImports."""
|
||||
# pylint: disable=unused-variable
|
||||
results = BackendImports()
|
||||
|
||||
try:
|
||||
from PyQt5 import QtWebKit
|
||||
from PyQt5 import QtWebKitWidgets
|
||||
except ImportError as e:
|
||||
results.webkit_available = False
|
||||
results.webkit_error = str(e)
|
||||
else:
|
||||
if qtutils.is_new_qtwebkit():
|
||||
results.webkit_available = True
|
||||
else:
|
||||
results.webkit_available = False
|
||||
results.webkit_error = "Unsupported legacy QtWebKit found"
|
||||
|
||||
try:
|
||||
from PyQt5 import QtWebEngineWidgets
|
||||
except ImportError as e:
|
||||
results.webengine_available = False
|
||||
results.webengine_error = str(e)
|
||||
else:
|
||||
results.webengine_available = True
|
||||
|
||||
assert results.webkit_available is not None
|
||||
assert results.webengine_available is not None
|
||||
if not results.webkit_available:
|
||||
assert results.webkit_error is not None
|
||||
if not results.webengine_available:
|
||||
assert results.webengine_error is not None
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def _handle_ssl_support(fatal=False):
|
||||
"""Check for full SSL availability.
|
||||
|
||||
If "fatal" is given, show an error and exit.
|
||||
"""
|
||||
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. This only affects downloads.")
|
||||
|
||||
if QSslSocket.supportsSsl():
|
||||
return
|
||||
|
||||
if fatal:
|
||||
errbox = msgbox.msgbox(parent=None,
|
||||
title="SSL error",
|
||||
text="Could not initialize SSL support.",
|
||||
icon=QMessageBox.Critical,
|
||||
plain_text=False)
|
||||
errbox.exec_()
|
||||
sys.exit(usertypes.Exit.err_init)
|
||||
|
||||
assert not fatal
|
||||
log.init.warning(text)
|
||||
|
||||
|
||||
def _check_backend_modules():
|
||||
"""Check for the modules needed for QtWebKit/QtWebEngine."""
|
||||
imports = _try_import_backends()
|
||||
|
||||
if imports.webkit_available and imports.webengine_available:
|
||||
return
|
||||
elif not imports.webkit_available and not imports.webengine_available:
|
||||
text = ("<p>qutebrowser needs QtWebKit or QtWebEngine, but neither "
|
||||
"could be imported!</p>"
|
||||
"<p>The errors encountered were:<ul>"
|
||||
"<li><b>QtWebKit:</b> {webkit_error}"
|
||||
"<li><b>QtWebEngine:</b> {webengine_error}"
|
||||
"</ul></p>".format(
|
||||
webkit_error=html.escape(imports.webkit_error),
|
||||
webengine_error=html.escape(imports.webengine_error)))
|
||||
errbox = msgbox.msgbox(parent=None,
|
||||
title="No backend library found!",
|
||||
text=text,
|
||||
icon=QMessageBox.Critical,
|
||||
plain_text=False)
|
||||
errbox.exec_()
|
||||
sys.exit(usertypes.Exit.err_init)
|
||||
elif objects.backend == usertypes.Backend.QtWebKit:
|
||||
if imports.webkit_available:
|
||||
return
|
||||
assert imports.webengine_available
|
||||
_show_dialog(
|
||||
backend=usertypes.Backend.QtWebKit,
|
||||
because="QtWebKit could not be imported",
|
||||
text="<p><b>The error encountered was:</b><br/>{}</p>".format(
|
||||
html.escape(imports.webkit_error))
|
||||
)
|
||||
elif objects.backend == usertypes.Backend.QtWebEngine:
|
||||
if imports.webengine_available:
|
||||
return
|
||||
assert imports.webkit_available
|
||||
_show_dialog(
|
||||
backend=usertypes.Backend.QtWebEngine,
|
||||
because="QtWebEngine could not be imported",
|
||||
text="<p><b>The error encountered was:</b><br/>{}</p>".format(
|
||||
html.escape(imports.webengine_error))
|
||||
)
|
||||
|
||||
# Should never be reached
|
||||
assert False
|
||||
|
||||
|
||||
def init():
|
||||
_check_backend_modules()
|
||||
if objects.backend == usertypes.Backend.QtWebEngine:
|
||||
_handle_ssl_support()
|
||||
_handle_wayland()
|
||||
_handle_nouveau_graphics()
|
||||
else:
|
||||
assert objects.backend == usertypes.Backend.QtWebKit, objects.backend
|
||||
_handle_ssl_support(fatal=True)
|
@ -36,10 +36,10 @@ except ImportError:
|
||||
import attr
|
||||
from PyQt5.QtCore import (pyqtSlot, qInstallMessageHandler, QObject,
|
||||
QSocketNotifier, QTimer, QUrl)
|
||||
from PyQt5.QtWidgets import QApplication, QDialog
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
|
||||
from qutebrowser.commands import cmdutils
|
||||
from qutebrowser.misc import earlyinit, crashdialog
|
||||
from qutebrowser.misc import earlyinit, crashdialog, ipc
|
||||
from qutebrowser.utils import usertypes, standarddir, log, objreg, debug, utils
|
||||
|
||||
|
||||
@ -236,7 +236,7 @@ class CrashHandler(QObject):
|
||||
info = self._get_exception_info()
|
||||
|
||||
try:
|
||||
objreg.get('ipc-server').ignored = True
|
||||
ipc.server.ignored = True
|
||||
except Exception:
|
||||
log.destroy.exception("Error while ignoring ipc")
|
||||
|
||||
|
@ -187,23 +187,6 @@ def check_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 "
|
||||
"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. This only affects downloads.")
|
||||
|
||||
if not QSslSocket.supportsSsl():
|
||||
if backend == usertypes.Backend.QtWebKit:
|
||||
_die("Could not initialize SSL support.")
|
||||
else:
|
||||
assert backend == usertypes.Backend.QtWebEngine
|
||||
log.init.warning(text)
|
||||
|
||||
|
||||
def _check_modules(modules):
|
||||
"""Make sure the given modules are available."""
|
||||
from qutebrowser.utils import log
|
||||
@ -247,35 +230,6 @@ def check_libraries():
|
||||
_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),
|
||||
}
|
||||
else:
|
||||
assert backend == usertypes.Backend.QtWebKit, backend
|
||||
modules = {
|
||||
'PyQt5.QtWebKit': _missing_str("PyQt5.QtWebKit"),
|
||||
'PyQt5.QtWebKitWidgets': _missing_str("PyQt5.QtWebKitWidgets"),
|
||||
}
|
||||
_check_modules(modules)
|
||||
|
||||
|
||||
def check_new_webkit(backend):
|
||||
"""Make sure we use QtWebEngine or a new QtWebKit."""
|
||||
from qutebrowser.utils import usertypes, qtutils
|
||||
if backend == usertypes.Backend.QtWebKit and not qtutils.is_new_qtwebkit():
|
||||
_die("qutebrowser does not support legacy QtWebKit versions anymore, "
|
||||
"see the installation docs for details.")
|
||||
|
||||
|
||||
def remove_inputhook():
|
||||
"""Remove the PyQt input hook.
|
||||
|
||||
@ -328,16 +282,3 @@ def early_init(args):
|
||||
remove_inputhook()
|
||||
check_ssl_support()
|
||||
check_optimize_flag()
|
||||
|
||||
|
||||
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)
|
||||
check_new_webkit(backend)
|
||||
|
@ -30,7 +30,7 @@ from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, Qt
|
||||
from PyQt5.QtNetwork import QLocalSocket, QLocalServer, QAbstractSocket
|
||||
|
||||
import qutebrowser
|
||||
from qutebrowser.utils import log, usertypes, error, objreg, standarddir, utils
|
||||
from qutebrowser.utils import log, usertypes, error, standarddir, utils
|
||||
|
||||
|
||||
CONNECT_TIMEOUT = 100 # timeout for connecting/disconnecting
|
||||
@ -40,6 +40,10 @@ ATIME_INTERVAL = 60 * 60 * 3 * 1000 # 3 hours
|
||||
PROTOCOL_VERSION = 1
|
||||
|
||||
|
||||
# The ipc server instance
|
||||
server = None
|
||||
|
||||
|
||||
def _get_socketname_windows(basedir):
|
||||
"""Get a socketname to use for Windows."""
|
||||
parts = ['qutebrowser', getpass.getuser()]
|
||||
@ -109,15 +113,15 @@ class ListenError(Error):
|
||||
message: The error message.
|
||||
"""
|
||||
|
||||
def __init__(self, server):
|
||||
def __init__(self, local_server):
|
||||
"""Constructor.
|
||||
|
||||
Args:
|
||||
server: The QLocalServer which has the error set.
|
||||
local_server: The QLocalServer which has the error set.
|
||||
"""
|
||||
super().__init__()
|
||||
self.code = server.serverError()
|
||||
self.message = server.errorString()
|
||||
self.code = local_server.serverError()
|
||||
self.message = local_server.errorString()
|
||||
|
||||
def __str__(self):
|
||||
return "Error while listening to IPC server: {} (error {})".format(
|
||||
@ -482,6 +486,7 @@ def send_or_listen(args):
|
||||
The IPCServer instance if no running instance was detected.
|
||||
None if an instance was running and received our request.
|
||||
"""
|
||||
global server
|
||||
socketname = _get_socketname(args.basedir)
|
||||
try:
|
||||
try:
|
||||
@ -492,7 +497,6 @@ def send_or_listen(args):
|
||||
log.init.debug("Starting IPC server...")
|
||||
server = IPCServer(socketname)
|
||||
server.listen()
|
||||
objreg.register('ipc-server', server)
|
||||
return server
|
||||
except AddressInUseError as e:
|
||||
# This could be a race condition...
|
||||
|
@ -164,6 +164,11 @@ class SaveManager(QObject):
|
||||
self.saveables[name].save(is_exit=is_exit, explicit=explicit,
|
||||
silent=silent, force=force)
|
||||
|
||||
def save_all(self, *args, **kwargs):
|
||||
"""Save all saveables."""
|
||||
for saveable in self.saveables:
|
||||
self.save(saveable, *args, **kwargs)
|
||||
|
||||
@pyqtSlot()
|
||||
def autosave(self):
|
||||
"""Slot used when the configs are auto-saved."""
|
||||
|
@ -171,12 +171,15 @@ def debug_cache_stats():
|
||||
prefix_info = configdata.is_valid_prefix.cache_info()
|
||||
# pylint: disable=protected-access
|
||||
render_stylesheet_info = config._render_stylesheet.cache_info()
|
||||
|
||||
history_info = None
|
||||
try:
|
||||
from PyQt5.QtWebKit import QWebHistoryInterface
|
||||
interface = QWebHistoryInterface.defaultInterface()
|
||||
history_info = interface.historyContains.cache_info()
|
||||
if interface is not None:
|
||||
history_info = interface.historyContains.cache_info()
|
||||
except ImportError:
|
||||
history_info = None
|
||||
pass
|
||||
|
||||
log.misc.debug('is_valid_prefix: {}'.format(prefix_info))
|
||||
log.misc.debug('_render_stylesheet: {}'.format(render_stylesheet_info))
|
||||
|
@ -17,7 +17,21 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""Early initialization and main entry point."""
|
||||
"""Early initialization and main entry point.
|
||||
|
||||
qutebrowser's initialization process roughly looks like this:
|
||||
|
||||
- This file gets imported, either via the setuptools entry point or
|
||||
__main__.py.
|
||||
- At import time, we check for the correct Python version and show an error if
|
||||
it's too old.
|
||||
- The main() function in this file gets invoked
|
||||
- Argument parsing takes place
|
||||
- earlyinit.early_init() gets invoked to do various low-level initialization
|
||||
and checks whether all dependencies are met.
|
||||
- app.run() gets called, which takes over.
|
||||
See the docstring of app.py for details.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import json
|
||||
|
@ -319,3 +319,18 @@ def test_qute_settings_persistence(short_tmpdir, request, quteproc_new):
|
||||
|
||||
quteproc_new.start(args)
|
||||
assert quteproc_new.get_setting('ignore_case') == 'always'
|
||||
|
||||
|
||||
@pytest.mark.no_xvfb
|
||||
@pytest.mark.no_ci
|
||||
def test_force_software_rendering(request, quteproc_new):
|
||||
"""Make sure we can force software rendering with -s."""
|
||||
if not request.config.webengine:
|
||||
pytest.skip("Only runs with QtWebEngine")
|
||||
|
||||
args = (_base_args(request.config) +
|
||||
['--temp-basedir', '-s', 'force_software_rendering', 'true'])
|
||||
quteproc_new.start(args)
|
||||
quteproc_new.open_path('chrome://gpu')
|
||||
message = 'Canvas: Software only, hardware acceleration unavailable'
|
||||
assert message in quteproc_new.get_content()
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
"""Tests for qutebrowser.config.configinit."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
import unittest.mock
|
||||
@ -25,23 +26,18 @@ import unittest.mock
|
||||
import pytest
|
||||
|
||||
from qutebrowser import qutebrowser
|
||||
from qutebrowser.config import (config, configdata, configexc, configfiles,
|
||||
configinit)
|
||||
from qutebrowser.config import config, 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', None)
|
||||
# Make sure we get no SSL warning
|
||||
monkeypatch.setattr(configinit.earlyinit, 'check_backend_ssl_support',
|
||||
lambda _backend: None)
|
||||
yield
|
||||
try:
|
||||
objreg.delete('config-commands')
|
||||
@ -49,10 +45,17 @@ def init_patch(qapp, fake_save_manager, monkeypatch, config_tmpdir,
|
||||
pass
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def args(fake_args):
|
||||
"""Arguments needed for the config to init."""
|
||||
fake_args.temp_settings = []
|
||||
return fake_args
|
||||
|
||||
|
||||
class TestEarlyInit:
|
||||
|
||||
@pytest.mark.parametrize('config_py', [True, 'error', False])
|
||||
def test_config_py(self, init_patch, config_tmpdir, caplog, fake_args,
|
||||
def test_config_py(self, init_patch, config_tmpdir, caplog, args,
|
||||
config_py):
|
||||
"""Test loading with only a config.py."""
|
||||
config_py_file = config_tmpdir / 'config.py'
|
||||
@ -65,7 +68,7 @@ class TestEarlyInit:
|
||||
'utf-8', ensure=True)
|
||||
|
||||
with caplog.at_level(logging.ERROR):
|
||||
configinit.early_init(fake_args)
|
||||
configinit.early_init(args)
|
||||
|
||||
# Check error messages
|
||||
expected_errors = []
|
||||
@ -95,7 +98,7 @@ class TestEarlyInit:
|
||||
@pytest.mark.parametrize('config_py', [True, 'error', False])
|
||||
@pytest.mark.parametrize('invalid_yaml', ['42', 'unknown', 'wrong-type',
|
||||
False])
|
||||
def test_autoconfig_yml(self, init_patch, config_tmpdir, caplog, fake_args,
|
||||
def test_autoconfig_yml(self, init_patch, config_tmpdir, caplog, args,
|
||||
load_autoconfig, config_py, invalid_yaml):
|
||||
"""Test interaction between config.py and autoconfig.yml."""
|
||||
# pylint: disable=too-many-locals,too-many-branches
|
||||
@ -122,7 +125,7 @@ class TestEarlyInit:
|
||||
'utf-8', ensure=True)
|
||||
|
||||
with caplog.at_level(logging.ERROR):
|
||||
configinit.early_init(fake_args)
|
||||
configinit.early_init(args)
|
||||
|
||||
# Check error messages
|
||||
expected_errors = []
|
||||
@ -161,16 +164,47 @@ class TestEarlyInit:
|
||||
else:
|
||||
assert config.instance._values == {'colors.hints.fg': 'magenta'}
|
||||
|
||||
def test_invalid_change_filter(self, init_patch, fake_args):
|
||||
def test_invalid_change_filter(self, init_patch, args):
|
||||
config.change_filter('foobar')
|
||||
with pytest.raises(configexc.NoOptionError):
|
||||
configinit.early_init(fake_args)
|
||||
configinit.early_init(args)
|
||||
|
||||
def test_temp_settings_valid(self, init_patch, args):
|
||||
args.temp_settings = [('colors.completion.fg', 'magenta')]
|
||||
configinit.early_init(args)
|
||||
assert config.instance._values['colors.completion.fg'] == 'magenta'
|
||||
|
||||
def test_temp_settings_invalid(self, caplog, init_patch, message_mock,
|
||||
args):
|
||||
"""Invalid temp settings should show an error."""
|
||||
args.temp_settings = [('foo', 'bar')]
|
||||
|
||||
with caplog.at_level(logging.ERROR):
|
||||
configinit.early_init(args)
|
||||
|
||||
msg = message_mock.getmsg()
|
||||
assert msg.level == usertypes.MessageLevel.error
|
||||
assert msg.text == "set: NoOptionError - No option 'foo'"
|
||||
assert 'colors.completion.fg' not in config.instance._values
|
||||
|
||||
def test_force_software_rendering(self, monkeypatch, init_patch, args):
|
||||
"""Setting force_software_rendering should set the environment var."""
|
||||
envvar = 'QT_XCB_FORCE_SOFTWARE_OPENGL'
|
||||
monkeypatch.setattr(configinit.objects, 'backend',
|
||||
usertypes.Backend.QtWebEngine)
|
||||
monkeypatch.delenv(envvar, raising=False)
|
||||
args.temp_settings = [('force_software_rendering', 'true')]
|
||||
args.backend = 'webengine'
|
||||
|
||||
configinit.early_init(args)
|
||||
|
||||
assert os.environ[envvar] == '1'
|
||||
|
||||
|
||||
@pytest.mark.parametrize('errors', [True, False])
|
||||
def test_late_init(init_patch, monkeypatch, fake_save_manager, fake_args,
|
||||
def test_late_init(init_patch, monkeypatch, fake_save_manager, args,
|
||||
mocker, errors):
|
||||
configinit.early_init(fake_args)
|
||||
configinit.early_init(args)
|
||||
if errors:
|
||||
err = configexc.ConfigErrorDesc("Error text", Exception("Exception"))
|
||||
errs = configexc.ConfigFileErrors("config.py", [err])
|
||||
@ -242,33 +276,23 @@ class TestQtArgs:
|
||||
assert configinit.qt_args(parsed) == [sys.argv[0], '--foo', '--bar']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('arg, confval, can_import, is_new_webkit, used', [
|
||||
@pytest.mark.parametrize('arg, confval, 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),
|
||||
('webkit', 'webengine', usertypes.Backend.QtWebKit),
|
||||
# set in config
|
||||
(None, 'webkit', usertypes.Backend.QtWebKit),
|
||||
])
|
||||
def test_get_backend(monkeypatch, fake_args, config_stub,
|
||||
arg, confval, can_import, is_new_webkit, used):
|
||||
def test_get_backend(monkeypatch, args, config_stub,
|
||||
arg, confval, 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
|
||||
args.backend = arg
|
||||
config_stub.val.backend = confval
|
||||
monkeypatch.setattr(configinit.qtutils, 'is_new_qtwebkit',
|
||||
lambda: is_new_webkit)
|
||||
monkeypatch.setattr('builtins.__import__', fake_import)
|
||||
|
||||
assert configinit.get_backend(fake_args) == used
|
||||
assert configinit.get_backend(args) == used
|
||||
|
@ -34,7 +34,7 @@ from PyQt5.QtTest import QSignalSpy
|
||||
|
||||
import qutebrowser
|
||||
from qutebrowser.misc import ipc
|
||||
from qutebrowser.utils import objreg, standarddir, utils
|
||||
from qutebrowser.utils import standarddir, utils
|
||||
from helpers import stubs
|
||||
|
||||
|
||||
@ -45,12 +45,8 @@ pytestmark = pytest.mark.usefixtures('qapp')
|
||||
def shutdown_server():
|
||||
"""If ipc.send_or_listen was called, make sure to shut server down."""
|
||||
yield
|
||||
try:
|
||||
server = objreg.get('ipc-server')
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
server.shutdown()
|
||||
if ipc.server is not None:
|
||||
ipc.server.shutdown()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@ -609,13 +605,6 @@ class TestSendOrListen:
|
||||
return self.Args(no_err_windows=True, basedir='/basedir/for/testing',
|
||||
command=['test'], target=None)
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def cleanup(self):
|
||||
try:
|
||||
objreg.delete('ipc-server')
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
@pytest.fixture
|
||||
def qlocalserver_mock(self, mocker):
|
||||
m = mocker.patch('qutebrowser.misc.ipc.QLocalServer', autospec=True)
|
||||
@ -639,8 +628,7 @@ class TestSendOrListen:
|
||||
assert isinstance(ret_server, ipc.IPCServer)
|
||||
msgs = [e.message for e in caplog.records]
|
||||
assert "Starting IPC server..." in msgs
|
||||
objreg_server = objreg.get('ipc-server')
|
||||
assert objreg_server is ret_server
|
||||
assert ret_server is ipc.server
|
||||
|
||||
with qtbot.waitSignal(ret_server.got_args):
|
||||
ret_client = ipc.send_or_listen(args)
|
||||
|
Loading…
Reference in New Issue
Block a user