[Synopsis-changes] Synopsis/Synopsis/Synopsis Processor.py,NONE,1.1 process.py,NONE,1.1

Stefan Seefeld stefan at synopsis.fresco.org
Tue Nov 11 02:56:20 UTC 2003


Update of /cvs/synopsis/Synopsis/Synopsis
In directory frida:/tmp/cvs-serv6622/Synopsis

Added Files:
	Processor.py process.py 
Log Message:
new scripting frontend

--- NEW FILE: Processor.py ---
# $Id: Processor.py,v 1.1 2003/11/11 02:56:17 stefan Exp $
#
# Copyright (C) 2003 Stefan Seefeld
# All rights reserved.
# Licensed to the public under the terms of the GNU LGPL (>= 2),
# see the file COPYING for details.
#

from Core import AST

class Parameter(object):
   """A Parameter is a documented value, kept inside a Processor."""
   def __init__(self, value, doc):
      self.value = value
      self.doc = doc

class Type(type):
   """Type is the Processor's __metaclass__."""
   def __init__(cls, name, bases, dict):
      """Generate a '_parameters' dictionary holding all the 'Parameter' objects.
      Then replace 'Parameter' objects by their values for convenient use inside
      the code."""
      parameters = {}
      for i in dict:
         if isinstance(dict[i], Parameter):
            parameters[i] = dict[i]
      for i in parameters:
         setattr(cls, i, dict[i].value)
      setattr(cls, '_parameters', parameters)

class Processor(object):
   """Processor documentation..."""

   __metaclass__ = Type
   
   def __new__(cls, *args, **kwds):
      """merge all parameter catalogs for easy access to documentation,
      then use keyword arguments to override default values."""
      instance = object.__new__(cls)
      # iterate over all base classes, starting at the 'Processor' base class
      # i.e. remove mixin classes
      hierarchy = list(filter(lambda i:isinstance(i, Processor), cls.__mro__))
      hierarchy.reverse()
      parameters = {}
      for c in hierarchy:
         parameters.update(c._parameters)
         setattr(instance, '_parameters', parameters)

      for p in kwds:
         if not p in instance._parameters:
            raise KeyError, "'%s' processor doesn't have '%s' parameter"%(cls.__name__, p)
         else:
            setattr(cls, p, kwds[p])

      return instance

   verbose = Parameter(False, "operate verbosely")

   def get_parameters(self):
      return self._parameters

   def set_parameters(self, kwds):
      """Sets the given parameters to override the default values."""
      for i in kwds:
         if i in self._parameters:
            setattr(self, i, kwds[i])
         elif i == 'input' or i == 'output':
            setattr(self, i, kwds[i]) # these are not in self._parameters but are legal
         else:
            raise TypeError, "No parameter '%s' in processor '%s'"%(i, self.__class__.__name__)

   def merge_input(self, ast):
      """Join the given ast with a set of asts to be read from 'input' parameter"""
      input = getattr(self, 'input', [])
      for file in input:
         ast.merge(AST.load(file))
      return ast

   def output_and_return_ast(self):
      """writes output if the 'output' attribute is set, then returns"""
      output = getattr(self, 'output', None)
      if output:
         AST.save(output, self.ast)
      return self.ast

   def process(self, ast, **kwds):
      """The process method provides the interface to be implemented by subclasses.
      
      Commonly used arguments are 'input' and 'output'. If 'input' is defined,
      it is interpreted as one or more input file names. If 'output' is defined, it
      is interpreted as an output file (or directory) name.
      This implementation may serve as a template for real processors."""

      # override default parameter values
      self.set_parameters(kwds)
      # merge in ast from 'input' parameter if given
      self.merge_input(ast)

      # do the real work here...
      
      # write to output (if given) and return ast
      return self.output_and_return_ast()

