Archive for December 2009
2
Formatting and processing text in TAL templates
0 Comments | Posted by Ignas Mikalajūnas in Programming
Marius Gedminas wrote about a useful technique for formatting paragraphs — registering a view for “*” that does the processing for you.
While developing SchoolTool we
used a bit more complicated, but slightly more secure technique
— adapters implementing IPathAdapter.
To get the same effect that was described in Marius blog post you do this:
import cgi
from zope.interface import implements
from zope.traversing.interfaces import ITraversable
from zope.traversing.interfaces import IPathAdapter
from zope.component import adapts
class TalFiltersPathAdapter(object):
"""Collection of filters to be used in views for text processing."""
adapts(None)
implements(IPathAdapter, ITraversable)
def __init__(self, context):
self.context = context
def traverse(self, name, furtherPath=()):
handler = getattr(self, name)
return handler(furtherPath)
def paragraphs(self, furtherPath=()):
if self.context is None:
return ''
paras = filter(None, [s.strip() for s in self.context.splitlines()])
return "".join('<p>%s</p>\n' % cgi.escape(p)
for p in paras)
Register it like this:
<zope:adapter
for="*"
name="filter"
provides="zope.traversing.interfaces.IPathAdapter"
factory=".TalFiltersPathAdapter"
/>
And use it like this:
<p tal:replace="structure object/attribute/filter:paragraphs" />
The benefits of this technique – you don’t have
a zope.Public view registered on all your objects, it
will not “hog” ‘paragraphs’ view name (view names unless they are
registered for a specific skin are a shared resource) and you
can only access this functionality from TAL templates. The downside
— it is slightly more complicated and more TAL specific, so if
you are not using TAL templates you can’t really use this.
1
Running browser in the middle of a Pylons test
3 Comments | Posted by Ignas Mikalajūnas in Programming
Tests are good! writing tests is good! Sometimes they break and you
can’t understand what’s wrong. Pylons is using webtest.TestApp for functional test and
TestResponse has a showbrowser method, a method that
displays the response in your browser, but most of the time it’s
just not enough.
When I started using Pylons, I missed the ability to start a server in the middle of the test so much (I had implemented this functionality for Zope3 functional tests before) that I just had to add it to my Pylons testing environment. As Pylons is a WSGI application, it was even easier than I expected:
import pylons
import webbrowser
import sys
import urllib2
from webtest import TestApp
from wsgiref.simple_server import make_server
def addPortToURL(url, port):
"""Add a port number to the url.
>>> addPortToURL('http://localhost/foo/bar/baz.html', 3000)
'http://localhost:3000/foo/bar/baz.html'
>>> addPortToURL('http://foo.bar.com/index.html?param=some-value', 555)
'http://foo.bar.com:555/index.html?param=some-value'
>>> addPortToURL('http://localhost:666/index.html', 555)
'http://localhost:555/index.html'
"""
(scheme, netloc, url, query, fragment) = urllib2.urlparse.urlsplit(url)
netloc = netloc.split(':')[0]
netloc = "%s:%s" % (netloc, port)
url = urllib2.urlparse.urlunsplit((scheme, netloc, url, query, fragment))
return url
class ServingTestApp(TestApp):
request = None
def do_request(self, req, status, expect_errors):
self.request = req
return super(ServingTestApp, self).do_request(req, status, expect_errors)
def serve(self, page_url=None):
try:
if page_url is None:
page_url = getattr(self.request, 'url', 'http://localhost/')
# XXX we rely on browser being slower than our server
webbrowser.open(addPortToURL(page_url, 5001))
print >> sys.stderr, 'Starting HTTP server...'
srv = make_server('localhost', 5001, pylons.test.pylonsapp)
srv.serve_forever()
except KeyboardInterrupt:
print >> sys.stderr, 'Stopped HTTP server.'
Now instead of the usual TestApp you just
use ServingTestApp
class TestController(TestCase):
def __init__(self, *args, **kwargs):
if pylons.test.pylonsapp:
wsgiapp = pylons.test.pylonsapp
else:
wsgiapp = loadapp('config:%s' % config['__file__'])
# Replace:
# self.app = TestApp(wsgiapp)
# With
self.app = ServingTestApp(wsgiapp)
url._push_object(URLGenerator(config['routes.map'], environ))
TestCase.__init__(self, *args, **kwargs)
And in your code you can do this:
from sample.tests import *
class TestHelloController(TestController):
def test_index(self):
response = self.app.get(url(controller='hello', action='index'))
# Test response...
self.app.serve()
This is immensely useful when you want to find out what to click next, while writing a test for a part of the application that you haven’t worked with for a while. Or browser stress-testing, because now you can just use your testing framework to generate thousands of throw away objects in a database that will be gone when a test is over, and will be there again when you run the test for the second time.
I use it a lot when I want to test the UI of deleting objects, or when I work on things that have a uni-directional workflow. Testing the step number 7 in your browser requires you completing the first 6 steps, doing that every time you want to look at the transition from step 7 to step 8 is just too much work.

