Package Martel :: Module Dispatch
[hide private]
[frames] | no frames]

Source Code for Module Martel.Dispatch

  1  from xml.sax import handler 
  2   
  3  # Do yourself a favor and don't use tags which mess this up. 
4 -def escape(s):
5 return s.replace(":", "__")
6 -def unescape(s):
7 return s.replace("__", ":")
8
9 -def _dir_class(obj):
10 x = dir(obj) 11 for parent in obj.__bases__: 12 x = x + _dir_class(parent) 13 return x
14 15
16 -def superdir(obj):
17 names = {} 18 for name in dir(obj) + _dir_class(obj.__class__): 19 names[name] = 1 20 return names.keys()
21 22
23 -class Multicall:
24 - def __init__(self, objs = None):
25 if objs is None: 26 self.objs = [] 27 else: 28 self.objs = objs
29 - def flatten(self):
30 newobjs = [] 31 for obj in self.objs: 32 if isinstance(obj, Multicall): 33 newobjs.extend(obj.flatten()) 34 else: 35 newobjs.append(obj) 36 assert newobjs, "cannot be empty" 37 return newobjs
38 - def simplify(self):
39 newobjs = self.flatten() 40 if len(newobjs) == 1: 41 return newobjs[0] 42 return self.__class__(newobjs)
43 44
45 -class MulticallStart(Multicall):
46 - def add(self, obj):
47 # start elements are first come, first served, so append 48 self.objs.append(obj)
49 - def __call__(self, tag, attrs):
50 for obj in self.objs: 51 obj(tag, attrs)
52
53 -class MulticallEnd(Multicall):
54 - def add(self, obj):
55 # end elements are last come, first served; preserves stack order 56 self.objs.insert(0, obj)
57 - def __call__(self, tag):
58 for obj in self.objs: 59 obj(tag)
60
61 -def _merge_methods(meth1, meth2, klass):
62 if meth1 is None: 63 assert meth2 is not None 64 return meth2 65 if meth2 is None: 66 return meth1 67 k = klass() 68 k.add(meth1) 69 k.add(meth2) 70 return k.simplify()
71
72 -class DispatchHandler:
73 - def __init__(self, prefix = ""):
74 start_table = self._start_table = {} 75 end_table = self._end_table = {} 76 self._acquired = [] 77 self._prefix = prefix 78 self.supported_features = [] 79 80 for methodname in superdir(self): 81 if methodname.startswith("start_"): 82 method = getattr(self, methodname) 83 escaped_tagname = methodname[6:] 84 tagname = prefix + unescape(escaped_tagname) 85 assert not start_table.has_key(tagname) # by construction 86 start_table[tagname] = method 87 elif methodname.startswith("end_"): 88 method = getattr(self, methodname) 89 escaped_tagname = methodname[4:] 90 tagname = prefix + unescape(escaped_tagname) 91 assert not end_table.has_key(tagname) # by construction 92 end_table[tagname] = method
93
94 - def get_supported_features(self):
95 return self.supported_features
96 97
98 - def acquire(self, obj, prefix = ""):
99 # Add the objects methods to the local tables, possibly with 100 # a prefix. 101 for tagname, new_method in obj._start_table.items(): 102 tagname = prefix + tagname 103 104 # If you get an AttributeError exception with the text 105 # "Show instance has no attribute '_start_table'" 106 # then you forgot to initialize the parent class 107 method = _merge_methods(self._start_table.get(tagname, None), 108 new_method, MulticallStart) 109 110 self._start_table[tagname] = method 111 112 for tagname, new_method in obj._end_table.items(): 113 tagname = prefix + tagname 114 method = _merge_methods(self._end_table.get(tagname, None), 115 new_method, MulticallEnd) 116 self._end_table[tagname] = method 117 118 # This isn't technically correct because one acquisition might 119 # support a feature while another might not, so in that case 120 # we need disable feature support. Getting that information 121 # is tricky, so I decided that for now if you want feature 122 # support you better know what you're doing. :( 123 d = {} 124 for x in self.supported_features + obj.get_supported_features(): 125 d[x] = 1 126 self.supported_features[:] = d.keys() 127 self._acquired.append(obj)
128
129 - def setCharacterSaver(self, saver):
130 # Is there a cycle here? use a weak object? 131 self.save_characters = saver.save_characters 132 saver.save_characters() 133 self.get_characters = saver.get_characters 134 saver.get_characters() 135 for obj in self._acquired: 136 obj.setCharacterSaver(saver)
137
138 - def save_characters(self):
139 raise AssertionError("Not yet set by a Dispatcher")
140
141 - def get_characters(self):
142 raise AssertionError("Not yet set by a Dispatcher")
143 144
145 -class Callback(DispatchHandler):
146 - def __init__(self, callback):
147 DispatchHandler.__init__(self) 148 self.callback = callback
149 150 # Do these need weak references?
151 -class RemapStart:
152 - def __init__(self, obj, new_tag):
153 self.obj = obj 154 self.new_tag = new_tag
155 - def __call__(self, tag, attrs):
156 self.obj.startElement(self.new_tag, attrs)
157 -class RemapEnd:
158 - def __init__(self, obj, new_tag):
159 self.obj = obj 160 self.new_tag = new_tag
161 - def __call__(self, tag):
162 self.obj.endElement(self.new_tag)
163 164
165 -class Dispatcher(handler.ContentHandler, DispatchHandler):
166 """Adapter from the standard SAX events"""
167 - def __init__(self, prefix = "", remap = {}):
168 DispatchHandler.__init__(self, prefix) 169 170 # remap is *not* prefixed, else we couldn't remap from outside 171 # our namespace. (Is that important?) 172 for old_tagname, new_tagname in remap.items(): 173 start = RemapStart(self, new_tagname) 174 method = _merge_methods(self._start_table.get(old_tagname, None), 175 start, MulticallStart) 176 self._start_table[old_tagname] = method 177 178 end = RemapEnd(self, new_tagname) 179 method = _merge_methods(self._end_table.get(old_tagname, None), 180 end, MulticallEnd) 181 self._end_table[old_tagname] = method 182 183 184 self._save_stack = [] 185 self.setCharacterSaver(self)
186
187 - def acquire(self, obj, prefix = ""):
188 if prefix[:1] == ":": 189 prefix = prefix[1:] 190 else: 191 prefix = self._prefix + prefix 192 DispatchHandler.acquire(self, obj, prefix) 193 obj.setCharacterSaver(self)
194
195 - def uses_tags(self):
196 # Get all the tags needed by both dictionaries 197 d = {} 198 d.update(self._start_table) 199 d.update(self._end_table) 200 return d.keys()
201 202 # Dispatch events to the appropriate handlers
203 - def startDocument(self):
204 self.startElement(self._prefix, {})
205
206 - def startElement(self, tag, attrs):
207 f = self._start_table.get(tag) 208 if f is not None: 209 f(tag, attrs)
210 - def endElement(self, tag):
211 f = self._end_table.get(tag) 212 if f is not None: 213 f(tag)
214
215 - def endDocument(self):
216 self.endElement(self._prefix)
217 218 # Stack-based way for the handlers to get data without 219 # stepping on each other's toes -- so long as the calls 220 # are balanced!
221 - def save_characters(self):
222 if self._save_stack: 223 self._save_stack.append(len(self._save_text)) 224 else: 225 self._save_text = "" 226 self._save_stack.append(0)
227 - def get_characters(self):
228 pos = self._save_stack.pop() 229 return self._save_text[pos:]
230
231 - def characters(self, s):
232 if self._save_stack: 233 self._save_text += s
234