diff --git a/qutebrowser/config/config.py b/qutebrowser/config/config.py index 98f2c67b3..facfcc553 100644 --- a/qutebrowser/config/config.py +++ b/qutebrowser/config/config.py @@ -312,10 +312,14 @@ class Config(QObject): name, deleted=deleted, renamed=renamed) raise exception from None - def get(self, name, url=None): - """Get the given setting converted for Python code.""" + def get(self, name, url=None, *, fallback=True): + """Get the given setting converted for Python code. + + Args: + fallback: Use the global value if there's no URL-specific one. + """ opt = self.get_opt(name) - obj = self.get_obj(name, url=url) + obj = self.get_obj(name, url=url, fallback=fallback) return opt.typ.to_py(obj) def _maybe_copy(self, value): @@ -329,14 +333,14 @@ class Config(QObject): assert value.__hash__ is not None, value return value - def get_obj(self, name, *, url=None): + def get_obj(self, name, *, url=None, fallback=True): """Get the given setting as object (for YAML/config.py). Note that the returned values are not watched for mutation. If a URL is given, return the value which should be used for that URL. """ self.get_opt(name) # To make sure it exists - value = self._values[name].get_for_url(url) + value = self._values[name].get_for_url(url, fallback=fallback) return self._maybe_copy(value) def get_obj_for_pattern(self, name, *, pattern): diff --git a/qutebrowser/config/configtypes.py b/qutebrowser/config/configtypes.py index 1c3bff8fb..f2bd12a74 100644 --- a/qutebrowser/config/configtypes.py +++ b/qutebrowser/config/configtypes.py @@ -60,7 +60,7 @@ from PyQt5.QtGui import QColor, QFont from PyQt5.QtWidgets import QTabWidget, QTabBar from qutebrowser.commands import cmdutils -from qutebrowser.config import configexc +from qutebrowser.config import configexc, configutils from qutebrowser.utils import standarddir, utils, qtutils, urlutils, urlmatch from qutebrowser.keyinput import keyutils @@ -149,6 +149,9 @@ class BaseType: value: The value to check. pytype: A Python type to check the value against. """ + if value is configutils.UNSET: + return + if (value is None or (pytype == list and value == []) or (pytype == dict and value == {})): if not self.none_ok: @@ -309,7 +312,9 @@ class MappingType(BaseType): def to_py(self, value): self._basic_py_validation(value, str) - if not value: + if value is configutils.UNSET: + return value + elif not value: return None self._validate_valid_values(value.lower()) return self.MAPPING[value.lower()] @@ -367,7 +372,9 @@ class String(BaseType): def to_py(self, value): self._basic_py_validation(value, str) - if not value: + if value is configutils.UNSET: + return value + elif not value: return None self._validate_encoding(value) @@ -399,7 +406,9 @@ class UniqueCharString(String): def to_py(self, value): value = super().to_py(value) - if not value: + if value is configutils.UNSET: + return value + elif not value: return None # Check for duplicate values @@ -455,7 +464,9 @@ class List(BaseType): def to_py(self, value): self._basic_py_validation(value, list) - if not value: + if value is configutils.UNSET: + return value + elif not value: return [] for val in value: @@ -534,6 +545,9 @@ class ListOrValue(BaseType): return value def to_py(self, value): + if value is configutils.UNSET: + return value + try: return [self.valtype.to_py(value)] except configexc.ValidationError: @@ -577,7 +591,8 @@ class FlagList(List): def to_py(self, value): vals = super().to_py(value) - self._check_duplicates(vals) + if vals is not configutils.UNSET: + self._check_duplicates(vals) return vals def complete(self): @@ -764,7 +779,9 @@ class Perc(_Numeric): def to_py(self, value): self._basic_py_validation(value, (float, int, str)) - if not value: + if value is configutils.UNSET: + return value + elif not value: return None if isinstance(value, str): @@ -907,7 +924,9 @@ class QtColor(BaseType): def to_py(self, value): self._basic_py_validation(value, str) - if not value: + if value is configutils.UNSET: + return value + elif not value: return None color = QColor(value) @@ -936,7 +955,9 @@ class QssColor(BaseType): def to_py(self, value): self._basic_py_validation(value, str) - if not value: + if value is configutils.UNSET: + return value + elif not value: return None functions = ['rgb', 'rgba', 'hsv', 'hsva', 'qlineargradient', @@ -981,7 +1002,9 @@ class Font(BaseType): def to_py(self, value): self._basic_py_validation(value, str) - if not value: + if value is configutils.UNSET: + return value + elif not value: return None if not self.font_regex.fullmatch(value): # pragma: no cover @@ -1000,7 +1023,9 @@ class FontFamily(Font): def to_py(self, value): self._basic_py_validation(value, str) - if not value: + if value is configutils.UNSET: + return value + elif not value: return None match = self.font_regex.fullmatch(value) @@ -1024,7 +1049,9 @@ class QtFont(Font): def to_py(self, value): self._basic_py_validation(value, str) - if not value: + if value is configutils.UNSET: + return value + elif not value: return None style_map = { @@ -1136,7 +1163,9 @@ class Regex(BaseType): def to_py(self, value): """Get a compiled regex from either a string or a regex object.""" self._basic_py_validation(value, (str, self._regex_type)) - if not value: + if value is configutils.UNSET: + return value + elif not value: return None elif isinstance(value, str): return self._compile_regex(value) @@ -1214,7 +1243,9 @@ class Dict(BaseType): def to_py(self, value): self._basic_py_validation(value, dict) - if not value: + if value is configutils.UNSET: + return value + elif not value: return self._fill_fixed_keys({}) self._validate_keys(value) @@ -1256,7 +1287,9 @@ class File(BaseType): def to_py(self, value): self._basic_py_validation(value, str) - if not value: + if value is configutils.UNSET: + return value + elif not value: return None value = os.path.expanduser(value) @@ -1282,7 +1315,9 @@ class Directory(BaseType): def to_py(self, value): self._basic_py_validation(value, str) - if not value: + if value is configutils.UNSET: + return value + elif not value: return None value = os.path.expandvars(value) value = os.path.expanduser(value) @@ -1309,7 +1344,9 @@ class FormatString(BaseType): def to_py(self, value): self._basic_py_validation(value, str) - if not value: + if value is configutils.UNSET: + return value + elif not value: return None try: @@ -1341,7 +1378,9 @@ class ShellCommand(List): def to_py(self, value): value = super().to_py(value) - if not value: + if value is configutils.UNSET: + return value + elif not value: return value if (self.placeholder and @@ -1365,7 +1404,9 @@ class Proxy(BaseType): def to_py(self, value): self._basic_py_validation(value, str) - if not value: + if value is configutils.UNSET: + return value + elif not value: return None try: @@ -1401,7 +1442,9 @@ class SearchEngineUrl(BaseType): def to_py(self, value): self._basic_py_validation(value, str) - if not value: + if value is configutils.UNSET: + return value + elif not value: return None if not ('{}' in value or '{0}' in value): @@ -1429,7 +1472,9 @@ class FuzzyUrl(BaseType): def to_py(self, value): self._basic_py_validation(value, str) - if not value: + if value is configutils.UNSET: + return value + elif not value: return None try: @@ -1463,6 +1508,9 @@ class Padding(Dict): def to_py(self, value): d = super().to_py(value) + if d is configutils.UNSET: + return d + return PaddingValues(**d) @@ -1472,7 +1520,9 @@ class Encoding(BaseType): def to_py(self, value): self._basic_py_validation(value, str) - if not value: + if value is configutils.UNSET: + return value + elif not value: return None try: codecs.lookup(value) @@ -1529,7 +1579,9 @@ class Url(BaseType): def to_py(self, value): self._basic_py_validation(value, str) - if not value: + if value is configutils.UNSET: + return value + elif not value: return None qurl = QUrl.fromUserInput(value) @@ -1545,7 +1597,9 @@ class SessionName(BaseType): def to_py(self, value): self._basic_py_validation(value, str) - if not value: + if value is configutils.UNSET: + return value + elif not value: return None if value.startswith('_'): raise configexc.ValidationError(value, "may not start with '_'!") @@ -1593,7 +1647,7 @@ class ConfirmQuit(FlagList): def to_py(self, value): values = super().to_py(value) - if not values: + if not values or values is configutils.UNSET: return values # Never can't be set with other options @@ -1630,7 +1684,9 @@ class TimestampTemplate(BaseType): def to_py(self, value): self._basic_py_validation(value, str) - if not value: + if value is configutils.UNSET: + return value + elif not value: return None try: @@ -1654,7 +1710,9 @@ class Key(BaseType): def to_py(self, value): self._basic_py_validation(value, str) - if not value: + if value is configutils.UNSET: + return value + elif not value: return None try: @@ -1673,7 +1731,9 @@ class UrlPattern(BaseType): def to_py(self, value): self._basic_py_validation(value, str) - if not value: + if value is configutils.UNSET: + return value + elif not value: return None try: diff --git a/tests/unit/config/test_config.py b/tests/unit/config/test_config.py index bf4f7c02d..6cef14130 100644 --- a/tests/unit/config/test_config.py +++ b/tests/unit/config/test_config.py @@ -480,6 +480,17 @@ class TestConfig: conf.set_obj(name, False, pattern=pattern) assert conf.get(name, url=QUrl('https://example.com/')) is False + @pytest.mark.parametrize('fallback, expected', [ + (True, True), + (False, configutils.UNSET) + ]) + def test_get_for_url_fallback(self, conf, fallback, expected): + """Test conf.get() with an URL and fallback.""" + value = conf.get('content.javascript.enabled', + url=QUrl('https://example.com/'), + fallback=fallback) + assert value is expected + @pytest.mark.parametrize('value', [{}, {'normal': {'a': 'nop'}}]) def test_get_bindings(self, config_stub, conf, value): """Test conf.get() with bindings which have missing keys.""" diff --git a/tests/unit/config/test_configtypes.py b/tests/unit/config/test_configtypes.py index 46119be7e..ee5223a38 100644 --- a/tests/unit/config/test_configtypes.py +++ b/tests/unit/config/test_configtypes.py @@ -34,7 +34,7 @@ from PyQt5.QtCore import QUrl from PyQt5.QtGui import QColor, QFont from PyQt5.QtNetwork import QNetworkProxy -from qutebrowser.config import configtypes, configexc +from qutebrowser.config import configtypes, configexc, configutils from qutebrowser.utils import debug, utils, qtutils, urlmatch from qutebrowser.browser.network import pac from qutebrowser.keyinput import keyutils @@ -274,6 +274,11 @@ class TestAll: with pytest.raises(configexc.ValidationError): meth(value) + @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 + def test_to_str_none(self, klass): assert klass().to_str(None) == ''