~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/hooks.py

  • Committer: Vincent Ladeuil
  • Date: 2011-02-10 12:37:27 UTC
  • mto: This revision was merged to the branch mainline in revision 5661.
  • Revision ID: v.ladeuil+lp@free.fr-20110210123727-8e0pu4wtlt6fj7nf
thread is already a python module, avoid confusion and use cethread instead.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007-2011 Canonical Ltd
 
1
# Copyright (C) 2007-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
16
16
 
17
17
 
18
18
"""Support for plugin hooking logic."""
19
 
 
20
19
from bzrlib import (
 
20
    pyutils,
21
21
    registry,
22
22
    symbol_versioning,
23
23
    )
26
26
import textwrap
27
27
 
28
28
from bzrlib import (
29
 
    _format_version_tuple,
30
 
    errors,
31
 
    pyutils,
32
 
    )
33
 
from bzrlib.i18n import gettext
 
29
        _format_version_tuple,
 
30
        errors,
 
31
        )
 
32
from bzrlib.help_topics import help_as_plain_text
34
33
""")
35
34
 
36
35
 
73
72
    ('bzrlib.branch', 'Branch.hooks', 'BranchHooks'),
74
73
    ('bzrlib.bzrdir', 'BzrDir.hooks', 'BzrDirHooks'),
75
74
    ('bzrlib.commands', 'Command.hooks', 'CommandHooks'),
76
 
    ('bzrlib.config', 'ConfigHooks', '_ConfigHooks'),
77
75
    ('bzrlib.info', 'hooks', 'InfoHooks'),
78
76
    ('bzrlib.lock', 'Lock.hooks', 'LockHooks'),
79
77
    ('bzrlib.merge', 'Merger.hooks', 'MergeHooks'),
117
115
    FOO hook is triggered.
118
116
    """
119
117
 
120
 
    def __init__(self, module=None, member_name=None):
121
 
        """Create a new hooks dictionary.
122
 
 
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)
127
 
        """
 
118
    def __init__(self):
128
119
        dict.__init__(self)
129
120
        self._callable_names = {}
130
 
        self._module = module
131
 
        self._member_name = member_name
132
 
 
133
 
    def add_hook(self, name, doc, introduced, deprecated=None):
134
 
        """Add a hook point to this dictionary.
135
 
 
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
140
 
            not-deprecated.
141
 
        """
142
 
        if name in self:
143
 
            raise errors.DuplicateKey(name)
144
 
        if self._module:
145
 
            callbacks = _lazy_hooks.setdefault(
146
 
                (self._module, self._member_name, name), [])
147
 
        else:
148
 
            callbacks = None
149
 
        hookpoint = HookPoint(name=name, doc=doc, introduced=introduced,
150
 
                              deprecated=deprecated, callbacks=callbacks)
151
 
        self[name] = hookpoint
152
 
 
153
 
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4)))
 
121
 
154
122
    def create_hook(self, hook):
155
123
        """Create a hook which can have callbacks registered for it.
156
124
 
198
166
        """
199
167
        return self._callable_names.get(a_callable, "No hook name")
200
168
 
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.
204
 
 
205
 
        :param hook_name: A hook name. See the __init__ method for the complete
206
 
            list of hooks.
207
 
        :param callable_module: Name of the module in which the callable is
208
 
            present.
209
 
        :param callable_member: Member name of the callable.
210
 
        :param name: A name to associate the callable with, to show users what
211
 
            is running.
212
 
        """
213
 
        try:
214
 
            hook = self[hook_name]
215
 
        except KeyError:
216
 
            raise errors.UnknownHook(self.__class__.__name__, hook_name)
217
 
        try:
218
 
            hook_lazy = getattr(hook, "hook_lazy")
219
 
        except AttributeError:
220
 
            raise errors.UnsupportedOperation(self.install_named_hook_lazy,
221
 
                self)
222
 
        else:
223
 
            hook_lazy(callable_module, callable_member, name)
224
 
 
225
169
    def install_named_hook(self, hook_name, a_callable, name):
226
170
        """Install a_callable in to the hook hook_name, and label it name.
227
171
 
228
 
        :param hook_name: A hook name. See the __init__ method for the complete
229
 
            list of hooks.
 
172
        :param hook_name: A hook name. See the __init__ method of BranchHooks
 
173
            for the complete list of hooks.
230
174
        :param a_callable: The callable to be invoked when the hook triggers.
231
175
            The exact signature will depend on the hook - see the __init__
232
 
            method for details on each hook.
 
176
            method of BranchHooks for details on each hook.
233
177
        :param name: A name to associate a_callable with, to show users what is
234
178
            running.
235
179
        """
245
189
        if name is not None:
246
190
            self.name_hook(a_callable, name)
247
191
 
248
 
    def uninstall_named_hook(self, hook_name, label):
249
 
        """Uninstall named hooks.
250
 
 
251
 
        :param hook_name: Hook point name
252
 
        :param label: Label of the callable to uninstall
253
 
        """
254
 
        try:
255
 
            hook = self[hook_name]
256
 
        except KeyError:
257
 
            raise errors.UnknownHook(self.__class__.__name__, hook_name)
258
 
        try:
259
 
            uninstall = getattr(hook, "uninstall")
260
 
        except AttributeError:
261
 
            raise errors.UnsupportedOperation(self.uninstall_named_hook, self)
262
 
        else:
263
 
            uninstall(label)
264
 
 
265
192
    def name_hook(self, a_callable, name):
266
193
        """Associate name with a_callable to show users what is running."""
267
194
        self._callable_names[a_callable] = name
280
207
        should describe the recommended replacement hook to register for.
281
208
    """
