From d77145a5b8907ebf4cd5addbb1ac25d850e1dc10 Mon Sep 17 00:00:00 2001 From: Marshall Lochbaum Date: Tue, 26 Jul 2016 21:29:40 -0400 Subject: [PATCH 01/13] Add GenList type and refactor *List types to use GenList --- qutebrowser/config/configtypes.py | 197 +++++++----------- tests/unit/config/test_configtypes.py | 4 +- .../config/test_configtypes_hypothesis.py | 2 + 3 files changed, 76 insertions(+), 127 deletions(-) diff --git a/qutebrowser/config/configtypes.py b/qutebrowser/config/configtypes.py index de1bbdf27..c5d04c2f7 100644 --- a/qutebrowser/config/configtypes.py +++ b/qutebrowser/config/configtypes.py @@ -299,30 +299,48 @@ class UniqueCharString(String): value, "String contains duplicate values!") -class List(BaseType): +class GenList(BaseType): """Base class for a (string-)list setting.""" - def __init__(self, none_ok=False, valid_values=None): + def __init__(self, inner_type, none_ok=False): super().__init__(none_ok) - self.valid_values = valid_values + self.inner_type = inner_type def transform(self, value): if not value: return None else: - return [v if v else None for v in value.split(',')] + return [self.inner_type.transform(v) for v in value.split(',')] def validate(self, value): self._basic_validation(value) if not value: return - vals = self.transform(value) - if None in vals: - raise configexc.ValidationError(value, "items may not be empty!") + for val in value.split(','): + self.inner_type.validate(val) -class FlagList(List): +class List(GenList): + + """Base class for a (string-)list setting.""" + + 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): + if self.inner_type.valid_values is not None: + super().validate(value) + else: + self._basic_validation(value) + if value: + vals = super().transform(value) + if None in vals: + raise configexc.ValidationError(value, "may not be empty!") + + +class FlagList(GenList): """Base class for a list setting that contains one or more flags. @@ -330,43 +348,40 @@ class FlagList(List): self.valid_values (if not empty). """ + def __init__(self, none_ok=False, valid_values=None): + super().__init__(BaseType(), none_ok) + self.inner_type.valid_values = valid_values + combinable_values = None 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: return - - vals = self.transform(value) - if None in vals and not self.none_ok: - raise configexc.ValidationError( - value, "May not contain empty values!") + vals = super().transform(value) # 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 is not None and - not set_vals.issubset(set(self.valid_values))): - raise configexc.ValidationError( - value, "List contains invalid values!") - def complete(self): - if self.valid_values is None: + valid_values = self.inner_type.valid_values + if valid_values is None: return None out = [] # Single value completions - for value in self.valid_values: - desc = self.valid_values.descriptions.get(value, "") + for value in valid_values: + desc = valid_values.descriptions.get(value, "") out.append((value, desc)) combinables = self.combinable_values if combinables is None: - combinables = list(self.valid_values) + combinables = list(valid_values) # Generate combinations of each possible value combination for size in range(2, len(combinables) + 1): for combination in itertools.combinations(combinables, size): @@ -456,27 +471,13 @@ class Int(BaseType): "smaller!".format(self.maxval)) -class IntList(List): +class IntList(GenList): """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!") + def __init__(self, none_ok=False, valid_values=None): + super().__init__(Int(none_ok=none_ok), none_ok=none_ok) + self.inner_type.valid_values = valid_values class Float(BaseType): @@ -559,7 +560,7 @@ class Perc(BaseType): "less!".format(self.maxval)) -class PercList(List): +class PercList(GenList): """Base class for a list of percentages. @@ -569,38 +570,7 @@ class PercList(List): """ 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!") + super().__init__(Perc(minval, maxval, none_ok), none_ok=none_ok) class PercOrInt(BaseType): @@ -885,34 +855,12 @@ class Regex(BaseType): return re.compile(value, self.flags) -class RegexList(List): +class RegexList(GenList): """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) + super().__init__(Regex(flags, none_ok), none_ok=none_ok) class File(BaseType): @@ -1061,7 +1009,7 @@ class WebKitBytes(BaseType): return int(val) * multiplicator -class WebKitBytesList(List): +class WebKitBytesList(GenList): """A size with an optional suffix. @@ -1071,24 +1019,14 @@ class WebKitBytesList(List): """ def __init__(self, maxsize=None, length=None, none_ok=False): - super().__init__(none_ok) + super().__init__(WebKitBytes(maxsize, none_ok), none_ok=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) + super().validate(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)) @@ -1401,29 +1339,35 @@ class VerticalPosition(BaseType): self.valid_values = ValidValues('top', 'bottom') -class UrlList(List): +class Url(BaseType): - """A list of URLs.""" + """A URL.""" def transform(self, value): if not value: return None else: - return [QUrl.fromUserInput(v) if v else None - for v in value.split(',')] + return QUrl.fromUserInput(value) def validate(self, value): self._basic_validation(value) if not value: return - vals = self.transform(value) - for val in vals: - if val is None: - raise configexc.ValidationError(value, "values may not be " - "empty!") - elif not val.isValid(): - raise configexc.ValidationError(value, "invalid URL - " - "{}".format(val.errorString())) + val = self.transform(value) + if val is None: + raise configexc.ValidationError(value, "URL may not be empty!") + elif not val.isValid(): + raise configexc.ValidationError(value, "invalid URL - " + "{}".format(val.errorString())) + + +class UrlList(GenList): + + """A list of URLs.""" + + def __init__(self, none_ok=False, valid_values=None): + super().__init__(Url(), none_ok) + class HeaderDict(BaseType): @@ -1519,7 +1463,8 @@ class ConfirmQuit(FlagList): def __init__(self, none_ok=False): 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."), ('multiple-tabs', "Show a confirmation if " "multiple tabs are opened."), diff --git a/tests/unit/config/test_configtypes.py b/tests/unit/config/test_configtypes.py index 38958be5d..97cf3f41c 100644 --- a/tests/unit/config/test_configtypes.py +++ b/tests/unit/config/test_configtypes.py @@ -398,7 +398,9 @@ class FlagListSubclass(configtypes.FlagList): def __init__(self, none_ok=False): 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: diff --git a/tests/unit/config/test_configtypes_hypothesis.py b/tests/unit/config/test_configtypes_hypothesis.py index c5a5b9ac0..477df599d 100644 --- a/tests/unit/config/test_configtypes_hypothesis.py +++ b/tests/unit/config/test_configtypes_hypothesis.py @@ -36,6 +36,8 @@ def gen_classes(): pass elif member is configtypes.MappingType: pass + elif member is configtypes.GenList: + pass elif member is configtypes.FormatString: yield functools.partial(member, fields=['a', 'b']) elif issubclass(member, configtypes.BaseType): From 9e8693391351965467494c47ba5f020e5daa3774 Mon Sep 17 00:00:00 2001 From: Marshall Lochbaum Date: Tue, 26 Jul 2016 22:21:59 -0400 Subject: [PATCH 02/13] Replace List with BaseList and rename GenList to List --- qutebrowser/config/configdata.py | 6 ++-- qutebrowser/config/configtypes.py | 36 +++++++------------ tests/unit/config/test_configtypes.py | 23 +++++++++--- .../config/test_configtypes_hypothesis.py | 2 +- 4 files changed, 35 insertions(+), 32 deletions(-) diff --git a/qutebrowser/config/configdata.py b/qutebrowser/config/configdata.py index 5110a6090..238aad78e 100644 --- a/qutebrowser/config/configdata.py +++ b/qutebrowser/config/configdata.py @@ -135,7 +135,7 @@ def data(readonly=False): "Whether to find text on a page case-insensitively."), ('startpage', - SettingValue(typ.List(), 'https://duckduckgo.com'), + SettingValue(typ.BaseList(), 'https://duckduckgo.com'), "The default page(s) to open at the start, separated by commas."), ('default-page', @@ -352,7 +352,7 @@ def data(readonly=False): "(requires restart)"), ('keyhint-blacklist', - SettingValue(typ.List(none_ok=True), ''), + SettingValue(typ.BaseList(none_ok=True), ''), "Keychains that shouldn't be shown in the keyhint dialog\n\n" "Globs are supported, so ';*' will blacklist all keychains" "starting with ';'. Use '*' to disable keyhints"), @@ -845,7 +845,7 @@ def data(readonly=False): "Whether host blocking is enabled."), ('host-blocking-whitelist', - SettingValue(typ.List(none_ok=True), 'piwik.org'), + SettingValue(typ.BaseList(none_ok=True), 'piwik.org'), "List of domains that should always be loaded, despite being " "ad-blocked.\n\n" "Domains may contain * and ? wildcards and are otherwise " diff --git a/qutebrowser/config/configtypes.py b/qutebrowser/config/configtypes.py index c5d04c2f7..62a72587e 100644 --- a/qutebrowser/config/configtypes.py +++ b/qutebrowser/config/configtypes.py @@ -299,7 +299,7 @@ class UniqueCharString(String): value, "String contains duplicate values!") -class GenList(BaseType): +class List(BaseType): """Base class for a (string-)list setting.""" @@ -311,19 +311,20 @@ class GenList(BaseType): if not value: return None else: - return [self.inner_type.transform(v) for v in value.split(',')] + return [self.inner_type.transform(v.strip()) + for v in value.split(',')] def validate(self, value): self._basic_validation(value) if not value: return for val in value.split(','): - self.inner_type.validate(val) + self.inner_type.validate(val.strip()) -class List(GenList): +class BaseList(List): - """Base class for a (string-)list setting.""" + """Base class for a list using BaseType.""" def __init__(self, none_ok=False, valid_values=None): super().__init__(BaseType(), none_ok) @@ -334,13 +335,9 @@ class List(GenList): super().validate(value) else: self._basic_validation(value) - if value: - vals = super().transform(value) - if None in vals: - raise configexc.ValidationError(value, "may not be empty!") -class FlagList(GenList): +class FlagList(BaseList): """Base class for a list setting that contains one or more flags. @@ -348,17 +345,10 @@ class FlagList(GenList): self.valid_values (if not empty). """ - def __init__(self, none_ok=False, valid_values=None): - super().__init__(BaseType(), none_ok) - self.inner_type.valid_values = valid_values - combinable_values = None def validate(self, value): - if self.inner_type.valid_values is not None: - super().validate(value) - else: - self._basic_validation(value) + super().validate(value) if not value: return vals = super().transform(value) @@ -471,7 +461,7 @@ class Int(BaseType): "smaller!".format(self.maxval)) -class IntList(GenList): +class IntList(List): """Base class for an int-list setting.""" @@ -560,7 +550,7 @@ class Perc(BaseType): "less!".format(self.maxval)) -class PercList(GenList): +class PercList(List): """Base class for a list of percentages. @@ -855,7 +845,7 @@ class Regex(BaseType): return re.compile(value, self.flags) -class RegexList(GenList): +class RegexList(List): """A list of regexes.""" @@ -1009,7 +999,7 @@ class WebKitBytes(BaseType): return int(val) * multiplicator -class WebKitBytesList(GenList): +class WebKitBytesList(List): """A size with an optional suffix. @@ -1361,7 +1351,7 @@ class Url(BaseType): "{}".format(val.errorString())) -class UrlList(GenList): +class UrlList(List): """A list of URLs.""" diff --git a/tests/unit/config/test_configtypes.py b/tests/unit/config/test_configtypes.py index 97cf3f41c..e0c8293cb 100644 --- a/tests/unit/config/test_configtypes.py +++ b/tests/unit/config/test_configtypes.py @@ -354,18 +354,31 @@ class TestString: 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): + super().__init__(configtypes.BaseType(none_ok_inner), none_ok_outer) + self.inner_type.valid_values = configtypes.ValidValues('foo', + 'bar', 'baz') + + class TestList: """Test List.""" @pytest.fixture def klass(self): - return configtypes.List + return ListSubclass @pytest.mark.parametrize('val', ['', 'foo', 'foo,bar', 'foo, bar']) 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']) def test_validate_invalid(self, klass, val): @@ -374,14 +387,14 @@ class TestList: def test_invalid_empty_value_none_ok(self, klass): with pytest.raises(configexc.ValidationError): - klass(none_ok=True).validate('foo,,bar') + klass(none_ok_outer=True).validate('foo,,bar') + klass(none_ok_inner=True).validate('') @pytest.mark.parametrize('val, expected', [ ('foo', ['foo']), ('foo,bar,baz', ['foo', 'bar', 'baz']), ('', None), - # Not implemented yet - pytest.mark.xfail(('foo, bar', ['foo', 'bar'])), + ('foo, bar', ['foo', 'bar']) ]) def test_transform(self, klass, val, expected): assert klass().transform(val) == expected diff --git a/tests/unit/config/test_configtypes_hypothesis.py b/tests/unit/config/test_configtypes_hypothesis.py index 477df599d..a5b40c417 100644 --- a/tests/unit/config/test_configtypes_hypothesis.py +++ b/tests/unit/config/test_configtypes_hypothesis.py @@ -36,7 +36,7 @@ def gen_classes(): pass elif member is configtypes.MappingType: pass - elif member is configtypes.GenList: + elif member is configtypes.List: pass elif member is configtypes.FormatString: yield functools.partial(member, fields=['a', 'b']) From 083baf1222ea9f95c71b5eecf1360adfefae7deb Mon Sep 17 00:00:00 2001 From: Marshall Lochbaum Date: Tue, 26 Jul 2016 23:05:42 -0400 Subject: [PATCH 03/13] Remove unnecessary List subclasses --- qutebrowser/config/configdata.py | 8 +- qutebrowser/config/configtypes.py | 45 +------ tests/unit/config/test_configtypes.py | 168 -------------------------- 3 files changed, 9 insertions(+), 212 deletions(-) diff --git a/qutebrowser/config/configdata.py b/qutebrowser/config/configdata.py index 238aad78e..84c670c60 100644 --- a/qutebrowser/config/configdata.py +++ b/qutebrowser/config/configdata.py @@ -254,7 +254,7 @@ def data(readonly=False): ('ui', sect.KeyValue( ('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%,' '200%,250%,300%,400%,500%'), "The available zoom levels, separated by commas."), @@ -826,7 +826,7 @@ def data(readonly=False): ('host-block-lists', SettingValue( - typ.UrlList(none_ok=True), + typ.List(typ.Url(), none_ok=True), 'http://www.malwaredomainlist.com/hostslist/hosts.txt,' 'http://someonewhocares.org/hosts/hosts,' 'http://winhelp2002.mvps.org/hosts.zip,' @@ -916,13 +916,13 @@ def data(readonly=False): "auto-follow."), ('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'\bcontinue\b'), "A comma-separated list of regexes to use for 'next' links."), ('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'\b(<<|«)\b'), "A comma-separated list of regexes to use for 'prev' links."), diff --git a/qutebrowser/config/configtypes.py b/qutebrowser/config/configtypes.py index 62a72587e..05fa6a15d 100644 --- a/qutebrowser/config/configtypes.py +++ b/qutebrowser/config/configtypes.py @@ -461,15 +461,6 @@ class Int(BaseType): "smaller!".format(self.maxval)) -class IntList(List): - - """Base class for an int-list setting.""" - - def __init__(self, none_ok=False, valid_values=None): - super().__init__(Int(none_ok=none_ok), none_ok=none_ok) - self.inner_type.valid_values = valid_values - - class Float(BaseType): """Base class for a float setting. @@ -550,19 +541,6 @@ class Perc(BaseType): "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__(Perc(minval, maxval, none_ok), none_ok=none_ok) - - class PercOrInt(BaseType): """Percentage or integer. @@ -845,14 +823,6 @@ class Regex(BaseType): return re.compile(value, self.flags) -class RegexList(List): - - """A list of regexes.""" - - def __init__(self, flags=0, none_ok=False): - super().__init__(Regex(flags, none_ok), none_ok=none_ok) - - class File(BaseType): """A file on the local filesystem.""" @@ -1171,10 +1141,14 @@ PaddingValues = collections.namedtuple('PaddingValues', ['top', 'bottom', 'left', 'right']) -class Padding(IntList): +class Padding(List): """Setting for paddings around elements.""" + def __init__(self, none_ok=False, valid_values=None): + super().__init__(Int(none_ok=none_ok), none_ok=none_ok) + self.inner_type.valid_values = valid_values + def validate(self, value): self._basic_validation(value) if not value: @@ -1351,15 +1325,6 @@ class Url(BaseType): "{}".format(val.errorString())) -class UrlList(List): - - """A list of URLs.""" - - def __init__(self, none_ok=False, valid_values=None): - super().__init__(Url(), none_ok) - - - class HeaderDict(BaseType): """A JSON-like dictionary for custom HTTP headers.""" diff --git a/tests/unit/config/test_configtypes.py b/tests/unit/config/test_configtypes.py index e0c8293cb..5537d45ea 100644 --- a/tests/unit/config/test_configtypes.py +++ b/tests/unit/config/test_configtypes.py @@ -595,37 +595,6 @@ class TestInt: 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: """Test Float.""" @@ -718,52 +687,6 @@ class TestPerc: 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: """Test PercOrInt.""" @@ -1239,59 +1162,6 @@ class TestRegex: 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): return configtypes.File(required=False, **kwargs) @@ -1976,44 +1846,6 @@ class TestEncoding: assert klass().transform(val) == expected -class TestUrlList: - - """Test UrlList.""" - - TESTS = { - 'http://qutebrowser.org/': [QUrl('http://qutebrowser.org/')], - 'http://qutebrowser.org/,http://heise.de/': - [QUrl('http://qutebrowser.org/'), QUrl('http://heise.de/')], - '': None, - } - - @pytest.fixture - def klass(self): - return configtypes.UrlList - - @pytest.mark.parametrize('val', sorted(TESTS)) - def test_validate_valid(self, klass, val): - klass(none_ok=True).validate(val) - - @pytest.mark.parametrize('val', [ - '', - 'foo,,bar', - '+', # invalid URL with QUrl.fromUserInput - ]) - def test_validate_invalid(self, klass, val): - with pytest.raises(configexc.ValidationError): - 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())) - def test_transform_single(self, klass, val, expected): - assert klass().transform(val) == expected - - class TestSessionName: """Test SessionName.""" From a3c7ed51d4b935683d99b87aa2063cfb2f721db5 Mon Sep 17 00:00:00 2001 From: Marshall Lochbaum Date: Tue, 26 Jul 2016 23:59:55 -0400 Subject: [PATCH 04/13] Create LengthList to remove WebKitBytesList and refactor Padding --- qutebrowser/config/configdata.py | 4 +- qutebrowser/config/configtypes.py | 61 +++++--------- tests/unit/config/test_configtypes.py | 84 +++++++++---------- .../config/test_configtypes_hypothesis.py | 2 +- 4 files changed, 64 insertions(+), 87 deletions(-) diff --git a/qutebrowser/config/configdata.py b/qutebrowser/config/configdata.py index 84c670c60..59998c090 100644 --- a/qutebrowser/config/configdata.py +++ b/qutebrowser/config/configdata.py @@ -684,8 +684,8 @@ def data(readonly=False): ('object-cache-capacities', SettingValue( - typ.WebKitBytesList(length=3, maxsize=MAXVALS['int'], - none_ok=True), ''), + typ.LengthList(typ.WebKitBytes(maxsize=MAXVALS['int'], + none_ok=True), none_ok=True, length=3), ''), "The capacities for the global memory cache for dead objects " "such as stylesheets or scripts. Syntax: cacheMinDeadCapacity, " "cacheMaxDead, totalCapacity.\n\n" diff --git a/qutebrowser/config/configtypes.py b/qutebrowser/config/configtypes.py index 05fa6a15d..0ed6ddb5e 100644 --- a/qutebrowser/config/configtypes.py +++ b/qutebrowser/config/configtypes.py @@ -337,6 +337,23 @@ class BaseList(List): self._basic_validation(value) +class LengthList(List): + + """Base class for a list with length checking.""" + + def __init__(self, inner_type, length=None, none_ok=False): + super().__init__(inner_type, none_ok) + self.length = length + + def validate(self, value): + super().validate(value) + if not value: + return + if self.length is not None and value.count(',')+1 != self.length: + raise configexc.ValidationError(value, "Exactly {} values need to " + "be set!".format(self.length)) + + class FlagList(BaseList): """Base class for a list setting that contains one or more flags. @@ -969,29 +986,6 @@ class WebKitBytes(BaseType): 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__(WebKitBytes(maxsize, none_ok), none_ok=none_ok) - self.length = length - - def validate(self, value): - super().validate(value) - if not value: - return - vals = super().transform(value) - 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): """A shellcommand which is split via shlex. @@ -1141,30 +1135,15 @@ PaddingValues = collections.namedtuple('PaddingValues', ['top', 'bottom', 'left', 'right']) -class Padding(List): +class Padding(LengthList): """Setting for paddings around elements.""" def __init__(self, none_ok=False, valid_values=None): - super().__init__(Int(none_ok=none_ok), none_ok=none_ok) + super().__init__(Int(minval=0, none_ok=none_ok), + none_ok=none_ok, length=4) self.inner_type.valid_values = valid_values - def validate(self, value): - self._basic_validation(value) - if not value: - return - try: - vals = self.transform(value) - 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): elems = super().transform(value) if elems is None: diff --git a/tests/unit/config/test_configtypes.py b/tests/unit/config/test_configtypes.py index 5537d45ea..32600e026 100644 --- a/tests/unit/config/test_configtypes.py +++ b/tests/unit/config/test_configtypes.py @@ -400,6 +400,47 @@ class TestList: assert klass().transform(val) == expected +class LengthListSubclass(configtypes.LengthList): + + """A subclass of LengthList which we use in tests.""" + + def __init__(self, length=None, none_ok_inner=False, none_ok_outer=False): + super().__init__(configtypes.Int(none_ok=none_ok_inner), + length=length, none_ok=none_ok_outer) + + +class TestLengthList: + + """Test List with length.""" + + @pytest.fixture + def klass(self): + return LengthListSubclass + + @pytest.mark.parametrize('val', ['', '0,1,2', '-5,4,2']) + def test_validate_valid(self, klass, val): + klass(none_ok_outer=True, length=3).validate(val) + + @pytest.mark.parametrize('val', ['', '1,,4']) + def test_validate_invalid(self, klass, val): + with pytest.raises(configexc.ValidationError): + klass().validate(val) + + def test_invalid_empty_value_none_ok(self, klass): + with pytest.raises(configexc.ValidationError): + klass(none_ok_outer=True).validate('1,,4') + klass(none_ok_inner=True).validate('') + + @pytest.mark.parametrize('val', ['-8', '0,-1', '1,2,3,4,5']) + def test_no_length_given(self, klass, val): + klass().validate(val) + + @pytest.mark.parametrize('val', ['-8', '0,-1', '1,2,3,4,5']) + def test_wrong_length(self, klass, val): + with pytest.raises(configexc.ValidationError): + klass(length=3).validate(val) + + class FlagListSubclass(configtypes.FlagList): """A subclass of FlagList which we use in tests. @@ -1437,49 +1478,6 @@ class TestWebKitByte: 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: """Test ShellCommand.""" diff --git a/tests/unit/config/test_configtypes_hypothesis.py b/tests/unit/config/test_configtypes_hypothesis.py index a5b40c417..a575cae3a 100644 --- a/tests/unit/config/test_configtypes_hypothesis.py +++ b/tests/unit/config/test_configtypes_hypothesis.py @@ -36,7 +36,7 @@ def gen_classes(): pass elif member is configtypes.MappingType: pass - elif member is configtypes.List: + elif member in [configtypes.List, configtypes.LengthList]: pass elif member is configtypes.FormatString: yield functools.partial(member, fields=['a', 'b']) From b4fec256dce123f1f8b70caa96c4a18096afb0f8 Mon Sep 17 00:00:00 2001 From: Marshall Lochbaum Date: Wed, 27 Jul 2016 10:20:10 -0400 Subject: [PATCH 05/13] Merge LengthList into List --- qutebrowser/config/configdata.py | 4 +- qutebrowser/config/configtypes.py | 28 +++------ tests/unit/config/test_configtypes.py | 59 +++++-------------- .../config/test_configtypes_hypothesis.py | 2 +- 4 files changed, 25 insertions(+), 68 deletions(-) diff --git a/qutebrowser/config/configdata.py b/qutebrowser/config/configdata.py index 59998c090..16b76a681 100644 --- a/qutebrowser/config/configdata.py +++ b/qutebrowser/config/configdata.py @@ -684,8 +684,8 @@ def data(readonly=False): ('object-cache-capacities', SettingValue( - typ.LengthList(typ.WebKitBytes(maxsize=MAXVALS['int'], - none_ok=True), none_ok=True, length=3), ''), + typ.List(typ.WebKitBytes(maxsize=MAXVALS['int'], + none_ok=True), none_ok=True, length=3), ''), "The capacities for the global memory cache for dead objects " "such as stylesheets or scripts. Syntax: cacheMinDeadCapacity, " "cacheMaxDead, totalCapacity.\n\n" diff --git a/qutebrowser/config/configtypes.py b/qutebrowser/config/configtypes.py index 0ed6ddb5e..c30f582dd 100644 --- a/qutebrowser/config/configtypes.py +++ b/qutebrowser/config/configtypes.py @@ -303,9 +303,10 @@ class List(BaseType): """Base class for a (string-)list setting.""" - def __init__(self, inner_type, none_ok=False): + def __init__(self, inner_type, none_ok=False, length=None): super().__init__(none_ok) self.inner_type = inner_type + self.length = length def transform(self, value): if not value: @@ -318,7 +319,11 @@ class List(BaseType): self._basic_validation(value) if not value: return - for val in value.split(','): + vals = value.split(',') + if self.length is not None and len(vals) != self.length: + raise configexc.ValidationError(value, "Exactly {} values need to " + "be set!".format(self.length)) + for val in vals: self.inner_type.validate(val.strip()) @@ -337,23 +342,6 @@ class BaseList(List): self._basic_validation(value) -class LengthList(List): - - """Base class for a list with length checking.""" - - def __init__(self, inner_type, length=None, none_ok=False): - super().__init__(inner_type, none_ok) - self.length = length - - def validate(self, value): - super().validate(value) - if not value: - return - if self.length is not None and value.count(',')+1 != self.length: - raise configexc.ValidationError(value, "Exactly {} values need to " - "be set!".format(self.length)) - - class FlagList(BaseList): """Base class for a list setting that contains one or more flags. @@ -1135,7 +1123,7 @@ PaddingValues = collections.namedtuple('PaddingValues', ['top', 'bottom', 'left', 'right']) -class Padding(LengthList): +class Padding(List): """Setting for paddings around elements.""" diff --git a/tests/unit/config/test_configtypes.py b/tests/unit/config/test_configtypes.py index 32600e026..cfb338643 100644 --- a/tests/unit/config/test_configtypes.py +++ b/tests/unit/config/test_configtypes.py @@ -361,8 +361,9 @@ class ListSubclass(configtypes.List): Valid values are 'foo', 'bar' and 'baz'. """ - def __init__(self, none_ok_inner=False, none_ok_outer=False): - super().__init__(configtypes.BaseType(none_ok_inner), none_ok_outer) + 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') @@ -375,8 +376,7 @@ class TestList: def klass(self): return ListSubclass - @pytest.mark.parametrize('val', - ['', 'foo', 'foo,bar', 'foo, bar']) + @pytest.mark.parametrize('val', ['', 'foo', 'foo,bar', 'foo, bar']) def test_validate_valid(self, klass, val): klass(none_ok_outer=True).validate(val) @@ -388,8 +388,18 @@ class TestList: def test_invalid_empty_value_none_ok(self, klass): with pytest.raises(configexc.ValidationError): 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', [ ('foo', ['foo']), ('foo,bar,baz', ['foo', 'bar', 'baz']), @@ -400,47 +410,6 @@ class TestList: assert klass().transform(val) == expected -class LengthListSubclass(configtypes.LengthList): - - """A subclass of LengthList which we use in tests.""" - - def __init__(self, length=None, none_ok_inner=False, none_ok_outer=False): - super().__init__(configtypes.Int(none_ok=none_ok_inner), - length=length, none_ok=none_ok_outer) - - -class TestLengthList: - - """Test List with length.""" - - @pytest.fixture - def klass(self): - return LengthListSubclass - - @pytest.mark.parametrize('val', ['', '0,1,2', '-5,4,2']) - def test_validate_valid(self, klass, val): - klass(none_ok_outer=True, length=3).validate(val) - - @pytest.mark.parametrize('val', ['', '1,,4']) - def test_validate_invalid(self, klass, val): - with pytest.raises(configexc.ValidationError): - klass().validate(val) - - def test_invalid_empty_value_none_ok(self, klass): - with pytest.raises(configexc.ValidationError): - klass(none_ok_outer=True).validate('1,,4') - klass(none_ok_inner=True).validate('') - - @pytest.mark.parametrize('val', ['-8', '0,-1', '1,2,3,4,5']) - def test_no_length_given(self, klass, val): - klass().validate(val) - - @pytest.mark.parametrize('val', ['-8', '0,-1', '1,2,3,4,5']) - def test_wrong_length(self, klass, val): - with pytest.raises(configexc.ValidationError): - klass(length=3).validate(val) - - class FlagListSubclass(configtypes.FlagList): """A subclass of FlagList which we use in tests. diff --git a/tests/unit/config/test_configtypes_hypothesis.py b/tests/unit/config/test_configtypes_hypothesis.py index a575cae3a..a5b40c417 100644 --- a/tests/unit/config/test_configtypes_hypothesis.py +++ b/tests/unit/config/test_configtypes_hypothesis.py @@ -36,7 +36,7 @@ def gen_classes(): pass elif member is configtypes.MappingType: pass - elif member in [configtypes.List, configtypes.LengthList]: + elif member is configtypes.List: pass elif member is configtypes.FormatString: yield functools.partial(member, fields=['a', 'b']) From b205ad500b94bcf836ab8d84cdd138316796645b Mon Sep 17 00:00:00 2001 From: Marshall Lochbaum Date: Wed, 27 Jul 2016 10:31:02 -0400 Subject: [PATCH 06/13] Add tests for configtypes.Url --- qutebrowser/config/configtypes.py | 4 +--- tests/unit/config/test_configtypes.py | 28 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/qutebrowser/config/configtypes.py b/qutebrowser/config/configtypes.py index c30f582dd..4907d9f2f 100644 --- a/qutebrowser/config/configtypes.py +++ b/qutebrowser/config/configtypes.py @@ -1285,9 +1285,7 @@ class Url(BaseType): if not value: return val = self.transform(value) - if val is None: - raise configexc.ValidationError(value, "URL may not be empty!") - elif not val.isValid(): + if not val.isValid(): raise configexc.ValidationError(value, "invalid URL - " "{}".format(val.errorString())) diff --git a/tests/unit/config/test_configtypes.py b/tests/unit/config/test_configtypes.py index cfb338643..391ae757a 100644 --- a/tests/unit/config/test_configtypes.py +++ b/tests/unit/config/test_configtypes.py @@ -1813,6 +1813,34 @@ class TestEncoding: assert klass().transform(val) == expected +class TestUrl: + + """Test Url.""" + + TESTS = { + 'http://qutebrowser.org/': QUrl('http://qutebrowser.org/'), + 'http://heise.de/': QUrl('http://heise.de/'), + '': None, + } + + @pytest.fixture + def klass(self): + return configtypes.Url + + @pytest.mark.parametrize('val', sorted(TESTS)) + def test_validate_valid(self, klass, val): + klass(none_ok=True).validate(val) + + @pytest.mark.parametrize('val', ['', '+']) + def test_validate_invalid(self, klass, val): + with pytest.raises(configexc.ValidationError): + klass().validate(val) + + @pytest.mark.parametrize('val, expected', sorted(TESTS.items())) + def test_transform_single(self, klass, val, expected): + assert klass().transform(val) == expected + + class TestSessionName: """Test SessionName.""" From 642abac634fe6f8bdb01a1c1871c300bf915a1e7 Mon Sep 17 00:00:00 2001 From: Marshall Lochbaum Date: Wed, 27 Jul 2016 13:06:15 -0400 Subject: [PATCH 07/13] Remove BaseList type --- qutebrowser/config/configdata.py | 7 ++++--- qutebrowser/config/configtypes.py | 24 ++++++++---------------- 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/qutebrowser/config/configdata.py b/qutebrowser/config/configdata.py index 16b76a681..1319f1948 100644 --- a/qutebrowser/config/configdata.py +++ b/qutebrowser/config/configdata.py @@ -135,7 +135,8 @@ def data(readonly=False): "Whether to find text on a page case-insensitively."), ('startpage', - SettingValue(typ.BaseList(), 'https://duckduckgo.com'), + SettingValue(typ.List(typ.Url(), none_ok=True), + 'https://duckduckgo.com'), "The default page(s) to open at the start, separated by commas."), ('default-page', @@ -352,7 +353,7 @@ def data(readonly=False): "(requires restart)"), ('keyhint-blacklist', - SettingValue(typ.BaseList(none_ok=True), ''), + SettingValue(typ.List(typ.String(), none_ok=True), ''), "Keychains that shouldn't be shown in the keyhint dialog\n\n" "Globs are supported, so ';*' will blacklist all keychains" "starting with ';'. Use '*' to disable keyhints"), @@ -845,7 +846,7 @@ def data(readonly=False): "Whether host blocking is enabled."), ('host-blocking-whitelist', - SettingValue(typ.BaseList(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 " "ad-blocked.\n\n" "Domains may contain * and ? wildcards and are otherwise " diff --git a/qutebrowser/config/configtypes.py b/qutebrowser/config/configtypes.py index 4907d9f2f..88e1a88b1 100644 --- a/qutebrowser/config/configtypes.py +++ b/qutebrowser/config/configtypes.py @@ -327,9 +327,15 @@ class List(BaseType): self.inner_type.validate(val.strip()) -class BaseList(List): +class FlagList(List): - """Base class for a list using BaseType.""" + """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). + """ + + combinable_values = None def __init__(self, none_ok=False, valid_values=None): super().__init__(BaseType(), none_ok) @@ -340,20 +346,6 @@ class BaseList(List): super().validate(value) else: self._basic_validation(value) - - -class FlagList(BaseList): - - """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). - """ - - combinable_values = None - - def validate(self, value): - super().validate(value) if not value: return vals = super().transform(value) From d51b1733e4827d852955ea112faa3e7e8f40d79a Mon Sep 17 00:00:00 2001 From: Marshall Lochbaum Date: Wed, 27 Jul 2016 13:14:51 -0400 Subject: [PATCH 08/13] Add List to test_configtypes_hypothesis --- tests/unit/config/test_configtypes_hypothesis.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/unit/config/test_configtypes_hypothesis.py b/tests/unit/config/test_configtypes_hypothesis.py index a5b40c417..329d7357c 100644 --- a/tests/unit/config/test_configtypes_hypothesis.py +++ b/tests/unit/config/test_configtypes_hypothesis.py @@ -37,7 +37,8 @@ def gen_classes(): elif member is configtypes.MappingType: pass elif member is configtypes.List: - pass + yield functools.partial(member, inner_type=configtypes.Int()) + yield functools.partial(member, inner_type=configtypes.Url()) elif member is configtypes.FormatString: yield functools.partial(member, fields=['a', 'b']) elif issubclass(member, configtypes.BaseType): From 2c37de3ac1e1cce10c85061eb778094943a32126 Mon Sep 17 00:00:00 2001 From: Marshall Lochbaum Date: Wed, 27 Jul 2016 14:21:25 -0400 Subject: [PATCH 09/13] Revert startpage config type to String instead of Url --- qutebrowser/config/configdata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qutebrowser/config/configdata.py b/qutebrowser/config/configdata.py index 1319f1948..34debc410 100644 --- a/qutebrowser/config/configdata.py +++ b/qutebrowser/config/configdata.py @@ -135,7 +135,7 @@ def data(readonly=False): "Whether to find text on a page case-insensitively."), ('startpage', - SettingValue(typ.List(typ.Url(), none_ok=True), + SettingValue(typ.List(typ.String(), none_ok=True), 'https://duckduckgo.com'), "The default page(s) to open at the start, separated by commas."), From b22634e2ebe2a41b30bb551b55b7a64a6398c169 Mon Sep 17 00:00:00 2001 From: Marshall Lochbaum Date: Fri, 29 Jul 2016 16:08:42 -0400 Subject: [PATCH 10/13] Correct qutebrowser.conf documentation for list types --- qutebrowser/config/config.py | 4 ++-- qutebrowser/config/configtypes.py | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/qutebrowser/config/config.py b/qutebrowser/config/config.py index 863ecd5c5..c4257d0cf 100644 --- a/qutebrowser/config/config.py +++ b/qutebrowser/config/config.py @@ -419,7 +419,7 @@ class ConfigManager(QObject): for optname, option in sect.items(): lines.append('#') - typestr = ' ({})'.format(option.typ.__class__.__name__) + typestr = ' ({})'.format(option.typ.get_name()) lines.append("# {}{}:".format(optname, typestr)) try: @@ -430,7 +430,7 @@ class ConfigManager(QObject): continue for descline in desc.splitlines(): 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.descriptions: for val in valid_values: diff --git a/qutebrowser/config/configtypes.py b/qutebrowser/config/configtypes.py index 88e1a88b1..d20962f4b 100644 --- a/qutebrowser/config/configtypes.py +++ b/qutebrowser/config/configtypes.py @@ -124,6 +124,14 @@ class BaseType: self.none_ok = none_ok 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): """Do some basic validation for the value (empty, non-printable chars). @@ -308,6 +316,17 @@ class List(BaseType): self.inner_type = inner_type self.length = length + _show_inner_type = True + + 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): if not value: return None @@ -341,6 +360,8 @@ class FlagList(List): super().__init__(BaseType(), none_ok) self.inner_type.valid_values = valid_values + _show_inner_type = False + def validate(self, value): if self.inner_type.valid_values is not None: super().validate(value) @@ -1124,6 +1145,8 @@ class Padding(List): none_ok=none_ok, length=4) self.inner_type.valid_values = valid_values + _show_inner_type = False + def transform(self, value): elems = super().transform(value) if elems is None: From aa40ff6dd43f512b6db65bf3f1b47088361564a3 Mon Sep 17 00:00:00 2001 From: Marshall Lochbaum Date: Mon, 1 Aug 2016 09:30:26 -0400 Subject: [PATCH 11/13] Remove none_ok from startpage config type --- qutebrowser/config/configdata.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/qutebrowser/config/configdata.py b/qutebrowser/config/configdata.py index 34debc410..f81d3cf8f 100644 --- a/qutebrowser/config/configdata.py +++ b/qutebrowser/config/configdata.py @@ -135,8 +135,7 @@ def data(readonly=False): "Whether to find text on a page case-insensitively."), ('startpage', - SettingValue(typ.List(typ.String(), none_ok=True), - 'https://duckduckgo.com'), + SettingValue(typ.List(typ.String()), 'https://duckduckgo.com'), "The default page(s) to open at the start, separated by commas."), ('default-page', From 5accef963d322e3e7c526f43e5628d89685e21a6 Mon Sep 17 00:00:00 2001 From: Marshall Lochbaum Date: Mon, 1 Aug 2016 09:31:59 -0400 Subject: [PATCH 12/13] Move _show_inner_type class variables before __init --- qutebrowser/config/configtypes.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/qutebrowser/config/configtypes.py b/qutebrowser/config/configtypes.py index d20962f4b..398a5c9bc 100644 --- a/qutebrowser/config/configtypes.py +++ b/qutebrowser/config/configtypes.py @@ -311,13 +311,13 @@ class List(BaseType): """Base class for a (string-)list setting.""" + _show_inner_type = True + def __init__(self, inner_type, none_ok=False, length=None): super().__init__(none_ok) self.inner_type = inner_type self.length = length - _show_inner_type = True - def get_name(self): name = super().get_name() if self._show_inner_type: @@ -356,12 +356,12 @@ class FlagList(List): 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 - _show_inner_type = False - def validate(self, value): if self.inner_type.valid_values is not None: super().validate(value) @@ -1140,13 +1140,13 @@ class Padding(List): """Setting for paddings around elements.""" + _show_inner_type = 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 - _show_inner_type = False - def transform(self, value): elems = super().transform(value) if elems is None: From a7f9461d1f2cb2e30253c7af9f623e8271cadf02 Mon Sep 17 00:00:00 2001 From: Marshall Lochbaum Date: Mon, 1 Aug 2016 10:15:51 -0400 Subject: [PATCH 13/13] Correct valid_values handling for asciidoc --- scripts/dev/src2asciidoc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/dev/src2asciidoc.py b/scripts/dev/src2asciidoc.py index 6316e0517..d03c26799 100755 --- a/scripts/dev/src2asciidoc.py +++ b/scripts/dev/src2asciidoc.py @@ -357,7 +357,7 @@ def _generate_setting_section(f, sectname, sect): f.write("=== {}".format(optname) + "\n") f.write(sect.descriptions[optname] + "\n") f.write("\n") - valid_values = option.typ.valid_values + valid_values = option.typ.get_valid_values() if valid_values is not None: f.write("Valid values:\n") f.write("\n")