Synopsis - Cross-Reference
File: Synopsis/Processor.py1# 2# Copyright (C) 2003 Stefan Seefeld 3# All rights reserved. 4# Licensed to the public under the terms of the GNU LGPL (>= 2), 5# see the file COPYING for details. 6# 7 8import IR 9 10class Error(Exception): 11 """An exception a processor may raise during processing.""" 12 13 def __init__(self, what): 14 15 self.what = what 16 17 def __str__(self): 18 return "%s: %s"%(self.__class__.__name__, self.what) 19 20class InvalidArgument(Error): pass 21class MissingArgument(Error): pass 22class InvalidCommand(Error): pass 23class InternalError(Error): pass 24 25class Parameter(object): 26 """A Parameter is a documented value, kept inside a Processor.""" 27 def __init__(self, value, doc): 28 self.value = value 29 self.doc = doc 30 31class Type(type): 32 """Type is the Processor's __metaclass__.""" 33 def __init__(cls, name, bases, dict): 34 """Generate a '_parameters' dictionary holding all the 'Parameter' objects. 35 Then replace 'Parameter' objects by their values for convenient use inside 36 the code.""" 37 parameters = {} 38 for i in dict: 39 if isinstance(dict[i], Parameter): 40 parameters[i] = dict[i] 41 for i in parameters: 42 setattr(cls, i, dict[i].value) 43 setattr(cls, '_parameters', parameters) 44 45class Parametrized(object): 46 """Parametrized implements handling of Parameter attributes.""" 47 48 __metaclass__ = Type 49 50 def __new__(cls, *args, **kwds): 51 """merge all parameter catalogs for easy access to documentation, 52 then use keyword arguments to override default values.""" 53 instance = object.__new__(cls) 54 # iterate over all base classes, starting at the 'Parametrized' base class 55 # i.e. remove mixin classes 56 hierarchy = list(filter(lambda i:issubclass(i, Parametrized), cls.__mro__)) 57 hierarchy.reverse() 58 parameters = {} 59 for c in hierarchy: 60 parameters.update(c._parameters) 61 setattr(instance, '_parameters', parameters) 62 63 for p in kwds: 64 if not p in instance._parameters: 65 raise InvalidArgument('"%s.%s" processor does not have "%s" parameter' 66 %(cls.__module__, cls.__name__, p)) 67 else: 68 setattr(instance, p, kwds[p]) 69 70 return instance 71 72 def __init__(self, **kwds): 73 """The constructor uses the keywords to update the parameter list.""" 74 75 self.set_parameters(kwds) 76 77 def clone(self, *args, **kwds): 78 """Create a copy of this Parametrized. 79 The only copied attributes are the ones corresponding to parameters.""" 80 81 new_kwds = dict([(k, getattr(self, k)) for k in self._parameters]) 82 new_kwds.update(kwds) 83 return type(self)(*args, **new_kwds) 84 85 86 def get_parameters(self): 87 88 return self._parameters 89 90 def set_parameters(self, kwds): 91 """Sets the given parameters to override the default values.""" 92 for i in kwds: 93 if i in self._parameters: 94 setattr(self, i, kwds[i]) 95 else: 96 raise InvalidArgument, "No parameter '%s' in '%s'"%(i, self.__class__.__name__) 97 98 99class Processor(Parametrized): 100 """Processor documentation...""" 101 102 verbose = Parameter(False, "operate verbosely") 103 debug = Parameter(False, "generate debug traces") 104 profile = Parameter(False, "output profile data") 105 input = Parameter([], "input files to process") 106 output = Parameter('', "output file to save the ir to") 107 108 def merge_input(self, ir): 109 """Join the given IR with a set of IRs to be read from 'input' parameter""" 110 input = getattr(self, 'input', []) 111 for file in input: 112 try: 113 ir.merge(IR.load(file)) 114 except: 115 raise InvalidArgument('unable to load IR from %s'%file) 116 return ir 117 118 def output_and_return_ir(self): 119 """writes output if the 'output' attribute is set, then returns""" 120 output = getattr(self, 'output', None) 121 if output: 122 self.ir.save(output) 123 return self.ir 124 125 def process(self, ir, **kwds): 126 """The process method provides the interface to be implemented by subclasses. 127 128 Commonly used arguments are 'input' and 'output'. If 'input' is defined, 129 it is interpreted as one or more input file names. If 'output' is defined, it 130 is interpreted as an output file (or directory) name. 131 This implementation may serve as a template for real processors.""" 132 133 # override default parameter values 134 self.set_parameters(kwds) 135 # merge in IR from 'input' parameter if given 136 self.ir = self.merge_input(ir) 137 138 # do the real work here... 139 140 # write to output (if given) and return IR 141 return self.output_and_return_ir() 142 143class Composite(Processor): 144 """A Composite processor.""" 145 146 processors = Parameter([], 'the list of processors this is composed of') 147 148 def __init__(self, *processors, **kwds): 149 """This __init__ is a convenience constructor that takes a var list 150 to list the desired processors. If the named values contain 'processors', 151 they override the var list.""" 152 if processors: self.processors = processors 153 self.set_parameters(kwds) 154 155 def process(self, ir, **kwds): 156 """apply a list of processors. The 'input' value is passed to the first 157 processor only, the 'output' to the last. 'verbose' and 'debug' are 158 passed down if explicitely given as named values. 159 All other keywords are ignored.""" 160 161 if not self.processors: 162 return super(Composite, self).process(ir, **kwds) 163 164 self.set_parameters(kwds) 165 166 if len(self.processors) == 1: 167 my_kwds = {} 168 if self.input: my_kwds['input'] = self.input 169 if self.output: my_kwds['output'] = self.output 170 if self.verbose: my_kwds['verbose'] = self.verbose 171 if self.debug: my_kwds['debug'] = self.debug 172 if self.profile: my_kwds['profile'] = self.profile 173 return self.processors[0].process(ir, **my_kwds) 174 175 # more than one processor... 176 # call the first, passing the 'input' parameter, if present 177 my_kwds = {} 178 if self.input: my_kwds['input'] = self.input 179 if self.verbose: my_kwds['verbose'] = self.verbose 180 if self.debug: my_kwds['debug'] = self.debug 181 if self.profile: my_kwds['profile'] = self.profile 182 ir = self.processors[0].process(ir, **my_kwds) 183 184 # deal with all between the first and the last; 185 # they only get 'verbose', 'debug', and 'profile' flags 186 my_kwds = {} 187 if self.verbose: my_kwds['verbose'] = self.verbose 188 if self.debug: my_kwds['debug'] = self.debug 189 if self.profile: my_kwds['profile'] = self.profile 190 if len(self.processors) > 2: 191 for p in self.processors[1:-1]: 192 ir = p.process(ir, **my_kwds) 193 194 # call the last, passing the 'output' parameter, if present 195 if self.output: my_kwds['output'] = self.output 196 ir = self.processors[-1].process(ir, **my_kwds) 197 198 return ir 199 200class Store(Processor): 201 """Store is a convenience class useful to write out the intermediate 202 state of the IR within a pipeline such as represented by the 'Composite'""" 203 204 def process(self, ir, **kwds): 205 """Simply store the current IR in the 'output' file.""" 206 207 self.set_parameters(kwds) 208 self.ir = self.merge_input(ir) 209 return self.output_and_return_ir() 210
Generated on Tue May 13 02:39:22 2008 by
synopsis (version 0.10)
synopsis (version 0.10)