Rick Copeland avatar Rick Copeland committed 849e112

Some performance tweaks

Comments (0)

Files changed (9)

     def py(self):
         yield self.line(self.prefix)
         yield self.line('def %s:' % (self.decl))
-        for child in self.body:
+        for child in optimize(self.body):
             for line in child.py():
                 yield line.indent()
 
     def py(self):
         yield self.line('@__kj__.flattener.decorate')
         yield self.line('def %s:' % (self.decl))
-        for child in self.body:
+        for child in optimize(self.body):
             for line in child.py():
                 yield line.indent()
         yield self.line('yield ' + self.call)
 
     def py(self):
         yield self.line('for %s:' % (self.decl))
-        for child in self.body:
+        for child in optimize(self.body):
             for line in child.py():
                 yield line.indent()
 
 
     def py(self):
         yield self.line('local.__kj__.push_switch(%s)' % self.decl)
-        for child in self.body:
+        for child in optimize(self.body):
             for line in child.py():
                 yield line
         yield self.line('local.__kj__.pop_switch()')
 
     def py(self):
         yield self.line('if local.__kj__.case(%s):' % self.decl)
-        for child in self.body:
+        for child in optimize(self.body):
             for line in child.py():
                 yield line.indent()
 
 
     def py(self):
         yield self.line('if %s:' % self.decl)
-        for child in self.body:
+        for child in optimize(self.body):
             for line in child.py():
                 yield line.indent()
 
 
     def py(self):
         yield self.line('else:')
-        for child in self.body:
+        for child in optimize(self.body):
             for line in child.py():
                 yield line.indent()
 
             assert line.startswith(prefix)
             yield line[len(prefix):]
 
+def optimize(iter_node):
+    last_node = None
+    for node in iter_node:
+        if type(node) == TextNode:
+            if (type(last_node) == TextNode
+                and last_node.guard == node.guard):
+                last_node.text += node.text
+            else:
+                if last_node is not None: yield last_node
+                last_node = node
+        else:
+            if last_node is not None: yield last_node
+            last_node = node
+    if last_node is not None:
+        yield last_node
+
 class PyLine(object):
 
     def __init__(self, filename, lineno, text, indent=0):

kajiki/perf/genshi_bench/basic.py

 import timeit
 
 __all__ = ['kajiki', 'mako', 'jinja2', 'genshi', 'genshi_text']
-__all__ = ['kajiki' ]
+# __all__ = ['kajiki' ]
 
 def kajiki(dirname, verbose=False):
     from kajiki import FileLoader
     loader = FileLoader(base=dirname)
     template = loader.load('template.html')
-    print template.py_text
     def render():
         data = dict(title='Just a test', user='joe',
                     items=['Number %d' % num for num in range(1, 15)])

kajiki/perf/genshi_bench/bigtable.py

         mako_tmpl.render(table=table)
 
 def test_kajiki():
-    """FastPt Template"""
+    """Kajiki Template"""
     kajiki_tmpl(dict(table=table)).render()
 
 def test_genshi():
         stats = pstats.Stats(prof)
         stats.strip_dirs()
         stats.sort_stats('time', 'calls')
-        # stats.print_stats(25)
-        stats.print_stats()
+        stats.print_stats(25)
+        # stats.print_stats()
         if '-v' in sys.argv:
             stats.print_callees()
             stats.print_callers()

kajiki/perf/genshi_bench/kajiki/base.html

+<!DOCTYPE html
+    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 <html>
-  <head>
-    <title>${title}</title>
-  </head>
-
-  <body>
-    <div id="header">
-      <h1>${title}</h1>
+  <py:block name="content">
+    <div id="header" py:block="header">
+      <h1>$title</h1>
     </div>
-    <py:block name="content"/>
-    <div id="footer"/>
-  </body>
-
+    <div id="footer" py:block="footer"/>
+  </py:block>
 </html>
-

kajiki/perf/genshi_bench/kajiki/template.html

+<!DOCTYPE html
+    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 <py:extends href="base.html">
+  <py:def function="greeting(name)">Hello, $name</py:def>
   <py:block name="content">
-    <py:import href="lib.html" alias="lib"/>
-    <div>${lib.greeting(user)}</div>
-    <div>${lib.greeting('me')}</div>
-    <div>${lib.greeting('world')}</div>
+    <head>
+      <title>$title</title>
+    </head>
+    <body>
+      <py:block name="header"/>
+      <div>${greeting(user)}</div>
+      <div>${greeting('me')}</div>
+      <div>${greeting('world')}</div>
 
