Make it possible for extensions to define init hooks

This commit is contained in:
Florian Bruhin 2018-12-10 12:30:47 +01:00
parent ec5a93a80d
commit 5b354164c5
5 changed files with 72 additions and 3 deletions

View File

@ -24,3 +24,4 @@ from qutebrowser.browser.browsertab import WebTabError, AbstractTab as Tab
from qutebrowser.browser.webelem import (Error as WebElemError, from qutebrowser.browser.webelem import (Error as WebElemError,
AbstractWebElement as WebElement) AbstractWebElement as WebElement)
from qutebrowser.utils.usertypes import ClickTarget, JsWorld from qutebrowser.utils.usertypes import ClickTarget, JsWorld
from qutebrowser.extensions.loader import InitContext

35
qutebrowser/api/hook.py Normal file
View File

@ -0,0 +1,35 @@
# Copyright 2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
#
# This file is part of qutebrowser.
#
# qutebrowser is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# qutebrowser is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
"""Hooks for extensions."""
import importlib
import types
import typing
from qutebrowser.extensions import loader
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.init_hook = func

View File

@ -17,4 +17,4 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. # along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
"""qutebrowser "extensions" which only use the qutebrowser.API API.""" """qutebrowser "extensions" which only use the qutebrowser.api API."""

View File

@ -20,7 +20,7 @@
"""Commands related to caret browsing.""" """Commands related to caret browsing."""
from qutebrowser.api import cmdutils, apitypes from qutebrowser.api import cmdutils, apitypes, hook
@cmdutils.register(modes=[cmdutils.KeyMode.caret]) @cmdutils.register(modes=[cmdutils.KeyMode.caret])

View File

@ -31,6 +31,23 @@ from qutebrowser import components
from qutebrowser.utils import log from qutebrowser.utils import log
@attr.s
class InitContext:
"""Context an extension gets in its init hook."""
@attr.s
class ModuleInfo:
"""Information attached to an extension module.
This gets used by qutebrowser.api.hook.
"""
init_hook = attr.ib(None) # type: typing.Optional[typing.Callable]
@attr.s @attr.s
class ExtensionInfo: class ExtensionInfo:
@ -39,6 +56,13 @@ class ExtensionInfo:
name = attr.ib() # type: str name = attr.ib() # type: str
def add_module_info(module: types.ModuleType) -> ModuleInfo:
"""Add ModuleInfo to a module (if not added yet)."""
if not hasattr(module, '__qute_module_info'):
module.__qute_module_info = ModuleInfo()
return module.__qute_module_info
def load_components() -> None: def load_components() -> None:
"""Load everything from qutebrowser.components.""" """Load everything from qutebrowser.components."""
for info in walk_components(): for info in walk_components():
@ -88,5 +112,14 @@ def _walk_pyinstaller() -> typing.Iterator[ExtensionInfo]:
def _load_component(info: ExtensionInfo) -> types.ModuleType: def _load_component(info: ExtensionInfo) -> types.ModuleType:
"""Load the given extension and run its init hook (if any)."""
log.extensions.debug("Importing {}".format(info.name)) log.extensions.debug("Importing {}".format(info.name))
return importlib.import_module(info.name) mod = importlib.import_module(info.name)
info = add_module_info(mod)
if info.init_hook is not None:
log.extensions.debug("Running init hook {!r}"
.format(info.init_hook.__name__))
info.init_hook(InitContext())
return mod