r47281 - Implement flattening optimization.
mithrandi-TA+aISz0psMTMxyoc4vAAJOcrHinNvQL0E9HWUfgJXw@public.gmane.org Sun, 17 Apr 2016 02:54:10 -0600 (MDT)
| Newsgroups | gmane.comp.python.twisted.commits |
|---|---|
| Message-ID | <[email protected]> |
Author: mithrandi
Date: Sun Apr 17 02:54:05 2016
New Revision: 47281
Modified:
branches/flatten-optimization-8300/twisted/web/_flatten.py
Log:
Implement flattening optimization.
Modified: branches/flatten-optimization-8300/twisted/web/_flatten.py
==============================================================================
--- branches/flatten-optimization-8300/twisted/web/_flatten.py (original)
+++ branches/flatten-optimization-8300/twisted/web/_flatten.py Sun Apr 17 02:54:05 2016
@@ -69,10 +69,10 @@
-def flattenWithAttributeEscaping(root):
+def writeWithAttributeEscaping(write):
"""
- Decorate the generator returned by L{_flattenElement} so that its output is
- properly quoted for inclusion within an XML attribute value.
+ Decorate a C{write} callable so that all output written is properly quoted
+ for inclusion within an XML attribute value.
If a L{Tag <twisted.web.template.Tag>} C{x} is flattened within the context
of the contents of another L{Tag <twisted.web.template.Tag>} C{y}, the
@@ -94,38 +94,22 @@
comments and CDATA, so if you were to serialize a L{comment
<twisted.web.template.Comment>} in an attribute you should get C{<y
attr="<-- comment -->" />}. Therefore in order to capture these
- meta-characters, the attribute generator from L{_flattenElement} context is
- wrapped with an L{flattenWithAttributeEscaping}.
-
- Because I{all} characters serialized in the context of an attribute are
- quoted before they are yielded by the generator returned by
- L{flattenWithAttributeEscaping}, on the "outside" of the L{_flattenElement}
- call, the L{_flattenElement} generator therefore no longer needs to quote
- text that appears directly within the attribute itself.
+ meta-characters, flattening is done with C{write} callable that is wrapped
+ with L{writeWithAttributeEscaping}.
The final case, and hopefully the much more common one as compared to
serializing L{Tag <twisted.web.template.Tag>} and arbitrary L{IRenderable}
objects within an attribute, is to serialize a simple string, and those
- should be passed through for L{flattenWithAttributeEscaping} to quote
+ should be passed through for L{writeWithAttributeEscaping} to quote
without applying a second, redundant level of quoting.
- @param root: A value that may be yielded by L{_flattenElement}; either an
- iterable yielding L{bytes} (or more iterables), or bytes itself.
- @type root: L{bytes} or C{iterable}
-
- @return: The same type as L{_flattenElement} returns, with all the bytes
- encoded for representation within an attribute.
- @rtype: the same type as the C{subFlatten} argument
- """
- if isinstance(root, bytes):
- root = escapeForContent(root)
- root = root.replace(b'"', b'"')
- yield root
- elif isinstance(root, Deferred):
- yield root.addCallback(flattenWithAttributeEscaping)
- else:
- for subroot in root:
- yield flattenWithAttributeEscaping(subroot)
+ @param write: A callable which will be invoked with the escaped L{bytes}.
+
+ @return: A callable that writes data with escaping.
+ """
+ def _write(data):
+ write(escapeForContent(data).replace(b'"', b'"'))
+ return _write
@@ -180,7 +164,8 @@
-def _flattenElement(request, root, slotData, renderFactory, dataEscaper):
+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.
@@ -193,6 +178,9 @@
L{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 C{list} of C{dict} mapping C{str} slot names to data
with which those slots will be replaced.
@@ -204,7 +192,7 @@
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{flattenWithAttributeEscaping}.
+ 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
@@ -217,22 +205,22 @@
of the same type.
"""
def keepGoing(newRoot, dataEscaper=dataEscaper,
- renderFactory=renderFactory):
- return _flattenElement(request, newRoot, slotData, renderFactory,
- dataEscaper)
+ renderFactory=renderFactory, write=write):
+ return _flattenElement(request, newRoot, write, slotData,
+ renderFactory, dataEscaper)
if isinstance(root, (bytes, unicode)):
- yield dataEscaper(root)
+ write(dataEscaper(root))
elif isinstance(root, slot):
slotValue = _getSlotValue(root.name, slotData, root.default)
yield keepGoing(slotValue)
elif isinstance(root, CDATA):
- yield b'<![CDATA['
- yield escapedCDATA(root.data)
- yield b']]>'
+ write(b'<![CDATA[')
+ write(escapedCDATA(root.data))
+ write(b']]>')
elif isinstance(root, Comment):
- yield b'<!--'
- yield escapedComment(root.data)
- yield b'-->'
+ write(b'<!--')
+ write(escapedComment(root.data))
+ write(b'-->')
elif isinstance(root, Tag):
slotData.append(root.slotData)
if root.render is not None:
@@ -249,23 +237,25 @@
yield keepGoing(root.children)
return
- yield b'<'
+ write(b'<')
if isinstance(root.tagName, unicode):
tagName = root.tagName.encode('ascii')
else:
tagName = root.tagName
- yield tagName
+ write(tagName)
for k, v in iteritems(root.attributes):
if isinstance(k, unicode):
k = k.encode('ascii')
- yield b' ' + k + b'="'
+ write(b' ' + k + b'="')
# Serialize the contents of the attribute, wrapping the results of
# that serialization so that _everything_ is quoted.
- attribute = keepGoing(v, attributeEscapingDoneOutside)
- yield flattenWithAttributeEscaping(attribute)
- yield b'"'
+ yield keepGoing(
+ v,
+ attributeEscapingDoneOutside,
+ write=writeWithAttributeEscaping(write))
+ write(b'"')
if root.children or nativeString(tagName) not in voidElements:
- yield b'>'
+ 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
@@ -274,16 +264,16 @@
# parse the tag within the attribute, all the quoting is still
# correct.
yield keepGoing(root.children, escapeForContent)
- yield b'</' + tagName + b'>'
+ write(b'</' + tagName + b'>')
else:
- yield b' />'
+ write(b' />')
elif isinstance(root, (tuple, list, GeneratorType)):
for element in root:
yield keepGoing(element)
elif isinstance(root, CharRef):
escaped = '&#%d;' % (root.ordinal,)
- yield escaped.encode('ascii')
+ write(escaped.encode('ascii'))
elif isinstance(root, Deferred):
yield root.addCallback(lambda result: (result, keepGoing(result)))
elif IRenderable.providedBy(root):
@@ -294,7 +284,7 @@
-def _flattenTree(request, root):
+def _flattenTree(request, root, write):
"""
Make C{root} into an iterable of L{bytes} and L{Deferred} by doing a depth
first traversal of the tree.
@@ -307,12 +297,15 @@
L{list}, L{GeneratorType}, L{Deferred}, or something providing
L{IRenderable}.
+ @param write: A callable which will be invoked with each L{bytes} produced
+ by flattening C{root}.
+
@return: An iterator which yields objects of type L{bytes} and L{Deferred}.
A L{Deferred} is only yielded when one is encountered in the process of
flattening C{root}. The returned iterator must not be iterated again
until the L{Deferred} is called back.
"""
- stack = [_flattenElement(request, root, [], None, escapeForContent)]
+ stack = [_flattenElement(request, root, write, [], None, escapeForContent)]
while stack:
try:
frame = stack[-1].gi_frame
@@ -328,7 +321,7 @@
raise FlattenerError(e, roots, extract_tb(exc_info()[2]))
else:
if type(element) is bytes:
- yield element
+ assert False
elif isinstance(element, Deferred):
def cbx(originalAndToFlatten):
original, toFlatten = originalAndToFlatten
@@ -366,8 +359,7 @@
result.errback()
else:
if type(element) is bytes:
- write(element)
- continue
+ assert False
else:
def cby(original):
_writeFlattenedData(state, write, result)
@@ -401,7 +393,7 @@
unexpected exception occurs.
"""
result = Deferred()
- state = _flattenTree(request, root)
+ state = _flattenTree(request, root, write)
_writeFlattenedData(state, write, result)
return result