18
18
"""Support for plugin hooking logic."""
19
from bzrlib import registry
24
20
from bzrlib.lazy_import import lazy_import
25
21
lazy_import(globals(), """
28
24
from bzrlib import (
29
_format_version_tuple,
33
from bzrlib.i18n import gettext
25
_format_version_tuple,
28
from bzrlib.help_topics import help_as_plain_text
37
class KnownHooksRegistry(registry.Registry):
38
# known_hooks registry contains
39
# tuple of (module, member name) which is the hook point
40
# module where the specific hooks are defined
41
# callable to get the empty specific Hooks for that attribute
43
def register_lazy_hook(self, hook_module_name, hook_member_name,
44
hook_factory_member_name):
45
self.register_lazy((hook_module_name, hook_member_name),
46
hook_module_name, hook_factory_member_name)
48
def iter_parent_objects(self):
49
"""Yield (hook_key, (parent_object, attr)) tuples for every registered
50
hook, where 'parent_object' is the object that holds the hook
53
This is useful for resetting/restoring all the hooks to a known state,
54
as is done in bzrlib.tests.TestCase._clear_hooks.
56
for key in self.keys():
57
yield key, self.key_to_parent_and_attribute(key)
59
def key_to_parent_and_attribute(self, (module_name, member_name)):
60
"""Convert a known_hooks key to a (parent_obj, attr) pair.
62
:param key: A tuple (module_name, member_name) as found in the keys of
63
the known_hooks registry.
64
:return: The parent_object of the hook and the name of the attribute on
65
that parent object where the hook is kept.
67
parent_mod, parent_member, attr = pyutils.calc_parent_name(module_name,
69
return pyutils.get_named_object(parent_mod, parent_member), attr
72
_builtin_known_hooks = (
73
('bzrlib.branch', 'Branch.hooks', 'BranchHooks'),
74
('bzrlib.bzrdir', 'BzrDir.hooks', 'BzrDirHooks'),
75
('bzrlib.commands', 'Command.hooks', 'CommandHooks'),
76
('bzrlib.config', 'ConfigHooks', '_ConfigHooks'),
77
('bzrlib.info', 'hooks', 'InfoHooks'),
78
('bzrlib.lock', 'Lock.hooks', 'LockHooks'),
79
('bzrlib.merge', 'Merger.hooks', 'MergeHooks'),
80
('bzrlib.msgeditor', 'hooks', 'MessageEditorHooks'),
81
('bzrlib.mutabletree', 'MutableTree.hooks', 'MutableTreeHooks'),
82
('bzrlib.smart.client', '_SmartClient.hooks', 'SmartClientHooks'),
83
('bzrlib.smart.server', 'SmartTCPServer.hooks', 'SmartServerHooks'),
84
('bzrlib.status', 'hooks', 'StatusHooks'),
85
('bzrlib.version_info_formats.format_rio', 'RioVersionInfoBuilder.hooks',
86
'RioVersionInfoBuilderHooks'),
87
('bzrlib.merge_directive', 'BaseMergeDirective.hooks',
88
'MergeDirectiveHooks'),
91
known_hooks = KnownHooksRegistry()
92
for (_hook_module, _hook_attribute, _hook_class) in _builtin_known_hooks:
93
known_hooks.register_lazy_hook(_hook_module, _hook_attribute, _hook_class)
94
del _builtin_known_hooks, _hook_module, _hook_attribute, _hook_class
32
known_hooks = registry.Registry()
33
# known_hooks registry contains
34
# tuple of (module, member name) which is the hook point
35
# module where the specific hooks are defined
36
# callable to get the empty specific Hooks for that attribute
37
known_hooks.register_lazy(('bzrlib.branch', 'Branch.hooks'), 'bzrlib.branch',
39
known_hooks.register_lazy(('bzrlib.bzrdir', 'BzrDir.hooks'), 'bzrlib.bzrdir',
41
known_hooks.register_lazy(('bzrlib.commands', 'Command.hooks'),
42
'bzrlib.commands', 'CommandHooks')
43
known_hooks.register_lazy(('bzrlib.info', 'hooks'),
44
'bzrlib.info', 'InfoHooks')
45
known_hooks.register_lazy(('bzrlib.lock', 'Lock.hooks'), 'bzrlib.lock',
47
known_hooks.register_lazy(('bzrlib.merge', 'Merger.hooks'), 'bzrlib.merge',
49
known_hooks.register_lazy(('bzrlib.msgeditor', 'hooks'), 'bzrlib.msgeditor',
51
known_hooks.register_lazy(('bzrlib.mutabletree', 'MutableTree.hooks'),
52
'bzrlib.mutabletree', 'MutableTreeHooks')
53
known_hooks.register_lazy(('bzrlib.smart.client', '_SmartClient.hooks'),
54
'bzrlib.smart.client', 'SmartClientHooks')
55
known_hooks.register_lazy(('bzrlib.smart.server', 'SmartTCPServer.hooks'),
56
'bzrlib.smart.server', 'SmartServerHooks')
57
known_hooks.register_lazy(('bzrlib.status', 'hooks'),
58
'bzrlib.status', 'StatusHooks')
59
known_hooks.register_lazy(
60
('bzrlib.version_info_formats.format_rio', 'RioVersionInfoBuilder.hooks'),
61
'bzrlib.version_info_formats.format_rio', 'RioVersionInfoBuilderHooks')
62
known_hooks.register_lazy(
63
('bzrlib.merge_directive', 'BaseMergeDirective.hooks'),
64
'bzrlib.merge_directive', 'MergeDirectiveHooks')
97
67
def known_hooks_key_to_object((module_name, member_name)):
117
98
FOO hook is triggered.
120
def __init__(self, module=None, member_name=None):
121
"""Create a new hooks dictionary.
123
:param module: The module from which this hooks dictionary should be loaded
124
(used for lazy hooks)
125
:param member_name: Name under which this hooks dictionary should be loaded.
126
(used for lazy hooks)
128
102
dict.__init__(self)
129
103
self._callable_names = {}
130
self._module = module
131
self._member_name = member_name
133
def add_hook(self, name, doc, introduced, deprecated=None):
134
"""Add a hook point to this dictionary.
136
:param name: The name of the hook, for clients to use when registering.
137
:param doc: The docs for the hook.
138
:param introduced: When the hook was introduced (e.g. (0, 15)).
139
:param deprecated: When the hook was deprecated, None for
143
raise errors.DuplicateKey(name)
145
callbacks = _lazy_hooks.setdefault(
146
(self._module, self._member_name, name), [])
149
hookpoint = HookPoint(name=name, doc=doc, introduced=introduced,
150
deprecated=deprecated, callbacks=callbacks)
151
self[name] = hookpoint
153
@symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4)))
154
105
def create_hook(self, hook):
155
106
"""Create a hook which can have callbacks registered for it.
199
150
return self._callable_names.get(a_callable, "No hook name")
201
def install_named_hook_lazy(self, hook_name, callable_module,
202
callable_member, name):
203
"""Install a_callable in to the hook hook_name lazily, and label it.
205
:param hook_name: A hook name. See the __init__ method for the complete
207
:param callable_module: Name of the module in which the callable is
209
:param callable_member: Member name of the callable.
210
:param name: A name to associate the callable with, to show users what
214
hook = self[hook_name]
216
raise errors.UnknownHook(self.__class__.__name__, hook_name)
218
hook_lazy = getattr(hook, "hook_lazy")
219
except AttributeError:
220
raise errors.UnsupportedOperation(self.install_named_hook_lazy,
223
hook_lazy(callable_module, callable_member, name)
225
152
def install_named_hook(self, hook_name, a_callable, name):
226
153
"""Install a_callable in to the hook hook_name, and label it name.
228
:param hook_name: A hook name. See the __init__ method for the complete
155
:param hook_name: A hook name. See the __init__ method of BranchHooks
156
for the complete list of hooks.
230
157
:param a_callable: The callable to be invoked when the hook triggers.
231
158
The exact signature will depend on the hook - see the __init__
232
method for details on each hook.
159
method of BranchHooks for details on each hook.
233
160
:param name: A name to associate a_callable with, to show users what is