Initial parsing

This commit is contained in:
Florian Bruhin 2017-06-09 17:21:36 +02:00
parent f965805099
commit 52f6ea2525
7 changed files with 405 additions and 1409 deletions

View File

@ -73,10 +73,11 @@ class change_filter: # pylint: disable=invalid-name
optname: The option to be filtered.
function: Whether a function rather than a method is decorated.
"""
if sectname not in configdata.DATA:
raise configexc.NoSectionError(sectname)
if optname is not None and optname not in configdata.DATA[sectname]:
raise configexc.NoOptionError(optname, sectname)
# FIXME:conf
# if sectname not in configdata.DATA:
# raise configexc.NoSectionError(sectname)
# if optname is not None and optname not in configdata.DATA[sectname]:
# raise configexc.NoOptionError(optname, sectname)
self._sectname = sectname
self._optname = optname
self._function = function
@ -256,6 +257,7 @@ def init(parent=None):
parent: The parent to pass to QObjects which get initialized.
"""
# _init_main_config(parent)
configdata.init()
_init_new_config(parent)
_init_key_config(parent)
_init_misc()

File diff suppressed because it is too large Load Diff

View File

@ -8,14 +8,14 @@ ignore_case:
start_page:
type:
name: List
elemtype: String
valtype: String
default: ["https://start.duckduckgo.com"]
desc: The default page(s) to open at the start.
yank_ignored_url_parameters:
type:
name: List
elemtype: String
valtype: String
default:
- ref
- utm_source
@ -78,7 +78,7 @@ editor.command:
editor.encoding:
type: Encoding
default: utf-8
desc: Encoding to use for the editor.
desc : Encoding to use for the editor.
content.private_browsing:
type: Bool
@ -198,7 +198,7 @@ history_session_interval:
zoom.levels:
type:
name: List
elemtype:
valtype:
name: Perc
minval: 0
default:
@ -354,7 +354,7 @@ window.hide_wayland_decoration:
keyhint.blacklist:
type:
name: List
elemtype:
valtype:
name: String
none_ok: true
default: ""
@ -417,11 +417,7 @@ content.user_agent:
content.proxy:
default: system
type:
name: Proxy
valid_values:
- system: "Use the system wide proxy."
- none: "Don't use any proxy"
type: Proxy
backend:
QtWebKit: true
QtWebEngine: Qt 5.8
@ -1015,7 +1011,7 @@ content.host_blocking.lists:
- "https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&mimetype=plaintext"
type:
name: List
elemtype: Url
valtype: Url
none_ok: true
desc: |
List of URLs of lists which contain hosts to block.
@ -1037,7 +1033,7 @@ content.host_blocking.whitelist:
- piwik.org
type:
name: List
valtype: string
valtype: String
none_ok: true
desc: >-
List of domains that should always be loaded, despite being ad-blocked.

View File

