/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
4763.2.4 by John Arbash Meinel
merge bzr.2.1 in preparation for NEWS entry.
1
# Copyright (C) 2007-2010 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
17
18
"""Support for plugin hooking logic."""
5436.2.1 by Andrew Bennetts
Add bzrlib.pyutils, which has get_named_object, a wrapper around __import__.
19
from bzrlib import (
20
    pyutils,
21
    registry,
22
    symbol_versioning,
23
    )
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
24
from bzrlib.lazy_import import lazy_import
25
lazy_import(globals(), """
4098.2.1 by Robert Collins
Allow self documenting hooks.
26
import textwrap
27
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
28
from bzrlib import (
4098.2.1 by Robert Collins
Allow self documenting hooks.
29
        _format_version_tuple,
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
30
        errors,
31
        )
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
32
from bzrlib.help_topics import help_as_plain_text
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
33
""")
34
35
5436.2.1 by Andrew Bennetts
Add bzrlib.pyutils, which has get_named_object, a wrapper around __import__.
36
class KnownHooksRegistry(registry.Registry):
37
    # known_hooks registry contains
38
    # tuple of (module, member name) which is the hook point
39
    # module where the specific hooks are defined
40
    # callable to get the empty specific Hooks for that attribute
41
42
    def register_lazy_hook(self, hook_module_name, hook_member_name,
43
            hook_factory_member_name):
44
        self.register_lazy((hook_module_name, hook_member_name),
45
            hook_module_name, hook_factory_member_name)
46
47
    def iter_parent_objects(self):
48
        """Yield (hook_key, (parent_object, attr)) tuples for every registered
49
        hook, where 'parent_object' is the object that holds the hook
50
        instance.
51
52
        This is useful for resetting/restoring all the hooks to a known state,
53
        as is done in bzrlib.tests.TestCase._clear_hooks.
54
        """
55
        for key in self.keys():
56
            yield key, self.key_to_parent_and_attribute(key)
57
58
    def key_to_parent_and_attribute(self, (module_name, member_name)):
59
        """Convert a known_hooks key to a (parent_obj, attr) pair.
60
61
        :param key: A tuple (module_name, member_name) as found in the keys of
62
            the known_hooks registry.
63
        :return: The parent_object of the hook and the name of the attribute on
64
            that parent object where the hook is kept.
65
        """
66
        parent_mod, parent_member, attr = pyutils.calc_parent_name(module_name,
67
            member_name)
68
        return pyutils.get_named_object(parent_mod, parent_member), attr
69
70
71
known_hooks = KnownHooksRegistry()
72
known_hooks.register_lazy_hook('bzrlib.branch', 'Branch.hooks', 'BranchHooks')
73
known_hooks.register_lazy_hook('bzrlib.bzrdir', 'BzrDir.hooks', 'BzrDirHooks')
74
known_hooks.register_lazy_hook(
75
    'bzrlib.commands', 'Command.hooks', 'CommandHooks')
76
known_hooks.register_lazy_hook('bzrlib.info', 'hooks', 'InfoHooks')
77
known_hooks.register_lazy_hook('bzrlib.lock', 'Lock.hooks', 'LockHooks')
78
known_hooks.register_lazy_hook('bzrlib.merge', 'Merger.hooks', 'MergeHooks')
79
known_hooks.register_lazy_hook(
80
    'bzrlib.msgeditor', 'hooks', 'MessageEditorHooks')
81
known_hooks.register_lazy_hook(
82
    'bzrlib.mutabletree', 'MutableTree.hooks', 'MutableTreeHooks')
83
known_hooks.register_lazy_hook(
84
    'bzrlib.smart.client', '_SmartClient.hooks', 'SmartClientHooks')
85
known_hooks.register_lazy_hook(
86
    'bzrlib.smart.server', 'SmartTCPServer.hooks', 'SmartServerHooks')
87
known_hooks.register_lazy_hook(
88
    'bzrlib.status', 'hooks', 'StatusHooks')
