[pypy-svn] r40438 - in pypy/branch/guido-buildtool-web/pypy/tool/build/web: . templates test

guido at codespeak.net guido at codespeak.net
Tue Mar 13 14:38:17 CET 2007


Author: guido
Date: Tue Mar 13 14:38:15 2007
New Revision: 40438

Added:
   pypy/branch/guido-buildtool-web/pypy/tool/build/web/README_TEMPLESSER.txt
   pypy/branch/guido-buildtool-web/pypy/tool/build/web/templesser.py
   pypy/branch/guido-buildtool-web/pypy/tool/build/web/test/test_templesser.py
Modified:
   pypy/branch/guido-buildtool-web/pypy/tool/build/web/README.txt
   pypy/branch/guido-buildtool-web/pypy/tool/build/web/app.py
   pypy/branch/guido-buildtool-web/pypy/tool/build/web/templates/build.html
   pypy/branch/guido-buildtool-web/pypy/tool/build/web/templates/buildersinfo.html
   pypy/branch/guido-buildtool-web/pypy/tool/build/web/templates/builds.html
   pypy/branch/guido-buildtool-web/pypy/tool/build/web/templates/serverstatus.html
Log:
Replaced Templess with Templesser, which is basically an extension to string
interpolation that allows repeating blocks and marking blocks as conditional.
This removes the dependency on a third-party library.


Modified: pypy/branch/guido-buildtool-web/pypy/tool/build/web/README.txt
==============================================================================
--- pypy/branch/guido-buildtool-web/pypy/tool/build/web/README.txt	(original)
+++ pypy/branch/guido-buildtool-web/pypy/tool/build/web/README.txt	Tue Mar 13 14:38:15 2007
@@ -31,19 +31,11 @@
 ------------
 
 The dependencies for this package are a reasonably new Python (tested on 2.4),
-a recent PyPy checkout or release (which you have, else you wouldn't be reading
-this ;) and the Templess templating library, which can be checked out from
-Subversion using the following command::
-
-  $ svn co http://johnnydebris.net/templess/trunk templess
-
-or downloaded from the following location::
-
-  http://johnnydebris.net/templess_package
-
-Minimal version required is 0.2.
+and a recent PyPy checkout or release (which you have, else you wouldn't be
+reading this ;).
 
 Questions, remarks, etc.
 ------------------------
 
 For questions, remarks, etc. about this product, mail guido at merlinux.de.
+

