Initial configexc refactoring

This commit is contained in:
Florian Bruhin 2017-06-13 12:20:44 +02:00
parent 5ab2c89a37
commit 3e3685b68b
9 changed files with 106 additions and 130 deletions

View File

@ -1541,6 +1541,7 @@ class CommandDispatcher:
command))
path = 'commands.html#{}'.format(command)
elif '->' in topic:
# FIXME:conf refactor
parts = topic.split('->')
if len(parts) != 2:
raise cmdexc.CommandError("Invalid help topic {}!".format(

View File

@ -110,8 +110,8 @@ class CommandRunner(QObject):
return default
parts = text.strip().split(maxsplit=1)
try:
alias = config.get('aliases', parts[0])
except (configexc.NoOptionError, configexc.NoSectionError):
alias = config.val.aliases[parts[0]]
except KeyError:
return default
try:
new_cmd = '{} {}'.format(alias, parts[1])

View File

@ -147,16 +147,15 @@ class CompletionItemDelegate(QStyledItemDelegate):
# We can't use drawContents because then the color would be ignored.
clip = QRectF(0, 0, rect.width(), rect.height())
self._painter.save()
if self._opt.state & QStyle.State_Selected:
option = 'completion.item.selected.fg'
color = config.val.completion.item.selected.fg
elif not self._opt.state & QStyle.State_Enabled:
option = 'completion.category.fg'
color = config.val.completion.category.fg
else:
option = 'completion.fg'
try:
self._painter.setPen(config.get('colors', option))
except configexc.NoOptionError:
self._painter.setPen(config.val.colors.completion.fg)
color = config.val.completion.fg
self._painter.setPen(color)
ctx = QAbstractTextDocumentLayout.PaintContext()
ctx.palette.setColor(QPalette.Text, self._painter.pen().color())
if clip.isValid():

View File

@ -46,6 +46,9 @@ from qutebrowser.utils import (message, objreg, utils, standarddir, log,
from qutebrowser.misc import objects
from qutebrowser.utils.usertypes import Completion
# FIXME:conf compat
from qutebrowser.config.newconfig import change_filter
UNSET = object()
@ -54,74 +57,6 @@ UNSET = object()
val = None
class change_filter: # pylint: disable=invalid-name
"""Decorator to filter calls based on a config section/option matching.
This could also be a function, but as a class (with a "wrong" name) it's
much cleaner to implement.
Attributes:
_option: An option or prefix to be filtered
_function: Whether a function rather than a method is decorated.
"""
def __init__(self, option, function=False):
"""Save decorator arguments.
Gets called on parse-time with the decorator arguments.
Args:
option: The option to be filtered.
function: Whether a function rather than a method is decorated.
"""
if (option not in configdata.DATA and
not configdata.is_valid_prefix(option)):
raise configexc.NoOptionError(option)
self._option = option
self._function = function
def _check_match(self, option):
"""Check if the given option matches the filter."""
if option is None:
# Called directly, not from a config change event.
return True
elif option == self._option:
return True
elif option.startswith(self._option + '.'):
# prefix match
return True
else:
return False
def __call__(self, func):
"""Filter calls to the decorated function.
Gets called when a function should be decorated.
Adds a filter which returns if we're not interested in the change-event
and calls the wrapped function if we are.
We assume the function passed doesn't take any parameters.
Args:
func: The function to be decorated.
Return:
The decorated function.
"""
if self._function:
@functools.wraps(func)
def wrapper(option=None):
if self._check_match(option):
return func()
else:
@functools.wraps(func)
def wrapper(wrapper_self, option=None):
if self._check_match(option):
return func(wrapper_self)
return wrapper
def get(*args, **kwargs):

View File

@ -52,35 +52,10 @@ class ValidationError(Error):
self.option = None
class NoSectionError(Error):
"""Raised when no section matches a requested option."""
def __init__(self, section):
super().__init__("Section {!r} does not exist!".format(section))
self.section = section
class NoOptionError(Error):
"""Raised when an option was not found."""
def __init__(self, option, section):
super().__init__("No option {!r} in section {!r}".format(
option, section))
def __init__(self, option):
super().__init__("No option {!r}".format(option))
self.option = option
self.section = section
class InterpolationSyntaxError(Error):
"""Raised when the source text contains invalid syntax.
Current implementation raises this exception when the source text into
which substitutions are made does not conform to the required syntax.
"""
def __init__(self, option, section, msg):
super().__init__(msg)
self.option = option
self.section = section

View File

@ -19,20 +19,17 @@
"""New qutebrowser configuration code."""
import functools
from PyQt5.QtCore import pyqtSignal, QObject
from qutebrowser.config import configdata
from qutebrowser.config import configdata, configexc
from qutebrowser.utils import utils, objreg
# An easy way to access the config from other code via config.val.foo
val = None
class UnknownOptionError(Exception):
"""Raised by NewConfigManager when an option is unknown."""
_change_filters = []
class SectionStub:
@ -47,6 +44,83 @@ class SectionStub:
return self._conf.get(self._name, item)
class change_filter: # pylint: disable=invalid-name
"""Decorator to filter calls based on a config section/option matching.
This could also be a function, but as a class (with a "wrong" name) it's
much cleaner to implement.
Attributes:
_option: An option or prefix to be filtered
_function: Whether a function rather than a method is decorated.
"""
def __init__(self, option, function=False):
"""Save decorator arguments.
Gets called on parse-time with the decorator arguments.
Args:
option: The option to be filtered.
function: Whether a function rather than a method is decorated.
"""
self._option = option
self._function = function
_change_filters.append(self)
def validate(self):
"""Make sure the configured option or prefix exists.
We can't do this in __init__ as configdata isn't ready yet.
"""
if (self._option not in configdata.DATA and
not configdata.is_valid_prefix(self._option)):
raise configexc.NoOptionError(self._option)
def _check_match(self, option):
"""Check if the given option matches the filter."""
if option is None:
# Called directly, not from a config change event.
return True
elif option == self._option:
return True
elif option.startswith(self._option + '.'):
# prefix match
return True
else:
return False
def __call__(self, func):
"""Filter calls to the decorated function.
Gets called when a function should be decorated.
Adds a filter which returns if we're not interested in the change-event
and calls the wrapped function if we are.
We assume the function passed doesn't take any parameters.
Args:
func: The function to be decorated.
Return:
The decorated function.
"""
if self._function:
@functools.wraps(func)
def wrapper(option=None):
if self._check_match(option):
return func()
else:
@functools.wraps(func)
def wrapper(wrapper_self, option=None):
if self._check_match(option):
return func(wrapper_self)
return wrapper
class NewConfigManager(QObject):
# FIXME:conf QObject?
@ -65,7 +139,7 @@ class NewConfigManager(QObject):
try:
val = self._values[option]
except KeyError as e:
raise UnknownOptionError(e)
raise configexc.NoOptionError(e)
return val.typ.from_py(val.default)
@ -117,5 +191,9 @@ def init(parent):
new_config = NewConfigManager(parent)
new_config.read_defaults()
objreg.register('config', new_config)
global val
val = ConfigContainer(new_config)
for cf in _change_filters:
cf.validate()

View File

@ -29,7 +29,7 @@ import urllib.parse
from PyQt5.QtCore import QUrl
from PyQt5.QtNetwork import QHostInfo, QHostAddress, QNetworkProxy
from qutebrowser.config import config, configexc
from qutebrowser.config import config
from qutebrowser.utils import log, qtutils, message, utils
from qutebrowser.commands import cmdexc
from qutebrowser.browser.network import pac
@ -70,8 +70,8 @@ def _parse_search_term(s):
if len(split) == 2:
engine = split[0]
try:
config.get('searchengines', engine)
except configexc.NoOptionError:
config.val.searchengines[engine]
except KeyError:
engine = None
term = s
else:

View File

@ -419,6 +419,8 @@ class ConfigStub(QObject):
data: The config data to return.
"""
# FIXME:conf refactor...
changed = pyqtSignal(str, str)
def __init__(self, parent=None):

View File

@ -29,24 +29,10 @@ def test_validation_error():
assert str(e) == "Invalid value 'val' - msg"
def test_no_section_error():
e = configexc.NoSectionError('sect')
assert e.section == 'sect'
assert str(e) == "Section 'sect' does not exist!"
def test_no_option_error():
e = configexc.NoOptionError('opt', 'sect')
assert e.section == 'sect'
e = configexc.NoOptionError('opt')
assert e.option == 'opt'
assert str(e) == "No option 'opt' in section 'sect'"
def test_interpolation_syntax_error():
e = configexc.InterpolationSyntaxError('opt', 'sect', 'msg')
assert e.section == 'sect'
assert e.option == 'opt'
assert str(e) == 'msg'
assert str(e) == "No option 'opt'"
def test_backend_error():