Skip to content

Instantly share code, notes, and snippets.

@sampsyo
Created July 11, 2010 20:04
Show Gist options
  • Save sampsyo/471779 to your computer and use it in GitHub Desktop.
Save sampsyo/471779 to your computer and use it in GitHub Desktop.
hack for argparse adding subcommand aliases
#!/usr/bin/env python
"""Aliases for argparse positional arguments."""
import argparse
class AliasedSubParsersAction(argparse._SubParsersAction):
class _AliasedPseudoAction(argparse.Action):
def __init__(self, name, aliases, help):
dest = name
if aliases:
dest += ' (%s)' % ','.join(aliases)
sup = super(AliasedSubParsersAction._AliasedPseudoAction, self)
sup.__init__(option_strings=[], dest=dest, help=help)
def add_parser(self, name, **kwargs):
if 'aliases' in kwargs:
aliases = kwargs['aliases']
del kwargs['aliases']
else:
aliases = []
parser = super(AliasedSubParsersAction, self).add_parser(name, **kwargs)
# Make the aliases work.
for alias in aliases:
self._name_parser_map[alias] = parser
# Make the help text reflect them, first removing old help entry.
if 'help' in kwargs:
help = kwargs.pop('help')
self._choices_actions.pop()
pseudo_action = self._AliasedPseudoAction(name, aliases, help)
self._choices_actions.append(pseudo_action)
return parser
if __name__ == '__main__':
# An example parser with subcommands.
parser = argparse.ArgumentParser()
parser.register('action', 'parsers', AliasedSubParsersAction)
parser.add_argument("--library", metavar="libfile", type=str,
help="library database filename")
subparsers = parser.add_subparsers(title="commands", metavar="COMMAND")
p_import = subparsers.add_parser("import", help="add files to library",
aliases=('imp', 'im'))
p_import.add_argument("filename", metavar="file", type=str, nargs="+",
help="the files to import")
p_remove = subparsers.add_parser("remove", aliases=('rm',),
help="remove items")
args = parser.parse_args()
print args
@angarg
Copy link

angarg commented Dec 2, 2018

Nice! Thanks for sharing.

@LiamGow
Copy link

LiamGow commented Apr 11, 2019

If I might add an updated version, the following includes xiaket's modification, and also overrides the default parsers action so you don't have to register it per parser.

I don't know much about python importing practices - this may be a horrible idea, but it works for me regardless of whether this file or argparse is imported first in another file, and works for recursive subparsers, so if you're lazy like me, maybe give it a try.

class AliasedSubParsersAction(argparse._SubParsersAction):
    old_init = staticmethod(argparse._ActionsContainer.__init__)

    @staticmethod
    def _containerInit(self, description, prefix_chars, argument_default, conflict_handler):
        AliasedSubParsersAction.old_init(self, description, prefix_chars, argument_default, conflict_handler)
        self.register('action', 'parsers', AliasedSubParsersAction)

    class _AliasedPseudoAction(argparse.Action):
        def __init__(self, name, aliases, help):
            dest = name
            if aliases:
                dest += ' (%s)' % ','.join(aliases)
            sup = super(AliasedSubParsersAction._AliasedPseudoAction, self)
            sup.__init__(option_strings=[], dest=dest, help=help)

    def add_parser(self, name, **kwargs):
        aliases = kwargs.pop('aliases', [])
        parser = super(AliasedSubParsersAction, self).add_parser(name, **kwargs)

        # Make the aliases work.
        for alias in aliases:
            self._name_parser_map[alias] = parser
        # Make the help text reflect them, first removing old help entry.
        if 'help' in kwargs:
            help = kwargs.pop('help')
            self._choices_actions.pop()
            pseudo_action = self._AliasedPseudoAction(name, aliases, help)
            self._choices_actions.append(pseudo_action)

        return parser


# override argparse to register new subparser action by default
argparse._ActionsContainer.__init__ = AliasedSubParsersAction._containerInit

@Helveg
Copy link

Helveg commented Nov 21, 2019

Works very nicely @LiamGow

@studersi
Copy link

studersi commented Mar 6, 2020

@sampsyo Thank you so much! This is just what I needed :-)

@panzi
Copy link

panzi commented Nov 15, 2020

Python now supports this built-in. Does anyone know in which exact version it was added to Python?
(And again, thank you for this gist.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment