summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Bruhin <me@the-compiler.org>2019-12-23 15:32:06 +0100
committerFlorian Bruhin <me@the-compiler.org>2019-12-23 15:32:06 +0100
commit450d2e9d33d78ca243bc0f0db54908482fb2a09b (patch)
treee67182bb5636c7aecf572c47416625eb7e3da4c0
parent1c0c405901d98fe7c1899260a1d574667c13e25e (diff)
parent9479b1a108e51f5d644d9943f797e09dd87dc609 (diff)
downloadqutebrowser-450d2e9d33d78ca243bc0f0db54908482fb2a09b.tar.gz
qutebrowser-450d2e9d33d78ca243bc0f0db54908482fb2a09b.zip
Merge branch 'circular-import'
-rw-r--r--qutebrowser/browser/downloadview.py4
-rw-r--r--qutebrowser/browser/webkit/webview.py4
-rw-r--r--qutebrowser/completion/completionwidget.py4
-rw-r--r--qutebrowser/config/config.py96
-rw-r--r--qutebrowser/config/configexc.py3
-rw-r--r--qutebrowser/config/configinit.py4
-rw-r--r--qutebrowser/config/configtypes.py108
-rw-r--r--qutebrowser/config/configutils.py30
-rw-r--r--qutebrowser/config/stylesheet.py116
-rw-r--r--qutebrowser/config/websettings.py12
-rw-r--r--qutebrowser/mainwindow/mainwindow.py4
-rw-r--r--qutebrowser/mainwindow/messageview.py13
-rw-r--r--qutebrowser/mainwindow/prompt.py4
-rw-r--r--qutebrowser/mainwindow/statusbar/bar.py14
-rw-r--r--qutebrowser/mainwindow/statusbar/progress.py4
-rw-r--r--qutebrowser/mainwindow/statusbar/url.py4
-rw-r--r--qutebrowser/mainwindow/tabwidget.py4
-rw-r--r--qutebrowser/misc/keyhintwidget.py4
-rw-r--r--qutebrowser/utils/javascript.py3
-rw-r--r--qutebrowser/utils/urlutils.py9
-rw-r--r--qutebrowser/utils/usertypes.py11
-rw-r--r--tests/helpers/fixtures.py5
-rw-r--r--tests/unit/config/test_config.py72
-rw-r--r--tests/unit/config/test_configcommands.py16
-rw-r--r--tests/unit/config/test_configtypes.py6
-rw-r--r--tests/unit/config/test_configutils.py48
-rw-r--r--tests/unit/config/test_stylesheet.py72
-rw-r--r--tests/unit/javascript/stylesheet/test_stylesheet_js.py (renamed from tests/unit/javascript/stylesheet/test_stylesheet.py)0
-rw-r--r--tests/unit/utils/test_urlutils.py23
-rw-r--r--tests/unit/utils/usertypes/test_misc.py9
30 files changed, 375 insertions, 331 deletions
diff --git a/qutebrowser/browser/downloadview.py b/qutebrowser/browser/downloadview.py
index 91a583d08..b0944046b 100644
--- a/qutebrowser/browser/downloadview.py
+++ b/qutebrowser/browser/downloadview.py
@@ -26,7 +26,7 @@ from PyQt5.QtCore import pyqtSlot, QSize, Qt, QTimer
from PyQt5.QtWidgets import QListView, QSizePolicy, QMenu, QStyleFactory
from qutebrowser.browser import downloads
-from qutebrowser.config import config
+from qutebrowser.config import stylesheet
from qutebrowser.utils import qtutils, utils
from qutebrowser.qt import sip
@@ -85,7 +85,7 @@ class DownloadView(QListView):
super().__init__(parent)
if not utils.is_mac:
self.setStyle(QStyleFactory.create('Fusion'))
- config.set_register_stylesheet(self)
+ stylesheet.set_register(self)
self.setResizeMode(QListView.Adjust)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed)
diff --git a/qutebrowser/browser/webkit/webview.py b/qutebrowser/browser/webkit/webview.py
index 1cd9d0a2a..88daf06aa 100644
--- a/qutebrowser/browser/webkit/webview.py
+++ b/qutebrowser/browser/webkit/webview.py
@@ -24,7 +24,7 @@ from PyQt5.QtWidgets import QStyleFactory
from PyQt5.QtWebKit import QWebSettings
from PyQt5.QtWebKitWidgets import QWebView, QWebPage
-from qutebrowser.config import config
+from qutebrowser.config import config, stylesheet
from qutebrowser.keyinput import modeman
from qutebrowser.utils import log, usertypes, utils, objreg, debug
from qutebrowser.browser.webkit import webpage
@@ -84,7 +84,7 @@ class WebView(QWebView):
self.setPage(page)
- config.set_register_stylesheet(self)
+ stylesheet.set_register(self)
def __repr__(self):
urlstr = self.url().toDisplayString(QUrl.EncodeUnicode) # type: ignore
diff --git a/qutebrowser/completion/completionwidget.py b/qutebrowser/completion/completionwidget.py
index 4d90a4305..5b1b080e4 100644
--- a/qutebrowser/completion/completionwidget.py
+++ b/qutebrowser/completion/completionwidget.py
@@ -28,7 +28,7 @@ import typing
from PyQt5.QtWidgets import QTreeView, QSizePolicy, QStyleFactory, QWidget
from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QItemSelectionModel, QSize
-from qutebrowser.config import config
+from qutebrowser.config import config, stylesheet
from qutebrowser.completion import completiondelegate
from qutebrowser.utils import utils, usertypes, debug, log
from qutebrowser.api import cmdutils
@@ -125,7 +125,7 @@ class CompletionView(QTreeView):
self._delegate = completiondelegate.CompletionItemDelegate(self)
self.setItemDelegate(self._delegate)
self.setStyle(QStyleFactory.create('Fusion'))
- config.set_register_stylesheet(self)
+ stylesheet.set_register(self)
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
self.setHeaderHidden(True)
self.setAlternatingRowColors(True)
diff --git a/qutebrowser/config/config.py b/qutebrowser/config/config.py
index 7e10a91f3..41ef6eb3d 100644
--- a/qutebrowser/config/config.py
+++ b/qutebrowser/config/config.py
@@ -23,13 +23,13 @@ import copy
import contextlib
import functools
import typing
-from typing import Any, Optional, FrozenSet
+from typing import Any, Optional
-from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QUrl
+from PyQt5.QtCore import pyqtSignal, QObject, QUrl
from qutebrowser.config import configdata, configexc, configutils
-from qutebrowser.utils import utils, log, jinja, urlmatch
-from qutebrowser.misc import objects, debugcachestats
+from qutebrowser.utils import utils, log, urlmatch
+from qutebrowser.misc import objects
from qutebrowser.keyinput import keyutils
if typing.TYPE_CHECKING:
@@ -278,7 +278,6 @@ class Config(QObject):
yaml_config: 'configfiles.YamlConfig',
parent: QObject = None) -> None:
super().__init__(parent)
- self.changed.connect(_render_stylesheet.cache_clear)
self._mutables = {} # type: MutableMapping[str, Tuple[Any, Any]]
self._yaml = yaml_config
self._init_values()
@@ -397,7 +396,7 @@ class Config(QObject):
"""Get the given setting as object (for YAML/config.py).
This gets the overridden value for a given pattern, or
- configutils.UNSET if no such override exists.
+ usertypes.UNSET if no such override exists.
"""
self.ensure_has_opt(name)
value = self._values[name].get_for_pattern(pattern, fallback=False)
@@ -434,7 +433,7 @@ class Config(QObject):
"""Get the given setting as string.
If a pattern is given, get the setting for the given pattern or
- configutils.UNSET.
+ usertypes.UNSET.
"""
opt = self.get_opt(name)
values = self._values[name]
@@ -617,86 +616,3 @@ class ConfigContainer:
return '{}.{}'.format(self._prefix, attr)
else:
return attr
-
-
-def set_register_stylesheet(obj: QObject, *,
- stylesheet: str = None,
- update: bool = True) -> None:
- """Set the stylesheet for an object.
-
- Also, register an update when the config is changed.
-
- Args:
- obj: The object to set the stylesheet for and register.
- Must have a STYLESHEET attribute if stylesheet is not given.
- stylesheet: The stylesheet to use.
- update: Whether to update the stylesheet on config changes.
- """
- observer = StyleSheetObserver(obj, stylesheet, update)
- observer.register()
-
-
-@debugcachestats.register()
-@functools.lru_cache()
-def _render_stylesheet(stylesheet: str) -> str:
- """Render the given stylesheet jinja template."""
- with jinja.environment.no_autoescape():
- template = jinja.environment.from_string(stylesheet)
- return template.render(conf=val)
-
-
-class StyleSheetObserver(QObject):
-
- """Set the stylesheet on the given object and update it on changes.
-
- Attributes:
- _obj: The object to observe.
- _stylesheet: The stylesheet template to use.
- _options: The config options that the stylesheet uses. When it's not
- necessary to listen for config changes, this attribute may be
- None.
- """
-
- def __init__(self, obj: QObject,
- stylesheet: Optional[str], update: bool) -> None:
- super().__init__()
- self._obj = obj
- self._update = update
-
- # We only need to hang around if we are asked to update.
- if update:
- self.setParent(self._obj)
- if stylesheet is None:
- self._stylesheet = obj.STYLESHEET # type: str
- else:
- self._stylesheet = stylesheet
-
- if update:
- self._options = jinja.template_config_variables(
- self._stylesheet) # type: Optional[FrozenSet[str]]
- else:
- self._options = None
-
- def _get_stylesheet(self) -> str:
- """Format a stylesheet based on a template.
-
- Return:
- The formatted template as string.
- """
- return _render_stylesheet(self._stylesheet)
-
- @pyqtSlot(str)
- def _maybe_update_stylesheet(self, option: str) -> None:
- """Update the stylesheet for obj if the option changed affects it."""
- assert self._options is not None
- if option in self._options:
- self._obj.setStyleSheet(self._get_stylesheet())
-
- def register(self) -> None:
- """Do a first update and listen for more."""
- qss = self._get_stylesheet()
- log.config.vdebug( # type: ignore
- "stylesheet for {}: {}".format(self._obj.__class__.__name__, qss))
- self._obj.setStyleSheet(qss)
- if self._update:
- instance.changed.connect(self._maybe_update_stylesheet)
diff --git a/qutebrowser/config/configexc.py b/qutebrowser/config/configexc.py
index d83ca403b..53b6689f8 100644
--- a/qutebrowser/config/configexc.py
+++ b/qutebrowser/config/configexc.py
@@ -22,7 +22,7 @@
import typing
import attr
-from qutebrowser.utils import jinja, usertypes, log
+from qutebrowser.utils import usertypes, log
class Error(Exception):
@@ -155,6 +155,7 @@ class ConfigFileErrors(Error):
def to_html(self) -> str:
"""Get the error texts as a HTML snippet."""
+ from qutebrowser.utils import jinja # circular import
template = jinja.environment.from_string("""
Errors occurred while reading {{ basename }}:
diff --git a/qutebrowser/config/configinit.py b/qutebrowser/config/configinit.py
index 21f7fa054..e2c359e72 100644
--- a/qutebrowser/config/configinit.py
+++ b/qutebrowser/config/configinit.py
@@ -28,7 +28,7 @@ from PyQt5.QtWidgets import QMessageBox
from qutebrowser.api import config as configapi
from qutebrowser.config import (config, configdata, configfiles, configtypes,
- configexc, configcommands)
+ configexc, configcommands, stylesheet)
from qutebrowser.utils import (objreg, usertypes, log, standarddir, message,
qtutils)
from qutebrowser.config import configcache
@@ -88,6 +88,8 @@ def early_init(args: argparse.Namespace) -> None:
configtypes.Font.monospace_fonts = config.val.fonts.monospace
config.instance.changed.connect(_update_monospace_fonts)
+ stylesheet.init()
+
_init_envvars()
diff --git a/qutebrowser/config/configtypes.py b/qutebrowser/config/configtypes.py
index be1f7edcf..475879de0 100644
--- a/qutebrowser/config/configtypes.py
+++ b/qutebrowser/config/configtypes.py
@@ -61,7 +61,7 @@ from PyQt5.QtWidgets import QTabWidget, QTabBar
from PyQt5.QtNetwork import QNetworkProxy
from qutebrowser.misc import objects, debugcachestats
-from qutebrowser.config import configexc, configutils
+from qutebrowser.config import configexc
from qutebrowser.utils import (standarddir, utils, qtutils, urlutils, urlmatch,
usertypes)
from qutebrowser.keyinput import keyutils
@@ -80,8 +80,8 @@ BOOLEAN_STATES = {'1': True, 'yes': True, 'true': True, 'on': True,
_Completions = typing.Optional[typing.Iterable[typing.Tuple[str, str]]]
-_StrUnset = typing.Union[str, configutils.Unset]
-_StrUnsetNone = typing.Union[None, str, configutils.Unset]
+_StrUnset = typing.Union[str, usertypes.Unset]
+_StrUnsetNone = typing.Union[None, str, usertypes.Unset]
class ValidValues:
@@ -168,7 +168,7 @@ class BaseType:
value: The value to check.
pytype: A Python type to check the value against.
"""
- if isinstance(value, configutils.Unset):
+ if isinstance(value, usertypes.Unset):
return
if (value is None or (pytype == list and value == []) or
@@ -342,7 +342,7 @@ class MappingType(BaseType):
def to_py(self, value: typing.Any) -> typing.Any:
self._basic_py_validation(value, str)
- if isinstance(value, configutils.Unset):
+ if isinstance(value, usertypes.Unset):
return value
elif not value:
return None
@@ -408,7 +408,7 @@ class String(BaseType):
def to_py(self, value: _StrUnset) -> _StrUnsetNone:
self._basic_py_validation(value, str)
- if isinstance(value, configutils.Unset):
+ if isinstance(value, usertypes.Unset):
return value
elif not value:
return None
@@ -450,7 +450,7 @@ class UniqueCharString(String):
def to_py(self, value: _StrUnset) -> _StrUnsetNone:
py_value = super().to_py(value)
- if isinstance(py_value, configutils.Unset):
+ if isinstance(py_value, usertypes.Unset):
return py_value
elif not py_value:
return None
@@ -510,10 +510,10 @@ class List(BaseType):
def to_py(
self,
- value: typing.Union[typing.List, configutils.Unset]
- ) -> typing.Union[typing.List, configutils.Unset]:
+ value: typing.Union[typing.List, usertypes.Unset]
+ ) -> typing.Union[typing.List, usertypes.Unset]:
self._basic_py_validation(value, list)
- if isinstance(value, configutils.Unset):
+ if isinstance(value, usertypes.Unset):
return value
elif not value:
return []
@@ -601,7 +601,7 @@ class ListOrValue(BaseType):
return value
def to_py(self, value: typing.Any) -> typing.Any:
- if isinstance(value, configutils.Unset):
+ if isinstance(value, usertypes.Unset):
return value
try:
@@ -652,10 +652,10 @@ class FlagList(List):
def to_py(
self,
- value: typing.Union[configutils.Unset, typing.List],
- ) -> typing.Union[configutils.Unset, typing.List]:
+ value: typing.Union[usertypes.Unset, typing.List],
+ ) -> typing.Union[usertypes.Unset, typing.List]:
vals = super().to_py(value)
- if not isinstance(vals, configutils.Unset):
+ if not isinstance(vals, usertypes.Unset):
self._check_duplicates(vals)
return vals
@@ -866,10 +866,10 @@ class Perc(_Numeric):
def to_py(
self,
- value: typing.Union[None, float, int, str, configutils.Unset]
- ) -> typing.Union[None, float, int, configutils.Unset]:
+ value: typing.Union[None, float, int, str, usertypes.Unset]
+ ) -> typing.Union[None, float, int, usertypes.Unset]:
self._basic_py_validation(value, (float, int, str))
- if isinstance(value, configutils.Unset):
+ if isinstance(value, usertypes.Unset):
return value
elif not value:
return None
@@ -1062,10 +1062,10 @@ class QtColor(BaseType):
except ValueError:
raise configexc.ValidationError(val, "must be a valid color value")
- def to_py(self, value: _StrUnset) -> typing.Union[configutils.Unset,
+ def to_py(self, value: _StrUnset) -> typing.Union[usertypes.Unset,
None, QColor]:
self._basic_py_validation(value, str)
- if isinstance(value, configutils.Unset):
+ if isinstance(value, usertypes.Unset):
return value
elif not value:
return None
@@ -1123,7 +1123,7 @@ class QssColor(BaseType):
def to_py(self, value: _StrUnset) -> _StrUnsetNone:
self._basic_py_validation(value, str)
- if isinstance(value, configutils.Unset):
+ if isinstance(value, usertypes.Unset):
return value
elif not value:
return None
@@ -1170,7 +1170,7 @@ class Font(BaseType):
def to_py(self, value: _StrUnset) -> _StrUnsetNone:
self._basic_py_validation(value, str)
- if isinstance(value, configutils.Unset):
+ if isinstance(value, usertypes.Unset):
return value
elif not value:
return None
@@ -1191,7 +1191,7 @@ class FontFamily(Font):
def to_py(self, value: _StrUnset) -> _StrUnsetNone:
self._basic_py_validation(value, str)
- if isinstance(value, configutils.Unset):
+ if isinstance(value, usertypes.Unset):
return value
elif not value:
return None
@@ -1215,10 +1215,10 @@ class QtFont(Font):
__doc__ = Font.__doc__ # for src2asciidoc.py
- def to_py(self, value: _StrUnset) -> typing.Union[configutils.Unset,
+ def to_py(self, value: _StrUnset) -> typing.Union[usertypes.Unset,
None, QFont]:
self._basic_py_validation(value, str)
- if isinstance(value, configutils.Unset):
+ if isinstance(value, usertypes.Unset):
return value
elif not value:
return None
@@ -1333,11 +1333,11 @@ class Regex(BaseType):
def to_py(
self,
- value: typing.Union[str, typing.Pattern[str], configutils.Unset]
- ) -> typing.Union[configutils.Unset, None, typing.Pattern[str]]:
+ value: typing.Union[str, typing.Pattern[str], usertypes.Unset]
+ ) -> typing.Union[usertypes.Unset, None, typing.Pattern[str]]:
"""Get a compiled regex from either a string or a regex object."""
self._basic_py_validation(value, (str, self._regex_type))
- if isinstance(value, configutils.Unset):
+ if isinstance(value, usertypes.Unset):
return value
elif not value:
return None
@@ -1425,10 +1425,10 @@ class Dict(BaseType):
def to_py(
self,
- value: typing.Union[typing.Dict, configutils.Unset, None]
- ) -> typing.Union[typing.Dict, configutils.Unset]:
+ value: typing.Union[typing.Dict, usertypes.Unset, None]
+ ) -> typing.Union[typing.Dict, usertypes.Unset]:
self._basic_py_validation(value, dict)
- if isinstance(value, configutils.Unset):
+ if isinstance(value, usertypes.Unset):
return value
elif not value:
return self._fill_fixed_keys({})
@@ -1477,7 +1477,7 @@ class File(BaseType):
def to_py(self, value: _StrUnset) -> _StrUnsetNone:
self._basic_py_validation(value, str)
- if isinstance(value, configutils.Unset):
+ if isinstance(value, usertypes.Unset):
return value
elif not value:
return None
@@ -1509,7 +1509,7 @@ class Directory(BaseType):
def to_py(self, value: _StrUnset) -> _StrUnsetNone:
self._basic_py_validation(value, str)
- if isinstance(value, configutils.Unset):
+ if isinstance(value, usertypes.Unset):
return value
elif not value:
return None
@@ -1546,7 +1546,7 @@ class FormatString(BaseType):
def to_py(self, value: _StrUnset) -> _StrUnsetNone:
self._basic_py_validation(value, str)
- if isinstance(value, configutils.Unset):
+ if isinstance(value, usertypes.Unset):
return value
elif not value:
return None
@@ -1590,10 +1590,10 @@ class ShellCommand(List):
def to_py(
self,
- value: typing.Union[typing.List, configutils.Unset],
- ) -> typing.Union[typing.List, configutils.Unset]:
+ value: typing.Union[typing.List, usertypes.Unset],
+ ) -> typing.Union[typing.List, usertypes.Unset]:
py_value = super().to_py(value)
- if isinstance(py_value, configutils.Unset):
+ if isinstance(py_value, usertypes.Unset):
return py_value
elif not py_value:
return []
@@ -1624,9 +1624,9 @@ class Proxy(BaseType):
def to_py(
self,
value: _StrUnset
- ) -> typing.Union[configutils.Unset, None, QNetworkProxy, _SystemProxy]:
+ ) -> typing.Union[usertypes.Unset, None, QNetworkProxy, _SystemProxy]:
self._basic_py_validation(value, str)
- if isinstance(value, configutils.Unset):
+ if isinstance(value, usertypes.Unset):
return value
elif not value:
return None
@@ -1666,7 +1666,7 @@ class SearchEngineUrl(BaseType):
def to_py(self, value: _StrUnset) -> _StrUnsetNone:
self._basic_py_validation(value, str)
- if isinstance(value, configutils.Unset):
+ if isinstance(value, usertypes.Unset):
return value
elif not value:
return None
@@ -1696,7 +1696,7 @@ class FuzzyUrl(BaseType):
def to_py(self, value: _StrUnset) -> _StrUnsetNone:
self._basic_py_validation(value, str)
- if isinstance(value, configutils.Unset):
+ if isinstance(value, usertypes.Unset):
return value
elif not value:
return None
@@ -1732,10 +1732,10 @@ class Padding(Dict):
def to_py( # type: ignore
self,
- value: typing.Union[configutils.Unset, typing.Dict, None],
- ) -> typing.Union[configutils.Unset, PaddingValues]:
+ value: typing.Union[usertypes.Unset, typing.Dict, None],
+ ) -> typing.Union[usertypes.Unset, PaddingValues]:
d = super().to_py(value)
- if isinstance(d, configutils.Unset):
+ if isinstance(d, usertypes.Unset):
return d
return PaddingValues(**d)
@@ -1747,7 +1747,7 @@ class Encoding(BaseType):
def to_py(self, value: _StrUnset) -> _StrUnsetNone:
self._basic_py_validation(value, str)
- if isinstance(value, configutils.Unset):
+ if isinstance(value, usertypes.Unset):
return value
elif not value:
return None
@@ -1807,9 +1807,9 @@ class Url(BaseType):
def to_py(
self,
value: _StrUnset
- ) -> typing.Union[configutils.Unset, None, QUrl]:
+ ) -> typing.Union[usertypes.Unset, None, QUrl]:
self._basic_py_validation(value, str)
- if isinstance(value, configutils.Unset):
+ if isinstance(value, usertypes.Unset):
return value
elif not value:
return None
@@ -1827,7 +1827,7 @@ class SessionName(BaseType):
def to_py(self, value: _StrUnset) -> _StrUnsetNone:
self._basic_py_validation(value, str)
- if isinstance(value, configutils.Unset):
+ if isinstance(value, usertypes.Unset):
return value
elif not value:
return None
@@ -1877,10 +1877,10 @@ class ConfirmQuit(FlagList):
def to_py(
self,
- value: typing.Union[configutils.Unset, typing.List],
- ) -> typing.Union[typing.List, configutils.Unset]:
+ value: typing.Union[usertypes.Unset, typing.List],
+ ) -> typing.Union[typing.List, usertypes.Unset]:
values = super().to_py(value)
- if isinstance(values, configutils.Unset):
+ if isinstance(values, usertypes.Unset):
return values
elif not values:
return []
@@ -1921,9 +1921,9 @@ class Key(BaseType):
def to_py(
self,
value: _StrUnset
- ) -> typing.Union[configutils.Unset, None, keyutils.KeySequence]:
+ ) -> typing.Union[usertypes.Unset, None, keyutils.KeySequence]:
self._basic_py_validation(value, str)
- if isinstance(value, configutils.Unset):
+ if isinstance(value, usertypes.Unset):
return value
elif not value:
return None
@@ -1945,9 +1945,9 @@ class UrlPattern(BaseType):
def to_py(
self,
value: _StrUnset
- ) -> typing.Union[configutils.Unset, None, urlmatch.UrlPattern]:
+ ) -> typing.Union[usertypes.Unset, None, urlmatch.UrlPattern]:
self._basic_py_validation(value, str)
- if isinstance(value, configutils.Unset):
+ if isinstance(value, usertypes.Unset):
return value
elif not value:
return None
diff --git a/qutebrowser/config/configutils.py b/qutebrowser/config/configutils.py
index 506c8eeda..1a7f612cb 100644
--- a/qutebrowser/config/configutils.py
+++ b/qutebrowser/config/configutils.py
@@ -28,24 +28,20 @@ import operator
from PyQt5.QtCore import QUrl
-from qutebrowser.utils import utils, urlmatch, urlutils
+from qutebrowser.utils import utils, urlmatch, usertypes
from qutebrowser.config import configexc
if typing.TYPE_CHECKING:
from qutebrowser.config import configdata
-class Unset:
+def _widened_hostnames(hostname: str) -> typing.Iterable[str]:
+ """A generator for widening string hostnames.
- """Sentinel object."""
-
- __slots__ = ()
-
- def __repr__(self) -> str:
- return '<UNSET>'
-
-
-UNSET = Unset()
+ Ex: a.c.foo -> [a.c.foo, c.foo, foo]"""
+ while hostname:
+ yield hostname
+ hostname = hostname.partition(".")[-1]
class ScopedValue:
@@ -213,7 +209,7 @@ class Values:
if fallback:
return self.opt.default
else:
- return UNSET
+ return usertypes.UNSET
def get_for_url(self, url: QUrl = None, *,
fallback: bool = True) -> typing.Any:
@@ -222,14 +218,14 @@ class Values:
This first tries to find a value matching the URL (if given).
If there's no match:
With fallback=True, the global/default setting is returned.
- With fallback=False, UNSET is returned.
+ With fallback=False, usertypes.UNSET is returned.
"""
self._check_pattern_support(url)
if url is None:
return self._get_fallback(fallback)
candidates = [] # type: typing.List[ScopedValue]
- widened_hosts = urlutils.widened_hostnames(url.host())
+ widened_hosts = _widened_hostnames(url.host())
# We must check the 'None' key as well, in case any patterns that
# did not have a domain match.
for host in itertools.chain(widened_hosts, [None]):
@@ -243,7 +239,7 @@ class Values:
return scoped.value
if not fallback:
- return UNSET
+ return usertypes.UNSET
return self._get_fallback(fallback)
@@ -256,7 +252,7 @@ class Values:
If there's no match:
With fallback=True, the global/default setting is returned.
- With fallback=False, UNSET is returned.
+ With fallback=False, usertypes.UNSET is returned.
"""
self._check_pattern_support(pattern)
if pattern is not None:
@@ -264,6 +260,6 @@ class Values:
return self._vmap[pattern].value
if not fallback:
- return UNSET
+ return usertypes.UNSET
return self._get_fallback(fallback)
diff --git a/qutebrowser/config/stylesheet.py b/qutebrowser/config/stylesheet.py
new file mode 100644
index 000000000..27432d01b
--- /dev/null
+++ b/qutebrowser/config/stylesheet.py
@@ -0,0 +1,116 @@
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2019 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/>.
+
+"""Handling of Qt qss stylesheets."""
+
+import functools
+from typing import Optional, FrozenSet
+
+from PyQt5.QtCore import pyqtSlot, QObject
+
+from qutebrowser.config import config
+from qutebrowser.misc import debugcachestats
+from qutebrowser.utils import jinja, log
+
+
+def set_register(obj: QObject,
+ stylesheet: str = None, *,
+ update: bool = True) -> None:
+ """Set the stylesheet for an object.
+
+ Also, register an update when the config is changed.
+
+ Args:
+ obj: The object to set the stylesheet for and register.
+ Must have a STYLESHEET attribute if stylesheet is not given.
+ stylesheet: The stylesheet to use.
+ update: Whether to update the stylesheet on config changes.
+ """
+ observer = _StyleSheetObserver(obj, stylesheet, update)
+ observer.register()
+
+
+@debugcachestats.register()
+@functools.lru_cache()
+def _render_stylesheet(stylesheet: str) -> str:
+ """Render the given stylesheet jinja template."""
+ with jinja.environment.no_autoescape():
+ template = jinja.environment.from_string(stylesheet)
+ return template.render(conf=config.val)
+
+
+def init() -> None:
+ config.instance.changed.connect(_render_stylesheet.cache_clear)
+
+
+class _StyleSheetObserver(QObject):
+
+ """Set the stylesheet on the given object and update it on changes.
+
+ Attributes:
+ _obj: The object to observe.
+ _stylesheet: The stylesheet template to use.
+ _options: The config options that the stylesheet uses. When it's not
+ necessary to listen for config changes, this attribute may be
+ None.
+ """
+
+ def __init__(self, obj: QObject,
+ stylesheet: Optional[str], update: bool) -> None:
+ super().__init__()
+ self._obj = obj
+ self._update = update
+
+ # We only need to hang around if we are asked to update.
+ if update:
+ self.setParent(self._obj)
+ if stylesheet is None:
+ self._stylesheet = obj.STYLESHEET # type: str
+ else:
+ self._stylesheet = stylesheet
+
+ if update:
+ self._options = jinja.template_config_variables(
+ self._stylesheet) # type: Optional[FrozenSet[str]]
+ else:
+ self._options = None
+
+ def _get_stylesheet(self) -> str:
+ """Format a stylesheet based on a template.
+
+ Return:
+ The formatted template as string.
+ """
+ return _render_stylesheet(self._stylesheet)
+
+ @pyqtSlot(str)
+ def _maybe_update_stylesheet(self, option: str) -> None:
+ """Update the stylesheet for obj if the option changed affects it."""
+ assert self._options is not None
+ if option in self._options:
+ self._obj.setStyleSheet(self._get_stylesheet())
+
+ def register(self) -> None:
+ """Do a first update and listen for more."""
+ qss = self._get_stylesheet()
+ log.config.vdebug( # type: ignore
+ "stylesheet for {}: {}".format(self._obj.__class__.__name__, qss))
+ self._obj.setStyleSheet(qss)
+ if self._update:
+ config.instance.changed.connect(self._maybe_update_stylesheet)
diff --git a/qutebrowser/config/websettings.py b/qutebrowser/config/websettings.py
index 7b839a009..2f78b561e 100644
--- a/qutebrowser/config/websettings.py
+++ b/qutebrowser/config/websettings.py
@@ -29,7 +29,7 @@ from PyQt5.QtCore import QUrl, pyqtSlot, qVersion
from PyQt5.QtGui import QFont
import qutebrowser
-from qutebrowser.config import config, configutils
+from qutebrowser.config import config
from qutebrowser.utils import log, usertypes, urlmatch, qtutils
from qutebrowser.misc import objects, debugcachestats
@@ -106,7 +106,7 @@ class AbstractSettings:
def set_attribute(self, name: str, value: typing.Any) -> bool:
"""Set the given QWebSettings/QWebEngineSettings attribute.
- If the value is configutils.UNSET, the value is reset instead.
+ If the value is usertypes.UNSET, the value is reset instead.
Return:
True if there was a change, False otherwise.
@@ -115,7 +115,7 @@ class AbstractSettings:
info = self._ATTRIBUTES[name]
for attribute in info.attributes:
- if value is configutils.UNSET:
+ if value is usertypes.UNSET:
self._settings.resetAttribute(attribute)
new_value = self.test_attribute(name)
else:
@@ -139,7 +139,7 @@ class AbstractSettings:
Return:
True if there was a change, False otherwise.
"""
- assert value is not configutils.UNSET # type: ignore
+ assert value is not usertypes.UNSET # type: ignore
family = self._FONT_SIZES[name]
old_value = self._settings.fontSize(family)
self._settings.setFontSize(family, value)
@@ -154,7 +154,7 @@ class AbstractSettings:
Return:
True if there was a change, False otherwise.
"""
- assert value is not configutils.UNSET # type: ignore
+ assert value is not usertypes.UNSET # type: ignore
family = self._FONT_FAMILIES[name]
if value is None:
font = QFont()
@@ -172,7 +172,7 @@ class AbstractSettings:
Return:
True if there was a change, False otherwise.
"""
- assert encoding is not configutils.UNSET # type: ignore
+ assert encoding is not usertypes.UNSET # type: ignore
old_value = self._settings.defaultTextEncoding()
self._settings.setDefaultTextEncoding(encoding)
return old_value != encoding
diff --git a/qutebrowser/mainwindow/mainwindow.py b/qutebrowser/mainwindow/mainwindow.py
index ac5df12e6..e0f047bfb 100644
--- a/qutebrowser/mainwindow/mainwindow.py
+++ b/qutebrowser/mainwindow/mainwindow.py
@@ -31,7 +31,7 @@ from PyQt5.QtWidgets import QWidget, QVBoxLayout, QApplication, QSizePolicy
from qutebrowser.commands import runners
from qutebrowser.api import cmdutils
-from qutebrowser.config import config, configfiles
+from qutebrowser.config import config, configfiles, stylesheet
from qutebrowser.utils import (message, log, usertypes, qtutils, objreg, utils,
jinja, debug)
from qutebrowser.mainwindow import messageview, prompt
@@ -269,7 +269,7 @@ class MainWindow(QWidget):
self._set_decoration(config.val.window.hide_decoration)
self.state_before_fullscreen = self.windowState()
- config.set_register_stylesheet(self)
+ stylesheet.set_register(self)
def _init_geometry(self, geometry):
"""Initialize the window geometry or load it from disk."""
diff --git a/qutebrowser/mainwindow/messageview.py b/qutebrowser/mainwindow/messageview.py
index ea14265aa..3660d3529 100644
--- a/qutebrowser/mainwindow/messageview.py
+++ b/qutebrowser/mainwindow/messageview.py
@@ -24,7 +24,7 @@ import typing
from PyQt5.QtCore import pyqtSlot, pyqtSignal, QTimer, Qt, QSize
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QSizePolicy
-from qutebrowser.config import config
+from qutebrowser.config import config, stylesheet
from qutebrowser.utils import usertypes
@@ -36,19 +36,19 @@ class Message(QLabel):
super().__init__(text, parent)
self.replace = replace
self.setAttribute(Qt.WA_StyledBackground, True)
- stylesheet = """
+ qss = """
padding-top: 2px;
padding-bottom: 2px;
"""
if level == usertypes.MessageLevel.error:
- stylesheet += """
+ qss += """
background-color: {{ conf.colors.messages.error.bg }};
color: {{ conf.colors.messages.error.fg }};
font: {{ conf.fonts.messages.error }};
border-bottom: 1px solid {{ conf.colors.messages.error.border }};
"""
elif level == usertypes.MessageLevel.warning:
- stylesheet += """
+ qss += """
background-color: {{ conf.colors.messages.warning.bg }};
color: {{ conf.colors.messages.warning.fg }};
font: {{ conf.fonts.messages.warning }};
@@ -56,7 +56,7 @@ class Message(QLabel):
1px solid {{ conf.colors.messages.warning.border }};
"""
elif level == usertypes.MessageLevel.info:
- stylesheet += """
+ qss += """
background-color: {{ conf.colors.messages.info.bg }};
color: {{ conf.colors.messages.info.fg }};
font: {{ conf.fonts.messages.info }};
@@ -66,8 +66,7 @@ class Message(QLabel):
raise ValueError("Invalid level {!r}".format(level))
# We don't bother with set_register_stylesheet here as it's short-lived
# anyways.
- config.set_register_stylesheet(self, stylesheet=stylesheet,
- update=False)
+ stylesheet.set_register(self, qss, update=False)
class MessageView(QWidget):
diff --git a/qutebrowser/mainwindow/prompt.py b/qutebrowser/mainwindow/prompt.py
index 6f7d1b9f7..ba6ff2f1b 100644
--- a/qutebrowser/mainwindow/prompt.py
+++ b/qutebrowser/mainwindow/prompt.py
@@ -33,7 +33,7 @@ from PyQt5.QtWidgets import (QWidget, QGridLayout, QVBoxLayout, QLineEdit,
QSpacerItem)
from qutebrowser.browser import downloads
-from qutebrowser.config import config, configtypes, configexc
+from qutebrowser.config import config, configtypes, configexc, stylesheet
from qutebrowser.utils import usertypes, log, utils, qtutils, objreg, message
from qutebrowser.keyinput import modeman
from qutebrowser.api import cmdutils
@@ -292,7 +292,7 @@ class PromptContainer(QWidget):
self.setObjectName('PromptContainer')
self.setAttribute(Qt.WA_StyledBackground, True)
- config.set_register_stylesheet(self)
+ stylesheet.set_register(self)
message.global_bridge.prompt_done.connect(self._on_prompt_done)
prompt_queue.show_prompts.connect(self._on_show_prompts)
diff --git a/qutebrowser/mainwindow/statusbar/bar.py b/qutebrowser/mainwindow/statusbar/bar.py
index 463e0c151..dd50024b3 100644
--- a/qutebrowser/mainwindow/statusbar/bar.py
+++ b/qutebrowser/mainwindow/statusbar/bar.py
@@ -25,7 +25,7 @@ from PyQt5.QtCore import pyqtSignal, pyqtSlot, pyqtProperty, Qt, QSize, QTimer
from PyQt5.QtWidgets import QWidget, QHBoxLayout, QStackedLayout, QSizePolicy
from qutebrowser.browser import browsertab
-from qutebrowser.config import config
+from qutebrowser.config import config, stylesheet
from qutebrowser.keyinput import modeman
from qutebrowser.utils import usertypes, log, objreg, utils
from qutebrowser.mainwindow.statusbar import (backforward, command, progress,
@@ -97,7 +97,7 @@ def _generate_stylesheet():
('passthrough', 'statusbar.passthrough'),
('private-command', 'statusbar.command.private'),
]
- stylesheet = """
+ qss = """
QWidget#StatusBar,
QWidget#StatusBar QLabel,
QWidget#StatusBar QLineEdit {
@@ -110,7 +110,7 @@ def _generate_stylesheet():
}
"""
for flag, option in flags:
- stylesheet += """
+ qss += """
QWidget#StatusBar[color_flags~="%s"],
QWidget#StatusBar[color_flags~="%s"] QLabel,
QWidget#StatusBar[color_flags~="%s"] QLineEdit {
@@ -122,7 +122,7 @@ def _generate_stylesheet():
}
""" % (flag, flag, flag, # noqa: S001
option + '.fg', flag, option + '.bg')
- return stylesheet
+ return qss
class StatusBar(QWidget):
@@ -158,7 +158,7 @@ class StatusBar(QWidget):
super().__init__(parent)
self.setObjectName(self.__class__.__name__)
self.setAttribute(Qt.WA_StyledBackground)
- config.set_register_stylesheet(self)
+ stylesheet.set_register(self)
self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Fixed)
@@ -298,7 +298,7 @@ class StatusBar(QWidget):
# Turning on is handled in on_current_caret_selection_toggled
log.statusbar.debug("Setting caret mode off")
self._color_flags.caret = ColorFlags.CaretMode.off
- config.set_register_stylesheet(self, update=False)
+ stylesheet.set_register(self, update=False)
def _set_mode_text(self, mode):
"""Set the mode text."""
@@ -382,7 +382,7 @@ class StatusBar(QWidget):
else:
self._set_mode_text("caret")
self._color_flags.caret = ColorFlags.CaretMode.on
- config.set_register_stylesheet(self, update=False)
+ stylesheet.set_register(self, update=False)
def resizeEvent(self, e):
"""Extend resizeEvent of QWidget to emit a resized signal afterwards.
diff --git a/qutebrowser/mainwindow/statusbar/progress.py b/qutebrowser/mainwindow/statusbar/progress.py
index 34c1954e8..389ddf12e 100644
--- a/qutebrowser/mainwindow/statusbar/progress.py
+++ b/qutebrowser/mainwindow/statusbar/progress.py
@@ -22,7 +22,7 @@
from PyQt5.QtCore import pyqtSlot, QSize
from PyQt5.QtWidgets import QProgressBar, QSizePolicy
-from qutebrowser.config import config
+from qutebrowser.config import stylesheet
from qutebrowser.utils import utils, usertypes
@@ -45,7 +45,7 @@ class Progress(QProgressBar):
def __init__(self, parent=None):
super().__init__(parent)
- config.set_register_stylesheet(self)
+ stylesheet.set_register(self)
self.enabled = False
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
self.setTextVisible(False)
diff --git a/qutebrowser/mainwindow/statusbar/url.py b/qutebrowser/mainwindow/statusbar/url.py
index d6aacb184..7a84b1e4e 100644
--- a/qutebrowser/mainwindow/statusbar/url.py
+++ b/qutebrowser/mainwindow/statusbar/url.py
@@ -24,7 +24,7 @@ import enum
from PyQt5.QtCore import pyqtSlot, pyqtProperty, QUrl
from qutebrowser.mainwindow.statusbar import textbase
-from qutebrowser.config import config
+from qutebrowser.config import stylesheet
from qutebrowser.utils import usertypes, urlutils
@@ -76,7 +76,7 @@ class UrlText(textbase.TextBase):
super().__init__(parent)
self._urltype = None
self.setObjectName(self.__class__.__name__)
- config.set_register_stylesheet(self)
+ stylesheet.set_register(self)
self._hover_url = None
self._normal_url = None
self._normal_url_type = UrlType.normal
diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py
index ba6ffb79f..6e3c00aad 100644
--- a/qutebrowser/mainwindow/tabwidget.py
+++ b/qutebrowser/mainwindow/tabwidget.py
@@ -32,7 +32,7 @@ from PyQt5.QtWidgets import (QTabWidget, QTabBar, QSizePolicy, QCommonStyle,
from PyQt5.QtGui import QIcon, QPalette, QColor
from qutebrowser.utils import qtutils, objreg, utils, usertypes, log
-from qutebrowser.config import config
+from qutebrowser.config import config, stylesheet
from qutebrowser.misc import objects, debugcachestats
from qutebrowser.browser import browsertab
@@ -400,7 +400,7 @@ class TabBar(QTabBar):
self._on_show_switching_delay_changed()
self.setAutoFillBackground(True)
self.drag_in_progress = False
- config.set_register_stylesheet(self)
+ stylesheet.set_register(self)
QTimer.singleShot(0, self.maybe_hide)
def __repr__(self):
diff --git a/qutebrowser/misc/keyhintwidget.py b/qutebrowser/misc/keyhintwidget.py
index 9cc00c6a8..68f9685d3 100644
--- a/qutebrowser/misc/keyhintwidget.py
+++ b/qutebrowser/misc/keyhintwidget.py
@@ -31,7 +31,7 @@ import re
from PyQt5.QtWidgets import QLabel, QSizePolicy
from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt
-from qutebrowser.config import config
+from qutebrowser.config import config, stylesheet
from qutebrowser.utils import utils, usertypes
from qutebrowser.misc import objects
from qutebrowser.keyinput import keyutils
@@ -72,7 +72,7 @@ class KeyHintView(QLabel):
self._show_timer = usertypes.Timer(self, 'keyhint_show')
self._show_timer.timeout.connect(self.show)
self._show_timer.setSingleShot(True)
- config.set_register_stylesheet(self)
+ stylesheet.set_register(self)
def __repr__(self):
return utils.get_repr(self, win_id=self._win_id)
diff --git a/qutebrowser/utils/javascript.py b/qutebrowser/utils/javascript.py
index f456d93c9..2c02975db 100644
--- a/qutebrowser/utils/javascript.py
+++ b/qutebrowser/utils/javascript.py
@@ -21,8 +21,6 @@
import typing
-from qutebrowser.utils import jinja
-
_InnerJsArgType = typing.Union[None, str, bool, int, float]
_JsArgType = typing.Union[_InnerJsArgType, typing.Sequence[_InnerJsArgType]]
@@ -83,5 +81,6 @@ def assemble(module: str, function: str, *args: _JsArgType) -> str:
def wrap_global(name: str, *sources: str) -> str:
"""Wrap a script using window._qutebrowser."""
+ from qutebrowser.utils import jinja # circular import
template = jinja.js_environment.get_template('global_wrapper.js')
return template.render(code='\n'.join(sources), name=name)
diff --git a/qutebrowser/utils/urlutils.py b/qutebrowser/utils/urlutils.py
index 51f0fd6cc..2908327dd 100644
--- a/qutebrowser/utils/urlutils.py
+++ b/qutebrowser/utils/urlutils.py
@@ -617,12 +617,3 @@ def proxy_from_url(url: QUrl) -> QNetworkProxy:
if url.password():
proxy.setPassword(url.password())
return proxy
-
-
-def widened_hostnames(hostname: str) -> typing.Iterable[str]:
- """A generator for widening string hostnames.
-
- Ex: a.c.foo -> [a.c.foo, c.foo, foo]"""
- while hostname:
- yield hostname
- hostname = hostname.partition(".")[-1]
diff --git a/qutebrowser/utils/usertypes.py b/qutebrowser/utils/usertypes.py
index 58fe950b2..65ab2d491 100644
--- a/qutebrowser/utils/usertypes.py
+++ b/qutebrowser/utils/usertypes.py
@@ -33,14 +33,17 @@ from qutebrowser.utils import log, qtutils, utils
_T = typing.TypeVar('_T')
-class UnsetObject:
+class Unset:
"""Class for an unset object."""
__slots__ = ()
+ def __repr__(self) -> str:
+ return '<UNSET>'
+
-UNSET = UnsetObject()
+UNSET = Unset()
class NeighborList(typing.Sequence[_T]):
@@ -60,7 +63,7 @@ class NeighborList(typing.Sequence[_T]):
Modes = enum.Enum('Modes', ['edge', 'exception'])
def __init__(self, items: typing.Sequence[_T] = None,
- default: typing.Union[_T, UnsetObject] = UNSET,
+ default: typing.Union[_T, Unset] = UNSET,
mode: Modes = Modes.exception) -> None:
"""Constructor.
@@ -79,7 +82,7 @@ class NeighborList(typing.Sequence[_T]):
self._items = list(items)
self._default = default
- if not isinstance(default, UnsetObject):
+ if not isinstance(default, Unset):
idx = self._items.index(default)
self._idx = idx # type: typing.Optional[int]
else:
diff --git a/tests/helpers/fixtures.py b/tests/helpers/fixtures.py
index 880438d93..045be5e94 100644
--- a/tests/helpers/fixtures.py
+++ b/tests/helpers/fixtures.py
@@ -43,7 +43,7 @@ from PyQt5.QtNetwork import QNetworkCookieJar
import helpers.stubs as stubsmod
from qutebrowser.config import (config, configdata, configtypes, configexc,
- configfiles, configcache)
+ configfiles, configcache, stylesheet)
from qutebrowser.api import config as configapi
from qutebrowser.utils import objreg, standarddir, utils, usertypes
from qutebrowser.browser import greasemonkey, history, qutescheme
@@ -319,6 +319,9 @@ def config_stub(stubs, monkeypatch, configdata_init, yaml_config_stub):
pass
conf.val = container # For easier use in tests
+
+ stylesheet.init()
+
return conf
diff --git a/tests/unit/config/test_config.py b/tests/unit/config/test_config.py
index 9eefedf15..d81cb91dd 100644
--- a/tests/unit/config/test_config.py
+++ b/tests/unit/config/test_config.py
@@ -23,10 +23,10 @@ import unittest.mock
import functools
import pytest
-from PyQt5.QtCore import QObject, QUrl
+from PyQt5.QtCore import QUrl
from PyQt5.QtGui import QColor
-from qutebrowser.config import config, configdata, configexc, configutils
+from qutebrowser.config import config, configdata, configexc
from qutebrowser.utils import usertypes, urlmatch
from qutebrowser.misc import objects
from qutebrowser.keyinput import keyutils
@@ -410,7 +410,7 @@ class TestConfig:
assert conf.get(name) == 'always'
if save_yaml:
- assert yaml_value(name) is configutils.UNSET
+ assert yaml_value(name) is usertypes.UNSET
else:
assert yaml_value(name) == 'never'
@@ -439,8 +439,8 @@ class TestConfig:
assert options == {name1, name2}
if save_yaml:
- assert yaml_value(name1) is configutils.UNSET
- assert yaml_value(name2) is configutils.UNSET
+ assert yaml_value(name1) is usertypes.UNSET
+ assert yaml_value(name2) is usertypes.UNSET
else:
assert yaml_value(name1) == 'never'
assert yaml_value(name2) is True
@@ -482,7 +482,7 @@ class TestConfig:
@pytest.mark.parametrize('fallback, expected', [
(True, True),
- (False, configutils.UNSET)
+ (False, usertypes.UNSET)
])
def test_get_for_url_fallback(self, conf, fallback, expected):
"""Test conf.get() with a URL and fallback."""
@@ -617,7 +617,7 @@ class TestConfig:
pattern = urlmatch.UrlPattern('*://example.com')
name = 'content.javascript.enabled'
value = conf.get_obj_for_pattern(name, pattern=pattern)
- assert value is configutils.UNSET
+ assert value is usertypes.UNSET
def test_get_str(self, conf):
assert conf.get_str('content.plugins') == 'false'
@@ -637,7 +637,7 @@ class TestConfig:
if save_yaml:
assert yaml_value(option) is True
else:
- assert yaml_value(option) is configutils.UNSET
+ assert yaml_value(option) is usertypes.UNSET
@pytest.mark.parametrize('method', ['set_obj', 'set_str'])
def test_set_invalid(self, conf, qtbot, method):
@@ -669,7 +669,7 @@ class TestConfig:
meth(option, value, save_yaml=True)
assert not conf._values[option]
- assert yaml_value(option) is configutils.UNSET
+ assert yaml_value(option) is usertypes.UNSET
@pytest.mark.parametrize('method, value', [
('set_obj', {}),
@@ -766,57 +766,3 @@ class TestContainer:
with pytest.raises(TypeError,
match="Can't use pattern without configapi!"):
config.ConfigContainer(config_stub, pattern=pattern)
-
-
-class StyleObj(QObject):
-
- def __init__(self, stylesheet=None, parent=None):
- super().__init__(parent)
- if stylesheet is not None:
- self.STYLESHEET = stylesheet # noqa: N801,N806 pylint: disable=invalid-name
- self.rendered_stylesheet = None
-
- def setStyleSheet(self, stylesheet):
- self.rendered_stylesheet = stylesheet
-
-
-def test_get_stylesheet(config_stub):
- config_stub.val.colors.hints.fg = 'magenta'
- observer = config.StyleSheetObserver(
- StyleObj(), stylesheet="{{ conf.colors.hints.fg }}", update=False)
- assert observer._get_stylesheet() == 'magenta'
-
-
-@pytest.mark.parametrize('delete', [True, False])
-@pytest.mark.parametrize('stylesheet_param', [True, False])
-@pytest.mark.parametrize('update', [True, False])
-def test_set_register_stylesheet(delete, stylesheet_param, update, qtbot,
- config_stub, caplog):
- config_stub.val.colors.hints.fg = 'magenta'
- stylesheet = "{{ conf.colors.hints.fg }}"
-
- with caplog.at_level(9): # VDEBUG
- if stylesheet_param:
- obj = StyleObj()
- config.set_register_stylesheet(obj, stylesheet=stylesheet,
- update=update)
- else:
- obj = StyleObj(stylesheet)
- config.set_register_stylesheet(obj, update=update)
-
- assert caplog.messages[-1] == 'stylesheet for StyleObj: magenta'
-
- assert obj.rendered_stylesheet == 'magenta'
-
- if delete:
- with qtbot.waitSignal(obj.destroyed):
- obj.deleteLater()
-
- config_stub.val.colors.hints.fg = 'yellow'
-
- if delete or not update:
- expected = 'magenta'
- else:
- expected = 'yellow'
-
- assert obj.rendered_stylesheet == expected
diff --git a/tests/unit/config/test_configcommands.py b/tests/unit/config/test_configcommands.py
index 382d41ad8..97dcd7c42 100644
--- a/tests/unit/config/test_configcommands.py
+++ b/tests/unit/config/test_configcommands.py
@@ -25,7 +25,7 @@ import unittest.mock
import pytest
from PyQt5.QtCore import QUrl
-from qutebrowser.config import configcommands, configutils
+from qutebrowser.config import configcommands
from qutebrowser.api import cmdutils
from qutebrowser.utils import usertypes, urlmatch
from qutebrowser.keyinput import keyutils
@@ -92,7 +92,7 @@ class TestSet:
commands.set(0, option, inp, temp=temp)
assert config_stub.get(option) == new_value
- assert yaml_value(option) == (configutils.UNSET if temp else new_value)
+ assert yaml_value(option) == (usertypes.UNSET if temp else new_value)
def test_set_with_pattern(self, monkeypatch, commands, config_stub):
monkeypatch.setattr(objects, 'backend', usertypes.Backend.QtWebKit)
@@ -295,7 +295,7 @@ class TestAdd:
assert str(config_stub.get(name)[-1]) == value
if temp:
- assert yaml_value(name) == configutils.UNSET
+ assert yaml_value(name) == usertypes.UNSET
else:
assert yaml_value(name)[-1] == value
@@ -328,7 +328,7 @@ class TestAdd:
assert str(config_stub.get(name)[key]) == value
if temp:
- assert yaml_value(name) == configutils.UNSET
+ assert yaml_value(name) == usertypes.UNSET
else:
assert yaml_value(name)[key] == value
@@ -379,7 +379,7 @@ class TestRemove:
assert value not in config_stub.get(name)
if temp:
- assert yaml_value(name) == configutils.UNSET
+ assert yaml_value(name) == usertypes.UNSET
else:
assert value not in yaml_value(name)
@@ -410,7 +410,7 @@ class TestRemove:
assert key not in config_stub.get(name)
if temp:
- assert yaml_value(name) == configutils.UNSET
+ assert yaml_value(name) == usertypes.UNSET
else:
assert key not in yaml_value(name)
@@ -446,7 +446,7 @@ class TestUnsetAndClear:
commands.config_unset(name, temp=temp)
assert config_stub.get(name) == 'always'
- assert yaml_value(name) == ('never' if temp else configutils.UNSET)
+ assert yaml_value(name) == ('never' if temp else usertypes.UNSET)
def test_unset_unknown_option(self, commands):
with pytest.raises(cmdutils.CommandError, match="No option 'tabs'"):
@@ -460,7 +460,7 @@ class TestUnsetAndClear:
commands.config_clear(save=save)
assert config_stub.get(name) == 'always'
- assert yaml_value(name) == (configutils.UNSET if save else 'never')
+ assert yaml_value(name) == (usertypes.UNSET if save else 'never')
class TestSource:
diff --git a/tests/unit/config/test_configtypes.py b/tests/unit/config/test_configtypes.py
index 55d225a7e..e766fca8a 100644
--- a/tests/unit/config/test_configtypes.py
+++ b/tests/unit/config/test_configtypes.py
@@ -34,8 +34,8 @@ from PyQt5.QtGui import QColor, QFont
from PyQt5.QtNetwork import QNetworkProxy
from qutebrowser.misc import objects
-from qutebrowser.config import configtypes, configexc, configutils
-from qutebrowser.utils import debug, utils, qtutils, urlmatch
+from qutebrowser.config import configtypes, configexc
+from qutebrowser.utils import debug, utils, qtutils, urlmatch, usertypes
from qutebrowser.browser.network import pac
from qutebrowser.keyinput import keyutils
from helpers import utils as testutils
@@ -277,7 +277,7 @@ class TestAll:
@pytest.mark.parametrize('none_ok', [True, False])
def test_unset(self, klass, none_ok):
typ = klass(none_ok=none_ok)
- assert typ.to_py(configutils.UNSET) is configutils.UNSET
+ assert typ.to_py(usertypes.UNSET) is usertypes.UNSET
def test_to_str_none(self, klass):
assert klass().to_str(None) == ''
diff --git a/tests/unit/config/test_configutils.py b/tests/unit/config/test_configutils.py
index 8cdee2081..2c99ec34b 100644
--- a/tests/unit/config/test_configutils.py
+++ b/tests/unit/config/test_configutils.py
@@ -23,19 +23,10 @@ import pytest
from PyQt5.QtCore import QUrl
from qutebrowser.config import configutils, configdata, configtypes
-from qutebrowser.utils import urlmatch
+from qutebrowser.utils import urlmatch, usertypes
from tests.helpers import utils
-def test_unset_object_identity():
- assert configutils.Unset() is not configutils.Unset()
- assert configutils.UNSET is configutils.UNSET
-
-
-def test_unset_object_repr():
- assert repr(configutils.UNSET) == '<UNSET>'
-
-
@pytest.fixture
def opt():
return configdata.Option(name='example.option', typ=configtypes.String(),
@@ -146,7 +137,7 @@ def test_clear(values):
assert values
values.clear()
assert not values
- assert values.get_for_url(fallback=False) is configutils.UNSET
+ assert values.get_for_url(fallback=False) is usertypes.UNSET
def test_get_matching(values):
@@ -155,12 +146,12 @@ def test_get_matching(values):
def test_get_unset(empty_values):
- assert empty_values.get_for_url(fallback=False) is configutils.UNSET
+ assert empty_values.get_for_url(fallback=False) is usertypes.UNSET
def test_get_no_global(empty_values, other_pattern, pattern):
empty_values.add('example.org value', pattern)
- assert empty_values.get_for_url(fallback=False) is configutils.UNSET
+ assert empty_values.get_for_url(fallback=False) is usertypes.UNSET
def test_get_unset_fallback(empty_values):
@@ -169,7 +160,7 @@ def test_get_unset_fallback(empty_values):
def test_get_non_matching(values):
url = QUrl('https://www.example.ch/')
- assert values.get_for_url(url, fallback=False) is configutils.UNSET
+ assert values.get_for_url(url, fallback=False) is usertypes.UNSET
def test_get_non_matching_fallback(values):
@@ -205,13 +196,13 @@ def test_get_pattern_none(values, pattern):
def test_get_unset_pattern(empty_values, pattern):
value = empty_values.get_for_pattern(pattern, fallback=False)
- assert value is configutils.UNSET
+ assert value is usertypes.UNSET
def test_get_no_global_pattern(empty_values, pattern, other_pattern):
empty_values.add('example.org value', other_pattern)
value = empty_values.get_for_pattern(pattern, fallback=False)
- assert value is configutils.UNSET
+ assert value is usertypes.UNSET
def test_get_unset_fallback_pattern(empty_values, pattern):
@@ -220,7 +211,7 @@ def test_get_unset_fallback_pattern(empty_values, pattern):
def test_get_non_matching_pattern(values, other_pattern):
value = values.get_for_pattern(other_pattern, fallback=False)
- assert value is configutils.UNSET
+ assert value is usertypes.UNSET
def test_get_non_matching_fallback_pattern(values, other_pattern):
@@ -263,3 +254,26 @@ def test_domain_lookup_sparse_benchmark(url, values, benchmark):
values.add(False, urlmatch.UrlPattern(line))
benchmark(lambda: values.get_for_url(url))
+
+
+class TestWiden:
+
+ @pytest.mark.parametrize('hostname, expected', [
+ ('a.b.c', ['a.b.c', 'b.c', 'c']),
+ ('foobarbaz', ['foobarbaz']),
+ ('', []),
+ ('.c', ['.c', 'c']),
+ ('c.', ['c.']),
+ ('.c.', ['.c.', 'c.']),
+ (None, []),
+ ])
+ def test_widen_hostnames(self, hostname, expected):
+ assert list(configutils._widened_hostnames(hostname)) == expected
+
+ @pytest.mark.parametrize('hostname', [
+ 'test.qutebrowser.org',
+ 'a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.z.y.z',
+ 'qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq.c',
+ ])
+ def test_bench_widen_hostnames(self, hostname, benchmark):
+ benchmark(lambda: list(configutils._widened_hostnames(hostname)))
diff --git a/tests/unit/config/test_stylesheet.py b/tests/unit/config/test_stylesheet.py
new file mode 100644
index 000000000..67bdd04b4
--- /dev/null
+++ b/tests/unit/config/test_stylesheet.py
@@ -0,0 +1,72 @@
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+# Copyright 2019 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/>.
+
+import pytest
+
+from PyQt5.QtCore import QObject
+
+from qutebrowser.config import stylesheet
+
+
+class StyleObj(QObject):
+
+ def __init__(self, stylesheet=None, parent=None):
+ super().__init__(parent)
+ if stylesheet is not None:
+ self.STYLESHEET = stylesheet # noqa: N801,N806 pylint: disable=invalid-name
+ self.rendered_stylesheet = None
+
+ def setStyleSheet(self, stylesheet):
+ self.rendered_stylesheet = stylesheet
+
+
+def test_get_stylesheet(config_stub):
+ config_stub.val.colors.hints.fg = 'magenta'
+ observer = stylesheet._StyleSheetObserver(
+ StyleObj(), stylesheet="{{ conf.colors.hints.fg }}", update=False)
+ assert observer._get_stylesheet() == 'magenta'
+
+
+@pytest.mark.parametrize('delete', [True, False])
+@pytest.mark.parametrize('stylesheet_param', [True, False])
+@pytest.mark.parametrize('update', [True, False])
+def test_set_register_stylesheet(delete, stylesheet_param, update, qtbot,
+ config_stub, caplog):
+ config_stub.val.colors.hints.fg = 'magenta'
+ qss = "{{ conf.colors.hints.fg }}"
+
+ with caplog.at_level(9): # VDEBUG
+ if stylesheet_param:
+ obj = StyleObj()
+ stylesheet.set_register(obj, qss, update=update)
+ else:
+ obj = StyleObj(qss)
+ stylesheet.set_register(obj, update=update)
+
+ assert caplog.messages[-1] == 'stylesheet for StyleObj: magenta'
+
+ assert obj.rendered_stylesheet == 'magenta'
+
+ if delete:
+ with qtbot.waitSignal(obj.destroyed):
+ obj.deleteLater()
+
+ config_stub.val.colors.hints.fg = 'yellow'
+
+ expected = 'magenta' if delete or not update else 'yellow'
+ assert obj.rendered_stylesheet == expected
diff --git a/tests/unit/javascript/stylesheet/test_stylesheet.py b/tests/unit/javascript/stylesheet/test_stylesheet_js.py
index 768ffaeb9..768ffaeb9 100644
--- a/tests/unit/javascript/stylesheet/test_stylesheet.py
+++ b/tests/unit/javascript/stylesheet/test_stylesheet_js.py
diff --git a/tests/unit/utils/test_urlutils.py b/tests/unit/utils/test_urlutils.py
index bf1ce47c8..b6d73319d 100644
--- a/tests/unit/utils/test_urlutils.py
+++ b/tests/unit/utils/test_urlutils.py
@@ -706,26 +706,3 @@ class TestProxyFromUrl:
def test_invalid(self, url, exception):
with pytest.raises(exception):
urlutils.proxy_from_url(QUrl(url))
-
-
-class TestWiden:
-
- @pytest.mark.parametrize('hostname, expected', [
- ('a.b.c', ['a.b.c', 'b.c', 'c']),
- ('foobarbaz', ['foobarbaz']),
- ('', []),
- ('.c', ['.c', 'c']),
- ('c.', ['c.']),
- ('.c.', ['.c.', 'c.']),
- (None, []),
- ])
- def test_widen_hostnames(self, hostname, expected):
- assert list(urlutils.widened_hostnames(hostname)) == expected
-
- @pytest.mark.parametrize('hostname', [
- 'test.qutebrowser.org',
- 'a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.z.y.z',
- 'qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq.c',
- ])
- def test_bench_widen_hostnames(self, hostname, benchmark):
- benchmark(lambda: list(urlutils.widened_hostnames(hostname)))
diff --git a/tests/unit/utils/usertypes/test_misc.py b/tests/unit/utils/usertypes/test_misc.py
index 1700b7f51..68eabc213 100644
--- a/tests/unit/utils/usertypes/test_misc.py
+++ b/tests/unit/utils/usertypes/test_misc.py
@@ -25,3 +25,12 @@ def test_abstract_certificate_error_wrapper():
err = object()
wrapper = usertypes.AbstractCertificateErrorWrapper(err)
assert wrapper._error is err
+
+
+def test_unset_object_identity():
+ assert usertypes.Unset() is not usertypes.Unset()
+ assert usertypes.UNSET is usertypes.UNSET
+
+
+def test_unset_object_repr():
+ assert repr(usertypes.UNSET) == '<UNSET>'