Search code examples
pythonoptparse

Python optparse: how to make a repeatable group of options?


Here is a simplified view of the script I am programming in python:

  • The script is used to compare two files.
  • The type (local or remote) of each file must be specified (mandatory)
  • For a local file:

    • Its filename must be specified (mandatory)
    • Its path can be specified (optional, default is .)
  • For a remote file:

    • Its filename must be specified (mandatory)
    • Its path must be specified (mandatory)
    • An boolean flag can be specified (optional)

So I would like to use it like this:

compare.py -L local.txt -L local2.txt -p /tmp/

or

compare.py -L local.txt -R remote.txt -p remoteDir/ --myFlag

So I need to define 2 groups of options (local/L and remote/R), each containing its own set of mandatory and optional options.

I have not found a way to achieve this with optparse (or even argparse, but I'd like to stick with optparse if possible as I'm writing the script for Python 2.6.7)

Is there any clean solution ?


Solution

  • I would do it with option callbacks + helper object which traces context of the current option. Example:

    from optparse import OptionParser
    
    class FileBase(object):
        def __init__(self, fname):
            self.fname = fname
            self.path = None
    class LocalFile(FileBase):
        pass
    class RemoteFile(FileBase):
        pass
    
    class FileOptionParser(object):
        def __init__(self):
            self.last_file = None
            self.files = []
    
        def set_path(self, option, opt, value, parser):
            self.last_file.path = value
    
        def set_file(self, option, opt, value, parser):
            if   option.dest=="local" : cls = LocalFile
            elif option.dest=="remote": cls = RemoteFile
            else:                       assert False
            self.last_file = cls(value)
            self.files.append(self.last_file)
            setattr(parser.values, option.dest, self.last_file)
    
    fop = FileOptionParser()
    
    parser = OptionParser()
    parser.add_option('-L', '--local',  type='string', action='callback', callback=fop.set_file)
    parser.add_option('-R', '--remote', type='string', action='callback', callback=fop.set_file)
    parser.add_option('-p', '--path',   type='string', action='callback', callback=fop.set_path)
    
    (options, args) = parser.parse_args()
    print [(f.fname, f.path) for f in fop.files]
    

    example is:

    > python test.py -L local.txt -R remote.txt -p remoteDir/
    [('local.txt', None), ('remote.txt', 'remoteDir/')]