class Composite(Processor):
   """A Composite processor."""

   processors = Parameter([], 'the list of processors this is composed of')

   def __init__(self, *processors, **kwds):
      """This __init__ is a convenience constructor that takes a var list
      to list the desired processors. If the named values contain 'processors',
      they override the var list."""
      if processors: self.processors = processors
      self.__dict__.update(kwds)

   def process(self, ast, **kwds):
      """apply a list of processors. The 'input' value is passed to the first
      processor only, the 'output' to the last."""

      if not self.processors:
         return ast

      elif len(self.processors) == 1:
         return self.processors[0].process(ast, **kwds)
      
      # first_kwds is a copy of kwds, but without 'output' defined
      first_kwds = kwds.copy()
      if first_kwds.has_key('output'):
         del first_kwds['output']
      # last_kwds is a copy of kwds, but without 'input' defined
      last_kwds = kwds.copy()
      if last_kwds.has_key('input'):
         del last_kwds['input']
      # remove 'input' and 'output' from kwds for all other processors
      # in the pipeline
      if kwds.has_key('input'):
         del kwds['input']
      if kwds.has_key('output'):
         del kwds['output']

      ast = self.processors[0].process(ast, **first_kwds)

      if len(self.processors) > 2:
         for p in self.processors[1:-1]:
            ast = p.process(ast, **kwds)

      return self.processors[-1].process(ast, **last_kwds)

class Store(Processor):
   """Store is a convenience class useful to write out the intermediate
   state of the ast within a pipeline such as represented by the 'Composite'"""

   output = Parameter('', 'the output filename')

   def process(self, ast, **kwds):
      """Simply store the current ast in the 'output' file."""

      self.__dict__.update(kwds)
      return self.output_and_return()

--- NEW FILE: process.py ---
# $Id: process.py,v 1.1 2003/11/11 02:56:17 stefan Exp $
#
# Copyright (C) 2003 Stefan Seefeld
# All rights reserved.
# Licensed to the public under the terms of the GNU LGPL (>= 2),
# see the file COPYING for details.
#

from Processor import Processor
from Core import AST

import sys

def error(msg):
   """Write an error message and exit."""
   sys.stderr.write(msg)
   sys.stderr.write('\n')
   sys.exit(-1)

def process(**commands):
   """Accept a set of commands and process according to command line options.
   The typical call will start with the name of the processor to be executed,
   followed by a set of parameters, followed by non-parameter arguments.
   All parameters are either of the form 'name=value', or '--name=value'.
   The first form expects 'value' to be valid python, the second a string.
   The remaining non-parameter arguments are associated with the 'input'
   parameter.
   Once this initialization is done, the named command's 'process' method
   is executed.
   """

   #first make sure the function was called with the correct argument types
   for c in commands:
      if not isinstance(commands[c], Processor):
         error("command '%s' isn't a valid processor"%c)

   if len(sys.argv) < 2:
      error("Usage : %s <command> [args] [input files]'%sys.argv[0]")

   elif sys.argv[1] == '--help':
      print "Usage: %s --help"%sys.argv[0]
      print "   or: %s <command> --help"%sys.argv[0]
      print "   or: %s <command> [parameters]"%sys.argv[0]
      print ""
      print "Available commands:"
      for c in commands:
         print "   %s"%c
      sys.exit(0)

   command = sys.argv[1]
   args = sys.argv[2:]

   if '--help' in args:
      print "Parameters for command '%s'"%command
      parameters = commands[command].get_parameters()
      tab = max(map(lambda x:len(x), parameters.keys()))
      for p in parameters:
         print "   %-*s     %s"%(tab, p, parameters[p].doc)
      sys.exit(0)

   props = {}
   # process all option arguments (i.e. those containing a '='
   while args:
      arg = args[0]
      if arg.find('=') == -1 and not arg.startswith('--'):
         break
      attribute = arg.split('=', 1)
      if len(attribute) == 2:
         name, value = attribute
         if name.startswith('--'):
            props[name[2:]] = value # it's a string
         else:
            try:
               props[name] = eval(value) # it's a python expression
            except:
               error("""an error occured trying to evaluate the value of \'%s\' (\'%s\')
to pass this as a string, please use %s="'%s'" """%(name, value, name, value))
      else:
         name = attribute[0]
         if name.startswith('--'):
            props[name[2:]] = True # flag the attribute as 'set'
         else:
            # the nearest thing to 'no python expression'
            # is None...
            props[name] = None
      args = args[1:]

   # remaining arguments are mapped to the 'input' value,
   # if that is not yet defined
   if args and 'input' not in props:
      props['input'] = args

   if command in commands:
      ast = AST.AST()
      try:
         commands[command].process(ast, **props)
      except KeyError, e:
         error('missing argument "%s"'%e)
   else:
      error('no command "%s"'%command)






More information about the Synopsis-changes mailing list