~bzr-pqm/bzr/bzr.dev

5752.3.8 by John Arbash Meinel
Merge bzr.dev 5764 to resolve release-notes (aka NEWS) conflicts
1
# Copyright (C) 2007-2011 Canonical Ltd
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
16
6379.6.7 by Jelmer Vernooij
Move importing from future until after doc string, otherwise the doc string will disappear.
17
"""Support for plugin hooking logic."""
18
6379.6.3 by Jelmer Vernooij
Use absolute_import.
19
from __future__ import absolute_import
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
20
5436.2.1 by Andrew Bennetts
Add bzrlib.pyutils, which has get_named_object, a wrapper around __import__.
21
from bzrlib import (
22
    registry,
23
    symbol_versioning,
24
    )
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
25
from bzrlib.lazy_import import lazy_import
26
lazy_import(globals(), """
4098.2.1 by Robert Collins
Allow self documenting hooks.
27
import textwrap
28
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
29
from bzrlib import (
5753.2.2 by Jelmer Vernooij
Remove some unnecessary imports, clean up lazy imports.
30
    _format_version_tuple,
31
    errors,
32
    pyutils,
33
    )
6150.3.7 by Jonathan Riddell
gettext() in plugin.py and hooks.py
34
from bzrlib.i18n import gettext
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
35
""")
36
37
5436.2.1 by Andrew Bennetts
Add bzrlib.pyutils, which has get_named_object, a wrapper around __import__.
38
class KnownHooksRegistry(registry.Registry):
39
    # known_hooks registry contains
40
    # tuple of (module, member name) which is the hook point
41
    # module where the specific hooks are defined
42
    # callable to get the empty specific Hooks for that attribute
43
44
    def register_lazy_hook(self, hook_module_name, hook_member_name,
45
            hook_factory_member_name):
46
        self.register_lazy((hook_module_name, hook_member_name),
47
            hook_module_name, hook_factory_member_name)
48
49
    def iter_parent_objects(self):
50
        """Yield (hook_key, (parent_object, attr)) tuples for every registered
51
        hook, where 'parent_object' is the object that holds the hook
52
        instance.
53
54
        This is useful for resetting/restoring all the hooks to a known state,
55
        as is done in bzrlib.tests.TestCase._clear_hooks.
56
        """
57
        for key in self.keys():
58
            yield key, self.key_to_parent_and_attribute(key)
59
60
    def key_to_parent_and_attribute(self, (module_name, member_name)):
61
        """Convert a known_hooks key to a (parent_obj, attr) pair.
62
63
        :param key: A tuple (module_name, member_name) as found in the keys of
64
            the known_hooks registry.
65
        :return: The parent_object of the hook and the name of the attribute on
66
            that parent object where the hook is kept.
67
        """
68
        parent_mod, parent_member, attr = pyutils.calc_parent_name(module_name,
69
            member_name)
70
        return pyutils.get_named_object(parent_mod, parent_member), attr
71
72
5436.2.5 by Andrew Bennetts
Tweak builtin known_hooks registration per poolie's review.
73
_builtin_known_hooks = (
74
    ('bzrlib.branch', 'Branch.hooks', 'BranchHooks'),
6207.3.2 by jelmer at samba
Move convenience methods to ControlDir.
75
    ('bzrlib.controldir', 'ControlDir.hooks', 'ControlDirHooks'),
5436.2.5 by Andrew Bennetts
Tweak builtin known_hooks registration per poolie's review.
76
    ('bzrlib.commands', 'Command.hooks', 'CommandHooks'),
5743.8.24 by Vincent Ladeuil
Clearly seaparate both sets of hooks for the old and new config implementations.
77
    ('bzrlib.config', 'ConfigHooks', '_ConfigHooks'),
5436.2.5 by Andrew Bennetts
Tweak builtin known_hooks registration per poolie's review.
78
    ('bzrlib.info', 'hooks', 'InfoHooks'),
79
    ('bzrlib.lock', 'Lock.hooks', 'LockHooks'),
80
    ('bzrlib.merge', 'Merger.hooks', 'MergeHooks'),
81
    ('bzrlib.msgeditor', 'hooks', 'MessageEditorHooks'),
82
    ('bzrlib.mutabletree', 'MutableTree.hooks', 'MutableTreeHooks'),
83
    ('bzrlib.smart.client', '_SmartClient.hooks', 'SmartClientHooks'),
84
    ('bzrlib.smart.server', 'SmartTCPServer.hooks', 'SmartServerHooks'),
85
    ('bzrlib.status', 'hooks', 'StatusHooks'),
5436.3.1 by Martin
Create new post_connect hook for transports
86
    ('bzrlib.transport', 'Transport.hooks', 'TransportHooks'),
5436.2.5 by Andrew Bennetts
Tweak builtin known_hooks registration per poolie's review.
87
    ('bzrlib.version_info_formats.format_rio', 'RioVersionInfoBuilder.hooks',
88
        'RioVersionInfoBuilderHooks'),
89
    ('bzrlib.merge_directive', 'BaseMergeDirective.hooks',
90
        'MergeDirectiveHooks'),
91
    )
