From e7c23312d7a7cbd033bb267a11327a8174887951 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 11 Apr 2014 07:16:16 +0200 Subject: [PATCH 1/8] Start implementing different config layers. With a namedtuple though, which is pretty much a fail. --- qutebrowser/config/config.py | 9 ++++--- qutebrowser/config/sections.py | 48 ++++++++++++++++++++++++++++------ qutebrowser/config/value.py | 46 ++++++++++++++++++++++---------- 3 files changed, 78 insertions(+), 25 deletions(-) diff --git a/qutebrowser/config/config.py b/qutebrowser/config/config.py index 6f34d1ffc..ab938784d 100644 --- a/qutebrowser/config/config.py +++ b/qutebrowser/config/config.py @@ -322,13 +322,16 @@ class Config(QObject): Return: The changed config part as string. """ + # FIXME adopt this for layering lines = [] for secname, section in self.config.items(): changed_opts = [] for optname, option in section.items(): - if (option.rawvalue is not None and - option.rawvalue != option.default): - keyval = '{} = {}'.format(optname, option) + if (option.values.temp is not None and + option.values.temp != option.default or + option.values.conf is not None and + option.values.conf != option.default): + keyval = '{} = {}'.format(optname, option) # FIXME layer? changed_opts.append(keyval) if changed_opts: lines.append('[{}]'.format(secname)) diff --git a/qutebrowser/config/sections.py b/qutebrowser/config/sections.py index 944db15ca..ab1124c70 100644 --- a/qutebrowser/config/sections.py +++ b/qutebrowser/config/sections.py @@ -64,13 +64,13 @@ class KeyValue: return self.values[key] def __setitem__(self, key, value): - """Set the value for key. + """Set the config value for key. Args: key: The key to set the value for, as a string. value: The value to set, as a string """ - self.values[key].value = value + self.setv('conf', key, value) def __iter__(self): """Iterate over all set values.""" @@ -85,15 +85,29 @@ class KeyValue: """Return whether the section contains a given key.""" return key in self.values + def setv(self, layer, key, value): + """Set the value on a layer. + + Arguments: + layer: The layer to set the value on, an element name of the + ValueLayers namedtuple. + key: The key of the element to set. + value: The value to set. + """ + self.values[key].setv(layer, value) + def items(self): """Get dict item tuples.""" return self.values.items() def from_cp(self, sect): - """Initialize the values from a configparser section.""" + """Initialize the values from a configparser section. + + We assume all keys already exist from the defaults. + """ for k, v in sect.items(): logging.debug("'{}' = '{}'".format(k, v)) - self.values[k].value = v + self.values[k].setv('conf', v) class ValueList: @@ -117,6 +131,9 @@ class ValueList: # KeyValue section. """ + # FIXME use a ChainMap for this + # FIXME how to handle value layers here? + def __init__(self, keytype, valtype, *defaults): """Wrap types over default values. Take care when overriding this.""" self.keytype = keytype @@ -149,14 +166,13 @@ class ValueList: return self.default[key] def __setitem__(self, key, value): - """Set the value for key. + """Set the config value for key. Args: key: The key to set the value for, as a string. value: The value to set, as a string """ - self.values[key] = SettingValue(self.valtype) - self.values[key].value = value + self.setv('conf', key, value) def __iter__(self): """Iterate over all set values.""" @@ -174,6 +190,22 @@ class ValueList: self.update_valdict() return key in self.valdict + def setv(self, layer, key, value): + """Set the value on a layer. + + Arguments: + layer: The layer to set the value on, an element name of the + ValueLayers namedtuple. + key: The key of the element to set. + value: The value to set. + """ + if key in self.values: + self.values[key].setv(layer, value) + else: + val = SettingValue(self.valtype) + val.setv(layer, value) + self.values[key] = val + def items(self): """Get dict items.""" self.update_valdict() @@ -186,4 +218,4 @@ class ValueList: for k, v in sect.items(): keytype.validate(k) valtype.validate(v) - self.values[k] = SettingValue(self.valtype, v) + self.setv('conf', k, v) diff --git a/qutebrowser/config/value.py b/qutebrowser/config/value.py index 05ddf14cc..c8ac59044 100644 --- a/qutebrowser/config/value.py +++ b/qutebrowser/config/value.py @@ -17,6 +17,10 @@ """A single value (with multiple layers possibly) in the config.""" +from collections import namedtuple + +ValueLayers = namedtuple('ValueLayers', 'temp, conf, default') + class SettingValue: @@ -26,9 +30,9 @@ class SettingValue: Attributes: typ: A BaseType subclass. - default: Default value if the user has not overridden it, as a string. - value: (property) The currently valid, most important value. - rawvalue: The current value as a raw string. + value: (readonly property) The currently valid, most important value. + _values: A namedtuple with the values on different layers, with the + most significant layer first. """ def __init__(self, typ, default=None): @@ -39,25 +43,39 @@ class SettingValue: default: Raw value to set. """ self.typ = typ() - self.rawvalue = None - self.default = default + self._values = ValueLayers(None, None, None) + self._values.default = default def __str__(self): """Get raw string value.""" return self.value + @property + def value(self): + """Get the currently valid value.""" + for val in self._values: + if val is not None: + return val + else: + raise ValueError("No valid config value found!") + + @property + def values(self): + """Readonly property for _values.""" + return self._values + def transformed(self): """Get the transformed value.""" v = self.value return self.typ.transform(v) - @property - def value(self): - """Get the currently valid value.""" - return self.rawvalue if self.rawvalue is not None else self.default + def setv(self, layer, value): + """Set the value on a layer. - @value.setter - def value(self, val): - """Set the currently valid value.""" - self.typ.validate(val) - self.rawvalue = val + Arguments: + layer: The layer to set the value on, an element name of the + ValueLayers namedtuple. + value: The value to set. + """ + self.typ.validate(value) + setattr(self._values, layer, value) From b0792203a5b9b4f4ff021678e998088838048bac Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 11 Apr 2014 17:49:15 +0200 Subject: [PATCH 2/8] Change namedtuple to OrderedDict for conflayers --- qutebrowser/config/sections.py | 4 ++-- qutebrowser/config/value.py | 17 ++++++++--------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/qutebrowser/config/sections.py b/qutebrowser/config/sections.py index ab1124c70..905100137 100644 --- a/qutebrowser/config/sections.py +++ b/qutebrowser/config/sections.py @@ -90,7 +90,7 @@ class KeyValue: Arguments: layer: The layer to set the value on, an element name of the - ValueLayers namedtuple. + ValueLayers dict. key: The key of the element to set. value: The value to set. """ @@ -195,7 +195,7 @@ class ValueList: Arguments: layer: The layer to set the value on, an element name of the - ValueLayers namedtuple. + ValueLayers dict. key: The key of the element to set. value: The value to set. """ diff --git a/qutebrowser/config/value.py b/qutebrowser/config/value.py index c8ac59044..95a4dfa4f 100644 --- a/qutebrowser/config/value.py +++ b/qutebrowser/config/value.py @@ -17,9 +17,7 @@ """A single value (with multiple layers possibly) in the config.""" -from collections import namedtuple - -ValueLayers = namedtuple('ValueLayers', 'temp, conf, default') +from collections import OrderedDict class SettingValue: @@ -31,7 +29,7 @@ class SettingValue: Attributes: typ: A BaseType subclass. value: (readonly property) The currently valid, most important value. - _values: A namedtuple with the values on different layers, with the + _values: An OrderedDict with the values on different layers, with the most significant layer first. """ @@ -43,8 +41,9 @@ class SettingValue: default: Raw value to set. """ self.typ = typ() - self._values = ValueLayers(None, None, None) - self._values.default = default + self._values = OrderedDict.fromkeys(['temp', 'conf', 'default']) + self._values['default'] = default + def __str__(self): """Get raw string value.""" @@ -53,7 +52,7 @@ class SettingValue: @property def value(self): """Get the currently valid value.""" - for val in self._values: + for val in self._values.values(): if val is not None: return val else: @@ -74,8 +73,8 @@ class SettingValue: Arguments: layer: The layer to set the value on, an element name of the - ValueLayers namedtuple. + ValueLayers dict. value: The value to set. """ self.typ.validate(value) - setattr(self._values, layer, value) + self._values[layer] = value From e12531e766065c025a7327fdbee5375008b54957 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 11 Apr 2014 19:32:20 +0200 Subject: [PATCH 3/8] Fix dict-change (change attibute to item access) --- qutebrowser/config/config.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qutebrowser/config/config.py b/qutebrowser/config/config.py index ab938784d..87b4236a7 100644 --- a/qutebrowser/config/config.py +++ b/qutebrowser/config/config.py @@ -172,7 +172,8 @@ class Config(QObject): else: lines += wrapper.wrap('Valid values: {}'.format(', '.join( valid_values))) - lines += wrapper.wrap('Default: {}'.format(option.default)) + lines += wrapper.wrap('Default: {}'.format( + option.values['default'])) return lines def _str_items(self, section): From 80ca9a8dd0b63c77dcdc000491278bc1e6077d01 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 11 Apr 2014 19:33:11 +0200 Subject: [PATCH 4/8] Whitespace fix --- qutebrowser/config/value.py | 1 - 1 file changed, 1 deletion(-) diff --git a/qutebrowser/config/value.py b/qutebrowser/config/value.py index 95a4dfa4f..d0c0bcd65 100644 --- a/qutebrowser/config/value.py +++ b/qutebrowser/config/value.py @@ -44,7 +44,6 @@ class SettingValue: self._values = OrderedDict.fromkeys(['temp', 'conf', 'default']) self._values['default'] = default - def __str__(self): """Get raw string value.""" return self.value From a3a81543a0e02e1121d5aa03d3b222b41b84fccf Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 11 Apr 2014 19:34:34 +0200 Subject: [PATCH 5/8] Don't write temp layer to config --- qutebrowser/config/config.py | 3 ++- qutebrowser/config/value.py | 35 ++++++++++++++++++++++++++++++----- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/qutebrowser/config/config.py b/qutebrowser/config/config.py index 87b4236a7..8947fecef 100644 --- a/qutebrowser/config/config.py +++ b/qutebrowser/config/config.py @@ -180,7 +180,8 @@ class Config(QObject): """Get the option items as string for section.""" lines = [] for optname, option in section.items(): - keyval = '{} = {}'.format(optname, option) + keyval = '{} = {}'.format(optname, option.get_first_value( + startlayer='conf')) lines.append(keyval) return lines diff --git a/qutebrowser/config/value.py b/qutebrowser/config/value.py index d0c0bcd65..df3023d50 100644 --- a/qutebrowser/config/value.py +++ b/qutebrowser/config/value.py @@ -51,17 +51,42 @@ class SettingValue: @property def value(self): """Get the currently valid value.""" - for val in self._values.values(): - if val is not None: - return val - else: - raise ValueError("No valid config value found!") + return self.get_first_value() @property def values(self): """Readonly property for _values.""" return self._values + def getlayers(self, startlayer): + """Get a dict of values starting with startlayer. + + Args: + startlayer: The first layer to include. + """ + # FIXME this could be done more efficiently with a view. + idx = list(self._values.keys()).index(startlayer) + d = self._values.copy() + for _ in range(idx): + d.popitem(last=False) + return d + + def get_first_value(self, startlayer=None): + """Get the first valid value starting from startlayer. + + Args: + startlayer: The first layer to include. + """ + if startlayer is None: + d = self._values + else: + d = self.getlayers(startlayer) + for val in d.values(): + if val is not None: + return val + else: + raise ValueError("No valid config value found!") + def transformed(self): """Get the transformed value.""" v = self.value From ab31f2a289f303b1ea7eee8476185025f9686d45 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 11 Apr 2014 19:34:46 +0200 Subject: [PATCH 6/8] Add set_temp command --- qutebrowser/config/config.py | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/qutebrowser/config/config.py b/qutebrowser/config/config.py index 8947fecef..b3db826c4 100644 --- a/qutebrowser/config/config.py +++ b/qutebrowser/config/config.py @@ -274,11 +274,26 @@ class Config(QObject): """ # FIXME completion for values try: - self.set(section, option, value) + self.set('conf', section, option, value) except (NoOptionError, NoSectionError, ValidationError) as e: message.error("set: {} - {}".format(e.__class__.__name__, e)) - def set(self, section, option, value): + @cmdutils.register(name='set_temp', instance='config', + completion=['section', 'option']) + def set_temp_wrapper(self, section, option, value): + """Set a temporary option. + + Wrapper for self.set() to output exceptions in the status bar. + + Arguments: + *args: Get passed to self.set(). + """ + try: + self.set('temp', section, option, value) + except (NoOptionError, NoSectionError, ValidationError) as e: + message.error("set: {} - {}".format(e.__class__.__name__, e)) + + def set(self, layer, section, option, value): """Set an option. Args: @@ -298,11 +313,11 @@ class Config(QObject): value = self._interpolation.before_set(self, section, option, value) try: - sectdict = self.config[section] + sect = self.config[section] except KeyError: raise NoSectionError(section) try: - sectdict[self.optionxform(option)] = value + sect.setv(layer, option, value) except KeyError: raise NoOptionError(option, section) else: @@ -414,7 +429,7 @@ class SectionProxy(MutableMapping): return self._conf.get(self._name, key) def __setitem__(self, key, value): - return self._conf.set(self._name, key, value) + return self._conf.set('conf', self._name, key, value) def __delitem__(self, key): if not (self._conf.has_option(self._name, key) and From 65b99dab45c3aa1e6ab181eb752bc1712b447fa0 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 11 Apr 2014 19:37:49 +0200 Subject: [PATCH 7/8] Fix false-positive check --- qutebrowser/config/value.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qutebrowser/config/value.py b/qutebrowser/config/value.py index df3023d50..bc4d11381 100644 --- a/qutebrowser/config/value.py +++ b/qutebrowser/config/value.py @@ -77,6 +77,7 @@ class SettingValue: Args: startlayer: The first layer to include. """ + # pylint: disable=useless-else-on-loop if startlayer is None: d = self._values else: From 313d6e3967afb0d073f6d0a61ec7fe67fdb194b4 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sun, 13 Apr 2014 21:48:51 +0200 Subject: [PATCH 8/8] Use slicing for getlayers for SettingsValue --- qutebrowser/config/value.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/qutebrowser/config/value.py b/qutebrowser/config/value.py index bc4d11381..544dcd0c3 100644 --- a/qutebrowser/config/value.py +++ b/qutebrowser/config/value.py @@ -64,11 +64,8 @@ class SettingValue: Args: startlayer: The first layer to include. """ - # FIXME this could be done more efficiently with a view. idx = list(self._values.keys()).index(startlayer) - d = self._values.copy() - for _ in range(idx): - d.popitem(last=False) + d = OrderedDict(list(self._values.items())[idx:]) return d def get_first_value(self, startlayer=None):