Rev 4120: 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. in http://people.ubuntu.com/~robertc/baz2.0/pending/Hooks.docs

Robert Collins robertc at robertcollins.net
Thu Mar 12 02:43:56 GMT 2009


At http://people.ubuntu.com/~robertc/baz2.0/pending/Hooks.docs

------------------------------------------------------------
revno: 4120
revision-id: robertc at robertcollins.net-20090312024346-jx3vpibkrwo1qxar
parent: pqm at pqm.ubuntu.com-20090312001649-6tvc2mmeyw992st3
committer: Robert Collins <robertc at robertcollins.net>
branch nick: Hooks.docs
timestamp: Thu 2009-03-12 13:43:46 +1100
message:
  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.
=== modified file 'NEWS'
--- a/NEWS	2009-03-12 00:16:49 +0000
+++ b/NEWS	2009-03-12 02:43:46 +0000
@@ -50,6 +50,13 @@
 
   IMPROVEMENTS:
 
+    * All bzr ``Hooks`` classes are now registered in
+      ``bzrlib.hooks.known_hooks``. This removes the separate list from
+      ``bzrlib.tests`` and ensures that all hooks registered there are
+      correctly isolated by the test suite (previously
+      ``MutableTreeHooks`` were not being isolated correctly).
+      (Robert Collins)
+
     * ``bzr add`` no longer prints ``add completed`` on success. Failure
       still prints an error message. (Robert Collins)
 

=== modified file 'bzrlib/hooks.py'
--- a/bzrlib/hooks.py	2009-03-11 04:34:21 +0000
+++ b/bzrlib/hooks.py	2009-03-12 02:43:46 +0000
@@ -17,6 +17,7 @@
 
 """Support for plugin hooking logic."""
 from bzrlib.lazy_import import lazy_import
+from bzrlib import registry
 from bzrlib.symbol_versioning import deprecated_method, one_five
 lazy_import(globals(), """
 import textwrap
@@ -28,6 +29,46 @@
 """)
 
 
