Rev 5669: (jelmer) Allow installing hooks without importing the hook point. (Jelmer in file:///home/pqm/archives/thelove/bzr/%2Btrunk/
Canonical.com Patch Queue Manager
pqm at pqm.ubuntu.com
Fri Feb 18 20:16:51 UTC 2011
At file:///home/pqm/archives/thelove/bzr/%2Btrunk/
------------------------------------------------------------
revno: 5669 [merge]
revision-id: pqm at pqm.ubuntu.com-20110218201649-woj0pygeukoq6z6h
parent: pqm at pqm.ubuntu.com-20110218181345-on7vdomzesup6tos
parent: jelmer at samba.org-20110218190143-zx87oqko2o0o4q1s
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Fri 2011-02-18 20:16:49 +0000
message:
(jelmer) Allow installing hooks without importing the hook point. (Jelmer
Vernooij)
modified:
bzrlib/hooks.py hooks.py-20070325015548-ix4np2q0kd8452au-1
bzrlib/tests/test_hooks.py test_hooks.py-20070628030849-89rtsbe5dmer5npz-1
bzrlib/version_info_formats/format_rio.py format_rio.py-20060809202444-ike7i9ub03gb432p-2
doc/en/user-guide/hooks.txt hooks.txt-20070829200551-7nr6e5a1io6x78uf-1
=== modified file 'bzrlib/hooks.py'
--- a/bzrlib/hooks.py 2011-02-18 17:18:56 +0000
+++ b/bzrlib/hooks.py 2011-02-18 17:44:53 +0000
@@ -115,9 +115,39 @@
FOO hook is triggered.
"""
- def __init__(self):
+ def __init__(self, module=None, member_name=None):
+ """Create a new hooks dictionary.
+
+ :param module: The module from which this hooks dictionary should be loaded
+ (used for lazy hooks)
+ :param member_name: Name under which this hooks dictionary should be loaded.
+ (used for lazy hooks)
+ """
dict.__init__(self)
self._callable_names = {}
+ self._module = module
+ self._member_name = member_name
+
+ def add_hook(self, name, doc, introduced, deprecated=None):
+ """Add a hook point to this dictionary.
+
+ :param name: The name of the hook, for clients to use when registering.
+ :param doc: The docs for the hook.
+ :param introduced: When the hook was introduced (e.g. (0, 15)).
+ :param deprecated: When the hook was deprecated, None for
+ not-deprecated.
+ """
+ if name in self:
+ raise errors.DuplicateKey(name)
+ if self._module:
+ callbacks = _lazy_hooks.setdefault(
+ (self._module, self._member_name, name), [])
+ else:
+ callbacks = None
+ hookpoint = HookPoint(name=name, doc=doc, introduced=introduced,
+ deprecated=deprecated,
+ callbacks=callbacks)
+ self[name] = hookpoint
def create_hook(self, hook):
"""Create a hook which can have callbacks registered for it.
@@ -231,7 +261,7 @@
should describe the recommended replacement hook to register for.
"""
- def __init__(self, name, doc, introduced, deprecated):
+ def __init__(self, name, doc, introduced, deprecated=None, callbacks=None):
"""Create a HookPoint.
:param name: The name of the hook, for clients to use when registering.
@@ -244,8 +274,10 @@
self.__doc__ = doc
self.introduced = introduced
self.deprecated = deprecated
- self._callbacks = []
- self._callback_names = {}
+ if callbacks is None:
+ self._callbacks = []
+ else:
+ self._callbacks = callbacks
def docs(self):
"""Generate the documentation for this HookPoint.
@@ -271,8 +303,7 @@
return '\n'.join(strings)
def __eq__(self, other):
- return (type(other) == type(self) and
- other.__dict__ == self.__dict__)
+ return (type(other) == type(self) and other.__dict__ == self.__dict__)
def hook_lazy(self, callback_module, callback_member, callback_label):
"""Lazily register a callback to be called when this HookPoint fires.
@@ -285,9 +316,7 @@
"""
obj_getter = registry._LazyObjectGetter(callback_module,
callback_member)
- self._callbacks.append(obj_getter)
- if callback_label is not None:
- self._callback_names[obj_getter] = callback_label
+ self._callbacks.append((obj_getter, callback_label))
def hook(self, callback, callback_label):
"""Register a callback to be called when this HookPoint fires.
@@ -297,12 +326,10 @@
processing.
"""
obj_getter = registry._ObjectGetter(callback)
- self._callbacks.append(obj_getter)
- if callback_label is not None:
- self._callback_names[obj_getter] = callback_label
+ self._callbacks.append((obj_getter, callback_label))
def __iter__(self):
- return (callback.get_obj() for callback in self._callbacks)
+ return (callback.get_obj() for callback, name in self._callbacks)
def __len__(self):
return len(self._callbacks)
@@ -313,10 +340,10 @@
strings.append(self.name)
strings.append("), callbacks=[")
callbacks = self._callbacks
- for callback in callbacks:
+ for (callback, callback_name) in callbacks:
strings.append(repr(callback.get_obj()))
strings.append("(")
- strings.append(self._callback_names[callback])
+ strings.append(callback_name)
strings.append("),")
if len(callbacks) == 1:
strings[-1] = ")"
@@ -380,4 +407,5 @@
running.
"""
key = (hookpoints_module, hookpoints_name, hook_name)
- _lazy_hooks.setdefault(key, []).append((a_callable, name))
+ obj_getter = registry._ObjectGetter(a_callable)
+ _lazy_hooks.setdefault(key, []).append((obj_getter, name))
=== modified file 'bzrlib/tests/test_hooks.py'
--- a/bzrlib/tests/test_hooks.py 2011-01-20 21:05:23 +0000
+++ b/bzrlib/tests/test_hooks.py 2011-01-23 00:24:17 +0000
@@ -19,14 +19,17 @@
from bzrlib import (
branch,
errors,
+ pyutils,
tests,
)
from bzrlib.hooks import (
HookPoint,
Hooks,
+ install_lazy_named_hook,
known_hooks,
known_hooks_key_to_object,
known_hooks_key_to_parent_and_attribute,
+ _lazy_hooks,
)
from bzrlib.symbol_versioning import (
deprecated_in,
@@ -112,6 +115,22 @@
hooks.install_named_hook('set_rh', None, "demo")
self.assertEqual("demo", hooks.get_hook_name(None))
+ hooks = Hooks("bzrlib.tests.test_hooks", "TestHooks.hooks")
+
+ def test_install_lazy_named_hook(self):
+ # When the hook points are not yet registered the hook is
+ # added to the _lazy_hooks dictionary in bzrlib.hooks.
+ self.hooks.add_hook('set_rh', "doc", (0, 15))
+ set_rh = lambda: None
+ install_lazy_named_hook('bzrlib.tests.test_hooks',
+ 'TestHooks.hooks', 'set_rh', set_rh, "demo")
+ set_rh_lazy_hooks = _lazy_hooks[
+ ('bzrlib.tests.test_hooks', 'TestHooks.hooks', 'set_rh')]
+ self.assertEquals(1, len(set_rh_lazy_hooks))
+ self.assertEquals(set_rh, set_rh_lazy_hooks[0][0].get_obj())
+ self.assertEquals("demo", set_rh_lazy_hooks[0][1])
+ self.assertEqual(list(TestHooks.hooks['set_rh']), [set_rh])
+
set_rh = lambda: None
def test_install_named_hook_lazy(self):
@@ -131,6 +150,17 @@
'set_rh', 'bzrlib.tests.test_hooks', 'TestHooks.set_rh',
"demo")
+ def test_valid_lazy_hooks(self):
+ # Make sure that all the registered lazy hooks are referring to existing
+ # hook points which allow lazy registration.
+ for key, callbacks in _lazy_hooks.iteritems():
+ (module_name, member_name, hook_name) = key
+ obj = pyutils.get_named_object(module_name, member_name)
+ self.assertEquals(obj._module, module_name)
+ self.assertEquals(obj._member_name, member_name)
+ self.assertTrue(hook_name in obj)
+ self.assertIs(callbacks, obj[hook_name]._callbacks)
+
class TestHook(tests.TestCase):
=== modified file 'bzrlib/version_info_formats/format_rio.py'
--- a/bzrlib/version_info_formats/format_rio.py 2010-02-17 17:11:16 +0000
+++ b/bzrlib/version_info_formats/format_rio.py 2011-02-18 19:01:43 +0000
@@ -84,10 +84,12 @@
"""Hooks for rio-formatted version-info output."""
def __init__(self):
- super(RioVersionInfoBuilderHooks, self).__init__()
- self.create_hook(hooks.HookPoint('revision',
+ super(RioVersionInfoBuilderHooks, self).__init__(
+ "bzrlib.version_info_formats.format_rio", "RioVersionInfoBuilder.hooks")
+ self.add_hook('revision',
"Invoked when adding information about a revision to the"
" RIO stanza that is printed. revision is called with a"
- " revision object and a RIO stanza.", (1, 15), None))
+ " revision object and a RIO stanza.", (1, 15), None)
+
RioVersionInfoBuilder.hooks = RioVersionInfoBuilderHooks()
=== modified file 'doc/en/user-guide/hooks.txt'
--- a/doc/en/user-guide/hooks.txt 2010-10-08 10:50:51 +0000
+++ b/doc/en/user-guide/hooks.txt 2011-02-18 18:19:50 +0000
@@ -54,6 +54,20 @@
itself. The third argument is a name ``'My post_push hook'``, which can be
used in progress messages and error messages.
+To reduce the start-up time of Bazaar it is also possible to "lazily" install hooks,
+using the ``bzrlib.hooks.install_lazy_named_hook`` function. This removes the need
+to load the module that contains the hook point just to install the hook. Here's lazy
+version of the example above::
+
+ from bzrlib import hooks
+
+ def post_push_hook(push_result):
+ print "The new revno is %d" % push_result.new_revno
+
+
+ hooks.install_lazy_named_hook('bzrlib.branch', 'Branch.hooks',
+ 'post_push', post_push_hook, 'My post_push hook')
+
Debugging hooks
---------------
More information about the bazaar-commits
mailing list