Merge branch 'mlochbaum-generalize-list-type'
This commit is contained in:
commit
220a387d2d
@ -151,6 +151,7 @@ Contributors, sorted by the number of commits in descending order:
|
|||||||
* Martin Tournoij
|
* Martin Tournoij
|
||||||
* Raphael Pierzina
|
* Raphael Pierzina
|
||||||
* Joel Torstensson
|
* Joel Torstensson
|
||||||
|
* Marshall Lochbaum
|
||||||
* Jan Verbeek
|
* Jan Verbeek
|
||||||
* Patric Schmitz
|
* Patric Schmitz
|
||||||
* Tarcisio Fedrizzi
|
* Tarcisio Fedrizzi
|
||||||
@ -163,7 +164,6 @@ Contributors, sorted by the number of commits in descending order:
|
|||||||
* Artur Shaik
|
* Artur Shaik
|
||||||
* Nathan Isom
|
* Nathan Isom
|
||||||
* Thorsten Wißmann
|
* Thorsten Wißmann
|
||||||
* Marshall Lochbaum
|
|
||||||
* Austin Anderson
|
* Austin Anderson
|
||||||
* Jimmy
|
* Jimmy
|
||||||
* Alexey "Averrin" Nabrodov
|
* Alexey "Averrin" Nabrodov
|
||||||
|
@ -419,7 +419,7 @@ class ConfigManager(QObject):
|
|||||||
for optname, option in sect.items():
|
for optname, option in sect.items():
|
||||||
|
|
||||||
lines.append('#')
|
lines.append('#')
|
||||||
typestr = ' ({})'.format(option.typ.__class__.__name__)
|
typestr = ' ({})'.format(option.typ.get_name())
|
||||||
lines.append("# {}{}:".format(optname, typestr))
|
lines.append("# {}{}:".format(optname, typestr))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -430,7 +430,7 @@ class ConfigManager(QObject):
|
|||||||
continue
|
continue
|
||||||
for descline in desc.splitlines():
|
for descline in desc.splitlines():
|
||||||
lines += wrapper.wrap(descline)
|
lines += wrapper.wrap(descline)
|
||||||
valid_values = option.typ.valid_values
|
valid_values = option.typ.get_valid_values()
|
||||||
if valid_values is not None:
|
if valid_values is not None:
|
||||||
if valid_values.descriptions:
|
if valid_values.descriptions:
|
||||||
for val in valid_values:
|
for val in valid_values:
|
||||||
|
@ -135,7 +135,7 @@ def data(readonly=False):
|
|||||||
"Whether to find text on a page case-insensitively."),
|
"Whether to find text on a page case-insensitively."),
|
||||||
|
|
||||||
('startpage',
|
('startpage',
|
||||||
SettingValue(typ.List(), 'https://duckduckgo.com'),
|
SettingValue(typ.List(typ.String()), 'https://duckduckgo.com'),
|
||||||
"The default page(s) to open at the start, separated by commas."),
|
"The default page(s) to open at the start, separated by commas."),
|
||||||
|
|
||||||
('default-page',
|
('default-page',
|
||||||
@ -254,7 +254,7 @@ def data(readonly=False):
|
|||||||
|
|
||||||
('ui', sect.KeyValue(
|
('ui', sect.KeyValue(
|
||||||
('zoom-levels',
|
('zoom-levels',
|
||||||
SettingValue(typ.PercList(minval=0),
|
SettingValue(typ.List(typ.Perc(minval=0)),
|
||||||
'25%,33%,50%,67%,75%,90%,100%,110%,125%,150%,175%,'
|
'25%,33%,50%,67%,75%,90%,100%,110%,125%,150%,175%,'
|
||||||
'200%,250%,300%,400%,500%'),
|
'200%,250%,300%,400%,500%'),
|
||||||
"The available zoom levels, separated by commas."),
|
"The available zoom levels, separated by commas."),
|
||||||
@ -352,7 +352,7 @@ def data(readonly=False):
|
|||||||
"(requires restart)"),
|
"(requires restart)"),
|
||||||
|
|
||||||
('keyhint-blacklist',
|
('keyhint-blacklist',
|
||||||
SettingValue(typ.List(none_ok=True), ''),
|
SettingValue(typ.List(typ.String(), none_ok=True), ''),
|
||||||
"Keychains that shouldn't be shown in the keyhint dialog\n\n"
|
"Keychains that shouldn't be shown in the keyhint dialog\n\n"
|
||||||
"Globs are supported, so ';*' will blacklist all keychains"
|
"Globs are supported, so ';*' will blacklist all keychains"
|
||||||
"starting with ';'. Use '*' to disable keyhints"),
|
"starting with ';'. Use '*' to disable keyhints"),
|
||||||
@ -684,8 +684,8 @@ def data(readonly=False):
|
|||||||
|
|
||||||
('object-cache-capacities',
|
('object-cache-capacities',
|
||||||
SettingValue(
|
SettingValue(
|
||||||
typ.WebKitBytesList(length=3, maxsize=MAXVALS['int'],
|
typ.List(typ.WebKitBytes(maxsize=MAXVALS['int'],
|
||||||
none_ok=True), ''),
|
none_ok=True), none_ok=True, length=3), ''),
|
||||||
"The capacities for the global memory cache for dead objects "
|
"The capacities for the global memory cache for dead objects "
|
||||||
"such as stylesheets or scripts. Syntax: cacheMinDeadCapacity, "
|
"such as stylesheets or scripts. Syntax: cacheMinDeadCapacity, "
|
||||||
"cacheMaxDead, totalCapacity.\n\n"
|
"cacheMaxDead, totalCapacity.\n\n"
|
||||||
@ -826,7 +826,7 @@ def data(readonly=False):
|
|||||||
|
|
||||||
('host-block-lists',
|
('host-block-lists',
|
||||||
SettingValue(
|
SettingValue(
|
||||||
typ.UrlList(none_ok=True),
|
typ.List(typ.Url(), none_ok=True),
|
||||||
'http://www.malwaredomainlist.com/hostslist/hosts.txt,'
|
'http://www.malwaredomainlist.com/hostslist/hosts.txt,'
|
||||||
'http://someonewhocares.org/hosts/hosts,'
|
'http://someonewhocares.org/hosts/hosts,'
|
||||||
'http://winhelp2002.mvps.org/hosts.zip,'
|
'http://winhelp2002.mvps.org/hosts.zip,'
|
||||||
@ -845,7 +845,7 @@ def data(readonly=False):
|
|||||||
"Whether host blocking is enabled."),
|
"Whether host blocking is enabled."),
|
||||||
|
|
||||||
('host-blocking-whitelist',
|
('host-blocking-whitelist',
|
||||||
SettingValue(typ.List(none_ok=True), 'piwik.org'),
|
SettingValue(typ.List(typ.String(), none_ok=True), 'piwik.org'),
|
||||||
"List of domains that should always be loaded, despite being "
|
"List of domains that should always be loaded, despite being "
|
||||||
"ad-blocked.\n\n"
|
"ad-blocked.\n\n"
|
||||||
"Domains may contain * and ? wildcards and are otherwise "
|
"Domains may contain * and ? wildcards and are otherwise "
|
||||||
@ -916,13 +916,13 @@ def data(readonly=False):
|
|||||||
"auto-follow."),
|
"auto-follow."),
|
||||||
|
|
||||||
('next-regexes',
|
('next-regexes',
|
||||||
SettingValue(typ.RegexList(flags=re.IGNORECASE),
|
SettingValue(typ.List(typ.Regex(flags=re.IGNORECASE)),
|
||||||
r'\bnext\b,\bmore\b,\bnewer\b,\b[>→≫]\b,\b(>>|»)\b,'
|
r'\bnext\b,\bmore\b,\bnewer\b,\b[>→≫]\b,\b(>>|»)\b,'
|
||||||
r'\bcontinue\b'),
|
r'\bcontinue\b'),
|
||||||
"A comma-separated list of regexes to use for 'next' links."),
|
"A comma-separated list of regexes to use for 'next' links."),
|
||||||
|
|
||||||
('prev-regexes',
|
('prev-regexes',
|
||||||
SettingValue(typ.RegexList(flags=re.IGNORECASE),
|
SettingValue(typ.List(typ.Regex(flags=re.IGNORECASE)),
|
||||||
r'\bprev(ious)?\b,\bback\b,\bolder\b,\b[<←≪]\b,'
|
r'\bprev(ious)?\b,\bback\b,\bolder\b,\b[<←≪]\b,'
|
||||||
r'\b(<<|«)\b'),
|
r'\b(<<|«)\b'),
|
||||||
"A comma-separated list of regexes to use for 'prev' links."),
|
"A comma-separated list of regexes to use for 'prev' links."),
|
||||||
|
@ -124,6 +124,14 @@ class BaseType:
|
|||||||
self.none_ok = none_ok
|
self.none_ok = none_ok
|
||||||
self.valid_values = None
|
self.valid_values = None
|
||||||
|
|
||||||
|
def get_name(self):
|
||||||
|
"""Get a name for the type for documentation"""
|
||||||
|
return self.__class__.__name__
|
||||||
|
|
||||||
|
def get_valid_values(self):
|
||||||
|
"""Get the type's valid values for documentation"""
|
||||||
|
return self.valid_values
|
||||||
|
|
||||||
def _basic_validation(self, value):
|
def _basic_validation(self, value):
|
||||||
"""Do some basic validation for the value (empty, non-printable chars).
|
"""Do some basic validation for the value (empty, non-printable chars).
|
||||||
|
|
||||||
@ -303,23 +311,39 @@ class List(BaseType):
|
|||||||
|
|
||||||
"""Base class for a (string-)list setting."""
|
"""Base class for a (string-)list setting."""
|
||||||
|
|
||||||
def __init__(self, none_ok=False, valid_values=None):
|
_show_inner_type = True
|
||||||
|
|
||||||
|
def __init__(self, inner_type, none_ok=False, length=None):
|
||||||
super().__init__(none_ok)
|
super().__init__(none_ok)
|
||||||
self.valid_values = valid_values
|
self.inner_type = inner_type
|
||||||
|
self.length = length
|
||||||
|
|
||||||
|
def get_name(self):
|
||||||
|
name = super().get_name()
|
||||||
|
if self._show_inner_type:
|
||||||
|
name += " of " + self.inner_type.get_name()
|
||||||
|
return name
|
||||||
|
|
||||||
|
def get_valid_values(self):
|
||||||
|
return self.inner_type.get_valid_values()
|
||||||
|
|
||||||
def transform(self, value):
|
def transform(self, value):
|
||||||
if not value:
|
if not value:
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
return [v if v else None for v in value.split(',')]
|
return [self.inner_type.transform(v.strip())
|
||||||
|
for v in value.split(',')]
|
||||||
|
|
||||||
def validate(self, value):
|
def validate(self, value):
|
||||||
self._basic_validation(value)
|
self._basic_validation(value)
|
||||||
if not value:
|
if not value:
|
||||||
return
|
return
|
||||||
vals = self.transform(value)
|
vals = value.split(',')
|
||||||
if None in vals:
|
if self.length is not None and len(vals) != self.length:
|
||||||
raise configexc.ValidationError(value, "items may not be empty!")
|
raise configexc.ValidationError(value, "Exactly {} values need to "
|
||||||
|
"be set!".format(self.length))
|
||||||
|
for val in vals:
|
||||||
|
self.inner_type.validate(val.strip())
|
||||||
|
|
||||||
|
|
||||||
class FlagList(List):
|
class FlagList(List):
|
||||||
@ -332,41 +356,40 @@ class FlagList(List):
|
|||||||
|
|
||||||
combinable_values = None
|
combinable_values = None
|
||||||
|
|
||||||
|
_show_inner_type = False
|
||||||
|
|
||||||
|
def __init__(self, none_ok=False, valid_values=None):
|
||||||
|
super().__init__(BaseType(), none_ok)
|
||||||
|
self.inner_type.valid_values = valid_values
|
||||||
|
|
||||||
def validate(self, value):
|
def validate(self, value):
|
||||||
self._basic_validation(value)
|
if self.inner_type.valid_values is not None:
|
||||||
|
super().validate(value)
|
||||||
|
else:
|
||||||
|
self._basic_validation(value)
|
||||||
if not value:
|
if not value:
|
||||||
return
|
return
|
||||||
|
vals = super().transform(value)
|
||||||
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
|
# Check for duplicate values
|
||||||
if len(set(vals)) != len(vals):
|
if len(set(vals)) != len(vals):
|
||||||
raise configexc.ValidationError(
|
raise configexc.ValidationError(
|
||||||
value, "List contains duplicate values!")
|
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 is not None and
|
|
||||||
not set_vals.issubset(set(self.valid_values))):
|
|
||||||
raise configexc.ValidationError(
|
|
||||||
value, "List contains invalid values!")
|
|
||||||
|
|
||||||
def complete(self):
|
def complete(self):
|
||||||
if self.valid_values is None:
|
valid_values = self.inner_type.valid_values
|
||||||
|
if valid_values is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
out = []
|
out = []
|
||||||
# Single value completions
|
# Single value completions
|
||||||
for value in self.valid_values:
|
for value in valid_values:
|
||||||
desc = self.valid_values.descriptions.get(value, "")
|
desc = valid_values.descriptions.get(value, "")
|
||||||
out.append((value, desc))
|
out.append((value, desc))
|
||||||
|
|
||||||
combinables = self.combinable_values
|
combinables = self.combinable_values
|
||||||
if combinables is None:
|
if combinables is None:
|
||||||
combinables = list(self.valid_values)
|
combinables = list(valid_values)
|
||||||
# Generate combinations of each possible value combination
|
# Generate combinations of each possible value combination
|
||||||
for size in range(2, len(combinables) + 1):
|
for size in range(2, len(combinables) + 1):
|
||||||
for combination in itertools.combinations(combinables, size):
|
for combination in itertools.combinations(combinables, size):
|
||||||
@ -456,29 +479,6 @@ class Int(BaseType):
|
|||||||
"smaller!".format(self.maxval))
|
"smaller!".format(self.maxval))
|
||||||
|
|
||||||
|
|
||||||
class IntList(List):
|
|
||||||
|
|
||||||
"""Base class for an int-list setting."""
|
|
||||||
|
|
||||||
def transform(self, value):
|
|
||||||
if not value:
|
|
||||||
return None
|
|
||||||
vals = super().transform(value)
|
|
||||||
return [int(v) if v is not None else None for v in vals]
|
|
||||||
|
|
||||||
def validate(self, value):
|
|
||||||
self._basic_validation(value)
|
|
||||||
if not value:
|
|
||||||
return
|
|
||||||
try:
|
|
||||||
vals = self.transform(value)
|
|
||||||
except ValueError:
|
|
||||||
raise configexc.ValidationError(value, "must be a list of "
|
|
||||||
"integers!")
|
|
||||||
if None in vals and not self.none_ok:
|
|
||||||
raise configexc.ValidationError(value, "items may not be empty!")
|
|
||||||
|
|
||||||
|
|
||||||
class Float(BaseType):
|
class Float(BaseType):
|
||||||
|
|
||||||
"""Base class for a float setting.
|
"""Base class for a float setting.
|
||||||
@ -559,50 +559,6 @@ class Perc(BaseType):
|
|||||||
"less!".format(self.maxval))
|
"less!".format(self.maxval))
|
||||||
|
|
||||||
|
|
||||||
class PercList(List):
|
|
||||||
|
|
||||||
"""Base class for a list of percentages.
|
|
||||||
|
|
||||||
Attributes:
|
|
||||||
minval: Minimum value (inclusive).
|
|
||||||
maxval: Maximum value (inclusive).
|
|
||||||
"""
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
def transform(self, value):
|
|
||||||
if not value:
|
|
||||||
return None
|
|
||||||
vals = super().transform(value)
|
|
||||||
return [int(v[:-1]) if v is not None else None for v in vals]
|
|
||||||
|
|
||||||
def validate(self, value):
|
|
||||||
self._basic_validation(value)
|
|
||||||
if not value:
|
|
||||||
return
|
|
||||||
vals = super().transform(value)
|
|
||||||
perctype = Perc(minval=self.minval, maxval=self.maxval)
|
|
||||||
try:
|
|
||||||
for val in vals:
|
|
||||||
if val is None:
|
|
||||||
if self.none_ok:
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
raise configexc.ValidationError(value, "items may not "
|
|
||||||
"be empty!")
|
|
||||||
else:
|
|
||||||
perctype.validate(val)
|
|
||||||
except configexc.ValidationError:
|
|
||||||
raise configexc.ValidationError(value, "must be a list of "
|
|
||||||
"percentages!")
|
|
||||||
|
|
||||||
|
|
||||||
class PercOrInt(BaseType):
|
class PercOrInt(BaseType):
|
||||||
|
|
||||||
"""Percentage or integer.
|
"""Percentage or integer.
|
||||||
@ -885,36 +841,6 @@ class Regex(BaseType):
|
|||||||
return re.compile(value, self.flags)
|
return re.compile(value, self.flags)
|
||||||
|
|
||||||
|
|
||||||
class RegexList(List):
|
|
||||||
|
|
||||||
"""A list of regexes."""
|
|
||||||
|
|
||||||
def __init__(self, flags=0, none_ok=False):
|
|
||||||
super().__init__(none_ok)
|
|
||||||
self.flags = flags
|
|
||||||
|
|
||||||
def transform(self, value):
|
|
||||||
if not value:
|
|
||||||
return None
|
|
||||||
vals = super().transform(value)
|
|
||||||
return [re.compile(v, self.flags) if v is not None else None
|
|
||||||
for v in vals]
|
|
||||||
|
|
||||||
def validate(self, value):
|
|
||||||
self._basic_validation(value)
|
|
||||||
if not value:
|
|
||||||
return
|
|
||||||
vals = super().transform(value)
|
|
||||||
|
|
||||||
for val in vals:
|
|
||||||
if val is None:
|
|
||||||
if not self.none_ok:
|
|
||||||
raise configexc.ValidationError(
|
|
||||||
value, "items may not be empty!")
|
|
||||||
else:
|
|
||||||
_validate_regex(val, self.flags)
|
|
||||||
|
|
||||||
|
|
||||||
class File(BaseType):
|
class File(BaseType):
|
||||||
|
|
||||||
"""A file on the local filesystem."""
|
"""A file on the local filesystem."""
|
||||||
@ -1061,39 +987,6 @@ class WebKitBytes(BaseType):
|
|||||||
return int(val) * multiplicator
|
return int(val) * multiplicator
|
||||||
|
|
||||||
|
|
||||||
class WebKitBytesList(List):
|
|
||||||
|
|
||||||
"""A size with an optional suffix.
|
|
||||||
|
|
||||||
Attributes:
|
|
||||||
length: The length of the list.
|
|
||||||
bytestype: The webkit bytes type.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, maxsize=None, length=None, none_ok=False):
|
|
||||||
super().__init__(none_ok)
|
|
||||||
self.length = length
|
|
||||||
self.bytestype = WebKitBytes(maxsize, none_ok=none_ok)
|
|
||||||
|
|
||||||
def transform(self, value):
|
|
||||||
if value == '':
|
|
||||||
return None
|
|
||||||
else:
|
|
||||||
vals = super().transform(value)
|
|
||||||
return [self.bytestype.transform(val) for val in vals]
|
|
||||||
|
|
||||||
def validate(self, value):
|
|
||||||
self._basic_validation(value)
|
|
||||||
if not value:
|
|
||||||
return
|
|
||||||
vals = super().transform(value)
|
|
||||||
for val in vals:
|
|
||||||
self.bytestype.validate(val)
|
|
||||||
if self.length is not None and len(vals) != self.length:
|
|
||||||
raise configexc.ValidationError(value, "exactly {} values need to "
|
|
||||||
"be set!".format(self.length))
|
|
||||||
|
|
||||||
|
|
||||||
class ShellCommand(BaseType):
|
class ShellCommand(BaseType):
|
||||||
|
|
||||||
"""A shellcommand which is split via shlex.
|
"""A shellcommand which is split via shlex.
|
||||||
@ -1243,25 +1136,16 @@ PaddingValues = collections.namedtuple('PaddingValues', ['top', 'bottom',
|
|||||||
'left', 'right'])
|
'left', 'right'])
|
||||||
|
|
||||||
|
|
||||||
class Padding(IntList):
|
class Padding(List):
|
||||||
|
|
||||||
"""Setting for paddings around elements."""
|
"""Setting for paddings around elements."""
|
||||||
|
|
||||||
def validate(self, value):
|
_show_inner_type = False
|
||||||
self._basic_validation(value)
|
|
||||||
if not value:
|
def __init__(self, none_ok=False, valid_values=None):
|
||||||
return
|
super().__init__(Int(minval=0, none_ok=none_ok),
|
||||||
try:
|
none_ok=none_ok, length=4)
|
||||||
vals = self.transform(value)
|
self.inner_type.valid_values = valid_values
|
||||||
except (ValueError, TypeError):
|
|
||||||
raise configexc.ValidationError(value, "must be a list of 4 "
|
|
||||||
"integers!")
|
|
||||||
if None in vals and not self.none_ok:
|
|
||||||
raise configexc.ValidationError(value, "items may not be empty!")
|
|
||||||
elems = self.transform(value)
|
|
||||||
if any(e is not None and e < 0 for e in elems):
|
|
||||||
raise configexc.ValidationError(value, "Values need to be "
|
|
||||||
"positive!")
|
|
||||||
|
|
||||||
def transform(self, value):
|
def transform(self, value):
|
||||||
elems = super().transform(value)
|
elems = super().transform(value)
|
||||||
@ -1401,29 +1285,24 @@ class VerticalPosition(BaseType):
|
|||||||
self.valid_values = ValidValues('top', 'bottom')
|
self.valid_values = ValidValues('top', 'bottom')
|
||||||
|
|
||||||
|
|
||||||
class UrlList(List):
|
class Url(BaseType):
|
||||||
|
|
||||||
"""A list of URLs."""
|
"""A URL."""
|
||||||
|
|
||||||
def transform(self, value):
|
def transform(self, value):
|
||||||
if not value:
|
if not value:
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
return [QUrl.fromUserInput(v) if v else None
|
return QUrl.fromUserInput(value)
|
||||||
for v in value.split(',')]
|
|
||||||
|
|
||||||
def validate(self, value):
|
def validate(self, value):
|
||||||
self._basic_validation(value)
|
self._basic_validation(value)
|
||||||
if not value:
|
if not value:
|
||||||
return
|
return
|
||||||
vals = self.transform(value)
|
val = self.transform(value)
|
||||||
for val in vals:
|
if not val.isValid():
|
||||||
if val is None:
|
raise configexc.ValidationError(value, "invalid URL - "
|
||||||
raise configexc.ValidationError(value, "values may not be "
|
"{}".format(val.errorString()))
|
||||||
"empty!")
|
|
||||||
elif not val.isValid():
|
|
||||||
raise configexc.ValidationError(value, "invalid URL - "
|
|
||||||
"{}".format(val.errorString()))
|
|
||||||
|
|
||||||
|
|
||||||
class HeaderDict(BaseType):
|
class HeaderDict(BaseType):
|
||||||
@ -1519,7 +1398,8 @@ class ConfirmQuit(FlagList):
|
|||||||
|
|
||||||
def __init__(self, none_ok=False):
|
def __init__(self, none_ok=False):
|
||||||
super().__init__(none_ok)
|
super().__init__(none_ok)
|
||||||
self.valid_values = ValidValues(
|
self.inner_type.none_ok = none_ok
|
||||||
|
self.inner_type.valid_values = ValidValues(
|
||||||
('always', "Always show a confirmation."),
|
('always', "Always show a confirmation."),
|
||||||
('multiple-tabs', "Show a confirmation if "
|
('multiple-tabs', "Show a confirmation if "
|
||||||
"multiple tabs are opened."),
|
"multiple tabs are opened."),
|
||||||
|
@ -357,7 +357,7 @@ def _generate_setting_section(f, sectname, sect):
|
|||||||
f.write("=== {}".format(optname) + "\n")
|
f.write("=== {}".format(optname) + "\n")
|
||||||
f.write(sect.descriptions[optname] + "\n")
|
f.write(sect.descriptions[optname] + "\n")
|
||||||
f.write("\n")
|
f.write("\n")
|
||||||
valid_values = option.typ.valid_values
|
valid_values = option.typ.get_valid_values()
|
||||||
if valid_values is not None:
|
if valid_values is not None:
|
||||||
f.write("Valid values:\n")
|
f.write("Valid values:\n")
|
||||||
f.write("\n")
|
f.write("\n")
|
||||||
|
@ -354,18 +354,31 @@ class TestString:
|
|||||||
assert klass(valid_values=valid_values).complete() == expected
|
assert klass(valid_values=valid_values).complete() == expected
|
||||||
|
|
||||||
|
|
||||||
|
class ListSubclass(configtypes.List):
|
||||||
|
|
||||||
|
"""A subclass of List which we use in tests. Similar to FlagList.
|
||||||
|
|
||||||
|
Valid values are 'foo', 'bar' and 'baz'.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, none_ok_inner=False, none_ok_outer=False, length=None):
|
||||||
|
super().__init__(configtypes.BaseType(none_ok_inner),
|
||||||
|
none_ok=none_ok_outer, length=length)
|
||||||
|
self.inner_type.valid_values = configtypes.ValidValues('foo',
|
||||||
|
'bar', 'baz')
|
||||||
|
|
||||||
|
|
||||||
class TestList:
|
class TestList:
|
||||||
|
|
||||||
"""Test List."""
|
"""Test List."""
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def klass(self):
|
def klass(self):
|
||||||
return configtypes.List
|
return ListSubclass
|
||||||
|
|
||||||
@pytest.mark.parametrize('val',
|
@pytest.mark.parametrize('val', ['', 'foo', 'foo,bar', 'foo, bar'])
|
||||||
['', 'foo', 'foo,bar', 'foo, bar'])
|
|
||||||
def test_validate_valid(self, klass, val):
|
def test_validate_valid(self, klass, val):
|
||||||
klass(none_ok=True).validate(val)
|
klass(none_ok_outer=True).validate(val)
|
||||||
|
|
||||||
@pytest.mark.parametrize('val', ['', 'foo,,bar'])
|
@pytest.mark.parametrize('val', ['', 'foo,,bar'])
|
||||||
def test_validate_invalid(self, klass, val):
|
def test_validate_invalid(self, klass, val):
|
||||||
@ -374,14 +387,24 @@ class TestList:
|
|||||||
|
|
||||||
def test_invalid_empty_value_none_ok(self, klass):
|
def test_invalid_empty_value_none_ok(self, klass):
|
||||||
with pytest.raises(configexc.ValidationError):
|
with pytest.raises(configexc.ValidationError):
|
||||||
klass(none_ok=True).validate('foo,,bar')
|
klass(none_ok_outer=True).validate('foo,,bar')
|
||||||
|
with pytest.raises(configexc.ValidationError):
|
||||||
|
klass(none_ok_inner=True).validate('')
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('val', ['', 'foo,bar', 'foo, bar'])
|
||||||
|
def test_validate_length(self, klass, val):
|
||||||
|
klass(none_ok_outer=True, length=2).validate(val)
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('val', ['bar', 'foo,bar', 'foo,bar,foo,bar'])
|
||||||
|
def test_wrong_length(self, klass, val):
|
||||||
|
with pytest.raises(configexc.ValidationError):
|
||||||
|
klass(length=3).validate(val)
|
||||||
|
|
||||||
@pytest.mark.parametrize('val, expected', [
|
@pytest.mark.parametrize('val, expected', [
|
||||||
('foo', ['foo']),
|
('foo', ['foo']),
|
||||||
('foo,bar,baz', ['foo', 'bar', 'baz']),
|
('foo,bar,baz', ['foo', 'bar', 'baz']),
|
||||||
('', None),
|
('', None),
|
||||||
# Not implemented yet
|
('foo, bar', ['foo', 'bar'])
|
||||||
pytest.mark.xfail(('foo, bar', ['foo', 'bar'])),
|
|
||||||
])
|
])
|
||||||
def test_transform(self, klass, val, expected):
|
def test_transform(self, klass, val, expected):
|
||||||
assert klass().transform(val) == expected
|
assert klass().transform(val) == expected
|
||||||
@ -398,7 +421,9 @@ class FlagListSubclass(configtypes.FlagList):
|
|||||||
|
|
||||||
def __init__(self, none_ok=False):
|
def __init__(self, none_ok=False):
|
||||||
super().__init__(none_ok)
|
super().__init__(none_ok)
|
||||||
self.valid_values = configtypes.ValidValues('foo', 'bar', 'baz')
|
self.inner_type.valid_values = configtypes.ValidValues('foo',
|
||||||
|
'bar', 'baz')
|
||||||
|
self.inner_type.none_ok = none_ok
|
||||||
|
|
||||||
|
|
||||||
class TestFlagList:
|
class TestFlagList:
|
||||||
@ -580,37 +605,6 @@ class TestInt:
|
|||||||
assert klass(none_ok=True).transform(val) == expected
|
assert klass(none_ok=True).transform(val) == expected
|
||||||
|
|
||||||
|
|
||||||
class TestIntList:
|
|
||||||
|
|
||||||
"""Test IntList."""
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def klass(self):
|
|
||||||
return configtypes.IntList
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('val', ['', '1,2', '1', '23,1337'])
|
|
||||||
def test_validate_valid(self, klass, val):
|
|
||||||
klass(none_ok=True).validate(val)
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('val', ['', '1,,2', '23,foo,1337'])
|
|
||||||
def test_validate_invalid(self, klass, val):
|
|
||||||
with pytest.raises(configexc.ValidationError):
|
|
||||||
klass().validate(val)
|
|
||||||
|
|
||||||
def test_invalid_empty_value_none_ok(self, klass):
|
|
||||||
klass(none_ok=True).validate('1,,2')
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('val, expected', [
|
|
||||||
('1', [1]),
|
|
||||||
('23,42', [23, 42]),
|
|
||||||
('', None),
|
|
||||||
('1,,2', [1, None, 2]),
|
|
||||||
('23, 42', [23, 42]),
|
|
||||||
])
|
|
||||||
def test_transform(self, klass, val, expected):
|
|
||||||
assert klass().transform(val) == expected
|
|
||||||
|
|
||||||
|
|
||||||
class TestFloat:
|
class TestFloat:
|
||||||
|
|
||||||
"""Test Float."""
|
"""Test Float."""
|
||||||
@ -703,52 +697,6 @@ class TestPerc:
|
|||||||
assert klass().transform(val) == expected
|
assert klass().transform(val) == expected
|
||||||
|
|
||||||
|
|
||||||
class TestPercList:
|
|
||||||
|
|
||||||
"""Test PercList."""
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def klass(self):
|
|
||||||
return configtypes.PercList
|
|
||||||
|
|
||||||
def test_minval_gt_maxval(self, klass):
|
|
||||||
with pytest.raises(ValueError):
|
|
||||||
klass(minval=2, maxval=1)
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('kwargs, val', [
|
|
||||||
({}, '23%,42%,1337%'),
|
|
||||||
({'minval': 2}, '2%,3%'),
|
|
||||||
({'maxval': 2}, '1%,2%'),
|
|
||||||
({'minval': 2, 'maxval': 3}, '2%,3%'),
|
|
||||||
({'none_ok': True}, '42%,,23%'),
|
|
||||||
({'none_ok': True}, ''),
|
|
||||||
])
|
|
||||||
def test_validate_valid(self, klass, kwargs, val):
|
|
||||||
klass(**kwargs).validate(val)
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('kwargs, val', [
|
|
||||||
({}, '23%,42,1337%'),
|
|
||||||
({'minval': 2}, '1%,2%'),
|
|
||||||
({'maxval': 2}, '2%,3%'),
|
|
||||||
({'minval': 2, 'maxval': 3}, '1%,2%'),
|
|
||||||
({'minval': 2, 'maxval': 3}, '3%,4%'),
|
|
||||||
({}, '42%,,23%'),
|
|
||||||
({}, ''),
|
|
||||||
])
|
|
||||||
def test_validate_invalid(self, klass, kwargs, val):
|
|
||||||
with pytest.raises(configexc.ValidationError):
|
|
||||||
klass(**kwargs).validate(val)
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('val, expected', [
|
|
||||||
('', None),
|
|
||||||
('1337%', [1337]),
|
|
||||||
('23%,42%,1337%', [23, 42, 1337]),
|
|
||||||
('23%,,42%', [23, None, 42]),
|
|
||||||
])
|
|
||||||
def test_transform(self, klass, val, expected):
|
|
||||||
assert klass().transform(val) == expected
|
|
||||||
|
|
||||||
|
|
||||||
class TestPercOrInt:
|
class TestPercOrInt:
|
||||||
|
|
||||||
"""Test PercOrInt."""
|
"""Test PercOrInt."""
|
||||||
@ -1224,59 +1172,6 @@ class TestRegex:
|
|||||||
klass().validate('foo')
|
klass().validate('foo')
|
||||||
|
|
||||||
|
|
||||||
class TestRegexList:
|
|
||||||
|
|
||||||
"""Test RegexList."""
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def klass(self):
|
|
||||||
return configtypes.RegexList
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('val', [
|
|
||||||
r'(foo|bar),[abcd]?,1337{42}',
|
|
||||||
r'(foo|bar),,1337{42}',
|
|
||||||
r'',
|
|
||||||
])
|
|
||||||
def test_validate_valid(self, klass, val):
|
|
||||||
klass(none_ok=True).validate(val)
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('val', [
|
|
||||||
r'(foo|bar),,1337{42}',
|
|
||||||
r'',
|
|
||||||
r'(foo|bar),((),1337{42}',
|
|
||||||
r'(' * 500,
|
|
||||||
], ids=['empty value', 'empty', 'unmatched parens', 'too many parens'])
|
|
||||||
def test_validate_invalid(self, klass, val):
|
|
||||||
with pytest.raises(configexc.ValidationError):
|
|
||||||
klass().validate(val)
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('val', [
|
|
||||||
r'foo\Xbar',
|
|
||||||
r'foo\Cbar',
|
|
||||||
])
|
|
||||||
def test_validate_maybe_valid(self, klass, val):
|
|
||||||
"""Those values are valid on some Python versions (and systems?).
|
|
||||||
|
|
||||||
On others, they raise a DeprecationWarning because of an invalid
|
|
||||||
escape. This tests makes sure this gets translated to a
|
|
||||||
ValidationError.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
klass().validate(val)
|
|
||||||
except configexc.ValidationError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('val, expected', [
|
|
||||||
('foo', [RegexEq('foo')]),
|
|
||||||
('foo,bar,baz', [RegexEq('foo'), RegexEq('bar'),
|
|
||||||
RegexEq('baz')]),
|
|
||||||
('foo,,bar', [RegexEq('foo'), None, RegexEq('bar')]),
|
|
||||||
('', None),
|
|
||||||
])
|
|
||||||
def test_transform(self, klass, val, expected):
|
|
||||||
assert klass().transform(val) == expected
|
|
||||||
|
|
||||||
|
|
||||||
def unrequired_class(**kwargs):
|
def unrequired_class(**kwargs):
|
||||||
return configtypes.File(required=False, **kwargs)
|
return configtypes.File(required=False, **kwargs)
|
||||||
|
|
||||||
@ -1552,49 +1447,6 @@ class TestWebKitByte:
|
|||||||
assert klass().transform(val) == expected
|
assert klass().transform(val) == expected
|
||||||
|
|
||||||
|
|
||||||
class TestWebKitBytesList:
|
|
||||||
|
|
||||||
"""Test WebKitBytesList."""
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def klass(self):
|
|
||||||
return configtypes.WebKitBytesList
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('kwargs, val', [
|
|
||||||
({}, '23,56k,1337'),
|
|
||||||
({'maxsize': 2}, '2'),
|
|
||||||
({'maxsize': 2048}, '2k'),
|
|
||||||
({'length': 3}, '1,2,3'),
|
|
||||||
({'none_ok': True}, '23,,42'),
|
|
||||||
({'none_ok': True}, ''),
|
|
||||||
])
|
|
||||||
def test_validate_valid(self, klass, kwargs, val):
|
|
||||||
klass(**kwargs).validate(val)
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('kwargs, val', [
|
|
||||||
({}, '23,56kk,1337'),
|
|
||||||
({'maxsize': 2}, '3'),
|
|
||||||
({'maxsize': 2}, '3k'),
|
|
||||||
({}, '23,,42'),
|
|
||||||
({'length': 3}, '1,2'),
|
|
||||||
({'length': 3}, '1,2,3,4'),
|
|
||||||
({}, '23,,42'),
|
|
||||||
({}, ''),
|
|
||||||
])
|
|
||||||
def test_validate_invalid(self, klass, kwargs, val):
|
|
||||||
with pytest.raises(configexc.ValidationError):
|
|
||||||
klass(**kwargs).validate(val)
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('val, expected', [
|
|
||||||
('1k', [1024]),
|
|
||||||
('23,2k,1337', [23, 2048, 1337]),
|
|
||||||
('23,,42', [23, None, 42]),
|
|
||||||
('', None),
|
|
||||||
])
|
|
||||||
def test_transform_single(self, klass, val, expected):
|
|
||||||
assert klass().transform(val) == expected
|
|
||||||
|
|
||||||
|
|
||||||
class TestShellCommand:
|
class TestShellCommand:
|
||||||
|
|
||||||
"""Test ShellCommand."""
|
"""Test ShellCommand."""
|
||||||
@ -1961,39 +1813,29 @@ class TestEncoding:
|
|||||||
assert klass().transform(val) == expected
|
assert klass().transform(val) == expected
|
||||||
|
|
||||||
|
|
||||||
class TestUrlList:
|
class TestUrl:
|
||||||
|
|
||||||
"""Test UrlList."""
|
"""Test Url."""
|
||||||
|
|
||||||
TESTS = {
|
TESTS = {
|
||||||
'http://qutebrowser.org/': [QUrl('http://qutebrowser.org/')],
|
'http://qutebrowser.org/': QUrl('http://qutebrowser.org/'),
|
||||||
'http://qutebrowser.org/,http://heise.de/':
|
'http://heise.de/': QUrl('http://heise.de/'),
|
||||||
[QUrl('http://qutebrowser.org/'), QUrl('http://heise.de/')],
|
|
||||||
'': None,
|
'': None,
|
||||||
}
|
}
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def klass(self):
|
def klass(self):
|
||||||
return configtypes.UrlList
|
return configtypes.Url
|
||||||
|
|
||||||
@pytest.mark.parametrize('val', sorted(TESTS))
|
@pytest.mark.parametrize('val', sorted(TESTS))
|
||||||
def test_validate_valid(self, klass, val):
|
def test_validate_valid(self, klass, val):
|
||||||
klass(none_ok=True).validate(val)
|
klass(none_ok=True).validate(val)
|
||||||
|
|
||||||
@pytest.mark.parametrize('val', [
|
@pytest.mark.parametrize('val', ['', '+'])
|
||||||
'',
|
|
||||||
'foo,,bar',
|
|
||||||
'+', # invalid URL with QUrl.fromUserInput
|
|
||||||
])
|
|
||||||
def test_validate_invalid(self, klass, val):
|
def test_validate_invalid(self, klass, val):
|
||||||
with pytest.raises(configexc.ValidationError):
|
with pytest.raises(configexc.ValidationError):
|
||||||
klass().validate(val)
|
klass().validate(val)
|
||||||
|
|
||||||
def test_validate_empty_item(self, klass):
|
|
||||||
"""Test validate with empty item and none_ok = False."""
|
|
||||||
with pytest.raises(configexc.ValidationError):
|
|
||||||
klass().validate('foo,,bar')
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('val, expected', sorted(TESTS.items()))
|
@pytest.mark.parametrize('val, expected', sorted(TESTS.items()))
|
||||||
def test_transform_single(self, klass, val, expected):
|
def test_transform_single(self, klass, val, expected):
|
||||||
assert klass().transform(val) == expected
|
assert klass().transform(val) == expected
|
||||||
|
@ -36,6 +36,9 @@ def gen_classes():
|
|||||||
pass
|
pass
|
||||||
elif member is configtypes.MappingType:
|
elif member is configtypes.MappingType:
|
||||||
pass
|
pass
|
||||||
|
elif member is configtypes.List:
|
||||||
|
yield functools.partial(member, inner_type=configtypes.Int())
|
||||||
|
yield functools.partial(member, inner_type=configtypes.Url())
|
||||||
elif member is configtypes.FormatString:
|
elif member is configtypes.FormatString:
|
||||||
yield functools.partial(member, fields=['a', 'b'])
|
yield functools.partial(member, fields=['a', 'b'])
|
||||||
elif issubclass(member, configtypes.BaseType):
|
elif issubclass(member, configtypes.BaseType):
|
||||||
|
Loading…
Reference in New Issue
Block a user