Skip to content
Permalink
Browse files
Initial config.py support
See #2795
  • Loading branch information
The-Compiler committed Sep 14, 2017
1 parent ed6933a commit cb806ae
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 13 deletions.
@@ -30,6 +30,7 @@ disable=no-self-use,
broad-except,
bare-except,
eval-used,
exec-used,
ungrouped-imports,
suppressed-message,
too-many-return-statements,
@@ -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)
@@ -19,6 +19,7 @@

"""Configuration files residing on disk."""

import types
import os.path
import textwrap
import configparser
@@ -90,6 +91,63 @@ def load(self):
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()
@@ -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):
@@ -22,7 +22,7 @@

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):

0 comments on commit cb806ae

Please sign in to comment.