[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