Rev 4127: Get missing command support sorted out. in http://people.ubuntu.com/~robertc/baz2.0/pending/Commands.hooks
Robert Collins
robertc at robertcollins.net
Sat May 23 22:01:53 BST 2009
At http://people.ubuntu.com/~robertc/baz2.0/pending/Commands.hooks
------------------------------------------------------------
revno: 4127
revision-id: robertc at robertcollins.net-20090523210151-69jmrka5l4eh0zf3
parent: robertc at robertcollins.net-20090523205712-lcwbfqk6vwavinuv
committer: Robert Collins <robertc at robertcollins.net>
branch nick: Commands.hooks
timestamp: Sun 2009-05-24 07:01:51 +1000
message:
Get missing command support sorted out.
=== modified file 'bzrlib/commands.py'
--- a/bzrlib/commands.py 2009-05-23 20:57:12 +0000
+++ b/bzrlib/commands.py 2009-05-23 21:01:51 +0000
@@ -49,10 +49,11 @@
)
""")
-from bzrlib import registry
-# Compatibility
from bzrlib.hooks import HookPoint, Hooks
+# Compatibility - Option used to be in commands.
from bzrlib.option import Option
+from bzrlib import registry
+from bzrlib.symbol_versioning import deprecated_function, deprecated_in
class CommandInfo(object):
@@ -182,17 +183,20 @@
return plugin_cmds.keys()
-def _get_cmd_dict(plugins_override=True):
- """Return name->class mapping for all commands."""
+ at deprecated_function(deprecated_in((1, 16, 0)))
+def get_all_cmds():
+ """Return canonical name and class for most commands.
+
+ NB: This does not return all commands since the introduction of
+ command hooks, and returning the class is not sufficient to
+ get correctly setup commands, which is why it is deprecated.
+
+ Use 'all_command_names' + 'get_cmd_object' instead.
+ """
d = _builtin_commands()
if plugins_override:
d.update(plugin_cmds.iteritems())
- return d
-
-
-def get_all_cmds(plugins_override=True):
- """Return canonical name and class for all registered commands."""
- for k, v in _get_cmd_dict(plugins_override=plugins_override).iteritems():
+ for k, v in d.iteritems():
yield k,v
@@ -220,7 +224,6 @@
# in a Unicode name. In that case, they should just get a
# 'command not found' error later.
# In the future, we may actually support Unicode command names.
-
cmd = None
# Get a command
for hook in Command.hooks['get_command']:
@@ -231,12 +234,11 @@
if not cmd.plugin_name():
break
if cmd is None:
- try:
- plugin_metadata, provider = probe_for_provider(cmd_name)
- raise errors.CommandAvailableInPlugin(cmd_name,
- plugin_metadata, provider)
- except errors.NoPluginAvailable:
- pass
+ for hook in Command.hooks['get_missing_command']:
+ cmd = hook(cmd_name)
+ if cmd is not None:
+ break
+ if cmd is None:
# No command found.
raise KeyError
# Allow plugins to extend commands
@@ -245,6 +247,16 @@
return cmd
+def _try_plugin_provider(cmd_name):
+ """Probe for a plugin provider having cmd_name."""
+ try:
+ plugin_metadata, provider = probe_for_provider(cmd_name)
+ raise errors.CommandAvailableInPlugin(cmd_name,
+ plugin_metadata, provider)
+ except errors.NoPluginAvailable:
+ pass
+
+
def probe_for_provider(cmd_name):
"""Look for a provider for cmd_name.
@@ -263,7 +275,7 @@
def _get_bzr_command(cmd_or_None, cmd_name):
"""Get a command from bzr's core."""
- cmds = _get_cmd_dict(plugins_override=False)
+ cmds = _builtin_commands()
try:
return cmds[cmd_name]()
except KeyError:
@@ -684,24 +696,19 @@
"Called when creating a single command. Called with "
"(cmd_or_None, command_name). get_command should either return "
"the cmd_or_None parameter, or a replacement Command object that "
- "should be used for the command.", (1, 14), None))
+ "should be used for the command.", (1, 16), None))
+ self.create_hook(HookPoint('get_missing_command',
+ "Called when creating a single command if no command could be "
+ "found. Called with (command_name). get_missing_command should "
+ "either return None, or a Command object to be used for the "
+ "command.", (1, 16), None))
self.create_hook(HookPoint('list_commands',
"Called when enumerating commands. Called with a dict of "
"cmd_name: cmd_class tuples for all the commands found "
"so far. This dict is safe to mutate - to remove a command or "
"to replace it with another (eg plugin supplied) version. "
"list_commands should return the updated dict of commands.",
- (1, 14), None))
- # We currently ship default hooks to get builtin and plugin supplied
- # command names.
- self.install_named_hook("list_commands", _list_bzr_commands,
- "bzr commands")
- self.install_named_hook("get_command", _get_bzr_command,
- "bzr commands")
- self.install_named_hook("get_command", _get_plugin_command,
- "bzr plugin commands")
- self.install_named_hook("get_command", _get_external_command,
- "bzr external command lookup")
+ (1, 16), None))
Command.hooks = CommandHooks()
@@ -1046,6 +1053,20 @@
return ignore_pipe
+def install_bzr_command_hooks():
+ """Install the hooks to supply bzr's own commands."""
+ Command.hooks.install_named_hook("list_commands", _list_bzr_commands,
+ "bzr commands")
+ Command.hooks.install_named_hook("get_command", _get_bzr_command,
+ "bzr commands")
+ Command.hooks.install_named_hook("get_command", _get_plugin_command,
+ "bzr plugin commands")
+ Command.hooks.install_named_hook("get_command", _get_external_command,
+ "bzr external command lookup")
+ Command.hooks.install_named_hook("get_missing_command", _try_plugin_provider,
+ "bzr plugin-provider-db check")
+
+
def main(argv=None):
"""Main entry point of command-line interface.
@@ -1062,7 +1083,6 @@
# Is this a final release version? If so, we should suppress warnings
if bzrlib.version_info[3] == 'final':
- from bzrlib import symbol_versioning
symbol_versioning.suppress_deprecation_warnings(override=False)
if argv is None:
argv = osutils.get_unicode_argv()
@@ -1078,6 +1098,7 @@
except UnicodeDecodeError:
raise errors.BzrError("argv should be list of unicode strings.")
argv = new_argv
+ install_bzr_command_hooks()
ret = run_bzr_catch_errors(argv)
trace.mutter("return code %d", ret)
return ret
=== modified file 'bzrlib/shellcomplete.py'
--- a/bzrlib/shellcomplete.py 2009-03-23 14:59:43 +0000
+++ b/bzrlib/shellcomplete.py 2009-05-23 21:01:51 +0000
@@ -64,15 +64,16 @@
outfile = sys.stdout
cmds = []
- for cmdname, cmdclass in commands.get_all_cmds():
- cmds.append((cmdname, cmdclass))
- for alias in cmdclass.aliases:
- cmds.append((alias, cmdclass))
+ for cmdname in commands.all_command_names():
+ cmd = commands.get_cmd_object(cmdname)))
+ cmds.append((cmdname, cmd))
+ for alias in cmd.aliases:
+ cmds.append((alias, cmd))
cmds.sort()
- for cmdname, cmdclass in cmds:
- if cmdclass.hidden:
+ for cmdname, cmd in cmds:
+ if cmd.hidden:
continue
- doc = getdoc(cmdclass)
+ doc = getdoc(cmd)
if doc is None:
outfile.write(cmdname + '\n')
else:
=== modified file 'bzrlib/tests/test_commands.py'
--- a/bzrlib/tests/test_commands.py 2009-05-23 20:57:12 +0000
+++ b/bzrlib/tests/test_commands.py 2009-05-23 21:01:51 +0000
@@ -163,6 +163,7 @@
del sys.modules['bzrlib.tests.fake_command']
global lazy_command_imported
lazy_command_imported = False
+ commands.install_bzr_command_hooks()
@staticmethod
def remove_fake():
@@ -205,6 +206,7 @@
# commands are registered).
# when they are simply created.
hook_calls = []
+ commands.install_bzr_command_hooks()
commands.Command.hooks.install_named_hook(
"extend_command", hook_calls.append, None)
# create a command, should not fire
@@ -244,6 +246,7 @@
def test_fires_on_get_cmd_object(self):
# The get_command(cmd) hook fires when commands are delivered to the
# ui.
+ commands.install_bzr_command_hooks()
hook_calls = []
class ACommand(commands.Command):
"""A sample command."""
@@ -271,11 +274,42 @@
self.assertIsInstance(hook_calls[0][1], builtins.cmd_info)
+class TestGetMissingCommandHook(tests.TestCase):
+
+ def test_fires_on_get_cmd_object(self):
+ # The get_missing_command(cmd) hook fires when commands are delivered to the
+ # ui.
+ hook_calls = []
+ class ACommand(commands.Command):
+ """A sample command."""
+ def get_missing_cmd(cmd_name):
+ hook_calls.append(('called', cmd_name))
+ if cmd_name in ('foo', 'info'):
+ return ACommand()
+ commands.Command.hooks.install_named_hook(
+ "get_missing_command", get_missing_cmd, None)
+ # create a command directly, should not fire
+ cmd = ACommand()
+ self.assertEqual([], hook_calls)
+ # ask by name, should fire and give us our command
+ cmd = commands.get_cmd_object('foo')
+ self.assertEqual([('called', 'foo')], hook_calls)
+ self.assertIsInstance(cmd, ACommand)
+ del hook_calls[:]
+ # ask by a name that is supplied by a builtin - the hook should not
+ # fire and we still get our object.
+ commands.install_bzr_command_hooks()
+ cmd = commands.get_cmd_object('info')
+ self.assertNotEqual(None, cmd)
+ self.assertEqual(0, len(hook_calls))
+
+
class TestListCommandHook(tests.TestCase):
def test_fires_on_all_command_names(self):
# The list_commands() hook fires when all_command_names() is invoked.
hook_calls = []
+ commands.install_bzr_command_hooks()
def list_my_commands(cmd_names):
hook_calls.append('called')
cmd_names.update(['foo', 'bar'])
=== modified file 'bzrlib/tests/test_options.py'
--- a/bzrlib/tests/test_options.py 2009-04-03 20:05:25 +0000
+++ b/bzrlib/tests/test_options.py 2009-05-23 21:01:51 +0000
@@ -324,8 +324,8 @@
def get_builtin_command_options(self):
g = []
- for cmd_name, cmd_class in sorted(commands.get_all_cmds()):
- cmd = cmd_class()
+ for cmd_name in sorted(commands.all_command_names()):
+ cmd = commands.get_cmd_object(cmd_name)
for opt_name, opt in sorted(cmd.options().items()):
g.append((cmd_name, opt))
return g
@@ -338,14 +338,15 @@
g = dict(option.Option.OPTIONS.items())
used_globals = {}
msgs = []
- for cmd_name, cmd_class in sorted(commands.get_all_cmds()):
- for option_or_name in sorted(cmd_class.takes_options):
+ for cmd_name in sorted(commands.all_command_names()):
+ cmd = commands.get_cmd_object(cmd_name)
+ for option_or_name in sorted(cmd.takes_options):
if not isinstance(option_or_name, basestring):
self.assertIsInstance(option_or_name, option.Option)
elif not option_or_name in g:
msgs.append("apparent reference to undefined "
"global option %r from %r"
- % (option_or_name, cmd_class))
+ % (option_or_name, cmd))
else:
used_globals.setdefault(option_or_name, []).append(cmd_name)
unused_globals = set(g.keys()) - set(used_globals.keys())
More information about the bazaar-commits
mailing list