2 from .. import dispatch
9 if isinstance(el, cons.element):
10 if el.ns not in names:
11 names[el.ns] = "n" + str(nid[0])
13 for ch in el.children:
22 def flatiter(root, short=True):
27 if i >= len(el.children):
33 if isinstance(ch, cons.element):
34 if short and len(ch.children) == 0:
39 elif isinstance(ch, cons.text):
41 elif isinstance(ch, cons.raw):
44 raise Exception("Unknown object in element tree: " + el)
46 class formatter(object):
47 def __init__(self, src, nsnames=None, charset="utf-8"):
49 self.nsnames = nsnames or {}
52 self.buf = bytearray()
53 self.charset = charset
55 def write(self, text):
56 self.buf.extend(text.encode(self.charset))
58 def quotewrite(self, buf):
59 buf = buf.replace('&', "&")
60 buf = buf.replace('<', "<")
61 buf = buf.replace('>', ">")
68 ns = self.nsnames[el.ns]
72 return ns + ":" + el.name
75 qc, qt = ("'", "'") if '"' in v else ('"', """)
77 v = v.replace('&', "&")
78 v = v.replace('<', "<")
79 v = v.replace('>', ">")
89 def attrs(self, attrs):
94 def inittag(self, el):
95 self.write("<" + self.elname(el))
96 attrs = el.attrs.items()
99 for ns, name in self.nsnames.items():
102 raise Exception("null namespace must have null name, not" + name)
104 nsnames.append(("xmlns" if name is None else ("xmlns:" + name), ns))
105 attrs = itertools.chain(attrs, iter(nsnames))
109 def starttag(self, el):
113 def shorttag(self, el):
117 def endtag(self, el):
118 self.write("</" + self.elname(el) + ">")
123 def rawcode(self, el):
127 self.write('<?xml version="1.0" encoding="' + self.charset + '" ?>\n')
128 if isinstance(el, cons.doctype):
129 self.write('<!DOCTYPE %s PUBLIC "%s" "%s">\n' % (el.rootname,
137 def handle(self, ev, el):
155 raise StopIteration()
157 ev, el = next(self.src)
158 except StopIteration:
162 ret = bytes(self.buf)
166 def nsname(self, el):
167 for t in type(self).__mro__:
168 ret = getattr(t, "defns", {}).get(el.ns, None)
173 ret = "n" + str(self.nextns)
177 def findnsnames(self, root):
181 if isinstance(el, cons.element):
182 if el.ns not in fnames:
186 for ch in el.children:
189 if None not in rnames:
190 fnames[root.ns] = None
191 rnames[None] = root.ns
192 self.nsnames = fnames
195 def output(cls, out, root, nsnames=None, doctype=None, **kw):
196 if isinstance(doctype, cons.doctype):
198 elif doctype is not None:
199 doctype = cons.doctype(root.name, doctype[0], doctype[1])
200 src = itertools.chain(iter([("^", doctype)]), flatiter(root))
201 self = cls(src=src, nsnames=nsnames, **kw)
203 self.findnsnames(root)
209 def fragment(cls, out, root, nsnames=None, **kw):
210 self = cls(src=flatiter(root), nsnames=nsnames, **kw)
212 self.findnsnames(root)
217 def format(cls, root, **kw):
219 cls.output(buf, root, **kw)
220 return buf.getvalue()
222 class indenter(formatter):
223 def __init__(self, indent=" ", *args, **kw):
224 super().__init__(*args, **kw)
231 self.last = None, None
232 self.lastendbr = True
234 def write(self, text):
235 lines = text.split("\n")
237 for ln in lines[:-1]:
238 self.buf.extend(ln.encode(self.charset))
239 self.buf.extend(b"\n")
241 self.buf.extend(lines[-1].encode(self.charset))
242 self.col += len(lines[-1])
247 self.buf.extend(("\n" + self.curind).encode(self.charset))
251 def inlinep(self, el):
252 for ch in el.children:
253 if isinstance(ch, cons.text):
258 self.stack.append((el, self.curind, self.inline))
261 el, self.curind, self.inline = self.stack.pop()
264 def starttag(self, el):
266 if self.last[0] == "<" and self.last[1].name == el.name and self.lastendbr:
271 self.inline = self.inline or self.inlinep(el)
272 self.curind += self.indent
275 def shorttag(self, el):
280 def endtag(self, el):
283 if il or (self.last[0] == ">" and self.last[1] == el):
284 self.lastendbr = False
287 self.lastendbr = True
297 def handle(self, ev, el):
298 super().handle(ev, el)
301 class textindenter(indenter):
307 if len(left) + self.col > self.maxcol:
308 bp = max(self.maxcol - self.col, 0)
309 for i in range(bp, -1, -1):
310 if left[i].isspace():
311 while i > 0 and left[i - 1].isspace(): i -= 1
314 for i in range(bp + 1, len(left)):
315 if left[i].isspace():
320 self.quotewrite(left)
323 self.quotewrite(left[:i])
325 left = left[i + 1:].lstrip()
327 self.quotewrite(left)
330 class response(dispatch.restart):
335 def __init__(self, root):
341 raise Exception("a subclass of wrw.sp.util.response must override ctype")
343 def handle(self, req):
344 ret = self.formatter.format(self.root, doctype=self.doctype, charset=self.charset)
345 req.ohead["Content-Type"] = self.ctype
346 req.ohead["Content-Length"] = len(ret)