89
known_hooks.register_lazy_hook(
90
    'bzrlib.version_info_formats.format_rio', 'RioVersionInfoBuilder.hooks',
91
    'RioVersionInfoBuilderHooks')
92
known_hooks.register_lazy_hook(
93
    'bzrlib.merge_directive', 'BaseMergeDirective.hooks', 'MergeDirectiveHooks')
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.
94
95
96
def known_hooks_key_to_object((module_name, member_name)):
97
    """Convert a known_hooks key to a object.
98
99
    :param key: A tuple (module_name, member_name) as found in the keys of
100
        the known_hooks registry.
101
    :return: The object this specifies.
102
    """
5436.2.1 by Andrew Bennetts
Add bzrlib.pyutils, which has get_named_object, a wrapper around __import__.
103
    return pyutils.get_named_object(module_name, member_name)
104
105
106
@symbol_versioning.deprecated_function(symbol_versioning.deprecated_in((2, 3)))
107
def known_hooks_key_to_parent_and_attribute(key):
108
    """See KnownHooksRegistry.key_to_parent_and_attribute."""
109
    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.
110
111
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
112
class Hooks(dict):
113
    """A dictionary mapping hook name to a list of callables.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
114
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
115
    e.g. ['FOO'] Is the list of items to be called when the
116
    FOO hook is triggered.
117
    """
118
2553.1.1 by Robert Collins
Give Hooks names.
119
    def __init__(self):
120
        dict.__init__(self)
121
        self._callable_names = {}
122
4098.2.1 by Robert Collins
Allow self documenting hooks.
123
    def create_hook(self, hook):
124
        """Create a hook which can have callbacks registered for it.
125
126
        :param hook: The hook to create. An object meeting the protocol of
4098.2.2 by Robert Collins
Review feedback.
127
            bzrlib.hooks.HookPoint. It's name is used as the key for future
4098.2.1 by Robert Collins
Allow self documenting hooks.
128
            lookups.
129
        """
130
        if hook.name in self:
131
            raise errors.DuplicateKey(hook.name)
132
        self[hook.name] = hook
133
134
    def docs(self):
135
        """Generate the documentation for this Hooks instance.
136
137
        This introspects all the individual hooks and returns their docs as well.
138
        """
139
        hook_names = sorted(self.keys())
140
        hook_docs = []
4108.3.1 by Robert Collins
Include the Hooks class in the Hooks docs.
141
        name = self.__class__.__name__
142
        hook_docs.append(name)
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
143
        hook_docs.append("-"*len(name))
4108.3.1 by Robert Collins
Include the Hooks class in the Hooks docs.
144
        hook_docs.append("")
4098.2.1 by Robert Collins
Allow self documenting hooks.
145
        for hook_name in hook_names:
146
            hook = self[hook_name]
147
            try:
148
                hook_docs.append(hook.docs())
149
            except AttributeError:
150
                # legacy hook
151
                strings = []
152
                strings.append(hook_name)
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
153
                strings.append("~" * len(hook_name))
4098.2.1 by Robert Collins
Allow self documenting hooks.
154
                strings.append("")
155
                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.
156
                    "method of '%s'\n" % (name,))
4098.2.1 by Robert Collins
Allow self documenting hooks.
157
                hook_docs.extend(strings)
158
        return "\n".join(hook_docs)
159
2553.1.1 by Robert Collins
Give Hooks names.
160
    def get_hook_name(self, a_callable):
161
        """Get the name for a_callable for UI display.
162
163
        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.
164
        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
165
        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.
166
        intended for debugging.
2553.1.1 by Robert Collins
Give Hooks names.
167
        """
168
        return self._callable_names.get(a_callable, "No hook name")
169
3256.2.5 by Daniel Watkins
Added install_named_hook.
170
    def install_named_hook(self, hook_name, a_callable, name):