92
5436.2.1 by Andrew Bennetts
Add bzrlib.pyutils, which has get_named_object, a wrapper around __import__.
93
known_hooks = KnownHooksRegistry()
5436.2.5 by Andrew Bennetts
Tweak builtin known_hooks registration per poolie's review.
94
for (_hook_module, _hook_attribute, _hook_class) in _builtin_known_hooks:
95
    known_hooks.register_lazy_hook(_hook_module, _hook_attribute, _hook_class)
96
del _builtin_known_hooks, _hook_module, _hook_attribute, _hook_class
4119.3.1 by Robert Collins
Create a single registry of all Hooks classes, removing the test suite knowledge of such hooks and allowing plugins to sensibly and safely define new hooks.
97
98
99
def known_hooks_key_to_object((module_name, member_name)):
100
    """Convert a known_hooks key to a object.
101
102
    :param key: A tuple (module_name, member_name) as found in the keys of
103
        the known_hooks registry.
104
    :return: The object this specifies.
105
    """
5436.2.1 by Andrew Bennetts
Add bzrlib.pyutils, which has get_named_object, a wrapper around __import__.
106
    return pyutils.get_named_object(module_name, member_name)
107
108
109
@symbol_versioning.deprecated_function(symbol_versioning.deprecated_in((2, 3)))
110
def known_hooks_key_to_parent_and_attribute(key):
111
    """See KnownHooksRegistry.key_to_parent_and_attribute."""
112
    return known_hooks.key_to_parent_and_attribute(key)
4119.3.1 by Robert Collins
Create a single registry of all Hooks classes, removing the test suite knowledge of such hooks and allowing plugins to sensibly and safely define new hooks.
113
114
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
115
class Hooks(dict):
116
    """A dictionary mapping hook name to a list of callables.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
117
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
118
    e.g. ['FOO'] Is the list of items to be called when the
119
    FOO hook is triggered.
120
    """
121
5622.3.11 by Jelmer Vernooij
Revert some unnecessary changes.
122
    def __init__(self, module=None, member_name=None):
5622.2.6 by Jelmer Vernooij
Put module/member information on Hooks, not individual hook points.
123
        """Create a new hooks dictionary.
124
125
        :param module: The module from which this hooks dictionary should be loaded
126
            (used for lazy hooks)
127
        :param member_name: Name under which this hooks dictionary should be loaded.
128
            (used for lazy hooks)
129
        """
2553.1.1 by Robert Collins
Give Hooks names.
130
        dict.__init__(self)
131
        self._callable_names = {}
6292.1.1 by Neil Martinsen-Burrell
try looking up names for lazy hooks with __module__ and __name__
132
        self._lazy_callable_names = {}
5622.2.6 by Jelmer Vernooij
Put module/member information on Hooks, not individual hook points.
133
        self._module = module
134
        self._member_name = member_name
135
6240.5.7 by Jelmer Vernooij
Drop exception suppression support.
136
    def add_hook(self, name, doc, introduced, deprecated=None):
