config: Improve error handling for missing sections
When the user does something like this with the new adblocking config:
c.content.host_blocking.lists = ...
Then we didn't notify her about the setting being renamed. Instead, she
got:
While getting 'content.host_blocking': No option 'content.host_blocking'
Unhandled exception: 'NoneType' object has no attribute 'lists'
Traceback (most recent call last):
File ".../qutebrowser/config/configfiles.py", line 805, in read_config_py
exec(code, module.__dict__)
File "/tmp/config.py", line 1, in <module>
c.content.host_blocking.lists = []
AttributeError: 'NoneType' object has no attribute 'lists'
This happens because we did something like (simplified):
with self._handle_error(...):
return self._config.get(...)
# End of method
Thus, if there was an error, nothing is returned and the method ends,
therefore returning an implicit None. When then trying to access .lists
on that None, we get the AttributeError.
Instead, we now permit this kind of wrong usage in config.py files.
If this is a qutebrowser-internal ConfigContainer, we would've already
raised in _handle_error() anyways.
What we now do instead is returning a new ConfigContainer, i.e.
allowing further access (while still capturing the error message).
Thus, this now leads to:
While getting 'content.host_blocking':
No option 'content.host_blocking'
While setting 'content.host_blocking.lists':
No option 'content.host_blocking.lists'
(this option was renamed to 'content.blocking.hosts.lists')
This still isn't optimal, but the best we can do without more magic:
At the point the first failure happens, we can't tell whether the user
wants to get an option or is just getting a prefix.
Fixes #5991
This commit is contained in:
parent
729e098bae
commit
2c382a761d
|
|
@ -603,6 +603,13 @@ class ConfigContainer:
|
|||
return self._config.get_mutable_obj(
|
||||
name, pattern=self._pattern)
|
||||
|
||||
# If we arrived here, there was an error while getting the config option. Most
|
||||
# likely, someone did something like "c.content.host_blocking.lists" but
|
||||
# "c.content.host_blocking" doesn't actually exist. To avoid an AttributeError
|
||||
# which leads to a confusing error message, return another ConfigContainer so
|
||||
# that the chain can keep going.
|
||||
return self._with_prefix(name)
|
||||
|
||||
def __setattr__(self, attr: str, value: Any) -> None:
|
||||
"""Set the given option in the config."""
|
||||
if attr.startswith('_'):
|
||||
|
|
|
|||
|
|
@ -761,6 +761,23 @@ class TestContainer:
|
|||
assert error.text == "While getting 'tabs.foobar'"
|
||||
assert str(error.exception) == "No option 'tabs.foobar'"
|
||||
|
||||
def test_confapi_missing_prefix(self, container):
|
||||
configapi = types.SimpleNamespace(errors=[])
|
||||
container._configapi = configapi
|
||||
container.content.host_blocking.lists = []
|
||||
|
||||
assert len(configapi.errors) == 2
|
||||
|
||||
error1 = configapi.errors[0]
|
||||
assert error1.text == "While getting 'content.host_blocking'"
|
||||
assert str(error1.exception) == "No option 'content.host_blocking'"
|
||||
|
||||
error2 = configapi.errors[1]
|
||||
assert error2.text == "While setting 'content.host_blocking.lists'"
|
||||
assert str(error2.exception) == (
|
||||
"No option 'content.host_blocking.lists' (this option was renamed to "
|
||||
"'content.blocking.hosts.lists')")
|
||||
|
||||
def test_pattern_no_configapi(self, config_stub):
|
||||
pattern = urlmatch.UrlPattern('https://example.com/')
|
||||
with pytest.raises(TypeError,
|
||||
|
|
|
|||
Loading…
Reference in New Issue