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:
parent
bc631d7d8b
commit
4881d81444
@ -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:
|
||||
|
@ -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")
|
||||
|
@ -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),
|
||||
|
Loading…
Reference in New Issue
Block a user