Use hook system for config_changed hook

This commit is contained in:
Florian Bruhin 2018-12-10 15:40:19 +01:00
parent 6001640a8a
commit 3b53270ee3
5 changed files with 51 additions and 28 deletions

View File

@ -24,8 +24,6 @@ import typing
from PyQt5.QtCore import QUrl
from qutebrowser.config import config
# pylint: disable=unused-import
from qutebrowser.config.config import change_filter
val = typing.cast('config.ConfigContainer', None)

View File

@ -26,14 +26,31 @@ import typing
from qutebrowser.extensions import loader
def _add_module_info(func: typing.Callable) -> loader.ModuleInfo:
"""Add module info to the given function."""
module = importlib.import_module(func.__module__)
return loader.add_module_info(module)
class init: # noqa: N801,N806 pylint: disable=invalid-name
"""Decorator to mark a function to run when initializing."""
def __call__(self, func: typing.Callable) -> typing.Callable:
module = importlib.import_module(func.__module__)
info = loader.add_module_info(module)
info = _add_module_info(func)
if info.init_hook is not None:
raise ValueError("init hook is already registered!")
info.init_hook = func
return func
class config_changed:
"""Decorator to get notified about changed configs."""
def __init__(self, option_filter=None):
self._filter = option_filter
def __call__(self, func: typing.Callable) -> typing.Callable:
info = _add_module_info(func)
info.config_changed_hooks.append((self._filter, func))

View File

@ -294,7 +294,6 @@ class HostBlocker:
message.info("adblock: Read {} hosts from {} sources.".format(
len(self._blocked_hosts), self._done_count))
@config.change_filter('content.host_blocking.lists')
def update_files(self):
"""Update files when the config changed."""
if not config.val.content.host_blocking.lists:
@ -337,6 +336,11 @@ def adblock_update():
_host_blocker.adblock_update()
@hook.config_changed('content.host_blocking.lists')
def on_config_changed():
_host_blocker.update_files()
@hook.init()
def init(context):
global _host_blocker
@ -344,6 +348,4 @@ def init(context):
config_dir=context.config_dir,
args=context.args)
_host_blocker.read_hosts()
context.signals.config_changed.connect(_host_blocker.update_files)
requests.register_filter(_host_blocker.filter_request)

View File

@ -86,7 +86,7 @@ class change_filter: # noqa: N801,N806 pylint: disable=invalid-name
not configdata.is_valid_prefix(self._option)):
raise configexc.NoOptionError(self._option)
def _check_match(self, option: typing.Optional[str]) -> bool:
def check_match(self, option: typing.Optional[str]) -> bool:
"""Check if the given option matches the filter."""
if option is None:
# Called directly, not from a config change event.
@ -119,7 +119,7 @@ class change_filter: # noqa: N801,N806 pylint: disable=invalid-name
@functools.wraps(func)
def func_wrapper(option: str = None) -> typing.Any:
"""Call the underlying function."""
if self._check_match(option):
if self.check_match(option):
return func()
return None
return func_wrapper
@ -128,7 +128,7 @@ class change_filter: # noqa: N801,N806 pylint: disable=invalid-name
def meth_wrapper(wrapper_self: typing.Any,
option: str = None) -> typing.Any:
"""Call the underlying function."""
if self._check_match(option):
if self.check_match(option):
return func(wrapper_self)
return None
return meth_wrapper

View File

@ -28,13 +28,17 @@ import pathlib
import attr
from PyQt5.QtCore import pyqtSignal, QObject
from PyQt5.QtCore import pyqtSlot, QObject
from qutebrowser import components
from qutebrowser.config import config
from qutebrowser.utils import log, standarddir, objreg
# ModuleInfo objects for all loaded plugins
_module_infos = []
@attr.s
class InitContext:
@ -43,14 +47,6 @@ class InitContext:
data_dir = attr.ib() # type: pathlib.Path
config_dir = attr.ib() # type: pathlib.Path
args = attr.ib() # type: argparse.Namespace
signals = attr.ib() # type: ExtensionSignals
class ExtensionSignals(QObject):
"""Signals exposed to an extension."""
config_changed = pyqtSignal(str)
@attr.s
@ -61,7 +57,11 @@ class ModuleInfo:
This gets used by qutebrowser.api.hook.
"""
_ConfigChangedHooksType = typing.List[typing.Tuple[str, typing.Callable]]
init_hook = attr.ib(None) # type: typing.Optional[typing.Callable]
config_changed_hooks = attr.ib(
attr.Factory(list)) # type: _ConfigChangedHooksType
@attr.s
@ -72,12 +72,6 @@ class ExtensionInfo:
name = attr.ib() # type: str
# Global extension signals, shared between all extensions.
# At some point we might want to make this per-extension, but then we'll need
# to find out what to set as its Qt parent so it's kept alive.
_extension_signals = ExtensionSignals()
def add_module_info(module: types.ModuleType) -> ModuleInfo:
"""Add ModuleInfo to a module (if not added yet)."""
# pylint: disable=protected-access
@ -138,8 +132,7 @@ def _get_init_context() -> InitContext:
"""Get an InitContext object."""
return InitContext(data_dir=pathlib.Path(standarddir.data()),
config_dir=pathlib.Path(standarddir.config()),
args=objreg.get('args'),
signals=_extension_signals)
args=objreg.get('args'))
def _load_component(info: ExtensionInfo) -> types.ModuleType:
@ -153,8 +146,21 @@ def _load_component(info: ExtensionInfo) -> types.ModuleType:
.format(mod_info.init_hook.__name__))
mod_info.init_hook(_get_init_context())
_module_infos.append(mod_info)
return mod
@pyqtSlot(str)
def _on_config_changed(changed_name: str) -> None:
"""Call config_changed hooks if the config changed."""
for mod_info in _module_infos:
for option, hook in mod_info.config_changed_hooks:
cfilter = config.change_filter(option)
cfilter.validate()
if cfilter.check_match(changed_name):
hook()
def init() -> None:
config.instance.changed.connect(_extension_signals.config_changed)
config.instance.changed.connect(_on_config_changed)