qutebrowser/qutebrowser/config/configdata.py

213 lines
6.2 KiB
Python
Raw Normal View History

2014-06-19 09:04:37 +02:00
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
2017-05-09 21:37:03 +02:00
# Copyright 2014-2017 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
2014-02-27 18:46:30 +01:00
#
# This file is part of qutebrowser.
#
# qutebrowser is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# qutebrowser is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
2014-04-17 17:44:27 +02:00
"""Configuration data for config.py.
Module attributes:
2017-07-01 23:09:34 +02:00
DATA: A dict of Option objects after init() has been called.
2014-04-17 17:44:27 +02:00
"""
2014-02-27 18:46:30 +01:00
2017-06-08 17:43:34 +02:00
# FIXME:conf reintroduce interpolation?
2014-09-25 22:41:42 +02:00
import sys
2014-05-01 15:27:32 +02:00
import re
2014-08-26 19:10:14 +02:00
import collections
import functools
2014-02-27 18:46:30 +01:00
2017-06-14 19:33:47 +02:00
from qutebrowser.config import configtypes
2017-06-09 17:21:36 +02:00
from qutebrowser.utils import usertypes, qtutils, utils
DATA = None
2014-05-04 01:06:52 +02:00
2017-06-08 17:43:34 +02:00
# FIXME:conf what to do about this?
2017-07-01 23:09:34 +02:00
DEFAULT_FONT_SIZE = '10pt' if sys.platform == 'darwin' else '8pt'
2017-06-08 17:43:34 +02:00
2017-06-15 14:34:16 +02:00
Option = collections.namedtuple('Option', ['name', 'typ', 'default',
2017-07-02 11:47:31 +02:00
'backends', 'raw_backends',
2017-07-01 22:58:50 +02:00
'description'])
2017-06-09 17:21:36 +02:00
def _raise_invalid_node(name, what, node):
"""Raise an exception for an invalid configdata YAML node.
Args:
name: The name of the setting being parsed.
what: The name of the thing being parsed.
node: The invalid node.
"""
raise ValueError("Invalid node for {} while reading {}: {!r}".format(
name, what, node))
def _parse_yaml_type(name, node):
if isinstance(node, str):
# e.g:
# type: Bool
# -> create the type object without any arguments
type_name = node
kwargs = {}
elif isinstance(node, dict):
# e.g:
# type:
# name: String
# none_ok: true
# -> create the type object and pass arguments
type_name = node.pop('name')
kwargs = node
valid_values = kwargs.get('valid_values', None)
if valid_values is not None:
kwargs['valid_values'] = configtypes.ValidValues(*valid_values)
2017-06-09 17:21:36 +02:00
else:
_raise_invalid_node(name, 'type', node)
try:
typ = getattr(configtypes, type_name)
except AttributeError as e:
raise AttributeError("Did not find type {} for {}".format(
type_name, name))
# Parse sub-types
try:
if typ is configtypes.Dict:
kwargs['keytype'] = _parse_yaml_type(name, kwargs['keytype'])
kwargs['valtype'] = _parse_yaml_type(name, kwargs['valtype'])
elif typ is configtypes.List:
kwargs['valtype'] = _parse_yaml_type(name, kwargs['valtype'])
except KeyError as e:
_raise_invalid_node(name, str(e), node)
try:
return typ(**kwargs)
except TypeError as e:
raise TypeError("Error while creating {} with {}: {}".format(
type_name, node, e))
def _parse_yaml_backends_dict(name, node):
"""Parse a dict definition for backends.
Example:
backends:
QtWebKit: true
QtWebEngine: Qt 5.9
"""
str_to_backend = {
'QtWebKit': usertypes.Backend.QtWebKit,
'QtWebEngine': usertypes.Backend.QtWebEngine,
}
if node.keys() != str_to_backend.keys():
_raise_invalid_node(name, 'backends', node)
backends = []
2017-06-14 19:33:47 +02:00
# The value associated to the key, and whether we should add that backend
# or not.
2017-06-09 17:21:36 +02:00
conditionals = {
True: True,
False: False,
'Qt 5.8': qtutils.version_check('5.8'),
'Qt 5.9': qtutils.version_check('5.9'),
}
for key in node.keys():
if conditionals[node[key]]:
backends.append(str_to_backend[key])
return backends
def _parse_yaml_backends(name, node):
"""Parse a backend node in the yaml.
It can have one of those four forms:
- Not present -> setting applies to both backends.
- backend: QtWebKit -> setting only available with QtWebKit
- backend: QtWebEngine -> setting only available with QtWebEngine
- backend:
QtWebKit: true
QtWebEngine: Qt 5.9
-> setting available based on the given conditionals.
2017-07-01 22:58:50 +02:00
Return:
2017-07-02 11:47:31 +02:00
A list of backends.
2017-06-09 17:21:36 +02:00
"""
if node is None:
2017-07-02 11:47:31 +02:00
return [usertypes.Backend.QtWebKit, usertypes.Backend.QtWebEngine]
2017-06-09 17:21:36 +02:00
elif node == 'QtWebKit':
2017-07-02 11:47:31 +02:00
return [usertypes.Backend.QtWebKit]
2017-06-09 17:21:36 +02:00
elif node == 'QtWebEngine':
2017-07-02 11:47:31 +02:00
return [usertypes.Backend.QtWebEngine]
2017-06-09 17:21:36 +02:00
elif isinstance(node, dict):
2017-07-02 11:47:31 +02:00
return _parse_yaml_backends_dict(name, node)
2017-06-09 17:21:36 +02:00
_raise_invalid_node(name, 'backends', node)
def _read_yaml(yaml_data):
"""Read config data from a YAML file.
Args:
yaml_data: The YAML string to parse.
Return:
A dict mapping option names to Option elements.
"""
parsed = {}
2017-06-13 13:05:24 +02:00
data = utils.yaml_load(yaml_data)
2017-06-09 17:21:36 +02:00
keys = {'type', 'default', 'desc', 'backend'}
for name, option in data.items():
if not set(option.keys()).issubset(keys):
raise ValueError("Invalid keys {} for {}".format(
option.keys(), name))
2017-07-02 11:47:31 +02:00
backends = option.get('backend', None)
2017-07-01 22:58:50 +02:00
2017-06-09 17:21:36 +02:00
parsed[name] = Option(
2017-06-15 14:34:16 +02:00
name=name,
2017-06-09 17:21:36 +02:00
typ=_parse_yaml_type(name, option['type']),
default=option['default'],
2017-07-02 11:47:31 +02:00
backends=_parse_yaml_backends(name, backends),
raw_backends=backends if isinstance(backends, dict) else None,
2017-06-09 17:21:36 +02:00
description=option['desc'])
# Make sure no key shadows another.
for key1 in parsed:
for key2 in parsed:
if key2.startswith(key1 + '.'):
raise ValueError("Shadowing keys {} and {}".format(key1, key2))
2017-06-09 17:21:36 +02:00
return parsed
@functools.lru_cache(maxsize=256)
2017-06-13 12:07:00 +02:00
def is_valid_prefix(prefix):
"""Check whether the given prefix is a valid prefix for some option."""
return any(key.startswith(prefix + '.') for key in DATA)
2017-06-09 17:21:36 +02:00
def init():
"""Initialize configdata from the YAML file."""
global DATA
DATA = _read_yaml(utils.read_file('config/configdata.yml'))