5622.2.6 by Jelmer Vernooij
Put module/member information on Hooks, not individual hook points.
137
        """Add a hook point to this dictionary.
138
139
        :param name: The name of the hook, for clients to use when registering.
140
        :param doc: The docs for the hook.
141
        :param introduced: When the hook was introduced (e.g. (0, 15)).
142
        :param deprecated: When the hook was deprecated, None for
143
            not-deprecated.
144
        """
145
        if name in self:
146
            raise errors.DuplicateKey(name)
147
        if self._module:
148
            callbacks = _lazy_hooks.setdefault(
149
                (self._module, self._member_name, name), [])
150
        else:
151
            callbacks = None
152
        hookpoint = HookPoint(name=name, doc=doc, introduced=introduced,
6240.5.7 by Jelmer Vernooij
Drop exception suppression support.
153
                              deprecated=deprecated, callbacks=callbacks)
5622.2.6 by Jelmer Vernooij
Put module/member information on Hooks, not individual hook points.
154
        self[name] = hookpoint
2553.1.1 by Robert Collins
Give Hooks names.
155
5622.3.13 by Jelmer Vernooij
Catch deprecation warnings.
156
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4)))
4098.2.1 by Robert Collins
Allow self documenting hooks.
157
    def create_hook(self, hook):
158
        """Create a hook which can have callbacks registered for it.
159
160
        :param hook: The hook to create. An object meeting the protocol of
4098.2.2 by Robert Collins
Review feedback.
161
            bzrlib.hooks.HookPoint. It's name is used as the key for future
4098.2.1 by Robert Collins
Allow self documenting hooks.
162
            lookups.
163
        """
164
        if hook.name in self:
165
            raise errors.DuplicateKey(hook.name)
166
        self[hook.name] = hook
167
168
    def docs(self):
169
        """Generate the documentation for this Hooks instance.
170
171
        This introspects all the individual hooks and returns their docs as well.
172
        """
173
        hook_names = sorted(self.keys())
174
        hook_docs = []
4108.3.1 by Robert Collins
Include the Hooks class in the Hooks docs.
175
        name = self.__class__.__name__
176
        hook_docs.append(name)
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
177
        hook_docs.append("-"*len(name))
4108.3.1 by Robert Collins
Include the Hooks class in the Hooks docs.
178
        hook_docs.append("")
4098.2.1 by Robert Collins
Allow self documenting hooks.
179
        for hook_name in hook_names:
180
            hook = self[hook_name]
181
            try:
182
                hook_docs.append(hook.docs())
183
            except AttributeError:
184
                # legacy hook
185
                strings = []
186
                strings.append(hook_name)
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
187
                strings.append("~" * len(hook_name))
4098.2.1 by Robert Collins
Allow self documenting hooks.
188
                strings.append("")
189
                strings.append("An old-style hook. For documentation see the __init__ "
4108.3.1 by Robert Collins
Include the Hooks class in the Hooks docs.
190
                    "method of '%s'\n" % (name,))
4098.2.1 by Robert Collins
Allow self documenting hooks.
191
                hook_docs.extend(strings)
192
        return "\n".join(hook_docs)
193
2553.1.1 by Robert Collins
Give Hooks names.
194
    def get_hook_name(self, a_callable):
195
        """Get the name for a_callable for UI display.
196
197
        If no name has been registered, the string 'No hook name' is returned.
2553.1.3 by Robert Collins
Increase docs in response to review feedback.
198
        We use a fixed string rather than repr or the callables module because
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
199
        the code names are rarely meaningful for end users and this is not
2553.1.3 by Robert Collins
Increase docs in response to review feedback.
200
        intended for debugging.
2553.1.1 by Robert Collins
Give Hooks names.
201
        """
6292.1.1 by Neil Martinsen-Burrell
try looking up names for lazy hooks with __module__ and __name__
202
        name = self._callable_names.get(a_callable, None)
203
        if name is None and a_callable is not None:
204
            name = self._lazy_callable_names.get((a_callable.__module__,
205
                                                  a_callable.__name__),
206
                                                 None)
