pythonpython-2.7argparse

Can two Python argparse objects be combined?


I have an object A which contains parserA - an argparse.ArgumentParser object There is also object B which contains parserB - another argparse.ArgumentParser

Object A contains an instance of object B, however object B's arguments now need to be parsed by the parser in object A (since A is the one being called from the command line with the arguments, not B)

Is there a way to write in Python object A: parserA += B.parserB?


Solution

  • argparse was developed around objects. Other than a few constants and utility functions it is all class definitions. The documentation focuses on use rather than that class structure. But it may help to understand a bit of that.

    parser = argparse.ArgumentParser(...)
    

    creates a parser object.

    arg1 = parser.add_argument(...)
    

    creates an argparse.Action (subclass actually) object and adds it to several parser attributes (lists). Normally we ignore the fact that the method returns this Action object, but occasionally I find it helpful. And when I build a parser in an interactive shell I see a this action.

    args = parser.parse_args()
    

    runs another method, and returns an namespace object (class argparse.Namespace).

    The group methods and subparsers methods also create and return objects (groups, actions and/or parsers).

    The ArgumentParser method takes a parents parameter, where the value is a list of parser objects.

    With

    parsera = argparse.ArgumentParser(parents=[parserb])
    

    during the creation of parsera, the actions and groups in parserb are copied to parsera. That way, parsera will recognize all the arguments that parserb does. I encourage you to test it.

    But there are a few qualifications. The copy is by reference. That is, parsera gets a pointer to each Action defined in parserb. Occasionally that creates problems (I won't get into that now). And one or the other has to have add_help=False. Normally a help action is added to a parser at creation. But if parserb also has a help there will be conflict (a duplication) that has to be resolved.

    But parents can't be used if parsera has been created independently of parserb. There's no existing mechanism for adding Actions from parserb. It might possible to make a new parser, with both as parents

    parserc = argparse.ArgumentParser(parents=[parsera, parserb])
    

    I could probably write a function that would add arguments from parserb to parsera, borrowing ideas from the method that implements parents. But I'd have to know how conflicts are to be resolved.

    Look at the argparse._ActionsContainer._add_container_actions to see how arguments (Actions) are copies from a parent to a parser. Something that may be confusing is that each Action is part of a group (user defined or one of the 2 default groups (seen in the help)) in addition to being in a parser.

    Another possibility is to use

    [argsA, extrasA] = parserA.parse_known_args()
    [argsB, extrasB] = parserB.parse_known_args()  # uses the same sys.argv 
    # or
    args = parserB.parse_args(extrasA, namespace=argsA)
    

    With this each parser handles the arguments it knows about, and returns the rest in the extras list.

    Unless the parsers are designed for this kind of integration, there will be rough edges with this kind of integration. It may be easier to deal with those conficts with Arnial's approach, which is to put the shared argument definitions in your own methods. Others like to put the argument parameters in some sort of database (list, dictionary, etc), and build the parser from that. You can wrap parser creation in as many layers of boilerplate as you find convenient.