0

When we use add_parser, the function builds and returns a Parser object, there is any way to do the same thing with an already built Parser object ?

I'd like to build from a class the parser corresponding to the object. This way from a main script I could call a class method that return the class parser and add it to a higher level parser to build the final command line.

How can I do this ? thanks, Jerome

user1595929
  • 1,284
  • 3
  • 13
  • 23

3 Answers3

0

I'm a little unclear about what you need, but may be this outline of what happens when you create subparsers will help.

p=argparse.ArgumentParser()

In [3]: sp=p.add_subparsers()

In [4]: sp
Out[4]: _SubParsersAction(option_strings=[], dest='==SUPPRESS==', nargs='A...',
    const=None, default=None, type=None, choices=OrderedDict(), help=None,
    metavar=None)

In [5]: sp._parser_class
Out[5]: argparse.ArgumentParser
In [7]: spp=sp.add_parser('cmd')

In [8]: spp
Out[8]: ArgumentParser(prog='ipython cmd', usage=None, description=None,
    version=None, formatter_class=<class 'argparse.HelpFormatter'>,     
    conflict_handler='error',add_help=True)

sp is a positional Action that takes choices (the subparser names). It has a hidden attribute _parser_class that is the class that it uses to create the new subparsers.

Argparse: how to handle variable number of arguments (nargs='*') raises the possibility of changing that _parser_class to produce different behavior in the subparsers. http://bugs.python.org/issue17204 is a related bug issue.

If the subparser just needs an extra method (or a customized version of an existing one), there are a couple of ways to do it:

  • subclass ArgumentParser and use the subclass for the parser (works if the parser does not use the new method).
  • use the above discussion to create a subparser with the new class
  • add the new method to the subparser object itself.

Threads like Python: changing methods and attributes at runtime discuss the ins and outs of changing the methods of an instance.

Community
  • 1
  • 1
hpaulj
  • 221,503
  • 14
  • 230
  • 353
0

You can so it like this: Normally define the ArgumentParser you want to use, then pass it to your modules:

parser = argparse.ArgumentParser()
some_module.add_arguments(parser)

and have an add_arguments() method in your modules/objects:

def add_arguments(parser):
    parser.add_argument(...)
Nils Werner
  • 34,832
  • 7
  • 76
  • 98
0

This is how I like to do it in my command line tool Python scripts.

I have a main class that represents the tool:

class MyToolCommandLineDriver(object):

    @classmethod
    def subcommand_map(cls):
        # create a mapping of subcommand names to classes
        return subcommand_map

    @classmethod
    def run(cls):
        subcommand_map = cls.subcommand_map()

        parser = argparse.ArgumentParser(description='Some Tool')
        parser.add_argument('--some_global_option', help='Some Help')

        subparsers = parser.add_subparsers(title='Subcommands', dest='subcommand_name')
        for subcommand_name, subcommand_class in subcommand_map.items():
            subparser = subparsers.add_parser(subcommand_name, help=subcommand_class.__doc__)
            subcommand_class.configure_argument_parser(subparser)

if __name__ == "__main__":
    MyToolCommandLineDriver.run()

Each of those subcommand_class items derives from a common base class and gets a chance to configure its own parser:

class AbstractSubcommand(object):

    @classmethod
    def configure_argument_parser(cls, parser):
        pass

class SubcommandFooBar(AbstractSubcommand):

    @classmethod
    def configure_argument_parser(cls, parser):
        parser.add_argument('some_argument', nargs='+', help='Some help')

You can see the full example here: https://github.com/liyanage/git-tools/blob/master/githelper/githelper.py

That is a subcommand-based tool I made. This setup lets each subcommand define its specific arguments, and it also results in useful usage messages for free, both at the top-level:

$ githelper.py -h
usage: githelper.py [-h] [--root_path ROOT_PATH] [--verbose]

    {status,reset-master-to-svn-branch,svn-conflicts,clone-externals,tree,svn-lineage,svn-diff,branch,each,svn-delete-resolve,svn-mergeinfo,svn-rebase,foo,checkout}

and for each subcommand:

$ githelper.py clone-externals -h
usage: githelper.py clone-externals [-h] svn_url checkout_directory

positional arguments:
  svn_url             The toplevel SVN repository to clone
  checkout_directory  The path to the sandbox directory to create
Marc Liyanage
  • 4,601
  • 2
  • 28
  • 28