207
        if name is None:
208
            return 'No hook name'
209
        return name
210
2553.1.1 by Robert Collins
Give Hooks names.
211
5622.1.1 by Jelmer Vernooij
Allow lazily loading hook callbacks.
212
    def install_named_hook_lazy(self, hook_name, callable_module,
213
        callable_member, name):
214
        """Install a_callable in to the hook hook_name lazily, and label it.
215
5622.1.2 by Jelmer Vernooij
Remove BranchHooks references.
216
        :param hook_name: A hook name. See the __init__ method for the complete
217
            list of hooks.
5622.1.1 by Jelmer Vernooij
Allow lazily loading hook callbacks.
218
        :param callable_module: Name of the module in which the callable is
219
            present.
220
        :param callable_member: Member name of the callable.
221
        :param name: A name to associate the callable with, to show users what
222
            is running.
223
        """
224
        try:
225
            hook = self[hook_name]
226
        except KeyError:
227
            raise errors.UnknownHook(self.__class__.__name__, hook_name)
228
        try:
229
            hook_lazy = getattr(hook, "hook_lazy")
230
        except AttributeError:
231
            raise errors.UnsupportedOperation(self.install_named_hook_lazy,
232
                self)
233
        else:
234
            hook_lazy(callable_module, callable_member, name)
6292.1.1 by Neil Martinsen-Burrell
try looking up names for lazy hooks with __module__ and __name__
235
        if name is not None:
236
            self.name_hook_lazy(callable_module, callable_member, name)
5622.1.1 by Jelmer Vernooij
Allow lazily loading hook callbacks.
237
3256.2.5 by Daniel Watkins
Added install_named_hook.
238
    def install_named_hook(self, hook_name, a_callable, name):
239
        """Install a_callable in to the hook hook_name, and label it name.
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
240
5622.1.2 by Jelmer Vernooij
Remove BranchHooks references.
241
        :param hook_name: A hook name. See the __init__ method for the complete
242
            list of hooks.
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
243
        :param a_callable: The callable to be invoked when the hook triggers.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
244
            The exact signature will depend on the hook - see the __init__
5622.1.2 by Jelmer Vernooij
Remove BranchHooks references.
245
            method for details on each hook.
3256.2.3 by Daniel Watkins
Added docs.
246
        :param name: A name to associate a_callable with, to show users what is
247
            running.
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
248
        """
249
        try:
4098.2.1 by Robert Collins
Allow self documenting hooks.
250
            hook = self[hook_name]
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
251
        except KeyError:
252
            raise errors.UnknownHook(self.__class__.__name__, hook_name)
4098.2.1 by Robert Collins
Allow self documenting hooks.
253
        try:
254
            # list hooks, old-style, not yet deprecated but less useful.
255
            hook.append(a_callable)
256
        except AttributeError:
257
            hook.hook(a_callable, name)
3256.2.10 by Daniel Watkins
Tightened exception scope, as suggested by Aaron.
258
        if name is not None:
259
            self.name_hook(a_callable, name)
2553.1.1 by Robert Collins
Give Hooks names.
260
5622.4.2 by Jelmer Vernooij
Allow uninstalling hooks.
261
    def uninstall_named_hook(self, hook_name, label):
262
        """Uninstall named hooks.
263
264
        :param hook_name: Hook point name
265
        :param label: Label of the callable to uninstall
266
        """
267
        try:
268
            hook = self[hook_name]
269
        except KeyError:
270
            raise errors.UnknownHook(self.__class__.__name__, hook_name)
271
        try:
272
            uninstall = getattr(hook, "uninstall")
273
        except AttributeError:
5743.8.21 by Vincent Ladeuil
Add test for config load hook for remote configs.
274
            raise errors.UnsupportedOperation(self.uninstall_named_hook, self)
5622.4.2 by Jelmer Vernooij
Allow uninstalling hooks.
275
        else:
276
            uninstall(label)
277
2553.1.1 by Robert Collins
Give Hooks names.
278
    def name_hook(self, a_callable, name):