Added: pypy/branch/guido-buildtool-web/pypy/tool/build/web/README_TEMPLESSER.txt
==============================================================================
--- (empty file)
+++ pypy/branch/guido-buildtool-web/pypy/tool/build/web/README_TEMPLESSER.txt	Tue Mar 13 14:38:15 2007
@@ -0,0 +1,75 @@
+Templesser
+===========
+
+What is it?
+-----------
+
+Templesser is basically an extension to string interpolation that introduces
+blocks that can be repeated and conditional blocks.
+
+Blocks start with '%(<name>)[b', where <name> is the name of the block,
+and end with '%(<name>)]b', where <name> is again the name of the block
+and must match the opening name.
+
+The 'b' in the previous case marks a basic repeat block, which is repeated for
+all items in a list; there are also 'c' (conditional) blocks that are rendered
+only if the value for the context key resolves to False (and not to an empty
+string).
+
+This module is a drop-in replacement for Templess' basic functionality,
+see the Templess documentation (http://templess.johnnydebris.net) for more
+details.
+
+Let's show some examples, the first displays how Templesser can do
+normal string interpolation::
+
+  >>> from pypy.tool.build.web.templesser import template
+  >>> t = template(u'foo %(bar)s baz')
+  >>> t.unicode({'bar': 'qux'})
+  u'foo qux baz'
+
+The second example displays how to deal with a simple conditional block::
+
+  >>> t = template(u'foo %(bar)[cbar %(bar)]cbaz')
+  >>> t.unicode({'bar': False})
+  u'foo baz'
+  >>> t.unicode({'bar': True})
+  u'foo bar baz'
+
+Now an example with a repeat block, note how the context value is a list
+type - this can be any type of iterable, but _must_ be a list type, even
+if there's only a single item to interpolate::
+
+  >>> t = template(u'foo %(bar)[b %(bar)]b baz')
+  >>> t.unicode({'bar': [1, 2, 3]})
+  u'foo 1 2 3 baz'
+
+A more useful example with a repeat block uses nested dictionaries as
+values for the list, resulting in a nested interpolation::
+
+  >>> t = template(u'foo %(bar)[b%(baz)s qux %(quux)s%(bar)]b quuux')
+  >>> t.unicode({'bar': [{'baz': 1, 'quux': 2},
+  ...                    {'baz': 'spam', 'quux': 'eggs'}]})
+  u'foo 1 qux 2 spam qux eggs quuux'
+
+Some quick notes
+=================
+
+* yes, this is ugly stuff...
+
+* my motivation? simplicity... it's inspired on templess
+  (http://templess.johnnydebris.net) which does similar things
+  but with a more elegant (XML based) syntax, we decided to remove
+  the templess dependency, but i didn't feel like modifying the
+  code too much, so i wanted to have something very simple with the
+  same api and similar behaviour
+
+* there has been no time spent on error reporting at all, so
+  it most probably sucks...
+
+Questions, remarks, etc.
+=========================
+
+For questions, remarks, bug reports and patches, send email to
+guido at merlinux.de.
+

Modified: pypy/branch/guido-buildtool-web/pypy/tool/build/web/app.py
==============================================================================
--- pypy/branch/guido-buildtool-web/pypy/tool/build/web/app.py	(original)
+++ pypy/branch/guido-buildtool-web/pypy/tool/build/web/app.py	Tue Mar 13 14:38:15 2007
@@ -9,7 +9,7 @@
 from pypy.tool.build.build import BuildRequest
 from pypy.tool.build.web.server import HTTPError, Collection, Handler, FsFile
 
-from templess import templess
+from pypy.tool.build.web import templesser
 
 mypath = py.magic.autopath().dirpath()
 
@@ -71,7 +71,7 @@
     """ a page displaying overall meta server statistics """
 
     def __call__(self, handler, path, query):
-        template = templess.template(
+        template = templesser.template(
             mypath.join('templates/serverstatus.html').read())
         return ({'Content-Type': 'text/html; charset=UTF-8'},
                 fix_html(template.unicode(self.get_status())))
@@ -81,7 +81,7 @@
 
 class BuildersInfoPage(ServerPage):
     def __call__(self, handler, path, query):
-        template = templess.template(
+        template = templesser.template(
             mypath.join('templates/buildersinfo.html').read())
         return ({'Content-Type': 'text/html; charset=UTF-8'},
                 fix_html(template.unicode({'builders':
@@ -92,6 +92,7 @@
         # some massaging of the data for Templess
         for binfo in infos:
             binfo['sysinfo'] = [binfo['sysinfo']]
+            binfo['not_busy'] = not binfo['busy_on']
             if binfo['busy_on']:
                 b = binfo['busy_on']
                 req = BuildRequest.fromstring(binfo['busy_on'])
@@ -101,7 +102,7 @@
                 d['href'] = '/builds/%s' % (id,)
                 d.pop('sysinfo', None) # same as builder
                 d.pop('build_end_time', None) # it's still busy ;)
-                # templess doesn't understand dicts this way...
+                # templesser doesn't understand dicts this way...
                 d['compileinfo'] = [{'key': k, 'value': v} for (k, v) in
                                     d['compileinfo'].items()]
                 for key in ['request_time', 'build_start_time']:
@@ -119,7 +120,7 @@
         self._buildid = buildid
 
     def __call__(self, handler, path, query):
-        template = templess.template(
+        template = templesser.template(
             mypath.join('templates/build.html').read())
         return ({'Content-Type': 'text/html; charset=UTF-8'},
                 fix_html(template.unicode(self.get_info())))
@@ -165,7 +166,7 @@
     """ display the list of available builds """
 
     def __call__(self, handler, path, query):
-        template = templess.template(
+        template = templesser.template(
             mypath.join('templates/builds.html').read())
         return ({'Content-Type': 'text/html; charset=UTF-8'},
                 fix_html(template.unicode({'builds': self.get_builds()})))
@@ -213,7 +214,7 @@
         self.builds = Builds(config)
     
     def index(self, handler, path, query):
-        template = templess.template(
+        template = templesser.template(
             mypath.join('templates/index.html').read())
         return ({'Content-Type': 'text/html; charset=UTF-8'},
                 fix_html(template.unicode({})))
@@ -222,7 +223,7 @@
 class AppHandler(Handler):
     def __init__(self, *args, **kwargs):
         self.application = Application(config)
-        super(AppHandler, self).__init__(*args, **kwargs)
+        Handler.__init__(self, *args, **kwargs)
 
 class MetaServerAccessor(object):
     def __init__(self, ms):

Modified: pypy/branch/guido-buildtool-web/pypy/tool/build/web/templates/build.html
==============================================================================
--- pypy/branch/guido-buildtool-web/pypy/tool/build/web/templates/build.html	(original)
+++ pypy/branch/guido-buildtool-web/pypy/tool/build/web/templates/build.html	Tue Mar 13 14:38:15 2007
@@ -1,6 +1,6 @@
-<html xmlns:t="http://johnnydebris.net/xmlns/templess">
+<html>
   <head>
-    <title>Build meta server build <t:tag t:replace="id" /></title>
+    <title>Build meta server build %(id)s</title>
   </head>
   <body>
     <ul class="sidebar">
@@ -8,43 +8,53 @@
       <li><a href="/buildersinfo">connected build servers</a></li>
       <li><a href="/builds">builds (both in-progress and done)</a></li>
     </ul>
-    <h2>Build <t:tag t:replace="id" /></h2>
+    <h2>Build %(id)s</h2>
     <div class="infoblock">
-      <div t:cond="url">
-        <span class="title">url:</span>
-        <a t:attr="href url" t:content="url"></a>
-      </div>
+      %(url)[c
+        <div>
+          <span class="title">url:</span>
+          <a href="%(url)s">%(url)s</a>
+        </div>
+      %(url)]c
       <div>
         <span class="title">status:</span>
-        <span t:content="status" />
-      </div>
-      <div t:cond="error" class="error">
-        <span class="title">error:</span>
-        <span t:content="error" />
+        <span>%(status)s</span>
       </div>
+      %(error)[c
+        <div class="error">
+          <span class="title">error:</span>
+          <span>%(error)s</span>
+        </div>
+      %(error)]c
       <div>
         <span class="title">svn url:</span>
-        <span t:content="svnurl" />
+        <span>%(svnurl)s</span>
       </div>
       <div>
         <span class="title">svn revision:</span>
-        <span t:content="svnrev" />
+        <span>%(svnrev)s</span>
       </div>
       <div>
         <span class="title">request time:</span>
-        <span t:content="request_time" />
-      </div>
-      <div t:cond="build_start_time">
-        <span class="title">started:</span>
-        <span t:content="build_start_time" />
+        <span>%(request_time)s</span>
       </div>
-      <div t:cond="build_end_time">
-        <span class="title">ended:</span>
-        <span t:content="build_end_time" />
-      </div>
-      <div t:cond="log">
-        <pre t:content="log" />
+      %(build_start_time)[c
+        <div>
+          <span class="title">started:</span>
+          <span>%(build_start_time)s</span>
+        </div>
+      %(build_start_time)]c
+      %(build_end_time)[c
+        <div>
+          <span class="title">ended:</span>
+          <span>%(build_end_time)s</span>
+        </div>
+      %(build_end_time)]c
+      %(log)[c
+      <div>
+        <pre>%(log)s</pre>
       </div>
+      %(log)]c
     </div>
   </body>
 </html>

Modified: pypy/branch/guido-buildtool-web/pypy/tool/build/web/templates/buildersinfo.html
==============================================================================
--- pypy/branch/guido-buildtool-web/pypy/tool/build/web/templates/buildersinfo.html	(original)
+++ pypy/branch/guido-buildtool-web/pypy/tool/build/web/templates/buildersinfo.html	Tue Mar 13 14:38:15 2007
@@ -1,4 +1,4 @@
-<html xmlns:t="http://johnnydebris.net/xmlns/templess">
+<html>
   <head>
     <title>Build meta server builders page</title>
     <link rel="stylesheet" type="text/css" href="/style" />
@@ -11,62 +11,70 @@
     </ul>
     <h2>Connected build servers</h2>
     <div class="builders">
-      <div class="builder" t:content="builders">
-        <h3 t:content="hostname" />
-        <div class="builderinfo">
-          <div class="infoblock">
-            <div class="title">sysinfo:</div>
-            <div class="dict" t:content="sysinfo">
-              <div class="pair">
-                <span class="key">os:</span>
-                <span class="value" t:content="os" />
-              </div>
-              <div class="pair">
-                <span class="key">maxint:</span>
-                <span class="value" t:content="maxint" />
-              </div>
-              <div class="pair">
-                <span class="key">byteorder:</span>
-                <span class="value" t:content="byteorder" />
+      %(builders)[b
+        <div class="builder">
+          <h3>%(hostname)s</h3>
+          <div class="builderinfo">
+            <div class="infoblock">
+              <div class="title">sysinfo:</div>
+              %(sysinfo)[b
+                <div class="dict">
+                  <div class="pair">
+                    <span class="key">os:</span>
+                    <span class="value">%(os)s</span>
+                  </div>
+                  <div class="pair">
+                    <span class="key">maxint:</span>
+                    <span class="value">%(maxint)s</span>
+                  </div>
+                  <div class="pair">
+                    <span class="key">byteorder:</span>
+                    <span class="value">%(byteorder)s</span>
+                  </div>
+                %(sysinfo)]b
               </div>
             </div>
-          </div>
-          <div class="infoblock">
-            <div class="title">busy on build:</div>
-            <div class="value" t:not="busy_on">nothing</div>
-            <div class="dict" t:cond="busy_on" t:content="busy_on">
-              <a class="title" t:attr="href href" t:content="id" />
-              <div class="pair">
-                <span class="key">request time:</span>
-                <span class="value" t:content="request_time" />
-              </div>
-              <div class="pair">
-                <span class="key">build start time:</span>
-                <span class="value" t:content="request_time" />
-              </div>
-              <div class="pair">
-                <span class="key">svn url:</span>
-                <span class="value" t:content="svnurl" />
-              </div>
-              <div class="pair">
-                <span class="key">svn revision:</span>
-                <span class="value" t:content="normalized_rev" />
-              </div>
-              <!--
-              <div class="pair">
-                <div class="key">compile info:</div>
-                <div class="sub">
-                  <div class="pair" t:content="compileinfo">
-                    <span class="key" t:content="key" />:
-                    <span class="value" t:content="value" />
+            <div class="infoblock">
+              <div class="title">busy on build:</div>
+              %(not_busy)[c<div class="value">nothing</div>%(not_busy)]c
+              %(busy_on)[c
+                <div class="dict">%(busy_on)s</div>
+                  <a class="title" href="%(href)s">%(id)s</a>
+                  <div class="pair">
+                    <span class="key">request time:</span>
+                    <span class="value">%(request_time)s</span>
+                  </div>
+                  <div class="pair">
+                    <span class="key">build start time:</span>
+                    <span class="value">%(request_time)s</span>
+                  </div>
+                  <div class="pair">
+                    <span class="key">svn url:</span>
+                    <span class="value">%(svnurl)s</span>
                   </div>
+                  <div class="pair">
+                    <span class="key">svn revision:</span>
+                    <span class="value">%(normalized_rev)s</span>
+                  </div>
+                  <!--
+                  <div class="pair">
+                    <div class="key">compile info:</div>
+                    <div class="sub">
+                      %%(compileinfo)[b
+                        <div class="pair">
+                          <span class="key">%%(key)s</span>:
+                          <span class="value">%%(value)s</span>
+                        </div>
+                      %%(compileinfo)]b
+                    </div>
+                  </div>
+                  -->
                 </div>
-              </div>
-              -->
+              %(busy_on)]c
             </div>
           </div>
         </div>
-      </div>
+      %(builders)]b
     </div>
   </body>
 </html>

Modified: pypy/branch/guido-buildtool-web/pypy/tool/build/web/templates/builds.html
==============================================================================
--- pypy/branch/guido-buildtool-web/pypy/tool/build/web/templates/builds.html	(original)
+++ pypy/branch/guido-buildtool-web/pypy/tool/build/web/templates/builds.html	Tue Mar 13 14:38:15 2007
@@ -1,4 +1,4 @@
-<html xmlns:t="http://johnnydebris.net/xmlns/templess">
+<html>
   <head>
     <title>Build meta server builds page</title>
   </head>
@@ -14,11 +14,13 @@
       for download) and waiting or in-progress ones, ordered by date. Click
       on the build to get more information.
     </p>
-    <div class="infoblock" t:content="builds">
-      <div class="title">
-        <a t:attr="href href" t:content="id" />
+    %(builds)[b
+      <div class="infoblock">
+        <div class="title">
+          <a href="%(href)s">%(id)s</a>
+        </div>
       </div>
-    </div>
+    %(builds)]b
   </body>
 </html>
 

Modified: pypy/branch/guido-buildtool-web/pypy/tool/build/web/templates/serverstatus.html
==============================================================================
--- pypy/branch/guido-buildtool-web/pypy/tool/build/web/templates/serverstatus.html	(original)
+++ pypy/branch/guido-buildtool-web/pypy/tool/build/web/templates/serverstatus.html	Tue Mar 13 14:38:15 2007
@@ -10,11 +10,11 @@
     </ul>
     <h2>Server status</h2>
     <ul>
-      <li>Connected build servers: <t:block t:replace="builders" /></li>
-      <li>Currently running builds: <t:block t:replace="running" /></li>
-      <li>Builds done: <t:block t:replace="done" /></li>
-      <li>Clients waiting for a build: <t:block t:replace="waiting" /></li>
-      <li>Builds for which no suitable server is available: <t:block t:replace="queued" /></li>
+      <li>Connected build servers: %(builders)s</li>
+      <li>Currently running builds: %(running)s</li>
+      <li>Builds done: %(done)s</li>
+      <li>Clients waiting for a build: %(waiting)s</li>
+      <li>Builds for which no suitable server is available: %(queued)s</li>
     </ul>
   </body>
 </html>

Added: pypy/branch/guido-buildtool-web/pypy/tool/build/web/templesser.py
==============================================================================
--- (empty file)
+++ pypy/branch/guido-buildtool-web/pypy/tool/build/web/templesser.py	Tue Mar 13 14:38:15 2007
@@ -0,0 +1,74 @@
+""" even lighter weight replacement for templess
+
+    see README_TEMPLESSER.txt for more details
+"""
+
+import re
+
+class template(object):
+    """ string interpolation on steriods """
+    def __init__(self, input):
+        self.input = input
+        
+    def unicode(self, context):
+        """ resolve interpolations using context as data dict """
+        data = self.input
+        data = self._resolve_repeats(data, context)
+        data = self._resolve_conditionals(data, context)
+        data = data % context
+        return data
+
+    _reg_cond_1 = re.compile(r'([^%])([%]{2})*[%][(]([^)]+)[)][[]c(.*)'
+                             r'[%][(]\3[)][]]c(.*?)$', re.S | re.U)
+    _reg_cond_2 = re.compile(r'^[%][(]([^)]+)[)][[]c(.*)'
+                             r'[%][(]\1[)][]]c(.*?)$', re.S | re.U)
+    def _resolve_conditionals(self, data, context):
+        while 1:
+            match = self._reg_cond_1.search(data)
+            offset = 2
+            if not match:
+                match = self._reg_cond_2.search(data)
+                if not match:
+                    break
+                offset = 0
+            key = match.group(offset + 1)
+            pre = data[:data.find(match.group(0))]
+            data = pre
+            if offset == 2:
+                data += (match.group(1) or '') + (match.group(2) or '')
+            if context[key]:
+                data += match.group(offset + 2) or ''
+            data += match.group(offset + 3) or ''
+        return data
+
+    _reg_rept_1 = re.compile(r'([^%])([%]{2})*[%][(]([^)]+)[)][[]b(.*)'
+                             r'[%][(]\3[)][]]b(.*?)$', re.S | re.U)
+    _reg_rept_2 = re.compile(r'^[%][(]([^)]+)[)][[]b(.*)'
+                             r'[%][(]\1[)][]]b(.*?)$', re.S | re.U)
+    def _resolve_repeats(self, data, context):
+        while 1:
+            match = self._reg_rept_1.search(data)
+            offset = 2
+            if not match:
+                match = self._reg_rept_2.search(data)
+                if not match:
+                    break
+                offset = 0
+            key = match.group(offset + 1)
+            # here we just assume the content is an iterable
+            processed = []
+            for subcontext in context[key]:
+                if isinstance(subcontext, dict):
+                    t = template(match.group(offset + 2))
+                    processed.append(t.unicode(subcontext))
+                else:
+                    if not type(subcontext) in [str, unicode]:
+                        subcontext = str(subcontext)
+                    processed.append(subcontext)
+            pre = data[:data.find(match.group(0))]
+            data = pre
+            if offset == 2:
+                data += (match.group(1) or '') + (match.group(2) or '')
+            data += ' '.join(processed) + match.group(offset + 3) or ''
+        return data
+

Added: pypy/branch/guido-buildtool-web/pypy/tool/build/web/test/test_templesser.py
==============================================================================
--- (empty file)
+++ pypy/branch/guido-buildtool-web/pypy/tool/build/web/test/test_templesser.py	Tue Mar 13 14:38:15 2007
@@ -0,0 +1,71 @@
+import py
+from pypy.tool.build.web.templesser import template
+
+def test_template_conditionals():
+    t = template('%(cond)[cfoo%(cond)]c')
+    u = t.unicode({'cond': True})
+    assert u == u'foo'
+    u = t.unicode({'cond': False})
+    assert u == u''
+
+def test_template_block():
+    t = template('%(block)[b%(foo)s%(block)]b')
+    u = t.unicode({'block': [{'foo': 'spam'}, {'foo': 'eggs'}]})
+    assert u == u'spam eggs'
+
+def test_combined():
+    t = template(u'%(block)[b%(cond)[cfoo%(cond)]c%(block)]b')
+    u = t.unicode({'block': [{'cond': False}]})
+    assert u == u''
+    u = t.unicode({'block': [{'cond': True}]})
+    assert u == u'foo'
+
+def test_nested_resolve_conditionals():
+    t = template(u'%(cond1)[cfoo%(cond2)[cbar%(cond2)]cbaz%(cond1)]c')
+    u = t.unicode({'cond1': False, 'cond2': True})
+    assert u == u''
+    u = t.unicode({'cond1': True, 'cond2': False})
+    assert u == u'foobaz'
+    u = t.unicode({'cond1': True, 'cond2': True})
+    assert u == u'foobarbaz'
+
+def test_newlines_block():
+    t = template(u'foo\n%(block)[b%(bar)s%(block)]b\nbaz')
+    u = t.unicode({'block': [{'bar': '1'}, {'bar': '2'}]})
+    assert u == u'foo\n1 2\nbaz'
+
+def test_keyerror():
+    t = template(u'%(cond)[c foo %(cond)]c')
+    py.test.raises(KeyError, 't.unicode({})')
+
+def test_escaping_conditional():
+    t = template(u'%%(cond)[c foo %%(cond)]c')
+    u = t.unicode({})
+    assert u == u'%(cond)[c foo %(cond)]c'
+
+def test_escaping_broken():
+    t = template(u'%%(cond)[c foo %(cond)]c')
+    py.test.raises(KeyError, 't.unicode({})')
+
+def test_quick_functional():
+    t = template(u"""\
+<html>
+  <head>
+    <title>%(title)s</title>
+  </head>
+  <body>
+    %(header)[c
+      <h3>%(header)s</h3>
+    %(header)]c
+    %(items)[b
+      %(name)[c<div>%(name)s</div>%(name)]c
+      %(values)[b
+        <div>%(value)s</div>
+      %(values)]b
+    %(items)]b
+  </body>
+</html>
+""")
+    u = t.unicode({'title': 'foo', 'header': False, 'items': []})
+    u = u.replace(' ', '').replace('\n', '')
+    assert u == u'<html><head><title>foo</title></head><body></body></html>'



More information about the Pypy-commit mailing list