File: Processor.py

package info (click to toggle)
synopsis 0.8.0-5
  • links: PTS
  • area: main
  • in suites: etch, etch-m68k
  • size: 10,112 kB
  • ctags: 12,996
  • sloc: cpp: 34,254; ansic: 33,620; python: 10,975; sh: 7,261; xml: 6,369; makefile: 773; asm: 445
file content (189 lines) | stat: -rw-r--r-- 6,523 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
#
# 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.
#

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 Parametrized(object):
   """Parametrized implements handling of Parameter attributes."""

   __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 'Parametrized' base class
      # i.e. remove mixin classes
      hierarchy = list(filter(lambda i:issubclass(i, Parametrized), 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(instance, p, kwds[p])

      return instance

   def __init__(self, **kwds):
      """The constructor uses the keywords to update the parameter list."""
      
      self.set_parameters(kwds)

   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])
         else:
            raise KeyError, "No parameter '%s' in '%s'"%(i, self.__class__.__name__)


class Error:
   """An exception a processor may raise during processing."""

   def __init__(self, what):

      self.__what = what

   def __str__(self):
      return "%s: %s"%(self.__class__.__name__, self.__what)


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

   verbose = Parameter(False, "operate verbosely")
   debug = Parameter(False, "generate debug traces")
   input = Parameter([], "input files to process")
   output = Parameter('', "output file to save the ast to")

   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.ast = 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.set_parameters(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. 'verbose' and 'debug' are
      passed down if explicitely given as named values.
      All other keywords are ignored."""

      if not self.processors:
         return super(Composite, self).process(ast, **kwds)

      self.set_parameters(kwds)

      if len(self.processors) == 1:
         my_kwds = {}
         if self.input: my_kwds['input'] = self.input
         if self.output: my_kwds['output'] = self.output
         if self.verbose: my_kwds['verbose'] = self.verbose
         if self.debug: my_kwds['debug'] = self.debug
         return self.processors[0].process(ast, **my_kwds)

      # more than one processor...

      # call the first, passing the 'input' parameter, if present
      my_kwds = {}
      if self.input: my_kwds['input'] = self.input
      if self.verbose: my_kwds['verbose'] = self.verbose
      if self.debug: my_kwds['debug'] = self.debug
      ast = self.processors[0].process(ast, **my_kwds)

      # deal with all between the first and the last;
      # they only get 'verbose' and 'debug' flags
      my_kwds = {}
      if kwds.has_key('verbose'): my_kwds['verbose'] = kwds['verbose']
      if kwds.has_key('debug'): my_kwds['debug'] = kwds['debug']
      if len(self.processors) > 2:
         for p in self.processors[1:-1]:
            ast = p.process(ast, **my_kwds)

      # call the last, passing the 'output' parameter, if present
      if self.output: my_kwds['output'] = self.output
      ast = self.processors[-1].process(ast, **my_kwds)

      return ast
   
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'"""

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

      self.set_parameters(kwds)
      self.ast = self.merge_input(ast)
      return self.output_and_return_ast()