Revert "Add types for most of qutebrowser.config"
It breaks 'tsh' because of the *values annotation.
This reverts commit 208d3db475
.
This commit is contained in:
parent
208d3db475
commit
7494d238ce
36
mypy.ini
36
mypy.ini
@ -61,39 +61,3 @@ disallow_incomplete_defs = True
|
|||||||
[mypy-qutebrowser.commands.cmdutils]
|
[mypy-qutebrowser.commands.cmdutils]
|
||||||
disallow_untyped_defs = True
|
disallow_untyped_defs = True
|
||||||
disallow_incomplete_defs = True
|
disallow_incomplete_defs = True
|
||||||
|
|
||||||
[mypy-qutebrowser.config.config]
|
|
||||||
disallow_untyped_defs = True
|
|
||||||
disallow_incomplete_defs = True
|
|
||||||
|
|
||||||
[mypy-qutebrowser.config.configcache]
|
|
||||||
disallow_untyped_defs = True
|
|
||||||
disallow_incomplete_defs = True
|
|
||||||
|
|
||||||
[mypy-qutebrowser.config.configcommands]
|
|
||||||
disallow_untyped_defs = True
|
|
||||||
disallow_incomplete_defs = True
|
|
||||||
|
|
||||||
[mypy-qutebrowser.config.configdata]
|
|
||||||
disallow_untyped_defs = True
|
|
||||||
disallow_incomplete_defs = True
|
|
||||||
|
|
||||||
[mypy-qutebrowser.config.configdiff]
|
|
||||||
disallow_untyped_defs = True
|
|
||||||
disallow_incomplete_defs = True
|
|
||||||
|
|
||||||
[mypy-qutebrowser.config.configexc]
|
|
||||||
disallow_untyped_defs = True
|
|
||||||
disallow_incomplete_defs = True
|
|
||||||
|
|
||||||
[mypy-qutebrowser.config.configfiles]
|
|
||||||
disallow_untyped_defs = True
|
|
||||||
disallow_incomplete_defs = True
|
|
||||||
|
|
||||||
[mypy-qutebrowser.config.configinit]
|
|
||||||
disallow_untyped_defs = True
|
|
||||||
disallow_incomplete_defs = True
|
|
||||||
|
|
||||||
[mypy-qutebrowser.config.configutils]
|
|
||||||
disallow_untyped_defs = True
|
|
||||||
disallow_incomplete_defs = True
|
|
||||||
|
@ -23,21 +23,18 @@ import copy
|
|||||||
import contextlib
|
import contextlib
|
||||||
import functools
|
import functools
|
||||||
import typing
|
import typing
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QUrl
|
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject
|
||||||
|
|
||||||
from qutebrowser.config import configdata, configexc, configutils
|
from qutebrowser.config import configdata, configexc, configutils
|
||||||
from qutebrowser.utils import utils, log, jinja, urlmatch
|
from qutebrowser.utils import utils, log, jinja
|
||||||
from qutebrowser.misc import objects
|
from qutebrowser.misc import objects
|
||||||
from qutebrowser.keyinput import keyutils
|
from qutebrowser.keyinput import keyutils
|
||||||
|
|
||||||
MYPY = False
|
MYPY = False
|
||||||
if MYPY: # pragma: no cover
|
if MYPY:
|
||||||
# pylint: disable=unused-import,useless-suppression
|
# pylint: disable=unused-import
|
||||||
from typing import Tuple, MutableMapping
|
from qutebrowser.config import configcache # pragma: no cover
|
||||||
from qutebrowser.config import configcache, configfiles
|
|
||||||
from qutebrowser.misc import savemanager
|
|
||||||
|
|
||||||
# An easy way to access the config from other code via config.val.foo
|
# An easy way to access the config from other code via config.val.foo
|
||||||
val = typing.cast('ConfigContainer', None)
|
val = typing.cast('ConfigContainer', None)
|
||||||
@ -64,7 +61,7 @@ class change_filter: # noqa: N801,N806 pylint: disable=invalid-name
|
|||||||
_function: Whether a function rather than a method is decorated.
|
_function: Whether a function rather than a method is decorated.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, option: str, function: bool = False) -> None:
|
def __init__(self, option, function=False):
|
||||||
"""Save decorator arguments.
|
"""Save decorator arguments.
|
||||||
|
|
||||||
Gets called on parse-time with the decorator arguments.
|
Gets called on parse-time with the decorator arguments.
|
||||||
@ -77,7 +74,7 @@ class change_filter: # noqa: N801,N806 pylint: disable=invalid-name
|
|||||||
self._function = function
|
self._function = function
|
||||||
change_filters.append(self)
|
change_filters.append(self)
|
||||||
|
|
||||||
def validate(self) -> None:
|
def validate(self):
|
||||||
"""Make sure the configured option or prefix exists.
|
"""Make sure the configured option or prefix exists.
|
||||||
|
|
||||||
We can't do this in __init__ as configdata isn't ready yet.
|
We can't do this in __init__ as configdata isn't ready yet.
|
||||||
@ -86,7 +83,7 @@ class change_filter: # noqa: N801,N806 pylint: disable=invalid-name
|
|||||||
not configdata.is_valid_prefix(self._option)):
|
not configdata.is_valid_prefix(self._option)):
|
||||||
raise configexc.NoOptionError(self._option)
|
raise configexc.NoOptionError(self._option)
|
||||||
|
|
||||||
def _check_match(self, option: typing.Optional[str]) -> bool:
|
def _check_match(self, option):
|
||||||
"""Check if the given option matches the filter."""
|
"""Check if the given option matches the filter."""
|
||||||
if option is None:
|
if option is None:
|
||||||
# Called directly, not from a config change event.
|
# Called directly, not from a config change event.
|
||||||
@ -99,7 +96,7 @@ class change_filter: # noqa: N801,N806 pylint: disable=invalid-name
|
|||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def __call__(self, func: typing.Callable) -> typing.Callable:
|
def __call__(self, func):
|
||||||
"""Filter calls to the decorated function.
|
"""Filter calls to the decorated function.
|
||||||
|
|
||||||
Gets called when a function should be decorated.
|
Gets called when a function should be decorated.
|
||||||
@ -117,21 +114,20 @@ class change_filter: # noqa: N801,N806 pylint: disable=invalid-name
|
|||||||
"""
|
"""
|
||||||
if self._function:
|
if self._function:
|
||||||
@functools.wraps(func)
|
@functools.wraps(func)
|
||||||
def func_wrapper(option: str = None) -> typing.Any:
|
def wrapper(option=None):
|
||||||
"""Call the underlying function."""
|
"""Call the underlying function."""
|
||||||
if self._check_match(option):
|
if self._check_match(option):
|
||||||
return func()
|
return func()
|
||||||
return None
|
return None
|
||||||
return func_wrapper
|
|
||||||
else:
|
else:
|
||||||
@functools.wraps(func)
|
@functools.wraps(func)
|
||||||
def meth_wrapper(wrapper_self: typing.Any,
|
def wrapper(wrapper_self, option=None):
|
||||||
option: str = None) -> typing.Any:
|
|
||||||
"""Call the underlying function."""
|
"""Call the underlying function."""
|
||||||
if self._check_match(option):
|
if self._check_match(option):
|
||||||
return func(wrapper_self)
|
return func(wrapper_self)
|
||||||
return None
|
return None
|
||||||
return meth_wrapper
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
class KeyConfig:
|
class KeyConfig:
|
||||||
@ -144,22 +140,17 @@ class KeyConfig:
|
|||||||
_config: The Config object to be used.
|
_config: The Config object to be used.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_ReverseBindings = typing.Dict[str, typing.MutableSequence[str]]
|
def __init__(self, config):
|
||||||
|
|
||||||
def __init__(self, config: 'Config') -> None:
|
|
||||||
self._config = config
|
self._config = config
|
||||||
|
|
||||||
def _validate(self, key: keyutils.KeySequence, mode: str) -> None:
|
def _validate(self, key, mode):
|
||||||
"""Validate the given key and mode."""
|
"""Validate the given key and mode."""
|
||||||
# Catch old usage of this code
|
# Catch old usage of this code
|
||||||
assert isinstance(key, keyutils.KeySequence), key
|
assert isinstance(key, keyutils.KeySequence), key
|
||||||
if mode not in configdata.DATA['bindings.default'].default:
|
if mode not in configdata.DATA['bindings.default'].default:
|
||||||
raise configexc.KeybindingError("Invalid mode {}!".format(mode))
|
raise configexc.KeybindingError("Invalid mode {}!".format(mode))
|
||||||
|
|
||||||
def get_bindings_for(
|
def get_bindings_for(self, mode):
|
||||||
self,
|
|
||||||
mode: str
|
|
||||||
) -> typing.Dict[keyutils.KeySequence, str]:
|
|
||||||
"""Get the combined bindings for the given mode."""
|
"""Get the combined bindings for the given mode."""
|
||||||
bindings = dict(val.bindings.default[mode])
|
bindings = dict(val.bindings.default[mode])
|
||||||
for key, binding in val.bindings.commands[mode].items():
|
for key, binding in val.bindings.commands[mode].items():
|
||||||
@ -169,9 +160,9 @@ class KeyConfig:
|
|||||||
bindings[key] = binding
|
bindings[key] = binding
|
||||||
return bindings
|
return bindings
|
||||||
|
|
||||||
def get_reverse_bindings_for(self, mode: str) -> '_ReverseBindings':
|
def get_reverse_bindings_for(self, mode):
|
||||||
"""Get a dict of commands to a list of bindings for the mode."""
|
"""Get a dict of commands to a list of bindings for the mode."""
|
||||||
cmd_to_keys = {} # type: KeyConfig._ReverseBindings
|
cmd_to_keys = {}
|
||||||
bindings = self.get_bindings_for(mode)
|
bindings = self.get_bindings_for(mode)
|
||||||
for seq, full_cmd in sorted(bindings.items()):
|
for seq, full_cmd in sorted(bindings.items()):
|
||||||
for cmd in full_cmd.split(';;'):
|
for cmd in full_cmd.split(';;'):
|
||||||
@ -184,10 +175,7 @@ class KeyConfig:
|
|||||||
cmd_to_keys[cmd].insert(0, str(seq))
|
cmd_to_keys[cmd].insert(0, str(seq))
|
||||||
return cmd_to_keys
|
return cmd_to_keys
|
||||||
|
|
||||||
def get_command(self,
|
def get_command(self, key, mode, default=False):
|
||||||
key: keyutils.KeySequence,
|
|
||||||
mode: str,
|
|
||||||
default: bool = False) -> str:
|
|
||||||
"""Get the command for a given key (or None)."""
|
"""Get the command for a given key (or None)."""
|
||||||
self._validate(key, mode)
|
self._validate(key, mode)
|
||||||
if default:
|
if default:
|
||||||
@ -196,11 +184,7 @@ class KeyConfig:
|
|||||||
bindings = self.get_bindings_for(mode)
|
bindings = self.get_bindings_for(mode)
|
||||||
return bindings.get(key, None)
|
return bindings.get(key, None)
|
||||||
|
|
||||||
def bind(self,
|
def bind(self, key, command, *, mode, save_yaml=False):
|
||||||
key: keyutils.KeySequence,
|
|
||||||
command: str, *,
|
|
||||||
mode: str,
|
|
||||||
save_yaml: bool = False) -> None:
|
|
||||||
"""Add a new binding from key to command."""
|
"""Add a new binding from key to command."""
|
||||||
if command is not None and not command.strip():
|
if command is not None and not command.strip():
|
||||||
raise configexc.KeybindingError(
|
raise configexc.KeybindingError(
|
||||||
@ -208,8 +192,8 @@ class KeyConfig:
|
|||||||
'mode'.format(key, mode))
|
'mode'.format(key, mode))
|
||||||
|
|
||||||
self._validate(key, mode)
|
self._validate(key, mode)
|
||||||
log.keyboard.vdebug( # type: ignore
|
log.keyboard.vdebug("Adding binding {} -> {} in mode {}.".format(
|
||||||
"Adding binding {} -> {} in mode {}.".format(key, command, mode))
|
key, command, mode))
|
||||||
|
|
||||||
bindings = self._config.get_mutable_obj('bindings.commands')
|
bindings = self._config.get_mutable_obj('bindings.commands')
|
||||||
if mode not in bindings:
|
if mode not in bindings:
|
||||||
@ -217,10 +201,7 @@ class KeyConfig:
|
|||||||
bindings[mode][str(key)] = command
|
bindings[mode][str(key)] = command
|
||||||
self._config.update_mutables(save_yaml=save_yaml)
|
self._config.update_mutables(save_yaml=save_yaml)
|
||||||
|
|
||||||
def bind_default(self,
|
def bind_default(self, key, *, mode='normal', save_yaml=False):
|
||||||
key: keyutils.KeySequence, *,
|
|
||||||
mode: str = 'normal',
|
|
||||||
save_yaml: bool = False) -> None:
|
|
||||||
"""Restore a default keybinding."""
|
"""Restore a default keybinding."""
|
||||||
self._validate(key, mode)
|
self._validate(key, mode)
|
||||||
|
|
||||||
@ -232,10 +213,7 @@ class KeyConfig:
|
|||||||
"Can't find binding '{}' in {} mode".format(key, mode))
|
"Can't find binding '{}' in {} mode".format(key, mode))
|
||||||
self._config.update_mutables(save_yaml=save_yaml)
|
self._config.update_mutables(save_yaml=save_yaml)
|
||||||
|
|
||||||
def unbind(self,
|
def unbind(self, key, *, mode='normal', save_yaml=False):
|
||||||
key: keyutils.KeySequence, *,
|
|
||||||
mode: str = 'normal',
|
|
||||||
save_yaml: bool = False) -> None:
|
|
||||||
"""Unbind the given key in the given mode."""
|
"""Unbind the given key in the given mode."""
|
||||||
self._validate(key, mode)
|
self._validate(key, mode)
|
||||||
|
|
||||||
@ -276,27 +254,24 @@ class Config(QObject):
|
|||||||
MUTABLE_TYPES = (dict, list)
|
MUTABLE_TYPES = (dict, list)
|
||||||
changed = pyqtSignal(str)
|
changed = pyqtSignal(str)
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self, yaml_config, parent=None):
|
||||||
yaml_config: 'configfiles.YamlConfig',
|
|
||||||
parent: QObject = None) -> None:
|
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.changed.connect(_render_stylesheet.cache_clear)
|
self.changed.connect(_render_stylesheet.cache_clear)
|
||||||
self._mutables = {} # type: MutableMapping[str, Tuple[Any, Any]]
|
self._mutables = {}
|
||||||
self._yaml = yaml_config
|
self._yaml = yaml_config
|
||||||
self._init_values()
|
self._init_values()
|
||||||
|
|
||||||
def _init_values(self) -> None:
|
def _init_values(self):
|
||||||
"""Populate the self._values dict."""
|
"""Populate the self._values dict."""
|
||||||
self._values = {} # type: typing.Mapping
|
self._values = {}
|
||||||
for name, opt in configdata.DATA.items():
|
for name, opt in configdata.DATA.items():
|
||||||
self._values[name] = configutils.Values(opt)
|
self._values[name] = configutils.Values(opt)
|
||||||
|
|
||||||
def __iter__(self) -> typing.Iterator[configutils.Values]:
|
def __iter__(self):
|
||||||
"""Iterate over configutils.Values items."""
|
"""Iterate over configutils.Values items."""
|
||||||
yield from self._values.values()
|
yield from self._values.values()
|
||||||
|
|
||||||
def init_save_manager(self,
|
def init_save_manager(self, save_manager):
|
||||||
save_manager: 'savemanager.SaveManager') -> None:
|
|
||||||
"""Make sure the config gets saved properly.
|
"""Make sure the config gets saved properly.
|
||||||
|
|
||||||
We do this outside of __init__ because the config gets created before
|
We do this outside of __init__ because the config gets created before
|
||||||
@ -304,10 +279,7 @@ class Config(QObject):
|
|||||||
"""
|
"""
|
||||||
self._yaml.init_save_manager(save_manager)
|
self._yaml.init_save_manager(save_manager)
|
||||||
|
|
||||||
def _set_value(self,
|
def _set_value(self, opt, value, pattern=None):
|
||||||
opt: 'configdata.Option',
|
|
||||||
value: Any,
|
|
||||||
pattern: urlmatch.UrlPattern = None) -> None:
|
|
||||||
"""Set the given option to the given value."""
|
"""Set the given option to the given value."""
|
||||||
if not isinstance(objects.backend, objects.NoBackend):
|
if not isinstance(objects.backend, objects.NoBackend):
|
||||||
if objects.backend not in opt.backends:
|
if objects.backend not in opt.backends:
|
||||||
@ -322,12 +294,12 @@ class Config(QObject):
|
|||||||
log.config.debug("Config option changed: {} = {}".format(
|
log.config.debug("Config option changed: {} = {}".format(
|
||||||
opt.name, value))
|
opt.name, value))
|
||||||
|
|
||||||
def _check_yaml(self, opt: 'configdata.Option', save_yaml: bool) -> None:
|
def _check_yaml(self, opt, save_yaml):
|
||||||
"""Make sure the given option may be set in autoconfig.yml."""
|
"""Make sure the given option may be set in autoconfig.yml."""
|
||||||
if save_yaml and opt.no_autoconfig:
|
if save_yaml and opt.no_autoconfig:
|
||||||
raise configexc.NoAutoconfigError(opt.name)
|
raise configexc.NoAutoconfigError(opt.name)
|
||||||
|
|
||||||
def read_yaml(self) -> None:
|
def read_yaml(self):
|
||||||
"""Read the YAML settings from self._yaml."""
|
"""Read the YAML settings from self._yaml."""
|
||||||
self._yaml.load()
|
self._yaml.load()
|
||||||
for values in self._yaml:
|
for values in self._yaml:
|
||||||
@ -335,7 +307,7 @@ class Config(QObject):
|
|||||||
self._set_value(values.opt, scoped.value,
|
self._set_value(values.opt, scoped.value,
|
||||||
pattern=scoped.pattern)
|
pattern=scoped.pattern)
|
||||||
|
|
||||||
def get_opt(self, name: str) -> 'configdata.Option':
|
def get_opt(self, name):
|
||||||
"""Get a configdata.Option object for the given setting."""
|
"""Get a configdata.Option object for the given setting."""
|
||||||
try:
|
try:
|
||||||
return configdata.DATA[name]
|
return configdata.DATA[name]
|
||||||
@ -346,10 +318,7 @@ class Config(QObject):
|
|||||||
name, deleted=deleted, renamed=renamed)
|
name, deleted=deleted, renamed=renamed)
|
||||||
raise exception from None
|
raise exception from None
|
||||||
|
|
||||||
def get(self,
|
def get(self, name, url=None, *, fallback=True):
|
||||||
name: str,
|
|
||||||
url: QUrl = None, *,
|
|
||||||
fallback: bool = True) -> Any:
|
|
||||||
"""Get the given setting converted for Python code.
|
"""Get the given setting converted for Python code.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -359,7 +328,7 @@ class Config(QObject):
|
|||||||
obj = self.get_obj(name, url=url, fallback=fallback)
|
obj = self.get_obj(name, url=url, fallback=fallback)
|
||||||
return opt.typ.to_py(obj)
|
return opt.typ.to_py(obj)
|
||||||
|
|
||||||
def _maybe_copy(self, value: Any) -> Any:
|
def _maybe_copy(self, value):
|
||||||
"""Copy the value if it could potentially be mutated."""
|
"""Copy the value if it could potentially be mutated."""
|
||||||
if isinstance(value, self.MUTABLE_TYPES):
|
if isinstance(value, self.MUTABLE_TYPES):
|
||||||
# For mutable objects, create a copy so we don't accidentally
|
# For mutable objects, create a copy so we don't accidentally
|
||||||
@ -370,10 +339,7 @@ class Config(QObject):
|
|||||||
assert value.__hash__ is not None, value
|
assert value.__hash__ is not None, value
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def get_obj(self,
|
def get_obj(self, name, *, url=None, fallback=True):
|
||||||
name: str, *,
|
|
||||||
url: QUrl = None,
|
|
||||||
fallback: bool = True) -> Any:
|
|
||||||
"""Get the given setting as object (for YAML/config.py).
|
"""Get the given setting as object (for YAML/config.py).
|
||||||
|
|
||||||
Note that the returned values are not watched for mutation.
|
Note that the returned values are not watched for mutation.
|
||||||
@ -383,10 +349,7 @@ class Config(QObject):
|
|||||||
value = self._values[name].get_for_url(url, fallback=fallback)
|
value = self._values[name].get_for_url(url, fallback=fallback)
|
||||||
return self._maybe_copy(value)
|
return self._maybe_copy(value)
|
||||||
|
|
||||||
def get_obj_for_pattern(
|
def get_obj_for_pattern(self, name, *, pattern):
|
||||||
self, name: str, *,
|
|
||||||
pattern: typing.Optional[urlmatch.UrlPattern]
|
|
||||||
) -> Any:
|
|
||||||
"""Get the given setting as object (for YAML/config.py).
|
"""Get the given setting as object (for YAML/config.py).
|
||||||
|
|
||||||
This gets the overridden value for a given pattern, or
|
This gets the overridden value for a given pattern, or
|
||||||
@ -396,8 +359,7 @@ class Config(QObject):
|
|||||||
value = self._values[name].get_for_pattern(pattern, fallback=False)
|
value = self._values[name].get_for_pattern(pattern, fallback=False)
|
||||||
return self._maybe_copy(value)
|
return self._maybe_copy(value)
|
||||||
|
|
||||||
def get_mutable_obj(self, name: str, *,
|
def get_mutable_obj(self, name, *, pattern=None):
|
||||||
pattern: urlmatch.UrlPattern = None) -> Any:
|
|
||||||
"""Get an object which can be mutated, e.g. in a config.py.
|
"""Get an object which can be mutated, e.g. in a config.py.
|
||||||
|
|
||||||
If a pattern is given, return the value for that pattern.
|
If a pattern is given, return the value for that pattern.
|
||||||
@ -422,8 +384,7 @@ class Config(QObject):
|
|||||||
|
|
||||||
return copy_value
|
return copy_value
|
||||||
|
|
||||||
def get_str(self, name: str, *,
|
def get_str(self, name, *, pattern=None):
|
||||||
pattern: urlmatch.UrlPattern = None) -> str:
|
|
||||||
"""Get the given setting as string.
|
"""Get the given setting as string.
|
||||||
|
|
||||||
If a pattern is given, get the setting for the given pattern or
|
If a pattern is given, get the setting for the given pattern or
|
||||||
@ -434,10 +395,7 @@ class Config(QObject):
|
|||||||
value = values.get_for_pattern(pattern)
|
value = values.get_for_pattern(pattern)
|
||||||
return opt.typ.to_str(value)
|
return opt.typ.to_str(value)
|
||||||
|
|
||||||
def set_obj(self, name: str,
|
def set_obj(self, name, value, *, pattern=None, save_yaml=False):
|
||||||
value: Any, *,
|
|
||||||
pattern: urlmatch.UrlPattern = None,
|
|
||||||
save_yaml: bool = False) -> None:
|
|
||||||
"""Set the given setting from a YAML/config.py object.
|
"""Set the given setting from a YAML/config.py object.
|
||||||
|
|
||||||
If save_yaml=True is given, store the new value to YAML.
|
If save_yaml=True is given, store the new value to YAML.
|
||||||
@ -448,10 +406,7 @@ class Config(QObject):
|
|||||||
if save_yaml:
|
if save_yaml:
|
||||||
self._yaml.set_obj(name, value, pattern=pattern)
|
self._yaml.set_obj(name, value, pattern=pattern)
|
||||||
|
|
||||||
def set_str(self, name: str,
|
def set_str(self, name, value, *, pattern=None, save_yaml=False):
|
||||||
value: str, *,
|
|
||||||
pattern: urlmatch.UrlPattern = None,
|
|
||||||
save_yaml: bool = False) -> None:
|
|
||||||
"""Set the given setting from a string.
|
"""Set the given setting from a string.
|
||||||
|
|
||||||
If save_yaml=True is given, store the new value to YAML.
|
If save_yaml=True is given, store the new value to YAML.
|
||||||
@ -466,9 +421,7 @@ class Config(QObject):
|
|||||||
if save_yaml:
|
if save_yaml:
|
||||||
self._yaml.set_obj(name, converted, pattern=pattern)
|
self._yaml.set_obj(name, converted, pattern=pattern)
|
||||||
|
|
||||||
def unset(self, name: str, *,
|
def unset(self, name, *, save_yaml=False, pattern=None):
|
||||||
save_yaml: bool = False,
|
|
||||||
pattern: urlmatch.UrlPattern = None) -> None:
|
|
||||||
"""Set the given setting back to its default."""
|
"""Set the given setting back to its default."""
|
||||||
opt = self.get_opt(name)
|
opt = self.get_opt(name)
|
||||||
self._check_yaml(opt, save_yaml)
|
self._check_yaml(opt, save_yaml)
|
||||||
@ -479,7 +432,7 @@ class Config(QObject):
|
|||||||
if save_yaml:
|
if save_yaml:
|
||||||
self._yaml.unset(name, pattern=pattern)
|
self._yaml.unset(name, pattern=pattern)
|
||||||
|
|
||||||
def clear(self, *, save_yaml: bool = False) -> None:
|
def clear(self, *, save_yaml=False):
|
||||||
"""Clear all settings in the config.
|
"""Clear all settings in the config.
|
||||||
|
|
||||||
If save_yaml=True is given, also remove all customization from the YAML
|
If save_yaml=True is given, also remove all customization from the YAML
|
||||||
@ -493,7 +446,7 @@ class Config(QObject):
|
|||||||
if save_yaml:
|
if save_yaml:
|
||||||
self._yaml.clear()
|
self._yaml.clear()
|
||||||
|
|
||||||
def update_mutables(self, *, save_yaml: bool = False) -> None:
|
def update_mutables(self, *, save_yaml=False):
|
||||||
"""Update mutable settings if they changed.
|
"""Update mutable settings if they changed.
|
||||||
|
|
||||||
Every time someone calls get_obj() on a mutable object, we save a
|
Every time someone calls get_obj() on a mutable object, we save a
|
||||||
@ -508,7 +461,7 @@ class Config(QObject):
|
|||||||
self.set_obj(name, new_value, save_yaml=save_yaml)
|
self.set_obj(name, new_value, save_yaml=save_yaml)
|
||||||
self._mutables = {}
|
self._mutables = {}
|
||||||
|
|
||||||
def dump_userconfig(self) -> str:
|
def dump_userconfig(self):
|
||||||
"""Get the part of the config which was changed by the user.
|
"""Get the part of the config which was changed by the user.
|
||||||
|
|
||||||
Return:
|
Return:
|
||||||
@ -537,10 +490,7 @@ class ConfigContainer:
|
|||||||
_pattern: The URL pattern to be used.
|
_pattern: The URL pattern to be used.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, config: Config,
|
def __init__(self, config, configapi=None, prefix='', pattern=None):
|
||||||
configapi: 'configfiles.ConfigAPI' = None,
|
|
||||||
prefix: str = '',
|
|
||||||
pattern: urlmatch.UrlPattern = None) -> None:
|
|
||||||
self._config = config
|
self._config = config
|
||||||
self._prefix = prefix
|
self._prefix = prefix
|
||||||
self._configapi = configapi
|
self._configapi = configapi
|
||||||
@ -548,13 +498,13 @@ class ConfigContainer:
|
|||||||
if configapi is None and pattern is not None:
|
if configapi is None and pattern is not None:
|
||||||
raise TypeError("Can't use pattern without configapi!")
|
raise TypeError("Can't use pattern without configapi!")
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self):
|
||||||
return utils.get_repr(self, constructor=True, config=self._config,
|
return utils.get_repr(self, constructor=True, config=self._config,
|
||||||
configapi=self._configapi, prefix=self._prefix,
|
configapi=self._configapi, prefix=self._prefix,
|
||||||
pattern=self._pattern)
|
pattern=self._pattern)
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def _handle_error(self, action: str, name: str) -> typing.Iterator[None]:
|
def _handle_error(self, action, name):
|
||||||
try:
|
try:
|
||||||
yield
|
yield
|
||||||
except configexc.Error as e:
|
except configexc.Error as e:
|
||||||
@ -563,7 +513,7 @@ class ConfigContainer:
|
|||||||
text = "While {} '{}'".format(action, name)
|
text = "While {} '{}'".format(action, name)
|
||||||
self._configapi.errors.append(configexc.ConfigErrorDesc(text, e))
|
self._configapi.errors.append(configexc.ConfigErrorDesc(text, e))
|
||||||
|
|
||||||
def __getattr__(self, attr: str) -> Any:
|
def __getattr__(self, attr):
|
||||||
"""Get an option or a new ConfigContainer with the added prefix.
|
"""Get an option or a new ConfigContainer with the added prefix.
|
||||||
|
|
||||||
If we get an option which exists, we return the value for it.
|
If we get an option which exists, we return the value for it.
|
||||||
@ -590,7 +540,7 @@ class ConfigContainer:
|
|||||||
return self._config.get_mutable_obj(
|
return self._config.get_mutable_obj(
|
||||||
name, pattern=self._pattern)
|
name, pattern=self._pattern)
|
||||||
|
|
||||||
def __setattr__(self, attr: str, value: Any) -> None:
|
def __setattr__(self, attr, value):
|
||||||
"""Set the given option in the config."""
|
"""Set the given option in the config."""
|
||||||
if attr.startswith('_'):
|
if attr.startswith('_'):
|
||||||
super().__setattr__(attr, value)
|
super().__setattr__(attr, value)
|
||||||
@ -600,7 +550,7 @@ class ConfigContainer:
|
|||||||
with self._handle_error('setting', name):
|
with self._handle_error('setting', name):
|
||||||
self._config.set_obj(name, value, pattern=self._pattern)
|
self._config.set_obj(name, value, pattern=self._pattern)
|
||||||
|
|
||||||
def _join(self, attr: str) -> str:
|
def _join(self, attr):
|
||||||
"""Get the prefix joined with the given attribute."""
|
"""Get the prefix joined with the given attribute."""
|
||||||
if self._prefix:
|
if self._prefix:
|
||||||
return '{}.{}'.format(self._prefix, attr)
|
return '{}.{}'.format(self._prefix, attr)
|
||||||
@ -608,10 +558,8 @@ class ConfigContainer:
|
|||||||
return attr
|
return attr
|
||||||
|
|
||||||
|
|
||||||
def set_register_stylesheet(obj: QObject, *,
|
def set_register_stylesheet(obj, *, stylesheet=None, update=True):
|
||||||
stylesheet: str = None,
|
"""Set the stylesheet for an object based on it's STYLESHEET attribute.
|
||||||
update: bool = True) -> None:
|
|
||||||
"""Set the stylesheet for an object.
|
|
||||||
|
|
||||||
Also, register an update when the config is changed.
|
Also, register an update when the config is changed.
|
||||||
|
|
||||||
@ -626,7 +574,7 @@ def set_register_stylesheet(obj: QObject, *,
|
|||||||
|
|
||||||
|
|
||||||
@functools.lru_cache()
|
@functools.lru_cache()
|
||||||
def _render_stylesheet(stylesheet: str) -> str:
|
def _render_stylesheet(stylesheet):
|
||||||
"""Render the given stylesheet jinja template."""
|
"""Render the given stylesheet jinja template."""
|
||||||
with jinja.environment.no_autoescape():
|
with jinja.environment.no_autoescape():
|
||||||
template = jinja.environment.from_string(stylesheet)
|
template = jinja.environment.from_string(stylesheet)
|
||||||
@ -642,9 +590,7 @@ class StyleSheetObserver(QObject):
|
|||||||
_stylesheet: The stylesheet template to use.
|
_stylesheet: The stylesheet template to use.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, obj: QObject,
|
def __init__(self, obj, stylesheet, update):
|
||||||
stylesheet: typing.Optional[str],
|
|
||||||
update: bool) -> None:
|
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._obj = obj
|
self._obj = obj
|
||||||
self._update = update
|
self._update = update
|
||||||
@ -653,11 +599,11 @@ class StyleSheetObserver(QObject):
|
|||||||
if self._update:
|
if self._update:
|
||||||
self.setParent(self._obj)
|
self.setParent(self._obj)
|
||||||
if stylesheet is None:
|
if stylesheet is None:
|
||||||
self._stylesheet = obj.STYLESHEET # type: str
|
self._stylesheet = obj.STYLESHEET
|
||||||
else:
|
else:
|
||||||
self._stylesheet = stylesheet
|
self._stylesheet = stylesheet
|
||||||
|
|
||||||
def _get_stylesheet(self) -> str:
|
def _get_stylesheet(self):
|
||||||
"""Format a stylesheet based on a template.
|
"""Format a stylesheet based on a template.
|
||||||
|
|
||||||
Return:
|
Return:
|
||||||
@ -666,15 +612,19 @@ class StyleSheetObserver(QObject):
|
|||||||
return _render_stylesheet(self._stylesheet)
|
return _render_stylesheet(self._stylesheet)
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def _update_stylesheet(self) -> None:
|
def _update_stylesheet(self):
|
||||||
"""Update the stylesheet for obj."""
|
"""Update the stylesheet for obj."""
|
||||||
self._obj.setStyleSheet(self._get_stylesheet())
|
self._obj.setStyleSheet(self._get_stylesheet())
|
||||||
|
|
||||||
def register(self) -> None:
|
def register(self):
|
||||||
"""Do a first update and listen for more."""
|
"""Do a first update and listen for more.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
update: if False, don't listen for future updates.
|
||||||
|
"""
|
||||||
qss = self._get_stylesheet()
|
qss = self._get_stylesheet()
|
||||||
log.config.vdebug( # type: ignore
|
log.config.vdebug("stylesheet for {}: {}".format(
|
||||||
"stylesheet for {}: {}".format(self._obj.__class__.__name__, qss))
|
self._obj.__class__.__name__, qss))
|
||||||
self._obj.setStyleSheet(qss)
|
self._obj.setStyleSheet(qss)
|
||||||
if self._update:
|
if self._update:
|
||||||
instance.changed.connect(self._update_stylesheet)
|
instance.changed.connect(self._update_stylesheet)
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
"""Commands related to the configuration."""
|
"""Commands related to the configuration."""
|
||||||
|
|
||||||
import typing
|
|
||||||
import os.path
|
import os.path
|
||||||
import contextlib
|
import contextlib
|
||||||
|
|
||||||
@ -32,34 +31,24 @@ from qutebrowser.config import configtypes, configexc, configfiles, configdata
|
|||||||
from qutebrowser.misc import editor
|
from qutebrowser.misc import editor
|
||||||
from qutebrowser.keyinput import keyutils
|
from qutebrowser.keyinput import keyutils
|
||||||
|
|
||||||
MYPY = False
|
|
||||||
if MYPY: # pragma: no cover
|
|
||||||
# pylint: disable=unused-import,useless-suppression
|
|
||||||
from qutebrowser.config.config import Config, KeyConfig
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigCommands:
|
class ConfigCommands:
|
||||||
|
|
||||||
"""qutebrowser commands related to the configuration."""
|
"""qutebrowser commands related to the configuration."""
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self, config, keyconfig):
|
||||||
config: 'Config',
|
|
||||||
keyconfig: 'KeyConfig') -> None:
|
|
||||||
self._config = config
|
self._config = config
|
||||||
self._keyconfig = keyconfig
|
self._keyconfig = keyconfig
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def _handle_config_error(self) -> typing.Iterator[None]:
|
def _handle_config_error(self):
|
||||||
"""Catch errors in set_command and raise CommandError."""
|
"""Catch errors in set_command and raise CommandError."""
|
||||||
try:
|
try:
|
||||||
yield
|
yield
|
||||||
except configexc.Error as e:
|
except configexc.Error as e:
|
||||||
raise cmdutils.CommandError(str(e))
|
raise cmdutils.CommandError(str(e))
|
||||||
|
|
||||||
def _parse_pattern(
|
def _parse_pattern(self, pattern):
|
||||||
self,
|
|
||||||
pattern: typing.Optional[str]
|
|
||||||
) -> typing.Optional[urlmatch.UrlPattern]:
|
|
||||||
"""Parse a pattern string argument to a pattern."""
|
"""Parse a pattern string argument to a pattern."""
|
||||||
if pattern is None:
|
if pattern is None:
|
||||||
return None
|
return None
|
||||||
@ -70,15 +59,14 @@ class ConfigCommands:
|
|||||||
raise cmdutils.CommandError("Error while parsing {}: {}"
|
raise cmdutils.CommandError("Error while parsing {}: {}"
|
||||||
.format(pattern, str(e)))
|
.format(pattern, str(e)))
|
||||||
|
|
||||||
def _parse_key(self, key: str) -> keyutils.KeySequence:
|
def _parse_key(self, key):
|
||||||
"""Parse a key argument."""
|
"""Parse a key argument."""
|
||||||
try:
|
try:
|
||||||
return keyutils.KeySequence.parse(key)
|
return keyutils.KeySequence.parse(key)
|
||||||
except keyutils.KeyParseError as e:
|
except keyutils.KeyParseError as e:
|
||||||
raise cmdutils.CommandError(str(e))
|
raise cmdutils.CommandError(str(e))
|
||||||
|
|
||||||
def _print_value(self, option: str,
|
def _print_value(self, option, pattern):
|
||||||
pattern: typing.Optional[urlmatch.UrlPattern]) -> None:
|
|
||||||
"""Print the value of the given option."""
|
"""Print the value of the given option."""
|
||||||
with self._handle_config_error():
|
with self._handle_config_error():
|
||||||
value = self._config.get_str(option, pattern=pattern)
|
value = self._config.get_str(option, pattern=pattern)
|
||||||
@ -93,9 +81,8 @@ class ConfigCommands:
|
|||||||
@cmdutils.argument('value', completion=configmodel.value)
|
@cmdutils.argument('value', completion=configmodel.value)
|
||||||
@cmdutils.argument('win_id', value=cmdutils.Value.win_id)
|
@cmdutils.argument('win_id', value=cmdutils.Value.win_id)
|
||||||
@cmdutils.argument('pattern', flag='u')
|
@cmdutils.argument('pattern', flag='u')
|
||||||
def set(self, win_id: int, option: str = None, value: str = None,
|
def set(self, win_id, option=None, value=None, temp=False, print_=False,
|
||||||
temp: bool = False, print_: bool = False,
|
*, pattern=None):
|
||||||
*, pattern: str = None) -> None:
|
|
||||||
"""Set an option.
|
"""Set an option.
|
||||||
|
|
||||||
If the option name ends with '?' or no value is provided, the
|
If the option name ends with '?' or no value is provided, the
|
||||||
@ -121,28 +108,28 @@ class ConfigCommands:
|
|||||||
raise cmdutils.CommandError("Toggling values was moved to the "
|
raise cmdutils.CommandError("Toggling values was moved to the "
|
||||||
":config-cycle command")
|
":config-cycle command")
|
||||||
|
|
||||||
parsed_pattern = self._parse_pattern(pattern)
|
pattern = self._parse_pattern(pattern)
|
||||||
|
|
||||||
if option.endswith('?') and option != '?':
|
if option.endswith('?') and option != '?':
|
||||||
self._print_value(option[:-1], pattern=parsed_pattern)
|
self._print_value(option[:-1], pattern=pattern)
|
||||||
return
|
return
|
||||||
|
|
||||||
with self._handle_config_error():
|
with self._handle_config_error():
|
||||||
if value is None:
|
if value is None:
|
||||||
self._print_value(option, pattern=parsed_pattern)
|
self._print_value(option, pattern=pattern)
|
||||||
else:
|
else:
|
||||||
self._config.set_str(option, value, pattern=parsed_pattern,
|
self._config.set_str(option, value, pattern=pattern,
|
||||||
save_yaml=not temp)
|
save_yaml=not temp)
|
||||||
|
|
||||||
if print_:
|
if print_:
|
||||||
self._print_value(option, pattern=parsed_pattern)
|
self._print_value(option, pattern=pattern)
|
||||||
|
|
||||||
@cmdutils.register(instance='config-commands', maxsplit=1,
|
@cmdutils.register(instance='config-commands', maxsplit=1,
|
||||||
no_cmd_split=True, no_replace_variables=True)
|
no_cmd_split=True, no_replace_variables=True)
|
||||||
@cmdutils.argument('command', completion=configmodel.bind)
|
@cmdutils.argument('command', completion=configmodel.bind)
|
||||||
@cmdutils.argument('win_id', value=cmdutils.Value.win_id)
|
@cmdutils.argument('win_id', value=cmdutils.Value.win_id)
|
||||||
def bind(self, win_id: str, key: str = None, command: str = None, *,
|
def bind(self, win_id, key=None, command=None, *, mode='normal',
|
||||||
mode: str = 'normal', default: bool = False) -> None:
|
default=False):
|
||||||
"""Bind a key to a command.
|
"""Bind a key to a command.
|
||||||
|
|
||||||
If no command is given, show the current binding for the given key.
|
If no command is given, show the current binding for the given key.
|
||||||
@ -187,7 +174,7 @@ class ConfigCommands:
|
|||||||
self._keyconfig.bind(seq, command, mode=mode, save_yaml=True)
|
self._keyconfig.bind(seq, command, mode=mode, save_yaml=True)
|
||||||
|
|
||||||
@cmdutils.register(instance='config-commands')
|
@cmdutils.register(instance='config-commands')
|
||||||
def unbind(self, key: str, *, mode: str = 'normal') -> None:
|
def unbind(self, key, *, mode='normal'):
|
||||||
"""Unbind a keychain.
|
"""Unbind a keychain.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -204,9 +191,8 @@ class ConfigCommands:
|
|||||||
@cmdutils.argument('option', completion=configmodel.option)
|
@cmdutils.argument('option', completion=configmodel.option)
|
||||||
@cmdutils.argument('values', completion=configmodel.value)
|
@cmdutils.argument('values', completion=configmodel.value)
|
||||||
@cmdutils.argument('pattern', flag='u')
|
@cmdutils.argument('pattern', flag='u')
|
||||||
def config_cycle(self, option: str, *values: str,
|
def config_cycle(self, option, *values, pattern=None, temp=False,
|
||||||
pattern: str = None,
|
print_=False):
|
||||||
temp: bool = False, print_: bool = False) -> None:
|
|
||||||
"""Cycle an option between multiple values.
|
"""Cycle an option between multiple values.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -216,15 +202,15 @@ class ConfigCommands:
|
|||||||
temp: Set value temporarily until qutebrowser is closed.
|
temp: Set value temporarily until qutebrowser is closed.
|
||||||
print_: Print the value after setting.
|
print_: Print the value after setting.
|
||||||
"""
|
"""
|
||||||
parsed_pattern = self._parse_pattern(pattern)
|
pattern = self._parse_pattern(pattern)
|
||||||
|
|
||||||
with self._handle_config_error():
|
with self._handle_config_error():
|
||||||
opt = self._config.get_opt(option)
|
opt = self._config.get_opt(option)
|
||||||
old_value = self._config.get_obj_for_pattern(
|
old_value = self._config.get_obj_for_pattern(option,
|
||||||
option, pattern=parsed_pattern)
|
pattern=pattern)
|
||||||
|
|
||||||
if not values and isinstance(opt.typ, configtypes.Bool):
|
if not values and isinstance(opt.typ, configtypes.Bool):
|
||||||
values = ('true', 'false')
|
values = ['true', 'false']
|
||||||
|
|
||||||
if len(values) < 2:
|
if len(values) < 2:
|
||||||
raise cmdutils.CommandError("Need at least two values for "
|
raise cmdutils.CommandError("Need at least two values for "
|
||||||
@ -233,25 +219,25 @@ class ConfigCommands:
|
|||||||
# Use the next valid value from values, or the first if the current
|
# Use the next valid value from values, or the first if the current
|
||||||
# value does not appear in the list
|
# value does not appear in the list
|
||||||
with self._handle_config_error():
|
with self._handle_config_error():
|
||||||
cycle_values = [opt.typ.from_str(val) for val in values]
|
values = [opt.typ.from_str(val) for val in values]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
idx = cycle_values.index(old_value)
|
idx = values.index(old_value)
|
||||||
idx = (idx + 1) % len(cycle_values)
|
idx = (idx + 1) % len(values)
|
||||||
value = cycle_values[idx]
|
value = values[idx]
|
||||||
except ValueError:
|
except ValueError:
|
||||||
value = cycle_values[0]
|
value = values[0]
|
||||||
|
|
||||||
with self._handle_config_error():
|
with self._handle_config_error():
|
||||||
self._config.set_obj(option, value, pattern=parsed_pattern,
|
self._config.set_obj(option, value, pattern=pattern,
|
||||||
save_yaml=not temp)
|
save_yaml=not temp)
|
||||||
|
|
||||||
if print_:
|
if print_:
|
||||||
self._print_value(option, pattern=parsed_pattern)
|
self._print_value(option, pattern=pattern)
|
||||||
|
|
||||||
@cmdutils.register(instance='config-commands')
|
@cmdutils.register(instance='config-commands')
|
||||||
@cmdutils.argument('option', completion=configmodel.customized_option)
|
@cmdutils.argument('option', completion=configmodel.customized_option)
|
||||||
def config_unset(self, option: str, temp: bool = False) -> None:
|
def config_unset(self, option, temp=False):
|
||||||
"""Unset an option.
|
"""Unset an option.
|
||||||
|
|
||||||
This sets an option back to its default and removes it from
|
This sets an option back to its default and removes it from
|
||||||
@ -266,8 +252,7 @@ class ConfigCommands:
|
|||||||
|
|
||||||
@cmdutils.register(instance='config-commands')
|
@cmdutils.register(instance='config-commands')
|
||||||
@cmdutils.argument('option', completion=configmodel.list_option)
|
@cmdutils.argument('option', completion=configmodel.list_option)
|
||||||
def config_list_add(self, option: str, value: str,
|
def config_list_add(self, option, value, temp=False):
|
||||||
temp: bool = False) -> None:
|
|
||||||
"""Append a value to a config option that is a list.
|
"""Append a value to a config option that is a list.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -288,8 +273,7 @@ class ConfigCommands:
|
|||||||
|
|
||||||
@cmdutils.register(instance='config-commands')
|
@cmdutils.register(instance='config-commands')
|
||||||
@cmdutils.argument('option', completion=configmodel.dict_option)
|
@cmdutils.argument('option', completion=configmodel.dict_option)
|
||||||
def config_dict_add(self, option: str, key: str, value: str,
|
def config_dict_add(self, option, key, value, temp=False, replace=False):
|
||||||
temp: bool = False, replace: bool = False) -> None:
|
|
||||||
"""Add a key/value pair to a dictionary option.
|
"""Add a key/value pair to a dictionary option.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -318,8 +302,7 @@ class ConfigCommands:
|
|||||||
|
|
||||||
@cmdutils.register(instance='config-commands')
|
@cmdutils.register(instance='config-commands')
|
||||||
@cmdutils.argument('option', completion=configmodel.list_option)
|
@cmdutils.argument('option', completion=configmodel.list_option)
|
||||||
def config_list_remove(self, option: str, value: str,
|
def config_list_remove(self, option, value, temp=False):
|
||||||
temp: bool = False) -> None:
|
|
||||||
"""Remove a value from a list.
|
"""Remove a value from a list.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -346,8 +329,7 @@ class ConfigCommands:
|
|||||||
|
|
||||||
@cmdutils.register(instance='config-commands')
|
@cmdutils.register(instance='config-commands')
|
||||||
@cmdutils.argument('option', completion=configmodel.dict_option)
|
@cmdutils.argument('option', completion=configmodel.dict_option)
|
||||||
def config_dict_remove(self, option: str, key: str,
|
def config_dict_remove(self, option, key, temp=False):
|
||||||
temp: bool = False) -> None:
|
|
||||||
"""Remove a key from a dict.
|
"""Remove a key from a dict.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -372,7 +354,7 @@ class ConfigCommands:
|
|||||||
self._config.update_mutables(save_yaml=not temp)
|
self._config.update_mutables(save_yaml=not temp)
|
||||||
|
|
||||||
@cmdutils.register(instance='config-commands')
|
@cmdutils.register(instance='config-commands')
|
||||||
def config_clear(self, save: bool = False) -> None:
|
def config_clear(self, save=False):
|
||||||
"""Set all settings back to their default.
|
"""Set all settings back to their default.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -382,7 +364,7 @@ class ConfigCommands:
|
|||||||
self._config.clear(save_yaml=save)
|
self._config.clear(save_yaml=save)
|
||||||
|
|
||||||
@cmdutils.register(instance='config-commands')
|
@cmdutils.register(instance='config-commands')
|
||||||
def config_source(self, filename: str = None, clear: bool = False) -> None:
|
def config_source(self, filename=None, clear=False):
|
||||||
"""Read a config.py file.
|
"""Read a config.py file.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -404,13 +386,13 @@ class ConfigCommands:
|
|||||||
raise cmdutils.CommandError(e)
|
raise cmdutils.CommandError(e)
|
||||||
|
|
||||||
@cmdutils.register(instance='config-commands')
|
@cmdutils.register(instance='config-commands')
|
||||||
def config_edit(self, no_source: bool = False) -> None:
|
def config_edit(self, no_source=False):
|
||||||
"""Open the config.py file in the editor.
|
"""Open the config.py file in the editor.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
no_source: Don't re-source the config file after editing.
|
no_source: Don't re-source the config file after editing.
|
||||||
"""
|
"""
|
||||||
def on_file_updated() -> None:
|
def on_file_updated():
|
||||||
"""Source the new config when editing finished.
|
"""Source the new config when editing finished.
|
||||||
|
|
||||||
This can't use cmdutils.CommandError as it's run async.
|
This can't use cmdutils.CommandError as it's run async.
|
||||||
@ -428,8 +410,7 @@ class ConfigCommands:
|
|||||||
ed.edit_file(filename)
|
ed.edit_file(filename)
|
||||||
|
|
||||||
@cmdutils.register(instance='config-commands')
|
@cmdutils.register(instance='config-commands')
|
||||||
def config_write_py(self, filename: str = None,
|
def config_write_py(self, filename=None, force=False, defaults=False):
|
||||||
force: bool = False, defaults: bool = False) -> None:
|
|
||||||
"""Write the current configuration to a config.py file.
|
"""Write the current configuration to a config.py file.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -448,13 +429,13 @@ class ConfigCommands:
|
|||||||
raise cmdutils.CommandError("{} already exists - use --force to "
|
raise cmdutils.CommandError("{} already exists - use --force to "
|
||||||
"overwrite!".format(filename))
|
"overwrite!".format(filename))
|
||||||
|
|
||||||
options = [] # type: typing.List
|
|
||||||
if defaults:
|
if defaults:
|
||||||
options = [(None, opt, opt.default)
|
options = [(None, opt, opt.default)
|
||||||
for _name, opt in sorted(configdata.DATA.items())]
|
for _name, opt in sorted(configdata.DATA.items())]
|
||||||
bindings = dict(configdata.DATA['bindings.default'].default)
|
bindings = dict(configdata.DATA['bindings.default'].default)
|
||||||
commented = True
|
commented = True
|
||||||
else:
|
else:
|
||||||
|
options = []
|
||||||
for values in self._config:
|
for values in self._config:
|
||||||
for scoped in values:
|
for scoped in values:
|
||||||
options.append((scoped.pattern, values.opt, scoped.value))
|
options.append((scoped.pattern, values.opt, scoped.value))
|
||||||
|
@ -24,18 +24,14 @@ Module attributes:
|
|||||||
DATA: A dict of Option objects after init() has been called.
|
DATA: A dict of Option objects after init() has been called.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import typing
|
|
||||||
from typing import Optional # pylint: disable=unused-import
|
|
||||||
import functools
|
import functools
|
||||||
|
|
||||||
import attr
|
import attr
|
||||||
from qutebrowser.config import configtypes
|
from qutebrowser.config import configtypes
|
||||||
from qutebrowser.utils import usertypes, qtutils, utils
|
from qutebrowser.utils import usertypes, qtutils, utils
|
||||||
|
|
||||||
DATA = typing.cast(typing.Mapping[str, 'Option'], None)
|
DATA = None
|
||||||
MIGRATIONS = typing.cast('Migrations', None)
|
MIGRATIONS = None
|
||||||
|
|
||||||
_BackendDict = typing.Mapping[str, typing.Union[str, bool]]
|
|
||||||
|
|
||||||
|
|
||||||
@attr.s
|
@attr.s
|
||||||
@ -46,15 +42,15 @@ class Option:
|
|||||||
Note that this is just an option which exists, with no value associated.
|
Note that this is just an option which exists, with no value associated.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = attr.ib() # type: str
|
name = attr.ib()
|
||||||
typ = attr.ib() # type: configtypes.BaseType
|
typ = attr.ib()
|
||||||
default = attr.ib() # type: typing.Any
|
default = attr.ib()
|
||||||
backends = attr.ib() # type: typing.Iterable[usertypes.Backend]
|
backends = attr.ib()
|
||||||
raw_backends = attr.ib() # type: Optional[typing.Mapping[str, bool]]
|
raw_backends = attr.ib()
|
||||||
description = attr.ib() # type: str
|
description = attr.ib()
|
||||||
supports_pattern = attr.ib(default=False) # type: bool
|
supports_pattern = attr.ib(default=False)
|
||||||
restart = attr.ib(default=False) # type: bool
|
restart = attr.ib(default=False)
|
||||||
no_autoconfig = attr.ib(default=False) # type: bool
|
no_autoconfig = attr.ib(default=False)
|
||||||
|
|
||||||
|
|
||||||
@attr.s
|
@attr.s
|
||||||
@ -67,13 +63,11 @@ class Migrations:
|
|||||||
deleted: A list of option names which have been removed.
|
deleted: A list of option names which have been removed.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
renamed = attr.ib(
|
renamed = attr.ib(default=attr.Factory(dict))
|
||||||
default=attr.Factory(dict)) # type: typing.Dict[str, str]
|
deleted = attr.ib(default=attr.Factory(list))
|
||||||
deleted = attr.ib(
|
|
||||||
default=attr.Factory(list)) # type: typing.List[str]
|
|
||||||
|
|
||||||
|
|
||||||
def _raise_invalid_node(name: str, what: str, node: typing.Any) -> None:
|
def _raise_invalid_node(name, what, node):
|
||||||
"""Raise an exception for an invalid configdata YAML node.
|
"""Raise an exception for an invalid configdata YAML node.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -85,16 +79,13 @@ def _raise_invalid_node(name: str, what: str, node: typing.Any) -> None:
|
|||||||
name, what, node))
|
name, what, node))
|
||||||
|
|
||||||
|
|
||||||
def _parse_yaml_type(
|
def _parse_yaml_type(name, node):
|
||||||
name: str,
|
|
||||||
node: typing.Union[str, typing.Mapping[str, typing.Any]],
|
|
||||||
) -> configtypes.BaseType:
|
|
||||||
if isinstance(node, str):
|
if isinstance(node, str):
|
||||||
# e.g:
|
# e.g:
|
||||||
# type: Bool
|
# type: Bool
|
||||||
# -> create the type object without any arguments
|
# -> create the type object without any arguments
|
||||||
type_name = node
|
type_name = node
|
||||||
kwargs = {} # type: typing.MutableMapping[str, typing.Any]
|
kwargs = {}
|
||||||
elif isinstance(node, dict):
|
elif isinstance(node, dict):
|
||||||
# e.g:
|
# e.g:
|
||||||
# type:
|
# type:
|
||||||
@ -132,10 +123,7 @@ def _parse_yaml_type(
|
|||||||
type_name, node, e))
|
type_name, node, e))
|
||||||
|
|
||||||
|
|
||||||
def _parse_yaml_backends_dict(
|
def _parse_yaml_backends_dict(name, node):
|
||||||
name: str,
|
|
||||||
node: _BackendDict,
|
|
||||||
) -> typing.Sequence[usertypes.Backend]:
|
|
||||||
"""Parse a dict definition for backends.
|
"""Parse a dict definition for backends.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
@ -172,10 +160,7 @@ def _parse_yaml_backends_dict(
|
|||||||
return backends
|
return backends
|
||||||
|
|
||||||
|
|
||||||
def _parse_yaml_backends(
|
def _parse_yaml_backends(name, node):
|
||||||
name: str,
|
|
||||||
node: typing.Union[None, str, _BackendDict],
|
|
||||||
) -> typing.Sequence[usertypes.Backend]:
|
|
||||||
"""Parse a backend node in the yaml.
|
"""Parse a backend node in the yaml.
|
||||||
|
|
||||||
It can have one of those four forms:
|
It can have one of those four forms:
|
||||||
@ -202,9 +187,7 @@ def _parse_yaml_backends(
|
|||||||
raise utils.Unreachable
|
raise utils.Unreachable
|
||||||
|
|
||||||
|
|
||||||
def _read_yaml(
|
def _read_yaml(yaml_data):
|
||||||
yaml_data: str,
|
|
||||||
) -> typing.Tuple[typing.Mapping[str, Option], Migrations]:
|
|
||||||
"""Read config data from a YAML file.
|
"""Read config data from a YAML file.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -266,12 +249,12 @@ def _read_yaml(
|
|||||||
|
|
||||||
|
|
||||||
@functools.lru_cache(maxsize=256)
|
@functools.lru_cache(maxsize=256)
|
||||||
def is_valid_prefix(prefix: str) -> bool:
|
def is_valid_prefix(prefix):
|
||||||
"""Check whether the given prefix is a valid prefix for some option."""
|
"""Check whether the given prefix is a valid prefix for some option."""
|
||||||
return any(key.startswith(prefix + '.') for key in DATA)
|
return any(key.startswith(prefix + '.') for key in DATA)
|
||||||
|
|
||||||
|
|
||||||
def init() -> None:
|
def init():
|
||||||
"""Initialize configdata from the YAML file."""
|
"""Initialize configdata from the YAML file."""
|
||||||
global DATA, MIGRATIONS
|
global DATA, MIGRATIONS
|
||||||
DATA, MIGRATIONS = _read_yaml(utils.read_file('config/configdata.yml'))
|
DATA, MIGRATIONS = _read_yaml(utils.read_file('config/configdata.yml'))
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
"""Code to show a diff of the legacy config format."""
|
"""Code to show a diff of the legacy config format."""
|
||||||
|
|
||||||
import typing # pylint: disable=unused-import,useless-suppression
|
|
||||||
import difflib
|
import difflib
|
||||||
import os.path
|
import os.path
|
||||||
|
|
||||||
@ -728,10 +727,10 @@ scroll right
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def get_diff() -> str:
|
def get_diff():
|
||||||
"""Get a HTML diff for the old config files."""
|
"""Get a HTML diff for the old config files."""
|
||||||
old_conf_lines = [] # type: typing.MutableSequence[str]
|
old_conf_lines = []
|
||||||
old_key_lines = [] # type: typing.MutableSequence[str]
|
old_key_lines = []
|
||||||
|
|
||||||
for filename, dest in [('qutebrowser.conf', old_conf_lines),
|
for filename, dest in [('qutebrowser.conf', old_conf_lines),
|
||||||
('keys.conf', old_key_lines)]:
|
('keys.conf', old_key_lines)]:
|
||||||
|
@ -19,10 +19,9 @@
|
|||||||
|
|
||||||
"""Exceptions related to config parsing."""
|
"""Exceptions related to config parsing."""
|
||||||
|
|
||||||
import typing
|
|
||||||
import attr
|
import attr
|
||||||
|
|
||||||
from qutebrowser.utils import jinja, usertypes
|
from qutebrowser.utils import jinja
|
||||||
|
|
||||||
|
|
||||||
class Error(Exception):
|
class Error(Exception):
|
||||||
@ -34,7 +33,7 @@ class NoAutoconfigError(Error):
|
|||||||
|
|
||||||
"""Raised when this option can't be set in autoconfig.yml."""
|
"""Raised when this option can't be set in autoconfig.yml."""
|
||||||
|
|
||||||
def __init__(self, name: str) -> None:
|
def __init__(self, name):
|
||||||
super().__init__("The {} setting can only be set in config.py!"
|
super().__init__("The {} setting can only be set in config.py!"
|
||||||
.format(name))
|
.format(name))
|
||||||
|
|
||||||
@ -43,11 +42,7 @@ class BackendError(Error):
|
|||||||
|
|
||||||
"""Raised when this setting is unavailable with the current backend."""
|
"""Raised when this setting is unavailable with the current backend."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(self, name, backend, raw_backends):
|
||||||
self, name: str,
|
|
||||||
backend: usertypes.Backend,
|
|
||||||
raw_backends: typing.Optional[typing.Mapping[str, bool]]
|
|
||||||
) -> None:
|
|
||||||
if raw_backends is None or not raw_backends[backend.name]:
|
if raw_backends is None or not raw_backends[backend.name]:
|
||||||
msg = ("The {} setting is not available with the {} backend!"
|
msg = ("The {} setting is not available with the {} backend!"
|
||||||
.format(name, backend.name))
|
.format(name, backend.name))
|
||||||
@ -62,7 +57,7 @@ class NoPatternError(Error):
|
|||||||
|
|
||||||
"""Raised when the given setting does not support URL patterns."""
|
"""Raised when the given setting does not support URL patterns."""
|
||||||
|
|
||||||
def __init__(self, name: str) -> None:
|
def __init__(self, name):
|
||||||
super().__init__("The {} setting does not support URL patterns!"
|
super().__init__("The {} setting does not support URL patterns!"
|
||||||
.format(name))
|
.format(name))
|
||||||
|
|
||||||
@ -76,7 +71,7 @@ class ValidationError(Error):
|
|||||||
msg: Additional error message.
|
msg: Additional error message.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, value: typing.Any, msg: str) -> None:
|
def __init__(self, value, msg):
|
||||||
super().__init__("Invalid value '{}' - {}".format(value, msg))
|
super().__init__("Invalid value '{}' - {}".format(value, msg))
|
||||||
self.option = None
|
self.option = None
|
||||||
|
|
||||||
@ -90,9 +85,7 @@ class NoOptionError(Error):
|
|||||||
|
|
||||||
"""Raised when an option was not found."""
|
"""Raised when an option was not found."""
|
||||||
|
|
||||||
def __init__(self, option: str, *,
|
def __init__(self, option, *, deleted=False, renamed=None):
|
||||||
deleted: bool = False,
|
|
||||||
renamed: str = None) -> None:
|
|
||||||
if deleted:
|
if deleted:
|
||||||
assert renamed is None
|
assert renamed is None
|
||||||
suffix = ' (this option was removed from qutebrowser)'
|
suffix = ' (this option was removed from qutebrowser)'
|
||||||
@ -116,18 +109,18 @@ class ConfigErrorDesc:
|
|||||||
traceback: The formatted traceback of the exception.
|
traceback: The formatted traceback of the exception.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
text = attr.ib() # type: str
|
text = attr.ib()
|
||||||
exception = attr.ib() # type: typing.Union[str, Exception]
|
exception = attr.ib()
|
||||||
traceback = attr.ib(None) # type: str
|
traceback = attr.ib(None)
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self):
|
||||||
if self.traceback:
|
if self.traceback:
|
||||||
return '{} - {}: {}'.format(self.text,
|
return '{} - {}: {}'.format(self.text,
|
||||||
self.exception.__class__.__name__,
|
self.exception.__class__.__name__,
|
||||||
self.exception)
|
self.exception)
|
||||||
return '{}: {}'.format(self.text, self.exception)
|
return '{}: {}'.format(self.text, self.exception)
|
||||||
|
|
||||||
def with_text(self, text: str) -> 'ConfigErrorDesc':
|
def with_text(self, text):
|
||||||
"""Get a new ConfigErrorDesc with the given text appended."""
|
"""Get a new ConfigErrorDesc with the given text appended."""
|
||||||
return self.__class__(text='{} ({})'.format(self.text, text),
|
return self.__class__(text='{} ({})'.format(self.text, text),
|
||||||
exception=self.exception,
|
exception=self.exception,
|
||||||
@ -138,15 +131,13 @@ class ConfigFileErrors(Error):
|
|||||||
|
|
||||||
"""Raised when multiple errors occurred inside the config."""
|
"""Raised when multiple errors occurred inside the config."""
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self, basename, errors):
|
||||||
basename: str,
|
|
||||||
errors: typing.Sequence[ConfigErrorDesc]) -> None:
|
|
||||||
super().__init__("Errors occurred while reading {}:\n{}".format(
|
super().__init__("Errors occurred while reading {}:\n{}".format(
|
||||||
basename, '\n'.join(' {}'.format(e) for e in errors)))
|
basename, '\n'.join(' {}'.format(e) for e in errors)))
|
||||||
self.basename = basename
|
self.basename = basename
|
||||||
self.errors = errors
|
self.errors = errors
|
||||||
|
|
||||||
def to_html(self) -> str:
|
def to_html(self):
|
||||||
"""Get the error texts as a HTML snippet."""
|
"""Get the error texts as a HTML snippet."""
|
||||||
template = jinja.environment.from_string("""
|
template = jinja.environment.from_string("""
|
||||||
Errors occurred while reading {{ basename }}:
|
Errors occurred while reading {{ basename }}:
|
||||||
|
@ -27,7 +27,6 @@ import textwrap
|
|||||||
import traceback
|
import traceback
|
||||||
import configparser
|
import configparser
|
||||||
import contextlib
|
import contextlib
|
||||||
import typing
|
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
from PyQt5.QtCore import pyqtSignal, QObject, QSettings
|
from PyQt5.QtCore import pyqtSignal, QObject, QSettings
|
||||||
@ -37,21 +36,16 @@ from qutebrowser.config import configexc, config, configdata, configutils
|
|||||||
from qutebrowser.keyinput import keyutils
|
from qutebrowser.keyinput import keyutils
|
||||||
from qutebrowser.utils import standarddir, utils, qtutils, log, urlmatch
|
from qutebrowser.utils import standarddir, utils, qtutils, log, urlmatch
|
||||||
|
|
||||||
MYPY = False
|
|
||||||
if MYPY:
|
|
||||||
# pylint: disable=unused-import, useless-suppression
|
|
||||||
from qutebrowser.misc import savemanager
|
|
||||||
|
|
||||||
|
|
||||||
# The StateConfig instance
|
# The StateConfig instance
|
||||||
state = typing.cast('StateConfig', None)
|
state = None
|
||||||
|
|
||||||
|
|
||||||
class StateConfig(configparser.ConfigParser):
|
class StateConfig(configparser.ConfigParser):
|
||||||
|
|
||||||
"""The "state" file saving various application state."""
|
"""The "state" file saving various application state."""
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._filename = os.path.join(standarddir.data(), 'state')
|
self._filename = os.path.join(standarddir.data(), 'state')
|
||||||
self.read(self._filename, encoding='utf-8')
|
self.read(self._filename, encoding='utf-8')
|
||||||
@ -65,8 +59,7 @@ class StateConfig(configparser.ConfigParser):
|
|||||||
for key in deleted_keys:
|
for key in deleted_keys:
|
||||||
self['general'].pop(key, None)
|
self['general'].pop(key, None)
|
||||||
|
|
||||||
def init_save_manager(self,
|
def init_save_manager(self, save_manager):
|
||||||
save_manager: 'savemanager.SaveManager') -> None:
|
|
||||||
"""Make sure the config gets saved properly.
|
"""Make sure the config gets saved properly.
|
||||||
|
|
||||||
We do this outside of __init__ because the config gets created before
|
We do this outside of __init__ because the config gets created before
|
||||||
@ -74,7 +67,7 @@ class StateConfig(configparser.ConfigParser):
|
|||||||
"""
|
"""
|
||||||
save_manager.add_saveable('state-config', self._save)
|
save_manager.add_saveable('state-config', self._save)
|
||||||
|
|
||||||
def _save(self) -> None:
|
def _save(self):
|
||||||
"""Save the state file to the configured location."""
|
"""Save the state file to the configured location."""
|
||||||
with open(self._filename, 'w', encoding='utf-8') as f:
|
with open(self._filename, 'w', encoding='utf-8') as f:
|
||||||
self.write(f)
|
self.write(f)
|
||||||
@ -91,20 +84,17 @@ class YamlConfig(QObject):
|
|||||||
VERSION = 2
|
VERSION = 2
|
||||||
changed = pyqtSignal()
|
changed = pyqtSignal()
|
||||||
|
|
||||||
_SettingsType = typing.Dict[str, typing.Dict[str, typing.Any]]
|
def __init__(self, parent=None):
|
||||||
|
|
||||||
def __init__(self, parent: QObject = None) -> None:
|
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self._filename = os.path.join(standarddir.config(auto=True),
|
self._filename = os.path.join(standarddir.config(auto=True),
|
||||||
'autoconfig.yml')
|
'autoconfig.yml')
|
||||||
self._dirty = False
|
self._dirty = None
|
||||||
|
|
||||||
self._values = {} # type: typing.Dict[str, configutils.Values]
|
self._values = {}
|
||||||
for name, opt in configdata.DATA.items():
|
for name, opt in configdata.DATA.items():
|
||||||
self._values[name] = configutils.Values(opt)
|
self._values[name] = configutils.Values(opt)
|
||||||
|
|
||||||
def init_save_manager(self,
|
def init_save_manager(self, save_manager):
|
||||||
save_manager: 'savemanager.SaveManager') -> None:
|
|
||||||
"""Make sure the config gets saved properly.
|
"""Make sure the config gets saved properly.
|
||||||
|
|
||||||
We do this outside of __init__ because the config gets created before
|
We do this outside of __init__ because the config gets created before
|
||||||
@ -112,21 +102,21 @@ class YamlConfig(QObject):
|
|||||||
"""
|
"""
|
||||||
save_manager.add_saveable('yaml-config', self._save, self.changed)
|
save_manager.add_saveable('yaml-config', self._save, self.changed)
|
||||||
|
|
||||||
def __iter__(self) -> typing.Iterator[configutils.Values]:
|
def __iter__(self):
|
||||||
"""Iterate over configutils.Values items."""
|
"""Iterate over configutils.Values items."""
|
||||||
yield from self._values.values()
|
yield from self._values.values()
|
||||||
|
|
||||||
def _mark_changed(self) -> None:
|
def _mark_changed(self):
|
||||||
"""Mark the YAML config as changed."""
|
"""Mark the YAML config as changed."""
|
||||||
self._dirty = True
|
self._dirty = True
|
||||||
self.changed.emit()
|
self.changed.emit()
|
||||||
|
|
||||||
def _save(self) -> None:
|
def _save(self):
|
||||||
"""Save the settings to the YAML file if they've changed."""
|
"""Save the settings to the YAML file if they've changed."""
|
||||||
if not self._dirty:
|
if not self._dirty:
|
||||||
return
|
return
|
||||||
|
|
||||||
settings = {} # type: YamlConfig._SettingsType
|
settings = {}
|
||||||
for name, values in sorted(self._values.items()):
|
for name, values in sorted(self._values.items()):
|
||||||
if not values:
|
if not values:
|
||||||
continue
|
continue
|
||||||
@ -145,10 +135,7 @@ class YamlConfig(QObject):
|
|||||||
""".lstrip('\n')))
|
""".lstrip('\n')))
|
||||||
utils.yaml_dump(data, f)
|
utils.yaml_dump(data, f)
|
||||||
|
|
||||||
def _pop_object(self,
|
def _pop_object(self, yaml_data, key, typ):
|
||||||
yaml_data: typing.Any,
|
|
||||||
key: str,
|
|
||||||
typ: type) -> typing.Any:
|
|
||||||
"""Get a global object from the given data."""
|
"""Get a global object from the given data."""
|
||||||
if not isinstance(yaml_data, dict):
|
if not isinstance(yaml_data, dict):
|
||||||
desc = configexc.ConfigErrorDesc("While loading data",
|
desc = configexc.ConfigErrorDesc("While loading data",
|
||||||
@ -171,7 +158,7 @@ class YamlConfig(QObject):
|
|||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def load(self) -> None:
|
def load(self):
|
||||||
"""Load configuration from the configured YAML file."""
|
"""Load configuration from the configured YAML file."""
|
||||||
try:
|
try:
|
||||||
with open(self._filename, 'r', encoding='utf-8') as f:
|
with open(self._filename, 'r', encoding='utf-8') as f:
|
||||||
@ -202,19 +189,18 @@ class YamlConfig(QObject):
|
|||||||
self._validate(settings)
|
self._validate(settings)
|
||||||
self._build_values(settings)
|
self._build_values(settings)
|
||||||
|
|
||||||
def _load_settings_object(self, yaml_data: typing.Any) -> '_SettingsType':
|
def _load_settings_object(self, yaml_data):
|
||||||
"""Load the settings from the settings: key."""
|
"""Load the settings from the settings: key."""
|
||||||
return self._pop_object(yaml_data, 'settings', dict)
|
return self._pop_object(yaml_data, 'settings', dict)
|
||||||
|
|
||||||
def _load_legacy_settings_object(self,
|
def _load_legacy_settings_object(self, yaml_data):
|
||||||
yaml_data: typing.Any) -> '_SettingsType':
|
|
||||||
data = self._pop_object(yaml_data, 'global', dict)
|
data = self._pop_object(yaml_data, 'global', dict)
|
||||||
settings = {}
|
settings = {}
|
||||||
for name, value in data.items():
|
for name, value in data.items():
|
||||||
settings[name] = {'global': value}
|
settings[name] = {'global': value}
|
||||||
return settings
|
return settings
|
||||||
|
|
||||||
def _build_values(self, settings: typing.Mapping) -> None:
|
def _build_values(self, settings):
|
||||||
"""Build up self._values from the values in the given dict."""
|
"""Build up self._values from the values in the given dict."""
|
||||||
errors = []
|
errors = []
|
||||||
for name, yaml_values in settings.items():
|
for name, yaml_values in settings.items():
|
||||||
@ -247,8 +233,7 @@ class YamlConfig(QObject):
|
|||||||
if errors:
|
if errors:
|
||||||
raise configexc.ConfigFileErrors('autoconfig.yml', errors)
|
raise configexc.ConfigFileErrors('autoconfig.yml', errors)
|
||||||
|
|
||||||
def _migrate_bool(self, settings: _SettingsType, name: str,
|
def _migrate_bool(self, settings, name, true_value, false_value):
|
||||||
true_value: str, false_value: str) -> None:
|
|
||||||
"""Migrate a boolean in the settings."""
|
"""Migrate a boolean in the settings."""
|
||||||
if name in settings:
|
if name in settings:
|
||||||
for scope, val in settings[name].items():
|
for scope, val in settings[name].items():
|
||||||
@ -256,7 +241,7 @@ class YamlConfig(QObject):
|
|||||||
settings[name][scope] = true_value if val else false_value
|
settings[name][scope] = true_value if val else false_value
|
||||||
self._mark_changed()
|
self._mark_changed()
|
||||||
|
|
||||||
def _handle_migrations(self, settings: _SettingsType) -> '_SettingsType':
|
def _handle_migrations(self, settings):
|
||||||
"""Migrate older configs to the newest format."""
|
"""Migrate older configs to the newest format."""
|
||||||
# Simple renamed/deleted options
|
# Simple renamed/deleted options
|
||||||
for name in list(settings):
|
for name in list(settings):
|
||||||
@ -314,7 +299,7 @@ class YamlConfig(QObject):
|
|||||||
|
|
||||||
return settings
|
return settings
|
||||||
|
|
||||||
def _validate(self, settings: _SettingsType) -> None:
|
def _validate(self, settings):
|
||||||
"""Make sure all settings exist."""
|
"""Make sure all settings exist."""
|
||||||
unknown = []
|
unknown = []
|
||||||
for name in settings:
|
for name in settings:
|
||||||
@ -327,19 +312,18 @@ class YamlConfig(QObject):
|
|||||||
for e in sorted(unknown)]
|
for e in sorted(unknown)]
|
||||||
raise configexc.ConfigFileErrors('autoconfig.yml', errors)
|
raise configexc.ConfigFileErrors('autoconfig.yml', errors)
|
||||||
|
|
||||||
def set_obj(self, name: str, value: typing.Any, *,
|
def set_obj(self, name, value, *, pattern=None):
|
||||||
pattern: urlmatch.UrlPattern = None) -> None:
|
|
||||||
"""Set the given setting to the given value."""
|
"""Set the given setting to the given value."""
|
||||||
self._values[name].add(value, pattern)
|
self._values[name].add(value, pattern)
|
||||||
self._mark_changed()
|
self._mark_changed()
|
||||||
|
|
||||||
def unset(self, name: str, *, pattern: urlmatch.UrlPattern = None) -> None:
|
def unset(self, name, *, pattern=None):
|
||||||
"""Remove the given option name if it's configured."""
|
"""Remove the given option name if it's configured."""
|
||||||
changed = self._values[name].remove(pattern)
|
changed = self._values[name].remove(pattern)
|
||||||
if changed:
|
if changed:
|
||||||
self._mark_changed()
|
self._mark_changed()
|
||||||
|
|
||||||
def clear(self) -> None:
|
def clear(self):
|
||||||
"""Clear all values from the YAML file."""
|
"""Clear all values from the YAML file."""
|
||||||
for values in self._values.values():
|
for values in self._values.values():
|
||||||
values.clear()
|
values.clear()
|
||||||
@ -362,15 +346,15 @@ class ConfigAPI:
|
|||||||
datadir: The qutebrowser data directory, as pathlib.Path.
|
datadir: The qutebrowser data directory, as pathlib.Path.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, conf: config.Config, keyconfig: config.KeyConfig):
|
def __init__(self, conf, keyconfig):
|
||||||
self._config = conf
|
self._config = conf
|
||||||
self._keyconfig = keyconfig
|
self._keyconfig = keyconfig
|
||||||
self.errors = [] # type: typing.List[configexc.ConfigErrorDesc]
|
self.errors = []
|
||||||
self.configdir = pathlib.Path(standarddir.config())
|
self.configdir = pathlib.Path(standarddir.config())
|
||||||
self.datadir = pathlib.Path(standarddir.data())
|
self.datadir = pathlib.Path(standarddir.data())
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def _handle_error(self, action: str, name: str) -> typing.Iterator[None]:
|
def _handle_error(self, action, name):
|
||||||
"""Catch config-related exceptions and save them in self.errors."""
|
"""Catch config-related exceptions and save them in self.errors."""
|
||||||
try:
|
try:
|
||||||
yield
|
yield
|
||||||
@ -388,40 +372,40 @@ class ConfigAPI:
|
|||||||
text = "While {} '{}' and parsing key".format(action, name)
|
text = "While {} '{}' and parsing key".format(action, name)
|
||||||
self.errors.append(configexc.ConfigErrorDesc(text, e))
|
self.errors.append(configexc.ConfigErrorDesc(text, e))
|
||||||
|
|
||||||
def finalize(self) -> None:
|
def finalize(self):
|
||||||
"""Do work which needs to be done after reading config.py."""
|
"""Do work which needs to be done after reading config.py."""
|
||||||
self._config.update_mutables()
|
self._config.update_mutables()
|
||||||
|
|
||||||
def load_autoconfig(self) -> None:
|
def load_autoconfig(self):
|
||||||
"""Load the autoconfig.yml file which is used for :set/:bind/etc."""
|
"""Load the autoconfig.yml file which is used for :set/:bind/etc."""
|
||||||
with self._handle_error('reading', 'autoconfig.yml'):
|
with self._handle_error('reading', 'autoconfig.yml'):
|
||||||
read_autoconfig()
|
read_autoconfig()
|
||||||
|
|
||||||
def get(self, name: str, pattern: str = None) -> typing.Any:
|
def get(self, name, pattern=None):
|
||||||
"""Get a setting value from the config, optionally with a pattern."""
|
"""Get a setting value from the config, optionally with a pattern."""
|
||||||
with self._handle_error('getting', name):
|
with self._handle_error('getting', name):
|
||||||
urlpattern = urlmatch.UrlPattern(pattern) if pattern else None
|
urlpattern = urlmatch.UrlPattern(pattern) if pattern else None
|
||||||
return self._config.get_mutable_obj(name, pattern=urlpattern)
|
return self._config.get_mutable_obj(name, pattern=urlpattern)
|
||||||
|
|
||||||
def set(self, name: str, value: typing.Any, pattern: str = None) -> None:
|
def set(self, name, value, pattern=None):
|
||||||
"""Set a setting value in the config, optionally with a pattern."""
|
"""Set a setting value in the config, optionally with a pattern."""
|
||||||
with self._handle_error('setting', name):
|
with self._handle_error('setting', name):
|
||||||
urlpattern = urlmatch.UrlPattern(pattern) if pattern else None
|
urlpattern = urlmatch.UrlPattern(pattern) if pattern else None
|
||||||
self._config.set_obj(name, value, pattern=urlpattern)
|
self._config.set_obj(name, value, pattern=urlpattern)
|
||||||
|
|
||||||
def bind(self, key: str, command: str, mode: str = 'normal') -> None:
|
def bind(self, key, command, mode='normal'):
|
||||||
"""Bind a key to a command, with an optional key mode."""
|
"""Bind a key to a command, with an optional key mode."""
|
||||||
with self._handle_error('binding', key):
|
with self._handle_error('binding', key):
|
||||||
seq = keyutils.KeySequence.parse(key)
|
seq = keyutils.KeySequence.parse(key)
|
||||||
self._keyconfig.bind(seq, command, mode=mode)
|
self._keyconfig.bind(seq, command, mode=mode)
|
||||||
|
|
||||||
def unbind(self, key: str, mode: str = 'normal') -> None:
|
def unbind(self, key, mode='normal'):
|
||||||
"""Unbind a key from a command, with an optional key mode."""
|
"""Unbind a key from a command, with an optional key mode."""
|
||||||
with self._handle_error('unbinding', key):
|
with self._handle_error('unbinding', key):
|
||||||
seq = keyutils.KeySequence.parse(key)
|
seq = keyutils.KeySequence.parse(key)
|
||||||
self._keyconfig.unbind(seq, mode=mode)
|
self._keyconfig.unbind(seq, mode=mode)
|
||||||
|
|
||||||
def source(self, filename: str) -> None:
|
def source(self, filename):
|
||||||
"""Read the given config file from disk."""
|
"""Read the given config file from disk."""
|
||||||
if not os.path.isabs(filename):
|
if not os.path.isabs(filename):
|
||||||
filename = str(self.configdir / filename)
|
filename = str(self.configdir / filename)
|
||||||
@ -432,7 +416,7 @@ class ConfigAPI:
|
|||||||
self.errors += e.errors
|
self.errors += e.errors
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def pattern(self, pattern: str) -> typing.Iterator[config.ConfigContainer]:
|
def pattern(self, pattern):
|
||||||
"""Get a ConfigContainer for the given pattern."""
|
"""Get a ConfigContainer for the given pattern."""
|
||||||
# We need to propagate the exception so we don't need to return
|
# We need to propagate the exception so we don't need to return
|
||||||
# something.
|
# something.
|
||||||
@ -446,21 +430,17 @@ class ConfigPyWriter:
|
|||||||
|
|
||||||
"""Writer for config.py files from given settings."""
|
"""Writer for config.py files from given settings."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(self, options, bindings, *, commented):
|
||||||
self,
|
|
||||||
options: typing.List,
|
|
||||||
bindings: typing.MutableMapping[str, typing.Mapping[str, str]], *,
|
|
||||||
commented: bool) -> None:
|
|
||||||
self._options = options
|
self._options = options
|
||||||
self._bindings = bindings
|
self._bindings = bindings
|
||||||
self._commented = commented
|
self._commented = commented
|
||||||
|
|
||||||
def write(self, filename: str) -> None:
|
def write(self, filename):
|
||||||
"""Write the config to the given file."""
|
"""Write the config to the given file."""
|
||||||
with open(filename, 'w', encoding='utf-8') as f:
|
with open(filename, 'w', encoding='utf-8') as f:
|
||||||
f.write('\n'.join(self._gen_lines()))
|
f.write('\n'.join(self._gen_lines()))
|
||||||
|
|
||||||
def _line(self, line: str) -> str:
|
def _line(self, line):
|
||||||
"""Get an (optionally commented) line."""
|
"""Get an (optionally commented) line."""
|
||||||
if self._commented:
|
if self._commented:
|
||||||
if line.startswith('#'):
|
if line.startswith('#'):
|
||||||
@ -470,7 +450,7 @@ class ConfigPyWriter:
|
|||||||
else:
|
else:
|
||||||
return line
|
return line
|
||||||
|
|
||||||
def _gen_lines(self) -> typing.Iterator[str]:
|
def _gen_lines(self):
|
||||||
"""Generate a config.py with the given settings/bindings.
|
"""Generate a config.py with the given settings/bindings.
|
||||||
|
|
||||||
Yields individual lines.
|
Yields individual lines.
|
||||||
@ -479,7 +459,7 @@ class ConfigPyWriter:
|
|||||||
yield from self._gen_options()
|
yield from self._gen_options()
|
||||||
yield from self._gen_bindings()
|
yield from self._gen_bindings()
|
||||||
|
|
||||||
def _gen_header(self) -> typing.Iterator[str]:
|
def _gen_header(self):
|
||||||
"""Generate the initial header of the config."""
|
"""Generate the initial header of the config."""
|
||||||
yield self._line("# Autogenerated config.py")
|
yield self._line("# Autogenerated config.py")
|
||||||
yield self._line("# Documentation:")
|
yield self._line("# Documentation:")
|
||||||
@ -501,7 +481,7 @@ class ConfigPyWriter:
|
|||||||
yield self._line("# config.load_autoconfig()")
|
yield self._line("# config.load_autoconfig()")
|
||||||
yield ''
|
yield ''
|
||||||
|
|
||||||
def _gen_options(self) -> typing.Iterator[str]:
|
def _gen_options(self):
|
||||||
"""Generate the options part of the config."""
|
"""Generate the options part of the config."""
|
||||||
for pattern, opt, value in self._options:
|
for pattern, opt, value in self._options:
|
||||||
if opt.name in ['bindings.commands', 'bindings.default']:
|
if opt.name in ['bindings.commands', 'bindings.default']:
|
||||||
@ -529,7 +509,7 @@ class ConfigPyWriter:
|
|||||||
opt.name, value, str(pattern)))
|
opt.name, value, str(pattern)))
|
||||||
yield ''
|
yield ''
|
||||||
|
|
||||||
def _gen_bindings(self) -> typing.Iterator[str]:
|
def _gen_bindings(self):
|
||||||
"""Generate the bindings part of the config."""
|
"""Generate the bindings part of the config."""
|
||||||
normal_bindings = self._bindings.pop('normal', {})
|
normal_bindings = self._bindings.pop('normal', {})
|
||||||
if normal_bindings:
|
if normal_bindings:
|
||||||
@ -547,7 +527,7 @@ class ConfigPyWriter:
|
|||||||
yield ''
|
yield ''
|
||||||
|
|
||||||
|
|
||||||
def read_config_py(filename: str, raising: bool = False) -> None:
|
def read_config_py(filename, raising=False):
|
||||||
"""Read a config.py file.
|
"""Read a config.py file.
|
||||||
|
|
||||||
Arguments;
|
Arguments;
|
||||||
@ -563,8 +543,8 @@ def read_config_py(filename: str, raising: bool = False) -> None:
|
|||||||
basename = os.path.basename(filename)
|
basename = os.path.basename(filename)
|
||||||
|
|
||||||
module = types.ModuleType('config')
|
module = types.ModuleType('config')
|
||||||
module.config = api # type: ignore
|
module.config = api
|
||||||
module.c = container # type: ignore
|
module.c = container
|
||||||
module.__file__ = filename
|
module.__file__ = filename
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -609,7 +589,7 @@ def read_config_py(filename: str, raising: bool = False) -> None:
|
|||||||
raise configexc.ConfigFileErrors('config.py', api.errors)
|
raise configexc.ConfigFileErrors('config.py', api.errors)
|
||||||
|
|
||||||
|
|
||||||
def read_autoconfig() -> None:
|
def read_autoconfig():
|
||||||
"""Read the autoconfig.yml file."""
|
"""Read the autoconfig.yml file."""
|
||||||
try:
|
try:
|
||||||
config.instance.read_yaml()
|
config.instance.read_yaml()
|
||||||
@ -621,7 +601,7 @@ def read_autoconfig() -> None:
|
|||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def saved_sys_properties() -> typing.Iterator[None]:
|
def saved_sys_properties():
|
||||||
"""Save various sys properties such as sys.path and sys.modules."""
|
"""Save various sys properties such as sys.path and sys.modules."""
|
||||||
old_path = sys.path.copy()
|
old_path = sys.path.copy()
|
||||||
old_modules = sys.modules.copy()
|
old_modules = sys.modules.copy()
|
||||||
@ -634,7 +614,7 @@ def saved_sys_properties() -> typing.Iterator[None]:
|
|||||||
del sys.modules[module]
|
del sys.modules[module]
|
||||||
|
|
||||||
|
|
||||||
def init() -> None:
|
def init():
|
||||||
"""Initialize config storage not related to the main config."""
|
"""Initialize config storage not related to the main config."""
|
||||||
global state
|
global state
|
||||||
state = StateConfig()
|
state = StateConfig()
|
||||||
|
@ -19,10 +19,8 @@
|
|||||||
|
|
||||||
"""Initialization of the configuration."""
|
"""Initialization of the configuration."""
|
||||||
|
|
||||||
import argparse
|
|
||||||
import os.path
|
import os.path
|
||||||
import sys
|
import sys
|
||||||
import typing
|
|
||||||
|
|
||||||
from PyQt5.QtWidgets import QMessageBox
|
from PyQt5.QtWidgets import QMessageBox
|
||||||
|
|
||||||
@ -32,14 +30,14 @@ from qutebrowser.config import (config, configdata, configfiles, configtypes,
|
|||||||
from qutebrowser.utils import (objreg, usertypes, log, standarddir, message,
|
from qutebrowser.utils import (objreg, usertypes, log, standarddir, message,
|
||||||
qtutils)
|
qtutils)
|
||||||
from qutebrowser.config import configcache
|
from qutebrowser.config import configcache
|
||||||
from qutebrowser.misc import msgbox, objects, savemanager
|
from qutebrowser.misc import msgbox, objects
|
||||||
|
|
||||||
|
|
||||||
# Error which happened during init, so we can show a message box.
|
# Error which happened during init, so we can show a message box.
|
||||||
_init_errors = None
|
_init_errors = None
|
||||||
|
|
||||||
|
|
||||||
def early_init(args: argparse.Namespace) -> None:
|
def early_init(args):
|
||||||
"""Initialize the part of the config which works without a QApplication."""
|
"""Initialize the part of the config which works without a QApplication."""
|
||||||
configdata.init()
|
configdata.init()
|
||||||
|
|
||||||
@ -87,7 +85,7 @@ def early_init(args: argparse.Namespace) -> None:
|
|||||||
_init_envvars()
|
_init_envvars()
|
||||||
|
|
||||||
|
|
||||||
def _init_envvars() -> None:
|
def _init_envvars():
|
||||||
"""Initialize environment variables which need to be set early."""
|
"""Initialize environment variables which need to be set early."""
|
||||||
if objects.backend == usertypes.Backend.QtWebEngine:
|
if objects.backend == usertypes.Backend.QtWebEngine:
|
||||||
software_rendering = config.val.qt.force_software_rendering
|
software_rendering = config.val.qt.force_software_rendering
|
||||||
@ -109,7 +107,7 @@ def _init_envvars() -> None:
|
|||||||
|
|
||||||
|
|
||||||
@config.change_filter('fonts.monospace', function=True)
|
@config.change_filter('fonts.monospace', function=True)
|
||||||
def _update_monospace_fonts() -> None:
|
def _update_monospace_fonts():
|
||||||
"""Update all fonts if fonts.monospace was set."""
|
"""Update all fonts if fonts.monospace was set."""
|
||||||
configtypes.Font.monospace_fonts = config.val.fonts.monospace
|
configtypes.Font.monospace_fonts = config.val.fonts.monospace
|
||||||
for name, opt in configdata.DATA.items():
|
for name, opt in configdata.DATA.items():
|
||||||
@ -125,7 +123,7 @@ def _update_monospace_fonts() -> None:
|
|||||||
config.instance.changed.emit(name)
|
config.instance.changed.emit(name)
|
||||||
|
|
||||||
|
|
||||||
def get_backend(args: argparse.Namespace) -> usertypes.Backend:
|
def get_backend(args):
|
||||||
"""Find out what backend to use based on available libraries."""
|
"""Find out what backend to use based on available libraries."""
|
||||||
str_to_backend = {
|
str_to_backend = {
|
||||||
'webkit': usertypes.Backend.QtWebKit,
|
'webkit': usertypes.Backend.QtWebKit,
|
||||||
@ -138,7 +136,7 @@ def get_backend(args: argparse.Namespace) -> usertypes.Backend:
|
|||||||
return str_to_backend[config.val.backend]
|
return str_to_backend[config.val.backend]
|
||||||
|
|
||||||
|
|
||||||
def late_init(save_manager: savemanager.SaveManager) -> None:
|
def late_init(save_manager):
|
||||||
"""Initialize the rest of the config after the QApplication is created."""
|
"""Initialize the rest of the config after the QApplication is created."""
|
||||||
global _init_errors
|
global _init_errors
|
||||||
if _init_errors is not None:
|
if _init_errors is not None:
|
||||||
@ -154,7 +152,7 @@ def late_init(save_manager: savemanager.SaveManager) -> None:
|
|||||||
configfiles.state.init_save_manager(save_manager)
|
configfiles.state.init_save_manager(save_manager)
|
||||||
|
|
||||||
|
|
||||||
def qt_args(namespace: argparse.Namespace) -> typing.List[str]:
|
def qt_args(namespace):
|
||||||
"""Get the Qt QApplication arguments based on an argparse namespace.
|
"""Get the Qt QApplication arguments based on an argparse namespace.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -180,7 +178,7 @@ def qt_args(namespace: argparse.Namespace) -> typing.List[str]:
|
|||||||
return argv
|
return argv
|
||||||
|
|
||||||
|
|
||||||
def _qtwebengine_args() -> typing.Iterator[str]:
|
def _qtwebengine_args():
|
||||||
"""Get the QtWebEngine arguments to use based on the config."""
|
"""Get the QtWebEngine arguments to use based on the config."""
|
||||||
if not qtutils.version_check('5.11', compiled=False):
|
if not qtutils.version_check('5.11', compiled=False):
|
||||||
# WORKAROUND equivalent to
|
# WORKAROUND equivalent to
|
||||||
@ -226,7 +224,7 @@ def _qtwebengine_args() -> typing.Iterator[str]:
|
|||||||
'never': '--no-referrers',
|
'never': '--no-referrers',
|
||||||
'same-domain': '--reduced-referrer-granularity',
|
'same-domain': '--reduced-referrer-granularity',
|
||||||
}
|
}
|
||||||
} # type: typing.Dict[str, typing.Dict[typing.Any, typing.Optional[str]]]
|
}
|
||||||
|
|
||||||
if not qtutils.version_check('5.11'):
|
if not qtutils.version_check('5.11'):
|
||||||
# On Qt 5.11, we can control this via QWebEngineSettings
|
# On Qt 5.11, we can control this via QWebEngineSettings
|
||||||
|
@ -21,19 +21,11 @@
|
|||||||
"""Utilities and data structures used by various config code."""
|
"""Utilities and data structures used by various config code."""
|
||||||
|
|
||||||
|
|
||||||
import typing
|
|
||||||
|
|
||||||
import attr
|
import attr
|
||||||
from PyQt5.QtCore import QUrl
|
|
||||||
|
|
||||||
from qutebrowser.utils import utils, urlmatch
|
from qutebrowser.utils import utils
|
||||||
from qutebrowser.config import configexc
|
from qutebrowser.config import configexc
|
||||||
|
|
||||||
MYPY = False
|
|
||||||
if MYPY: # pragma: no cover
|
|
||||||
# pylint: disable=unused-import,useless-suppression
|
|
||||||
from qutebrowser.config import configdata
|
|
||||||
|
|
||||||
|
|
||||||
class _UnsetObject:
|
class _UnsetObject:
|
||||||
|
|
||||||
@ -41,7 +33,7 @@ class _UnsetObject:
|
|||||||
|
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self):
|
||||||
return '<UNSET>'
|
return '<UNSET>'
|
||||||
|
|
||||||
|
|
||||||
@ -58,8 +50,8 @@ class ScopedValue:
|
|||||||
pattern: The UrlPattern for the value, or None for global values.
|
pattern: The UrlPattern for the value, or None for global values.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
value = attr.ib() # type: typing.Any
|
value = attr.ib()
|
||||||
pattern = attr.ib() # type: typing.Optional[urlmatch.UrlPattern]
|
pattern = attr.ib()
|
||||||
|
|
||||||
|
|
||||||
class Values:
|
class Values:
|
||||||
@ -81,17 +73,15 @@ class Values:
|
|||||||
opt: The Option being customized.
|
opt: The Option being customized.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self, opt, values=None):
|
||||||
opt: 'configdata.Option',
|
|
||||||
values: typing.MutableSequence = None) -> None:
|
|
||||||
self.opt = opt
|
self.opt = opt
|
||||||
self._values = values or []
|
self._values = values or []
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self):
|
||||||
return utils.get_repr(self, opt=self.opt, values=self._values,
|
return utils.get_repr(self, opt=self.opt, values=self._values,
|
||||||
constructor=True)
|
constructor=True)
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self):
|
||||||
"""Get the values as human-readable string."""
|
"""Get the values as human-readable string."""
|
||||||
if not self:
|
if not self:
|
||||||
return '{}: <unchanged>'.format(self.opt.name)
|
return '{}: <unchanged>'.format(self.opt.name)
|
||||||
@ -106,7 +96,7 @@ class Values:
|
|||||||
scoped.pattern, self.opt.name, str_value))
|
scoped.pattern, self.opt.name, str_value))
|
||||||
return '\n'.join(lines)
|
return '\n'.join(lines)
|
||||||
|
|
||||||
def __iter__(self) -> typing.Iterator['ScopedValue']:
|
def __iter__(self):
|
||||||
"""Yield ScopedValue elements.
|
"""Yield ScopedValue elements.
|
||||||
|
|
||||||
This yields in "normal" order, i.e. global and then first-set settings
|
This yields in "normal" order, i.e. global and then first-set settings
|
||||||
@ -114,25 +104,23 @@ class Values:
|
|||||||
"""
|
"""
|
||||||
yield from self._values
|
yield from self._values
|
||||||
|
|
||||||
def __bool__(self) -> bool:
|
def __bool__(self):
|
||||||
"""Check whether this value is customized."""
|
"""Check whether this value is customized."""
|
||||||
return bool(self._values)
|
return bool(self._values)
|
||||||
|
|
||||||
def _check_pattern_support(
|
def _check_pattern_support(self, arg):
|
||||||
self, arg: typing.Optional[urlmatch.UrlPattern]) -> None:
|
|
||||||
"""Make sure patterns are supported if one was given."""
|
"""Make sure patterns are supported if one was given."""
|
||||||
if arg is not None and not self.opt.supports_pattern:
|
if arg is not None and not self.opt.supports_pattern:
|
||||||
raise configexc.NoPatternError(self.opt.name)
|
raise configexc.NoPatternError(self.opt.name)
|
||||||
|
|
||||||
def add(self, value: typing.Any,
|
def add(self, value, pattern=None):
|
||||||
pattern: urlmatch.UrlPattern = None) -> None:
|
|
||||||
"""Add a value with the given pattern to the list of values."""
|
"""Add a value with the given pattern to the list of values."""
|
||||||
self._check_pattern_support(pattern)
|
self._check_pattern_support(pattern)
|
||||||
self.remove(pattern)
|
self.remove(pattern)
|
||||||
scoped = ScopedValue(value, pattern)
|
scoped = ScopedValue(value, pattern)
|
||||||
self._values.append(scoped)
|
self._values.append(scoped)
|
||||||
|
|
||||||
def remove(self, pattern: urlmatch.UrlPattern = None) -> bool:
|
def remove(self, pattern=None):
|
||||||
"""Remove the value with the given pattern.
|
"""Remove the value with the given pattern.
|
||||||
|
|
||||||
If a matching pattern was removed, True is returned.
|
If a matching pattern was removed, True is returned.
|
||||||
@ -143,11 +131,11 @@ class Values:
|
|||||||
self._values = [v for v in self._values if v.pattern != pattern]
|
self._values = [v for v in self._values if v.pattern != pattern]
|
||||||
return old_len != len(self._values)
|
return old_len != len(self._values)
|
||||||
|
|
||||||
def clear(self) -> None:
|
def clear(self):
|
||||||
"""Clear all customization for this value."""
|
"""Clear all customization for this value."""
|
||||||
self._values = []
|
self._values = []
|
||||||
|
|
||||||
def _get_fallback(self, fallback: typing.Any) -> typing.Any:
|
def _get_fallback(self, fallback):
|
||||||
"""Get the fallback global/default value."""
|
"""Get the fallback global/default value."""
|
||||||
for scoped in self._values:
|
for scoped in self._values:
|
||||||
if scoped.pattern is None:
|
if scoped.pattern is None:
|
||||||
@ -158,8 +146,7 @@ class Values:
|
|||||||
else:
|
else:
|
||||||
return UNSET
|
return UNSET
|
||||||
|
|
||||||
def get_for_url(self, url: QUrl = None, *,
|
def get_for_url(self, url=None, *, fallback=True):
|
||||||
fallback: bool = True) -> typing.Any:
|
|
||||||
"""Get a config value, falling back when needed.
|
"""Get a config value, falling back when needed.
|
||||||
|
|
||||||
This first tries to find a value matching the URL (if given).
|
This first tries to find a value matching the URL (if given).
|
||||||
@ -178,9 +165,7 @@ class Values:
|
|||||||
|
|
||||||
return self._get_fallback(fallback)
|
return self._get_fallback(fallback)
|
||||||
|
|
||||||
def get_for_pattern(self,
|
def get_for_pattern(self, pattern, *, fallback=True):
|
||||||
pattern: typing.Optional[urlmatch.UrlPattern], *,
|
|
||||||
fallback: bool = True) -> typing.Any:
|
|
||||||
"""Get a value only if it's been overridden for the given pattern.
|
"""Get a value only if it's been overridden for the given pattern.
|
||||||
|
|
||||||
This is useful when showing values to the user.
|
This is useful when showing values to the user.
|
||||||
|
Loading…
Reference in New Issue
Block a user