171
        """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
172
173
        :param hook_name: A hook name. See the __init__ method of BranchHooks
174
            for the complete list of hooks.
175
        :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
176
            The exact signature will depend on the hook - see the __init__
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
177
            method of BranchHooks for details on each hook.
3256.2.3 by Daniel Watkins
Added docs.
178
        :param name: A name to associate a_callable with, to show users what is
179
            running.
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
180
        """
181
        try:
4098.2.1 by Robert Collins
Allow self documenting hooks.
182
            hook = self[hook_name]
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
183
        except KeyError:
184
            raise errors.UnknownHook(self.__class__.__name__, hook_name)
4098.2.1 by Robert Collins
Allow self documenting hooks.
185
        try:
186
            # list hooks, old-style, not yet deprecated but less useful.
187
            hook.append(a_callable)
188
        except AttributeError:
189
            hook.hook(a_callable, name)
3256.2.10 by Daniel Watkins
Tightened exception scope, as suggested by Aaron.
190
        if name is not None:
191
            self.name_hook(a_callable, name)
2553.1.1 by Robert Collins
Give Hooks names.
192
193
    def name_hook(self, a_callable, name):
194
        """Associate name with a_callable to show users what is running."""
195
        self._callable_names[a_callable] = name
4098.2.1 by Robert Collins
Allow self documenting hooks.
196
197
4098.2.2 by Robert Collins
Review feedback.
198
class HookPoint(object):
4098.2.1 by Robert Collins
Allow self documenting hooks.
199
    """A single hook that clients can register to be called back when it fires.
200
201
    :ivar name: The name of the hook.
5393.3.1 by Parth Malwankar
initial post_status hook is now working
202
    :ivar doc: The docs for using the hook.
4098.2.1 by Robert Collins
Allow self documenting hooks.
203
    :ivar introduced: A version tuple specifying what version the hook was
204
        introduced in. None indicates an unknown version.
205
    :ivar deprecated: A version tuple specifying what version the hook was
4098.5.15 by Aaron Bentley
Implement hook for bzr send.
206
        deprecated or superseded in. None indicates that the hook is not
207
        superseded or deprecated. If the hook is superseded then the doc
4098.2.1 by Robert Collins
Allow self documenting hooks.
208
        should describe the recommended replacement hook to register for.
209
    """
210
211
    def __init__(self, name, doc, introduced, deprecated):
4098.2.2 by Robert Collins
Review feedback.
212
        """Create a HookPoint.
4098.2.3 by Robert Collins
White space difference-of-opinion.
213
4098.2.1 by Robert Collins
Allow self documenting hooks.
214
        :param name: The name of the hook, for clients to use when registering.
215
        :param doc: The docs for the hook.
216
        :param introduced: When the hook was introduced (e.g. (0, 15)).
217
        :param deprecated: When the hook was deprecated, None for
218
            not-deprecated.
219
        """
220
        self.name = name
221
        self.__doc__ = doc
222
        self.introduced = introduced
223
        self.deprecated = deprecated
224
        self._callbacks = []
225
        self._callback_names = {}
226
227
    def docs(self):
4098.2.2 by Robert Collins
Review feedback.
228
        """Generate the documentation for this HookPoint.
4098.2.3 by Robert Collins
White space difference-of-opinion.
229
4098.2.1 by Robert Collins
Allow self documenting hooks.
230
        :return: A string terminated in \n.
231
        """
232
        strings = []
233
        strings.append(self.name)
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
234
        strings.append('~'*len(self.name))
4098.2.1 by Robert Collins
Allow self documenting hooks.
235
        strings.append('')
236
        if self.introduced:
237
            introduced_string = _format_version_tuple(self.introduced)
238
        else:
239
            introduced_string = 'unknown'
240
        strings.append('Introduced in: %s' % introduced_string)
241
        if self.deprecated:
242
            deprecated_string = _format_version_tuple(self.deprecated)
