[TransWarp] Extending AbstractCommand

John Landahl john at landahl.org
Tue Oct 14 17:30:47 EDT 2003


And now for something completely different.  I'm looking to add a subclass 
of AbstractCommand which does automatic option parsing using 
Optik/OptionParser.  Below is what I've done so far, which seems to work 
pretty well.  Any suggestions or comments?


   def parser():
       try: return importString('OptionParser.OptionParser')
       except:
           try: return importString('optik.OptionParser')
           except: return None
   parser = parser()

   class OptionedCommand(AbstractCommand):
       optionList = binding.Require('List of option descriptors')

       def parser(self):
           if parser: return parser()
           else: return None
       parser = binding.Make(parser)

       def parsedOptions(self):
           if not self.parser:
               return (None, None)

           for opt in self.optionList:
               if type(opt) in [types.ListType, types.TupleType]:
                   opt = dict(opt)
               elif type(opt) is not types.DictType:
                   continue

               args = opt['options']
               del opt['options']

               self.parser.add_option(*args, **opt)

           return self.parser.parse_args(self.argv[1:])

       parsedOptions = binding.Make(parsedOptions)

       options = binding.Make(lambda self: self.parsedOptions[0])
       args    = binding.Make(lambda self: self.parsedOptions[1])

An example command class:

   class aCommand(OptionedCommand):
       usage = '''Usage: aCommand -c <arg> -d <arg>'''

       optionList = binding.Make(lambda:
           [
               Items(options=('-c', '--create'), action='store', 
type='string', dest='create'),
               Items(options=('-d', '--delete'), action='store', 
type='string', dest='delete'),
           ]
       )

       def _run(self):
           print 'create:', self.options.create
           print 'delete:', self.options.delete
           print 'args:  ', self.args

Using the example manually:

   r = config.makeRoot()
   c = aCommand(r, argv=['aCommand', '-c', 'cArg'])

It seems that this could be extended to use Optik's built-in help 
generator to build the usage string dynamically.

I really like how PEAK's binding and "create once" functionality allows 
the results of arbitrarily complex code to be provided as properties.  
This is an amazing leap forward in code reusability and flexibility, and 
makes for classes that are very easy to use and extend.  In this example, 
writers of OptionedCommand subclasses need only provide a list of option 
descriptors, and may then simply use self.options and self.args as needed 
-- without needing to know that they spring into life on demand and are 
the result of several dynamic layers of computation.  This is really great 
stuff.



More information about the PEAK mailing list