Search code examples
pythonconfigurationmodulecode-organization

How to dynamically add options to optparser?


I have a system, where you can modify, which modules will be loaded (and run; "module" is not necessarily python module, it can combine several modules). The program can run module A and B. Now, I want to have an option that every module can define (add) its own parameters. Let's say A wants to have -n and B wants to have -s for something. But there is one common parameter -c, which the main system itself needs. What is the best way to achieve this?

So far I have been using single optparse.OptionParser instance and passed it to every module, when they are initialized. Then the module can modify (add a new parameter) if needed.


Solution

  • When I had this problem I ended up using a class derived from ArgumentParser that added the ability to register callback functions that would be executed once the arguments were parsed:

    import argparse
    
    class ArgumentParser(argparse.ArgumentParser):
       def __init__(self, *p, **kw):
          super(ArgumentParser, self).__init__(*p, **kw)
          self._reactions = []
       def add_reaction(self, handler):
          self._reactions.append(handler)
       def parse_known_args(self, args=None, namespace=None):
          (args, argv) = super(ArgumentParser, self).parse_known_args(args, namespace)
          for reaction in self._reactions:
             reaction(args)
          return (args, argv)
    

    This way the parser object still needs to be passed to all the modules to register their command line switches, but the modules can react to the switches "on their own":

    def arguments_parsed(args):
       if args.datafile:
          load_stuff(args.datafile)
    
    def add_arguments(ap):
       ap.add_argument('--datafile',
             help="Load additional input data")
       ap.add_reaction(arguments_parsed)
    

    This uses argparse, but the same could probably be done with optparse.

    It is not tested with advanced features like subparsers and probably won't work there, but could easily be extended to do so.