-    <h2>Loop</h2>
-    <ul py:if="items">
-      <li py:for="idx, item in enumerate(items)" py:content="item"
-          class="${idx + 1 == len(items) and 'last' or None}" />
-    </ul>
+      <h2>Loop</h2>
+      <ul py:if="items">
+        <li py:for="idx, item in enumerate(items)" py:content="item"
+            class="${idx + 1 == len(items) and 'last' or None}" />
+      </ul>
+      <py:block name="footer"/>
+    </body>
   </py:block>
 </py:extends>

kajiki/template.py

+import re
 import types
 from cStringIO import StringIO
 from cgi import escape
 from functools import update_wrapper
 from pprint import pprint
 
-from webhelpers.html import literal
-
 import kajiki
-from .util import flattener
+from .util import flattener, literal
 from .html_utils import HTML_EMPTY_ATTRS
 from . import lnotab
 
+re_escape = re.compile(r'&|<|>')
+escape_dict ={
+    '&':'&amp;',
+    '<':'&lt;',
+    '>':'&gt;'}
+
 class _obj(object):
     def __init__(self, **kw):
         for k,v in kw.iteritems():
             yield unicode(chunk)
 
     def render(self):
-        # return self.__main__()
-        return self.__main__().accumulate_str()
+        return u''.join(self)
 
     def _extend(self, parent):
         if isinstance(parent, basestring):
     def _escape(self, value):
         if type(value) == flattener:
             return value
-        if hasattr(value, '__html__'):
-            return value.__html__()
+        uval = unicode(value)
+        if re_escape.search(uval):
+            return escape(uval)
         else:
-            return escape(unicode(value))
+            return uval
 
     def _render_attrs(self, attrs, mode):
         if hasattr(attrs, 'items'):

kajiki/tests/test_xml.py

         assert rsp == '<input type="checkbox" CHECKED>', rsp
         tpl = XMLTemplate('''<!DOCTYPE html>\n<input type="checkbox" checked="$checked"/>''')
         rsp = tpl(dict(checked=True)).render()
-        assert rsp == '<!DOCTYPE html><input type="checkbox" checked="True"/>', rsp
+        assert rsp == '<!DOCTYPE html><input type="checkbox" CHECKED>', rsp
         tpl = XMLTemplate('''<input type="checkbox" checked="$checked"/>''',
                           mode='html5')
         rsp = tpl(dict(checked=True)).render()
 from random import randint
 from threading import local
 
-from webhelpers.html import literal
-
 def debug():# pragma no cover
     def pm(etype, value, tb): 
         import pdb, traceback
 class flattener(object):
 
     def __init__(self, iterator):
+        while type(iterator) == flattener:
+            iterator = iterator.iterator
         self.iterator = iterator
 
     @classmethod
             else:
                 yield x
 
+def literal(text):
+    return flattener(iter([text]))
+
 class NameGen(object):
     lcl = local()
     def __init__(self):

kajiki/xml_template.py

     return inner
 
 class _Compiler(object):
+    mode_lookup = {
+        'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd':'xml',
+        'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd':'xml',
+        'http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd':'xml',
+        'http://www.w3.org/TR/html4/strict.dtd':'html',
+        'http://www.w3.org/TR/html4/loose.dtd':'html',
+        'http://www.w3.org/TR/html4/frameset.dtd':'html',
+        }
 
-    def __init__(self, filename, doc, mode='xml', is_fragment=False):
+    def __init__(self, filename, doc, mode='xml', is_fragment=False, force_mode=False):
         self.filename = filename
         self.doc = doc
         self.mode = mode
         self.in_def = False
         self.is_child = False
         self.is_fragment = is_fragment
+        if not force_mode and self.doc.doctype:
+            if self.doc.doctype.toxml().lower() == '<!doctype html>':
+                self.mode = 'html5'
+            elif self.doc.doctype.systemId is None:
+                self.mode = 'html'
+            else:
+                self.mode = self.mode_lookup.get(
+                    self.doc.doctype.systemId, 'xml')
 
     def compile(self):
         body = list(self._compile_node(self.doc.firstChild))
-        if not self.is_fragment: # Never emit doctypes on fragments
+        if not self.is_fragment and not self.is_child: # Never emit doctypes on fragments
             if self.mode == 'xml' and self.doc.doctype:
                 dt = ir.TextNode(self.doc.doctype.toxml())
                 dt.filename = self.filename
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.