Overriding commands in plugins
Matt Nordhoff
mnordhoff at mattnordhoff.com
Mon Mar 8 18:08:41 GMT 2010
Michael Gliwinski wrote:
> On Monday 08 Mar 2010 15:34:52 Matt Nordhoff wrote:
>> Michael Gliwinski wrote:
>>> Hi all,
>>>
>>> What is the best/proper/right/working way to override some functionality
>>> of a command in a plugin?
>>>
>>> Let's say for example that I want to change 'switch' to print a nice
>>> message before it goes on doing its work :)
>>>
>>> From looking at some of the examples I did:
>>>
>>> class cmd_switch(bzrlib.builtins.cmd_switch):
>>> __doc__ = bzrlib.builtins.cmd_switch.__doc__
>>> def run(**kwargs):
>>> self.outf.write('Hello World!'\n)
>>> super(cmd_switch, self).run(**kwargs)
>>>
>>> but it seems to me that doesn't take into account the fact that switch
>>> may be overriden by other plugins. So it seems the result of that may be
>>> a bit random, depending on which plugin is loaded first?
>>>
>>> Then I also saw this done in the loom plugin:
>>>
>>> class cmd_switch(bzrlib.builtins.cmd_switch):
>>> def run_argv_aliases(self, argv, alias_argv=None):
>>> try:
>>> super(cmd_switch, self).run_argv_aliases(list(argv),
>>> alias_argv) except (errors.MustUseDecorated, errors.BzrOptionsError): if
>>> self._original_command is None:
>>> raise
>>> self._original_command().run_argv_aliases(argv, alias_argv)
>>>
>>> so, IIUC this uses the original command (returned by
>>> bzrlib.commands.plugin_cmds.register) stored in _original_command
>>> attribute, tries to run the overriden command and in case of problems
>>> falls back to original command, yes?
>>>
>>> Is this the more "proper" way? Does it have to be done in
>>> run_argv_aliases? I.e. if I'm only adding certain functionality it would
>>> be bit more convenient to do it in run.
>>>
>>> I'll try to update http://doc.bazaar.canonical.com/plugins/en/plugin-
>>> development.html#extending-an-existing-command if I can figure out
>>> something solid to add there.
>>>
>>> Thanks,
>>> Michael
>> I know of another way, using bzrlib.commands.get_command_object(), and I
>> /think/ it's the best way. As an example, this is what Loggerhead does:
>>
>> import bzrlib.builtins
>> from bzrlib.commands import get_cmd_object, register_command
>> from bzrlib.option import Option
>>
>> _original_command = get_cmd_object('serve')
>>
>> class cmd_serve(bzrlib.builtins.cmd_serve):
>> __doc__ = _original_command.__doc__
>>
>> takes_options = _original_command.takes_options + [
>> Option('http', help=HELP)]
>>
>> def run(self, *args, **kw):
>> if 'http' in kw:
>> # ... snip ...
>> else:
>> super(cmd_serve, self).run(*args, **kw)
>>
>> <http://bazaar.launchpad.net/~loggerhead-team/loggerhead/trunk-rich/annotat
>> e/head%3A/__init__.py#L91> (<http://xrl.us/bgxved>)
>>
>
> OK, so from what I can see using get_cmd_object instead of return value of
> register gives you support for command hooks and access to original command
> even if your command is defined in a different module than where the command is
> registered, sounds about right?
>
> Still, in your run() you use `super(cmd_serve, self).run(...)', doesn't that
> bypass any other classes that might have overriden cmd_serve (since you have
> to inherit from builtin so super will use it't run(), yes?)
>
> Thanks for the tip,
> Michael
Hmm, you're right, that's probably a problem...
Well, I didn't write that code originally, and it's only used in old
versions of bzr (recent versions have a registry so it's not necessary
to override the command), and it's only cmd_serve, so I don't care very
much. :-P
--
Matt Nordhoff
More information about the bazaar
mailing list