279
        """Associate name with a_callable to show users what is running."""
280
        self._callable_names[a_callable] = name
4098.2.1 by Robert Collins
Allow self documenting hooks.
281
6292.1.1 by Neil Martinsen-Burrell
try looking up names for lazy hooks with __module__ and __name__
282
    def name_hook_lazy(self, callable_module, callable_member, callable_name):
283
        self._lazy_callable_names[(callable_module, callable_member)]= \
284
            callable_name
285
4098.2.1 by Robert Collins
Allow self documenting hooks.
286
4098.2.2 by Robert Collins
Review feedback.
287
class HookPoint(object):
4098.2.1 by Robert Collins
Allow self documenting hooks.
288
    """A single hook that clients can register to be called back when it fires.
289
290
    :ivar name: The name of the hook.
5393.3.1 by Parth Malwankar
initial post_status hook is now working
291
    :ivar doc: The docs for using the hook.
4098.2.1 by Robert Collins
Allow self documenting hooks.
292
    :ivar introduced: A version tuple specifying what version the hook was
293
        introduced in. None indicates an unknown version.
294
    :ivar deprecated: A version tuple specifying what version the hook was
4098.5.15 by Aaron Bentley
Implement hook for bzr send.
295
        deprecated or superseded in. None indicates that the hook is not
296
        superseded or deprecated. If the hook is superseded then the doc
4098.2.1 by Robert Collins
Allow self documenting hooks.
297
        should describe the recommended replacement hook to register for.
298
    """
299
6240.5.7 by Jelmer Vernooij
Drop exception suppression support.
300
    def __init__(self, name, doc, introduced, deprecated=None, callbacks=None):
4098.2.2 by Robert Collins
Review feedback.
301
        """Create a HookPoint.
4098.2.3 by Robert Collins
White space difference-of-opinion.
302
4098.2.1 by Robert Collins
Allow self documenting hooks.
303
        :param name: The name of the hook, for clients to use when registering.
304
        :param doc: The docs for the hook.
305
        :param introduced: When the hook was introduced (e.g. (0, 15)).
306
        :param deprecated: When the hook was deprecated, None for
307
            not-deprecated.
308
        """
309
        self.name = name
310
        self.__doc__ = doc
311
        self.introduced = introduced
312
        self.deprecated = deprecated
5622.2.6 by Jelmer Vernooij
Put module/member information on Hooks, not individual hook points.
313
        if callbacks is None:
314
            self._callbacks = []
315
        else:
316
            self._callbacks = callbacks
4098.2.1 by Robert Collins
Allow self documenting hooks.
317
318
    def docs(self):
4098.2.2 by Robert Collins
Review feedback.
319
        """Generate the documentation for this HookPoint.
4098.2.3 by Robert Collins
White space difference-of-opinion.
320
4098.2.1 by Robert Collins
Allow self documenting hooks.
321
        :return: A string terminated in \n.
322
        """
323
        strings = []
324
        strings.append(self.name)
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
325
        strings.append('~'*len(self.name))
4098.2.1 by Robert Collins
Allow self documenting hooks.
326
        strings.append('')
327
        if self.introduced:
328
            introduced_string = _format_version_tuple(self.introduced)
329
        else:
330
            introduced_string = 'unknown'
6150.3.7 by Jonathan Riddell
gettext() in plugin.py and hooks.py
331
        strings.append(gettext('Introduced in: %s') % introduced_string)
4098.2.1 by Robert Collins
Allow self documenting hooks.
332
        if self.deprecated:
333
            deprecated_string = _format_version_tuple(self.deprecated)
6150.3.7 by Jonathan Riddell
gettext() in plugin.py and hooks.py
334
            strings.append(gettext('Deprecated in: %s') % deprecated_string)
4098.2.1 by Robert Collins
Allow self documenting hooks.
335
        strings.append('')
4070.11.2 by Martin Pool
Ask textwrap not to break long words or on hyphens
336
        strings.extend(textwrap.wrap(self.__doc__,
4070.11.5 by Martin Pool
textwrap break_on_hyphens option is not available in python2.5
337
            break_long_words=False))
