Add new configtype: FlagList

This class contains validation code shared by ConfirmQuit and
URLSegmentList, that is it checks for duplicate values and compares
each value to valid_values.
This commit is contained in:
Daniel 2015-10-01 00:40:21 +02:00
parent bc631d7d8b
commit 4881d81444
3 changed files with 90 additions and 84 deletions

View File

@ -472,6 +472,10 @@ class CommandDispatcher:
window: Open the link in a new window.
"""
segments = config.get('general', 'url-incdec-segments')
if segments is None:
segments = set()
else:
segments = set(segments)
try:
new_url = urlutils.incdec_number(url, incdec, segments=segments)
except urlutils.IncDecError as error:

View File

@ -260,6 +260,37 @@ class List(BaseType):
raise configexc.ValidationError(value, "items may not be empty!")
class FlagList(List):
"""Base class for a list setting that contains one or more flags.
Lists with duplicate flags are invalid and each item is checked against
self.valid_values (if not empty).
"""
def validate(self, value):
self._basic_validation(value)
if not value:
return
vals = self.transform(value)
if None in vals and not self.none_ok:
raise configexc.ValidationError(
value, "May not contain empty values!")
# Check for duplicate values
if len(set(vals)) != len(vals):
raise configexc.ValidationError(
value, "List contains duplicate values!")
# Check if each value is valid, ignores None values
set_vals = set(val for val in vals if val)
if (self.valid_values and self.valid_values.values and
not set_vals.issubset(set(self.valid_values))):
raise configexc.ValidationError(
value, "List contains invalid values!")
class Bool(BaseType):
"""Base class for a boolean setting."""
@ -1340,7 +1371,7 @@ class AcceptCookies(BaseType):
('never', "Don't accept cookies at all."))
class ConfirmQuit(List):
class ConfirmQuit(FlagList):
"""Whether to display a confirmation when the window is closed."""
@ -1355,18 +1386,11 @@ class ConfirmQuit(List):
combinable_values = ('multiple-tabs', 'downloads')
def validate(self, value):
self._basic_validation(value)
super().validate(value)
if not value:
return
values = []
for v in self.transform(value):
if v:
values.append(v)
elif self.none_ok:
pass
else:
raise configexc.ValidationError(value, "May not contain empty "
"values!")
values = [x for x in self.transform(value) if x]
# Never can't be set with other options
if 'never' in values and len(values) > 1:
raise configexc.ValidationError(
@ -1375,14 +1399,6 @@ class ConfirmQuit(List):
elif 'always' in values and len(values) > 1:
raise configexc.ValidationError(
value, "List cannot contain always!")
# Values have to be valid
elif not set(values).issubset(set(self.valid_values.values)):
raise configexc.ValidationError(
value, "List contains invalid values!")
# List can't have duplicates
elif len(set(values)) != len(values):
raise configexc.ValidationError(
value, "List contains duplicate values!")
def complete(self):
combinations = []
@ -1580,35 +1596,8 @@ class TabBarShow(BaseType):
"tabs."))
class URLSegmentList(List):
class URLSegmentList(FlagList):
"""A list of URL segments."""
valid_values = ValidValues('host', 'path', 'query', 'anchor')
def transform(self, value):
values = super().transform(value)
if values is None:
return set()
return set(values)
def validate(self, value):
self._basic_validation(value)
segments = super().transform(value)
if segments is None:
# Basic validation already checked if none_ok is True
return
faulty_segments = set(segments) - set(self.valid_values.values)
# faulty_segments is a set of options that are given but not valid
if None in faulty_segments:
raise configexc.ValidationError(value,
"Empty list item is not allowed")
if faulty_segments:
error_str = ("List contains invalid segments: {}"
.format(', '.join(faulty_segments)))
raise configexc.ValidationError(value, error_str)
# Make sure there are no duplicates
if len(set(segments)) != len(segments):
raise configexc.ValidationError(value,
"List contains duplicate segments")

View File

@ -363,6 +363,56 @@ class TestList:
assert klass().transform(val) == expected
class FlagListSubclass(configtypes.FlagList):
"""A subclass of FlagList which we use in tests.
Valid values are 'foo', 'bar' and 'baz'.
"""
valid_values = configtypes.ValidValues('foo', 'bar', 'baz')
class TestFlagList:
"""Test FlagList."""
@pytest.fixture
def klass(self):
return FlagListSubclass
@pytest.fixture
def klass_valid_none(self):
"""Return a FlagList with valid_values = None"""
return configtypes.FlagList
@pytest.mark.parametrize('val', ['', 'foo', 'foo,bar', 'foo,'])
def test_validate_valid(self, klass, val):
klass(none_ok=True).validate(val)
@pytest.mark.parametrize('val', ['qux', 'foo,qux', 'foo,foo'])
def test_validate_invalid(self, klass, val):
with pytest.raises(configexc.ValidationError):
klass(none_ok=True).validate(val)
@pytest.mark.parametrize('val', ['', 'foo,', 'foo,,bar'])
def test_validate_empty_value_not_okay(self, klass, val):
with pytest.raises(configexc.ValidationError):
klass(none_ok=False).validate(val)
@pytest.mark.parametrize('val, expected', [
('', None),
('foo', ['foo']),
('foo,bar', ['foo', 'bar']),
])
def test_transform(self, klass, val, expected):
assert klass().transform(val) == expected
@pytest.mark.parametrize('val', ['spam', 'spam,eggs'])
def test_validate_values_none(self, klass_valid_none, val):
klass_valid_none().validate(val)
class TestBool:
"""Test Bool."""
@ -1902,43 +1952,6 @@ class TestUserAgent:
"""Simple smoke test for completion."""
klass().complete()
class TestURLSegmentList:
"""Test URLSegmentList."""
@pytest.fixture
def klass(self):
return configtypes.URLSegmentList
@pytest.mark.parametrize('val', [
'', 'host', 'path', 'query', 'anchor', 'host,path,anchor',
])
def test_validate_valid(self, klass, val):
klass(none_ok=True).validate(val)
def test_validate_empty(self, klass):
with pytest.raises(configexc.ValidationError):
klass(none_ok=False).validate('')
@pytest.mark.parametrize('val', [
'foo', 'bar', 'foo,bar', 'host,path,foo', 'host,host', 'host,',
])
def test_validate_invalid(self, klass, val):
with pytest.raises(configexc.ValidationError):
klass(none_ok=True).validate(val)
@pytest.mark.parametrize('val, expected', [
('', set()),
('path', {'path'}),
('path,query', {'path', 'query'}),
])
def test_transform(self, klass, val, expected):
assert klass().transform(val) == expected
def test_complete(self, klass):
"""Simple smoke test for completion."""
klass().complete()
@pytest.mark.parametrize('first, second, equal', [
(re.compile('foo'), RegexEq('foo'), True),