diff options
author | Florian Bruhin <me@the-compiler.org> | 2021-01-20 13:57:09 +0100 |
---|---|---|
committer | Florian Bruhin <me@the-compiler.org> | 2021-01-20 14:12:39 +0100 |
commit | 2c382a761de5c72071bfc952e11c608016139c1f (patch) | |
tree | a346745e4b16f5c8344ea57ef773872dc3fdf4be /tests | |
parent | 729e098baeabd301e983b8dd50c00293ba757975 (diff) | |
download | qutebrowser-2c382a761de5c72071bfc952e11c608016139c1f.tar.gz qutebrowser-2c382a761de5c72071bfc952e11c608016139c1f.zip |
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
Diffstat (limited to 'tests')
-rw-r--r-- | tests/unit/config/test_config.py | 17 |
1 files changed, 17 insertions, 0 deletions
diff --git a/tests/unit/config/test_config.py b/tests/unit/config/test_config.py index b30ab4bee..03eb61c8b 100644 --- a/tests/unit/config/test_config.py +++ b/tests/unit/config/test_config.py @@ -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, |