4634.21.1 by Ian Clatworthy
Tweak hook help to only report deprecation information if actually deprecated
243
            strings.append('Deprecated in: %s' % deprecated_string)
4098.2.1 by Robert Collins
Allow self documenting hooks.
244
        strings.append('')
4070.11.2 by Martin Pool
Ask textwrap not to break long words or on hyphens
245
        strings.extend(textwrap.wrap(self.__doc__,
4070.11.5 by Martin Pool
textwrap break_on_hyphens option is not available in python2.5
246
            break_long_words=False))
4098.2.1 by Robert Collins
Allow self documenting hooks.
247
        strings.append('')
248
        return '\n'.join(strings)
249
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
250
    def __eq__(self, other):
251
        return (type(other) == type(self) and 
252
            other.__dict__ == self.__dict__)
253
4098.2.1 by Robert Collins
Allow self documenting hooks.
254
    def hook(self, callback, callback_label):
4098.2.2 by Robert Collins
Review feedback.
255
        """Register a callback to be called when this HookPoint fires.
4098.2.1 by Robert Collins
Allow self documenting hooks.
256
4098.2.2 by Robert Collins
Review feedback.
257
        :param callback: The callable to use when this HookPoint fires.
4098.2.1 by Robert Collins
Allow self documenting hooks.
258
        :param callback_label: A label to show in the UI while this callback is
259
            processing.
260
        """
261
        self._callbacks.append(callback)
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
262
        if callback_label is not None:
263
            self._callback_names[callback] = callback_label
4098.2.1 by Robert Collins
Allow self documenting hooks.
264
265
    def __iter__(self):
266
        return iter(self._callbacks)
267
4119.3.3 by Robert Collins
Provide a __len__ on HookPoint so that 'if somehookpoint' will behave as it did when they were lists.
268
    def __len__(self):
269
        return len(self._callbacks)
270
4098.2.1 by Robert Collins
Allow self documenting hooks.
271
    def __repr__(self):
272
        strings = []
4098.2.2 by Robert Collins
Review feedback.
273
        strings.append("<%s(" % type(self).__name__)
4098.2.1 by Robert Collins
Allow self documenting hooks.
274
        strings.append(self.name)
275
        strings.append("), callbacks=[")
276
        for callback in self._callbacks:
277
            strings.append(repr(callback))
278
            strings.append("(")
279
            strings.append(self._callback_names[callback])
280
            strings.append("),")
281
        if len(self._callbacks) == 1:
282
            strings[-1] = ")"
283
        strings.append("]>")
284
        return ''.join(strings)
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
285
286
287
_help_prefix = \
288
"""
289
Hooks
290
=====
291
292
Introduction
293
------------
294
295
A hook of type *xxx* of class *yyy* needs to be registered using::
296
297
  yyy.hooks.install_named_hook("xxx", ...)
298
5255.1.1 by Ian Clatworthy
Fix PDF generation of User Reference
299
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.
300
301
The class that contains each hook is given before the hooks it supplies. For
302
instance, BranchHooks as the class is the hooks class for
303
`bzrlib.branch.Branch.hooks`.
304
305
Each description also indicates whether the hook runs on the client (the
306
machine where bzr was invoked) or the server (the machine addressed by
307
the branch URL).  These may be, but are not necessarily, the same machine.
308
309
Plugins (including hooks) are run on the server if all of these is true:
310
311
  * The connection is via a smart server (accessed with a URL starting with
312
    "bzr://", "bzr+ssh://" or "bzr+http://", or accessed via a "http://"
313
    URL when a smart server is available via HTTP).
314
315
  * The hook is either server specific or part of general infrastructure rather
316
    than client specific code (such as commit).
317
318
"""
319
320
def hooks_help_text(topic):
321
    segments = [_help_prefix]
322
    for hook_key in sorted(known_hooks.keys()):
323
        hooks = known_hooks_key_to_object(hook_key)
324
        segments.append(hooks.docs())
325
    return '\n'.join(segments)