4098.2.1 by Robert Collins
Allow self documenting hooks.
338
        strings.append('')
339
        return '\n'.join(strings)
340
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
341
    def __eq__(self, other):
5622.2.6 by Jelmer Vernooij
Put module/member information on Hooks, not individual hook points.
342
        return (type(other) == type(self) and other.__dict__ == self.__dict__)
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
343
5622.1.1 by Jelmer Vernooij
Allow lazily loading hook callbacks.
344
    def hook_lazy(self, callback_module, callback_member, callback_label):
345
        """Lazily register a callback to be called when this HookPoint fires.
346
347
        :param callback_module: Module of the callable to use when this
348
            HookPoint fires.
349
        :param callback_member: Member name of the callback.
350
        :param callback_label: A label to show in the UI while this callback is
351
            processing.
352
        """
353
        obj_getter = registry._LazyObjectGetter(callback_module,
354
            callback_member)
5622.2.6 by Jelmer Vernooij
Put module/member information on Hooks, not individual hook points.
355
        self._callbacks.append((obj_getter, callback_label))
5622.1.1 by Jelmer Vernooij
Allow lazily loading hook callbacks.
356
4098.2.1 by Robert Collins
Allow self documenting hooks.
357
    def hook(self, callback, callback_label):
4098.2.2 by Robert Collins
Review feedback.
358
        """Register a callback to be called when this HookPoint fires.
4098.2.1 by Robert Collins
Allow self documenting hooks.
359
4098.2.2 by Robert Collins
Review feedback.
360
        :param callback: The callable to use when this HookPoint fires.
4098.2.1 by Robert Collins
Allow self documenting hooks.
361
        :param callback_label: A label to show in the UI while this callback is
362
            processing.
363
        """
5622.1.1 by Jelmer Vernooij
Allow lazily loading hook callbacks.
364
        obj_getter = registry._ObjectGetter(callback)
5622.2.6 by Jelmer Vernooij
Put module/member information on Hooks, not individual hook points.
365
        self._callbacks.append((obj_getter, callback_label))
5622.1.2 by Jelmer Vernooij
Remove BranchHooks references.
366
5622.4.2 by Jelmer Vernooij
Allow uninstalling hooks.
367
    def uninstall(self, label):
368
        """Uninstall the callback with the specified label.
369
370
        :param label: Label of the entry to uninstall
371
        """
5622.4.3 by Jelmer Vernooij
Remove all callables with specified label, not just the first.
372
        entries_to_remove = []
5622.4.2 by Jelmer Vernooij
Allow uninstalling hooks.
373
        for entry in self._callbacks:
374
            (entry_callback, entry_label) = entry
375
            if entry_label == label:
5622.4.3 by Jelmer Vernooij
Remove all callables with specified label, not just the first.
376
                entries_to_remove.append(entry)
377
        if entries_to_remove == []:
5622.4.2 by Jelmer Vernooij
Allow uninstalling hooks.
378
            raise KeyError("No entry with label %r" % label)
5622.4.3 by Jelmer Vernooij
Remove all callables with specified label, not just the first.
379
        for entry in entries_to_remove:
380
            self._callbacks.remove(entry)
5622.4.2 by Jelmer Vernooij
Allow uninstalling hooks.
381
4098.2.1 by Robert Collins
Allow self documenting hooks.
382
    def __iter__(self):
5622.2.6 by Jelmer Vernooij
Put module/member information on Hooks, not individual hook points.
383
        return (callback.get_obj() for callback, name in self._callbacks)
4098.2.1 by Robert Collins
Allow self documenting hooks.
384
4119.3.3 by Robert Collins
Provide a __len__ on HookPoint so that 'if somehookpoint' will behave as it did when they were lists.
385
    def __len__(self):
5622.2.6 by Jelmer Vernooij
Put module/member information on Hooks, not individual hook points.
386
        return len(self._callbacks)
