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,
139 raise StopIteration()
141 ev, el = next(self.src)
142 except StopIteration:
159 ret = bytes(self.buf)
163 def nsname(self, el):
164 for t in type(self).__mro__:
165 ret = getattr(t, "defns", {}).get(el.ns, None)
170 ret = "n" + str(self.nextns)
174 def findnsnames(self, root):
178 if isinstance(el, cons.element):
179 if el.ns not in fnames:
183 for ch in el.children:
186 if None not in rnames:
187 fnames[root.ns] = None
188 rnames[None] = root.ns
189 self.nsnames = fnames
192 def output(cls, out, root, nsnames=None, doctype=None, **kw):
193 if isinstance(doctype, cons.doctype):
195 elif doctype is not None:
196 doctype = cons.doctype(root.name, doctype[0], doctype[1])
197 src = itertools.chain(iter([("^", doctype)]), flatiter(root))
198 self = cls(src=src, nsnames=nsnames, **kw)
200 self.findnsnames(root)
206 def fragment(cls, out, root, nsnames=None, **kw):
207 self = cls(src=flatiter(root), nsnames=nsnames, **kw)
209 self.findnsnames(root)
214 def format(cls, root, **kw):
216 cls.output(buf, root, **kw)
217 return buf.getvalue()
219 class indenter(formatter):
220 def __init__(self, indent=" ", *args, **kw):
221 super().__init__(*args, **kw)
229 def write(self, text):
230 lines = text.split("\n")
232 for ln in lines[:-1]:
233 self.buf.extend(ln.encode(self.charset))
234 self.buf.extend(b"\n")
236 self.buf.extend(lines[-1].encode(self.charset))
237 self.col += len(lines[-1])
242 self.buf.extend(("\n" + self.curind).encode(self.charset))
246 def inlinep(self, el):
247 for ch in el.children:
248 if isinstance(ch, cons.text):
253 self.stack.append((el, self.curind, self.inline))
256 el, self.curind, self.inline = self.stack.pop()
259 def starttag(self, el):
263 self.inline = self.inline or self.inlinep(el)
264 self.curind += self.indent
267 def shorttag(self, el):
272 def endtag(self, el):
286 class textindenter(indenter):
292 if len(left) + self.col > self.maxcol:
293 bp = max(self.maxcol - self.col, 0)
294 for i in range(bp, -1, -1):
295 if left[i].isspace():
296 while i > 0 and left[i - 1].isspace(): i -= 1
299 for i in range(bp + 1, len(left)):
300 if left[i].isspace():
305 self.quotewrite(left)
308 self.quotewrite(left[:i])
310 left = left[i + 1:].lstrip()
312 self.quotewrite(left)
315 class response(dispatch.restart):
320 def __init__(self, root):
326 raise Exception("a subclass of wrw.sp.util.response must override ctype")
328 def handle(self, req):
329 ret = self.formatter.format(self.root, doctype=self.doctype, charset=self.charset)
330 req.ohead["Content-Type"] = self.ctype
331 req.ohead["Content-Length"] = len(ret)