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 ir.merge(IR.load(file))
113 return ir
114
115 def output_and_return_ir(self):
116 """writes output if the 'output' attribute is set, then returns"""
117 output = getattr(self, 'output', None)
118 if output:
119 self.ir.save(output)
120 return self.ir
121
122 def process(self, ir, **kwds):
123 """The process method provides the interface to be implemented by subclasses.
124
125 Commonly used arguments are 'input' and 'output'. If 'input' is defined,
126 it is interpreted as one or more input file names. If 'output' is defined, it
127 is interpreted as an output file (or directory) name.
128 This implementation may serve as a template for real processors."""
129
130
131 self.set_parameters(kwds)
132
133 self.ir = self.merge_input(ir)
134
135
136
137
138 return self.output_and_return_ir()
139
140class Composite(Processor):
141 """A Composite processor."""
142
143 processors = Parameter([], 'the list of processors this is composed of')
144
145 def __init__(self, *processors, **kwds):
146 """This __init__ is a convenience constructor that takes a var list
147 to list the desired processors. If the named values contain 'processors',
148 they override the var list."""
149 if processors: self.processors = processors
150 self.set_parameters(kwds)
151
152 def process(self, ir, **kwds):
153 """apply a list of processors. The 'input' value is passed to the first
154 processor only, the 'output' to the last. 'verbose' and 'debug' are
155 passed down if explicitely given as named values.
156 All other keywords are ignored."""
157
158 if not self.processors:
159 return super(Composite, self).process(ir, **kwds)
160
161 self.set_parameters(kwds)
162
163 if len(self.processors) == 1:
164 my_kwds = {}
165 if self.input: my_kwds['input'] = self.input
166 if self.output: my_kwds['output'] = self.output
167 if self.verbose: my_kwds['verbose'] = self.verbose
168 if self.debug: my_kwds['debug'] = self.debug
169 if self.profile: my_kwds['profile'] = self.profile
170 return self.processors[0].process(ir, **my_kwds)
171
172
173
174 my_kwds = {}
175 if self.input: my_kwds['input'] = self.input
176 if self.verbose: my_kwds['verbose'] = self.verbose
177 if self.debug: my_kwds['debug'] = self.debug
178 if self.profile: my_kwds['profile'] = self.profile
179 ir = self.processors[0].process(ir, **my_kwds)
180
181
182
183 my_kwds = {}
184 if self.verbose: my_kwds['verbose'] = self.verbose
185 if self.debug: my_kwds['debug'] = self.debug
186 if self.profile: my_kwds['profile'] = self.profile
187 if len(self.processors) > 2:
188 for p in self.processors[1:-1]:
189 ir = p.process(ir, **my_kwds)
190
191
192 if self.output: my_kwds['output'] = self.output
193 ir = self.processors[-1].process(ir, **my_kwds)
194
195 return ir
196
197class Store(Processor):
198 """Store is a convenience class useful to write out the intermediate
199 state of the IR within a pipeline such as represented by the 'Composite'"""
200
201 def process(self, ir, **kwds):
202 """Simply store the current IR in the 'output' file."""
203
204 self.set_parameters(kwds)
205 self.ir = self.merge_input(ir)
206 return self.output_and_return_ir()
207
Generated on Wed Mar 19 02:48:22 2008 by
synopsis (version 0.10)