summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Bruhin <git@the-compiler.org>2017-09-14 16:16:14 +0200
committerFlorian Bruhin <git@the-compiler.org>2017-09-14 17:38:33 +0200
commitcb806aefa3b1a367fb6e79332504466a9e07781f (patch)
treecfc5c3f149c082a4bdc000e549222ba25e17d3d8
parented6933a839f6266d04b89d1e37b0914c7efbdf72 (diff)
downloadqutebrowser-cb806aefa3b1a367fb6e79332504466a9e07781f.tar.gz
qutebrowser-cb806aefa3b1a367fb6e79332504466a9e07781f.zip
Initial config.py support
See #2795
-rw-r--r--.pylintrc1
-rw-r--r--qutebrowser/config/config.py9
-rw-r--r--qutebrowser/config/configfiles.py58
-rw-r--r--tests/unit/config/test_config.py30
-rw-r--r--tests/unit/config/test_configfiles.py83
5 files changed, 168 insertions, 13 deletions
diff --git a/.pylintrc b/.pylintrc
index 04d689ffd..31e91a3d3 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -30,6 +30,7 @@ disable=no-self-use,
broad-except,
bare-except,
eval-used,
+ exec-used,
ungrouped-imports,
suppressed-message,
too-many-return-statements,
diff --git a/qutebrowser/config/config.py b/qutebrowser/config/config.py
index 67e44fa8a..58359a335 100644
--- a/qutebrowser/config/config.py
+++ b/qutebrowser/config/config.py
@@ -625,13 +625,16 @@ def init(parent=None):
val = ConfigContainer(instance)
key_instance = KeyConfig(instance)
+ for cf in _change_filters:
+ cf.validate()
+
configtypes.Font.monospace_fonts = val.fonts.monospace
config_commands = ConfigCommands(instance, key_instance)
objreg.register('config-commands', config_commands)
- for cf in _change_filters:
- cf.validate()
- instance.read_yaml()
+ config_api = configfiles.read_config_py()
+ if getattr(config_api, 'load_autoconfig', True):
+ instance.read_yaml()
configfiles.init(instance)
diff --git a/qutebrowser/config/configfiles.py b/qutebrowser/config/configfiles.py
index 515d9e28d..1ac6668c8 100644
--- a/qutebrowser/config/configfiles.py
+++ b/qutebrowser/config/configfiles.py
@@ -19,6 +19,7 @@
"""Configuration files residing on disk."""
+import types
import os.path
import textwrap
import configparser
@@ -90,6 +91,63 @@ class YamlConfig:
pass
+class ConfigAPI:
+
+ """Object which gets passed to config.py as "config" object.
+
+ This is a small wrapper over the Config object, but with more
+ straightforward method names (get/set call get_obj/set_obj) and a more
+ shallow API.
+
+ Attributes:
+ _config: The main Config object to use.
+ _keyconfig: The KeyConfig object.
+ val: A matching ConfigContainer object.
+ load_autoconfig: Whether autoconfig.yml should be loaded.
+ """
+
+ def __init__(self, config, keyconfig, container):
+ self._config = config
+ self._keyconfig = keyconfig
+ self.val = container
+ self.load_autoconfig = True
+
+ def get(self, name):
+ return self._config.get_obj(name)
+
+ def set(self, name, value):
+ self._config.set_obj(name, value)
+
+ def bind(self, key, command, *, mode, force=False):
+ self._keyconfig.bind(key, command, mode=mode, force=force)
+
+ def unbind(self, key, *, mode):
+ self._keyconfig.unbind(key, mode=mode)
+
+
+def read_config_py(filename=None):
+ """Read a config.py file."""
+ from qutebrowser.config import config
+ # FIXME:conf error handling
+ if filename is None:
+ filename = os.path.join(standarddir.config(), 'config.py')
+ if not os.path.exists(filename):
+ return None
+
+ api = ConfigAPI(config.instance, config.key_instance, config.val)
+ module = types.ModuleType('config')
+ module.config = api
+ module.c = api.val
+ module.__file__ = filename
+
+ with open(filename, mode='rb') as f:
+ source = f.read()
+ code = compile(source, filename, 'exec')
+ exec(code, module.__dict__)
+
+ return api
+
+
def init(config):
"""Initialize config storage not related to the main config."""
state = StateConfig()
diff --git a/tests/unit/config/test_config.py b/tests/unit/config/test_config.py
index 5a67651da..9ddf5d373 100644
--- a/tests/unit/config/test_config.py
+++ b/tests/unit/config/test_config.py
@@ -852,16 +852,24 @@ def init_patch(qapp, fake_save_manager, monkeypatch, config_tmpdir,
monkeypatch.setattr(config, 'key_instance', None)
monkeypatch.setattr(config, '_change_filters', [])
yield
- objreg.delete('config-commands')
- try:
- objreg.delete('state-config')
- except KeyError:
- pass
+ for obj in ['config-commands', 'state-config', 'command-history']:
+ try:
+ objreg.delete(obj)
+ except KeyError:
+ pass
-def test_init(init_patch, fake_save_manager, config_tmpdir):
- (config_tmpdir / 'autoconfig.yml').write_text(
- 'global:\n colors.hints.fg: magenta', 'utf-8', ensure=True)
+@pytest.mark.parametrize('load_autoconfig', [True, False])
+def test_init(init_patch, fake_save_manager, config_tmpdir, load_autoconfig):
+ autoconfig_file = config_tmpdir / 'autoconfig.yml'
+ config_py_file = config_tmpdir / 'config.py'
+
+ autoconfig_file.write_text('global:\n colors.hints.fg: magenta\n',
+ 'utf-8', ensure=True)
+ config_py_lines = ['c.colors.hints.bg = "red"']
+ if not load_autoconfig:
+ config_py_lines.append('config.load_autoconfig = False')
+ config_py_file.write_text('\n'.join(config_py_lines), 'utf-8', ensure=True)
config.init()
@@ -875,7 +883,11 @@ def test_init(init_patch, fake_save_manager, config_tmpdir):
fake_save_manager.add_saveable.assert_any_call(
'yaml-config', unittest.mock.ANY)
- assert config.instance._values['colors.hints.fg'] == 'magenta'
+ assert config.instance._values['colors.hints.bg'] == 'red'
+ if load_autoconfig:
+ assert config.instance._values['colors.hints.fg'] == 'magenta'
+ else:
+ assert 'colors.hints.fg' not in config.instance._values
def test_init_invalid_change_filter(init_patch):
diff --git a/tests/unit/config/test_configfiles.py b/tests/unit/config/test_configfiles.py
index f20d19759..d077e9918 100644
--- a/tests/unit/config/test_configfiles.py
+++ b/tests/unit/config/test_configfiles.py
@@ -22,7 +22,7 @@ import sys
import pytest
-from qutebrowser.config import configfiles
+from qutebrowser.config import config, configfiles
from qutebrowser.utils import objreg
from PyQt5.QtCore import QSettings
@@ -91,6 +91,87 @@ def test_yaml_config(fake_save_manager, config_tmpdir, old_config, insert):
assert ' tabs.show: never' in lines
+class TestConfigPy:
+
+ """Tests for ConfigAPI and read_config_py()."""
+
+ pytestmark = pytest.mark.usefixtures('config_stub', 'key_config_stub')
+
+ class ConfPy:
+
+ """Helper class to get a confpy fixture."""
+
+ def __init__(self, tmpdir):
+ self._confpy = tmpdir / 'config.py'
+ self.filename = str(self._confpy)
+
+ def write(self, *lines):
+ text = '\n'.join(lines)
+ self._confpy.write_text(text, 'utf-8', ensure=True)
+
+ @pytest.fixture
+ def confpy(self, tmpdir):
+ return self.ConfPy(tmpdir)
+
+ @pytest.mark.parametrize('line', [
+ 'c.colors.hints.bg = "red"',
+ 'config.val.colors.hints.bg = "red"',
+ 'config.set("colors.hints.bg", "red")',
+ ])
+ def test_set(self, confpy, line):
+ confpy.write(line)
+ configfiles.read_config_py(confpy.filename)
+ assert config.instance._values['colors.hints.bg'] == 'red'
+
+ @pytest.mark.parametrize('set_first', [True, False])
+ @pytest.mark.parametrize('get_line', [
+ 'c.colors.hints.fg',
+ 'config.get("colors.hints.fg")',
+ ])
+ def test_get(self, confpy, set_first, get_line):
+ """Test whether getting options works correctly.
+
+ We test this by doing the following:
+ - Set colors.hints.fg to some value (inside the config.py with
+ set_first, outside of it otherwise).
+ - In the config.py, read .fg and set .bg to the same value.
+ - Verify that .bg has been set correctly.
+ """
+ # pylint: disable=bad-config-option
+ config.val.colors.hints.fg = 'green'
+ if set_first:
+ confpy.write('c.colors.hints.fg = "red"',
+ 'c.colors.hints.bg = {}'.format(get_line))
+ expected = 'red'
+ else:
+ confpy.write('c.colors.hints.bg = {}'.format(get_line))
+ expected = 'green'
+ configfiles.read_config_py(confpy.filename)
+ assert config.instance._values['colors.hints.bg'] == expected
+
+ def test_bind(self, confpy):
+ confpy.write('config.bind(",a", "message-info foo", mode="normal")')
+ configfiles.read_config_py(confpy.filename)
+ expected = {'normal': {',a': 'message-info foo'}}
+ assert config.instance._values['bindings.commands'] == expected
+
+ def test_unbind(self, confpy):
+ confpy.write('config.unbind("o", mode="normal")')
+ configfiles.read_config_py(confpy.filename)
+ expected = {'normal': {'o': None}}
+ assert config.instance._values['bindings.commands'] == expected
+
+ def test_reading_default_location(self, config_tmpdir):
+ (config_tmpdir / 'config.py').write_text(
+ 'c.colors.hints.bg = "red"', 'utf-8')
+ configfiles.read_config_py()
+ assert config.instance._values['colors.hints.bg'] == 'red'
+
+ def test_reading_missing_default_location(self, config_tmpdir):
+ assert not (config_tmpdir / 'config.py').exists()
+ configfiles.read_config_py() # Should not crash
+
+
@pytest.fixture
def init_patch(qapp, fake_save_manager, config_tmpdir, data_tmpdir,
config_stub):