Rev 5411: (mbp) support for pre_status and post_status hooks. (Parth Malwankar) in file:///home/pqm/archives/thelove/bzr/%2Btrunk/
Canonical.com Patch Queue Manager
pqm at pqm.ubuntu.com
Wed Sep 8 12:09:26 BST 2010
At file:///home/pqm/archives/thelove/bzr/%2Btrunk/
------------------------------------------------------------
revno: 5411 [merge]
revision-id: pqm at pqm.ubuntu.com-20100908110922-fki7swxcw814v7u0
parent: pqm at pqm.ubuntu.com-20100906113342-s41muavhjutdc7xr
parent: parth.malwankar at gmail.com-20100831140656-m7bsce4gr5j9tqo6
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Wed 2010-09-08 12:09:22 +0100
message:
(mbp) support for pre_status and post_status hooks. (Parth Malwankar)
modified:
NEWS NEWS-20050323055033-4e00b5db738777ff
bzrlib/hooks.py hooks.py-20070325015548-ix4np2q0kd8452au-1
bzrlib/status.py status.py-20050505062338-431bfa63ec9b19e6
bzrlib/tests/test_status.py test_status.py-20060516190614-fbf6432e4a6e8aa5
=== modified file 'NEWS'
--- a/NEWS 2010-09-03 02:53:10 +0000
+++ b/NEWS 2010-09-08 11:09:22 +0000
@@ -64,6 +64,10 @@
New Features
************
+* Added ``pre_status`` and ``post_status`` hooks. This allows plugins
+ to register custom handlers which will be invoked before/after the
+ standard status output is displayed. (Parth Malwankar)
+
* ``bzr break-lock --config [location]`` can now break config files
locks. (Vincent Ladeuil, #525571)
=== modified file 'bzrlib/hooks.py'
--- a/bzrlib/hooks.py 2010-05-26 04:26:59 +0000
+++ b/bzrlib/hooks.py 2010-08-28 06:13:48 +0000
@@ -54,6 +54,8 @@
'bzrlib.smart.client', 'SmartClientHooks')
known_hooks.register_lazy(('bzrlib.smart.server', 'SmartTCPServer.hooks'),
'bzrlib.smart.server', 'SmartServerHooks')
+known_hooks.register_lazy(('bzrlib.status', 'hooks'),
+ 'bzrlib.status', 'StatusHooks')
known_hooks.register_lazy(
('bzrlib.version_info_formats.format_rio', 'RioVersionInfoBuilder.hooks'),
'bzrlib.version_info_formats.format_rio', 'RioVersionInfoBuilderHooks')
@@ -179,13 +181,13 @@
"""A single hook that clients can register to be called back when it fires.
:ivar name: The name of the hook.
+ :ivar doc: The docs for using the hook.
:ivar introduced: A version tuple specifying what version the hook was
introduced in. None indicates an unknown version.
:ivar deprecated: A version tuple specifying what version the hook was
deprecated or superseded in. None indicates that the hook is not
superseded or deprecated. If the hook is superseded then the doc
should describe the recommended replacement hook to register for.
- :ivar doc: The docs for using the hook.
"""
def __init__(self, name, doc, introduced, deprecated):
=== modified file 'bzrlib/status.py'
--- a/bzrlib/status.py 2010-04-30 11:35:43 +0000
+++ b/bzrlib/status.py 2010-08-31 13:49:48 +0000
@@ -18,6 +18,7 @@
from bzrlib import (
delta as _mod_delta,
+ hooks as _mod_hooks,
log,
osutils,
tsort,
@@ -150,6 +151,10 @@
old.lock_read()
new.lock_read()
try:
+ for hook in hooks['pre_status']:
+ hook(StatusHookParams(old, new, to_file, versioned,
+ show_ids, short, verbose))
+
specific_files, nonexistents \
= _filter_nonexistent(specific_files, old, new)
want_unversioned = not versioned
@@ -210,6 +215,9 @@
show_pending_merges(new, to_file, short, verbose=verbose)
if nonexistents:
raise errors.PathsDoNotExist(nonexistents)
+ for hook in hooks['post_status']:
+ hook(StatusHookParams(old, new, to_file, versioned,
+ show_ids, short, verbose))
finally:
old.unlock()
new.unlock()
@@ -356,3 +364,80 @@
# their groups individually. But for consistency of this
# function's API, it's better to sort both than just 'nonexistent'.
return sorted(remaining), sorted(nonexistent)
+
+
+class StatusHooks(_mod_hooks.Hooks):
+ """A dictionary mapping hook name to a list of callables for status hooks.
+
+ e.g. ['post_status'] Is the list of items to be called when the
+ status command has finished printing the status.
+ """
+
+ def __init__(self):
+ """Create the default hooks.
+
+ These are all empty initially, because by default nothing should get
+ notified.
+ """
+ _mod_hooks.Hooks.__init__(self)
+ self.create_hook(_mod_hooks.HookPoint('post_status',
+ "Called with argument StatusHookParams after Bazaar has "
+ "displayed the status. StatusHookParams has the attributes "
+ "(old_tree, new_tree, to_file, versioned, show_ids, short, "
+ "verbose). The last four arguments correspond to the command "
+ "line options specified by the user for the status command. "
+ "to_file is the output stream for writing.",
+ (2, 3), None))
+ self.create_hook(_mod_hooks.HookPoint('pre_status',
+ "Called with argument StatusHookParams before Bazaar "
+ "displays the status. StatusHookParams has the attributes "
+ "(old_tree, new_tree, to_file, versioned, show_ids, short, "
+ "verbose). The last four arguments correspond to the command "
+ "line options specified by the user for the status command. "
+ "to_file is the output stream for writing.",
+ (2, 3), None))
+
+
+hooks = StatusHooks()
+
+
+class StatusHookParams(object):
+ """Object holding parameters passed to post_status hooks.
+
+ :ivar old_tree: Start tree (basis tree) for comparison.
+ :ivar new_tree: Working tree.
+ :ivar to_file: If set, write to this file.
+ :ivar versioned: Show only versioned files.
+ :ivar show_ids: Show internal object ids.
+ :ivar short: Use short status indicators.
+ :ivar verbose: Verbose flag.
+ """
+
+ def __init__(self, old_tree, new_tree, to_file, versioned, show_ids,
+ short, verbose):
+ """Create a group of post_status hook parameters.
+
+ :param old_tree: Start tree (basis tree) for comparison.
+ :param new_tree: Working tree.
+ :param to_file: If set, write to this file.
+ :param versioned: Show only versioned files.
+ :param show_ids: Show internal object ids.
+ :param short: Use short status indicators.
+ :param verbose: Verbose flag.
+ """
+ self.old_tree = old_tree
+ self.new_tree = new_tree
+ self.to_file = to_file
+ self.versioned = versioned
+ self.show_ids = show_ids
+ self.short = short
+ self.verbose = verbose
+
+ def __eq__(self, other):
+ return self.__dict__ == other.__dict__
+
+ def __repr__(self):
+ return "<%s(%s, %s, %s, %s, %s, %s, %s)>" % (self.__class__.__name__,
+ self.old_tree, self.new_tree, self.to_file, self.versioned,
+ self.show_ids, self.short, self.verbose)
+
=== modified file 'bzrlib/tests/test_status.py'
--- a/bzrlib/tests/test_status.py 2009-10-06 14:40:37 +0000
+++ b/bzrlib/tests/test_status.py 2010-08-31 13:49:48 +0000
@@ -17,7 +17,10 @@
from StringIO import StringIO
-from bzrlib import config
+from bzrlib import (
+ config,
+ status as _mod_status,
+ )
from bzrlib.revisionspec import RevisionSpec
from bzrlib.status import show_pending_merges, show_tree_status
from bzrlib.tests import TestCaseWithTransport
@@ -130,3 +133,67 @@
revision=[RevisionSpec.from_string("revid:%s" % r1_id),
RevisionSpec.from_string("revid:%s" % r2_id)])
# return does not matter as long as it did not raise.
+
+
+class TestHooks(TestCaseWithTransport):
+
+ def test_constructor(self):
+ """Check that creating a StatusHooks instance has the right defaults.
+ """
+ hooks = _mod_status.StatusHooks()
+ self.assertTrue("post_status" in hooks, "post_status not in %s" % hooks)
+ self.assertTrue("pre_status" in hooks, "pre_status not in %s" % hooks)
+
+ def test_installed_hooks_are_StatusHooks(self):
+ """The installed hooks object should be a StatusHooks.
+ """
+ # the installed hooks are saved in self._preserved_hooks.
+ self.assertIsInstance(self._preserved_hooks[_mod_status][1],
+ _mod_status.StatusHooks)
+
+ def test_post_status_hook(self):
+ """Ensure that post_status hook is invoked with the right args.
+ """
+ calls = []
+ _mod_status.hooks.install_named_hook('post_status', calls.append, None)
+ self.assertLength(0, calls)
+ tree = self.make_branch_and_tree('.')
+ r1_id = tree.commit('one', allow_pointless=True)
+ r2_id = tree.commit('two', allow_pointless=True)
+ r2_tree = tree.branch.repository.revision_tree(r2_id)
+ output = StringIO()
+ show_tree_status(tree, to_file=output,
+ revision=[RevisionSpec.from_string("revid:%s" % r1_id),
+ RevisionSpec.from_string("revid:%s" % r2_id)])
+ self.assertLength(1, calls)
+ params = calls[0]
+ self.assertIsInstance(params, _mod_status.StatusHookParams)
+ attrs = ['old_tree', 'new_tree', 'to_file', 'versioned',
+ 'show_ids', 'short', 'verbose']
+ for a in attrs:
+ self.assertTrue(hasattr(params, a),
+ 'Attribute "%s" not found in StatusHookParam' % a)
+
+ def test_pre_status_hook(self):
+ """Ensure that pre_status hook is invoked with the right args.
+ """
+ calls = []
+ _mod_status.hooks.install_named_hook('pre_status', calls.append, None)
+ self.assertLength(0, calls)
+ tree = self.make_branch_and_tree('.')
+ r1_id = tree.commit('one', allow_pointless=True)
+ r2_id = tree.commit('two', allow_pointless=True)
+ r2_tree = tree.branch.repository.revision_tree(r2_id)
+ output = StringIO()
+ show_tree_status(tree, to_file=output,
+ revision=[RevisionSpec.from_string("revid:%s" % r1_id),
+ RevisionSpec.from_string("revid:%s" % r2_id)])
+ self.assertLength(1, calls)
+ params = calls[0]
+ self.assertIsInstance(params, _mod_status.StatusHookParams)
+ attrs = ['old_tree', 'new_tree', 'to_file', 'versioned',
+ 'show_ids', 'short', 'verbose']
+ for a in attrs:
+ self.assertTrue(hasattr(params, a),
+ 'Attribute "%s" not found in StatusHookParam' % a)
+
More information about the bazaar-commits
mailing list