File: Synopsis/Processor.py 1
2
3
4
5
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
55
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
134 self.set_parameters(kwds)
135
136 self.ir = self.merge_input(ir)
137
138
139
140
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
176
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
185
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
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 Thu Apr 16 16:27:14 2009 by
synopsis (version devel)