@ -35,7 +35,7 @@ from PyQt5.QtWidgets import QTabWidget, QTabBar
from qutebrowser.commands import cmdutils
from qutebrowser.config import configexc
from qutebrowser.utils import standarddir, utils
from qutebrowser.utils import standarddir, utils, qtutils
SYSTEM_PROXY = object() # Return value for Proxy type
@ -313,27 +313,27 @@ class List(BaseType):
"""Base class for a (string-)list setting."""
_show_inner_type = True
_show_valtype = True
def __init__(self, inner_type, none_ok=False, length=None):
def __init__(self, valtype, none_ok=False, length=None):
super().__init__(none_ok)
self.inner_type = inner_type
self.valtype = valtype
self.length = length
def get_name(self):
name = super().get_name()
if self._show_inner_type:
name += " of " + self.inner_type.get_name()
if self._show_valtype:
name += " of " + self.valtype.get_name()
return name
def get_valid_values(self):
return self.inner_type.get_valid_values()
return self.valtype.get_valid_values()
def transform(self, value):
if not value:
return None
else:
return [self.inner_type.transform(v.strip())
return [self.valtype.transform(v.strip())
for v in value.split(',')]
def validate(self, value):
@ -345,7 +345,7 @@ class List(BaseType):
raise configexc.ValidationError(value, "Exactly {} values need to "
"be set!".format(self.length))
for val in vals:
self.inner_type.validate(val.strip())
self.valtype.validate(val.strip())
class FlagList(List):
@ -358,14 +358,14 @@ class FlagList(List):
combinable_values = None
_show_inner_type = False
_show_valtype = False
def __init__(self, none_ok=False, valid_values=None):
super().__init__(BaseType(), none_ok)
self.inner_type.valid_values = valid_values
self.valtype.valid_values = valid_values
def validate(self, value):
if self.inner_type.valid_values is not None:
if self.valtype.valid_values is not None:
super().validate(value)
else:
self._basic_validation(value)
@ -379,7 +379,7 @@ class FlagList(List):
value, "List contains duplicate values!")
def complete(self):
valid_values = self.inner_type.valid_values
valid_values = self.valtype.valid_values
if valid_values is None:
return None
@ -453,11 +453,22 @@ class Int(BaseType):
def __init__(self, minval=None, maxval=None, none_ok=False):
super().__init__(none_ok)
if maxval is not None and minval is not None and maxval < minval:
raise ValueError("minval ({}) needs to be <= maxval ({})!".format(
minval, maxval))
self.minval = minval
self.maxval = maxval
self.minval = self._parse_limit(minval)
self.maxval = self._parse_limit(maxval)
if self.maxval is not None and self.minval is not None:
if self.maxval < self.minval:
raise ValueError("minval ({}) needs to be <= maxval ({})!"
.format(self.minval, self.maxval))
def _parse_limit(self, value):
if value == 'maxint':
return qtutils.MAXVALS['int']
elif value == 'maxint64':
return qtutils.MAXVALS['int64']
else:
if value is not None:
assert isinstance(value, int), value
return value
def transform(self, value):
if not value:
@ -1056,12 +1067,12 @@ class Padding(List):
"""Setting for paddings around elements."""
_show_inner_type = False
_show_valtype = False
def __init__(self, none_ok=False, valid_values=None):
super().__init__(Int(minval=0, none_ok=none_ok),
none_ok=none_ok, length=4)
self.inner_type.valid_values = valid_values
self.valtype.valid_values = valid_values
def transform(self, value):
elems = super().transform(value)
@ -1179,10 +1190,17 @@ class Url(BaseType):
"{}".format(val.errorString()))
class HeaderDict(BaseType):
class Dict(BaseType):
"""A JSON-like dictionary for custom HTTP headers."""
# FIXME:conf validate correctly
def __init__(self, keytype, valtype, none_ok=False):
super().__init__(none_ok)
self.keytype = keytype
self.valtype = valtype
def _validate_str(self, value, what):
"""Check if the given thing is an ascii-only string.
@ -1274,8 +1292,8 @@ class ConfirmQuit(FlagList):
def __init__(self, none_ok=False):
super().__init__(none_ok)
self.inner_type.none_ok = none_ok
self.inner_type.valid_values = ValidValues(
self.valtype.none_ok = none_ok
self.valtype.valid_values = ValidValues(
('always', "Always show a confirmation."),
('multiple-tabs', "Show a confirmation if "
"multiple tabs are opened."),

View File

@ -48,14 +48,16 @@ class NewConfigManager(QObject):
super().__init__(parent)
self._values = {}
def _key(self, sect, opt):
return sect + ' -> ' + opt
def _key(self, sect, opt=None):
if opt is None:
# New usage
return sect
return sect + '.' + opt
def read_defaults(self):
for name, section in configdata.data().items():
for key, value in section.items():
self._values[self._key(name, key)] = value
for name, option in configdata.DATA.items():
self._values[name] = option
def get(self, section, option):
val = self._values[self._key(section, option)]
return val.typ.transform(val.value())
return val.typ.transform(val.default)

View File

@ -23,16 +23,11 @@ import sys
import os
import os.path
import yaml
import astroid
from pylint import interfaces, checkers
from pylint.checkers import utils
sys.path.insert(
0, os.path.join(os.path.dirname(__file__), os.pardir, os.pardir,
os.pardir))
from qutebrowser.config import configdata
class ConfigChecker(checkers.BaseChecker):

View File

@ -18,27 +18,198 @@
"""Tests for qutebrowser.config.configdata."""
import textwrap
import yaml
import pytest
from qutebrowser.config import configdata
from qutebrowser.config import configdata, configtypes
from qutebrowser.utils import usertypes
@pytest.mark.parametrize('sect', configdata.DATA.keys())
def test_section_desc(sect):
"""Make sure every section has a description."""
desc = configdata.SECTION_DESC[sect]
assert isinstance(desc, str)
def test_init():
"""Test reading the default yaml file."""
configdata.init()
assert isinstance(configdata.DATA, dict)
assert 'ignore_case' in configdata.DATA
def test_data():
"""Some simple sanity tests on data()."""
data = configdata.data()
assert 'general' in data
assert 'ignore-case' in data['general']
class TestReadYaml:
def test_readonly_data():
"""Make sure DATA is readonly."""
with pytest.raises(ValueError, match="Trying to modify a read-only "
"config!"):
configdata.DATA['general'].setv('temp', 'ignore-case', 'true', 'true')
def test_valid(self):
data = textwrap.dedent("""
test1:
type: Bool
default: true
desc: Hello World
test2:
type: String
default: foo
backend: QtWebKit
desc: Hello World 2
""")
data = configdata._read_yaml(data)
assert data.keys() == {'test1', 'test2'}
assert data['test1'].description == "Hello World"
assert data['test2'].default == "foo"
assert data['test2'].backends == [usertypes.Backend.QtWebKit]
assert isinstance(data['test1'].typ, configtypes.Bool)
def test_invalid_keys(self):
"""Test reading with unknown keys."""
data = textwrap.dedent("""
test:
type: Bool
default: true
desc: Hello World
hello: world
""",)
with pytest.raises(ValueError, match='Invalid keys'):
configdata._read_yaml(data)
class TestParseYamlType:
def _yaml(self, s):
"""Get the type from parsed YAML data."""
return yaml.load(textwrap.dedent(s))['type']
def test_simple(self):
"""Test type which is only a name."""
data = self._yaml("type: Bool")
typ = configdata._parse_yaml_type('test', data)
assert isinstance(typ, configtypes.Bool)
assert not typ.none_ok
def test_complex(self):
"""Test type parsing with arguments."""
data = self._yaml("""
type:
name: String
minlen: 2
""")
typ = configdata._parse_yaml_type('test', data)
assert isinstance(typ, configtypes.String)
assert not typ.none_ok
assert typ.minlen == 2
def test_list(self):
"""Test type parsing with a list and subtypes."""
data = self._yaml("""
type:
name: List
valtype: String
""")
typ = configdata._parse_yaml_type('test', data)
assert isinstance(typ, configtypes.List)
assert isinstance(typ.valtype, configtypes.String)
assert not typ.none_ok
assert not typ.valtype.none_ok
def test_dict(self):
"""Test type parsing with a dict and subtypes."""
data = self._yaml("""
type:
name: Dict
keytype: String
valtype:
name: Int
minval: 10
""")
typ = configdata._parse_yaml_type('test', data)
assert isinstance(typ, configtypes.Dict)
assert isinstance(typ.keytype, configtypes.String)
assert isinstance(typ.valtype, configtypes.Int)
assert not typ.none_ok
assert typ.valtype.minval == 10
def test_invalid_node(self):
"""Test type parsing with invalid node type."""
data = self._yaml("type: 42")
with pytest.raises(ValueError, match="Invalid node for test while "
"reading type: 42"):
configdata._parse_yaml_type('test', data)
def test_unknown_type(self):
"""Test type parsing with type which doesn't exist."""
data = self._yaml("type: Foobar")
with pytest.raises(AttributeError,
match="Did not find type Foobar for test"):
configdata._parse_yaml_type('test', data)
def test_unknown_dict(self):
"""Test type parsing with a dict without keytype."""
data = self._yaml("type: Dict")
with pytest.raises(ValueError, match="Invalid node for test while "
"reading 'keytype': 'Dict'"):
configdata._parse_yaml_type('test', data)
def test_unknown_args(self):
"""Test type parsing with unknown type arguments."""
data = self._yaml("""
type:
name: Int
answer: 42
""")
with pytest.raises(TypeError, match="Error while creating Int"):
configdata._parse_yaml_type('test', data)
class TestParseYamlBackend:
def _yaml(self, s):
"""Get the type from parsed YAML data."""
return yaml.load(textwrap.dedent(s))['backend']
@pytest.mark.parametrize('backend, expected', [
('QtWebKit', [usertypes.Backend.QtWebKit]),
('QtWebEngine', [usertypes.Backend.QtWebEngine]),
# This is also what _parse_yaml_backends gets when backend: is not given
# at all
('null', [usertypes.Backend.QtWebKit, usertypes.Backend.QtWebEngine]),
])
def test_simple(self, backend, expected):
"""Check a simple "backend: QtWebKit"."""
data = self._yaml("backend: {}".format(backend))
backends = configdata._parse_yaml_backends('test', data)
assert backends == expected
@pytest.mark.parametrize('webkit, has_new_version, expected', [
(True, True, [usertypes.Backend.QtWebKit,
usertypes.Backend.QtWebEngine]),
(False, True, [usertypes.Backend.QtWebEngine]),
(True, False, [usertypes.Backend.QtWebKit]),
])
def test_dict(self, monkeypatch, webkit, has_new_version, expected):
data = self._yaml("""
backend:
QtWebKit: {}
QtWebEngine: Qt 5.8
""".format('true' if webkit else 'false'))
monkeypatch.setattr(configdata.qtutils, 'version_check',
lambda v: has_new_version)
backends = configdata._parse_yaml_backends('test', data)
assert backends == expected
@pytest.mark.parametrize('yaml_data', [
# Wrong type
"backend: 42",
# Unknown key
"""
backend:
QtWebKit: true
QtWebEngine: true
foo: bar
""",
# Missing key
"""
backend:
QtWebKit: true
""",
])
def test_invalid_backend(self, yaml_data):
with pytest.raises(ValueError, match="Invalid node for test while "
"reading backends:"):
configdata._parse_yaml_backends('test', self._yaml(yaml_data))