+known_hooks = registry.Registry()
+known_hooks.register_lazy(('bzrlib.branch', 'Branch.hooks'), 'bzrlib.branch',
+    'BranchHooks')
+known_hooks.register_lazy(('bzrlib.commands', 'Command.hooks'),
+    'bzrlib.commands', 'CommandHooks')
+known_hooks.register_lazy(('bzrlib.mutabletree', 'MutableTree.hooks'),
+    'bzrlib.mutabletree', 'MutableTreeHooks')
+known_hooks.register_lazy(('bzrlib.smart.client', '_SmartClient.hooks'),
+    'bzrlib.smart.client', 'SmartClientHooks')
+known_hooks.register_lazy(('bzrlib.smart.server', 'SmartTCPServer.hooks'),
+    'bzrlib.smart.server', 'SmartServerHooks')
+
+
+def known_hooks_key_to_object((module_name, member_name)):
+    """Convert a known_hooks key to a object.
+
+    :param key: A tuple (module_name, member_name) as found in the keys of
+        the known_hooks registry.
+    :return: The object this specifies.
+    """
+    return registry._LazyObjectGetter(module_name, member_name).get_obj()
+
+
+def known_hooks_key_to_parent_and_attribute((module_name, member_name)):
+    """Convert a known_hooks key to a object.
+
+    :param key: A tuple (module_name, member_name) as found in the keys of
+        the known_hooks registry.
+    :return: The object this specifies.
+    """
+    member_list = member_name.rsplit('.', 1)
+    if len(member_list) == 2:
+        parent_name, attribute = member_list
+    else:
+        parent_name = None
+        attribute = member_name
+    parent = known_hooks_key_to_object((module_name, parent_name))
+    return parent, attribute
+
+
 class Hooks(dict):
     """A dictionary mapping hook name to a list of callables.
 

=== modified file 'bzrlib/registry.py'
--- a/bzrlib/registry.py	2009-02-24 09:13:04 +0000
+++ b/bzrlib/registry.py	2009-03-12 02:43:46 +0000
@@ -62,10 +62,15 @@
         return super(_LazyObjectGetter, self).get_obj()
 
     def _do_import(self):
-        obj = __import__(self._module_name, globals(), locals(),
-                         [self._member_name])
-        if self._member_name:
-            obj = getattr(obj, self._member_name)
+        if self._member_name:
+            segments = self._member_name.split('.')
+            names = segments[0:1]
+        else:
+            names = [self._member_name]
+        obj = __import__(self._module_name, globals(), locals(), names)
+        if self._member_name:
+            for segment in segments:
+                obj = getattr(obj, segment)
         self._obj = obj
         self._imported = True
 

=== modified file 'bzrlib/tests/__init__.py'
--- a/bzrlib/tests/__init__.py	2009-03-10 01:29:23 +0000
+++ b/bzrlib/tests/__init__.py	2009-03-12 02:43:46 +0000
@@ -53,6 +53,7 @@
     bzrdir,
     debug,
     errors,
+    hooks,
     memorytree,
     osutils,
     progress,
@@ -814,22 +815,15 @@
 
     def _clear_hooks(self):
         # prevent hooks affecting tests
-        import bzrlib.branch
-        import bzrlib.smart.client
-        import bzrlib.smart.server
-        self._preserved_hooks = {
-            bzrlib.branch.Branch: bzrlib.branch.Branch.hooks,
-            bzrlib.mutabletree.MutableTree: bzrlib.mutabletree.MutableTree.hooks,
-            bzrlib.smart.client._SmartClient: bzrlib.smart.client._SmartClient.hooks,
-            bzrlib.smart.server.SmartTCPServer: bzrlib.smart.server.SmartTCPServer.hooks,
-            bzrlib.commands.Command: bzrlib.commands.Command.hooks,
-            }
+        self._preserved_hooks = {}
+        for key, factory in hooks.known_hooks.items():
+            parent, name = hooks.known_hooks_key_to_parent_and_attribute(key)
+            current_hooks = hooks.known_hooks_key_to_object(key)
+            self._preserved_hooks[parent] = (name, current_hooks)
         self.addCleanup(self._restoreHooks)
-        # reset all hooks to an empty instance of the appropriate type
-        bzrlib.branch.Branch.hooks = bzrlib.branch.BranchHooks()
-        bzrlib.smart.client._SmartClient.hooks = bzrlib.smart.client.SmartClientHooks()
-        bzrlib.smart.server.SmartTCPServer.hooks = bzrlib.smart.server.SmartServerHooks()
-        bzrlib.commands.Command.hooks = bzrlib.commands.CommandHooks()
+        for key, factory in hooks.known_hooks.items():
+            parent, name = hooks.known_hooks_key_to_parent_and_attribute(key)
+            setattr(parent, name, factory())
 
     def _silenceUI(self):
         """Turn off UI for duration of test"""
@@ -1288,8 +1282,8 @@
             osutils.set_or_unset_env(name, value)
 
     def _restoreHooks(self):
-        for klass, hooks in self._preserved_hooks.items():
-            setattr(klass, 'hooks', hooks)
+        for klass, (name, hooks) in self._preserved_hooks.items():
+            setattr(klass, name, hooks)
 
     def knownFailure(self, reason):
         """This test has failed for some known reason."""

=== modified file 'bzrlib/tests/test_hooks.py'
--- a/bzrlib/tests/test_hooks.py	2009-03-11 04:34:21 +0000
+++ b/bzrlib/tests/test_hooks.py	2009-03-12 02:43:46 +0000
@@ -16,10 +16,13 @@
 
 """Tests for the core Hooks logic."""
 
-from bzrlib import errors
+from bzrlib import branch, errors
 from bzrlib.hooks import (
     HookPoint,
     Hooks,
+    known_hooks,
+    known_hooks_key_to_object,
+    known_hooks_key_to_parent_and_attribute,
     )
 from bzrlib.errors import (
     UnknownHook,
@@ -180,3 +183,33 @@
         self.assertEqual(
             '<HookPoint(foo), callbacks=[%s(my callback)]>' %
             callback_repr, repr(hook))
+
+
+class TestHookRegistry(TestCase):
+
+    def test_items_are_reasonable_keys(self):
+        # All the items in the known_hooks registry need to map from
+        # (module_name, member_name) tuples to the callable used to get an
+        # empty Hooks of for that attribute. This is used to support the test
+        # suite which needs to generate empty hooks (and HookPoints) to ensure
+        # isolation and prevent tests failing spuriously.
+        for key, factory in known_hooks.items():
+            self.assertTrue(callable(factory),
+                "The factory(%r) for %r is not callable" % (factory, key))
+            obj = known_hooks_key_to_object(key)
+            self.assertIsInstance(obj, Hooks)
+            new_hooks = factory()
+            self.assertIsInstance(obj, Hooks)
+            self.assertEqual(type(obj), type(new_hooks))
+
+    def test_known_hooks_key_to_object(self):
+        self.assertIs(branch.Branch.hooks,
+            known_hooks_key_to_object(('bzrlib.branch', 'Branch.hooks')))
+
+    def test_known_hooks_key_to_parent_and_attribute(self):
+        self.assertEqual((branch.Branch, 'hooks'),
+            known_hooks_key_to_parent_and_attribute(
+            ('bzrlib.branch', 'Branch.hooks')))
+        self.assertEqual((branch, 'Branch'),
+            known_hooks_key_to_parent_and_attribute(
+            ('bzrlib.branch', 'Branch')))




More information about the bazaar-commits mailing list