def _flattenElement(request, root, write, slotData, renderFactory, dataEscaper): """ Make C{root} slightly more flat by yielding all its immediate contents as strings, deferreds or generators that are recursive calls to itself. @param request: A request object which will be passed to L{IRenderable.render}. @param root: An object to be made flatter. This may be of type C{unicode}, L{str}, L{slot}, L{Tag <twisted.web.template.Tag>}, L{tuple}, L{list}, L{types.GeneratorType}, L{Deferred}, or an object that implements L{IRenderable}. @param write: A callable which will be invoked with each L{bytes} produced by flattening C{root}. @param slotData: A L{list} of L{dict} mapping L{str} slot names to data with which those slots will be replaced. @param renderFactory: If not L{None}, an object that provides L{IRenderable}. @param dataEscaper: A 1-argument callable which takes L{bytes} or L{unicode} and returns L{bytes}, quoted as appropriate for the rendering context. This is really only one of two values: L{attributeEscapingDoneOutside} or L{escapeForContent}, depending on whether the rendering context is within an attribute or not. See the explanation in L{writeWithAttributeEscaping}. @return: An iterator that eventually yields L{bytes} that should be written to the output. However it may also yield other iterators or L{Deferred}s; if it yields another iterator, the caller will iterate it; if it yields a L{Deferred}, the result of that L{Deferred} will either be L{bytes}, in which case it's written, or another generator, in which case it is iterated. See L{_flattenTree} for the trampoline that consumes said values. @rtype: An iterator which yields L{bytes}, L{Deferred}, and more iterators of the same type. """ def keepGoing(newRoot, dataEscaper=dataEscaper, renderFactory=renderFactory, write=write): return _flattenElement(request, newRoot, write, slotData, renderFactory, dataEscaper) if isinstance(root, (bytes, str)): write(dataEscaper(root)) elif isinstance(root, slot): slotValue = _getSlotValue(root.name, slotData, root.default) yield keepGoing(slotValue) elif isinstance(root, CDATA): write(b"<![CDATA[") write(escapedCDATA(root.data)) write(b"]]>") elif isinstance(root, Comment): write(b"<!--") write(escapedComment(root.data)) write(b"-->") elif isinstance(root, Tag): slotData.append(root.slotData) if root.render is not None: rendererName = root.render rootClone = root.clone(False) rootClone.render = None renderMethod = renderFactory.lookupRenderMethod(rendererName) result = renderMethod(request, rootClone) yield keepGoing(result) slotData.pop() return if not root.tagName: yield keepGoing(root.children) return write(b"<") if isinstance(root.tagName, str): tagName = root.tagName.encode("ascii") else: tagName = root.tagName write(tagName) for k, v in root.attributes.items(): if isinstance(k, str): k = k.encode("ascii") write(b" " + k + b'="') # Serialize the contents of the attribute, wrapping the results of # that serialization so that _everything_ is quoted. yield keepGoing(v, attributeEscapingDoneOutside, write=writeWithAttributeEscaping(write)) write(b'"') if root.children or nativeString(tagName) not in voidElements: write(b">") # Regardless of whether we're in an attribute or not, switch back # to the escapeForContent dataEscaper. The contents of a tag must # be quoted no matter what; in the top-level document, just so # they're valid, and if they're within an attribute, they have to # be quoted so that after applying the *un*-quoting required to re- # parse the tag within the attribute, all the quoting is still # correct. yield keepGoing(root.children, escapeForContent) write(b"</" + tagName + b">") else: write(b" />") elif isinstance(root, (tuple, list, GeneratorType)): for element in root: yield keepGoing(element) elif isinstance(root, CharRef): escaped = "&#%d;" % (root.ordinal, ) write(escaped.encode("ascii")) elif isinstance(root, Deferred): yield root.addCallback(lambda result: (result, keepGoing(result))) elif iscoroutine(root): d = ensureDeferred(root) yield d.addCallback(lambda result: (result, keepGoing(result))) elif IRenderable.providedBy(root): result = root.render(request) yield keepGoing(result, renderFactory=root) else: raise UnsupportedType(root)
def _flattenElement(request, root, slotData, renderFactory, inAttribute): """ Make C{root} slightly more flat by yielding all its immediate contents as strings, deferreds or generators that are recursive calls to itself. @param request: A request object which will be passed to L{IRenderable.render}. @param root: An object to be made flatter. This may be of type C{unicode}, C{str}, L{slot}, L{Tag}, L{URL}, L{tuple}, L{list}, L{GeneratorType}, L{Deferred}, or an object that implements L{IRenderable}. @param slotData: A C{list} of C{dict} mapping C{str} slot names to data with which those slots will be replaced. @param renderFactory: If not C{None}, An object that provides L{IRenderable}. @param inAttribute: A flag which, if set, indicates that C{str} and C{unicode} instances encountered must be quoted as for XML tag attribute values. @return: An iterator which yields C{str}, L{Deferred}, and more iterators of the same type. """ if isinstance(root, (str, unicode)): yield escapedData(root, inAttribute) elif isinstance(root, slot): slotValue = _getSlotValue(root.name, slotData, root.default) yield _flattenElement(request, slotValue, slotData, renderFactory, inAttribute) elif isinstance(root, CDATA): yield '<![CDATA[' yield escapedCDATA(root.data) yield ']]>' elif isinstance(root, Comment): yield '<!--' yield escapedComment(root.data) yield '-->' elif isinstance(root, Tag): slotData.append(root.slotData) if root.render is not None: rendererName = root.render rootClone = root.clone(False) rootClone.render = None renderMethod = renderFactory.lookupRenderMethod(rendererName) result = renderMethod(request, rootClone) yield _flattenElement(request, result, slotData, renderFactory, False) slotData.pop() return if not root.tagName: yield _flattenElement(request, root.children, slotData, renderFactory, False) return yield '<' if isinstance(root.tagName, unicode): tagName = root.tagName.encode('ascii') else: tagName = str(root.tagName) yield tagName for k, v in root.attributes.iteritems(): if isinstance(k, unicode): k = k.encode('ascii') yield ' ' + k + '="' yield _flattenElement(request, v, slotData, renderFactory, True) yield '"' if root.children or tagName not in voidElements: yield '>' yield _flattenElement(request, root.children, slotData, renderFactory, False) yield '</' + tagName + '>' else: yield ' />' elif isinstance(root, (tuple, list, GeneratorType)): for element in root: yield _flattenElement(request, element, slotData, renderFactory, inAttribute) elif isinstance(root, CharRef): yield '&#%d;' % (root.ordinal, ) elif isinstance(root, Deferred): yield root.addCallback(lambda result: (result, _flattenElement(request, result, slotData, renderFactory, inAttribute))) elif IRenderable.providedBy(root): result = root.render(request) yield _flattenElement(request, result, slotData, root, inAttribute) else: raise UnsupportedType(root)