4119.3.3 by Robert Collins
Provide a __len__ on HookPoint so that 'if somehookpoint' will behave as it did when they were lists.
387
4098.2.1 by Robert Collins
Allow self documenting hooks.
388
    def __repr__(self):
389
        strings = []
4098.2.2 by Robert Collins
Review feedback.
390
        strings.append("<%s(" % type(self).__name__)
4098.2.1 by Robert Collins
Allow self documenting hooks.
391
        strings.append(self.name)
392
        strings.append("), callbacks=[")
5622.2.6 by Jelmer Vernooij
Put module/member information on Hooks, not individual hook points.
393
        callbacks = self._callbacks
5622.2.3 by Jelmer Vernooij
More work on lazy hook points.
394
        for (callback, callback_name) in callbacks:
5622.1.1 by Jelmer Vernooij
Allow lazily loading hook callbacks.
395
            strings.append(repr(callback.get_obj()))
4098.2.1 by Robert Collins
Allow self documenting hooks.
396
            strings.append("(")
5622.2.3 by Jelmer Vernooij
More work on lazy hook points.
397
            strings.append(callback_name)
4098.2.1 by Robert Collins
Allow self documenting hooks.
398
            strings.append("),")
5622.2.2 by Jelmer Vernooij
pump
399
        if len(callbacks) == 1:
4098.2.1 by Robert Collins
Allow self documenting hooks.
400
            strings[-1] = ")"
401
        strings.append("]>")
402
        return ''.join(strings)
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
403
404
405
_help_prefix = \
406
"""
407
Hooks
408
=====
409
410
Introduction
411
------------
412
413
A hook of type *xxx* of class *yyy* needs to be registered using::
414
415
  yyy.hooks.install_named_hook("xxx", ...)
416
5255.1.1 by Ian Clatworthy
Fix PDF generation of User Reference
417
See :doc:`Using hooks<../user-guide/hooks>` in the User Guide for examples.
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
418
419
The class that contains each hook is given before the hooks it supplies. For
420
instance, BranchHooks as the class is the hooks class for
421
`bzrlib.branch.Branch.hooks`.
422
423
Each description also indicates whether the hook runs on the client (the
424
machine where bzr was invoked) or the server (the machine addressed by
425
the branch URL).  These may be, but are not necessarily, the same machine.
426
427
Plugins (including hooks) are run on the server if all of these is true:
428
429
  * The connection is via a smart server (accessed with a URL starting with
430
    "bzr://", "bzr+ssh://" or "bzr+http://", or accessed via a "http://"
431
    URL when a smart server is available via HTTP).
432
433
  * The hook is either server specific or part of general infrastructure rather
434
    than client specific code (such as commit).
435
436
"""
437
438
def hooks_help_text(topic):
439
    segments = [_help_prefix]
440
    for hook_key in sorted(known_hooks.keys()):
441
        hooks = known_hooks_key_to_object(hook_key)
442
        segments.append(hooks.docs())
443
    return '\n'.join(segments)
5622.2.1 by Jelmer Vernooij
Some work on lazy hooks.
444
445
5622.3.4 by Jelmer Vernooij
clear/store lazy hooks during tests too.
446
# Lazily registered hooks. Maps (module, name, hook_name) tuples
447
# to lists of tuples with objectgetters and names
5622.2.1 by Jelmer Vernooij
Some work on lazy hooks.
448
_lazy_hooks = {}
449
450
451
def install_lazy_named_hook(hookpoints_module, hookpoints_name, hook_name,
452
    a_callable, name):
453
    """Install a callable in to a hook lazily, and label it name.
454
455
    :param hookpoints_module: Module name of the hook points.
456
    :param hookpoints_name: Name of the hook points.
457
    :param hook_name: A hook name.
458
    :param callable: a callable to call for the hook.
459
    :param name: A name to associate a_callable with, to show users what is
460
        running.
461
    """
462
    key = (hookpoints_module, hookpoints_name, hook_name)
5622.2.3 by Jelmer Vernooij
More work on lazy hook points.
463
    obj_getter = registry._ObjectGetter(a_callable)
464
    _lazy_hooks.setdefault(key, []).append((obj_getter, name))