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):
43 elif isinstance(ch, cons.comment):
46 raise Exception("Unknown object in element tree: " + el)
48 class formatter(object):
49 def __init__(self, src, nsnames=None, charset="utf-8"):
51 self.nsnames = nsnames or {}
54 self.buf = bytearray()
55 self.charset = charset
57 def write(self, text):
58 self.buf.extend(text.encode(self.charset))
60 def quotewrite(self, buf):
61 buf = buf.replace('&', "&")
62 buf = buf.replace('<', "<")
63 buf = buf.replace('>', ">")
70 ns = self.nsnames[el.ns]
74 return ns + ":" + el.name
77 qc, qt = ("'", "'") if '"' in v else ('"', """)
79 v = v.replace('&', "&")
80 v = v.replace('<', "<")
81 v = v.replace('>', ">")
91 def attrs(self, attrs):
96 def inittag(self, el):
97 self.write("<" + self.elname(el))
98 attrs = el.attrs.items()
101 for ns, name in self.nsnames.items():
104 raise Exception("null namespace must have null name, not" + name)
106 nsnames.append(("xmlns" if name is None else ("xmlns:" + name), ns))
107 attrs = itertools.chain(attrs, iter(nsnames))
111 def starttag(self, el):
115 def shorttag(self, el):
119 def endtag(self, el):
120 self.write("</" + self.elname(el) + ">")
125 def rawcode(self, el):
128 def comment(self, el):
129 self.write("<!-- " + str(el) + " -->")
132 self.write('<?xml version="1.0" encoding="' + self.charset + '" ?>\n')
133 if isinstance(el, cons.doctype):
134 self.write('<!DOCTYPE %s PUBLIC "%s" "%s">\n' % (el.rootname,
142 def handle(self, ev, el):
162 raise StopIteration()
164 ev, el = next(self.src)
165 except StopIteration:
169 ret = bytes(self.buf)
173 def nsname(self, el):
174 for t in type(self).__mro__:
175 ret = getattr(t, "defns", {}).get(el.ns, None)
180 ret = "n" + str(self.nextns)
184 def findnsnames(self, root):
188 if isinstance(el, cons.element):
189 if el.ns not in fnames:
193 for ch in el.children:
196 if None not in rnames:
197 fnames[root.ns] = None
198 rnames[None] = root.ns
199 self.nsnames = fnames
202 def output(cls, out, root, nsnames=None, doctype=None, **kw):
203 if isinstance(doctype, cons.doctype):
205 elif doctype is not None:
206 doctype = cons.doctype(root.name, doctype[0], doctype[1])
207 src = itertools.chain(iter([("^", doctype)]), flatiter(root))
208 self = cls(src=src, nsnames=nsnames, **kw)
210 self.findnsnames(root)
216 def fragment(cls, out, root, nsnames=None, **kw):
217 self = cls(src=flatiter(root), nsnames=nsnames, **kw)
219 self.findnsnames(root)
224 def format(cls, root, **kw):
226 cls.output(buf, root, **kw)
227 return buf.getvalue()
229 class indenter(formatter):
230 def __init__(self, indent=" ", *args, **kw):
231 super().__init__(*args, **kw)
238 self.last = None, None
239 self.lastendbr = True
241 def write(self, text):
242 lines = text.split("\n")
244 for ln in lines[:-1]:
245 self.buf.extend(ln.encode(self.charset))
246 self.buf.extend(b"\n")
248 self.buf.extend(lines[-1].encode(self.charset))
249 self.col += len(lines[-1])
254 self.buf.extend(("\n" + self.curind).encode(self.charset))
258 def inlinep(self, el):
259 for ch in el.children:
260 if isinstance(ch, cons.text):
265 self.stack.append((el, self.curind, self.inline))
268 el, self.curind, self.inline = self.stack.pop()
271 def starttag(self, el):
273 if self.last[0] == "<" and self.last[1].name == el.name and self.lastendbr:
278 self.inline = self.inline or self.inlinep(el)
279 self.curind += self.indent
282 def shorttag(self, el):
287 def endtag(self, el):
290 if il or (self.last[0] == ">" and self.last[1] == el):
291 self.lastendbr = False
294 self.lastendbr = True
304 def handle(self, ev, el):
305 super().handle(ev, el)
308 class textindenter(indenter):
314 if len(left) + self.col > self.maxcol:
315 bp = max(self.maxcol - self.col, 0)
316 for i in range(bp, -1, -1):
317 if left[i].isspace():
318 while i > 0 and left[i - 1].isspace(): i -= 1
321 for i in range(bp + 1, len(left)):
322 if left[i].isspace():
327 self.quotewrite(left)
330 self.quotewrite(left[:i])
332 left = left[i + 1:].lstrip()
334 self.quotewrite(left)
337 class response(dispatch.restart):
342 def __init__(self, root):
348 raise Exception("a subclass of wrw.sp.util.response must override ctype")
350 def handle(self, req):
351 ret = self.formatter.format(self.root, doctype=self.doctype, charset=self.charset)
352 req.ohead["Content-Type"] = self.ctype
353 req.ohead["Content-Length"] = len(ret)