282
209
 
283
 
    def __init__(self, name, doc, introduced, deprecated=None, callbacks=None):
 
210
    def __init__(self, name, doc, introduced, deprecated):
284
211
        """Create a HookPoint.
285
212
 
286
213
        :param name: The name of the hook, for clients to use when registering.
293
220
        self.__doc__ = doc
294
221
        self.introduced = introduced
295
222
        self.deprecated = deprecated
296
 
        if callbacks is None:
297
 
            self._callbacks = []
298
 
        else:
299
 
            self._callbacks = callbacks
 
223
        self._callbacks = []
 
224
        self._callback_names = {}
300
225
 
301
226
    def docs(self):
302
227
        """Generate the documentation for this HookPoint.
311
236
            introduced_string = _format_version_tuple(self.introduced)
312
237
        else:
313
238
            introduced_string = 'unknown'
314
 
        strings.append(gettext('Introduced in: %s') % introduced_string)
 
239
        strings.append('Introduced in: %s' % introduced_string)
315
240
        if self.deprecated:
316
241
            deprecated_string = _format_version_tuple(self.deprecated)
317
 
            strings.append(gettext('Deprecated in: %s') % deprecated_string)
 
242
            strings.append('Deprecated in: %s' % deprecated_string)
318
243
        strings.append('')
319
244
        strings.extend(textwrap.wrap(self.__doc__,
320
245
            break_long_words=False))
322
247
        return '\n'.join(strings)
323
248
 
324
249
    def __eq__(self, other):
325
 
        return (type(other) == type(self) and other.__dict__ == self.__dict__)
326
 
 
327
 
    def hook_lazy(self, callback_module, callback_member, callback_label):
328
 
        """Lazily register a callback to be called when this HookPoint fires.
329
 
 
330
 
        :param callback_module: Module of the callable to use when this
331
 
            HookPoint fires.
332
 
        :param callback_member: Member name of the callback.
333
 
        :param callback_label: A label to show in the UI while this callback is
334
 
            processing.
335
 
        """
336
 
        obj_getter = registry._LazyObjectGetter(callback_module,
337
 
            callback_member)
338
 
        self._callbacks.append((obj_getter, callback_label))
 
250
        return (type(other) == type(self) and 
 
251
            other.__dict__ == self.__dict__)
339
252
 
340
253
    def hook(self, callback, callback_label):
341
254
        """Register a callback to be called when this HookPoint fires.
344
257
        :param callback_label: A label to show in the UI while this callback is
345
258
            processing.
346
259
        """
347
 
        obj_getter = registry._ObjectGetter(callback)
348
 
        self._callbacks.append((obj_getter, callback_label))
349
 
 
350
 
    def uninstall(self, label):
351
 
        """Uninstall the callback with the specified label.
352
 
 
353
 
        :param label: Label of the entry to uninstall
354
 
        """
355
 
        entries_to_remove = []
356
 
        for entry in self._callbacks:
357
 
            (entry_callback, entry_label) = entry
358
 
            if entry_label == label:
359
 
                entries_to_remove.append(entry)
360
 
        if entries_to_remove == []:
361
 
            raise KeyError("No entry with label %r" % label)
362
 
        for entry in entries_to_remove:
363
 
            self._callbacks.remove(entry)
 
260
        self._callbacks.append(callback)
 
261
        if callback_label is not None:
 
262
            self._callback_names[callback] = callback_label
364
263
 
365
264
    def __iter__(self):
366
 
        return (callback.get_obj() for callback, name in self._callbacks)
 
265
        return iter(self._callbacks)
367
266
 
368
267
    def __len__(self):
369
268
        return len(self._callbacks)
373
272
        strings.append("<%s(" % type(self).__name__)
374
273
        strings.append(self.name)
375
274
        strings.append("), callbacks=[")
376
 
        callbacks = self._callbacks
377
 
        for (callback, callback_name) in callbacks:
378
 
            strings.append(repr(callback.get_obj()))
 
275
        for callback in self._callbacks:
 
276
            strings.append(repr(callback))
379
277
            strings.append("(")
380
 
            strings.append(callback_name)
 
278
            strings.append(self._callback_names[callback])
381
279
            strings.append("),")
382
 
        if len(callbacks) == 1:
 
280
        if len(self._callbacks) == 1:
383
281
            strings[-1] = ")"
384
282
        strings.append("]>")
385
283
        return ''.join(strings)
424
322
        hooks = known_hooks_key_to_object(hook_key)
425
323
        segments.append(hooks.docs())
426
324
    return '\n'.join(segments)
427
 
 
428
 
 
429
 
# Lazily registered hooks. Maps (module, name, hook_name) tuples
430
 
# to lists of tuples with objectgetters and names
431
 
_lazy_hooks = {}
432
 
 
433
 
 
434
 
def install_lazy_named_hook(hookpoints_module, hookpoints_name, hook_name,
435
 
    a_callable, name):
436
 
    """Install a callable in to a hook lazily, and label it name.
437
 
 
438
 
    :param hookpoints_module: Module name of the hook points.
439
 
    :param hookpoints_name: Name of the hook points.
440
 
    :param hook_name: A hook name.
441
 
    :param callable: a callable to call for the hook.
442
 
    :param name: A name to associate a_callable with, to show users what is
443
 
        running.
444
 
    """
445
 
    key = (hookpoints_module, hookpoints_name, hook_name)
446
 
    obj_getter = registry._ObjectGetter(a_callable)
447
 
    _lazy_hooks.setdefault(key, []).append((obj_getter, name))