Creating Python class wrapper for a command line tool

Rico Huijbers E.A.M.Huijbers at REMOVEstudent.tue.nl
Wed May 26 11:21:02 EDT 2004


Edvard Majakari wrote:
> Hi,
> 
> I was wondering what would be the most elegant way for creating a Python class
> wrapper for a command line utility, which takes three types of arguments:
> 
> 1. options with values (--foo=bar)
> 
> 2. boolean options (--squibble)
> 
> 3. data lines (MUNGE:x:y:z:frob)
> 
> So, when you call the program from command line it looks like
> 
> command --foo=bar --hip=hurray --squibble --optimize \
>    MUNGE1:x1:y:z:frob1 \
>    MUNGE2:x2:y:z:frob2 \
>    MUNGE3:x3:y:z:frob3
> 
> and it produces something. The idea is to make class with methods for
> setting the options and data, and then calling write() after all options
> have been set. 
> 
> My current model is like this:
> 
> class Wrapper:
> 
>     def __init__(self, **kwargs):
>         """Initialize object"""
> 
>         opts = []
> 
>         for key, val in kwargs.items():
>             if isinstance(val, str) and val.find(' ') > -1:
>                 val = '"%s"' % val
>             opts.append("--%s %s" % (key.replace('_', '-'), val))
> 
>     def setbool(self, opt):
>         pass
> 
>     def data(self, data):
>         pass
> 
>     def write(self):
>         pass
> 
> 
> The init method might look a bit odd; the reason is that I thought to call
> Wrapper like this:
> 
> obj = Wrapper(use_bar=foo, treshold=10, name="alley cat")
> 
> and __init__() would transform those keyword parameters to long string
> of the form '--use_bar foo --threshold 10 --name "alley cat"'
> 
> However, using **kwargs I cannot use boolean values, because a key in a
> dictionary must have a value. Using parameter boolean=True would transform
> to --boolean 1, which is not correct. That's why I added a separate method
> setbool(), but that doesn't seem nice. To wrap the command line call
> 
> command --optimize --use_bar foo --threshold 10 --name "alley cat" \
>    MUNGE1:x1:y:z:frob1 \
>    MUNGE2:x2:y:z:frob2 \
>    MUNGE3:x3:y:z:frob3
> 
> obj = Wrapper(use_bar=foo, treshold=10, name="alley cat")
> obj.setbool('optimize')
> obj.data("MUNGE1:x1:y:z:frob1")
> obj.data("MUNGE2:x2:y:z:frob2")
> obj.data("MUNGE3:x3:y:z:frob3")
> 
> This is acceptable, but I'm sure many of you professional Pythonistas have
> a more elegant solution. What do you think?

I am by no means a professional Pythonist... but it doesn't seem too bad 
to me to check for each kwarg whether it's type is boolean and if so, 
just add it to the command line as a flag instead of as a value 
parameter. And data arguments can be passed in the *args tuple.

I'm thinking along the lines of:

def __init__(self, *args, **kwargs):
     self.opts = []

     for key, val in kwargs:
         cmdkey = key.replace('_', '-')

	if isinstance(val, bool):
             self.opts.append("--%s" % cmdkey)
         else:
             if isinstance(val, str) and val.find(' ') > -1:
                 val = '"%s"' % val
             self.opts.append("--%s %s" % (cmdkey, val))


Which would be called like:

obj = Wrapper("MUNGE1:x1:y:z:frob1", "MUNGE2:x2:y:z:frob2", 
"MUNGE3:x3:y:z:frob3", optimize=True, use_bar=foo, threshold=10, 
name="Alley cat")

This just wraps everything in the constructor. Alternatively, you could 
go for setting each command-line parameter via a method flag.

obj = Wrapper()
obj.boolParam('optimize')
obj.valueParam('use_bar', 'foo')
obj.valueParam('threshold', 10)
obj.valueParam('name', 'alley cat')
obj.dataParam("MUNGE1:x1:y:z:frob1")
obj.dataParam("MUNGE2:x2:y:z:frob2")
obj.dataParam("MUNGE3:x3:y:z:frob3")

Combining the two methods seems ugly to me, though.

- Rico



More information about the Python-list mailing list