<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title>Fetchez le Python</title><link>http://blog.ziade.org</link><description>Tarek Ziadé</description><atom:link href="http://blog.ziade.org/feed" rel="self"></atom:link><lastBuildDate>Sat, 12 May 2012 15:00:00 +0200</lastBuildDate><item><title>Circus 0.4 coming soon</title><link>http://blog.ziade.org/2012/05/12/circus-04-coming-soon/</link><description>&lt;img alt="http://circus.readthedocs.org/en/latest/_images/circus-medium.png" src="http://circus.readthedocs.org/en/latest/_images/circus-medium.png" /&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Circus is a program that will let you run and watch multiple processes.
Like Supervisord, BluePill and Daemontools.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The next release of Circus, that should be happening soon, has some
new features I want to hilight in this post:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;strong&gt;circusd-stats&lt;/strong&gt;, a process that streams statistics on Circus.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;circus-top&lt;/strong&gt;, a top-like console&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;circushttpd&lt;/strong&gt;, a Web Managment interface&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="section" id="circusd-stats"&gt;
&lt;h2&gt;circusd-stats&lt;/h2&gt;
&lt;p&gt;In Circus 0.3, if you want to know how much CPU and Memory each process takes,
you can use circusctl:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ bin/circusctl stats dummy
dummy:
1: 91238  python root 0 3M 2G 62.6 0.1 0:04.80
3: 91240  python root 0 3M 2G 63.2 0.1 0:04.80
2: 91239  python root 0 3M 2G 63.1 0.1 0:04.80
5: 91242  python root 0 3M 2G 62.8 0.1 0:04.82
4: 91241  python root 0 3M 2G 62.6 0.1 0:04.76
&lt;/pre&gt;
&lt;p&gt;I was a bit amazed by the inaccuracy of our CPU stats when I compared them to
a simple call to ps or top.&lt;/p&gt;
&lt;p&gt;The problem was that &lt;strong&gt;psutil&lt;/strong&gt;, the library we use to build the stats, has
two ways of calculating the CPU usage:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;a blocking call, that calculates the usage of the CPU for a bit of time,
like 500ms.&lt;/li&gt;
&lt;li&gt;a non-blocking call, that just returns the value since the last call,
that will be accurate as long as you wait at least 100ms between two calls.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The problem with any of these techniques is that, when you have hundreds of
processes running, you can't really do this calculation on the fly without
making the client wait for a long time. And Circus naively used the
non-blocking calls, meaning that the CPU usage values was relying on the
frequency of client calls.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;circusd-stats&lt;/strong&gt; solves this problem by constantly calculating in a
separate process all the stats for all the processes. It has a dedicated
thread per watcher, and simply listens to Circus pub/sub socket to know
when a process is added or removed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;circusd-stats&lt;/strong&gt; itself publishes at a constant pace in a new,
dedicated ZMQ pub/sub channel the stats for each process, and also
aggregated stats for each watcher.&lt;/p&gt;
&lt;p&gt;From there, any feature that needs accurate stats can register to
that pub/sub channel to receive the stats. Stats can be filtered by
watcher name, by PID, or the client can get all of them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;circusd-stats&lt;/strong&gt; CPU usage can be quite important, so we've added
configurable intervals between two stats calls.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="circus-top"&gt;
&lt;h2&gt;circus-top&lt;/h2&gt;
&lt;p&gt;The first stats client we've added into Circus is &lt;strong&gt;circus-top&lt;/strong&gt;, a
console script that uses &lt;strong&gt;curses&lt;/strong&gt; to display a console dashboard
where every process CPU and memory usage is constantly refreshed.&lt;/p&gt;
&lt;img alt="http://ziade.org/circus-top.png" src="http://ziade.org/circus-top.png" /&gt;
&lt;p&gt;It's a read-only console, so you can't do anything like killing
a process, but it is a good way to check if there's any issue
with your system&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="circushttpd"&gt;
&lt;h2&gt;circushttpd&lt;/h2&gt;
&lt;p&gt;I have already talked about the Web Admin tool we were building
here : &lt;a class="reference external" href="http://blog.ziade.org/2012/04/23/a-web-admin-for-circus/"&gt;http://blog.ziade.org/2012/04/23/a-web-admin-for-circus/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And well, thanks to Adnane, it now look really great. Check out
the new screencast: &lt;a class="reference external" href="https://plus.google.com/106436370949746015255/posts/U6yKgbLn4a4"&gt;https://plus.google.com/106436370949746015255/posts/U6yKgbLn4a4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;circushttpd is a heavy user of stats, and instead of using
the stats command &lt;strong&gt;circusd&lt;/strong&gt; provides, it now uses the &lt;strong&gt;circusd-stats&lt;/strong&gt;
stream.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="what-s-next"&gt;
&lt;h2&gt;What's next&lt;/h2&gt;
&lt;p&gt;We're currently changing &lt;strong&gt;circusdhttp&lt;/strong&gt; so it uses &lt;strong&gt;socket.io&lt;/strong&gt; instead
of the current polling. &lt;strong&gt;socket.io&lt;/strong&gt; will use web sockets when the browser
is compatible, and fallback to XHR-polling.&lt;/p&gt;
&lt;p&gt;Once this integration is ready, we'll cut a 0.4&lt;/p&gt;
&lt;p&gt;An exciting feature is planned for 0.5 : &lt;strong&gt;circus-cluster&lt;/strong&gt;, a tool
to drive several instances of &lt;strong&gt;circus&lt;/strong&gt; we'll call &lt;strong&gt;Circus Nodes&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This new feature will allow you to run and manage a whole cluster.
&lt;strong&gt;circus-top&lt;/strong&gt; and &lt;strong&gt;circushttpd&lt;/strong&gt; will be adapted to work with
&lt;strong&gt;circus-cluster&lt;/strong&gt; as well.&lt;/p&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sat, 12 May 2012 15:00:00 +0200</pubDate><guid>http://blog.ziade.org/2012/05/12/circus-04-coming-soon/</guid><category>python</category><category>mozilla</category></item><item><title>A Web admin for Circus</title><link>http://blog.ziade.org/2012/04/23/a-web-admin-for-circus/</link><description>&lt;p&gt;We're working on adding a Web Administration Panel to Circus. Like many tools
out there, the idea is to let you control Circus from the web.&lt;/p&gt;
&lt;a href="http://blog.ziade.org/circus-web-panel.m4v"&gt;
  &lt;img src="http://blog.ziade.org/circus-web-ui.png" height="200px" width="600px"
        title="Click to see the screencast"&gt;
  &lt;/img&gt;
&lt;/a&gt;&lt;p&gt;This new feature is currently built with:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;The &lt;a class="reference external" href="http://bottlepy.org/"&gt;Bottle Framework&lt;/a&gt; -- A one-module wsgi framework.&lt;/li&gt;
&lt;li&gt;The &lt;a class="reference external" href="http://omnipotent.net/jquery.sparkline/"&gt;Sparkline jQuery Extension&lt;/a&gt; -- A JS lib
to draw cute little charts.&lt;/li&gt;
&lt;li&gt;The &lt;a class="reference external" href="https://github.com/mozilla-services/circus/blob/master/circus/client.py"&gt;Circus Client class&lt;/a&gt;,
that let you connect to a Circus system and interact with it via ZMQ messages.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The prototype currently does AJAX Polls to update the data on the page every
second, but a lot of things are being worked out to improve this.&lt;/p&gt;
&lt;p&gt;For instance, the web app keeps in memory the last 100 stats calls in order
to build the charts series, and serves them to the clients on every poll.&lt;/p&gt;
&lt;p&gt;We're going to change this by collecting and building the series on the client
only.&lt;/p&gt;
&lt;p&gt;We'll also add a bit of &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Memoization"&gt;Memoization&lt;/a&gt; so we
serve back the same stats values in a given timespan to avoid overloading Circus with
web stats calls.&lt;/p&gt;
&lt;p&gt;This is especially important when the Web UI displays charts for hundreds of
processes.&lt;/p&gt;
&lt;p&gt;And err... we need to make the UI pretty and usable. I am doing my best
here, but let's face it -- I suck at CSS :)&lt;/p&gt;
&lt;p&gt;If you are a designer and want to help there...&lt;/p&gt;
&lt;p&gt;You can see a screencast of the prototype here: &lt;a class="reference external" href="http://blog.ziade.org/circus-web-panel.m4v"&gt;http://blog.ziade.org/circus-web-panel.m4v&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This screencast was built with the &lt;em&gt;crypto&lt;/em&gt; demo of the Powerhose project at:
&lt;a class="reference external" href="https://github.com/mozilla-services/powerhose/blob/master/examples/crypto.ini"&gt;https://github.com/mozilla-services/powerhose/blob/master/examples/crypto.ini&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This demo runs crypto workers, a broker, and a web app.&lt;/p&gt;
&lt;p&gt;In this screencast I do the following:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;connect the Circus Web Admin app to a Circus system&lt;/li&gt;
&lt;li&gt;check how the processes of the &lt;em&gt;workers&lt;/em&gt; watcher do&lt;/li&gt;
&lt;li&gt;add &amp;amp; remove some processes&lt;/li&gt;
&lt;li&gt;start &amp;amp; restart the &lt;em&gt;web&lt;/em&gt; worker&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Links:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;the doc: &lt;a class="reference external" href="http://circus.readthedocs.org/en/latest/index.html"&gt;http://circus.readthedocs.org/en/latest/index.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;the repo: &lt;a class="reference external" href="https://github.com/mozilla-services/circus"&gt;https://github.com/mozilla-services/circus&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Please let us know what you think !&lt;/p&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Mon, 23 Apr 2012 18:51:00 +0200</pubDate><guid>http://blog.ziade.org/2012/04/23/a-web-admin-for-circus/</guid><category>python</category><category>mozilla</category></item><item><title>Circus 0.3 released</title><link>http://blog.ziade.org/2012/04/17/circus-03-released/</link><description>&lt;img alt="http://circus.readthedocs.org/en/latest/_images/circus-medium.png" src="http://circus.readthedocs.org/en/latest/_images/circus-medium.png" /&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Circus is a program that will let you run and watch multiple processes.
Like Supervisord, BluePill and Daemontools.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The third release of Circus is here. It wanted to hightlight three new features
we've added since 0.1:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;rlimit support&lt;/li&gt;
&lt;li&gt;stderr and stdout streaming&lt;/li&gt;
&lt;li&gt;flapping detection&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="section" id="rlimit-support"&gt;
&lt;h2&gt;rlimit support&lt;/h2&gt;
&lt;p&gt;Circus comes now with &lt;a class="reference external" href="http://docs.python.org/library/resource.html#resource-limits"&gt;rlimit&lt;/a&gt;
support. For example, if you want a specific process to have a limited number of open files
to 100, you can use in your Circus configuration file the &lt;strong&gt;rlimit_nofile&lt;/strong&gt; option:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
[circus]
check_delay = 5
endpoint = tcp://127.0.0.1:5555

[watcher:myprogram]
cmd = myprogram
rlimit_nofile = 500
&lt;/pre&gt;
&lt;p&gt;This feature is built-in in the Python standard library, in the &lt;em&gt;resource&lt;/em&gt; module so that
was easy to add. The next step here would be to see how Circus could interact with tools
like &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Cgroups"&gt;cgroups&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="stderr-and-stdout-streaming"&gt;
&lt;h2&gt;stderr and stdout streaming&lt;/h2&gt;
&lt;p&gt;That feature is a must have, and we worked quite a bit on it to make sure it's fast
even with hundreds of processes being watched : the ability to stream the standard
output and standard error streams of all processes back into Circus.&lt;/p&gt;
&lt;p&gt;For example, if you want &lt;strong&gt;all&lt;/strong&gt; processes to write their stdout continously
into a file, you can write:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
[watcher:myprogram]
cmd = myprogram
numprocesses = 100

# will push in test.log the stream every 300 ms
stdout_stream.class = FileStream
stdout_stream.filename = myprogram.log
stdout_stream.refresh_time = 0.3
&lt;/pre&gt;
&lt;p&gt;The core of this feature is a call to the &lt;strong&gt;select()&lt;/strong&gt; function from the
standard library on the PIPEs opened on each process.&lt;/p&gt;
&lt;p&gt;The gist of this code is:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
import fcntl

for pipe in pipes:
    fcntl.fcntl(pipe, fcntl.F_SETFL, os.O_NONBLOCK)

while True:
    rlist, __, __ = select(pipes, [], [])
    for pipe in rlist:
        try:
            data = pipe.read(self.buffer)
            redirect_to_circus(data)
        except IOError, ex:
            if ex[0] != errno.EAGAIN:
                raise
&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;redirect_to_circus&lt;/strong&gt; basically redirects the stream to
whatever class you've configured in &lt;strong&gt;stdout_stream.class&lt;/strong&gt;.
You can provide your own class if you want to implement
a specific stream handler.&lt;/p&gt;
&lt;p&gt;We've implemented this stream redirector with one thread per
watcher that operates the select() calls, but also have a Gevent
implementation that uses greenlets instead of threads and
Gevent's own select() implementation.&lt;/p&gt;
&lt;p&gt;The threaded version is the default one, but you can pick the
gevent backend with the &lt;strong&gt;stream_backend&lt;/strong&gt; option.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="flapping"&gt;
&lt;h2&gt;Flapping&lt;/h2&gt;
&lt;p&gt;Yet another important feature to have: the ability to detect
that a process that's launched constantly dies. That happens
when the process command line is wrong or when a resource the
program uses is not reachable for example.&lt;/p&gt;
&lt;p&gt;Tools like daemontools will simply try again and again to run
the service, eating in the process the whole CPU of your server.&lt;/p&gt;
&lt;p&gt;With this feature built-in and enabled by default, Circus detects
the flapping of processes, and try again &lt;em&gt;later&lt;/em&gt; then eventually
quit trying.&lt;/p&gt;
&lt;p&gt;Of course, the flapping dance is published in the PUB/SUB
channel so it's easy to build a script that will send you
an alert when it happens.&lt;/p&gt;
&lt;div class="section" id="what-s-next"&gt;
&lt;h3&gt;What's next&lt;/h3&gt;
&lt;p&gt;We've been doing extensive load testing for a Mozilla project that's
going in production next week, and Circus seems to be quite stable so
far. It's handling around 150 processes per server right now, and
everything's working like a charm.&lt;/p&gt;
&lt;p&gt;I think Circus has reached a level now where it could replace tools
like Daemontools in some of our set ups.&lt;/p&gt;
&lt;p&gt;The next major step is to refactor a bit the tool to make it more
pluggable.&lt;/p&gt;
&lt;p&gt;For example, the flapping detection is built-in, but could be
factored out as a plug-in since it subscribes to the PUB/SUB channel
to get notified when a process is restarted, and could control
everything as a client like circusctl.&lt;/p&gt;
&lt;p&gt;In other words, it would be nice to provide a base class that gets
notifications and acts upon. The auto-grow feature I want
to add --a feature where Circus adds automatically workers depending
on the load-- could be a plugin based on that class.&lt;/p&gt;
&lt;p&gt;Links:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;the doc: &lt;a class="reference external" href="http://circus.readthedocs.org/en/latest/index.html"&gt;http://circus.readthedocs.org/en/latest/index.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;the release: &lt;a class="reference external" href="http://pypi.python.org/pypi/circus/0.3.1"&gt;http://pypi.python.org/pypi/circus/0.3.1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;the repo: &lt;a class="reference external" href="https://github.com/mozilla-services/circus"&gt;https://github.com/mozilla-services/circus&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Please let us know what you think !&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziade</dc:creator><pubDate>Tue, 17 Apr 2012 21:29:00 +0200</pubDate><guid>http://blog.ziade.org/2012/04/17/circus-03-released/</guid><category>python</category><category>mozilla</category></item><item><title>Signpic - Signing Pictures with PIL</title><link>http://blog.ziade.org/2012/04/14/signpic-signing-pictures-with-pil/</link><description>&lt;img alt="http://awesomeness.openphoto.me/custom/201204/351aa7-IMGP1722_wm_870x550.jpg" src="http://awesomeness.openphoto.me/custom/201204/351aa7-IMGP1722_wm_870x550.jpg" style="width: 435px; height: 275px;" /&gt;
&lt;p&gt;I am starting to really like taking pictures, thanks to the new Pentax K5 I bought.&lt;/p&gt;
&lt;p&gt;My photo processing looks roughly like this:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;I shoot many pics and delete 90% of them&lt;/li&gt;
&lt;li&gt;I edit them in Adobe Lightroom 4&lt;/li&gt;
&lt;li&gt;I push them on my openphoto.me account&lt;/li&gt;
&lt;li&gt;they are stored in my Dropbox account&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What I wanted to add is a way to sign my pictures by adding a small text on the bottom
right corner. Not because I want to look like some kind of pro photographer -- I am not ;)&lt;/p&gt;
&lt;p&gt;But signing is useful when you send pics around to your family &amp;amp; friends: I want them
to know that they are more pics at &lt;a class="reference external" href="http://tarek.openphoto.me"&gt;http://tarek.openphoto.me&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I found a cool little script at &lt;a class="reference external" href="http://www.turboradness.com/watermark-your-images-with-python"&gt;http://www.turboradness.com/watermark-your-images-with-python&lt;/a&gt;
that provides this feature with PIL and will let you sign pictures with a signature
image.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;signpic&lt;/strong&gt; is a small script inspired from that blog post that adds:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;a console script you can use to sign your pics or directories of pics
using a provided signature image, or the signature image located at
~/.signature.jpg&lt;/li&gt;
&lt;li&gt;a &lt;strong&gt;--powerhose&lt;/strong&gt; mode that will run a Powerhose cluster to perform the
operations. This is very useful to speed up the process when you need to sign a
bunch of pictures - a single signing takes a few seconds....
Powerhose is a Request-Reply Broker pattern in ZMQ, and speed up the signing
task.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you want to give it a shot:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ pip install signpic
$ bin/signpic ~/Desktop/pics --phose
Using signature file '/Users/tarek/.signature.jpg'
Looking for files in '/Users/tarek/Desktop/pics'
+++++++++++++.............Done.
&lt;/pre&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziade</dc:creator><pubDate>Sat, 14 Apr 2012 23:05:00 +0200</pubDate><guid>http://blog.ziade.org/2012/04/14/signpic-signing-pictures-with-pil/</guid><category>python</category></item><item><title>GitHub based services</title><link>http://blog.ziade.org/2012/04/06/github-based-services/</link><description>&lt;p&gt;Ok, maybe I am late at the party, and some people will think what I am writing
here is obvious, but nevermind. ;)&lt;/p&gt;
&lt;p&gt;There's a trend these days on Github-based online services. That is -- point
me your Github repo and I'll do something with it everytime you push a change.&lt;/p&gt;
&lt;p&gt;Notification hooks have existed for years now, and a lot of teams have their
own Jenkins CI (we do at Mozilla), or their checkins mailing list. But the
thing is : what's the point to maintain your own stuff anymore when there's a
service for it you can hook in a few minutes.&lt;/p&gt;
&lt;p&gt;For a Python project there are two absolutely awesome services:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://readthedocs.org"&gt;ReadTheDocs&lt;/a&gt; -- creates a website out of your
project
Sphinx docs, exactly
like what we've set with &lt;a class="reference external" href="http://packages.python.org"&gt;http://packages.python.org&lt;/a&gt; but it's all automatically
done for you on every commit. &lt;strong&gt;win&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://travis-ci.org"&gt;Travis-CI&lt;/a&gt; -- run your tests on every change.
Sends mail on failure. Again, nothing's new here, but  as a developer, the
only thing you have to do is to add a YAML file in your
repo. &lt;strong&gt;win&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Travis does not replace functional tests or tests you want to run on a
specific infrastructure. But for day-to-day usage it's perfect.&lt;/p&gt;
&lt;p&gt;In fact, there are more and more people that have standardized their README
page that appears at PyPI with those two tools.&lt;/p&gt;
&lt;p&gt;For example, look at Circus -- &lt;a class="reference external" href="http://pypi.python.org/pypi/circus"&gt;http://pypi.python.org/pypi/circus&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It displays the Travis build status directly on PyPI, and provides a link
to Read the docs.&lt;/p&gt;
&lt;p&gt;Out of my head here are some services I'd love to enable from my Github
admin panel:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;A &lt;a class="reference external" href="http://nedbatchelder.com/code/coverage/"&gt;Coverage&lt;/a&gt; Dashboard like
this one -- &lt;a class="reference external" href="http://nedbatchelder.com/code/coverage/sample_html/"&gt;http://nedbatchelder.com/code/coverage/sample_html/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;A similar Dashboard but with profiling info, using
&lt;a class="reference external" href="http://www.hexacosa.net/project/pyprof2html/"&gt;http://www.hexacosa.net/project/pyprof2html/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;A &lt;a class="reference external" href="http://funkload.nuxeo.org"&gt;Funkload Trending&lt;/a&gt; Dashboard. Read about
this here: &lt;a class="reference external" href="http://ziade.org/2011/06/10/continuous-load-testing-wint-funkload"&gt;http://ziade.org/2011/06/10/continuous-load-testing-wint-funkload&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The first and second ones are simple to produce - all it takes is a call to
the script that generates the HTML report. It needs to run in some VM though.&lt;/p&gt;
&lt;p&gt;Ah. mm wait. Maybe the ReadTheDocs project could add support to these,
or maybe I can try to sneak in those dashboards as Sphinx extensions...&lt;/p&gt;
&lt;p&gt;The last one is harder because it involves some thoughts on the
load testing architecture and well, it won't tell you if your application
is slow on &lt;em&gt;your&lt;/em&gt; hardware -- it will just detect that your web service
got suddenly slower after that commit.&lt;/p&gt;
&lt;p&gt;There are hundreds of services on Github already -- curious to know
which ones Python folk use.&lt;/p&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziade</dc:creator><pubDate>Fri, 06 Apr 2012 10:20:00 +0200</pubDate><guid>http://blog.ziade.org/2012/04/06/github-based-services/</guid><category>python</category><category>mozilla</category></item><item><title>Circus 0.1 released</title><link>http://blog.ziade.org/2012/03/20/circus-01-released/</link><description>&lt;img alt="http://dl.dropbox.com/u/8617023/circus.png" src="http://dl.dropbox.com/u/8617023/circus.png" /&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Circus is a program that will let you run and watch multiple processes.
Like Supervisord, BluePill and Daemontools.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;We wanted to release a first version of Circus &lt;em&gt;before&lt;/em&gt; Pycon
originally. Then Pycon was here, so we decided we would release
it before Pycon would end. And Pycon ended. And we had a few bugs
to fix.&lt;/p&gt;
&lt;p&gt;But today it's happening !&lt;/p&gt;
&lt;p&gt;Circus 0.1 is an alpha release, don't use it in production yet !&lt;/p&gt;
&lt;p&gt;But this first release contains a lot of stuff already:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;a pub/sub channel so you can monitor what's going on.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;circusctl&lt;/em&gt; : an amazing console script that let you interact
with the system.&lt;/li&gt;
&lt;li&gt;a full documentation for the &lt;em&gt;circusctl&lt;/em&gt; CLI.&lt;/li&gt;
&lt;li&gt;an API that's simple enough in most cases.&lt;/li&gt;
&lt;li&gt;... many more things really. I had to stop Benoit from adding
features ;) -- did I mention that Benoit is amazing ?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I also added a section on how Circus is different from other tools here:
&lt;a class="reference external" href="http://circus.readthedocs.org/en/latest/index.html#why-should-i-use-circus-instead-of-x"&gt;http://circus.readthedocs.org/en/latest/index.html#why-should-i-use-circus-instead-of-x&lt;/a&gt; ,
since a lot of people asked about it.&lt;/p&gt;
&lt;p&gt;One very important feature for me is to be able to use Circus with
a few lines of Python in a program that needs to spawn workers. The use case
for me is to run &lt;strong&gt;Powerhose&lt;/strong&gt;, a library that let you dispatch some tasks
across several workers, no matter what language they're built with.&lt;/p&gt;
&lt;p&gt;This is all it takes to run and maintain 4 workers:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
from circus import get_arbiter

arbiter = get_arbiter(&amp;quot;worker&amp;quot;, 4)
try:
    arbiter.start()
finally:
    arbiter.stop()
&lt;/pre&gt;
&lt;p&gt;Links:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;the doc: &lt;a class="reference external" href="http://circus.readthedocs.org/en/latest/index.html"&gt;http://circus.readthedocs.org/en/latest/index.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;the release: &lt;a class="reference external" href="http://pypi.python.org/pypi/circus/0.1"&gt;http://pypi.python.org/pypi/circus/0.1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;the repo: &lt;a class="reference external" href="https://github.com/mozilla-services/circus"&gt;https://github.com/mozilla-services/circus&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Please let us know what you think !&lt;/p&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziade</dc:creator><pubDate>Tue, 20 Mar 2012 21:55:00 +0100</pubDate><guid>http://blog.ziade.org/2012/03/20/circus-01-released/</guid><category>python</category><category>mozilla</category></item><item><title>Pycon - Packaging Sprint/Bof Report</title><link>http://blog.ziade.org/2012/03/15/pycon-packaging-sprintbof-report/</link><description>&lt;p&gt;We did a packaging open space and a sprint during Pycon and the talks and
work we did was quite constructive. The good thing is that most people
that are involved in the maintenance of the packaging tools in our
eco-system were present -- that helps a lot.&lt;/p&gt;
&lt;p&gt;I don't want to go into great details here, mostly because I am currently
in a train going back to Dijon, completely jet-lagged from the Pycon
trip. But I do want to blog about it now to make sure I write down
while it's fresh.&lt;/p&gt;
&lt;div class="note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;Just a quick note on the terms I use to avoid confusion:&lt;/p&gt;
&lt;ul class="last simple"&gt;
&lt;li&gt;&lt;strong&gt;distutils&lt;/strong&gt; is now frozen in Python&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;packaging&lt;/strong&gt; is the new distutils, with backward incompatible changes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;distutils2&lt;/strong&gt; is the backport of packaging for Python 2.5 to 2.7&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;p&gt;On to the top of my head, here what we've decided to do and
worked on. I am adding a &lt;strong&gt;[status]&lt;/strong&gt; marker for the ones I know.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;release a Python 2 Distutils2 alpha version for people to play with [done]&lt;/li&gt;
&lt;li&gt;add oauth support in PyPI&lt;/li&gt;
&lt;li&gt;make SSL the default way to register and upload projects on PyPI&lt;/li&gt;
&lt;li&gt;intensive testing and debugging of Distutils2 [lots done here, but ongoing]&lt;/li&gt;
&lt;li&gt;poke at using Distutils2 in zc.buildout [ongoing]&lt;/li&gt;
&lt;li&gt;start a discussion on Distutils-SIG about the &lt;em&gt;next&lt;/em&gt; binary format [done]&lt;/li&gt;
&lt;li&gt;publish setup.cfg at PyPI so tools or humans can read it without
downloading and/or executing some code.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That does not sound like a lot of achievements, but this Pycon was in my
opinion the best Pycon ever to put all the people involved in packaging
on the same page and agree on some plans for the future.&lt;/p&gt;
&lt;p&gt;That did not include the people from the scientific community, like last
year or the year before. I was told that's because they have an aversion
for the Distutils codebase. Guess what - mee to :)&lt;/p&gt;
&lt;p&gt;But I think we can find a mutual ground for several reasons:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;what we're building in Python is based on &lt;strong&gt;standards&lt;/strong&gt; we define in PEPs.
So that's orthogonal to an implementation. For example Scientific build
tools could definitely consume our metadata, or our setup.cfg
(&lt;a class="reference external" href="http://docs.python.org/dev/packaging/setupcfg.html"&gt;http://docs.python.org/dev/packaging/setupcfg.html&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;the &lt;strong&gt;packaging&lt;/strong&gt; package in the standard library has been revamped, uses
clean, modern python. It has useful tools in it -- for example, if you
need to browse PyPI or the installed packages, you have now reference
implementations there, that will be used by pip and zc.buildout in the
long term. Notice that they are backported in the standalone distutils2
package so they can already be used&lt;/li&gt;
&lt;li&gt;the compilers part is still horrible, but we made it pluggable. If you
build a build tool, distutils2 can be configured to use it. So you can
hate that code, but you should try to be compatible with at least the
definition of extensions in setup.cfg.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So, like last year and the year before, I'd like to say again that
anyone from the scientific community interested in helping in the
standards or anything else is more than welcome.&lt;/p&gt;
&lt;p&gt;If you're curious, you can see the etherpad we used here during Pycon:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://etherpad.mozilla.org/packaging-bof"&gt;http://etherpad.mozilla.org/packaging-bof&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://etherpad.mozilla.org/packaging-sprint"&gt;http://etherpad.mozilla.org/packaging-sprint&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On a side-note, using a pad like this was extremely useful during the
open space, since everyone could interact live in it.&lt;/p&gt;
&lt;p&gt;Oh by the way, kudos to Eric Araujo for the work he's been doing in the
last months in packaging.&lt;/p&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziade</dc:creator><pubDate>Thu, 15 Mar 2012 14:54:00 +0100</pubDate><guid>http://blog.ziade.org/2012/03/15/pycon-packaging-sprintbof-report/</guid><category>python</category></item><item><title>Circus Sprint Report</title><link>http://blog.ziade.org/2012/03/14/circus-sprint-report/</link><description>&lt;img alt="http://dl.dropbox.com/u/8617023/circus.png" src="http://dl.dropbox.com/u/8617023/circus.png" /&gt;
&lt;p&gt;Benoit and I organized a sprint at Pycon for Circus, the process watcher
we're building (see &lt;a class="reference external" href="http://ziade.org/2012/02/24/circus-a-process-controller"&gt;http://ziade.org/2012/02/24/circus-a-process-controller&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;We had quite some interest on the topic, and a few
contributors. I was myself naviguating between the Circus sprint and the
Packaging one. On Circus we were most of the time discussing features
and adding test coverage and documentation.&lt;/p&gt;
&lt;p&gt;Unfortunately, a few bugs prevented us from releasing the first public
version, but that should be out there in a few days.&lt;/p&gt;
&lt;p&gt;You can read the doc while we're building it here:
&lt;a class="reference external" href="http://circus.readthedocs.org/en/latest/index.html"&gt;http://circus.readthedocs.org/en/latest/index.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And have a look at the architecture here:
&lt;a class="reference external" href="http://circus.readthedocs.org/en/latest/architecture"&gt;http://circus.readthedocs.org/en/latest/architecture&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Stuff we did during the sprint:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;more test coverage&lt;/li&gt;
&lt;li&gt;hooked the project in Travis - &lt;a class="reference external" href="http://travis-ci.org/#!/mozilla-services/circus"&gt;http://travis-ci.org/#!/mozilla-services/circus&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;more doc&lt;/li&gt;
&lt;li&gt;flapping detection&lt;/li&gt;
&lt;li&gt;added pub/sub channel to allow any client to watch over
what's going on -- any event, like a process that restarts,
is published in the pub/sub channel&lt;/li&gt;
&lt;li&gt;started an pub/sub client example: a web monitoring
page based on websockets, using
Flask -- that app will be a subscriber of Circus. (not in the repo yet)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Stuff we need to finish / Features we're thinking about:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;factor out the flapping detector as a pub/sub plugin.&lt;/li&gt;
&lt;li&gt;stream the process stdout/stderr so clients can subscribe to them&lt;/li&gt;
&lt;li&gt;refactor how the commands are plugged in Circus -- mainly to
be able to produce a doc like this one : &lt;a class="reference external" href="http://www.redis.io/commands"&gt;http://www.redis.io/commands&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;add an encryption layer for the zmq channels&lt;/li&gt;
&lt;li&gt;discuss with Chris McDonough wrt Supervisord common bits.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And welcome to our new contributors, Neil Chintomby and Ori Livneh !&lt;/p&gt;
&lt;p&gt;If you want to have a look, for us at &lt;a class="reference external" href="https://github.com/mozilla-services/circus"&gt;https://github.com/mozilla-services/circus&lt;/a&gt;&lt;/p&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziade</dc:creator><pubDate>Wed, 14 Mar 2012 15:28:00 +0100</pubDate><guid>http://blog.ziade.org/2012/03/14/circus-sprint-report/</guid><category>python</category></item><item><title>PCard - Password Card</title><link>http://blog.ziade.org/2012/03/14/pcard-password-card/</link><description>&lt;img alt="http://dl.dropbox.com/u/8617023/pcard.png" src="http://dl.dropbox.com/u/8617023/pcard.png" /&gt;
&lt;p&gt;If you go to &lt;a class="reference external" href="http://passwordcard.org"&gt;http://passwordcard.org&lt;/a&gt;, there's a password card generator
you can use to create a 8 x 30 card filled with random characters, like
the one you see here.&lt;/p&gt;
&lt;p&gt;The first line is composed of icons and the idea is that you memorize
one icon per service you use, then get a pass phrase by reading the
characters on that column, or going in diagonal.&lt;/p&gt;
&lt;p&gt;Of course that does not work if you have hundreds of passwords. But
if you have a dozen very important passwords, the great thing about
this card is that it can be printed and fit in your wallet, so you
don't depend on any computer or untrusted Internet access when you
want a password.&lt;/p&gt;
&lt;p&gt;I am not sure to understand why passwordcard.org has the &lt;strong&gt;key&lt;/strong&gt;
used to generate the card printed on it, since it can be easy for
someone that gives a glimpse to memorize that short sequence (8 chars)
and regenerate the card with it. He won't get your passwords but will be
closer to them I guess. Of course when you use that card you are not
supposed to put you finger on it and follow a line in public ;)&lt;/p&gt;
&lt;p&gt;Anyways, I wanted to print out a card for myself, but wanted to
run it on my own computer for more safety, and remove the key on the
printed version -- I also wanted a command line tool to print out
the card in a shell.&lt;/p&gt;
&lt;p&gt;And you know what happens when a Python programmer tries to change a
Java program that's quite small. He ends up coding it back in Python
after staring at it a few minutes :o&lt;/p&gt;
&lt;p&gt;Anyways, &lt;a class="reference external" href="http://pcard.ziade.org"&gt;http://pcard.ziade.org&lt;/a&gt; is my own version, the code is
at &lt;a class="reference external" href="https://github.com/tarekziade/pcard"&gt;https://github.com/tarekziade/pcard&lt;/a&gt; . There's a command line tool
and a bottle-based web generator.&lt;/p&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziade</dc:creator><pubDate>Wed, 14 Mar 2012 12:00:00 +0100</pubDate><guid>http://blog.ziade.org/2012/03/14/pcard-password-card/</guid><category>python</category></item><item><title>Pycon WebDev Summit Report - Python Application Packages</title><link>http://blog.ziade.org/2012/03/10/pycon-webdev-summit-report-python-application-packages/</link><description>&lt;img alt="http://awesomeness.openphoto.me/custom/201203/1331302621-pyweb5_600x950.jpg" src="http://awesomeness.openphoto.me/custom/201203/1331302621-pyweb5_600x950.jpg" /&gt;
&lt;p&gt;We had a web summit at Pycon last Thursday and a few discussions next on
that &lt;strong&gt;Python application package&lt;/strong&gt; idea. I had conversations myself with
a bunch of different people, trying to understand where they were at
and what were their goals in this.&lt;/p&gt;
&lt;p&gt;If you want to know what it is all about, read Ian blog's here:
&lt;a class="reference external" href="http://blog.ianbicking.org/2012/02/29/python-application-package"&gt;http://blog.ianbicking.org/2012/02/29/python-application-package&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;He describes one possible solution to create &lt;em&gt;Python application packages&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The final goal is to be able to make it easier for cloud providers to run
our apps. I should mention that they don't really have an issue here, they
already run Python apps (Mozilla too ;)).  But they use their own standards
-- so  the idea is to see if a common standard is possible.&lt;/p&gt;
&lt;p&gt;During the summit, and right after, I suggested we would work together on
defining what we wanted to achieve
and to start a draft of a standard in a PEP during the Pycon sprint.&lt;/p&gt;
&lt;p&gt;It's interesting to see how saying the word &lt;em&gt;PEP&lt;/em&gt; can scare some people out
by the way. When related to packaging, that word induce to some people
things like &lt;em&gt;long process&lt;/em&gt;,  &lt;em&gt;consensus hard to achieve&lt;/em&gt;,
&lt;em&gt;fights on mailing list&lt;/em&gt; and so on. Some people want to solve their problems
&lt;strong&gt;right now&lt;/strong&gt;, which is awesome, but...&lt;/p&gt;
&lt;p&gt;But if their ambition is to see their solution
adopted by the community at large, they might fall into the trap we went in
with &lt;strong&gt;setuptools&lt;/strong&gt; a few years ago, which is to create and push a new
de-facto standard on the top of an existing, official standard in a way
that creates chaos in the packaging eco-system -- because of two competing
standards. So while it's great to build tools, I think that kind of
standard should be discussed together from the beginning to see where are
the common grounds.&lt;/p&gt;
&lt;p&gt;So I would like to step back a little bit and clarify a few things:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;strong&gt;A Standard is not an implementation&lt;/strong&gt; -- and this is in particular important
when we're thinking about building something to describe a Python application
standard -- so let's drop any bit of code for now imo.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Solving this problem is not specific to a Web application&lt;/strong&gt; -- defining
a Python application &lt;em&gt;that can be launched&lt;/em&gt; is not specific to a web app.
In fact, as Kenneth put it, we're talking about defining a &lt;em&gt;service&lt;/em&gt; here.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A deployment format is not a standard&lt;/strong&gt; -- I think some people were
confusing formats and standards. We were talking about how Java WAR files were cool
because you could just deploy that file and have the server &lt;em&gt;run it&lt;/em&gt;, like the
Python eggs a few years ago I guess.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;This standard is not here to compete with other packaging system&lt;/strong&gt; -- whathever
we build should not be a way to &amp;quot;avoid having to deal with system libraries&amp;quot;.
It should be possible to deploy an application using virtualenv and a local directory,
but again, this is orthogonal to having a standard. As a RPM packager I want to
be able to deploy your &lt;em&gt;service&lt;/em&gt; in my own fashion, and that standard should help me too.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A Python web app is also a Python package&lt;/strong&gt; -- this is important to note because a
package already has metadata information see PEP 345 -
&lt;a class="reference external" href="http://www.python.org/dev/peps/pep-0345"&gt;http://www.python.org/dev/peps/pep-0345&lt;/a&gt;. And in fact, many things I see in Ian
proposal are already solved by that PEP.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Based on these remarks, I would like during the sprint to work in two directions:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;thinking about extending PEP 345 to introduce fields specific to the notion
of &lt;em&gt;running a service&lt;/em&gt; and describe how it would look like in
packaging/distutils2's &lt;em&gt;setup.cfg&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;thinking about how to introduce this standard in the existing eco-system for
people to use immediatly on existing projects.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So far my thinking is that we should provide a field called &lt;strong&gt;Application&lt;/strong&gt; that
points a script to run an application -- like what you would point with the &lt;strong&gt;script&lt;/strong&gt;
option in distutils or in distribute/setuptools &lt;strong&gt;console entry point&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;I am not sure yet about the environment and the config files.&lt;/p&gt;
&lt;p&gt;Next episode during the sprints !&lt;/p&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziade</dc:creator><pubDate>Sat, 10 Mar 2012 08:00:00 +0100</pubDate><guid>http://blog.ziade.org/2012/03/10/pycon-webdev-summit-report-python-application-packages/</guid><category>python</category><category>mozilla</category></item><item><title>Moving to Pelican</title><link>http://blog.ziade.org/2012/03/05/moving-to-pelican/</link><description>&lt;p&gt;So, in my current quest of reducing the ads around me, I realized my blog
hosted at wordpress.com was full of them.&lt;/p&gt;
&lt;p&gt;So I decided to move to Alexis' Pelican blog system : &lt;a class="reference external" href="http://pelican.notmyidea.org"&gt;http://pelican.notmyidea.org&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Pelican creates a static html blog using reStructuredText or Markdown files
and that makes it easy enough to push blog entries at github and have them
published automatically on a server.&lt;/p&gt;
&lt;p&gt;Pelican can work by default with Disqus and has a default CSS that's
simple and readable.&lt;/p&gt;
&lt;p&gt;What I am losing from Wordpress is the whole Dashboard that I really liked,
and in particular the stats. Knowing that one of my blog post made it to
Reddit or hacker news was a good thing -- I could go there and answer questions
or read the comments.&lt;/p&gt;
&lt;p&gt;I almost got myself a pro account at wordpress because I did not want to
maintain yet another stats tool on my server. But getting away from wordpress
and getting back under my own domain was more tempting.&lt;/p&gt;
&lt;p&gt;Pelican has an import feature for Wordpress, that failed to produce the
proper reStructuredText for me, but worked quite well with Markdown.&lt;/p&gt;
&lt;p&gt;I was also able to tweak the URLs so they would look exactly like the
Wordpress ones, that is, URL with the year, month and day. I was also
lucky enough about the slug part of the URL: the import generated
the &lt;strong&gt;same&lt;/strong&gt; slugs than Wordpress ! In other words a simple redirection
of the domain does the trick to avoid losing any indexing.&lt;/p&gt;
&lt;p&gt;I am paying 12 bucks to wordpress to get this redirect but that's worth
it because people hitting tarekziade.wordpress.com should find back
the result transparently on blog.ziade.org.&lt;/p&gt;
&lt;p&gt;I also missed a way to create static &lt;em&gt;pages&lt;/em&gt;, like what Wordpress has.
So I added this feature to Pelican and will propose it for inclusion
to Alexis.&lt;/p&gt;
&lt;p&gt;Overall I really recommend Pelican. It's well crafted, easy to use and
it's Python !&lt;/p&gt;
&lt;p&gt;Thanks to Alexis and other contributors.&lt;/p&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziade</dc:creator><pubDate>Mon, 05 Mar 2012 20:44:00 +0100</pubDate><guid>http://blog.ziade.org/2012/03/05/moving-to-pelican/</guid><category>python</category></item><item><title>More privacy please</title><link>http://blog.ziade.org/2012/02/29/more-privacy-please/</link><description>&lt;p&gt;It became extremely annoying for me to get targeted ads, since I do most
of my shopping online, even groceries. I am a great target for
advertising operators :) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Google seems to be very transparent about letting you opt-out third
party cookies as a logged-in user, there's a page for this &lt;a href="http://www.google.com/ads/preferences/"&gt;here&lt;/a&gt;. You
can also make sure your web history is disabled on &lt;a href="https://www.google.com/history/lookup?ctz=-60"&gt;this page&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;[caption id="" align="alignnone" width="390" caption="(c) bhikku
cc-by-nc 2.0"][&lt;img alt="image" src="http://farm1.staticflickr.com/1/1187679_be6f9fdefa_z.jpg?zz=1" /&gt;][][/caption] &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But I don't want to chase options on every online service I use, just
to opt-out and protect my privacy. All these options should be off by
default in my opinion, or at least you should be warned, no ? I don't
know if it's still accurate but there's &lt;a href="http://www.legislation.gov.uk/uksi/2003/2426/contents/made"&gt;a law in Europe&lt;/a&gt; for this. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Anyways, this is not a new problem. But I realized that while I was
shocked by how cookies could breach your privacy +10 years ago, I was
not taking any action to protect myself. Let's fix this. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I am trying to fine-tune my browser and change my search habits to make
sure I am leaking as less data as possible. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here's a list of actions I have done, if you have better ideas please
comment, I'll try to keep that list up-to-date. &lt;br /&gt;
-   Use Firefox and &lt;a href="http://support.mozilla.org/en-US/kb/Disabling%20third%20party%20cookies"&gt;disable third-party cookies&lt;/a&gt;.
-   Activate &lt;a href="http://dnt.mozilla.org/"&gt;Do Not Track&lt;/a&gt; - there's now [a good list of ads
    companies][] that should honor it.
-   Install &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/cookie-monster/"&gt;Cookie Monster&lt;/a&gt; to get more control on cookies.
-   Install the &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/ghostery/"&gt;Ghostery plugin&lt;/a&gt;to detect and block ads scripts. It's
    pretty enlightening.
-   Install &lt;a href="https://www.eff.org/https-everywhere"&gt;HTTPS Everywhere&lt;/a&gt;. (nothing really a counter-measure for
    data leaking but it's safer - use it!)&lt;/p&gt;
&lt;p&gt;I have also decided to give a shot to &lt;a href="http://duckduckgo.com/"&gt;duckduckgo.com&lt;/a&gt; instead of
google.com for search.&lt;a href="http://donttrack.us/"&gt;They don't track&lt;/a&gt; at all. You can&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/duck-duck-go-ssl/"&gt;install the
ssl search bar add-on&lt;/a&gt; and also &lt;a href="http://www.howinthetech.com/changing-the-search-engine-in-firefoxs-awesome-bar/"&gt;tweak your Awesome bar&lt;/a&gt; so it uses
it too (I set it to &lt;em&gt;https://duckduckgo.com/?q=&lt;/em&gt; and it seems to work).&lt;/p&gt;
&lt;p&gt;It's been a week of Duck Duck Going, and I like it so far. It's
refreshing to use a new search engine. I have not suffered from bad
results so far -- nothing compared to what made me move from Altavista
to Google a very long time ago :D &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;And oh... nooo.. what now.. &lt;a href="https://panopticlick.eff.org/index.php?action=log&amp;amp;js=yes"&gt;Browser Fingerprint&lt;/a&gt; ??!&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;bhikku cc-by-nc 2.0&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;[&lt;img alt="image" src="http://farm1.staticflickr.com/1/1187679_be6f9fdefa_z.jpg?zz=1" /&gt;]: http://www.flickr.com/photos/bhikku/1187679/sizes/z/in/photostream/&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Wed, 29 Feb 2012 10:44:00 +0100</pubDate><guid>http://blog.ziade.org/2012/02/29/more-privacy-please/</guid></item><item><title>circus - a process controller</title><link>http://blog.ziade.org/2012/02/24/circus-a-process-controller/</link><description>&lt;p&gt;Benoit -- from Gunicorn fame -- and myself have started to work on
Circus, a process controller. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I really like this small project for several reasons: &lt;br /&gt;
-   it's the first time we're building a Mozilla Services library from
    the ground with a contributor that's not for the Mozilla community
    but rather from the Python community
-   this library is the last bit we're missing in our Python server
    stack to have a full control over it&lt;/p&gt;
&lt;p&gt;&lt;img alt="image" src="http://farm3.staticflickr.com/2602/3988814835_83f13f6d53.jpg" /&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Why a new lib ?&lt;/h3&gt;
&lt;p&gt;A process controller is mostly a script that manages processes, sends
signals to them, re-spawns them whenever they die. There are numerous
libraries out there that are already doing this so creating this new
library can sound like NIH. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I have looked at several projects before we've started this one, like
&lt;a href="http://supervisord.org/"&gt;supervisord&lt;/a&gt;, &lt;a href="https://github.com/arya/bluepill"&gt;BluePill&lt;/a&gt;, and so on. But none of them met exactly
all of my requirements - here's my audacious list: &lt;br /&gt;
-   can be used as a program and as a library -- so in Python
-   a way to query the system to perform live operations via a (secured)
    remote console such as &lt;br /&gt;
   -   add or remove workers
    -   restart the workers, broadcast signals
    -   get some stats on what's going on&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;advanced process management features, like &lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;controlled flapping - trying to restart n times a worker that
        dies on startup -- then abandon&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;auto-grow - add workers if all workers are 100% busy n seconds
    (for CPU-bound workers)&lt;/li&gt;
&lt;li&gt;auto-shrink - remove workers if they are bored&lt;/li&gt;
&lt;li&gt;run in containment tools like cgroups&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;meta-controller that can drive several controller remotely, to
    manage a cluster (later)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;BluePill looked very promising because it has most features we wanted,
but unfortunately, since we'd like to use it as a library, it's a
blocker. Also, while the DSL is quite sexy, that's not something we'd
want to use as-is because we're in a ini-file land were every tool is
configured via a new &lt;em&gt;[section]&lt;/em&gt; in a config file. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Supervisord is excellent -- and widely used. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I have tried to extend it and I must admit I had a little hard time to
wrap my head into it. This is purely technical, but some choices made in
Supervisord make it hard for me to extend it the way we want -- like the
fact that the main class is driven by a configuration where I wanted to
completely separate these two concepts. I want to be able to create a
class and tell it to run &lt;em&gt;n&lt;/em&gt; workers without having to create a
configuration object in the middle. There's also now the subprocess
module in Python, and while Supervisord is probably compatible with
older versions of Python, we want 2.6+ so we can make the code way less
verbose. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here's an example on how to run 3 workers on a given command with
Circus - KISS: &lt;br /&gt;
   from circus import get_trainer&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;trainer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_trainer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;try:&lt;/span&gt;

    &lt;span class="n"&gt;trainer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;finally:&lt;/span&gt;

    &lt;span class="n"&gt;trainer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Anyways -- we'll still be suffering from our choice for a bit -- we'll
encounter issues that other projects have encountered before. But I
think that's for the best, and Benoit has a lot of experience in this
area with Gunicorn - I expect both project to exchange a lot of pieces.&lt;/p&gt;
&lt;h3&gt;Current status&lt;/h3&gt;
&lt;p&gt;We're busy polishing the tool, but it's already in a usable state. For
Mozilla, the main use case is to run Crypto Workers for Powerhose (&lt;a href="http://tarekziade.wordpress.com/2012/02/06/scaling-crypto-work-in-python/"&gt;read
about Powerhose here&lt;/a&gt;) and we can already do this. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Turns out all Powerhose does is wrapping the &lt;strong&gt;&lt;em&gt;get_trainer()&lt;/em&gt;&lt;/strong&gt; call
into a class called &lt;em&gt;Workers&lt;/em&gt;. (see
&lt;a href=""&gt;https://github.com/mozilla-services/powerhose/blob/master/powerhose/client/workers.py)&lt;/a&gt;
and when the web application is launched, it runs a Powerhose Master and
some PowerHose Workers that way -- delegating all the process management
tasks to Circus. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For our Ops, Circus provide a small console that will let them watch
the workers, add some, restart them, etc. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;We've reached a point where we have almost all the features we wanted
for our needs, but I suspect the project will gain many more features
with the contributions of Benoit and maybe other folks in the future. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Nothing's released yet -- I'll wait for it to pass our benches, QA
tests before I cut a release. But the code is growing here if you're
curious : &lt;a href="https://github.com/mozilla-services/circus"&gt;https://github.com/mozilla-services/circus&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Yeah, it's under-tested because I did not come up with a nice testing
environment yet - it's hard to do this properly when you deal with
processes and signals -- and mocking this is a bit of a non-sense. I
suspect the best way will be to run functional tests with workers that
produce some content the test can check out.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;The Flying Circus official food (cc)&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;https:&lt;/span&gt;&lt;span class="sr"&gt;//gi&lt;/span&gt;&lt;span class="n"&gt;thub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="sr"&gt;/mozilla-services/&lt;/span&gt;&lt;span class="n"&gt;powerhose&lt;/span&gt;&lt;span class="sr"&gt;/blob/m&lt;/span&gt;&lt;span class="n"&gt;aster&lt;/span&gt;&lt;span class="sr"&gt;/powerhose/c&lt;/span&gt;&lt;span class="n"&gt;lient&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;workers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 24 Feb 2012 16:58:00 +0100</pubDate><guid>http://blog.ziade.org/2012/02/24/circus-a-process-controller/</guid></item><item><title>Defining a wsgi app deployment standard</title><link>http://blog.ziade.org/2012/02/10/defining-a-wsgi-app-deployment-standard/</link><description>&lt;p&gt;Next month at Pycon, we'll have a web summit and I'm invited there to
talk about how I deploy web applications. This is not a new topic, as it
was already discussed a bit last year -- &lt;a href="http://blog.ianbicking.org/2011/03/31/python-webapp-package/"&gt;see Ian Bicking's thought on
the topic&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;My presentation at the summit will be in two parts. I want to 1/
explain how I organized our Python deployments at Mozilla (using RPMs)
2/ make an initial proposal for a deployment standard that would work
for the community at large - I intend to work on this during Pycon and
later on the dedicated SIG. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here's an overview of the deployment standard idea... &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;How we deploy usually&lt;/h3&gt;
&lt;p&gt;If I want to roughly summarize how people deploy their web applications
these days, from my knowledge I'd say that there are two main
categories. &lt;br /&gt;
1.  Deployments that need to be done in the context of an existing
    packaging system -- like RPM or DPKG
2.  Deployments that are done in no particular context, where we want it
    to &lt;em&gt;just work&lt;/em&gt;. -- like a directory containing a virtualenv and all
    the dependencies needed.&lt;/p&gt;
&lt;p&gt;In both cases, preparing a deployment usually consists of fetching
Python packages at PyPI and maybe compile some of them. These steps are
usually done using tools like &lt;em&gt;zc.buildout&lt;/em&gt; or &lt;em&gt;virtualenv + pip&lt;/em&gt;, and
in the case of Mozilla Services, a custom tool that transforms all
dependencies into RPMs. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In one case we end up with a directory filled with everything needed to
run the application, except the system dependencies, and in the other
case with a collection of RPMs that can be deployed on the target
system. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But in both cases, we end up using the same thing: &lt;strong&gt;a complete list of
Python dependencies&lt;/strong&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The trick with using tools like zc.buildout or pip is that from an
initial list of dependencies, you end up pulling indirect dependencies.
For instance, the &lt;em&gt;Pyramid&lt;/em&gt; package will pull the &lt;em&gt;Mako&lt;/em&gt; package and so
on. A good practice is to have them listed in a single place and to pin
each package to a specific version before releasing the app. Both pip
and zc.buildout have tools to do this. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Deployments practices I have seen so far: &lt;br /&gt;
-   a collection of rpms/debian packages/etc are built using tools like
    bdist_rpms etc.
-   a virtualenv-based directory is created in-place in production or as
    a pre-build binary release that's archived and copied in production
-   a zc-buildout-based directory is created in-place in production or
    as a pre-build binary release that's archived and copied in
    production&lt;/p&gt;
&lt;p&gt;The part that's still fuzzy for everyone that is not using RPMs or
Debian packages is how to list system-level dependencies. We introduced
in PEP 345 the notion of &lt;em&gt;hint&lt;/em&gt; where you can define system level
dependencies which name may not be the actual name on the target system.
So if you say you need &lt;strong&gt;&lt;em&gt;libxml-dev&lt;/em&gt;&lt;/strong&gt;, which is valid under Debian,
people that deploy your system will know they'll need &lt;strong&gt;&lt;em&gt;libxml-devel&lt;/em&gt;&lt;/strong&gt;
under Fedora. Yeah no magic here, it's a tough issue. &lt;a href="http://www.python.org/dev/peps/pep-0345/#requires-external-multiple-use"&gt;see
Requires-External&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;The Standard&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;EDIT : Ian has a much more rich standard proposal &lt;a href="https://github.com/ianb/pywebapp/blob/master/docs/spec.txt"&gt;here&lt;/a&gt;. (see the
comments)&lt;/em&gt;&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The standard I have in mind is a very lightweight standard that could
be useful in all our deployment practices - it's a thin layer on the top
of the WSGIstandard. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;A wsgi application is a directory containing: &lt;br /&gt;
-   a text file located in the directory at &lt;strong&gt;&lt;em&gt;dependencies.txt&lt;/em&gt;&lt;/strong&gt;,
    listing all dependencies - possibly reusing Pip's requirements
    format
-   a text file located in the directory at
    &lt;strong&gt;&lt;em&gt;external&lt;/em&gt;&lt;/strong&gt;&lt;strong&gt;&lt;em&gt;-dependencies.txt&lt;/em&gt;&lt;/strong&gt;, listing all system
    dependencies - possibly reusing PEP 345 format
-   a Python script located it the directory at &lt;strong&gt;&lt;em&gt;bin/wsgiapp&lt;/em&gt;&lt;/strong&gt; with
    an "application" variable. The shebang line of the Python script
    might also point to a local Python interpreter (a virtualenv
    version)&lt;/p&gt;
&lt;p&gt;From there we have all kind of possible scenarios where the application
can be built and/or run with the usual set of tools &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here's one example of a deployment from scratch : &lt;br /&gt;
-   The repository of the project is cloned
-   A virtualenv is created in the repository clone
-   pip, which gets installed with virtualenv, is used to install all
    dependencies describes in&lt;strong&gt;&lt;em&gt; dependencies.txt&lt;/em&gt;&lt;/strong&gt;
-   gunicorn is used to run the app locally using "cd bin; gunicorn
    wsgiapp:application"
-   the directory is zipped and sent in production
-   the directory is unzipped
-   virtualenv is run again in the directory
-   the app is hooked to Apache+mod_wsgi&lt;/p&gt;
&lt;p&gt;Another scenario I'd use in our RPM environment: &lt;br /&gt;
-   The repository of the project is cloned
-   a RPM is built for each package in &lt;strong&gt;&lt;em&gt;dependencies.txt&lt;/em&gt;&lt;/strong&gt;
-   if possible, &lt;strong&gt;&lt;em&gt;external-dependencies.txt&lt;/em&gt;&lt;/strong&gt; is used to feed a spec
    file.
-   the app is deployed using the RPM collection&lt;/p&gt;
&lt;p&gt;That's the idea, roughly -- a light standard to point a wsgi app and a
list of dependencies.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 10 Feb 2012 00:14:00 +0100</pubDate><guid>http://blog.ziade.org/2012/02/10/defining-a-wsgi-app-deployment-standard/</guid></item><item><title>Scaling Crypto work in Python</title><link>http://blog.ziade.org/2012/02/06/scaling-crypto-work-in-python/</link><description>&lt;p&gt;We're building a new service at &lt;a href="https://wiki.mozilla.org/Services/"&gt;Services&lt;/a&gt; called the &lt;strong&gt;&lt;em&gt;Token
Server&lt;/em&gt;&lt;/strong&gt; - The idea is simple : give us a &lt;a href="https://browserid.org/"&gt;Browser ID&lt;/a&gt; assertion and
a service name, and the Token Server will send you back a token that's
good for 30 minutes to use for the specific service. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;That indirection makes our live easier to manage user authentication
and resource allocation for our services . A few examples: &lt;br /&gt;
-   when a new user wants to use Firefox Sync, we can check which server
    has the smallest number of allocated users, and tell the user to go
    there
-   we can manage a user from a central place
-   we can manage a user we've never heard about before without asking
    her to register specifically to each service -- that's the whole
    point of Browser ID&lt;/p&gt;
&lt;p&gt;I won't get into more details because that's not the intent of this
blog post. But if you are curious the full draft spec is here
-&lt;a href="https://wiki.mozilla.org/Services/Sagrada/TokenServer"&gt;https://wiki.mozilla.org/Services/Sagrada/TokenServer&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;What's this post is really about is how to build this token server. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The server is a single web service that gets a Browser ID assertion and
does the following: &lt;br /&gt;
1.  verify the assertion
2.  create a token, which is a simple JSON mapping
3.  encrypt and sign the token&lt;/p&gt;
&lt;h3&gt;The GIL, Gevent, greenlet and the likes&lt;/h3&gt;
&lt;p&gt;Implementing this using &lt;a href="http://packages.python.org/cornice/"&gt;Cornice&lt;/a&gt; and a crypto lib is quite simple,
but has one major issue : the crypto work is CPU intensive, and even if
the libraries we can use have C code under the hood, it seems that the
GIL is not released enough to let your threads really use several cores.
For example, we benched M2Crypto and it was obvious that a
multi-threaded app was locked by the GIL. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But we don't use threads in our Python servers -- we use Gevent
workers, which are based on greenlets. But while greenlets help on I/O
bound calls, it won't help on CPU bound work : you're tied into a single
thread in this case and each greenlet that does some CPU work blocks the
other ones. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It's easy to demonstrate -- see
&lt;a href="http://tarek.pastebin.mozilla.org/1476644"&gt;http://tarek.pastebin.mozilla.org/1476644&lt;/a&gt; If I run it on my Mac Book
Air, the pure Python synchronous version is always faster (huh, the
gevent version is *much* slower, not sure why..) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So the sanest option is to use separate processes and set up a
messaging queue between the web service that needs some crypto work to
be done and specialized crypto workers. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;We're back in that case to our beloved 100% I/O bound model we know how
to scale using NGinx + GUnicorn + GEvent &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For the crypto workers, we want it to be as fast as possible, so we
started to look at &lt;a href="http://cryptopp.com/"&gt;Crypto++&lt;/a&gt; which seems promising because it uses
CPU-specific calls in ASM. There's the &lt;a href="https://tahoe-lafs.org/trac/pycryptopp"&gt;pycryptopp&lt;/a&gt; binding that's
available to work with Crypto++ but we happen to need to do some tasks
that are not available in that lib yet -- like &lt;a href="http://en.wikipedia.org/wiki/HKDF"&gt;HKDF&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Yeah, at that point it became obvious we'd use pure C++ for that part,
and drive it from Python. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Message passing&lt;/h3&gt;
&lt;p&gt;Back to our Token server -- we need to send crypto work to our workers
and get back the result. The first option that comes in mind is to use
&lt;a href="http://docs.python.org/library/multiprocessing.html"&gt;multiprocessing&lt;/a&gt; to spawn our C++ workers and to feed them with work.&lt;/p&gt;
&lt;p&gt;The model is quite simple, but now that we have one piece in C++, it's
getting harder to use the built-in tools in multiprocessing to
communicate with our workers -- we need to be lower level and start to
work with signals or sockets. And well, I am not sure what would be left
of multiprocessing then. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This is doable but a bit of a pain to do correctly (and in a portable
way.) Moreover, if we want to have a robust system, we need to have
things like a hearbeat, which requires more inter-process message
passing. And now I need to code it in Python &lt;strong&gt;&lt;em&gt;and&lt;/em&gt;&lt;/strong&gt; C++ &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Hold on -- Let me summarize my requirements: &lt;br /&gt;
-   inter-process communication
-   something less painful than signals or sockets
-   very very very fast&lt;/p&gt;
&lt;p&gt;I got tempted by &lt;a href="http://en.wikipedia.org/wiki/Memory-mapped_file"&gt;Memory Mapped Files&lt;/a&gt;, but the drawbacks I've read
here and there scared me. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;ZeroMQ&lt;/h3&gt;
&lt;p&gt;It turns out &lt;a href="http://www.zeromq.org/"&gt;zeromq&lt;/a&gt; is perfect for this job - there are clients in
Python and C++, and defining a protocol to exchange data from the Python
web server to the crypto workers is quite simple. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In fact, this can be done as a reusable library that takes care of
passing messages to workers and getting back results. It has been done
hundreds of times, there are many examples in the zmq website, but I
have failed to find any Python packaged library that would let me push
some work to workers transparently, via a simple &lt;em&gt;execute()&lt;/em&gt; call -- if
you know one tell me!. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So I am building one since it's quite short and simple -- The project
is called &lt;strong&gt;&lt;em&gt;PowerHose&lt;/em&gt;&lt;/strong&gt; and is located here
:&lt;a href="https://github.com/mozilla-services/powerhose"&gt;https://github.com/mozilla-services/powerhose&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here is its descriptions/limitations: &lt;br /&gt;
-   Powerhose is based on a single master and multiple workers protocol
-   The Master opens a socket and waits for workers to register
    themselves into it
-   The worker registers itself to the master, provides the path to its
    own socket, and wait for some work on it.
-   Workers are performing the work synchronously and send back the
    result immediatly.
-   The master load-balances on available workers, and if all are busy
    waits a bit before it times out.
-   The worker pings the master on a regular basis and exits if it's
    unable to reach it. It attempts several time to reconnect to give a
    chance to the master to come back.
-   Workers are language agnostic and a master could run heterogeneous
    workers (one in C, one in Python etc..)
-   Powerhose is not serializing/deserializing the data - it sends plain
    strings. This is the responsibility of the program that uses it.
-   Powerhose is not responsible to respawn a master or a worker that
    dies. I plan to use &lt;a href="http://cr.yp.to/daemontools.html"&gt;daemontools&lt;/a&gt; for this, and maybe provide a
    script that runs all workers at once.
-   Powerhose do not queue works and just rely on zeromq sockets.&lt;/p&gt;
&lt;p&gt;The library implements this protocol and gives two tools to use it: &lt;br /&gt;
-   A JobRunner class in Python, you can use to send some work to be
    done
-   A Worker class in Python &lt;strong&gt;and&lt;/strong&gt; C++, you can use as a base class to
    implement workers&lt;/p&gt;
&lt;p&gt;Here's an example of using Powerhose: &lt;br /&gt;
-   The Server -
    [https://github.com/mozilla-services/powerhose/blob/master/examples/square_master.py][]
-   The Python worker -
    [https://github.com/mozilla-services/powerhose/blob/master/examples/square_worker.py][]
-   The C++ worker (don't look at the code :) -
    [https://github.com/mozilla-services/powerhose/blob/master/examples/square_worker.cpp][]&lt;/p&gt;
&lt;p&gt;For the Token server, we'll have: &lt;br /&gt;
-   A JobRunner in our Cornice application
-   A C++ worker that uses Crypto++&lt;/p&gt;
&lt;p&gt;The first benches look fantastic -- probably faster that anything I'd
have implemented myself using plain sockets :) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I'll try to package Powerhose so other projects at Mozilla can use it.
I am wondering if this could be useful to more people, since I failed to
find that kind of tool. How do &lt;em&gt;you&lt;/em&gt; scale your CPU-bound web apps ?&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;https:&lt;/span&gt;&lt;span class="sr"&gt;//gi&lt;/span&gt;&lt;span class="n"&gt;thub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="sr"&gt;/mozilla-services/&lt;/span&gt;&lt;span class="n"&gt;powerhose&lt;/span&gt;&lt;span class="sr"&gt;/blob/m&lt;/span&gt;&lt;span class="n"&gt;aster&lt;/span&gt;&lt;span class="sr"&gt;/examples/s&lt;/span&gt;&lt;span class="n"&gt;quare_master&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
&lt;span class="n"&gt;https:&lt;/span&gt;&lt;span class="sr"&gt;//gi&lt;/span&gt;&lt;span class="n"&gt;thub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="sr"&gt;/mozilla-services/&lt;/span&gt;&lt;span class="n"&gt;powerhose&lt;/span&gt;&lt;span class="sr"&gt;/blob/m&lt;/span&gt;&lt;span class="n"&gt;aster&lt;/span&gt;&lt;span class="sr"&gt;/examples/s&lt;/span&gt;&lt;span class="n"&gt;quare_worker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
&lt;span class="n"&gt;https:&lt;/span&gt;&lt;span class="sr"&gt;//gi&lt;/span&gt;&lt;span class="n"&gt;thub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="sr"&gt;/mozilla-services/&lt;/span&gt;&lt;span class="n"&gt;powerhose&lt;/span&gt;&lt;span class="sr"&gt;/blob/m&lt;/span&gt;&lt;span class="n"&gt;aster&lt;/span&gt;&lt;span class="sr"&gt;/examples/s&lt;/span&gt;&lt;span class="n"&gt;quare_worker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cpp&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Mon, 06 Feb 2012 09:03:00 +0100</pubDate><guid>http://blog.ziade.org/2012/02/06/scaling-crypto-work-in-python/</guid></item><item><title>Mozillians, win a free pass for Pycon US - take 2</title><link>http://blog.ziade.org/2012/01/04/mozillians-win-a-free-pass-for-pycon-us-take-2/</link><description>&lt;p&gt;I am extending the contest until Feb the 1st - &lt;a href="https://tarekziade.wordpress.com/2011/10/13/mozillians-win-a-free-pass-for-pycon-us/"&gt;Mozillians, win a free
pass for Pycon US&lt;/a&gt;&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Wed, 04 Jan 2012 23:41:00 +0100</pubDate><guid>http://blog.ziade.org/2012/01/04/mozillians-win-a-free-pass-for-pycon-us-take-2/</guid></item><item><title>The fear of CRUD</title><link>http://blog.ziade.org/2012/01/03/the-fear-of-crud/</link><description>&lt;p&gt;&lt;a href="http://packages.python.org/cornice/"&gt;Cornice&lt;/a&gt; is growing steadily, and we are thinking about the different
ways to use it for our needs. One use case that comes often when we
build web services is the need to publish a SQL Database via HTTP. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For instance, in a project I am working on, we might expose a list of
servers and some information about them, that are stored in a SQL DB .
The goal is to allow some management scripts to interact with the DB, to
set and retrieve information about the servers, like: "can I use &lt;em&gt;server
12&lt;/em&gt; as a node for &lt;em&gt;application X&lt;/em&gt; ?" &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Interacting with CURL or a similar tool is simpler and more portable
than coding yet another SQL client for this, so the idea is to see how
this kind of web service can be done is the minimum pain with Cornice. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;What I am thinking about building is a small &lt;a href="https://en.wikipedia.org/wiki/Create,_read,_update_and_delete"&gt;CRUD&lt;/a&gt; interface that
glues Cornice and SQLAlchemy. The latter has a way to define a database
schema explicitly via &lt;a href="http://www.sqlalchemy.org/docs/orm/mapper_config.html"&gt;&lt;em&gt;mappings&lt;/em&gt;&lt;/a&gt; meaning that it's easy to write a
generic layer that exposes the database to the web via Cornice
definitions. The work consists of transforming POST &amp;amp; PUT requests that
contains data to write to the DB into SQLAlchemy objects, and
transforming select results asked via GET requests into the proper
responses. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Nothing very new, there are tons of existing systems that implement
CRUD on the top of &lt;a href="https://en.wikipedia.org/wiki/Object-Relational_Mapping"&gt;ORMs&lt;/a&gt; or plain SQL libraries. The only reason to
build yet another one is to use it in the context of our current toolset
which is composed of Cornice, Pyramid &amp;amp; SQLAlchemy for most projects.
The whole code will probably be less than 300 lines at the end anyways.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;*Oh my&lt;/strong&gt;.* &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Turns out this idea is really freaking out some people around me.
There's a strong aversion of some coders against anything that looks a
bit like &lt;a href="https://en.wikipedia.org/wiki/Active_record"&gt;Active Records&lt;/a&gt; -- in the Rails Context. In other words
anything that would completely automate the serialization &amp;amp;
deserialization layer and make it hard to tweak some code. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Another criticism is that a CRUD system would not be able to scale in
the context of a big database, like Firefox Sync, that uses numerous
databases to shard data. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Turns out building a CRUD on tools like SQLAlchemy or Pyramid is not
really going to ruin your scalability as long as: &lt;br /&gt;
-   you can tweak the serialization / deserialization
-   you can override any operation in the CRUD operations when needed
-   you don't shoot yourself in the foot by using CRUD with some code or
    DB that is not meant to be used that way
-   you can use the power of the underlying tools without being blocked
    by the lib&lt;/p&gt;
&lt;p&gt;For the latter, Ben Bangert was pointing me at &lt;a href="http://www.sqlalchemy.org/docs/orm/extensions/horizontal_shard.html"&gt;SQLAlchemy horizontal
feature&lt;/a&gt;, which is basically what I wrote from scratch last year to
make the Sync server shard across databases... At this point I sense
that Firefox Sync could have been built with a CRUD lib, and be as
efficient as it is today, because when I look at the queries produced by
the code and the one a CRUD lib would produce, we are one or two tweaks
away. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Anyways, here's a first attempt at such a library. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Defining the model&lt;/h3&gt;
&lt;p&gt;In SQLAlchemy, you can define the DB model using mappings, which are
simple classes containing a description of the tables. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For example, if I have a class "users" with a field "id" and a field
"name", the mapping will look like this: &lt;br /&gt;
   class Users(_Base):                                           &lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;__tablename__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;users&amp;#39;&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;primary_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;nullable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;What I started to do is write a meta class one can use in a class to
publish the mapping via HTTP. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here's an example: &lt;br /&gt;
   from cornicesqla import MetaDBView&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;myapp&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DBSession&lt;/span&gt;

&lt;span class="n"&gt;class&lt;/span&gt; &lt;span class="n"&gt;UsersView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;__metaclass__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MetaDBView&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;mapping&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Users&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;/users/{id}&amp;#39;&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;collection_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;/users&amp;#39;&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DBSession&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;What we have here is the definition of a view for the Users mapping.
The class defines an URI for the collection (collection_path) and for
each user (path). The session attribute is an SQLAlchemy &lt;a href="http://www.sqlalchemy.org/docs/orm/session.html"&gt;session&lt;/a&gt;
object you usually define when you work with that tool. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;That's it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The model gets published, and you can GET, PUT, POST and DELETE on
&lt;em&gt;/users&lt;/em&gt; and &lt;em&gt;/users/someid.&lt;/em&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The code of the prototype is &lt;a href="https://github.com/mozilla-services/cornice-sqla/blob/master/cornicesqla/views.py"&gt;here&lt;/a&gt; and you can find &lt;a href="https://github.com/mozilla-services/cornice-sqla/tree/master/cornicesqla/tests"&gt;a working
example in the tests here&lt;/a&gt;. It's called &lt;strong&gt;cornice-sqla&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Tweaking serialization &amp;amp; data validation&lt;/h3&gt;
&lt;p&gt;By default, &lt;em&gt;cornice-sqla&lt;/em&gt; will serialize and deserialize using JSON
but you can tweak these steps by providing a custom serializer, or
deserializer (or both.) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Let's say you want to use the &lt;a href="http://docs.pylonsproject.org/projects/colander/en/latest/"&gt;Colander libary&lt;/a&gt; to validate and
serialize the data. To do this, you just have to write your serializer
method into the view class &lt;br /&gt;
   class UsersValidation(colander.MappingSchema):&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;colander&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SchemaNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;colander&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="n"&gt;class&lt;/span&gt; &lt;span class="n"&gt;UsersView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;__metaclass__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MetaDBView&lt;/span&gt;

    &lt;span class="n"&gt;mapping&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Users&lt;/span&gt;

    &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;/users/{id}&amp;#39;&lt;/span&gt;

    &lt;span class="n"&gt;collection_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;/users&amp;#39;&lt;/span&gt;

    &lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DBSession&lt;/span&gt;

    &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;Unserialize the data from the request, to serialize it for the DB&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

        &lt;span class="n"&gt;try:&lt;/span&gt;

            &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;except&lt;/span&gt; &lt;span class="n"&gt;ValueError:&lt;/span&gt;

            &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;body&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;item&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Bad Json data!&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="c1"&gt;# let&amp;#39;s quit&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt;

        &lt;span class="n"&gt;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;UsersValidation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="n"&gt;try:&lt;/span&gt;

            &lt;span class="n"&gt;deserialized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deserialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;except&lt;/span&gt; &lt;span class="n"&gt;Invalid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e:&lt;/span&gt;

            &lt;span class="c1"&gt;# the struct is invalid&lt;/span&gt;

            &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;body&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;item&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;deserialized&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Colander is used here to validate the incoming request and create a
flat mapping we can push into the DB. Cornice's error system is in usage
here, as explained &lt;a href="http://packages.python.org/cornice/validation.html"&gt;here&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;You can tweak the data that gets back from the DB with
&lt;strong&gt;&lt;em&gt;unserialize()&lt;/em&gt;&lt;/strong&gt;, and for the collection URI, use
&lt;strong&gt;&lt;em&gt;collection_serialize()&lt;/em&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;em&gt;collection_unserialize()&lt;/em&gt;&lt;/strong&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Tweaking C, R, U or D&lt;/h3&gt;
&lt;p&gt;cornice-sqla is based on a fresh feature Gael added into Cornice
lately: &lt;a href="https://github.com/mozilla-services/cornice/blob/master/docs/source/resources.rst"&gt;resources&lt;/a&gt;. A resource is a class where you can define
&lt;em&gt;get()&lt;/em&gt;, &lt;em&gt;post()&lt;/em&gt;, &lt;em&gt;delete()&lt;/em&gt; and &lt;em&gt;put()&lt;/em&gt; methods for a given URI. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;cornice-sqla views are based on resources, meaning that you can
override anyone of those methods and do whatever you want if you don't
want the CRUD part. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;What's next&lt;/h3&gt;
&lt;p&gt;I need to make sure everything you can do in Cornice (acls various
options etc) can still be done in cornice-sqla, and start to work with
more complex DB schema that include relations etc. I also need to add
basic missing features like batching and some docs. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;My hope at the end is that this small library will reduce considerably
the code needed in some of our projects that interact with SQL.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 03 Jan 2012 00:17:00 +0100</pubDate><guid>http://blog.ziade.org/2012/01/03/the-fear-of-crud/</guid></item><item><title>Pyramid @ Python 3</title><link>http://blog.ziade.org/2011/12/25/pyramid-python-3/</link><description>&lt;p&gt;If you have been following closely the latest work done by Chris on
WebOb, you know that WebOb and eventually Pyramid became Python 3
compatible. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;That makes Python 3 a very tempting target for a new web project. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Paste &amp;amp; PasteScript still need to be ported to Python 3 and the Pyramid
team has chosen not to. They have created their own paster replacer
instead, which can be used to initiate a Pyramid project or run the app
using the &lt;em&gt;.ini&lt;/em&gt; file. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I am wondering if it would not be simpler at this point to drop Paste
and use this replacer for all Python 3 frameworks that are using the
Paste script and templates features. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Besides all the features Pyramid and its libs turns out most of the
libs you usually need to build a classical web app already support
Python 3, like SQLALchemy and PyMysql for MySQL access, Pylibmc for
Memcached; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Things I am still missing in Python 3: &lt;br /&gt;
-   gevent
-   gunicorn
-   python-ldap
-   Cornice -- I will port it soon&lt;/p&gt;
&lt;p&gt;If you want to give it a shot, get the latest Python 3.2 and grab more
details at :&lt;a href="https://github.com/Pylons/pyramid/wiki/Python-3-Porting"&gt;https://github.com/Pylons/pyramid/wiki/Python-3-Porting&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And if you miss one lib, &lt;a href="https://plus.google.com/u/1/106436370949746015255/posts/SAwkyVyUhWV"&gt;add it here&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Merry Christmas !&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sun, 25 Dec 2011 11:15:00 +0100</pubDate><guid>http://blog.ziade.org/2011/12/25/pyramid-python-3/</guid></item><item><title>Tutorial - build your web services with Cornice</title><link>http://blog.ziade.org/2011/12/21/tutorial-build-your-web-services-with-cornice/</link><description>&lt;p&gt;At this stage, I think we've added enough helpers in Cornice to get
anyone started in building web services in Python. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;As a reminder, Cornice provides helpers to build &amp;amp; document REST-ish
Web Services with Pyramid, a Python web framework. The main benefits of
Cornice are: &lt;br /&gt;
-   automatic handling of some HTTP errors - Ask yourself: is your app
    handling properly 405 or 406 errors?
-   automatic web service documentation via a Sphinx extension.
-   a simple way to validate and convert requests data, and return
    structured 400 responses.&lt;/p&gt;
&lt;p&gt;This is a small tutorial, extracted from our documentation. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Let’s create a full working application with &lt;strong&gt;Cornice&lt;/strong&gt;. We want to
create a light messaging service. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;You can find its whole source code at
&lt;a href=""&gt;https://github.com/mozilla-services/cornice/blob/master/examples/messaging&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Features: &lt;br /&gt;
-   users can register to the service
-   users can list all registered users
-   users can send messages
-   users can retrieve the latest messages
-   messages have three fields: sender, content, color (red or black)
-   adding a message is done through authentication&lt;/p&gt;
&lt;p&gt;Limitations: &lt;br /&gt;
-   there’s a single channel for all messages.
-   if a user with the same name is already registered, he cannot
    register.
-   all messages and users are kept in memory.&lt;/p&gt;
&lt;h2&gt;Design&lt;/h2&gt;
&lt;p&gt;The application provides two services: &lt;br /&gt;
-   &lt;strong&gt;users&lt;/strong&gt;, at &lt;strong&gt;/users&lt;/strong&gt;: where you can list all users or register a
    new one
-   &lt;strong&gt;messages&lt;/strong&gt;, at &lt;strong&gt;/&lt;/strong&gt;: where you can read the messages or add new
    ones&lt;/p&gt;
&lt;p&gt;On the server, the data is kept in memory. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;We’ll provide a single CLI client in Python, using Curses. &lt;br /&gt;
&lt;/p&gt;
&lt;h2&gt;Setting up the development environment&lt;/h2&gt;
&lt;p&gt;To create this application, we’ll use Python 2.7. Make sure you have it
on your system, then install &lt;strong&gt;virtualenv&lt;/strong&gt; (see
&lt;a href="http://pypi.python.org/pypi/virtualenv"&gt;http://pypi.python.org/pypi/virtualenv&lt;/a&gt;.) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Create a new directory and a virtualenv in it: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;$ mkdir messaging&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;cd&lt;/span&gt; &lt;span class="n"&gt;messaging&lt;/span&gt;

&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;virtualenv&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nb"&gt;no&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt; &lt;span class="n"&gt;packages&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Once you have it, install Cornice in it with Pip: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;$ bin/pip install Cornice&lt;/p&gt;
&lt;p&gt;Cornice provides a Paster Template you can use to create a new
application: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;$ bin/paster create -t cornice messaging&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;Selected&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;implied&lt;/span&gt; &lt;span class="n"&gt;templates:&lt;/span&gt;

&lt;span class="n"&gt;cornice&lt;/span&gt;&lt;span class="c1"&gt;#cornice  A Cornice application&lt;/span&gt;

&lt;span class="n"&gt;Variables:&lt;/span&gt;

&lt;span class="n"&gt;egg:&lt;/span&gt;      &lt;span class="n"&gt;messaging&lt;/span&gt;

&lt;span class="nb"&gt;package&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;messaging&lt;/span&gt;

&lt;span class="n"&gt;project:&lt;/span&gt;  &lt;span class="n"&gt;messaging&lt;/span&gt;

&lt;span class="n"&gt;Enter&lt;/span&gt; &lt;span class="n"&gt;appname&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Application&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;Messaging&lt;/span&gt;

&lt;span class="n"&gt;Enter&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;One&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="n"&gt;simple&lt;/span&gt; &lt;span class="n"&gt;messaging&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;Enter&lt;/span&gt; &lt;span class="n"&gt;author&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Author&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;Tarek&lt;/span&gt;

&lt;span class="n"&gt;Creating&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt; &lt;span class="n"&gt;cornice&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="n"&gt;Generating&lt;/span&gt; &lt;span class="n"&gt;Application&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="n"&gt;Running&lt;/span&gt; &lt;span class="n"&gt;python2&lt;/span&gt;&lt;span class="mf"&gt;.7&lt;/span&gt; &lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; &lt;span class="n"&gt;egg_info&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Once your application is generated, go there and call &lt;em&gt;develop&lt;/em&gt; against
it: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;$ cd messaging&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="err"&gt;../&lt;/span&gt;&lt;span class="nv"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; &lt;span class="n"&gt;develop&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The application can now be launched via Paster, it provides a default
“Hello” service, you can check: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;$ ../bin/paster serve messaging.ini&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;Starting&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;PID&lt;/span&gt; &lt;span class="mi"&gt;7618&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;serving&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="mf"&gt;0.0.0.0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="mf"&gt;127.0.0.1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Once the application is running, visit &lt;a href="http://127.0.0.1:5000/"&gt;http://127.0.0.1:5000&lt;/a&gt; in
your browser or Curl and make sure you get: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;{'Hello': 'World'}&lt;/p&gt;
&lt;h2&gt;Defining the services&lt;/h2&gt;
&lt;p&gt;Let’s open the file in messaging/views.py, it contains all the
Services: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;from cornice import Service&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;hello&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;hello&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Simplest app&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;@hello&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;get_info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;Returns Hello in JSON.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Hello&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;World&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;Users managment&lt;/h3&gt;
&lt;p&gt;We’re going to get rid of the Hello service, and change this file in
order to add our first service - the users managment &lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;_USERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;users&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;/users&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Users&amp;quot;&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="nv"&gt;@users&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;valid_token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;get_users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;Returns a list of all users.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;users&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;_USERS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;

&lt;span class="nv"&gt;@users&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;create_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;Adds a new user.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;validated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;user&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;_USERS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;token&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;token&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;%s-%s&amp;#39;&lt;/span&gt; &lt;span class="nv"&gt;%&lt;/span&gt; &lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;token&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])}&lt;/span&gt;

&lt;span class="nv"&gt;@users&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;valid_token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;del_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;Removes the user.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;validated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;user&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;del&lt;/span&gt; &lt;span class="n"&gt;_USERS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;goodbye&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;What we have here is 3 methods on &lt;strong&gt;/users&lt;/strong&gt;: &lt;br /&gt;
-   &lt;strong&gt;GET&lt;/strong&gt;: simply return the list of users names – the keys of _USERS
-   &lt;strong&gt;PUT&lt;/strong&gt;: adds a new user and returns a unique token
-   &lt;strong&gt;DELETE&lt;/strong&gt;: removes the user.&lt;/p&gt;
&lt;p&gt;Remarks: &lt;br /&gt;
-   &lt;strong&gt;PUT&lt;/strong&gt; uses the &lt;strong&gt;unique&lt;/strong&gt; validator to make sure that the user
    name is not already taken. That validator is also in charge of
    generating a unique token associated with the user.
-   &lt;strong&gt;GET&lt;/strong&gt; users the &lt;strong&gt;valid_token&lt;/strong&gt; to verify that a
    &lt;strong&gt;X-Messaging-Token&lt;/strong&gt; header is provided in the request, with a
    valid token. That also identifies the user.
-   &lt;strong&gt;DELETE&lt;/strong&gt; also identifies the user then removes it.&lt;/p&gt;
&lt;p&gt;Validators are filling the &lt;strong&gt;request.validated&lt;/strong&gt; mapping, the service
can then use. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here’s their code: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;import os&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;binascii&lt;/span&gt;

&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;webob&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;_create_token&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;binascii&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;b2a_hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urandom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;valid_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;X-Messaging-Token&amp;#39;&lt;/span&gt;

    &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;None:&lt;/span&gt;

        &lt;span class="n"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTPUnauthorized&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;-&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

        &lt;span class="n"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTPUnauthorized&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;

    &lt;span class="n"&gt;valid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;_USERS&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;_USERS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;valid:&lt;/span&gt;

        &lt;span class="n"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTPUnauthorized&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;validated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;user&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;_USERS:&lt;/span&gt;

        &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;url&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;This user exists!&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

        &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;token&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;_create_token&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;

        &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;validated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;user&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;When the validator finds errors, it adds them to the &lt;strong&gt;request.errors&lt;/strong&gt;
mapping, and that will return a 400 with the errors. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Let’s try our application so far with CURL: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;$ curl http://localhost:5000/users&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;status&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;error&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;errors&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;location&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;header&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

                                &lt;span class="s"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;X-Messaging-Token&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

                                &lt;span class="s"&gt;&amp;quot;description&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;No token&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}]}&lt;/span&gt;

&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;curl&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt; &lt;span class="n"&gt;PUT&lt;/span&gt; &lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;localhost:5000&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;tarek&amp;#39;&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;token&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;tarek-a15fa2ea620aac8aad3e1b97a64200ed77dc7524&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;curl&lt;/span&gt; &lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;localhost:5000&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;H&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;X-Messaging-Token:tarek-a15fa2ea620aac8aad3e1b97a64200ed77dc7524&amp;quot;&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;users&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;tarek&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;

&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;curl&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt; &lt;span class="n"&gt;DELETE&lt;/span&gt; &lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;localhost:5000&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;H&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;X-Messaging-Token:tarek-a15fa2ea620aac8aad3e1b97a64200ed77dc7524&amp;quot;&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Goodbye&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;tarek&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;Messages managment&lt;/h3&gt;
&lt;p&gt;Now that we have users, let’s post and get messages. This is done via
two very simple functions we’re adding in the &lt;code&gt;views.py&lt;/code&gt; file: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;messages = Service(name='messages', path='/', description="Messages")&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;_MESSAGES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt;

&lt;span class="nv"&gt;@messages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;get_messages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;Returns the 5 latest messages&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_MESSAGES&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nv"&gt;@messages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;valid_token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;valid_message&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;post_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;Adds a message&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="n"&gt;_MESSAGES&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;validated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;message&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;status&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;added&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The first one simply returns the five first messages in a list, and the
second one inserts a new message in the beginning of the list. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;POST&lt;/strong&gt; uses two validators: &lt;br /&gt;
-   &lt;code&gt;valid_token()&lt;/code&gt;: the function we used previously that makes sure the
    user is registered
-   &lt;code&gt;valid_message()&lt;/code&gt;: a function that looks at the message provided in
    the POST body, and puts it in the validated dict.&lt;/p&gt;
&lt;p&gt;Here’s the &lt;code&gt;valid_message()&lt;/code&gt; function: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;def valid_message(request):&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;    &lt;span class="n"&gt;try:&lt;/span&gt;

        &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;except&lt;/span&gt; &lt;span class="n"&gt;ValueError:&lt;/span&gt;

        &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;body&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;message&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Not valid JSON&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt;

    &lt;span class="c1"&gt;# make sure we have the fields we want&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;text&amp;#39;&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;message:&lt;/span&gt;

        &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;body&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;text&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Missing text&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;color&amp;#39;&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;color&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;red&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;black&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;body&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;color&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;only red and black supported&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;elif&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;color&amp;#39;&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;message:&lt;/span&gt;

        &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;color&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;black&amp;#39;&lt;/span&gt;

    &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;user&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;validated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;user&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;validated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;message&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This function extracts the json body, then checks that it contains a
text key at least. It adds a color or use the one that was provided, and
reuse the user name provided by the previous validator with the token
control. &lt;br /&gt;
&lt;/p&gt;
&lt;h2&gt;Generating the documentation&lt;/h2&gt;
&lt;p&gt;Now that we have a nifty web application, let’s add some doc. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Go back to the root of your project and install Sphinx: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;$ bin/pip install Sphinx&lt;/p&gt;
&lt;p&gt;Then create a Sphinx structure with &lt;strong&gt;sphinx-quickstart&lt;/strong&gt;: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;$ mkdir docs&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;sphinx&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;quickstart&lt;/span&gt;

&lt;span class="n"&gt;Welcome&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;Sphinx&lt;/span&gt; &lt;span class="mf"&gt;1.0.7&lt;/span&gt; &lt;span class="n"&gt;quickstart&lt;/span&gt; &lt;span class="n"&gt;utility&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;..&lt;/span&gt;

&lt;span class="n"&gt;Enter&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;documentation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Root&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;documentation&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;docs&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Separate&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt; &lt;span class="n"&gt;directories&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Project&lt;/span&gt; &lt;span class="n"&gt;name:&lt;/span&gt; &lt;span class="n"&gt;Messaging&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Author&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;Tarek&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Project&lt;/span&gt; &lt;span class="n"&gt;version:&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Create&lt;/span&gt; &lt;span class="n"&gt;Makefile&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Y&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Create&lt;/span&gt; &lt;span class="n"&gt;Windows&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Y&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Once the initial structure is created, we need to declare the Cornice
extension, by editing the &lt;code&gt;source/conf.py&lt;/code&gt; file. We want to change
&lt;strong&gt;extensions = []&lt;/strong&gt; into: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;import cornice&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;extensions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;cornice.sphinxext&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The last step is to document your services by editing the
&lt;code&gt;source/index.rst&lt;/code&gt; file like this: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Welcome to Messaging's documentation!&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;=====================================&lt;/span&gt;

&lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="nn"&gt;services::&lt;/span&gt;

   &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;package&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;messaging&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The &lt;strong&gt;services&lt;/strong&gt; directive is told to look at the services in the
&lt;strong&gt;messaging&lt;/strong&gt; package. When the documentation is built, you will get a
nice output of all the services we’ve described earlier. &lt;br /&gt;
&lt;/p&gt;
&lt;h2&gt;The Client&lt;/h2&gt;
&lt;p&gt;A simple client to use against our service can do three things: &lt;br /&gt;
1.  let the user register a name
2.  poll for the latest messages
3.  let the user send a message !&lt;/p&gt;
&lt;p&gt;Without going into great details, there’s a Python CLI against
messaging that uses Curses. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;See
&lt;a href=""&gt;https://github.com/mozilla-services/cornice/blob/master/examples/messaging/messaging/client.py&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Going deeper&lt;/h1&gt;
&lt;p&gt;If you want to dig deeper, here are a few links: &lt;br /&gt;
-   Documentation - &lt;a href="http://packages.python.org/cornice"&gt;http://packages.python.org/cornice&lt;/a&gt;
-   PyPI - &lt;a href="http://pypi.python.org/pypi/cornice"&gt;http://pypi.python.org/pypi/cornice&lt;/a&gt;
-   Repository - &lt;a href="https://github.com/mozilla-services/cornice"&gt;https://github.com/mozilla-services/cornice&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We'd love feedback &amp;amp; new contributors ! &lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;https:&lt;/span&gt;&lt;span class="sr"&gt;//gi&lt;/span&gt;&lt;span class="n"&gt;thub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="sr"&gt;/mozilla-services/co&lt;/span&gt;&lt;span class="n"&gt;rnice&lt;/span&gt;&lt;span class="sr"&gt;/blob/m&lt;/span&gt;&lt;span class="n"&gt;aster&lt;/span&gt;&lt;span class="sr"&gt;/examples/m&lt;/span&gt;&lt;span class="n"&gt;essaging&lt;/span&gt;
&lt;span class="n"&gt;https:&lt;/span&gt;&lt;span class="sr"&gt;//gi&lt;/span&gt;&lt;span class="n"&gt;thub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="sr"&gt;/mozilla-services/co&lt;/span&gt;&lt;span class="n"&gt;rnice&lt;/span&gt;&lt;span class="sr"&gt;/blob/m&lt;/span&gt;&lt;span class="n"&gt;aster&lt;/span&gt;&lt;span class="sr"&gt;/examples/m&lt;/span&gt;&lt;span class="n"&gt;essaging&lt;/span&gt;&lt;span class="sr"&gt;/messaging/c&lt;/span&gt;&lt;span class="n"&gt;lient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Wed, 21 Dec 2011 11:50:00 +0100</pubDate><guid>http://blog.ziade.org/2011/12/21/tutorial-build-your-web-services-with-cornice/</guid></item><item><title>New Year&amp;#039;s Python Meme 2011</title><link>http://blog.ziade.org/2011/12/20/new-year039s-python-meme-2011/</link><description>&lt;p&gt;Hey &lt;a href="http://tarekziade.wordpress.com/2009/12/28/new-years-python-meme"&gt;I did this in 2009&lt;/a&gt;, let's try again -- I am adding one extra
question this year &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. What’s the coolest Python application, framework or library you
have discovered in 2011 ?&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GEvent &amp;amp; Pyramid&lt;/strong&gt;. Not discoveries, but a daily usage. GEvent was
for me a fantastic way to make the Firefox Sync Python server scale
without being forced to write callback-style code. Pyramid is a very
elegant framework, that takes the simplicity from Pylons and the power
and experience from Repoze &amp;amp; the Zope world. A good sign for me is that
we don't have to deal with the ZCA ;) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. What new programming technique did you learn in 2011 ?&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Better behaviour in high loaded server apps&lt;/strong&gt;. During the last year,
when we wrote all the pieces that makes Firefox Sync today, I've learned
how to be more careful on how my apps would react when a back-end breaks
or cease to reply, when a database gets slow, or when some service
that's used restarts -- or when my own app restarts, still hammered by
many requests. I did a fair amount of work on this, like smart pools of
connectors and better testing techniques, and make decisions on what
features survive when some third-party server is down, and what features
just go 503. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. What’s the name of the open source project you contributed the
most in 2011 ? What did you do ? &lt;br /&gt;
&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Mozilla&lt;/strong&gt;. I have not contributed as much as last year in Python
because my work at Mozilla takes most of my time, but the good news is
that all our stuff is open source so.. The most useful stuff for the
community we've started is probably &lt;a href="https://github.com/mozilla-services/cornice"&gt;Cornice&lt;/a&gt;. But we've written and
writing a myriad of apps and libs. See
&lt;a href="https://github.com/mozilla-services"&gt;https://github.com/mozilla-services&lt;/a&gt; and
&lt;a href="http://hg.mozilla.org/services"&gt;http://hg.mozilla.org/services&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In Python I still interact a bit with what's going on in Packaging and
hope I'll be able to spend more time on it in 2012. But some packaging
work I needed at work was also useful for the community, like pypi2rpm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. What was the Python blog or website you read the most in 2011 ?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Like in the past few years, Python Reddit. And I think I am not alone
in that case. 90% of my blog hits come from Reddit &lt;img alt=":-)" src="https://s-ssl.wordpress.com/wp-includes/images/smilies/icon_smile.gif?m=1305848327g" /&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. What are the three top things you want to learn in 2012 ?&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I'd like to learn how to program in a few new languages, just to give
them a shot. Maybe Haskell. I'd also like to finish a spare project I
have started with Benoit, and try to launch it, promote &amp;amp; market it.
Last, I'd like to learn more about Firefox arcanes -- just for my
culture. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6. What are the top software, app or lib you wish someone would write
in 2012 ?&lt;/strong&gt; &lt;br /&gt;
-   I want to take a picture of a wine bottle and have it recognized in
    an online app, where I can share my thoughts about its taste.
-   I want an Android virtual ping-pong application, where you can use
    your phone as paddle and see the e-ball through the camera &amp;amp; play
    with a friend.&lt;/p&gt;
&lt;p&gt;Want to do your own list ? here's how: &lt;br /&gt;
-   copy-paste the questions and answer to them in your blog
-   tweet it with the [#2012pythonmeme][] hashtag&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 20 Dec 2011 11:28:00 +0100</pubDate><guid>http://blog.ziade.org/2011/12/20/new-year039s-python-meme-2011/</guid></item><item><title>Mozilla Apps -- server side</title><link>http://blog.ziade.org/2011/12/14/mozilla-apps-server-side/</link><description>&lt;p&gt;Yesterday, we've launched the &lt;strong&gt;&lt;em&gt;Developer Preview&lt;/em&gt;&lt;/strong&gt; for Apps, you can
play with at &lt;a href="https://apps-preview.mozilla.org/"&gt;https://apps-preview.mozilla.org&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The server side is composed of many pieces, and while they are subject
to change since this is just a preview, I think it's quite interesting
to describe some of them already -- and maybe get more contributors in
the process, since everything is open source and contributors are
welcome. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here's an overview of the system -- we used: &lt;br /&gt;
-   &lt;strong&gt;Django&lt;/strong&gt; for the App MarketPlace
-   &lt;strong&gt;Cornice&lt;/strong&gt; for the Sync APIs
-   &lt;strong&gt;Node.js&lt;/strong&gt; for the Sauropod APIs
-   &lt;strong&gt;HBase&lt;/strong&gt; for the Sauropod DB&lt;/p&gt;
&lt;h3&gt;The App MarketPlace - Django&lt;/h3&gt;
&lt;p&gt;The App MarketPlace located at &lt;a href="https://apps-preview.mozilla.org"&gt;https://apps-preview.mozilla.org&lt;/a&gt; is
where you can upload your own Apps, or install some. There's a payment
process for non-free Apps, and you can see which one you have bought in
your profile. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The development is driven by the WebDev team, and is based on Django --
see &lt;a href="https://github.com/mozilla/zamboni/tree/master/apps/webapps"&gt;https://github.com/mozilla/zamboni/tree/master/apps/webapps&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I can't really describe this part, as I did not follow it closely. But
basically, the Market Place keeps track of your apps and payment
receipts, for other parts of the system to interact with. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;The Dashboard - HTML + JS&lt;/h3&gt;
&lt;p&gt;Once you start to install Open Web Applications, you are redirected to
a &lt;em&gt;Dashboard&lt;/em&gt; at &lt;a href="https://myapps.mozillalabs.com"&gt;https://myapps.mozillalabs.com&lt;/a&gt;. This Dashboard is
an HTML application that lists your installed Apps associated to your
Browser ID. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;What's pretty cool is that no matter where you've installed a given App
-- Firefox on your Desktop, your Phone, it will appear on this
dashboard, and synced across devices.This is done via a Sync service
called &lt;strong&gt;&lt;em&gt;AppSync&lt;/em&gt;&lt;/strong&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Code pointers for the Dashboard: &lt;br /&gt;
-   &lt;a href="https://github.com/mozilla/openwebapps/tree/develop/site"&gt;https://github.com/mozilla/openwebapps/tree/develop/site&lt;/a&gt;
-   &lt;a href=""&gt;https://github.com/mozilla/openwebapps/blob/develop/addons/jetpack/lib/sync.js&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;AppSync&lt;/h3&gt;
&lt;p&gt;AppSync is the part I worked on. Its design was mainly done by Ian
Bicking, who then worked on the client side implementation while I was
working on the server side one. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It's quite similar to what we did for Firefox Sync, except that: &lt;br /&gt;
-   AppSync supports BrowserID
-   The data is stored in Sauropod&lt;/p&gt;
&lt;p&gt;Securing this data is part of a much larger ongoing project called
&lt;a href="https://wiki.mozilla.org/Sauropod"&gt;Sauropod&lt;/a&gt;. The idea is that any database access has to be done with
credentials, and that Sauropod is in charge of controlling them and
dealing with the storage. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In the long term, depending how Sauropod evolves with respect to
encryption, and how Firefox Sync itself evolves with respect to Browser
ID and Sauropod access, we might merge both projects. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Or maybe Sauropod will provide APIs one day that are good enough for
direct clients interactions, turning AppSync into a simple proxy ? &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Time will tell ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Anyway, here's an overview below of the AppSync system we've set up for
this preview. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;We have the AppSync server itself, that's built using &lt;a href="http://packages.python.org/cornice/"&gt;Cornice&lt;/a&gt;. It
provides the synchronisation APIs described in this document:
&lt;a href="https://wiki.mozilla.org/Apps/Sync/Spec"&gt;https://wiki.mozilla.org/Apps/Sync/Spec&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Every time a client wants to write new data, we call the &lt;strong&gt;&lt;em&gt;Sauropod&lt;/em&gt;&lt;/strong&gt;
server which is a very simple GET/SET api built with Node.js. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The flow is: &lt;br /&gt;
-   AppSync server asks for a new Sauropod session, using Browser ID
    credentials
-   Sauropod verifies the Browser ID credentials then create a DB token
    into a session
-   AppSync uses this token until its not valid anymore
-   Sauropod calls in turn an HBase cluster to access the data
-   Every write is mirrored in a MySQL database in AppSync. This
    mirroring was set so we can turn off Sauropod if we need to, and
    still have a working system. This will eventually go away later.&lt;/p&gt;
&lt;p&gt;&lt;img alt="image" src="http://ziade.org/appsync.png" title="AppSync Cluster" /&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Find the code here: &lt;br /&gt;
-   Server &lt;a href="https://github.com/mozilla/appsync"&gt;https://github.com/mozilla/appsync&lt;/a&gt;
-   Client &lt;a href="https://github.com/mozilla/openwebapps/tree/develop/sync"&gt;https://github.com/mozilla/openwebapps/tree/develop/sync&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;What's Next&lt;/h3&gt;
&lt;p&gt;I am really excited by this project. There are a lot of people involved
in the Mozilla community, and seeing all the moving pieces assembled to
build an &lt;strong&gt;&lt;em&gt;Open&lt;/em&gt;&lt;/strong&gt; App environement is pretty cool. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;We're going to work in the upcoming months on consolidating the whole
system, making sure it scales well and correct the design as the
feedback comes back. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you want to get involved, you can install the preview, play with the
available Apps and even maybe write your own Apps for the Market Place,
or help us in the coding. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;We're hanging in #openwebapps on Mozilla's IRC &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;EDIT : &lt;a href="http://kix.in/2011/12/15/behind-the-mozilla-apps-developer-preview/"&gt;Anant wrote a very nice blog post on the topic&lt;/a&gt;&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;https:&lt;/span&gt;&lt;span class="sr"&gt;//gi&lt;/span&gt;&lt;span class="n"&gt;thub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="sr"&gt;/mozilla/o&lt;/span&gt;&lt;span class="n"&gt;penwebapps&lt;/span&gt;&lt;span class="sr"&gt;/blob/&lt;/span&gt;&lt;span class="n"&gt;develop&lt;/span&gt;&lt;span class="sr"&gt;/addons/&lt;/span&gt;&lt;span class="n"&gt;jetpack&lt;/span&gt;&lt;span class="sr"&gt;/lib/s&lt;/span&gt;&lt;span class="n"&gt;ync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;js&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Wed, 14 Dec 2011 14:43:00 +0100</pubDate><guid>http://blog.ziade.org/2011/12/14/mozilla-apps-server-side/</guid></item><item><title>QA script on web services</title><link>http://blog.ziade.org/2011/12/12/qa-script-on-web-services/</link><description>&lt;p&gt;The other task Alexis and I are going to work on this week, besides
Cornice, is a QA script for web services. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The goal is simple : check that a set of web services are HTTP
compliant. For example, does your application send the proper 406 error
when an unsupported Accept is asked by the client ? &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you document properly your web services, asking for an unsupported
Accept should not occur of course, but in most projects those protocol
details are often a bit vague. And someone that writes a client software
will inevitably make some assumptions based on his HTTP knowledge on how
the application is supposed to behave. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Richard Newman came up with a fair list of tests we could run against a
web app already, and we've started to summarise and add more of them
here: &lt;a href="https://wiki.mozilla.org/Services/WALint"&gt;https://wiki.mozilla.org/Services/WALint&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The idea of the script is to print out a report of errors and warnings
it found on a web app, exactly like a lint tool would do on some code.
That's what I called it &lt;strong&gt;&lt;em&gt;WALint&lt;/em&gt;&lt;/strong&gt; (Web App Lint). Alexis doesn't like
the name but he did not find a better name yet ;) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The way it works is that you describe in a configuration file the URIs
of your web services, then WALint runs tests against them, using what we
called &lt;em&gt;controllers&lt;/em&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Each controller is in charge of trying out something on the web app,
using a small HTTP test client (using WebTest), given a path and a
method. WALint will provide built-in controllers and will be extensible.
We will have Mozilla-specific tests, like the maximum size of a query
string, or the maximum size of the request, since those limits are
specific to the used stack. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;We got bitten by this is the past in Sync - one web service failed to
work properly because the client was building a super long query string,
that was truncated along the way in our stack. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Our final goal with this tool is to be able to add in Jenkins these
controls for all our web apps, and catch more problems before they occur
in production. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Since it's also useful while you build your code, WALint will have a
UnitTest integration, so you can run it as a functional test from within
your test suite -- In that case, it will run directly against the code.&lt;/p&gt;
&lt;p&gt;As usual, feedback &amp;amp; contributions are welcome. The code is being built
here: &lt;a href="https://github.com/mozilla-services/walint"&gt;https://github.com/mozilla-services/walint&lt;/a&gt;&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Mon, 12 Dec 2011 11:00:00 +0100</pubDate><guid>http://blog.ziade.org/2011/12/12/qa-script-on-web-services/</guid></item><item><title>Cornice - Validators and 406s brought to you by Alexis</title><link>http://blog.ziade.org/2011/12/07/cornice-validators-and-406s-brought-to-you-by-alexis/</link><description>&lt;p&gt;&lt;a href="http://notmyidea.org/"&gt;Alexis&lt;/a&gt; started to work with me this week from my house and we're
having good times. More geeks from Afpy are joining us this week-end for
wine-drinking and working on a Python technical writing project. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;While working myself on &lt;a href="https://wiki.mozilla.org/Apps/Sync/Spec"&gt;AppSync&lt;/a&gt; with the fine folks from Mozilla
Labs, I am mentoring Alexis to get him up to speed on our projects &amp;amp;
standards at Mozilla Services.[&lt;img alt="image" src="http://tarekziade.files.wordpress.com/2011/12/alexis1.jpg?w=1024" /&gt;][] &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href="http://packages.python.org/cornice/"&gt;Cornice&lt;/a&gt;, is a perfect match for him to start working -- it's
isolated enough so he can have fun and produce features that are
immediately useful, yet learn our standards &amp;amp; processes. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So we worked on its design with the help of its principal user, Ben
Bangert, and I asked Alexis to blog about the work &amp;amp; doc we produced
since Monday. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Read up =&gt; &lt;a href="http://blog.notmyidea.org/introducing-cornice.html"&gt;&lt;strong&gt;Introducing Cornice&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;alexis&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;[&lt;img alt="image" src="http://tarekziade.files.wordpress.com/2011/12/alexis1.jpg?w=1024" /&gt;]: http://tarekziade.files.wordpress.com/2011/12/alexis1.jpg&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Wed, 07 Dec 2011 19:09:00 +0100</pubDate><guid>http://blog.ziade.org/2011/12/07/cornice-validators-and-406s-brought-to-you-by-alexis/</guid></item><item><title>Syntastic now with Flake8</title><link>http://blog.ziade.org/2011/12/04/syntastic-now-with-flake8/</link><description>&lt;p&gt;If you're a VIM user and a Python coder, that could interest you. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.vim.org/scripts/script.php?script_id=2736"&gt;Syntastic&lt;/a&gt;, a syntax checker for VIM now includes &lt;a href="http://pypi.python.org/pypi/flake8"&gt;Flake8&lt;/a&gt; as a
checker for Python code. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Syntastic can be used to check for syntax errors in files opened in
VIM. It has a pretty extensive list of supported languages. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For Python it had PyFlakes, and since the latest version, it has my
small glue script "Flake8", that reunites in a single stream: pep8,
PyFlakes and a McCabe checker. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For Flake8 the next step is to support Python 3 -- as soon as I find
some time, or a volunteer ;) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Thanks to &lt;em&gt;Sylvain Soliman&lt;/em&gt; (and Clayton Parker) !&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sun, 04 Dec 2011 22:36:00 +0100</pubDate><guid>http://blog.ziade.org/2011/12/04/syntastic-now-with-flake8/</guid></item><item><title>Cornice -- web services with Pyramid</title><link>http://blog.ziade.org/2011/11/29/cornice-web-services-with-pyramid/</link><description>&lt;p&gt;Since we've &lt;a href="https://tarekziade.wordpress.com/2011/10/21/building-web-services-with-pyramid/"&gt;initially started Cornice at Services&lt;/a&gt;, we had more
discussion about how we could make it easier for developers to validate
an incoming request. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Our goal is : &lt;br /&gt;
-   to be able to validate a request and if needed, to convert it to
    specific data structures
-   to complete the documentation of our web services in Sphinx with
    those validation steps&lt;/p&gt;
&lt;p&gt;Here's a concrete example: A &lt;em&gt;PUT&lt;/em&gt; request used to create a user in a
database should come as a JSON object in the request body, which content
would be validated and turned into a Person object, then sent to a SQL
backend. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The web service needs in that case to reject any malformed request with
a 400, and we'd also want to document this constraint in the web service
documentation. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;There are some existing tools in the validation arena, like
&lt;a href="http://www.formencode.org/en/latest/index.html"&gt;FormEncode&lt;/a&gt; or &lt;a href="http://docs.pylonsproject.org/projects/colander/en/latest/"&gt;Colander&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;FormEncode and Colander both provide data validation via schemas, and
FormEncode offers HTMLform generation. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;When you need to validate an incoming request in your web service,
those tools can fit your needs or simply be overkill. So, we wanted to
integrate a validation step in Cornice without forcing the usage of a
specific validation tool. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;A simple callable&lt;/h3&gt;
&lt;p&gt;What we did is added a &lt;em&gt;validator&lt;/em&gt; option that needs to point to a
callable. The callable receives the request object and has to return an
HTTP error code followed by a reason in case the request does not
comply. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here's an example, a &lt;strong&gt;&lt;em&gt;GET /foo&lt;/em&gt;&lt;/strong&gt; that will return a 402 if the&lt;strong&gt;&lt;em&gt;
X-Paid&lt;/em&gt;&lt;/strong&gt; header is missing : &lt;br /&gt;
       from cornice import Service&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;    &lt;span class="n"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;foo&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;/foo&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;has_paid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;The request must have an X-Paid header containing a token.&lt;/span&gt;

&lt;span class="s"&gt;        This header proves the user has paid&lt;/span&gt;

&lt;span class="s"&gt;        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;X-Paid&amp;#39;&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers:&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;402&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;You need to pay !&amp;#39;&lt;/span&gt;

    &lt;span class="nv"&gt;@foo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;has_paid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;get_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;Returns the value.&lt;/span&gt;

&lt;span class="s"&gt;        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Hello&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;From there, Cornice will call &lt;em&gt;has_paid&lt;/em&gt; prior to &lt;em&gt;get_value&lt;/em&gt;.
Cornice is also able to build the documentation of the web service, by
merging the docstrings of &lt;em&gt;get_value&lt;/em&gt; and &lt;em&gt;has_paid&lt;/em&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Sphinx integration&lt;/h3&gt;
&lt;p&gt;We provide a Sphinx extension for documenting a Cornice based project.
You can inject in your Sphinx document the web service description, via
a &lt;strong&gt;&lt;em&gt;service&lt;/em&gt;&lt;/strong&gt; directive. &lt;br /&gt;
   .. services::&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;   &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;package&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;demoapp&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;In this example, the directive looks for all Cornice definitions in the
&lt;strong&gt;&lt;em&gt;demoapp&lt;/em&gt;&lt;/strong&gt; package by scanning it, and injects their documentation in
the Sphinx document. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The service directive provides a few options. For instance the
&lt;strong&gt;&lt;em&gt;name&lt;/em&gt;&lt;/strong&gt; option will let you inject the documentation of one specific
web service. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Colander integration&lt;/h3&gt;
&lt;p&gt;I said earlier simple callables where good enough for simple validation
cases. Let's take a more complex example. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Let's say, you have a &lt;strong&gt;&lt;em&gt;Person&lt;/em&gt;&lt;/strong&gt; schema in Colander, that defines a
person's attributes -- &lt;a href="http://docs.pylonsproject.org/projects/colander/en/latest/basics.html#defining-a-schema-imperatively"&gt;See Colander docs for details&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;And you want to provide a &lt;strong&gt;&lt;em&gt;PUT&lt;/em&gt;&lt;/strong&gt; Web Service to create a person,
where the request body is the person data serialized in JSON. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here's the full Cornice definition &lt;br /&gt;
      from cornice import Service&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;   &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;cornice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;schemas&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;save_converted&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get_converted&lt;/span&gt;

   &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;check_person&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

      &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;The request body must be a JSON object describing the Person&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

      &lt;span class="n"&gt;try:&lt;/span&gt;

          &lt;span class="n"&gt;person&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="n"&gt;except&lt;/span&gt; &lt;span class="n"&gt;ValueError:&lt;/span&gt;

          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Bad Json data!&amp;#39;&lt;/span&gt;

      &lt;span class="n"&gt;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

      &lt;span class="n"&gt;try:&lt;/span&gt;

          &lt;span class="n"&gt;deserialized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deserialized&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="n"&gt;except&lt;/span&gt; &lt;span class="n"&gt;InvalidError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e:&lt;/span&gt;

           &lt;span class="c1"&gt;# the struct is invalid&lt;/span&gt;

           &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;

      &lt;span class="n"&gt;save_converted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;person&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;deserialized&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;person&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;/person/{id}&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

   &lt;span class="err"&gt; &lt;/span&gt;&lt;span class="nv"&gt;@service&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;check_person&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;data_posted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="n"&gt;person&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_converted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;person&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

       &lt;span class="err"&gt; &lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;In this example, the validator checks that the request body is valid
Json, then pass the unserialized mapping to Colander to check that it's
a valid Person record. Last, it uses the &lt;strong&gt;&lt;em&gt;save_converted&lt;/em&gt;&lt;/strong&gt; function
we provide, to save the Person object into the request. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The web service then can pick it up with the &lt;strong&gt;&lt;em&gt;get_converted&lt;/em&gt;&lt;/strong&gt;
function. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Next Steps&lt;/h3&gt;
&lt;p&gt;Our next steps is to build a library of useful built-in validators.
Things like: &lt;br /&gt;
-   is the request body is JSON ?
-   do we have param X, if yes is it an integer ?
-   any reusable validator we'll think about&lt;/p&gt;
&lt;p&gt;The final goals here are : &lt;br /&gt;
1.  have our web services code written in two phases: the validation
    phase, and the code itself. Because they are a lot of reusable bits
    in that first phase.
2.  reuse as much as possible docstrings to document our web services,
    to avoid the &lt;em&gt;doc-is-not-up-to-date-anymore&lt;/em&gt; plague&lt;/p&gt;
&lt;p&gt;You can follow the development of Cornice here:
&lt;a href="https://github.com/mozilla-services/cornice"&gt;https://github.com/mozilla-services/cornice&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;And even participate by joining our Mailing List:
&lt;a href="https://mail.mozilla.org/listinfo/services-dev"&gt;https://mail.mozilla.org/listinfo/services-dev&lt;/a&gt;&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 29 Nov 2011 11:18:00 +0100</pubDate><guid>http://blog.ziade.org/2011/11/29/cornice-web-services-with-pyramid/</guid></item><item><title>A French Python Subreddit</title><link>http://blog.ziade.org/2011/11/03/a-french-python-subreddit/</link><description>&lt;p&gt;Are you a French speaking Python programmer ? Are you wishing sometimes
you could debate on Python in la langue de Molière ? Are you tired of
the current Economic crisis and want to find a peaceful place to hang in
? &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Then, you should join us in our brand new French Speaking Python
Subreddit and &lt;a href="https://www.youtube.com/watch?v=YV0LGMGuLN0"&gt;fetch some Vaches&lt;/a&gt; with us ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href="http://reddit.afpy.org"&gt;http://reddit.afpy.org&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 03 Nov 2011 11:49:00 +0100</pubDate><guid>http://blog.ziade.org/2011/11/03/a-french-python-subreddit/</guid></item><item><title>ldappool: a simple pool for python-ldap</title><link>http://blog.ziade.org/2011/10/28/ldappool-a-simple-pool-for-python-ldap/</link><description>&lt;p&gt;Over the past few months, we've done quite some tweaking at Mozilla
Service to make our Python platform work well for the Sync platform. One
bottleneck we have is the number of open connections we can have on a
given LDAP server. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;On very high load, we needed to make sure we could control the flow of
open connections, and when possible reduce the number of connectors as
much as possible, by recycling/reusing them. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href="http://python-ldap.org/"&gt;python-ldap&lt;/a&gt; does not come with a pool, so we've built our own with
the Ops team. &lt;br /&gt;
The pool keeps LDAP connectors alive and let you reuse them,
drastically reducing the time spent to initiate a ldap connection. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The pool has useful features like: &lt;br /&gt;
-   transparent reconnection on server restarts, or timeouts/downtimes
-   configurable pool size and connectors timeouts
-   a context manager to simplify acquiring and releasing a connector
-   configurable max lifetime for connectors&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;max lifetime&lt;/em&gt; feature is useful when you add a new server in a
pool of ldap servers: if the pool is already filled with connectors, a
server you will introduce will never have a chance to get a connector. &lt;br /&gt;
We avoid this problem by introducing a max lifetime for our connectors:
the pool gets fresh connectors after a bit of time and can access all
our resources. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The pool is used via a context manager, and is recycled when you leave
the with block: &lt;br /&gt;
   from ldappool import ConnectionManager&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;cm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ConnectionManager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;ldap://localhost&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;cm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;uid=user,ou=logins,dc=mozilla&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;password&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;as&lt;/span&gt; &lt;span class="n"&gt;conn:&lt;/span&gt;

      &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="n"&gt;something&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;I have extracted it from our code based and released it on its own at
pypi: &lt;a href="http://pypi.python.org/pypi/ldappool/"&gt;http://pypi.python.org/pypi/ldappool/&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Let us know if you try it !&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 28 Oct 2011 19:16:00 +0200</pubDate><guid>http://blog.ziade.org/2011/10/28/ldappool-a-simple-pool-for-python-ldap/</guid></item><item><title>Building Web Services with Pyramid</title><link>http://blog.ziade.org/2011/10/21/building-web-services-with-pyramid/</link><description>&lt;h3&gt;Using Pyramid for now on&lt;/h3&gt;
&lt;p&gt;Last year, when we started to write servers apps in Python at Mozilla
Services, we've built a light micro-framework on the top of WebOb and
Routes. That made a lot of sense back then because all our applications
were pure JSON web services --except the &lt;em&gt;reCaptcha&lt;/em&gt; view we display
when you register in Sync-- &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The framework just needed to route a request to a function and let us
do our work in &lt;strong&gt;&lt;em&gt;pure Python&lt;/em&gt;&lt;/strong&gt; from there. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Given the nature of our apps, we did not pick an async framework for
the sake of code simplicity. And with Gevent, you still can boost a
synchronous app that's waiting for some I/O, &lt;a href="http://www.gevent.org/intro.html#monkey-patching"&gt;by making the socket layer
cooperative&lt;/a&gt;. IOW, making your synchronous app do some work
asynchronously transparently. And that works: The Sync server uses this
feature for SQL and LDAP queries, and is able to handle much more
concurrent requests that way. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;When we made the choice of WebOb/Routes, Pyramid was on my radar and
seemed like a good option too, but was still a bit new, so we just used
WebOb, knowing that a move to Pyramid would be quite easy since it's
based on it too. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Today, Pyramid is quite spread and mature, and for developing Sagrada
components, it makes a lot of sense to adopt it. Having Ben Bangert and
Rob Miller in the team also made that choice natural : they're involved
in the project so they can sneak in our crazy patches ;) &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Cornice - web service builder&lt;/h3&gt;
&lt;p&gt;Building web services for Sagrada, or for any project as a matter of
fact, should be as simple as possible for developers. In all the
frameworks I've used in the past, it was often requiring good chunks of
boiler-plate code, or I had to use some tools that were making too many
choices for me. For example, we want to build REST APIs, but sometimes
our APIs are not strictly REST, just REST-ish I would say :) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In the past week, we've worked on a little extension called Cornice,
that tries to simplify as much as possible the usage of Pyramid to write
Web Services the way we like, with the experience we had writing Sync,
Account portal, Easy Setup, and all the other services that are
currently running in prod. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The result is a simple class that can be used to define a service on
the server, then you can declare your APIs. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here's an extract taken from &lt;a href="https://github.com/mozilla-services/demoapp/blob/master/demoapp/views.py"&gt;demoapp&lt;/a&gt;, &lt;br /&gt;
   from collections import defaultdict&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;cornice&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Service&lt;/span&gt;

&lt;span class="n"&gt;user_info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;users&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;/{username}/info&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;_USERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defaultdict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;@user_info&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;get_info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;Returns the public information about a **user**.&lt;/span&gt;

&lt;span class="s"&gt;    If the user does not exists, returns an empty dataset.&lt;/span&gt;

&lt;span class="s"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;matchdict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;username&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_USERS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nv"&gt;@user_info&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;set_info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;Set the public information for a **user**.&lt;/span&gt;

&lt;span class="s"&gt;    You have to be that user, and *authenticated*.&lt;/span&gt;

&lt;span class="s"&gt;    Returns *True* or *False*.&lt;/span&gt;

&lt;span class="s"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;matchdict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;username&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;_USERS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json_body&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;success&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;True&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Please don't look at what the code does, it's crappy. The _USERS stuff
should be in a Pyramid registry. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But look at how I've defined my service: a &lt;strong&gt;&lt;em&gt;Service&lt;/em&gt;&lt;/strong&gt; instance for a
given path on our server, followed by two functions, one for the GET,
and one for the POST. By default, the APIs are returning JSON, but you
can change every option Pyramid offers when you declare a view. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;And once this is done, I also get for free a documentation page at&lt;strong&gt;&lt;em&gt;
/__apidocs__&lt;/em&gt;&lt;/strong&gt; by grabbing the docstrings and also looking at the
various Service instance that were declared. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;[&lt;img alt="image" src="http://ziade.org/cornice-apidocs.png" title="apidocs in action" /&gt;][] &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Cornice is just syntaxic sugar, but we might add a few extra features
late, maybe a fine-grained description of the params of each web
services, so we could publish a manifest that could be introspected. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But it's good enough for people to start building Web Services for
Sagrada in a standard, easy-to-read, fashion. They just need to call a
&lt;strong&gt;&lt;em&gt;config.include('cornice')&lt;/em&gt;&lt;/strong&gt; in their Pyramid app, and use the
&lt;strong&gt;&lt;em&gt;Service&lt;/em&gt;&lt;/strong&gt; class.&lt;/p&gt;
&lt;p&gt;[&lt;img alt="image" src="http://ziade.org/cornice-apidocs.png" title="apidocs in action" /&gt;]: http://ziade.org/cornice-apidocs.png&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 21 Oct 2011 16:14:00 +0200</pubDate><guid>http://blog.ziade.org/2011/10/21/building-web-services-with-pyramid/</guid></item><item><title>Mozillians, win a free pass for Pycon US</title><link>http://blog.ziade.org/2011/10/13/mozillians-win-a-free-pass-for-pycon-us/</link><description>&lt;p&gt;The &lt;a href="http://us.pycon.org/2012/"&gt;10th Pycon US conference&lt;/a&gt; will happen next March in Santa Clara,
California. This is the biggest Pythoneers gathering in the world. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you've never been to a Pycon and you like Python, you should
consider coming -- It's a pretty intense week during which you can meet
all the people that are behind the frameworks, libs or event the
language itself. Some Mozillians will be there, no doubt. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It's an exciting time at Mozilla right now - in the WebDev team and the
Services team for example, because we have a lot of folks who are very
active in the Python community. Mozilla itself is active in the
community and people are interested in what we build. They use our open
source Python modules and we use theirs. And we'd love to have more of
this. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Since Mozilla is a Gold sponsor this year, we get 5 free registrations
to the conference, and we would like to give them away to some members
of the Mozilla community. The pass does not cover the hotel and the
travelling, but a registration costs from $200 to $450 (early bird), so
if your budget is tight, it's nice to get a free pass ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I thought about picking people in &lt;a href="http://mozillians.org/"&gt;http://mozillians.org/&lt;/a&gt; with a
silly script, but I think it could be more interesting to take this
opportunity to find out what people are doing with Python in the Mozilla
project. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So here's the deal -- if you are or have used Python to help out the
Mozilla project, blog about it. Explain your project, how you've used
Python in it. It can be on any topic, anything, really. Maybe you're
more of a JS guy but you're using Python scripts for some specific
tasks, or maybe you're working everyday with Django or Flask, etc. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Once the blog is published, send me an e-mail (&lt;a href="mailto:tarek@ziade.org"&gt;tarek@ziade.org&lt;/a&gt;)
with a "[pycon+mozilla]" header, and just the link in the body, before
~~December the 1st~~. February the 1st. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I will pick the five stories I found the most interesting and announce
the winners shortly after.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 13 Oct 2011 15:13:00 +0200</pubDate><guid>http://blog.ziade.org/2011/10/13/mozillians-win-a-free-pass-for-pycon-us/</guid></item><item><title>OSDC Slides - High performance Python web apps</title><link>http://blog.ziade.org/2011/10/04/osdc-slides-high-performance-python-web-apps/</link><description>&lt;p&gt;We gave a talk last month with Benoit in Paris about web servers. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The talk consisted of a first part were we've explained how a web
server works, and we got into details like what's a backlog etc. and
explained how the technology evolved from a single CGI process to modern
setups based on event loops or such things. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;We've also presented the stack I've set up for the Firefox Sync server,
and gave an overview about stress tests tools. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;You can grab the PDF translated in English here: &lt;a href="http://ziade.org/ha_osdcfr_20110923.pdf"&gt;High Availability
Server Apps - benoitc + tarek&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Or watch them on SlideShare here: &lt;a href="http://www.slideshare.net/tarek.ziade/high-availability-server-apps"&gt;High Availability Server Apps&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;The bottom line is : use Nginx, Gunicorn and Gevent&lt;/em&gt;&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you wonder why we have socks and shoes on our slides, it's because
"socket" sounds a lot like "chaussette" (means socks) in French, so it's
a (stupid but efficient) joke :)&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;High Availability Server Apps&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 04 Oct 2011 16:15:00 +0200</pubDate><guid>http://blog.ziade.org/2011/10/04/osdc-slides-high-performance-python-web-apps/</guid></item><item><title>The French Python User Group (Afpy) is 8 y.o.</title><link>http://blog.ziade.org/2011/10/02/the-french-python-user-group-afpy-is-8-yo/</link><description>&lt;p&gt;Wow. I've realized this today. We've founded that user group in 2003,
when I've proposed a Pythoneers meeting in Paris through my Python/Plone
forum. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;We've created &lt;a href="http://afpy.org/"&gt;Afpy.org&lt;/a&gt; after that, the non-profit organization, and
we had many meetings since then, including various Pycon France. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Many people I've met through the Afpy are now people that I consider
friends for life, even if I don't see them much these days. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;And Python still rocks 8 years after we've started all of this, getting
bigger and bigger every year, thanks to the awesome leadership of Guido
and all Python core dev. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The bottom line is: if you like Python and don't have a user group in
your area, create one.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sun, 02 Oct 2011 20:55:00 +0200</pubDate><guid>http://blog.ziade.org/2011/10/02/the-french-python-user-group-afpy-is-8-yo/</guid></item><item><title>Mozilla sponsors Pycon US</title><link>http://blog.ziade.org/2011/09/30/mozilla-sponsors-pycon-us/</link><description>&lt;p&gt;Mozilla is now officially listed as a &lt;a href="http://us.pycon.org/2012/sponsors/"&gt;Pycon US sponsor&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This is great news for Python and for the Mozilla Community: more and
more projects in Mozilla are using Python. It's the mainstream language
for all the work we're doing in the &lt;a href="https://blog.mozilla.com/services/"&gt;Services team&lt;/a&gt; on server-side,
and numerous projects like &lt;a href="http://blog.mozilla.com/sumo/"&gt;SUMO&lt;/a&gt; or &lt;a href="http://blog.mozilla.com/webdev/category/amo/"&gt;AMO&lt;/a&gt; are powered by Python. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So, if you are involved in the Mozilla community and in Python, you
should consider &lt;a href="http://us.pycon.org/2012/cfp/"&gt;submitting a talk or a tutorial&lt;/a&gt;. Or just attend to
the conference in &lt;a href="http://us.pycon.org/2012/venue/"&gt;Santa Clara next March&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Pycons are always &lt;a href="http://mike.pirnat.com/2011/09/29/memories-of-pycon/"&gt;a lot of fun&lt;/a&gt;.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 30 Sep 2011 10:08:00 +0200</pubDate><guid>http://blog.ziade.org/2011/09/30/mozilla-sponsors-pycon-us/</guid></item><item><title>PyPI : CDN vs Mirrors</title><link>http://blog.ziade.org/2011/09/29/pypi-cdn-vs-mirrors/</link><description>&lt;p&gt;We had a few discussions in the last days about what would be a good way
to make PyPI more reliable. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It think there were a bit of confusion about the mirroring protocol
(PEP 381) and its goals, versus the reliability of the current PyPI main
server. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Some people were basically saying (I am paraphrashing) &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt; Just move PyPI to a CDN and be done with it, this mirroring thing
is too complicated.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Well, ok. We could set up a CDN for our PyPI files and have all our
archives at Amazon or elsewhere. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But since the mirroring protocol is implemented and available on
server-side (We have &lt;a href="http://pypi.python.org/mirrors"&gt;5 active mirrors&lt;/a&gt;), and since Pip already
supports switching to a mirror, the shortest path to a better PyPI is
simply to : &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;create a new mirror in a &amp;lt;put the name of the best provider&gt; CDN
and register it as a mirror at PyPI&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And if it's so better, so reliable and fast, maybe we'll move it up in
the mirrors list, as the first one so all clients should pick it by
default. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;And the day &amp;lt;put the name of the best provider&gt; is down. (yeah it
happens, remember EC2 a few months ago), you will all be thankful that
we have other mirrors and a protocol to switch over them ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So, if you think a CDN is the magic solution, go ahead. Grab
&lt;a href="http://pypi.python.org/pypi/pep381client"&gt;pep381client&lt;/a&gt;, set up your monster infrastructure, and let me know so
I add it in the list or mirrors. And maybe we will never ever call
another server again. Or maybe not. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For further info, here is a detailed summary of PyPI status we've built
with Noah: &lt;a href="http://wiki.python.org/moin/BetterPyPI"&gt;http://wiki.python.org/moin/BetterPyPI&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Related: I am going to submit a tutorial on how to work with PyPI,
mirrors, private packages etc., to show how I do at Mozilla&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 29 Sep 2011 18:30:00 +0200</pubDate><guid>http://blog.ziade.org/2011/09/29/pypi-cdn-vs-mirrors/</guid></item><item><title>Sagrada - Creation and Hosting of Web Services</title><link>http://blog.ziade.org/2011/09/22/sagrada-creation-and-hosting-of-web-services/</link><description>&lt;h3&gt;tl;dr&lt;/h3&gt;
&lt;p&gt;I am building for &lt;a href="https://wiki.mozilla.org/Services/Roadmaps/Server/Sequence"&gt;Sagrada&lt;/a&gt; a web application that can be used to
create and deploy web services through the web with a few Python
functions. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href="http://ziade.org/redbarrel3.mpeg"&gt;See the prototype demo quick screencast&lt;/a&gt; -- If you can't view it in
your browser, download it and use VLC -- The music is &lt;a href="http://ccmixter.org/files/mcjackinthebox/33612"&gt;MC Jack IN the
Box CC-AN 3.0&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;EDIT&lt;/em&gt;&lt;/strong&gt; Here's a better demo, with Ace integration, where you can
edit/create Python files in the browser --
&lt;a href="http://ziade.org/redbarrel4.mpeg"&gt;http://ziade.org/redbarrel4.mpeg&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Now read below for the long story :) &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;The Sagrada Project&lt;/h3&gt;
&lt;p&gt;We had a all-hands last week in San Jose, which gathered +600
Mozillians (yes, we're growing fast). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;My team has started to brainstorm about Sagrada, the big project we're
starting, and I've started to think about a few topics. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;One long term goal of Sagrada is to let developers deploy on our
servers their own web services. It's quite hard to put a strict
definition for the term "web service", but the term is usually used
these days to describe a server-side application that can be queried via
the HTTP protocol, receiving and sending back Json objects. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So, the intent is to provide a &lt;strong&gt;Service Container&lt;/strong&gt; for developers
that need a server side for their applications. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The server side of Firefox Sync is a good example: It's a set of web
services Firefox calls to power Sync. &lt;a href="http://docs.services.mozilla.com/storage/apis-1.1.html"&gt;See the API definitions here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For Sagrada the specific question I am interested to solve is : &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt; What would be the easiest way to write and deploy web services into
our infrastructure ?&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;To start off, here are a few assumptions I am making about what
developers would probably expect from this kind of service -- &lt;em&gt;but those
are my own assumptions, and will change with the feedback I'll get in
the process&lt;/em&gt; : &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Building and deploying a web service should be easy to do whether
you are a Python programmer or a JavaScript programmer.&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;While Python is our main target, being able to write JavaScript on
server side should also be a goal -- &lt;em&gt;And if the tool is extensible
enough, why not other languages in the future ?&lt;/em&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Moreover, most steps when building web services are not specific to a
language. For instance, describing what HTTP method should be used,
what's the web service URL, what kind of request body is expected, etc,
is usually done in a specification document. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For example, when I've built the Easy Setup server for Sync, I've
written this specification document :
&lt;a href="http://docs.services.mozilla.com/keyexchange/apis.html"&gt;http://docs.services.mozilla.com/keyexchange/apis.html&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It contains a description of each web service used by Firefox when you
add a new device to your Sync account. The only thing that is not
expressed in this document is the just the piece of code that does the
job. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So, what if this document could be used directly by the server to run
the application ? The only thing that would miss is a few functions. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The bottom line is that web services can be described in a &lt;a href="https://secure.wikimedia.org/wikipedia/en/wiki/Domain-specific_language"&gt;Domain
Specific Language&lt;/a&gt; (DSL) that can be used to generate the
documentation automatically (and it stays accurate and up to date) but
also to set up in the server things we usually do on the code side: the
request dispatching. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In that case, the portion of code to write is reduced to building the
response itself. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Even if we just support Python in a v1, if building a web service boils
down to writing a specification document, and a few Python functions to
build responses, I think that lowers the barrier enough for most
developers. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;And if we hide the DSL behind a nice user interface developers can use
to build their apps, that's even better. That's what's happening in the
screencast I've recorded. The forms generate portions of DSL. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. The tool should be framework-agnostic if possible.&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;While tools like &lt;a href="http://nodejs.org/"&gt;Node.js&lt;/a&gt; or &lt;a href="https://www.pylonsproject.org/"&gt;Pyramid&lt;/a&gt; or &lt;em&gt;&amp;lt;put your favorite
framework here&gt;&lt;/em&gt; provide great features to write web apps, web services
people will build will be running in specific environments, where we
will want to set up and control our own stack. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In other words, we'll want to isolate as much as possible the code
written by developers. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Ideally, we'd just pass a request to a function and ask for a response.
Python for instance has a CGI-like standard called &lt;a href="http://www.wsgi.org/en/latest/index.html"&gt;WSGI&lt;/a&gt;, where the
request is described in a mapping and the response is a sequence of
string + a mapping of headers. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So telling developers: &lt;em&gt;"Hey, your function will receive a WSGI
request, send me back a WSGI response"&lt;/em&gt;, sounds like a good basis. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I am pretty sure we can do the same in Javascript. And if we don't have
such standard in Javascript, let's create it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. The tool should provide a Web UI, and CLI and a standalone
server.&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Web services should not be locked in our servers, developer should be
able to edit them through the web, or via the console even if it's
remote. They should be able to upload or download their apps in our
environment, like what they would do with &lt;a href="https://code.google.com/appengine/"&gt;Google App Engine&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But they should also be able to create, run their apps on their own
environment e.g. have a independent web server that can run their
services. Not a toy server used for development only, but something they
can really deploy in production. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For the remote console, &lt;a href="https://twitter.com/#!/benoitc"&gt;Benoit&lt;/a&gt; was telling me this morning: why not
&lt;a href="http://ipython.org/"&gt;iPython&lt;/a&gt; ? I dig this: editing web services through iPython would
rock. If you don't know about this tool check it out it's amazing. It
provides among other things, a way to build an interactive shell for a
distant app. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;A web service DSL&lt;/h3&gt;
&lt;p&gt;Back to our DSL. When you write a web service, it's always the same
story no matter what framework you're using, you're basically doing
these steps (I am over-simplying for now) &lt;br /&gt;
1.  Define a route for your service on the server
2.  Build the response
3.  Send back the response&lt;/p&gt;
&lt;p&gt;These steps can be described in a simple DSL. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here's a basic example: &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="n"&gt;hello_world&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;

 &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Simplest application: Hello World!&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

 &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="n"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

 &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

 &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="n"&gt;python:somemodule&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hello&lt;/span&gt;

 &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;/blockquote&gt;
&lt;dl&gt;
&lt;dt&gt;With a function &lt;em&gt;hello&lt;/em&gt; located in &lt;em&gt;somemodule&lt;/em&gt; that can look like this&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;def hello(request):&lt;/p&gt;
&lt;p&gt;return 'Hello World'&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;The application in that case is composed of &lt;br /&gt;
-   a DSL file
-   a Python file with a single function&lt;/p&gt;
&lt;p&gt;It's easy from there with the proper DSL parser to: &lt;br /&gt;
-   deploy those two files in our infrastructure
-   run the app
-   provide auto-generated documentation for the service&lt;/p&gt;
&lt;h3&gt;Architecture&lt;/h3&gt;
&lt;p&gt;The prototype I've written for the demo does the following: &lt;br /&gt;
-   The tool is a web application that provides forms to create Service
    Containers
-   Each Service Container has a unique root on the server
-   In each container you can add web services.
-   For each container, the DSL is built on the fly, then the
    corresponding AST is kept in memory. The web UI allow users to
    modify it on the fly
-   When a request comes in, it's rooted to the right Service Container
    depending on the beginning of the path, then the AST is used to find
    out what function(s) should be used. The function is then executed
    in a sandbox.&lt;/p&gt;
&lt;p&gt;The prototype also provides a command-line tool to start a server by
loading an arbitrary DSL file. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;The DSL Parser&lt;/h3&gt;
&lt;p&gt;I will not go in to great details here, you can look at my previous
posts mentioning &lt;strong&gt;RedBarrel&lt;/strong&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The current DSL is implemented with &lt;a href="http://www.dabeaz.com/ply/"&gt;PLY&lt;/a&gt; and can be found &lt;a href="https://bitbucket.org/tarek/redbarrel/src/9f466fd5c2eb/redbarrel/dsl/parser.py"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Sandboxing the code&lt;/h3&gt;
&lt;p&gt;One thing we want to do when a developer uploads some code that is
potentially going to be executed, is to sandbox it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This is not really for security reasons, because we'll still need to
protect our users by setting up VMs. This is mostly to &lt;br /&gt;
pre-configure what the user is allowed to do in the code and provide
some useful feedback when he's doing things &lt;br /&gt;
we did not allow, like writing to the filesystem or using sockets &lt;em&gt;-- I
am not saying we will allow or disallow these&lt;/em&gt; &lt;br /&gt;
&lt;em&gt; particular ones, I don't know yet.&lt;/em&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In the current prototype, I've used &lt;a href="http://pypi.python.org/pypi/pysandbox/"&gt;pysandbox&lt;/a&gt; from Victor Stinner .
It's used to load and execute the uploaded code. &lt;br /&gt;
see &lt;a href="https://bitbucket.org/tarek/redbarrel/src/9f466fd5c2eb/redbarrel/libraries.py"&gt;how I use it&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;What's Next&lt;/h3&gt;
&lt;p&gt;Right now the web interface to build an application is simplistic
compared to what the DSL can do. For instance you could chain several
functions to perform pre- and post- processing for a request. So some
functions can be reused in several services, like authentication. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Also, we want to provide our own Mozilla libs, like a way to
authenticate against our own user database, or use a key-value storage.
Basically, all the libraries we're currently building for Sagrada. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It's unclear at this point what imports we will allow in the scripts,
and how we will publish our own libraries people will be able to use. I
intend to clarify these in the upcoming days, and enhance the prototype
to allow it to do more things. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Also, I'd like to write the Easy Setup server using this tool. I'll
also try to organize a coding/brainstorming sprint since I have 5/6
people that worked with me on these topics to hack on this. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you are interested or have some feedback, please comment !&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 22 Sep 2011 16:01:00 +0200</pubDate><guid>http://blog.ziade.org/2011/09/22/sagrada-creation-and-hosting-of-web-services/</guid></item><item><title>Mozilla Services is hiring</title><link>http://blog.ziade.org/2011/09/19/mozilla-services-is-hiring/</link><description>&lt;p&gt;My team at Mozilla (Services) is looking for talented people to work on
Python-based server apps, such as Firefox Sync. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Currently, we're mainly writing web services. Firefox Sync and most
projects we have, uses a micro-framework and stack I've initiated last
year, based on WebOb and Routes, GUnicorn and NGinx. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For Sync, the code is a thin layer on the top of a big MySQL cluster
(more than 100 servers) and some LDAP Servers for the user
authentication. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;We're responsible for writing the code &amp;amp; tests, and releasing it so the
Service Operations team can deploy it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;That includes some packaging work, having a TDD approach, and caring
about the beauty of the code. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I like my work for many reasons, here are a few: &lt;br /&gt;
-   It's all open source
-   I work with very smart &amp;amp; talented people
-   We work on big scale projects
-   We're trying whenever it makes sense, to make it possible for people
    to run our apps on their own servers.
-   Anyone is welcome to contribute. In practice, we don't have a lot of
    contributions on server-side because the community uses our own
    deployed services, but there are still a few people out there that
    are running their own server and sending us back some
    feedback/patches. That's the interaction I like the most.&lt;/p&gt;
&lt;p&gt;And check out the big project we're starting in Services: &lt;br /&gt;
-   &lt;a href=""&gt;https://blog.mozilla.com/services/2011/09/15/introducing-project-sagrada/&lt;/a&gt;
-   &lt;a href="https://wiki.mozilla.org/Services/Sagrada"&gt;https://wiki.mozilla.org/Services/Sagrada&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The only caveat of my job is that I am currently the only member in my
timezone, so I feel disconnected sometimes. But this is changing: the
major part of the server team is in PDT (California), but we now have
someone in Australia (yay!) and it is planned to have some other folks
in my timezone (or at least, close) soon. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I'd love to see more Europeans join the team. &lt;a href="http://hire.jobvite.com/j/?cj=oWtFVfwJ&amp;amp;s=tarek"&gt;Click here to apply&lt;/a&gt;
or contact me. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;You can check my previous blog entries in the &lt;a href="https://tarekziade.wordpress.com/category/mozilla/"&gt;Mozilla category&lt;/a&gt; to
see what I have been up to in this area.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;https:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;blog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mozilla&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="sr"&gt;/services/&lt;/span&gt;&lt;span class="mi"&gt;2011&lt;/span&gt;&lt;span class="sr"&gt;/09/&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="sr"&gt;/introducing-project-sagrada/&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Mon, 19 Sep 2011 16:18:00 +0200</pubDate><guid>http://blog.ziade.org/2011/09/19/mozilla-services-is-hiring/</guid></item><item><title>Pycon Japan Retrospective</title><link>http://blog.ziade.org/2011/09/05/pycon-japan-retrospective/</link><description>&lt;p&gt;I was back last week from my trip in Japan for &lt;a href="http://2011.pycon.jp/english-information"&gt;Pycon Japan&lt;/a&gt;, and I
wish I had spent an extra week there to see more places there. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Pycon Japan&lt;/h3&gt;
&lt;p&gt;Pycon Japan was a blast, really well-organized and from what I've heard
they had more than 200 attendees, which is a good size for a single day
conference. I took some notes for Pycon France. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;[caption id="attachment_2029" align="alignright" width="406"
caption="Some PyconJ organizers (Picture from Manabu
Terada)"][&lt;img alt="image" src="http://tarekziade.files.wordpress.com/2011/09/pycon-japan.jpeg" /&gt;][][/caption] &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I gave a keynote session -- you can grab the slides here:
&lt;a href="http://ziade.org/pyconjp-keynote.html"&gt;http://ziade.org/pyconjp-keynote.html&lt;/a&gt;. The keynote was mostly about
Packaging and a little bit on my (subjective ;)) opinions about the
uptake of Python 3, PyPy etc. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I've spent the day chatting with people, and even with Guido himself,
since the organizers set up a Skype-like video chat system were we could
do live 1:1 chats with him. Guido spent quite some time with every
attendee that wanted to speak with him. Thumbs up to Guido and to the
organizers for this nice idea. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Most talks were in Japanese but lots of speakers used dual
English/Japanese slides so I could follow a bit. The lightning talk
session at the end was fun -- as usual for those, and the organizers
ended up offering some Mozilla Goodies after a giant rock/scissor/paper
game. Then we had a party in a pub and ended up in another bar late at
night. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;On a side note, I was amazed by the number of attendees asking me for
an autograph on my book, &lt;a href="http://www.amazon.co.jp/エキスパートPythonプログラミング-Tarek-Ziade/dp/4048686291"&gt;Expert Python Programming&lt;/a&gt;, translated in
Japanese. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Also, check out &lt;a href="http://www.ianlewis.org/en/pycon-jp-2011-en"&gt;Ian Lewis' blog about the event&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Blockdiag&lt;/h3&gt;
&lt;p&gt;Komiya Takeshi showed me his tool called &lt;strong&gt;blockdiag&lt;/strong&gt;, which is a DSL
you can use to add diagrams in your documentation. The nice thing is
that it provides a Sphinx extension so you can add diagrams in your
documentation through simple expressions, and have Sphinx generate for
you the diagrams on the fly. There's even an interactive online shell:
&lt;a href="http://interactive.blockdiag.com/"&gt;http://interactive.blockdiag.com/&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I've challenged Komiya to write a few diagrams I have for some Mozilla
projects using his tool, and it took a few seconds for him to build
them. So, I am going to use this in the future. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Tokyo, Kyoto, Nara&lt;/h3&gt;
&lt;p&gt;After the conference, we left with my wife to visit the city, then
other places in Japan: Kyoto and Nara. Nara and its big park filled with
temples is a very beautiful place. It was not the best time in the year
to visit because it was very hot, but it still was really worth it. The
best time is probably in March, when the cherry blossoms are opening. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;We went to some beautiful parks and temples, and slept in traditional
Ryokans. &lt;a href="https://plus.google.com/photos/106436370949746015255/albums/5648838880137891441"&gt;More pics here&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;[caption id="" align="alignnone" width="440" caption="Todai-ji in
Nara"]&lt;img alt="image" src="https://lh3.googleusercontent.com/-RZTj0Yy_boI/TmS1vPF-ZJI/AAAAAAAABV8/0nchPdJ_trA/s1280/IMGP0201.jpg" /&gt;[/caption] &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Japan has met my expectations : the food is great, people are extremely
nice, and there are beautiful places to see. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;A huge Thanks to the Pycon Japan Team for inviting me and taking care
of Amina and I (Naokata Jay Hotta, Yasushi Masuda, Ian Lewis, Manabu
Terada, Morimoto Tetsuya, etc..)&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;pycon-japan&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;[&lt;img alt="image" src="http://tarekziade.files.wordpress.com/2011/09/pycon-japan.jpeg" /&gt;]: http://tarekziade.files.wordpress.com/2011/09/pycon-japan.jpeg
    "Todai-ji in Nara"&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Mon, 05 Sep 2011 16:42:00 +0200</pubDate><guid>http://blog.ziade.org/2011/09/05/pycon-japan-retrospective/</guid></item><item><title>5 tips for packaging your Python projects</title><link>http://blog.ziade.org/2011/08/19/5-tips-for-packaging-your-python-projects/</link><description>&lt;p&gt;Next week I am keynoting at &lt;a href="http://2011.pycon.jp/english-information"&gt;Pycon Japan&lt;/a&gt;, and one thing I will talk
about is packaging of course. And in particular: what advice can I give
my audience on how to package Python projects &lt;strong&gt;&lt;em&gt;today&lt;/em&gt;&lt;/strong&gt; ? &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This is a hard task, because we are in some kind of transitional state.&lt;/p&gt;
&lt;p&gt;Anyways, I wrote down a list of advices and removed everything that was
dependent on the tools we did not release yet -- that's another part in
my keynote. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here's a list. Most of them are not controversial. If you see something
missing or want to rant about one, please comment. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Tip # 1 -- Use a &lt;a href="http://www.python.org/dev/peps/pep-0386/"&gt;PEP 386&lt;/a&gt; compatible scheme for your versions&lt;/h3&gt;
&lt;p&gt;Having several version scheme in our eco-system is pure madness. It
breaks interoperability, and makes it impossible to write tools that
handle versions properly. By using a &lt;a href="http://www.python.org/dev/peps/pep-0386/"&gt;PEP 386&lt;/a&gt;-friendly scheme now,
you are making your project future-proof ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;PyPI already rejects any &lt;a href="http://www.python.org/dev/peps/pep-0345/"&gt;Metadata 1.2&lt;/a&gt; project that does not comply
to this policy. You probably don't know this because no tools produces
Metadata 1.2 packages yet. But that's going to be the default in Python
3.3 and distutils2. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So long "devdevdev123" and "3765-2011-test" versions ! &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Tip #2 -- try to make setup.py as dumb and simple as possible&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;setup.py&lt;/em&gt;&lt;/strong&gt; is not your personal build system. I have seen crazy
things in some projects. Remember that setup.py is used by installers
for a lot of different tasks. Like getting the metadata fields of the
project. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here's a simple test: make sure &lt;strong&gt;&lt;em&gt;"python setup.py --name"&lt;/em&gt;&lt;/strong&gt; (double
dash) eturns the name field without any external dependency, and without
calling any function or method. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Remember that &lt;em&gt;setup.py&lt;/em&gt; is going away in Python 3.3 and distutils2,
replaced by simple options in &lt;strong&gt;&lt;em&gt;setup.cfg&lt;/em&gt;&lt;/strong&gt;. Don't be scared, you will
still able to do complex tasks. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;My advice: don't do anything else that feeding &lt;strong&gt;&lt;em&gt;setup()&lt;/em&gt;&lt;/strong&gt; with
options in there. Put all your build things in another place, and if
they need to be called by setup.py, make sure they are called only when
needed. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Tip #3 -- Do not make any assumption about which installer will be&lt;/h3&gt;
&lt;p&gt;used&lt;/p&gt;
&lt;p&gt;Make sure your &lt;strong&gt;&lt;em&gt;setup.py&lt;/em&gt;&lt;/strong&gt; can be run by a vanilla Python
(==distutils). Even if you use setuptools or distribute, in most case
you can manage to have it working in both tools. You can always tell the
user to do extra steps manually if he needs to. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Forcing the installation of an installer, by using the &lt;strong&gt;&lt;em&gt;ez_setup&lt;/em&gt;&lt;/strong&gt;
script for instance, without asking, is a bit rude to the end-user. It's
basically forcing the end user to use a new installer. If you do this in
your setup.py, ask first ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Or simply tell the user "This project only works with the XXX installer
-- install it if you want. Aborting." &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Tip #4 -- Do not release unstable releases at pypi&lt;/h3&gt;
&lt;p&gt;Our installers are not --&lt;em&gt;yet&lt;/em&gt;-- smart enough to prefer stable releases
when they are asked to get a project at PyPI. That's how PyPI is built:
every project has a directory with all releases and it's up to the
installer to decide which one is the "latest". The only tool out there
that's smart about it is zc.buildout. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So when you push an alpha release or a rc release at PyPI, it's going
to land in people environments unless they have mature processes to
update their stuff -- or simply because they make the assumption that
PyPI is where stable release go. So do not make assumptions about how
your users are updating your project. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Prefer another explicit channel for your beta testers. All installers
know how to install from any url or directory. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Tip #5 -- Be cautious about your data files&lt;/h3&gt;
&lt;p&gt;Distutils or Distribute or Python itself have no way to explicitly make
a difference between a doc file or a media file or a configuration file.
They are all &lt;strong&gt;&lt;em&gt;data files&lt;/em&gt;&lt;/strong&gt;. Worse, since they are no universal place
for data files on the various OSes, people tend to treat their data
files like Python modules so they are able to find them back on the
target system without trouble. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Yeah that's broken, and we've fixed it in 3.3. But until then, that's
unfortunately the most protable way to do this. So what you can do is
document clearly how you handle your data files and create a single
function or module that reads them. That'll help the downstream
maintainers to handle your project.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 19 Aug 2011 16:05:00 +0200</pubDate><guid>http://blog.ziade.org/2011/08/19/5-tips-for-packaging-your-python-projects/</guid></item><item><title>Afpy Camp Wrapup -- RedBarrel + Pistil + 0mq</title><link>http://blog.ziade.org/2011/08/08/afpy-camp-wrapup-redbarrel-pistil-0mq/</link><description>&lt;p&gt;&lt;img alt="image" src="https://lh4.googleusercontent.com/-ov7H4MpuS88/Tj7fpDpA7hI/AAAAAAAABMI/zBIp62HBJL0/s288/11+-+1" /&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;We had a lot of fun at the Afpy Computer Camp this year. Some people
worked on RedBarrel with me and some others on various topics, including
Pyti -- A GSOC project. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In RedBarrel, besides fixing a lot of bugs we did the following: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;We've finished two socket.io based demos&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a &lt;a href="https://bitbucket.org/tarek/redbarrel/src/tip/redbarrel/demos/rtmonitor/"&gt;monitoring page&lt;/a&gt; that displays in real-time using &lt;a href="https://code.google.com/p/flot/"&gt;Flot&lt;/a&gt; the
    CPU and memory usage of the server&lt;/li&gt;
&lt;li&gt;an &lt;a href="https://bitbucket.org/tarek/redbarrel/src/tip/redbarrel/demos/shortener/"&gt;url shortener&lt;/a&gt; that provides a monitoring page where you can
    see on a google map a live stream of the location of the URL
    visitors.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We've integrated &lt;a href="https://github.com/meebo/pistil"&gt;Pistil&lt;/a&gt; into Redbarrel&lt;/p&gt;
&lt;p&gt;We started to integrate scaling features&lt;/p&gt;
&lt;p&gt;Pistil is Benoit's new project. It's a project very similar to
&lt;a href="http://gunicorn.org/"&gt;Gunicorn&lt;/a&gt; that allows you to define how a web server behaves via
"workers" and "arbiters". Pistil is basically replacing all Gunicorn
components except the command line script. One nice feature Benoit added
to Pistil this week-end is the ability to define different kind of
workers. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;That's because in Redbarrel we need: &lt;br /&gt;
-   some RedBarrel workers, each one running a RedBarrel wsgi server to
    build the responses -- &lt;a href="https://bitbucket.org/tarek/redbarrel/src/43dfbce63b29/redbarrel/server.py#cl-92"&gt;see this code&lt;/a&gt;
-   One Flash server policy worker for the flash fallback in socket.io
    -- &lt;a href="https://bitbucket.org/tarek/redbarrel/src/43dfbce63b29/redbarrel/server.py#cl-150"&gt;see this code&lt;/a&gt;
-   A Broadcast server -- I'll explain why later&lt;/p&gt;
&lt;p&gt;Once you've integrated Pistil, you just need to add a custom
command-line script in your app and call&lt;strong&gt;&lt;em&gt; arbiter.run()&lt;/em&gt;&lt;/strong&gt;&lt;a href="https://bitbucket.org/tarek/redbarrel/src/43dfbce63b29/redbarrel/util.py#cl-56"&gt;-- see this
code&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;From now on, RedBarrel doesn't need GUnicorn anymore to run and manage
several processes. yay. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Scaling RedBarrel&lt;/h3&gt;
&lt;p&gt;The goal of integrating Pistil into Redbarrel was to make it possible
to run several processes (==workers) to handle more requests. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;RedBarrel is using Gevent underneath via gevent.socketio, so that gives
a single worker the ability to run a lot of concurrent requests already.
But still, if you're doing database accesses in your application, you
will need to run several workers if you want to handle several hundreds
of concurrent requests. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;And that leads to another issue : if the application uses the socket.io
features, we need the ability to broadcast messages to &lt;strong&gt;all&lt;/strong&gt; connected
clients no matter which process they're in. gevent.socketio implements
this feature using Gevent queues in memory, so &lt;strong&gt;it's constrained to a
single process&lt;/strong&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you have several processes, you need to be able to broadcast
messages to all the workers in order to reach all connected clients. A
inter-process communication protocol needs to be set up for this. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.zeromq.org/"&gt;zeromq&lt;/a&gt; to the rescue ! &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;zeromq integration&lt;/h3&gt;
&lt;p&gt;zeromq seems to be the best tool for this, because it hides all the
complexity when you need to set up a simple communication protocol
between several processes and servers. zeromq also allows you to use
different transports, whether they are local to the same server (ipc) or
between servers (tcp). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;zeromq provides several behaviors to exchange messages : PUSH/PULL,
PUB/SUB etc. see &lt;a href="http://zguide.zeromq.org/chapter:all"&gt;its nice doc&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For our use case, we need to be able to broadcast messages from any
worker to all other workers. Instead of linking all workers to each
other, a simpler pattern is to have a single server that is responsible
for all the broadcasting. Workers subscribe to it to receive messages,
but also can send messages to broadcast to the server. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Setting this up with zeromq is done by using a bi-directional
communication through 2 channels: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;A &lt;strong&gt;publisher/subscribers&lt;/strong&gt; channel.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the publisher is a single zeromq server that is able to broadcast
    messages to several subscribers.&lt;/li&gt;
&lt;li&gt;each worker becomes a subscriber and receives messages from the
    publisher.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A &lt;strong&gt;push/pull&lt;/strong&gt; channel.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;each worker may push messages to be broadcasted to the publisher&lt;/li&gt;
&lt;li&gt;the publisher pulls messages and broadcasts them to all subscribers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That sounds like a complex setup, but is not at all ! In my first load
tests it seems very efficient. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;To implement this in RedBarrel, we used &lt;a href="http://zeromq.github.com/pyzmq/"&gt;pymzq&lt;/a&gt;. We also needed to
use[gevent_zmq][]. This small library simply makes pymzq green, so it's
compatible with Gevent. The code I added is composed of a Broadcaster
class and a Client class. It's very straightforward. &lt;a href="https://bitbucket.org/tarek/redbarrel/src/43dfbce63b29/redbarrel/broadcast.py"&gt;You can read the
code here&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Now when RedBarrel is launched, one Broadcaster is launched and every
worker has one zmq Client, allowing inter-worker
broadcasting.&lt;img alt="image" src="http://tarekziade.files.wordpress.com/2011/08/biere-e1312794381879.jpg?w=768" /&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Next steps&lt;/h3&gt;
&lt;p&gt;The next steps are: &lt;br /&gt;
-   make gevent.socketio use our zeromq system. gevent.socketio
    currently uses Queues to push and pull messages to be broadcasted.
    We can keep these queues and just feed them via zeromq. What I need
    to do here is to hook the worker's broadcasting feature into the
    code that interacts with the queue.
-   allow inter-servers communication. this will simply be done by
    allowing a broadcaster to become a subscriber of other servers
    broadcasters, so it can broadcast locally to its own subscriber what
    was broadcasted in the other server. What I am unsure about yet is
    how the whole thing will scale under a very heavy message load.&lt;/p&gt;
&lt;p&gt;Some other features ideas we had: &lt;br /&gt;
-   provide a key/value storage using redis and a pure-python fallback
    and allow its definition via the DSL.
-   add an event system based on the zeromq integration. one event that
    comes in mind: broadcast to all workers an event everytime a key is
    changed in the storage.&lt;/p&gt;
&lt;p&gt;And, yeah, we ate and drunk a lot..&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;Dinner at the Afpy Camp&amp;quot;&lt;/span&gt;
&lt;span class="s"&gt;&amp;quot;biere&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Mon, 08 Aug 2011 18:12:00 +0200</pubDate><guid>http://blog.ziade.org/2011/08/08/afpy-camp-wrapup-redbarrel-pistil-0mq/</guid></item><item><title>RedBarrel is now socket.io compatible</title><link>http://blog.ziade.org/2011/08/03/redbarrel-is-now-socketio-compatible/</link><description>&lt;p&gt;I got really excited by web sockets the other week-end, so I decided
that &lt;a href="http://redbarrel.readthedocs.org/en/latest/"&gt;RedBarrel&lt;/a&gt; should support them out of the box and provide a very
easy way to work with them, like what socket.io can provide on the
server-side. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;The usual "Chat" application&lt;/h3&gt;
&lt;p&gt;I have a first version of sockets support in RedBarrel. Most web socket
demos include a chat application. I ripped the one I found here:
&lt;a href="https://bitbucket.org/denis/websocket/src/tip/examples"&gt;https://bitbucket.org/denis/websocket/src/tip/examples&lt;/a&gt; and changed
it until it worked in RedBarrel. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The whole application is composed of the RBR file, a python module an
an html page with its JS libs. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here's the relevant parts of the RBR file: &lt;br /&gt;
   # hooks the chat() function as a socket.io service&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="nb"&gt;socket&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;

    &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="n"&gt;SOCKETIO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="n"&gt;python:redbarrel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;demos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sockets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;

&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;# that&amp;#39;s the html file that displays the chat room&lt;/span&gt;

&lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="n"&gt;chat_html&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;

    &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="n"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="n"&gt;file:chat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;

&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;And the whole app is contained in one module: &lt;br /&gt;
   import json&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;_BUFFER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt;

&lt;span class="n"&gt;_D&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;_BUFFER&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_BUFFER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

        &lt;span class="n"&gt;del&lt;/span&gt; &lt;span class="n"&gt;_BUFFER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;globs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;A Chat room using web sockets&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="n"&gt;socketio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;socketio&lt;/span&gt;

    &lt;span class="n"&gt;sid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;socketio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;

    &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;announce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="n"&gt;socketio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;broadcast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_D&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;announcement&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;socketio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;on_connect&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;

        &lt;span class="n"&gt;announce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sid&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;&amp;#39; connected&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;socketio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_D&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;buffer&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;_BUFFER&lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;True:&lt;/span&gt;

        &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;socketio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

            &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;message&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]]}&lt;/span&gt;

            &lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="n"&gt;socketio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;broadcast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_D&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;socketio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connected&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;

                &lt;span class="n"&gt;announce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sid&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;&amp;#39; disconnected&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Nothing fancy here, gevent.socketio does all the magic. But I am very
glad that I was able to integrate this in RedBarrel with the
no-boiler-code-at-all philosophy I want to keep. That is 1/ define a RBR
file 2/ do the coding &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;How the feature was added&lt;/h3&gt;
&lt;p&gt;The problem with web sockets is that their implementations may vary or
may be nonexistent under some browser flavors. I've hacked something
that worked, then looked at how I could make the thing work under
Firefox, IE and Chrome and started to feel like back in the old days
when we had to work out our Javascript code for all the different
browsers. This may be still true but at least they are some decent
libraries now that make most of the JS code cross-compatible. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;socket.io&lt;/h3&gt;
&lt;p&gt;&lt;a href="http://socket.io"&gt;socket.io&lt;/a&gt; seems to do a very good job in this area for web sockets
by providing a lot of different &lt;em&gt;transports&lt;/em&gt; implementations for
applications that want to have "websocket-like" features. A Transport
here is just one way to send and receive data between the client and the
server. It may be the shiny web sockets, it may be something else. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;socket.io has a websocket implementation -- sorry I am not following
the RFCs on this, but I know there are at least 2 versions -- &lt;strong&gt;but
also&lt;/strong&gt; some fallbacks in case the browser does not seem to be compatible
with web sockets. That includes a flash component and various other
techniques based on async calls, like &lt;a href="http://en.wikipedia.org/wiki/Comet_(programming)#XMLHttpRequest_long_polling"&gt;XHR polling&lt;/a&gt;. And it works
really well -- I don't know how the fallback algorithm works internally,
but I was able to have a socket.io app running against the latest
Firefox, an old one and Chrome. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;gevent.socketio&lt;/h3&gt;
&lt;p&gt;On the server side, I wanted to implement in RedBarrel something that
looked as simple as what socket.io offers. So I needed to: &lt;br /&gt;
-   implement every transport protocol socket.io supports
-   provide an async layer for all the broadcasting work&lt;/p&gt;
&lt;p&gt;But.. it turns out that the &lt;a href="http://pypi.python.org/pypi/gevent-socketio/"&gt;gevent.socketio&lt;/a&gt; project already offers
all of this -- it implements requests handlers for all the transports
and a socket object with what we need. It uses &lt;a href="http://gevent.org/"&gt;GEvent&lt;/a&gt; in the
background. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So basically all I had to do was to : &lt;br /&gt;
1.  extend the RedBarrel DSL so we can define "sockets" in applications
2.  make the RedBarrel wsgi application use gevent.socketio to handle
    incoming requests
3.  expose the socket object in the WebOb request object on the fly
4.  let the code do whatever it wants with the request and the socket,
    hopefully something useful&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;was easy -- you can now define one path that has &lt;strong&gt;SOCKETIO&lt;/strong&gt; as its
&lt;em&gt;method&lt;/em&gt;. That tells RedBarrel that the application will use socket.io
and calls should be transmitted to the code pointed. The syntax might
evolve since "path socket" (see the example at the beginning of this
post) is not really needed. I might change it to something more straight
forward. &lt;br /&gt;
&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;was done by switching to gevent.socketio own wsgi handlers + gevent
runner. Once gevent.socketio has done its prep work like the web socket
handsake, it goes back to the wsgi application -- so in our case the
generic RedBarrel application that runs the DSL. &lt;br /&gt;
&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When RedBarrel is called, it looks for the environ, where
gevent.socketio adds a socket object and simply stick it to the WebOb
request object. &lt;br /&gt;
&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;the code that is called receives, like for classical calls, a WebOb
object and can use the attached socket. &lt;br /&gt;
&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;What's next&lt;/h3&gt;
&lt;p&gt;I am going to add a sexy demo -- I asked people on G+ what demo I
should add and it looks like I'll add a real-time server monitoring demo
(using &lt;a href="http://code.google.com/p/flot/"&gt;Flot&lt;/a&gt;) with these features: &lt;br /&gt;
-   people can look in real-time what's going on (CPU, Memory) via
    constantly updated diagrams
-   people can talk to each other on the page ("Hey bob, the server is
    melting down, don't you think?")&lt;/p&gt;
&lt;p&gt;If you have a small websocket app in Python and would be interested to
see how it could fit in RedBarrel, please let me know, I'd be happy to
give it a shot&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Wed, 03 Aug 2011 18:18:00 +0200</pubDate><guid>http://blog.ziade.org/2011/08/03/redbarrel-is-now-socketio-compatible/</guid></item><item><title>Pycon France and Pycon Japan this summer</title><link>http://blog.ziade.org/2011/08/01/pycon-france-and-pycon-japan-this-summer/</link><description>&lt;p&gt;It's awesome to see all those Pycon conferences getting organized
throughout the world. &lt;a href="http://www.pycon.org"&gt;http://www.pycon.org&lt;/a&gt; lists most of them. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For me, Pycon is also a good opportunity to travel and meet people -- I
try to attend to Pycon US every year, and when possible to an extra
Pycon elsewhere. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This summer I'll attend to &lt;a href="http://pycon.fr"&gt;Pycon France&lt;/a&gt; and &lt;a href="http://2011.pycon.jp/english-information"&gt;Pycon Japan&lt;/a&gt;. I've
submitted a proposal for the first one, that will happen in Rennes,
France in September. If you're in France, you should attend. Rennes is
lovely and while the conferences are in French, there will probably be
some social events before and after, where english speakers won't be
left aside ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I am also giving a keynote for the second one that's happening in
Tokyo, Japan at the end of this month. If you happen to be a Pythoneer
in Tokyo, speaking French or English, I'd love to meet there.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Mon, 01 Aug 2011 16:56:00 +0200</pubDate><guid>http://blog.ziade.org/2011/08/01/pycon-france-and-pycon-japan-this-summer/</guid></item><item><title>How to stress test your app using Funkload -- part 2</title><link>http://blog.ziade.org/2011/07/28/how-to-stress-test-your-app-using-funkload-part-2/</link><description>&lt;p&gt;The first part of this blog post is &lt;a href="http://tarekziade.wordpress.com/2011/07/27/how-to-stress-test-your-app-using-funkload-part-1/"&gt;here&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I've initially split it in two parts because I've encountered a few
problems with the distributed feature, so I thought it was going to take
Ben a few days to fix them. But he did fix them within the hour... \o/&lt;/p&gt;
&lt;p&gt;So &lt;strong&gt;Hurra for Ben&lt;/strong&gt; and here's the second part of my small tutorial. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Running a distributed test&lt;/h3&gt;
&lt;p&gt;If you want to hammer a server, you cannot run all the virtual
concurrent users from a single box. You will end up eating all your
resources and will not be able to simulate a very big load. If you need
to run a couple of hundreds of concurrent users, one box is enough. But
you'll need more boxes to get more load, obviously. Depending on the
complexity of the tests, I usually end up having at the most 200 CU
(Concurrent Users) per node. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In the past, I was using Fabric to do distributed tests for Funkload,
see &lt;a href="http://tarekziade.wordpress.com/2010/12/09/funkload-fabric-quick-and-dirty-distributed-load-system/"&gt;here&lt;/a&gt;. But this is not needed anymore because Funkload has now a
built-in support to distribute the charge amongst several nodes. It uses
a similar technique that I used in Fabric, by driving the nodes via SSH
using &lt;a href="http://www.lag.net/paramiko/"&gt;Paramiko&lt;/a&gt;. The benefit though, is that it does all the heavy
lifting for you: your test suite gets deployed on the nodes, and the XML
result files gets downloaded for you on the master. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;All you have to do once the test is over, is to merge the XML files and
generate reports. And the merging is taken care of by a Funkload script
so... Let's do this ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;First of all, you need to install Paramiko in your environment: &lt;br /&gt;
   $ bin/pip install paramiko&lt;/p&gt;
&lt;p&gt;Also, move to a trunk snapshot version of Funkload, since there are a
couple of fixes there for this feature: &lt;br /&gt;
   $ bin/pip install -U -f http://funkload.nuxeo.org/snapshots Funkload&lt;/p&gt;
&lt;p&gt;Next, select a few boxes that will be your nodes to run the tests, and
make sure they have Python and virtualenv installed, and that you can
access via SSH to them without having to type anything. The simplest way
to do this is to copy over an ssh key with an empty passphrase. &lt;a href="http://linuxproblem.org/art_9.html"&gt;See
this article if you don't know how to do it.&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Notice: having a ssh key that does not require a passphrase is a
potential security hole, so make sure these key are used only for this
purpose, and safe.&lt;/em&gt;&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The next step is to add a &lt;strong&gt;&lt;em&gt;[distribute]&lt;/em&gt;&lt;/strong&gt; section in the Simple.conf
file: &lt;br /&gt;
   [distribute]&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;log_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;distributed&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;simple&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;

&lt;span class="n"&gt;funkload_location&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;pypi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/packages/so&lt;/span&gt;&lt;span class="n"&gt;urce&lt;/span&gt;&lt;span class="sr"&gt;/f/&lt;/span&gt;&lt;span class="n"&gt;funkload&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;funkload&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;1.16.0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gz&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;These options tell Funkload which Funkload release should be used on
the node, and where to put the logs. The script will install it in a
virtualenv on every node, prior to running the tests. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Once everything is set up, you can run the test using all your nodes
with the &lt;strong&gt;&lt;em&gt;--distribute&lt;/em&gt;&lt;/strong&gt; flag and the &lt;strong&gt;&lt;em&gt;--distribute-workers&lt;/em&gt;&lt;/strong&gt;
option, that gives a list of the nodes. The script deploy the tests into
the nodes, run them, and grab back the XML files. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In my environment I have a master, and two nodes (node1 and node2) &lt;br /&gt;
   $ bin/fl-run-bench --distribute --distribute-workers=node1,node2 test_simple.py Simple.test_simple&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;========================================================================&lt;/span&gt;

&lt;span class="n"&gt;Benching&lt;/span&gt; &lt;span class="n"&gt;Simple&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test_simple&lt;/span&gt;

&lt;span class="o"&gt;========================================================================&lt;/span&gt;

&lt;span class="n"&gt;Access&lt;/span&gt; &lt;span class="k"&gt;our&lt;/span&gt; &lt;span class="n"&gt;Demo&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;

&lt;span class="o"&gt;------------------------------------------------------------------------&lt;/span&gt;

&lt;span class="n"&gt;Configuration&lt;/span&gt;

&lt;span class="o"&gt;=============&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Current&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2011&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;07&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="n"&gt;T14:11:47&lt;/span&gt;&lt;span class="mf"&gt;.720959&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Configuration&lt;/span&gt; &lt;span class="n"&gt;file:&lt;/span&gt; &lt;span class="sr"&gt;/home/&lt;/span&gt;&lt;span class="n"&gt;tarek&lt;/span&gt;&lt;span class="sr"&gt;/dev/&lt;/span&gt;&lt;span class="n"&gt;hg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mozilla&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/funkload-demo/&lt;/span&gt;&lt;span class="n"&gt;Simple&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Distributed&lt;/span&gt; &lt;span class="n"&gt;output:&lt;/span&gt; &lt;span class="n"&gt;distributed&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;simple&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Server:&lt;/span&gt; &lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//m&lt;/span&gt;&lt;span class="n"&gt;aster:5000&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Cycles:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Cycle&lt;/span&gt; &lt;span class="n"&gt;duration:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Sleeptime&lt;/span&gt; &lt;span class="n"&gt;between&lt;/span&gt; &lt;span class="n"&gt;request:&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Sleeptime&lt;/span&gt; &lt;span class="n"&gt;between&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Startup&lt;/span&gt; &lt;span class="n"&gt;delay&lt;/span&gt; &lt;span class="n"&gt;between&lt;/span&gt; &lt;span class="n"&gt;thread:&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Workers&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;node1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;node2&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Preparing&lt;/span&gt; &lt;span class="n"&gt;sandboxes&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="n"&gt;workers&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Starting&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="n"&gt;workers&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;node1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;returned&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;node2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;returned&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Received&lt;/span&gt; &lt;span class="n"&gt;bench&lt;/span&gt; &lt;span class="nb"&gt;log&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;node1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;into&lt;/span&gt; &lt;span class="n"&gt;distributed&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;simple&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;node1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;simple&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;bench&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Received&lt;/span&gt; &lt;span class="n"&gt;bench&lt;/span&gt; &lt;span class="nb"&gt;log&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;node2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;into&lt;/span&gt; &lt;span class="n"&gt;distributed&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;simple&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;node2&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;simple&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;bench&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Once the test is over, you will find two XML files on your master, you
can merge to produce an HTML report: &lt;br /&gt;
   $ bin/fl-build-report --html -o html distributed-simple-test.log/node1-simple-bench.xml distributed-simple-test.log/node1-simple-bench.xml&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;Merging&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="n"&gt;files:&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt;

&lt;span class="n"&gt;nodes:&lt;/span&gt; &lt;span class="n"&gt;tarek&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;laptop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tarek&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;laptop&lt;/span&gt;

&lt;span class="n"&gt;cycles&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;node:&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;cycles&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;all&lt;/span&gt; &lt;span class="n"&gt;nodes:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;Results&lt;/span&gt; &lt;span class="n"&gt;merged&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tmp&lt;/span&gt; &lt;span class="n"&gt;file:&lt;/span&gt; &lt;span class="sr"&gt;/tmp/&lt;/span&gt;&lt;span class="n"&gt;fl&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;mrg&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;JL62Bi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt;

&lt;span class="n"&gt;Creating&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt; &lt;span class="n"&gt;report:&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="sr"&gt;/home/&lt;/span&gt;&lt;span class="n"&gt;tarek&lt;/span&gt;&lt;span class="sr"&gt;/dev/&lt;/span&gt;&lt;span class="n"&gt;hg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mozilla&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/funkload-demo/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;

&lt;span class="n"&gt;done:&lt;/span&gt;

&lt;span class="sr"&gt;/home/&lt;/span&gt;&lt;span class="n"&gt;tarek&lt;/span&gt;&lt;span class="sr"&gt;/dev/&lt;/span&gt;&lt;span class="n"&gt;hg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mozilla&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/funkload-demo/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="sr"&gt;/test_simple-20110728T141152/i&lt;/span&gt;&lt;span class="n"&gt;ndex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Congrats, you're now able to run distributed tests ! &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Monitoring the server&lt;/h3&gt;
&lt;p&gt;The last feature I want to show is the monitoring. Funkload with let
you monitor: &lt;br /&gt;
-   the network traffic on a given interface
-   the CPU load average
-   the memory usage
-   the number of concurrent users the server is handling over time&lt;/p&gt;
&lt;p&gt;This is very useful to detect memory leaks or abnormal consumption of
memory. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;To do this, you need to run a monitor server provided by Funkload on
the server you want to watch. Once Funkload is installed there, create a
&lt;strong&gt;&lt;em&gt;monitor.conf&lt;/em&gt;&lt;/strong&gt; file. &lt;br /&gt;
   [server]&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;

&lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8008&lt;/span&gt;

&lt;span class="n"&gt;interval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;.5&lt;/span&gt;

&lt;span class="n"&gt;interface&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;eth0&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;master&lt;/span&gt;

&lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8008&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Here &lt;strong&gt;&lt;em&gt;server&lt;/em&gt;&lt;/strong&gt; is the server I am benching, and the monitor server
will run on the port 8008. The interface parameter will just tell
Funkload which one to watch. The client section tells the monitor server
which server will call it, so our bench master. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Once this is saved, simply run the monitor server with: &lt;br /&gt;
   $ fl-monitor-ctl monitor.conf start&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;Starting&lt;/span&gt; &lt;span class="n"&gt;monitor&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;localhost:8008&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;as&lt;/span&gt; &lt;span class="n"&gt;daemon&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;use stop to stop it, obviously. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Last but not least, back to our master bench server, open Simple.conf
and add these sections: &lt;br /&gt;
   [monitor]&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;hosts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;localhost&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;localhost&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;application&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;

&lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8008&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;You're all set ! Simply run the benches as usual, and you should see a
new section in the reports you're generating, with four new graphs. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;[&lt;img alt="image" src="http://tarekziade.files.wordpress.com/2011/07/localhost_monitorcpu.png" /&gt;][] &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;[&lt;img alt="image" src="http://tarekziade.files.wordpress.com/2011/07/localhost_monitormemfree.png" /&gt;][] &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;That's all. I hope you found this mini-tutorial interesting, and that
you'll give Funkload a shot. I've tried many tools, like The Grinder,
Apache JMeter, and some proprietary things from Mercury etc. And
Funkload, out of the box, beats them all because it let me create my own
stress tests without any crazy interface of framework. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Thanks for all the work Ben.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;localhost_MonitorCPU&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;[&lt;img alt="image" src="http://tarekziade.files.wordpress.com/2011/07/localhost_monitorcpu.png" /&gt;]: http://tarekziade.files.wordpress.com/2011/07/localhost_monitorcpu.png
    "localhost_MonitorMemFree"
  [&lt;img alt="image" src="http://tarekziade.files.wordpress.com/2011/07/localhost_monitormemfree.png" /&gt;]: http://tarekziade.files.wordpress.com/2011/07/localhost_monitormemfree.png&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 28 Jul 2011 14:52:00 +0200</pubDate><guid>http://blog.ziade.org/2011/07/28/how-to-stress-test-your-app-using-funkload-part-2/</guid></item><item><title>How to stress test your app using Funkload -- part 1</title><link>http://blog.ziade.org/2011/07/27/how-to-stress-test-your-app-using-funkload-part-1/</link><description>&lt;p&gt;&lt;strong&gt;&lt;em&gt;EDIT: &lt;a href="http://tarekziade.wordpress.com/2011/07/28/how-to-stress-test-your-app-using-funkload-part-2/"&gt;Part 2 is now published&lt;/a&gt;(distributed tests and
monitoring)&lt;/em&gt;&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href="http://funkload.nuxeo.org/"&gt;Funkload&lt;/a&gt; is a functional and stress test tool that can be used on
your web applications. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It's my favorite stress tool for these reasons: &lt;br /&gt;
-   stress tests are implemented as PyUnit tests, so they can also be
    used as functional tests
-   the test runner is very light, it's dead easy to run a stress test
    on your box against a local app
-   it's simple to do a distributed test with a few extra options --
    Funkload will drive the other boxes using SSH
-   Funkload provides nice reporting features out of the box: you can do
    trends and diff reports
-   You can monitor the server being tested&lt;/p&gt;
&lt;p&gt;This blog post demonstrates how to use Funkload on a dummy app and
generate report. A second blog post will explain how to run the test
using several test servers, and how to monitor the server that runs the
application. &lt;br /&gt;
&lt;/p&gt;
&lt;h4&gt;The tested application&lt;/h4&gt;
&lt;p&gt;The dummy application we'll stress test for this demo is a very simple
WSGI application that can be run in plain Python, or using Gunicorn: &lt;br /&gt;
   from wsgiref.simple_server import make_server&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start_response&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;200 OK&amp;#39;&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Content-type&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;text/plain&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;start_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Hello World&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;httpd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;make_server&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Serving on port 5000&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;try:&lt;/span&gt;

&lt;span class="err"&gt;       &lt;/span&gt; &lt;span class="n"&gt;httpd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;serve_forever&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;except&lt;/span&gt; &lt;span class="n"&gt;KeyboardInterrupt:&lt;/span&gt;

&lt;span class="err"&gt;       &lt;/span&gt; &lt;span class="n"&gt;pass&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Every time the application is called, no matter what's the request path
or method, it will sleep for 100 ms then return a Hello World. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Let's run our application: &lt;br /&gt;
   $ python wsgiapp.py&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;Serving&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The application is ready to get some hits ! &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Installing Funkload&lt;/h3&gt;
&lt;p&gt;Let's open a new shell and create a local environment in a directory,
using &lt;a href="http://pypi.python.org/pypi/virtualenv"&gt;Virtualenv&lt;/a&gt;, and install Funkload in it: &lt;br /&gt;
   $ virtualenv --no-site-package --distribute .&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;New&lt;/span&gt; &lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="n"&gt;executable&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="sr"&gt;/bin/&lt;/span&gt;&lt;span class="n"&gt;python2&lt;/span&gt;&lt;span class="mf"&gt;.6&lt;/span&gt;

&lt;span class="n"&gt;Also&lt;/span&gt; &lt;span class="n"&gt;creating&lt;/span&gt; &lt;span class="n"&gt;executable&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="sr"&gt;/bin/&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;

&lt;span class="n"&gt;Installing&lt;/span&gt; &lt;span class="n"&gt;distribute&lt;/span&gt;&lt;span class="o"&gt;.................................&lt;/span&gt;&lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;pip&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="n"&gt;Funkload&lt;/span&gt;

&lt;span class="n"&gt;Downloading&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;unpacking&lt;/span&gt; &lt;span class="n"&gt;Funkload&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="n"&gt;Successfully&lt;/span&gt; &lt;span class="n"&gt;installed&lt;/span&gt; &lt;span class="n"&gt;docutils&lt;/span&gt; &lt;span class="n"&gt;Funkload&lt;/span&gt; &lt;span class="n"&gt;webunit&lt;/span&gt;

&lt;span class="n"&gt;Cleaning&lt;/span&gt; &lt;span class="n"&gt;up&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Once everything's installed, you will get in the local &lt;strong&gt;bin&lt;/strong&gt;
directory a few Funkload scripts: &lt;br /&gt;
   $ ls bin/fl*&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="sr"&gt;/fl-build-report    bin/&lt;/span&gt;&lt;span class="n"&gt;fl&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;install&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;demo&lt;/span&gt;

&lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="sr"&gt;/fl-record     bin/&lt;/span&gt;&lt;span class="n"&gt;fl&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;

&lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="sr"&gt;/fl-credential-ctl  bin/&lt;/span&gt;&lt;span class="n"&gt;fl&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;monitor&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ctl&lt;/span&gt;

&lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;fl&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;bench&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The two scripts that interest us right now are &lt;strong&gt;&lt;em&gt;fl-run-test&lt;/em&gt;&lt;/strong&gt; and
&lt;strong&gt;&lt;em&gt;fl-run-bench&lt;/em&gt;&lt;/strong&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;A first Funkload test&lt;/h3&gt;
&lt;p&gt;Let's create a &lt;strong&gt;test_simple.py&lt;/strong&gt; module in our directory: &lt;br /&gt;
   import unittest&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;

&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;funkload&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FunkLoadTestCase&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FunkLoadTestCase&lt;/span&gt;

&lt;span class="n"&gt;class&lt;/span&gt; &lt;span class="n"&gt;Simple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FunkLoadTestCase&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;setUp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

&lt;span class="err"&gt;       &lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;server_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conf_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;main&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;url&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;test_simple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

&lt;span class="err"&gt;       &lt;/span&gt; &lt;span class="n"&gt;server_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;server_url&lt;/span&gt;

        &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Get url&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="err"&gt;       &lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="err"&gt;       &lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Hello World&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;main&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;unittest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This test simply checks that the server returns &lt;strong&gt;&lt;em&gt;Hello World&lt;/em&gt;&lt;/strong&gt; and
that the response status code is &lt;strong&gt;&lt;em&gt;200&lt;/em&gt;&lt;/strong&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;To run this test, Funkload needs a few options to run. These options
can be placed in a configuration file. Let's create a &lt;strong&gt;Simple.conf&lt;/strong&gt;
file with this content: &lt;br /&gt;
   [main]&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Demo&lt;/span&gt;

&lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Simple&lt;/span&gt; &lt;span class="n"&gt;demo&lt;/span&gt;

&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;localhost:5000&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;test_simple&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Access&lt;/span&gt; &lt;span class="k"&gt;our&lt;/span&gt; &lt;span class="n"&gt;Demo&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ftest&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;log_to&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;console&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;

&lt;span class="n"&gt;log_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simple&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;

&lt;span class="n"&gt;result_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simple&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt;

&lt;span class="n"&gt;sleep_time_min&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="n"&gt;sleep_time_max&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;bench&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;cycles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;

&lt;span class="n"&gt;duration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;

&lt;span class="n"&gt;startup_delay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;

&lt;span class="n"&gt;sleep_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;

&lt;span class="n"&gt;cycle_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

&lt;span class="n"&gt;log_to&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;

&lt;span class="n"&gt;log_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simple&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;bench&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;

&lt;span class="n"&gt;result_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simple&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;bench&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt;

&lt;span class="n"&gt;sleep_time_min&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="n"&gt;sleep_time_max&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Those are defining options Funkload will use when it's running. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Now we can try out our Funkload script with the&lt;strong&gt;&lt;em&gt; fl-run-test&lt;/em&gt;&lt;/strong&gt;
script, which will run the tests just once: &lt;br /&gt;
   $ bin/fl-run-test test_simple.py&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;----------------------------------------------------------------------&lt;/span&gt;

&lt;span class="n"&gt;Ran&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="mf"&gt;0.104&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;

&lt;span class="n"&gt;OK&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Victory ! The test is working nicely. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Let's now try a full bench, using &lt;strong&gt;&lt;em&gt;fl-run-bench&lt;/em&gt;&lt;/strong&gt;. The bench script
takes an extra option which is the test method to use to run the bench:&lt;/p&gt;
&lt;p&gt;$ bin/fl-run-bench test_simple.py Simple.test_simple&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;========================================================================&lt;/span&gt;

&lt;span class="n"&gt;Benching&lt;/span&gt; &lt;span class="n"&gt;Simple&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test_simple&lt;/span&gt;

&lt;span class="o"&gt;========================================================================&lt;/span&gt;

&lt;span class="n"&gt;Access&lt;/span&gt; &lt;span class="k"&gt;our&lt;/span&gt; &lt;span class="n"&gt;Demo&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;

&lt;span class="o"&gt;------------------------------------------------------------------------&lt;/span&gt;

&lt;span class="n"&gt;Configuration&lt;/span&gt;

&lt;span class="o"&gt;=============&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Current&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2011&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;07&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="n"&gt;T12:02:35&lt;/span&gt;&lt;span class="mf"&gt;.319172&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Configuration&lt;/span&gt; &lt;span class="n"&gt;file:&lt;/span&gt; &lt;span class="sr"&gt;/home/&lt;/span&gt;&lt;span class="n"&gt;tarek&lt;/span&gt;&lt;span class="sr"&gt;/dev/&lt;/span&gt;&lt;span class="n"&gt;hg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mozilla&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/funkload-demo/&lt;/span&gt;&lt;span class="n"&gt;Simple&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Log&lt;/span&gt; &lt;span class="n"&gt;xml:&lt;/span&gt; &lt;span class="sr"&gt;/home/&lt;/span&gt;&lt;span class="n"&gt;tarek&lt;/span&gt;&lt;span class="sr"&gt;/dev/&lt;/span&gt;&lt;span class="n"&gt;hg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mozilla&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/funkload-demo/sim&lt;/span&gt;&lt;span class="n"&gt;ple&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;bench&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Server:&lt;/span&gt; &lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;localhost:5000&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Cycles:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Cycle&lt;/span&gt; &lt;span class="n"&gt;duration:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Sleeptime&lt;/span&gt; &lt;span class="n"&gt;between&lt;/span&gt; &lt;span class="n"&gt;request:&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Sleeptime&lt;/span&gt; &lt;span class="n"&gt;between&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Startup&lt;/span&gt; &lt;span class="n"&gt;delay&lt;/span&gt; &lt;span class="n"&gt;between&lt;/span&gt; &lt;span class="n"&gt;thread:&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;

&lt;span class="n"&gt;Benching&lt;/span&gt;

&lt;span class="o"&gt;========&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;setUpBench&lt;/span&gt; &lt;span class="n"&gt;hook:&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;Cycle&lt;/span&gt; &lt;span class="c1"&gt;#0 with 5 virtual users&lt;/span&gt;

&lt;span class="o"&gt;-----------------------------&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;setUpCycle&lt;/span&gt; &lt;span class="n"&gt;hook:&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Current&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2011&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;07&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="n"&gt;T12:02:35&lt;/span&gt;&lt;span class="mf"&gt;.321906&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Starting&lt;/span&gt; &lt;span class="n"&gt;threads:&lt;/span&gt; &lt;span class="o"&gt;.....&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Logging&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;until&lt;/span&gt; &lt;span class="mi"&gt;2011&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;07&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="n"&gt;T12:02:45&lt;/span&gt;&lt;span class="mf"&gt;.380090&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="o"&gt;..............................................................................................&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Waiting&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;threads:&lt;/span&gt; &lt;span class="o"&gt;.....&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Waiting&lt;/span&gt; &lt;span class="n"&gt;cycle&lt;/span&gt; &lt;span class="n"&gt;sleeptime&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;s:&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;tearDownCycle&lt;/span&gt; &lt;span class="n"&gt;hook:&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;End&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;cycle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;11.66&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;elapsed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Cycle&lt;/span&gt; &lt;span class="n"&gt;result:&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;SUCCESSFUL&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;94&lt;/span&gt; &lt;span class="n"&gt;success&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;failure&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;Cycle&lt;/span&gt; &lt;span class="c1"&gt;#1 with 10 virtual users&lt;/span&gt;

&lt;span class="o"&gt;------------------------------&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;setUpCycle&lt;/span&gt; &lt;span class="n"&gt;hook:&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Current&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2011&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;07&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="n"&gt;T12:02:46&lt;/span&gt;&lt;span class="mf"&gt;.986399&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Starting&lt;/span&gt; &lt;span class="n"&gt;threads:&lt;/span&gt; &lt;span class="o"&gt;..........&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Logging&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;until&lt;/span&gt; &lt;span class="mi"&gt;2011&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;07&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="n"&gt;T12:02:57&lt;/span&gt;&lt;span class="mf"&gt;.117788&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="o"&gt;..............................................................................................&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Waiting&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;threads:&lt;/span&gt; &lt;span class="o"&gt;..........&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Waiting&lt;/span&gt; &lt;span class="n"&gt;cycle&lt;/span&gt; &lt;span class="n"&gt;sleeptime&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;s:&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;tearDownCycle&lt;/span&gt; &lt;span class="n"&gt;hook:&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;End&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;cycle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;12.86&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;elapsed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Cycle&lt;/span&gt; &lt;span class="n"&gt;result:&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;SUCCESSFUL&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;94&lt;/span&gt; &lt;span class="n"&gt;success&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;failure&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;Cycle&lt;/span&gt; &lt;span class="c1"&gt;#2 with 20 virtual users&lt;/span&gt;

&lt;span class="o"&gt;------------------------------&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;setUpCycle&lt;/span&gt; &lt;span class="n"&gt;hook:&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Current&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2011&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;07&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="n"&gt;T12:02:59&lt;/span&gt;&lt;span class="mf"&gt;.844273&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Starting&lt;/span&gt; &lt;span class="n"&gt;threads:&lt;/span&gt; &lt;span class="o"&gt;....................&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Logging&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;until&lt;/span&gt; &lt;span class="mi"&gt;2011&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;07&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="n"&gt;T12:03:10&lt;/span&gt;&lt;span class="mf"&gt;.100680&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="o"&gt;................................................................................................&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Waiting&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;threads:&lt;/span&gt; &lt;span class="o"&gt;....................&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Waiting&lt;/span&gt; &lt;span class="n"&gt;cycle&lt;/span&gt; &lt;span class="n"&gt;sleeptime&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;s:&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;tearDownCycle&lt;/span&gt; &lt;span class="n"&gt;hook:&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;End&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;cycle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;23.18&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;elapsed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Cycle&lt;/span&gt; &lt;span class="n"&gt;result:&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;SUCCESSFUL&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;96&lt;/span&gt; &lt;span class="n"&gt;success&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;failure&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;tearDownBench&lt;/span&gt; &lt;span class="n"&gt;hook:&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;Result&lt;/span&gt;

&lt;span class="o"&gt;======&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Success:&lt;/span&gt; &lt;span class="mi"&gt;284&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Failures:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Errors:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="n"&gt;Bench&lt;/span&gt; &lt;span class="n"&gt;status:&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;SUCCESSFUL&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The script runs three cycles of respectively 5, 10 and 20 virtual
users, for 10 seconds each. This is configured with the &lt;strong&gt;cycles&lt;/strong&gt; and
&lt;strong&gt;duration&lt;/strong&gt; options in the &lt;strong&gt;&lt;em&gt;[bench]&lt;/em&gt;&lt;/strong&gt; section of the configuration
file (&lt;em&gt;cycles=5:10:20&lt;/em&gt; and &lt;em&gt;duration=10&lt;/em&gt;), but you can also provide
these options through the command line. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Let's say you want to run 2 then 5 users for 5 seconds each: &lt;br /&gt;
   $ bin/fl-run-bench --cycles=2:5 --duration=5 test_simple.py Simple.test_simple&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;========================================================================&lt;/span&gt;

&lt;span class="n"&gt;Benching&lt;/span&gt; &lt;span class="n"&gt;Simple&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test_simple&lt;/span&gt;

&lt;span class="o"&gt;========================================================================&lt;/span&gt;

&lt;span class="n"&gt;Access&lt;/span&gt; &lt;span class="k"&gt;our&lt;/span&gt; &lt;span class="n"&gt;Demo&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;

&lt;span class="o"&gt;------------------------------------------------------------------------&lt;/span&gt;

&lt;span class="n"&gt;Configuration&lt;/span&gt;

&lt;span class="o"&gt;=============&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Current&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2011&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;07&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="n"&gt;T12:07:56&lt;/span&gt;&lt;span class="mf"&gt;.294745&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Configuration&lt;/span&gt; &lt;span class="n"&gt;file:&lt;/span&gt; &lt;span class="sr"&gt;/home/&lt;/span&gt;&lt;span class="n"&gt;tarek&lt;/span&gt;&lt;span class="sr"&gt;/dev/&lt;/span&gt;&lt;span class="n"&gt;hg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mozilla&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/funkload-demo/&lt;/span&gt;&lt;span class="n"&gt;Simple&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Log&lt;/span&gt; &lt;span class="n"&gt;xml:&lt;/span&gt; &lt;span class="sr"&gt;/home/&lt;/span&gt;&lt;span class="n"&gt;tarek&lt;/span&gt;&lt;span class="sr"&gt;/dev/&lt;/span&gt;&lt;span class="n"&gt;hg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mozilla&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/funkload-demo/sim&lt;/span&gt;&lt;span class="n"&gt;ple&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;bench&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Server:&lt;/span&gt; &lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;localhost:5000&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Cycles:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Cycle&lt;/span&gt; &lt;span class="n"&gt;duration:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Sleeptime&lt;/span&gt; &lt;span class="n"&gt;between&lt;/span&gt; &lt;span class="n"&gt;request:&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Sleeptime&lt;/span&gt; &lt;span class="n"&gt;between&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Startup&lt;/span&gt; &lt;span class="n"&gt;delay&lt;/span&gt; &lt;span class="n"&gt;between&lt;/span&gt; &lt;span class="n"&gt;thread:&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;

&lt;span class="n"&gt;Benching&lt;/span&gt;

&lt;span class="o"&gt;========&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;setUpBench&lt;/span&gt; &lt;span class="n"&gt;hook:&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;Cycle&lt;/span&gt; &lt;span class="c1"&gt;#0 with 2 virtual users&lt;/span&gt;

&lt;span class="o"&gt;-----------------------------&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;setUpCycle&lt;/span&gt; &lt;span class="n"&gt;hook:&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Current&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2011&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;07&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="n"&gt;T12:07:56&lt;/span&gt;&lt;span class="mf"&gt;.297701&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Starting&lt;/span&gt; &lt;span class="n"&gt;threads:&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Logging&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;until&lt;/span&gt; &lt;span class="mi"&gt;2011&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;07&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="n"&gt;T12:08:01&lt;/span&gt;&lt;span class="mf"&gt;.322419&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="o"&gt;.............................&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Waiting&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;threads:&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Waiting&lt;/span&gt; &lt;span class="n"&gt;cycle&lt;/span&gt; &lt;span class="n"&gt;sleeptime&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;s:&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;tearDownCycle&lt;/span&gt; &lt;span class="n"&gt;hook:&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;End&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;cycle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;6.28&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;elapsed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Cycle&lt;/span&gt; &lt;span class="n"&gt;result:&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;SUCCESSFUL&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;29&lt;/span&gt; &lt;span class="n"&gt;success&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;failure&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;Cycle&lt;/span&gt; &lt;span class="c1"&gt;#1 with 5 virtual users&lt;/span&gt;

&lt;span class="o"&gt;-----------------------------&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;setUpCycle&lt;/span&gt; &lt;span class="n"&gt;hook:&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Current&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2011&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;07&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="n"&gt;T12:08:02&lt;/span&gt;&lt;span class="mf"&gt;.574411&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Starting&lt;/span&gt; &lt;span class="n"&gt;threads:&lt;/span&gt; &lt;span class="o"&gt;.....&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Logging&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;until&lt;/span&gt; &lt;span class="mi"&gt;2011&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;07&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="n"&gt;T12:08:07&lt;/span&gt;&lt;span class="mf"&gt;.637394&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="o"&gt;.............................................&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Waiting&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;threads:&lt;/span&gt; &lt;span class="o"&gt;.....&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Waiting&lt;/span&gt; &lt;span class="n"&gt;cycle&lt;/span&gt; &lt;span class="n"&gt;sleeptime&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;s:&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;tearDownCycle&lt;/span&gt; &lt;span class="n"&gt;hook:&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;End&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;cycle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;6.60&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;elapsed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Cycle&lt;/span&gt; &lt;span class="n"&gt;result:&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;SUCCESSFUL&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;45&lt;/span&gt; &lt;span class="n"&gt;success&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;failure&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;tearDownBench&lt;/span&gt; &lt;span class="n"&gt;hook:&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;Result&lt;/span&gt;

&lt;span class="o"&gt;======&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Success:&lt;/span&gt; &lt;span class="mi"&gt;74&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Failures:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Errors:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="n"&gt;Bench&lt;/span&gt; &lt;span class="n"&gt;status:&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;SUCCESSFUL&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Now let's check the reporting features.. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Reporting&lt;/h3&gt;
&lt;p&gt;Everytime you are running a bench, an XML file is produced. In our case
it's &lt;strong&gt;&lt;em&gt;simple-test.xml&lt;/em&gt;&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The XML file contains raw results and can be used to produce reports.
fl-build-report takes these XML file and produce reports out of them. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For example, you can create an HTML report with the &lt;strong&gt;&lt;em&gt;html&lt;/em&gt;&lt;/strong&gt; option.&lt;/p&gt;
&lt;p&gt;Make sure you have &lt;strong&gt;gnuplot&lt;/strong&gt; installed, then run: &lt;br /&gt;
   $ bin/fl-build-report --html --output-directory=html simple-bench.xml&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;Creating&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt; &lt;span class="n"&gt;report:&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="n"&gt;done:&lt;/span&gt;

&lt;span class="sr"&gt;/home/&lt;/span&gt;&lt;span class="n"&gt;tarek&lt;/span&gt;&lt;span class="sr"&gt;/dev/&lt;/span&gt;&lt;span class="n"&gt;hg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mozilla&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/funkload-demo/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="sr"&gt;/test_simple-20110727T120756/i&lt;/span&gt;&lt;span class="n"&gt;ndex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The result is a nice HTML page containing various diagrams, like the
number of requests per seconds depending on the number of virtual
concurrent users. &lt;br /&gt;
[&lt;img alt="image" src="http://tarekziade.files.wordpress.com/2011/07/requests_rps.png" /&gt;][] &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Another nice report is the diff report, which takes two already
generated reports, and build a diff one -- if you get some errors, make
sure you have the latest &lt;strong&gt;&lt;em&gt;gnuplot&lt;/em&gt;&lt;/strong&gt; installed. &lt;br /&gt;
   $ bin/fl-build-report -o html --diff html/test_simple-20110727T123642 html/test_simple-20110727T123718&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;Creating&lt;/span&gt; &lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="n"&gt;report&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;done:&lt;/span&gt;

&lt;span class="sr"&gt;/home/&lt;/span&gt;&lt;span class="n"&gt;tarek&lt;/span&gt;&lt;span class="sr"&gt;/dev/&lt;/span&gt;&lt;span class="n"&gt;hg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mozilla&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/funkload-demo/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="sr"&gt;/diff_simple-20110727T_123718_vs_123642/i&lt;/span&gt;&lt;span class="n"&gt;ndex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The diagram you get will provide a clear overview of the differences
between the two runs. This is useful if you want to check for speed
regression when you've changed some code. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;[&lt;img alt="image" src="http://tarekziade.files.wordpress.com/2011/07/rps_diff.png" /&gt;][] &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The trending report has the same goal, but can be built using more that
two runs: &lt;br /&gt;
   $ bin/fl-build-report -o html --trend html/*&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;Creating&lt;/span&gt; &lt;span class="n"&gt;trend&lt;/span&gt; &lt;span class="n"&gt;report&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;done:&lt;/span&gt;

&lt;span class="sr"&gt;/home/&lt;/span&gt;&lt;span class="n"&gt;tarek&lt;/span&gt;&lt;span class="sr"&gt;/dev/&lt;/span&gt;&lt;span class="n"&gt;hg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mozilla&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/funkload-demo/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="sr"&gt;/trend-report/i&lt;/span&gt;&lt;span class="n"&gt;ndex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;That's useful to see how your application is doing over time. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;[&lt;img alt="image" src="http://tarekziade.files.wordpress.com/2011/07/trend_avg.png" /&gt;][] &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In the next post we see two extra features Funkload provides: &lt;br /&gt;
-   run distributed tests
-   monitor the benched server&lt;/p&gt;
&lt;p&gt;&lt;a href="http://tarekziade.wordpress.com/2011/07/28/how-to-stress-test-your-app-using-funkload-part-2/"&gt;Go to part 2&lt;/a&gt;&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;requests_rps&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;[&lt;img alt="image" src="http://tarekziade.files.wordpress.com/2011/07/requests_rps.png" /&gt;]: http://tarekziade.files.wordpress.com/2011/07/requests_rps.png
    "rps_diff"
  [&lt;img alt="image" src="http://tarekziade.files.wordpress.com/2011/07/rps_diff.png" /&gt;]: http://tarekziade.files.wordpress.com/2011/07/rps_diff.png
    "trend_avg"
  [&lt;img alt="image" src="http://tarekziade.files.wordpress.com/2011/07/trend_avg.png" /&gt;]: http://tarekziade.files.wordpress.com/2011/07/trend_avg.png&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Wed, 27 Jul 2011 13:07:00 +0200</pubDate><guid>http://blog.ziade.org/2011/07/27/how-to-stress-test-your-app-using-funkload-part-1/</guid></item><item><title>Anatomy of a web service -- Part 3</title><link>http://blog.ziade.org/2011/07/19/anatomy-of-a-web-service-part-3/</link><description>&lt;p&gt;I am pursuing the RedBarrel experiment, mainly by writing some
documentation to show how things work. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you don't recall what I am talking about, read back the two previous
blog entries: &lt;br /&gt;
-   &lt;a href="http://tarekziade.wordpress.com/2011/06/09/anatomy-of-a-web-service"&gt;Anatomy of a Web Service -- part 1&lt;/a&gt;
-   [Anatomy of a Web Service -- part 2 &lt;br /&gt;
   ][]&lt;/p&gt;
&lt;p&gt;I added a full demo in the documentation, that explains how to write an
URL shortener Web Service with RedBarrel (don't look at the code itself
-- it's irrelevant)&lt;a href="http://redbarrel.readthedocs.org/en/latest/demo/"&gt;:
http://redbarrel.readthedocs.org/en/latest/demo/&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you happen to have some web services based on Pylons, Pyramid or
anything on the top of WebOb, I'd be very happy to try to refactor them
using RedBarrel as an experiment, to see how things work out. (not for
you to use, but just for me to exercise the idea)&lt;/p&gt;
&lt;p&gt;[Anatomy of a Web Service -- part 2 &lt;br /&gt;
 ]: http://tarekziade.wordpress.com/2011/06/17/anatomy-of-a-web-service-part-2-redbarrel/&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 19 Jul 2011 17:15:00 +0200</pubDate><guid>http://blog.ziade.org/2011/07/19/anatomy-of-a-web-service-part-3/</guid></item><item><title>Firefox Sync Server is now 100% Python</title><link>http://blog.ziade.org/2011/07/12/firefox-sync-server-is-now-100-python/</link><description>&lt;p&gt;Two weeks ago, we pushed the last bit of the Python Sync Server in
production, and there's no more PHP. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For the client-side it's not changing anything, since the Python server
is just a re-write of the existing PHP server. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Lessons learned&lt;/h3&gt;
&lt;p&gt;The first push we did of the storage part on that week went really bad
and we had to rollback urgently, fix the problems and push it back a few
days later. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The main problem we had in production was related to the MySQL driver
we used in conjunction with &lt;a href="http://gunicorn.org/"&gt;Gunicorn&lt;/a&gt; and &lt;a href="http://gevent.org/"&gt;GEvent&lt;/a&gt;. We picked
&lt;a href="http://code.google.com/p/pymysql/"&gt;PyMySQL&lt;/a&gt; because we wanted GEvent's ability to monkey patch the
socket module -- using MySQL-Python would have been useless for this
since it uses C code. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;When you use Gevent workers with GUnicorn, sockets become automatically
cooperative and you can handle more parallel requests that are waiting
for data from the SQL server. &lt;a href="http://gevent.org/intro.html#monkey-patching"&gt;Read more about this here&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;And that's exactly what the Sync server is: a thin layer of web
services on the top of a database, sending requests and waiting for the
results. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;PyMySQL was working fine in our load tests and in staging. We were
happily pushing the load and had slightly better performances than the
PHP stack.We were not expecting a huge difference since most of the time
(I'd say around 80%) is spent waiting for the SQL server and the Python
server is using the same database. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But the main difference is that the Python stack stays persistent in
memory, so we can pool connectors and avoid recreating TCP connections
for every request. I don't have any hard numbers yet, as we're
collecting them, but we've definitely reduced the time taken by our web
services in those 20% spent outside the SQL server. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;But.&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But as soon as we pushed in production, everything started to lock.
Some queries were just hanging and incoming requests were piling up
until we were unable to cope with the load. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;What happened is that PyMySQL is using &lt;strong&gt;&lt;em&gt;socket.send()&lt;/em&gt;&lt;/strong&gt; to send data
to the MySQL server, without checking that all the bytes were really
sent. And on high load, with Gevent, doing this will not work anymore
because you're not necessarily sending all bytes at once. The API to be
used is &lt;strong&gt;&lt;em&gt;send.sendall()&lt;/em&gt;&lt;/strong&gt; to make sure everything is sent. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here's an extract of the doc for &lt;strong&gt;send()&lt;/strong&gt;: &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;socket.send&lt;/code&gt;(&lt;em&gt;string&lt;/em&gt;[, &lt;em&gt;flags&lt;/em&gt;])
  ~ Send data to the socket. The socket must be connected to a remote
    socket. The optional &lt;em&gt;flags&lt;/em&gt; argument has the same meaning as for
    [&lt;code&gt;recv()&lt;/code&gt;][] above. Returns the number of bytes sent. Applications
    are responsible for checking that all data has been sent; if only
    some of the data was transmitted, the application needs to attempt
    delivery of the remaining data.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And for &lt;strong&gt;sendall()&lt;/strong&gt;: &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;socket.sendall&lt;/code&gt;(&lt;em&gt;string&lt;/em&gt;[, &lt;em&gt;flags&lt;/em&gt;])
  ~ Send data to the socket. The socket must be connected to a remote
    socket. The optional &lt;em&gt;flags&lt;/em&gt; argument has the same meaning as for
    [&lt;code&gt;recv()&lt;/code&gt;][] above. Unlike [&lt;code&gt;send()&lt;/code&gt;][], this method continues to
    send data from &lt;em&gt;string&lt;/em&gt; until either all data has been sent or an
    error occurs. &lt;code&gt;None&lt;/code&gt; is returned on success. On error, an
    exception is raised, and there is no way to determine how much
    data, if any, was successfully sent.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As soon as we've changed the code in the driver, (PyMySQL's author was
told about this, and the tip is now fixed. &lt;a href="https://bugs.launchpad.net/myconnpy/+bug/711520"&gt;Also there's the same
problem in MyConPy it seems..&lt;/a&gt;.) everything went smoothly. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So the question you're probably wondering is: why didn't we caught this
issue in our load test environment ? The reason is that our load test
script was not asserting all the responses the web server was returning,
and we did not detect those errors and the locked queries were basically
timing out in a mass of normal behavior. They "came back" as valid. The
load test infrastructure, while filled with hundreds of thousands of
fake users' data, has less databases than in production so this kind of
issue is not bubbling up as hard. While our load test infrastructure is
very realistic, it will never be exactly like production. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The other thing is that the Grinder outputs raw data and we just used
the Query Per Second indicator. I suspect we would have caught this
issue with Funkload because it provides some results diagrams were you
can see things like min and max. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So the main lessons learned here are: &lt;br /&gt;
-   make sure the load test scripts assert all the responses (status +
    content)
-   make sure your load testing tools detect any abnormal behavior --
    like a very very long request, even if it's a fraction in a mass of
    normal behavior&lt;/p&gt;
&lt;p&gt;I am very thankful to the Services Ops team, and in particular Pete who
drove the production push. These guys rock. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;What's next&lt;/h3&gt;
&lt;p&gt;Now that everything works well, there are a few things we need to tweak
in order to have a better system: &lt;br /&gt;
-   &lt;a href="https://bugzilla.mozilla.org/show_bug.cgi?id=668664"&gt;Kill pending queries when a Gunicorn worker is restarted&lt;/a&gt;
-   See if we can cache a few LDAP calls
-   See if we can use several GUnicorn servers behind one Nginx -- the
    CPU is under-used.&lt;/p&gt;
&lt;p&gt;But overall, I hereby declare the Python push as a success.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;socket.socket.recv&amp;quot;&lt;/span&gt;
&lt;span class="s"&gt;&amp;quot;socket.socket.send&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 12 Jul 2011 12:36:00 +0200</pubDate><guid>http://blog.ziade.org/2011/07/12/firefox-sync-server-is-now-100-python/</guid></item><item><title>Mozilla Services - Year 1 !</title><link>http://blog.ziade.org/2011/06/21/mozilla-services-year-1/</link><description>&lt;p&gt;What’s this ? read &lt;a href="http://tarekziade.wordpress.com/2010/11/30/rsync-mozillaservices-community-week-47/"&gt;this post&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;What happened&lt;/h3&gt;
&lt;p&gt;Time flies... This is a special entry because today it's been exactly
one year since I've joined Mozilla. So I'll try to summarize what I've
done during the past year, what worked well, what needs more work. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I was the first &lt;em&gt;"Python guy"&lt;/em&gt; hired in the Services team. Since the
plan was (and still is) to do all future server-side work in Python,
this gave me the opportunity to set up the basis for our eco-system.
Since then, more Python devs have joined the team and others have gained
some experience in the language, and we're now all Python guys ;) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I&lt;strong&gt; created a micro-framework&lt;/strong&gt; for all our server-side applications.
It's a thin layer on the top of WebOb + Routes, with some specific
features we need (like a hearbeat page for all our apps). Nothing fancy
but it makes it easy for someone to start a Server application sharing a
common standard. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I&lt;strong&gt; re-wrote all the PHP code&lt;/strong&gt; that is needed to run a Sync server
(Registration + Storage) in Python, using the micro-framework. This was
done months ago, and both the core and the apps have evolved since then,
following the changes that were made in the PHP parts. Pushing the work
in production was the hardest part because it supposes a lot of
coordination between Devs and Ops. And Ops had zillions of stuff to do
for the Firefox 4 launch, so we missed that launch. Since then, the
Registration part was pushed, and we're about to push the Storage part.&lt;/p&gt;
&lt;p&gt;I wrote the &lt;strong&gt;Easy Setup server&lt;/strong&gt;, that is used for example when you
set up your Android phone's Sync account by transferring your desktop
Sync profile. This was really fun because it was the first project I did
from scratch. I worked on the design with Phillip and Stefan and I
really enjoyed it. A small, fun and interesting project. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I added a &lt;strong&gt;IP blacklisting middleware&lt;/strong&gt; into the Easy Setup server,
that rejects calls from an IP if it has done too many attempts in a
short time window. This work led to a new project that is currently
under development by Ryan: a blacklisting server that can work across
several applications. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I've set up a &lt;strong&gt;documentation center&lt;/strong&gt; at
&lt;a href="http://docs.services.mozilla.com/"&gt;http://docs.services.mozilla.com/&lt;/a&gt;that is built using Sphinx. The
sweet thing about it is that its content lives in a &lt;a href="https://hg.mozilla.org/services/docs"&gt;Mercurial
repository&lt;/a&gt;, so developers can change the documentation directly. The
goal of this website is to provide a single place for all our APIs
specifications, and our development guidelines, with a minimal overlap
with sites like MDN etc. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I've created &lt;a href="http://pypi.python.org/pypi/pypi2rpm"&gt;pypi2rpm&lt;/a&gt; and we worked with Richard S. on deploying
our Python applications using it. The tool is built on the new packaging
tool I am working on for Python and let you create RPMs files for our
projects. It's a mix of a PyPI crawler, a version sorter and a RPM
creator. We've created a release process based on this:
&lt;a href="http://docs.services.mozilla.com/server-devguide/release.html"&gt;http://docs.services.mozilla.com/server-devguide/release.html&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I've set up a &lt;strong&gt;Jenkins server&lt;/strong&gt; for our server projects. Can't link it
because it currently leaves in our intranet. The Jenkins server does the
following for every server application: &lt;br /&gt;
-   builds it
-   runs its tests
-   build all its RPMs under Centos5 and Rhel6
-   Install all the RPMs under a Centos5 chroot and check that there's
    no dependency error&lt;/p&gt;
&lt;p&gt;I've set up a &lt;strong&gt;PyPI Mirror&lt;/strong&gt; in our intranet so Jenkins (or someone
creating RPMs) does not rely on any external resource. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Also: we're having regular MoPy meetings now at Mozilla \o/ &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;What's next&lt;/h3&gt;
&lt;p&gt;During the next months we will do a lot of work in the Sync
application(s) to support new features and optimize existing ones. We'll
also start new projects. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For the tooling/framework parts, here are a few paths where I'd like to
see us going. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Continuous load testing&lt;/strong&gt;: one goal I have is to make it possible for
anyone to run a load test on one of our applications via Jenkins, on a
given stage or dev server. Then see the results, compare them with some
previous runs etc. The second goal is to make load tests an easy thing
for developers to add early in a project. Writing load tests makes your
application faster, just because you focus on it, that's a fact. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I've made good progress in this topic and some stuff are going to land
into Funkload itself to make it easier to automate load tests and
reports generation. The next task I have is to work with other teams
involved with Services, and make sure their use cases in this area are
provided by the tools. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A standard for our API specifications&lt;/strong&gt;. This is a personal project I
have, but it is directly inspired by our work at Services. I want to
reduce the path from a server API specification to its implementation.
For this &lt;a href="https://tarekziade.wordpress.com/2011/06/17/anatomy-of-a-web-service-part-2-redbarrel/"&gt;I am working on a DSL&lt;/a&gt; that can be used to describe some
APIs, produce their documentation and eventually, be used to run a
server. If my experiment goes well, I'll introduce it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cross-team code review&lt;/strong&gt;. Inspired by Jeff and some other people
during a MoPy meeting, I started to write a small web site that could be
used by a dev to ask for code reviews from people outside its team. The
idea is that the website tries to find automatically someone that has
the required skill to do the review. A review will cost you some credits
and by doing a review you'll earn some. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;On working remotely&lt;/h3&gt;
&lt;p&gt;I am a big fan of remote working. I am doing it for years. It works
quite well within the Services team, and half of the team is doing it.
The one thing that's hard to manage is the timezones: I am the only guy
in Europe and I am 9 hours in front of Mountain View. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This means that if I need to work &amp;amp; chat with my colleagues, I have to
do it on the evenings. Also, some events have to be scheduled on the PDT
timezone. For instance, production pushes are starting at midnight for
me. I had to adapt a bit my schedule for this, it was hard at first but
now it's working well. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But... I would really love to see some more people in my team hired in
Europe, so I can speak with them on daytime. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;As a conclusion for year 1, I love my job at Mozilla. I'm working with
a lot of smart guys and we get things done, mostly because everyone in
this team is passionate. I a looking forward for year 2.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 21 Jun 2011 16:13:00 +0200</pubDate><guid>http://blog.ziade.org/2011/06/21/mozilla-services-year-1/</guid></item><item><title>Anatomy of a Web Service, part 2 - &amp;quot;RedBarrel&amp;quot;</title><link>http://blog.ziade.org/2011/06/17/anatomy-of-a-web-service-part-2-quotredbarrelquot/</link><description>&lt;p&gt;&lt;em&gt;I was talking about web services the other day: &lt;a href="http://tarekziade.wordpress.com/2011/06/09/anatomy-of-a-web-service/"&gt;read it back&lt;/a&gt; as an
introduction to this post.&lt;/em&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I am pursuing this DSL experiment as I have now finished a working
prototype of a micro-framework. I've called it RedBarrel (&lt;a href="http://orangecow.org/pythonet/sketches/package.htm"&gt;Monty
reference&lt;/a&gt;). I've called the DSL files "RBR files". &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;RedBarrel is a pure Python implementation of the DSL I've described in
the previous post and does the following: &lt;br /&gt;
-   loads the DSL file and run a WSGI web application (via &lt;strong&gt;&lt;em&gt;rb-run&lt;/em&gt;&lt;/strong&gt;)
-   Allows you to check the syntax of an RBR file (via &lt;strong&gt;&lt;em&gt;rb-check&lt;/em&gt;&lt;/strong&gt;)
-   generates a documentation page for the APIs at &lt;strong&gt;/__doc__ &lt;/strong&gt;Note
    that &lt;strong&gt;&lt;em&gt;description&lt;/em&gt;&lt;/strong&gt; fields can be in reStructuredtext and are
    rendered in HTML
-   publishes the DSL file at &lt;strong&gt;&lt;em&gt;/__api__&lt;/em&gt;&lt;/strong&gt;
-   runs the code pointed in the DSL and does the post- and pre-
    processing as described&lt;/p&gt;
&lt;p&gt;Here's an example of a web service, that capitalizes the string you
sent to it -- and requires authentication and json input. &lt;br /&gt;
   define path capitalize (&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;    &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;A web service with several post/pre processing&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="n"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;capitalize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="n"&gt;python:redbarrel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;demos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;capitalize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;

        &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Success&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

        &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;The request is probably malformed&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

        &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="mi"&gt;401&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Authentication failure&amp;quot;&lt;/span&gt;

    &lt;span class="p"&gt;),&lt;/span&gt;

    &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;

        &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Send a string in json and the server returns it Capitalized.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

        &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;

    &lt;span class="p"&gt;),&lt;/span&gt;

    &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;

        &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;Authorization&lt;/span&gt; &lt;span class="n"&gt;validates&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;redbarrel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;demos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;401&lt;/span&gt;

    &lt;span class="p"&gt;),&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;

        &lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;application/json&amp;quot;&lt;/span&gt;

    &lt;span class="p"&gt;),&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;

        &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;The string, Capitalized !&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

        &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;503&lt;/span&gt;

    &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;[caption id="attachment_1862" align="alignleft" width="542"
caption="The /__doc__ page in RedBarrel"][&lt;img alt="image" src="http://tarekziade.files.wordpress.com/2011/06/barrel.png" /&gt;][][/caption] &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The one thing I am not entirely sure about yet, is if I want to provide
helpers to instantiate some objects in memory when the server starts.
That's useful when you want to keep a DB connector open or simply avoid
initializing many things on every request. But that's very easy to
implement with global variables... Maybe an "application context" could
be created, for some functions to add objects inside. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Anyways, I've started to re-write a few web services to see how the DSL
fits, and so far it looks useful: I have reduced a lot the boiler-plate
code and the API is self-documented. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The code is &lt;em&gt;still&lt;/em&gt; at &lt;a href="https://bitbucket.org/tarek/redbarrel"&gt;Bitbucket&lt;/a&gt; and I am looking for some feedback
or other people that are writing web services that want to experiment
with it !&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;The /__doc__ page in RedBarrel&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;[&lt;img alt="image" src="http://tarekziade.files.wordpress.com/2011/06/barrel.png" /&gt;]: http://tarekziade.files.wordpress.com/2011/06/barrel.png&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 17 Jun 2011 15:14:00 +0200</pubDate><guid>http://blog.ziade.org/2011/06/17/anatomy-of-a-web-service-part-2-quotredbarrelquot/</guid></item><item><title>Continuous Load Testing wint Funkload</title><link>http://blog.ziade.org/2011/06/10/continuous-load-testing-wint-funkload/</link><description>&lt;p&gt;There's one thing we're often neglect when we build applications:
&lt;strong&gt;performance trending&lt;/strong&gt;. In other words, are things getting slower over
time because the code base grows, or because someone introduced a&lt;em&gt; O(n)&lt;/em&gt;
piece of code somewhere ? &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In very big projects this is a major concern: the code grows and the
performances are steadily decreasing. I read somewhere that this became
a problem in the Linux Kernel at some point. Can't find a good link on
this. If you know about this story, please let me know. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In smaller base code, it's still important to watch, and by following
the changes you can detect such issues. But you can't focus all the time
on everything. That's why doing it automatically and continuously is
important. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For our Services at Mozilla, we're benching our APIs with stress tests,
but what we miss is a bit of automation, so we can do &lt;strong&gt;&lt;em&gt;continuous load
testing&lt;/em&gt;&lt;/strong&gt; and keep a history of our performances. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Some examples: &lt;br /&gt;
-   I want to see after a new feature has been introduced, if my RPS
    average on my API remains the same.
-   When some indexes are changed in the Database, what's the impact on
    the overall performances ?&lt;/p&gt;
&lt;p&gt;Getting good statistics from continuous load testing requires a stable
environment with realistic data, so the trend means something, and
that's hard to keep over long periods of time. But it's manageable for
shorter periods, like weeks, I guess. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Funkload trending&lt;/h3&gt;
&lt;p&gt;I've started to work on this topic this week, to try to set up some
automation in our stress tests, and I've the tool it takes to do this
easily: &lt;a href="http://funkload.nuxeo.org"&gt;Funkload&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;[caption id="" align="alignright" width="531" caption="Disclaimer:
These are not Firefox Sync performances, just a sample from Funkload
docs ;)"]&lt;img alt="image" src="http://funkload.nuxeo.org/report-example/trend-report/trend_spps.png" /&gt;[/caption] &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Everytime you run a Funkload stress test, it produces its results in an
XML file you can store. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Funkload then provides a script to generate reports for every bench
your run, but is also able to produce trend reports, using Gnuplot. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So what I've started to do is: &lt;br /&gt;
-   run Funkload via Jenkins against the server that has the deployed
    APIs
-   collect all produced XML files
-   refresh and publish the trend report&lt;/p&gt;
&lt;p&gt;Grinder is nice, and I could do the same thing with the raw data. But
why bother, Funkload does already all the reporting needed \o/&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;Trend&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 10 Jun 2011 13:08:00 +0200</pubDate><guid>http://blog.ziade.org/2011/06/10/continuous-load-testing-wint-funkload/</guid></item><item><title>Anatomy of a Web Service</title><link>http://blog.ziade.org/2011/06/09/anatomy-of-a-web-service/</link><description>&lt;p&gt;This is &lt;a href="https://tarekziade.wordpress.com/2011/02/14/a-light-description-language-for-rest-web-services/"&gt;cycling in my head&lt;/a&gt; for a while now, and I think it's close
to become something concrete. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Let me summarize the idea: web services are most of the time doing the
same post- and pre-processing tasks over and over and there should be a
way to describe them via a DSL. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Nothing revolutionary here, but what if Nginx could handle for you all
the boring parts and let you just handle the meat of your services.
Having a DSL to describe web services potentially allows such
delegation. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Anatomy of a Web Service&lt;/h3&gt;
&lt;p&gt;A Web Service is basically doing these four steps: &lt;br /&gt;
1.  [&lt;strong&gt;pre-processing&lt;/strong&gt;] Check the request body and headers, and
    potentially reject it. Rejection can be due to a Basic
    Authentication failure, an unexpected value for the request body,
    etc.
2.  [&lt;strong&gt;routing&lt;/strong&gt;] Find what code or application should be called to
    build the response. This is usually computed with the path
    information and sometimes some headers.
3.  [&lt;strong&gt;execution&lt;/strong&gt;] Invoke the code to build the response
4.  [&lt;strong&gt;post-processing&lt;/strong&gt;] Return the response built and maybe do some
    post-processing or post-assertions like converting the content-type
    etc.&lt;/p&gt;
&lt;p&gt;Steps &lt;strong&gt;1.&lt;/strong&gt;, &lt;strong&gt;2.&lt;/strong&gt; and &lt;strong&gt;4&lt;/strong&gt;. could be delegated to a proxy as long
as it has enough details on what should be done. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In Python, when you build web services using a WSGI framework like
Pylons, Pyramid, or simply Routes + WebOb, all of these steps happen in
your code. You define the &lt;strong&gt;routing&lt;/strong&gt; using Routes descriptions, or
using more clever dispatching systems like what Pyramid offers, then
delegate the &lt;strong&gt;execution&lt;/strong&gt; to a controller class or a simple function,
after a potential &lt;strong&gt;pre-processing&lt;/strong&gt;. Although the pre-processing part
is often merged with the execution part because they are closely
related. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For instance, if you have a web service that requires a JSON mapping in
the request body, you could write something that looks like: &lt;br /&gt;
   def my_webservice(request):&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;try:&lt;/span&gt;

&lt;span class="err"&gt;       &lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;except&lt;/span&gt; &lt;span class="n"&gt;ValueError:&lt;/span&gt;

&lt;span class="err"&gt;       &lt;/span&gt; &lt;span class="n"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;HTTPBadRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Unknown format -- we want JSON&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;  &lt;/span&gt; &lt;span class="c1"&gt;# this raises a 400&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="n"&gt;something&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Of course you can always generalize this by using a decorator to
clearly separate the pre-processing part: &lt;br /&gt;
   @if_not_json(400)&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;my_webservice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="n"&gt;something&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Same thing for the post-processing step: &lt;br /&gt;
   @if_not_json(400)&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;@convert_output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;application/json&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;my_webservice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="n"&gt;something&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Err... well, in some &lt;a href="http://bottlepy.org/docs/dev/"&gt;frameworks&lt;/a&gt;, the routing itself is expressed as
a decorator: &lt;br /&gt;
   @route('/here/is/my/webservice')&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;@if_not_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;@convert_output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;application/json&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;my_webservice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="n"&gt;something&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;It turns out that there are a lot of pre/post steps that can be pushed
to a meta level. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Delegation of pre- and post-processing steps&lt;/h3&gt;
&lt;p&gt;A web application is most of the time accessed through a proxy. At
Mozilla Services, &lt;a href="http://docs.services.mozilla.com/server-devguide/overview.html"&gt;we use Nginx for all our Python applications&lt;/a&gt;.
NGinx is here --among other things-- to pool incoming requests and
dispatch them to our Python application. The proxying job is pretty dumb
right now, as everything that comes in is directly sent to the Python
backend. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;What if we were able to delegate all the pre- and post-processing we've
seen earlier to NGinx ? &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;There would be some benefits, like a faster rejection of bad requests:
no need to invoke the Python application anymore and spend CPU cycles in
a backend for this. If some pre-requisites are not met, we can 400 right
away. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Having all the pre-processing at the proxy level also make it simpler
to modify them without touching the web service code itself. That can be
a default as well of course in some cases : your application logic is
split in two parts and this can be hard to follow. But as long as the
full description of the web service is in a single place, I think it's
fine. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Last, we've talked about Python, but each piece could be implemented in
a different language, as long as NGinx is able to invoke it. Using Lua
for all the pre-processing part is not a bad idea.. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;The DSL&lt;/h3&gt;
&lt;p&gt;The last time I've talked about this topic, someone talked about
&lt;a href="http://lumberjaph.net/misc/2010/09/17/spore.html"&gt;SPORE&lt;/a&gt; which is indeed quite similar to what I want to achieve. I
guess the biggest difference is that SPORE focuses on providing a DSL to
build clients that can interact with an existing set of server APIs. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;What I want on my side is to provide a DSL API developers can use to
create web services, and eventually have a proxy like NGinx use it to
run the application. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;A developer ideally would: &lt;br /&gt;
1.  describe her web services in a DSL file
2.  implement the execution part
3.  test them in a development environment where a web server would load
    the DSL and the code
4.  deploy the web service in production with NGinx&lt;/p&gt;
&lt;p&gt;I could start off with SPORE but I want to experiment with my own DSL
and build it little by little. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I started to build it the other week-end, and it looks like this: &lt;br /&gt;
   define path hello (&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Simplest app&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="n"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="n"&gt;python:demos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hello&lt;/span&gt;

&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;define&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;A counter&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="n"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="n"&gt;python:demos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;counter&lt;/span&gt;

&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;define&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;An html page&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="n"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;index&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="n"&gt;python:demos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;

&lt;span class="err"&gt;       &lt;/span&gt; &lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;text-html&amp;quot;&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;define&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;A web service that 400 if the body is not json&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="n"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="n"&gt;python:demos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;

&lt;span class="err"&gt;       &lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Some details: &lt;br /&gt;
-   &lt;em&gt;python:demo.post&lt;/em&gt; means here: the code to be invoked is located in
    the "demo.post" callable - that's the fully qualified name to reach
    it, so demo can be a package, or a module.
-   &lt;em&gt;unless type is xxx return xxx&lt;/em&gt; is a full part of the DSL, a
    recognized structure. When parsing the file, it's loaded in an AST
    and executed on each request against the body.&lt;/p&gt;
&lt;h3&gt;Implementation details&lt;/h3&gt;
&lt;p&gt;I used &lt;a href="http://www.dabeaz.com/ply/ply.html"&gt;PLY&lt;/a&gt; to read the DSL files, and it'll check many aspects of
the DSL file like: &lt;br /&gt;
-   make sure the method is known (GET/POST/etc)
-   make sure the HTTP codes used are valid ones
-   control that the URL path is valid
-   etc.&lt;/p&gt;
&lt;p&gt;I wrote a small Python application that loads the DSL file at startup
in an AST. Then it provides a web server that will do the
post/pre-processing, and eventually delegate the execution to some
Python code, by passing a request object using WebOb. The URL is for now
using a simple regexp pattern backed by Routes. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I've also wrote: &lt;br /&gt;
-   a script that validates a DSL file
-   a __doc__ web page in the small Python server, that displays the
    available web services
-   an __api__ page that just publish the DSL file for client-side
    discovery. (fwiw)&lt;/p&gt;
&lt;p&gt;You can have a look at the ugly code of the prototype here:
&lt;a href="https://bitbucket.org/tarek/redbarrel"&gt;https://bitbucket.org/tarek/redbarrel&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The next steps in this experiment will be to rewrite one of our small
Services app with it, and see how it comes out. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;All in all, I am having a lot of fun doing this, and it's eating some
of the free time I have when I should be really doing some work on
Python packaging... meh ...&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 09 Jun 2011 09:34:00 +0200</pubDate><guid>http://blog.ziade.org/2011/06/09/anatomy-of-a-web-service/</guid></item><item><title>Help us ironing Packaging</title><link>http://blog.ziade.org/2011/06/02/help-us-ironing-packaging/</link><description>&lt;p&gt;&lt;strong&gt;packaging&lt;/strong&gt; has landed &lt;a href="http://hg.python.org/cpython/file/3dbf4c9b3ed4/Lib/packaging"&gt;in the standard library&lt;/a&gt;, but the road to
Python 3.3 is still filled with a lot of work. We've pushed the
Documentation yesterday in the tip, and it now appears here:
&lt;a href="http://docs.python.org/dev/packaging/"&gt;http://docs.python.org/dev/packaging/&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;There are a lot of stuff you can do to help us improving packaging. If
you wish to help out, read up. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;1. Install a Python 3 development environment&lt;/h3&gt;
&lt;p&gt;The first step is to install a Python development environment &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;There's a full dev guide here: &lt;a href="http://docs.python.org/devguide/"&gt;http://docs.python.org/devguide/&lt;/a&gt; but
it basically boils down to run make on the tip: &lt;br /&gt;
   $ hg clone https://hg.python.org/cpython     (very long)&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;cd&lt;/span&gt; &lt;span class="n"&gt;cpython&lt;/span&gt;

&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="err"&gt;./&lt;/span&gt;&lt;span class="nv"&gt;configure&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;make&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Once this is done, you'll have a Python interpreter you can run: &lt;br /&gt;
   $ ./python&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;Python&lt;/span&gt; &lt;span class="mf"&gt;3.3&lt;/span&gt;&lt;span class="n"&gt;a0&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default:94066c3e2236&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;May&lt;/span&gt; &lt;span class="mi"&gt;31&lt;/span&gt; &lt;span class="mi"&gt;2011&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;08&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;29&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;53&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;GCC&lt;/span&gt; &lt;span class="mf"&gt;4.5.2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;linux2&lt;/span&gt;

&lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;help&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;copyright&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;credits&amp;quot;&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;license&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;more&lt;/span&gt; &lt;span class="n"&gt;information&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Python 3, yay !&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;Python&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;yay&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;2. Try out the pysetup script, as an end-user&lt;/h3&gt;
&lt;p&gt;This script is a global script people will be able to use to check
what's installed on a Python installation, to install things, remove
them, etc. The script has still a lot of rough edges, which is a shame
since it's just the tip of a feature-rich system. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It's located in Tools/script/pysetup3 in a dev environment, and here's
a demonstration of how to install the lastest Mako release, check that
it's installed, look at some of its metadata, then remove it: &lt;br /&gt;
   $ sudo ./python Tools/scripts/pysetup3 install Mako&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;Checking&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;installation&lt;/span&gt; &lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="n"&gt;Getting&lt;/span&gt; &lt;span class="n"&gt;information&lt;/span&gt; &lt;span class="n"&gt;about&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Mako&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="n"&gt;Installing&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;mako&amp;#39;&lt;/span&gt; &lt;span class="mf"&gt;0.4.1&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;lots&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="err"&gt;./&lt;/span&gt;&lt;span class="nv"&gt;python&lt;/span&gt; &lt;span class="n"&gt;Tools&lt;/span&gt;&lt;span class="sr"&gt;/scripts/&lt;/span&gt;&lt;span class="n"&gt;pysetup3&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;

&lt;span class="n"&gt;SQLAlchemy&lt;/span&gt; &lt;span class="mf"&gt;0.7.0&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="sr"&gt;/usr/&lt;/span&gt;&lt;span class="nb"&gt;local&lt;/span&gt;&lt;span class="sr"&gt;/lib/&lt;/span&gt;&lt;span class="n"&gt;python3&lt;/span&gt;&lt;span class="mf"&gt;.3&lt;/span&gt;&lt;span class="sr"&gt;/site-packages/&lt;/span&gt;&lt;span class="n"&gt;SQLAlchemy&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.7.0&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;py3&lt;/span&gt;&lt;span class="mf"&gt;.3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dist&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;

&lt;span class="n"&gt;distribute&lt;/span&gt; &lt;span class="mf"&gt;0.6.17&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="sr"&gt;/usr/&lt;/span&gt;&lt;span class="nb"&gt;local&lt;/span&gt;&lt;span class="sr"&gt;/lib/&lt;/span&gt;&lt;span class="n"&gt;python3&lt;/span&gt;&lt;span class="mf"&gt;.3&lt;/span&gt;&lt;span class="sr"&gt;/site-packages/&lt;/span&gt;&lt;span class="n"&gt;distribute&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.6.17&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;py3&lt;/span&gt;&lt;span class="mf"&gt;.3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dist&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;

&lt;span class="n"&gt;Mako&lt;/span&gt; &lt;span class="mf"&gt;0.4.1&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="sr"&gt;/usr/&lt;/span&gt;&lt;span class="nb"&gt;local&lt;/span&gt;&lt;span class="sr"&gt;/lib/&lt;/span&gt;&lt;span class="n"&gt;python3&lt;/span&gt;&lt;span class="mf"&gt;.3&lt;/span&gt;&lt;span class="sr"&gt;/site-packages/&lt;/span&gt;&lt;span class="n"&gt;Mako&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.4.1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;py3&lt;/span&gt;&lt;span class="mf"&gt;.3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dist&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;

&lt;span class="n"&gt;Found&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;projects&lt;/span&gt; &lt;span class="n"&gt;installed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="err"&gt;./&lt;/span&gt;&lt;span class="nv"&gt;python&lt;/span&gt; &lt;span class="n"&gt;Tools&lt;/span&gt;&lt;span class="sr"&gt;/scripts/&lt;/span&gt;&lt;span class="n"&gt;pysetup3&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt; &lt;span class="n"&gt;Mako&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;Version&lt;/span&gt;

&lt;span class="n"&gt;Version:&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mf"&gt;0.4.1&lt;/span&gt;

&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="err"&gt;./&lt;/span&gt;&lt;span class="nv"&gt;python&lt;/span&gt; &lt;span class="n"&gt;Tools&lt;/span&gt;&lt;span class="sr"&gt;/scripts/&lt;/span&gt;&lt;span class="n"&gt;pysetup3&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt; &lt;span class="n"&gt;Mako&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;Author&lt;/span&gt;

&lt;span class="n"&gt;Author:&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;Mike&lt;/span&gt; &lt;span class="n"&gt;Bayer&lt;/span&gt;

&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;sudo&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="sr"&gt;/python Tools/sc&lt;/span&gt;&lt;span class="n"&gt;ripts&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;pysetup3&lt;/span&gt; &lt;span class="n"&gt;remove&lt;/span&gt; &lt;span class="n"&gt;Mako&lt;/span&gt;

&lt;span class="n"&gt;Removing&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Mako&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="sr"&gt;/usr/&lt;/span&gt;&lt;span class="nb"&gt;local&lt;/span&gt;&lt;span class="sr"&gt;/lib/&lt;/span&gt;&lt;span class="n"&gt;python3&lt;/span&gt;&lt;span class="mf"&gt;.3&lt;/span&gt;&lt;span class="sr"&gt;/site-packages/m&lt;/span&gt;&lt;span class="n"&gt;ako&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;parsetree&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;

&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;lots&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="sr"&gt;/usr/&lt;/span&gt;&lt;span class="nb"&gt;local&lt;/span&gt;&lt;span class="sr"&gt;/lib/&lt;/span&gt;&lt;span class="n"&gt;python3&lt;/span&gt;&lt;span class="mf"&gt;.3&lt;/span&gt;&lt;span class="sr"&gt;/site-packages/&lt;/span&gt;&lt;span class="n"&gt;Mako&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.4.1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;py3&lt;/span&gt;&lt;span class="mf"&gt;.3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dist&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;RECORD&lt;/span&gt;

&lt;span class="n"&gt;Proceed&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)?&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;

&lt;span class="n"&gt;Success:&lt;/span&gt; &lt;span class="n"&gt;removed&lt;/span&gt; &lt;span class="mi"&gt;52&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="n"&gt;dirs&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;So go ahead, play with this script, discover its features and: &lt;br /&gt;
-   tell us what feels wrong
-   tell us what kind of features you wish you had in this script
-   found a bug, have a patch, tell us !&lt;/p&gt;
&lt;h3&gt;3. Make your project packaging-ready, as a developer&lt;/h3&gt;
&lt;p&gt;The sweet thing is that adding packaging support in your project is
risk-free because it's just adding a few sections in your setup.cfg
file. setup.py can stick around, and older installers will still pick it
up. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So, here is how you can do: You can tollow the tutorial if you want to
do something from
scratch:&lt;a href="http://docs.python.org/dev/packaging/tutorial.html"&gt;http://docs.python.org/dev/packaging/tutorial.html&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Or you can use the magic create command in your Project root directory,
to create a setup,cfg out of your setup.py file ! &lt;br /&gt;
   $ sudo ./python Tools/scripts/pysetup3 create&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="n"&gt;legacy&lt;/span&gt; &lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; &lt;span class="n"&gt;has&lt;/span&gt; &lt;span class="n"&gt;been&lt;/span&gt; &lt;span class="n"&gt;found&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;Would&lt;/span&gt; &lt;span class="n"&gt;you&lt;/span&gt; &lt;span class="n"&gt;like&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;convert&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;

&lt;span class="n"&gt;Wrote&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;setup.cfg&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;If you're starting your project from scratch You can also generate a&lt;em&gt;
setup.py&lt;/em&gt; that will extract the options out of setup.cfg. Very handy to
provide backward compatibility and avoid maintaining two files ! &lt;br /&gt;
   $ sudo ./python Tools/scripts/pysetup3 generate-setup&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; &lt;span class="n"&gt;was&lt;/span&gt; &lt;span class="n"&gt;generated&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;So go ahead, learn how setup,cfg works, reads its specs at
&lt;a href="http://docs.python.org/dev/packaging/setupcfg.html"&gt;http://docs.python.org/dev/packaging/setupcfg.html&lt;/a&gt; and: &lt;br /&gt;
-   tell us what feels wrong
-   tell us what kind of features you wish you had in this file
-   found a bug, have a patch, tell us !
-   tell us if you were unable to convert your project&lt;/p&gt;
&lt;p&gt;Once the project is packaging ready, you can even register and upload a
new version of it at PyPI and check that pysetup knows how to install it&lt;/p&gt;
&lt;h3&gt;4. Give use some feedback&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;You can add new bugs/feature requests at&lt;a href="http://bugs.python.org/"&gt;http://bugs.python.org/&lt;/a&gt;
    under the Distutils2 component, that will be really really helpful.&lt;/li&gt;
&lt;li&gt;You can tell us what's weird with our documentation, what misses,
    etc. That goes to the Documentation+Distutils2 components&lt;/li&gt;
&lt;/ul&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 02 Jun 2011 12:23:00 +0200</pubDate><guid>http://blog.ziade.org/2011/06/02/help-us-ironing-packaging/</guid></item><item><title>Mozilla Services - Week 22</title><link>http://blog.ziade.org/2011/05/31/mozilla-services-week-22/</link><description>&lt;p&gt;What's this ? read &lt;a href="http://tarekziade.wordpress.com/2010/11/30/rsync-mozillaservices-community-week-47/"&gt;this post&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;h2&gt;What happened&lt;/h2&gt;
&lt;p&gt;I am getting ready for an important push today, that will switch some
services under Python. Things are looking good. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;On a side note: my fishes kept on dying in my aquarium and I finally
found the problem by having the water analyzed. The&lt;a href="http://fr.wikipedia.org/wiki/Oze_(rivière)"&gt;nice little river&lt;/a&gt;
in my small town (175 people), that looks so clean, is basically
saturated with Nitrates. I used it for my aquarium because I did not
want to use tap water combined with some products to remove the
chlorine. I thought I was helping out those poor fishes with nice river
water, but I was killing them. This is insane. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Cross-team reviews&lt;/h3&gt;
&lt;p&gt;During the previous summit, we discuss at the MoPy meeting an
interesting idea. What if people could ask for a code review from anyone
from another team, that has the skills to do the review. I first thought
about doing some kind of plugin for Bugzilla and have for every
registered user a set of skillx, then propose a reviewer in the patch
UI. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But that means people need to use the Bugzilla review process, and
sometimes they use something else. I also wanted this review to be just
an extra review with a low-commitment from the reviewers. In Bugzilla,
if you are asked to review something, it will stay there waiting for
your review for ever even if you don't review it. I don't think there's
a way to &lt;em&gt;timeout&lt;/em&gt; a review. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Last, cross-team review could be something broader than Mozilla teams.
What about getting a review from someone in another Python project ? &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Anyways, I started "Bug Brothers", a prototype to do this. There's a
demo running here: &lt;a href="http://bugbro.ziade.org/"&gt;http://bugbro.ziade.org&lt;/a&gt; and the code is here:
&lt;a href="https://bitbucket.org/tarek/bugbro"&gt;https://bitbucket.org/tarek/bugbro&lt;/a&gt;. This was a good opportunity to
try Pyramid, and yeah &amp;lt;blush&gt; no tests and the code is not very clean.&lt;/p&gt;
&lt;p&gt;It's not finished but it already allows people to ask for reviews,
provide a link to a diff. When you review something you get credits, and
when you ask for a review you pay credits. Everything is email-driven. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The next steps are to add more features like &lt;a href="http://code.google.com/p/rietveld/"&gt;Rietveld&lt;/a&gt;. a tighter
integration to Bugzilla, github. etc -- but without introducing a
dependency to any tool so it can work for every team. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Mozilla Pulse&lt;/h3&gt;
&lt;p&gt;Coming from the Plone/Zope/Python world, I miss my checkins mailing
lists in Mozilla projects. That is, getting a mail everytime a commit is
done in one of the projects you work on. You can always read the Atom
feeds in the various Mercurial repos, but that's not the same. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;What I want is a diff in a mail can quickly look at. This is very
useful to get instant reviews from other people. You usually catch more
typos or mistakes. It also help initiating coding discussions. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Christian Legnitto has started the &lt;a href="http://pulse.mozilla.org/"&gt;Mozilla Pulse&lt;/a&gt; project, which is
exactly what I needed: a way to get notified on every change in the
Mozilla eco-system. I was waiting for Pulse to get hooked in all our
repos and this is now done. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The script to send e-mails on commits is very simple:
&lt;a href="https://bitbucket.org/tarek/services-pulse/src/tip/consumer.py"&gt;https://bitbucket.org/tarek/services-pulse/src/tip/consumer.py&lt;/a&gt;. I
need to add a diff view in the e-mail and a few options, but that's
basically it. For now, it keeps only events happening in
hg.mozila.org/services, and it will send e-mail to our services mailing
list. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Overall, Pulse is a good way for anyone to watch a particular area in
the Mozilla project &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Stop guessing encodings&lt;/h3&gt;
&lt;p&gt;We had a bug in our Services code, related to a password containing an
non-ascii character. It's a shame that as a French I did not insist on
unicode vs str before. So here we go. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In Python 2 we have two types to deal with strings. We can use the
&lt;strong&gt;str&lt;/strong&gt; type or the &lt;strong&gt;unicode&lt;/strong&gt; type. The &lt;strong&gt;str&lt;/strong&gt; type is basically
storing &lt;strong&gt;&lt;em&gt;bytes&lt;/em&gt;&lt;/strong&gt; so a string is encoded using a particular encoding,
By default the encoding is ascii. The &lt;strong&gt;unicode&lt;/strong&gt; type encodes strings
as 16 or 32 bits integers and covers the unicode table. The most common
error is to make no assumption whatsoever on the type of the string you
get. What will happen is that some functions that need bytes will simply
try to decode unicodes using the ascii coded, or vice-versa: &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;import base64&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encodestring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;I am oke&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="s"&gt;&amp;#39;SSBhbSBva2U=\n&amp;#39;&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;I am oké&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;Traceback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="k"&gt;last&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="sr"&gt;&amp;lt;module&amp;gt;&lt;/span&gt;

&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;encode&lt;/span&gt;

&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/usr/lib/python2.7/base64.py&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;315&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;encodestring&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;pieces&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;binascii&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;b2a_base64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;UnicodeEncodeError:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;ascii&amp;#39;&lt;/span&gt; &lt;span class="n"&gt;codec&lt;/span&gt; &lt;span class="n"&gt;can&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;t encode character u&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;xe9&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ordinal&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;One solution that comes in mind is to check for the type of the string
in your function: &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;def encode(data):&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unicode&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="err"&gt;        &lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;utf8&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encodestring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;I am oké&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="s"&gt;&amp;#39;SSBhbSBva8Op\n&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This is tempting but leads to another issue: if by default your program
is able to deal with string or unicode for all your strings, there are
high chances that you'll miss to check for the type somewhere or combine
str and unicode in some places. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;A much better approach is to use internally in your program only
unicode and deal with conversions in inputs and outputs. In a Python web
app it boils down to check that all inputs are unicode (beware of JSON).&lt;/p&gt;
&lt;p&gt;The other issue is the encoding and the decoding. What codec should we
use ? The asnswer is utf-8, because it's the most universal. To make
sure there's no misunderstanding: a unicode is a &lt;em&gt;decoded&lt;/em&gt; string, and a
str is &lt;em&gt;encoded&lt;/em&gt;. So you can decode() str and encode() unicode: &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;u'é'.encode('utf8')&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;#39;\xc3\xa9&amp;#39;&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;é&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;utf8&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;\xe9&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;So, use only unicode in your apps, and when encoding, use the utf8
codec by default. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;More on this here: &lt;a href="http://docs.python.org/howto/unicode.html"&gt;http://docs.python.org/howto/unicode.html&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;h2&gt;What's next&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;More Python deployments&lt;/li&gt;
&lt;li&gt;Some benches in the Sync server&lt;/li&gt;
&lt;/ul&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 31 May 2011 14:27:00 +0200</pubDate><guid>http://blog.ziade.org/2011/05/31/mozilla-services-week-22/</guid></item><item><title>The Architecture of Open Source Applications</title><link>http://blog.ziade.org/2011/05/23/the-architecture-of-open-source-applications/</link><description>&lt;p&gt;&lt;img alt="image" src="http://www.aosabook.org/images/cover.jpg" title="Book cover" /&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A&lt;/strong&gt;bout a year ago, Greg Wilson has started a very ambitious book
project about the architecture of open source applications. The line-up
was impressive, since more than 20 people have started to write about
various topics. From the Battle of Wesnoth to Audacity, via Mercurial.
And the project is, as of today, completed ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.aosabook.org/en/index.html"&gt;Everything is available online&lt;/a&gt;, but you should consider buying it
at LuLu because all royalties are going to Amnesty International. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I can't really give a detailed review about the book yet because I am
still reading it, but I already can tell you it's awesome :) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;And I am proud to say that I've contributed &lt;a href="http://www.aosabook.org/en/packaging.html"&gt;a chapter about Python
packaging&lt;/a&gt;in which I am trying to explain what we're doing to improve
the situation. This is a good timing since I've just pushed &lt;em&gt;packaging&lt;/em&gt;
into the standard library. So if you want a bit of background in this
area of Python, read up. The chapter should be pleasant to read thanks
to the work of Greg Wilson and Amy Brown. They managed to convert an
awful Frenglish chapter into something that is readable. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Again, you should buy a copy of that book.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Mon, 23 May 2011 22:26:00 +0200</pubDate><guid>http://blog.ziade.org/2011/05/23/the-architecture-of-open-source-applications/</guid></item><item><title>&amp;quot;packaging&amp;quot; has landed in the stdlib</title><link>http://blog.ziade.org/2011/05/22/quotpackagingquot-has-landed-in-the-stdlib/</link><description>&lt;p&gt;I've pushed in the standard library the new version of Distutils ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This new version, initially known as &lt;strong&gt;distutils2&lt;/strong&gt; was renamed to
&lt;strong&gt;&lt;em&gt;packaging&lt;/em&gt;&lt;/strong&gt; (Tres Seaver came up with this name during the last
Summit), and contains all the improvement we have been working on during
the last 2 years or so on the distutils codebase. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;There are still a lot of thing to do until Python 3.3 is out, but
having packaging back in the standard library makes our work easier, and
will boost its polishing for the upcoming release. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This work is still not very useful to the community since we need to
backport it to Python 2. The plan is to run 3to2 and have a standalone
version released under the Distutils2 name. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This tool has countless improvements I will explain later, whenever we
push the documentation in the coming days. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Just to whet your appetite: &lt;br /&gt;
-   for &lt;strong&gt;end-users&lt;/strong&gt; Python gains a &lt;strong&gt;pysetup&lt;/strong&gt; script you can use to
    install, uninstall projects, browse installed projects, browse PyPI,
    and many other things.
-   for &lt;strong&gt;developers&lt;/strong&gt;, no more setup.py file. You just define
    everything in new setup.cfg sections. You can also define your data
    files in details.
-   for &lt;strong&gt;os packagers&lt;/strong&gt;, you can relocate data files where they should
    be installed, without breaking the code.&lt;/p&gt;
&lt;p&gt;~~nb : the push busted all the buildbots in the process, and I've been
busy in the last days to fix them. I still have cryptic issues under BSD
and Solaris, but most problems have been fixed. Thanks a lot to Victor,
Ezio, Antoine, Ned, David and many more people that helped me there~~
&lt;em&gt;Buildbot is now back to normal, everything was fixed \o/&lt;/em&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;nb 2: the commands and compiler parts are not really different from
distutils, but we have numerous things to change before 3.3, and I'll
try to get more people involved in that part because I don't know what's
a compiler :). If you're doing sci stuff w/ Python, know compilers, and
want to help us, let me know.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sun, 22 May 2011 23:02:00 +0200</pubDate><guid>http://blog.ziade.org/2011/05/22/quotpackagingquot-has-landed-in-the-stdlib/</guid></item><item><title>Mozilla Services, week 16/17</title><link>http://blog.ziade.org/2011/05/02/mozilla-services-week-1617/</link><description>&lt;p&gt;What's this ? read &lt;a href="http://tarekziade.wordpress.com/2010/11/30/rsync-mozillaservices-community-week-47/"&gt;this post&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;What happened&lt;/h3&gt;
&lt;p&gt;I took a bit of vacation and came back last week. I took a lot of
pictures and I love &lt;a href="http://bit.ly/irH6AC"&gt;this one taken at the Amneville Zoo near Metz&lt;/a&gt;. I
was quite amazed by the quality of this Zoo by the way. &lt;br /&gt;
&lt;/p&gt;
&lt;h4&gt;Share Server (F1)&lt;/h4&gt;
&lt;p&gt;We're continuing our work to get the F1 server ready, and Rob Miller,
who joined last month (yay!) is helping me in this task. We're working
on: &lt;br /&gt;
-   removing the Pylons layer and using our own WSGI micro-framework
    like in other Services Apps -- since the F1 server is just a small
    oauth proxy, that should depend on nothing else than the library for
    every service (GMail, Twitter, etc.)
-   setting up the Services Status DB. This is explained here:
    &lt;a href="https://wiki.mozilla.org/Services/F1/Server/ServicesStatusDB"&gt;https://wiki.mozilla.org/Services/F1/Server/ServicesStatusDB&lt;/a&gt;.
    Basically this will let us turn on and off the proxy for a given
    service
-   continuing the &lt;em&gt;Grand Split&lt;/em&gt;. The static web pages are now living on
    their own.&lt;/p&gt;
&lt;p&gt;The Services Status DB involved some research/brainstroming with Shane,
on the best way to do it, and we decided we would: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;1/ Use Membase to store the status of every service. Membase provides
&lt;a href="http://dustin.github.com/2010/06/29/memcached-vbuckets.html"&gt;vbuckets&lt;/a&gt; and has a really nice web managment interface to
add/remove/modify the cluster. Pylibmc and libmemcached do not
understand vbuckets yet, but I am working on it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;2/ Reject calls at the Nginx level when the service is down. I first
wrote a &lt;a href="https://bitbucket.org/tarek/nginx-sstatus/overview"&gt;NGinx module in C&lt;/a&gt; but we will not use it. The problem with
NGinx modules is that everytime you change them you need to recompile
and redeploy Nginx. &lt;em&gt;Meh&lt;/em&gt;. Instead, as I was suggested in the Nginx
mailing list, we're adding the Lua module in Nginx, that will allow us
to script the behavior we want from within the Nginx config files. &lt;br /&gt;
&lt;/p&gt;
&lt;h4&gt;Documentation&lt;/h4&gt;
&lt;p&gt;Hey check this out : &lt;a href="http://docs.services.mozilla.com/"&gt;http://docs.services.mozilla.com&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This is the sweet documentation center I've set up for Mozilla
Services. I have started to add more content about each one of our
Service, basically extracting what we have here and there in our wiki. I
am trying to follow a similar pattern for each one of them. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;While wikis are awesome to work collectively on specs, I love
&lt;a href="http://sphinx.pocoo.org"&gt;Sphinx&lt;/a&gt; because it makes it so easy to consolidate documentation for
a project, and share a glossary, terms, definitions etc. And the
documentation is treated like code, all in an Hg repository here:
&lt;a href="https://hg.mozilla.org/services/docs/"&gt;https://hg.mozilla.org/services/docs/&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;There's a lot of content to gather from various places in the wiki(s)
in order to build a complete list of our services, but the idea is to
add documentation there once the API has stabilized. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;What's planned&lt;/h3&gt;
&lt;p&gt;More Share Server work. I will also try to push on more docs. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Also there's a new side project I've started that might be interesting
to share here, I will try to blog later in the week about it. In a few
words: it's a tool that should help &lt;a href="https://wiki.mozilla.org/MoPy/"&gt;MoPy&lt;/a&gt; people to share knowledge
and try to maximize cross-team reuse of code when possible.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Mon, 02 May 2011 16:43:00 +0200</pubDate><guid>http://blog.ziade.org/2011/05/02/mozilla-services-week-1617/</guid></item><item><title>Python Packaging Documentation sprint April 2-3</title><link>http://blog.ziade.org/2011/04/01/python-packaging-documentation-sprint-april-2-3/</link><description>&lt;p&gt;This is a short notice but here we go: We're having an online
documentation sprint this week-end. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The goal is to clean up the documentation for packaging before I do the
merge in the stdlib. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you want to help please read this page :
&lt;a href="http://wiki.python.org/moin/Packaging/Sprints/DocumentationSprint"&gt;http://wiki.python.org/moin/Packaging/Sprints/DocumentationSprint&lt;/a&gt;
and join the IRC tomorrow and/or the day after. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Anyone can help here, since the documentation aims to three targeted
readers: &lt;br /&gt;
-   Developers that want to package/release their projects
-   OS Packagers
-   End-users&lt;/p&gt;
&lt;p&gt;I'll be sprinting myself Sunday since I am travelling today and
tomorrow. Kudos to Kelsey, who's leading the sprint effort.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 01 Apr 2011 15:29:00 +0200</pubDate><guid>http://blog.ziade.org/2011/04/01/python-packaging-documentation-sprint-april-2-3/</guid></item><item><title>bdist_rpm is dead, long life to Py2RPM</title><link>http://blog.ziade.org/2011/03/25/bdist_rpm-is-dead-long-life-to-py2rpm/</link><description>&lt;p&gt;One thing I've learned along the way while working on Python packaging
matters is that it's quite impossible to maintain in the stdlib scope
tools like bdist_rpm, which builds RPMs using Distutils' enabled
projects. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I've asked around, and it turns out 9 out of 10 of the packagers are
not using it for their daily RPM packaging work, unless they are also
the developers of the projects to package. They use their own tools. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;There are several reasons: &lt;br /&gt;
-   bdist_rpm makes a lot of assumptions when creating the spec files.
-   there are no way to customize some sections in the spec file that
    will be generated.
-   the spec file is most of the time edited by packagers before it's
    actually used, so the automatic full translation from Python
    metadata to RPM metadata is a false good idea. And while packagers
    have templates, they eventually end up working manually in the spec.
-   What's good for RedHat 5 is not for the latest Fedora
-   Python sdtlib cycle does not match the distro cycles. So even if we
    update it, it would be soon deprecated again&lt;/p&gt;
&lt;p&gt;So, when it was suggested a few months ago to add a bdist_deb command
in Distutils, I told people that anything related to creating deb files
should be a project on its own. And &lt;a href="http://pypi.python.org/pypi/stdeb"&gt;stdeb&lt;/a&gt; seems to have this role
now. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For Windows, it's a bit different: tools like bdist_msi are easier to
maintain and we don't have many flavors of packaging in the win32 world,
just one. The release cycle of Python's stdlib fully works here. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Following this principle&lt;strong&gt; bdist_rpm has been removed from
Distutils&lt;/strong&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So for the RPM world I want the same standalone project, that provides
RPM goodies for Pythoneers, on the top of Distutils2 a.k.a. the new
&lt;strong&gt;&lt;em&gt;packaging&lt;/em&gt;&lt;/strong&gt; module in Python 3.3, to replace bdist_rpm. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;As a matter of fact I've started this project a while ago for my work
at Mozilla and called it &lt;strong&gt;&lt;em&gt;pypi2rpm&lt;/em&gt;&lt;/strong&gt; then. It's called like this
because its main feature is to create for CentOS a RPM package out of a
project released at PyPI. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Try it: &lt;br /&gt;
   $ pip install pypi2rpm&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;pypi2rpm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; &lt;span class="n"&gt;SQLAlchemy&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="n"&gt;long&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="sr"&gt;/tmp/o&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;python26&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.6.6&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;noarch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rpm&lt;/span&gt; &lt;span class="n"&gt;written&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This script uses &lt;strong&gt;Distutils2&lt;/strong&gt; to browse PyPI and sort versions, then
uses a custom version of bdist_rpm to invoke rpmbuild. This script can
also create a RPM out of an existing project, by using an existing .spec
file. It bypasses setup.py. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But this is just a quick script I've created for my needs. In the long
term, I want to rename it to Py2RPM and make it the replacer of
bdist_rpm. The features I want it to provide are: &lt;br /&gt;
-   A standalone script that can be used to create RPMs, by using a
    configurable command-line call
-   A standalone script that can be used to generate a spec file out of
    a setup.cfg file
-   A way to read all options from setup.cfg, and to avoid to duplicate
    metadata fields like name, version etc
-   A way to be aware of the differences between the various RPM-based
    distros.
-   A feature to call via RPC the various CentOS, Fedora, etc
    repositories, to check if the name is already taken, if some files
    conflicts, etc
-   A distutils2 command, so the script can be called from the top-level
    &lt;strong&gt;pysetup&lt;/strong&gt; script if wanted.&lt;/p&gt;
&lt;p&gt;On a side note (but this is another blog post), setup.cfg is being
spec-ed out and the spec will be versioned. This will allow other tools
from the RPM world, &lt;em&gt;{put a packaging system name here}&lt;/em&gt;, to consume it
and provide similar features. And this file will soon be available at
PyPI so we can work with it without downloading the tarball of the
release. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you're interested into Py2RPM, please get in touch with me. I am not
a RPM expert at all, and I'd be happy to have more people involved.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 25 Mar 2011 16:39:00 +0100</pubDate><guid>http://blog.ziade.org/2011/03/25/bdist_rpm-is-dead-long-life-to-py2rpm/</guid></item><item><title>Mozilla Services / Week 10/11</title><link>http://blog.ziade.org/2011/03/23/mozilla-services-week-1011/</link><description>&lt;p&gt;What's this ? read &lt;a href="http://tarekziade.wordpress.com/2010/11/30/rsync-mozillaservices-community-week-47/"&gt;this post&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;What happened&lt;/h3&gt;
&lt;p&gt;Well.. Firefox 4 is out and the number of downloads is impressive.
Check this page: &lt;a href="http://glow.mozilla.org/"&gt;http://glow.mozilla.org/&lt;/a&gt;. It's almost 7 millions
right now ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Back to my Services dev topics :D &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It's been a while since I've posted a bi-weekly status update, because
I was busy traveling to Pycon, then to Vancouver to work with the
messaging team on F1. If you've never heard of it, check &lt;a href="https://mozillalabs.com/messaging/?p=343"&gt;Bryan's
post&lt;/a&gt;. Then &lt;a href="http://f1.mozillamessaging.com/"&gt;go there&lt;/a&gt;, install the Add-on and enjoy it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The work there was quite interesting as we've started to think about
how to scale F1 so it can be used by millions of users. On server-side
--the part I am the most interested in-- they have built a Pylons
application that acts like a oauth proxy, used by the Add-On to send
tweets, e-mails etc to third-party services like Twitter, GMail or
Facebook. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The choice of Pylons made sense back when the project started because
there was a database that stored some user data, and a few panels that
are displayed when you want to manage your settings. But that database
has been removed from the server and the code has shrinked to a few
static pages and an oauth lib used to communicate with the various
services. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;One thing we will do is to move to a lighter web application and just
use WebOb and Routes. Pylons became overkill for that application, and
has now joined the cemetery of deprecated frameworks (Pyramid is the new
thing). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;On the scaling part, we're still working hard to come up with the best
design, and we're trying to keep this wiki page up-to-date with that
work: &lt;a href="https://wiki.mozilla.org/Services/F1/Server/Architecture"&gt;https://wiki.mozilla.org/Services/F1/Server/Architecture&lt;/a&gt;. Note
that this is a work in progress, so nothing is settled in stone. The
most interesting part is to decide if we do a synchronous architecture
or asynchronous one. I personally think a synchronous architecture is
simpler to start with, since we already have a working application that
does what we want, and since we can always move to an async model later.&lt;/p&gt;
&lt;p&gt;Another interesting part I am working on is how to do functional tests
against our application, knowing that most APIs will call in turn a
third-party server like Twitter or GMail. We need to mock those but in
the meantime find a smart way to replay real sessions. I've worked on
this topic in the past and blogged about it. I had created an initial
version of a small mock tool but not really used it until this week. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Mark Hammond has been working on the topic as well and we need to
synchronize our efforts next, but the general idea we've discussed and
we'll use is to record a session that occurs with the real servers, dump
it in a file, then allow the tests to reuse the recorded session instead
of calling the real server again. This also needs to be done through a
real TCP call to a third party server, and not mocked in Python, for a
realistic behavior. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;My first take on this is a simple proxy called &lt;strong&gt;recproxy&lt;/strong&gt; which is a
new version of my previous proxy. You can use it like this: &lt;br /&gt;
   class TestSendController(unittest.TestCase):&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;    &lt;span class="nv"&gt;@recproxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;https&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;twitter.com&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;test_send.rec&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;test_send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

       &lt;span class="o"&gt;....&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This proxy does the following: &lt;br /&gt;
-   A proxy server is launched on the localhost
-   Any call via urrlib2 which destination is https://twitter.com/xxxx
    is intercepted and sent to the local proxy
-   The local proxy reads the test_send.rec file that contains
    request/response pairs. If a request in that file corresponds to the
    request, the response is returned.&lt;/p&gt;
&lt;p&gt;Of course, this means that the &lt;em&gt;test_send.rec&lt;/em&gt; has to be created
first. To do it, all you have to do is to run the tests with a
&lt;strong&gt;&lt;em&gt;PROXY&lt;/em&gt;&lt;/strong&gt; environment variable set to 1. This will call the proxy
server, but instead of returning values found in the file, it will proxy
the requests to the real server and record the responses into the file.&lt;/p&gt;
&lt;p&gt;Since we want to share the recorded files and obfuscate sensitive data,
you can replace those data by &lt;em&gt;XXX&lt;/em&gt; in the file. The proxy will match
the incoming requests using a regular expression that will replace XXX
by anything. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here's an example of such a rec file: &lt;br /&gt;
   POST /statuses/update.json HTTP/1.1&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;Accept&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Encoding:&lt;/span&gt; &lt;span class="n"&gt;identity&lt;/span&gt;

&lt;span class="n"&gt;Connection:&lt;/span&gt; &lt;span class="nb"&gt;close&lt;/span&gt;

&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Length:&lt;/span&gt; &lt;span class="n"&gt;XXX&lt;/span&gt;

&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Type:&lt;/span&gt; &lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;urlencoded&lt;/span&gt;

&lt;span class="n"&gt;Host:&lt;/span&gt; &lt;span class="n"&gt;localhost:65535&lt;/span&gt;

&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Agent:&lt;/span&gt; &lt;span class="n"&gt;Python&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;2.6&lt;/span&gt;

&lt;span class="n"&gt;oauth_consumer_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;XXX&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;oauth_nonce&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;XXX&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;oauth_signature_method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;HMAC&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;SHA1&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;oauth_timestamp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;XXX&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;oauth_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;XXX&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;oauth_version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;%20http%3A%2F%2Fwp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;me&lt;/span&gt;&lt;span class="nv"&gt;%2FpgWjI&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;oauth_signature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;XXX&lt;/span&gt;

&lt;span class="o"&gt;====&lt;/span&gt;

&lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="n"&gt;OK&lt;/span&gt;

&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Type:&lt;/span&gt; &lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;UTF&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;

&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Length:&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;=======&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This is basically a mock of a successful status update. And since the
proxy works as a test function decorator, you can create records for bad
behaviors as well, when you are fixing a bug that happens when there's a
unexpected answer from the third party server. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;You can also mock several servers (like bit.ly and twitter) by simply
adding more decorators to the function. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The recproxy code is &lt;a href="https://github.com/tarekziade/f1/blob/develop/linkdrop/tests/functional/proxy.py"&gt;here&lt;/a&gt;. It's still an early version and will
probably evolve a lot. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;What's planned&lt;/h3&gt;
&lt;p&gt;In the next weeks we will continue to work on the architecture for F1,
and also add more tests with Mark &amp;amp; Shane. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I am looking forward to synchronize my testing work with what Mark has
done. Also, I'll probably make the proxy tool a lib, because that's what
we want to use in other projects.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Wed, 23 Mar 2011 15:09:00 +0100</pubDate><guid>http://blog.ziade.org/2011/03/23/mozilla-services-week-1011/</guid></item><item><title>State of packaging (post-Pycon)</title><link>http://blog.ziade.org/2011/03/21/state-of-packaging-post-pycon/</link><description>&lt;h3&gt;Language Summit&lt;/h3&gt;
&lt;p&gt;The language summit this year was less focused on packaging since the
work is ongoing in this front. But we still made a few important
decisions: &lt;br /&gt;
-   Distutils2 will be named "&lt;strong&gt;packaging&lt;/strong&gt;" in the standard library and
    released with Python 3.3
-   A backport for 2.4 to 3.2 will be provided and be called Distutils2&lt;/p&gt;
&lt;p&gt;The reason for this is to avoid any name clash in the future when
people will have to deal with the two versions. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Conferences&lt;/h3&gt;
&lt;p&gt;I gave a talk about Distutils2 and people seemed to like it. At the end
I had to hide behind the desk because people asked questions I did not
want to answer ;) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href="http://pycon.blip.tv/file/4880990/"&gt;The video is here&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Questions&lt;/h3&gt;
&lt;p&gt;I have run &lt;a href="http://www.google.com/moderator/#15/e=6439e&amp;amp;t=6439e.40"&gt;a Google Moderator&lt;/a&gt; before Pycon so people could ask
questions. Here are the answers. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Q: In your long term view, should distutils2 completely replace pip? &lt;br /&gt;
A: No. But Pip will probably move to a thiner layer on the top of
packaging, to provide its specifics (requirements freeze etc) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Q: What's the future of "entry points" ? &lt;br /&gt;
A: packaging will allow adding extra custom metadata. So the plan is to
create a third-party project that provides a similar feature than entry
points. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Q: Can distutils grow better support for compiling shared modules that
are accessed via ctypes? This doesn't work on Windows because the wrong
symbols are exported &lt;br /&gt;
A: Distutils is the poor's man tool for compiling shared modules. The
plan is to make it easier too hook third-party tools to do this. We will
still provide a minimal support, but nothing much fancier than what we
already have. Although, your ctype problem sounds like a bug to me, we
could fix. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Q: In PEP 345, why is license optional? I understand there are many
packages today that don't declare their license (which sucks!), but has
there been any talk of changing that? &lt;br /&gt;
A: It's not really planned since people can simply add a licence text
in their releases if they want. If it was mandatory what would be the
default license ? &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Q: How can we get dependency resolution in an *offline mode* without
running a local index (aka web server) and/or find links (aka clunky
solution)? &lt;br /&gt;
A: By building a local cache of the metadata information &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Q: Will distutils2 manage the dependecies with packages or will left
that to pip? &lt;br /&gt;
A: Distutils2 will manage them &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Q: How about a standard binary distribution format, with metadata like
dependencies, like eggs but without the bad bits?" &lt;br /&gt;
A: What about the existing one ? (bdist) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Q: Is there a mechanism to provide more information about C-extensions,
specifically, which are needed and which are cPython specific
accelerators. &lt;br /&gt;
A: You can specify flags or environment variables, but nothing has
changed here compared to the previous version &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Q: Considering Python's "batteries included" philosophy, why should
distutils2 not include pip and virtualenv in its scope? &lt;br /&gt;
A: Virtualenv is being added under the pythonv name --work in
progress--. Pip2 will stay a third party project and just provides what
Distutils2 does not in the future: developer tools that should not be
placed in the stdlib. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Q: There are only one question - when? Python 3.3, I hope?" &lt;br /&gt;
A: Yes 3.3. The merge is imminent. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Sprints&lt;/h3&gt;
&lt;p&gt;We worked on porting Distutils2 into the stdlib, and it's almost ready
here: &lt;a href="https://bitbucket.org/tarek/cpython/"&gt;https://bitbucket.org/tarek/cpython/&lt;/a&gt; The merge is imminent ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Other interesting features we're adding: &lt;br /&gt;
-   People will be able to add extra metadata in their projects. They
    will be published at PyPI and also installed and queriable.
-   &lt;strong&gt;setup.cfg &lt;/strong&gt;will have its own specification and will be versioned.
    It will be published at PyPI as well so people can get all kind of
    information about project without downloading the tarball ! Also,
    other tools like Bento will be able to read the cfg file and use it
    to provide their build features.
-   lots of other stuff I have to remember -- I will add them here
    later.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Mon, 21 Mar 2011 12:11:00 +0100</pubDate><guid>http://blog.ziade.org/2011/03/21/state-of-packaging-post-pycon/</guid></item><item><title>Pycon 2011 - Ask questions on packaging</title><link>http://blog.ziade.org/2011/03/10/pycon-2011-ask-questions-on-packaging/</link><description>&lt;p&gt;30 minutes is very short -- that's the length of my Python talk about
packaging. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In order to give a chance to people that could not make it to Pycon, or
for those who won't be able to ask them during the talk, I have created
a Moderator here: &lt;a href="http://bit.ly/pycon2010"&gt;http://bit.ly/pycon2010&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Add questions there and vote for them -- I'll answer to them at the end
of the talk if I have spare time, or in my blog.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 10 Mar 2011 14:03:00 +0100</pubDate><guid>http://blog.ziade.org/2011/03/10/pycon-2011-ask-questions-on-packaging/</guid></item><item><title>Thoughts on Twitter noise</title><link>http://blog.ziade.org/2011/02/24/thoughts-on-twitter-noise/</link><description>&lt;p&gt;I was following a little more than 100 people on Twitter, and I realized
I was mainly following my own technical tribe. By &lt;strong&gt;tribe&lt;/strong&gt; I mean
people gravitating around Python core, and since a few months a few
Mozillians. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Well, what happens is that most tweets that are sent by people I follow
are just circling around, and I get a lot of noise. And I am evolving in
some kind of &lt;em&gt;closed circle&lt;/em&gt; of information. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For Example, if the PSF account sends a tweet. It's retweeted by Jesse,
Voidspace etc.. The most interesting Mozilla news will be retweeted by
people like Chris Beard, or Alex Limi. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So why do I have (and we all have it seems) so many followers that are
like me ? I think that's related to the fact that people follow friends
or acquaintances they meet in conferences etc. We use Twitter a little
bit like Facebook in this respect. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But I don't want to do this anymore, I think Twitter has much more
value than just listing people you know or you like. I want to cut the
noise there and try to get more value out of it. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Reducing my own noise&lt;/h3&gt;
&lt;p&gt;It turns out I am what Klout calls a &lt;em&gt;Specialist&lt;/em&gt; (&lt;a href="http://klout.com/tarek_ziade"&gt;see here)&lt;/a&gt;. But I
am redundant. I mean, look at this: &lt;br /&gt;
1.  Terry sends &lt;a href="http://www.gossamer-threads.com/lists/python/dev/898715"&gt;a mail at Python-dev&lt;/a&gt; about the fact that Python
    3.2.0 was released twenty years after 0.9
2.  &lt;a href="http://twitter.com/#!/tarek_ziade/status/40482916232470528"&gt;I tweet it&lt;/a&gt;
3.  Michael &lt;a href="http://twitter.com/#!/voidspace/status/40484693799481344"&gt;tweets it&lt;/a&gt; right after
4.  Many other tweet it in turn.&lt;/p&gt;
&lt;p&gt;So what it really means is : does it make sense to duplicate on Twitter
news related to Python when they are already published in other medium ?
I guess it made sense for this one, since it brings to a larger circle
of Python fans this news. Guido himself tweeted it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;And I like to tweet those things -- And I have more than 1k followers
that seem to be mainly interested in Python, so they want those tweets
and consider that I am a specialized reporter. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;One way to cut the noise on my side would be to check if someone has
already talked about it, and eventually retweet it, like when you try
send a link in Reddit and it tells you someone has already done it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Or maybe just become more specialized -- stop talking about wine or
random Python news I know they will be tweeted eventually. And also,
maybe limit myself to 5 tweets a day. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Reducing other people noise&lt;/h3&gt;
&lt;p&gt;In order to cut the noise, I need to keep only a few "Specialists", for
every domain I am interested in. I could probably just follow
&lt;a href="http://twitter.com/#!/voidspace"&gt;voidspace&lt;/a&gt; to get the latest news from the Python world. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The real value though, would be to follow &lt;em&gt;other&lt;/em&gt; people that talk
about other topics than Python or Mozilla. That does not necessarily
means I'll follow random people, but rather people that are partially
sharing my interests and opening a window to other worlds at the same
time. People that are in my tribes, but also part of other tribes.
That's a little bit what Malcom Gladwell calls &lt;a href="http://en.wikipedia.org/wiki/The_Tipping_Point"&gt;connectors&lt;/a&gt; I guess. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For example, &lt;a href="http://twitter.com/#!/ogrisel"&gt;ogrisel&lt;/a&gt; is part of my Python user group but really
focuses on AI. I enjoy his updates because they are food for thoughts. I
also enjoy &lt;a href="http://twitter.com/#!/zooko"&gt;zooko&lt;/a&gt; updates because he has one foot in Python and one
foot in security. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So, I guess the most logical thing for me to do with Twitter would be
to: &lt;br /&gt;
-   stop following Python or Mozilla people, except a very few
    "Specialists"
-   follow more "Connectors" like ogrisel&lt;/p&gt;
&lt;p&gt;I am already down to 40 people I follow and will probably cut it down
in half. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I already feel better today, my Tweetdeck backlog was greatly reduced.
I wanted to tweet something about the weather but I held it this time ;)&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 24 Feb 2011 09:42:00 +0100</pubDate><guid>http://blog.ziade.org/2011/02/24/thoughts-on-twitter-noise/</guid></item><item><title>QA portal ideas</title><link>http://blog.ziade.org/2011/02/21/qa-portal-ideas/</link><description>&lt;p&gt;GSOC is coming back. We'll soon have to select some projects for
CPython, and I'll probably suggest a few Distutils2 topics -- it's an
endless project ;). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;There's a project I want to start, a QA portal. I am not sure how this
could fit in a GSOC proposal, but I think there could be some interest
in the community. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I could definitely use such a tool for our Mozilla Services projects. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But let's face it: I have no time, and this is far too ambitious. But
let me dream a bit... &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Level 1 - PyPI Watcher&lt;/h3&gt;
&lt;p&gt;For level 1, the idea is to create a portal that subscribes to all PyPI
events (registering or uploading of new releases) and creates a QA
report on specific projects I want to watch. The idea is not new, and we
had a student on this last year -- But the project was not finished. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Basically I want a website like &lt;a href="http://pypants.org/"&gt;PyPants&lt;/a&gt;, but with more ambitious
features. Maybe a good idea would be to approach the PyPants creators
and see if the project could be extended. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Some tests I'd like the portal to perform: &lt;br /&gt;
-   run a real install of the project in a VM, and returns a report on
    how the system was impacted with a tool like SystemTap. The VM part
    is mainly to avoid any security issue when running third-party code.
    While I supposedly trust the projects I select, you never know.
-   try to detect and run the tests, along with a coverage report.
-   run the usual metrics (pep8, maccabe, pylint, etc)&lt;/p&gt;
&lt;p&gt;Difficulties: &lt;br /&gt;
-   The VM part is not that easy to set up and scale, but feasible with
    Amazon for instance
-   What about Windows ? -- dropping its support for now seems a good
    idea :D&lt;/p&gt;
&lt;h3&gt;Level 2 - Clone detection&lt;/h3&gt;
&lt;p&gt;The next stage I would like to see the project take is &lt;strong&gt;clone
detection&lt;/strong&gt;. Tools like &lt;a href="http://clonedigger.sourceforge.net/documentation.html"&gt;CloneDigger&lt;/a&gt; allows to detect similarities in
code. It will tell you for instance that function A is very similar to
function B. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;What I would want to see is an global index of clones. The users select
a project as being the master project, and a list of dependencies or
other projects. Then the portal will report any similar functions,
methods or modules it found. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Difficulties: &lt;br /&gt;
-   Set up the right thresholds in the algorithm that detects the
    clones. There could be a lot of back and forth here before the
    results make any sense.
-   Scaling it. For instance, while it would be easy with CloneDigger to
    keep for each project the cluster of statements (&lt;a href="http://clonedigger.sourceforge.net/documentation.html"&gt;see
    doc&lt;/a&gt;), so you don't have to rebuild everything every
    time, we are still doing a comparison work that grows at a
    logarithmic speed. I am not sure how to deal with it but that's an
    interesting issue to solve.&lt;/p&gt;
&lt;h3&gt;Level 3 - ~~Hudson~~ Jenkins integration&lt;/h3&gt;
&lt;p&gt;Once we have a shiny Level 1+2 QA Portal, I'd like to see it integrated
in Hudson somehow. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;One idea would be to create two web services in the QA Portal, a Hudson
plugin can call. One to ask for a fresh report generation, and one to
get the result and display it in a dashboard etc. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I am not sure how Hudson deals with running tasks asynchronously
though, but I am pretty sure this is doable. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Ok let's stop dreaming -- back to work ;)&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Mon, 21 Feb 2011 11:25:00 +0100</pubDate><guid>http://blog.ziade.org/2011/02/21/qa-portal-ideas/</guid></item><item><title>Do not upload dev releases at PyPI</title><link>http://blog.ziade.org/2011/02/15/do-not-upload-dev-releases-at-pypi/</link><description>&lt;p&gt;The &lt;a href="http://groups.google.com/group/sqlalchemy/t/c831f7fe7268c51f"&gt;discussion we had&lt;/a&gt; on the SQLAlchemy mailing list, triggered by
the release of &lt;em&gt;0.7.1b&lt;/em&gt;, made me realize that we need a heads-up on this
problem. &lt;em&gt;EDIT: a &lt;a href="http://mail.python.org/pipermail/distutils-sig/2011-February/017355.html"&gt;similar problem occurred&lt;/a&gt; for psycopg2 today.&lt;/em&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Until our packaging ecosystem knows how to handle properly
&lt;em&gt;development releases&lt;/em&gt;, the best practice for &lt;em&gt;mature&lt;/em&gt; projects is to
avoid publishing anything that is not a &lt;em&gt;final release&lt;/em&gt; at PyPI (or any
download link that points to a development release) &lt;br /&gt;
&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;By development releases, as opposed to final releases, I include: &lt;br /&gt;
-   any release that is a snapshot of the trunk or tip of the project
-   any alpha, beta, or release candidate&lt;/p&gt;
&lt;p&gt;And by &lt;em&gt;mature&lt;/em&gt; project, I mean any project that already published one
stable release at PyPI. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The reason is that our current set of packaging tools do not know how
to make a difference between a final release and a development release.&lt;/p&gt;
&lt;p&gt;Setuptools' &lt;em&gt;easy_install&lt;/em&gt; script will scan the simple index page,
order the releases number via its version sorting algorithm, then take
the "latest" version. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So calling: &lt;br /&gt;
   $ easy_install Foo&lt;/p&gt;
&lt;p&gt;will install the latest uploaded release for the &lt;em&gt;Foo&lt;/em&gt; project, even if
it's a development version. If you use Setuptools' &lt;em&gt;install_requires&lt;/em&gt;
option in your setup.py, the same thing will happen. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;To prevent it, you can tell the tool which version you want, and even
provide a complex condition, like: &lt;br /&gt;
   $ easy_install SQLAlchemy &amp;gt;=0.5, &amp;lt;0.7&lt;/p&gt;
&lt;p&gt;But there's no way to tell it to get the latest final. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;zc.buildout is the only tool in my knowledge that prevents this, with
&lt;a href="http://pypi.python.org/pypi/zc.buildout#preferring-final-releases"&gt;the prefer-final option&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;How Distutils2 and PEP 386 fixes the problem&lt;/h3&gt;
&lt;p&gt;But uploading release candidates is a great way to get feedback from
the community, and it's currently frustrating not to be able to push
development versions at PyPI. Because depending on the installers our
users will use, they might be unable to control if they want to opt in
using non final releases. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;And, well, what's a final release ? what's a beta ? Are we sure all
tools agree on the versionning schemes ? &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;How can I make sure my beta version will be recognized as a beta
release by all tools out there ? &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.python.org/dev/peps/pep-0386/"&gt;PEP 386 solves this&lt;/a&gt; by defining a version scheme. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;And guess what: &lt;br /&gt;
-   The new &lt;a href="http://www.python.org/dev/peps/pep-0345/"&gt;Metadata 1.2 - PEP 345&lt;/a&gt;, implemented in Distutils2,
    recognizes only PEP 386 versions
-   PyPI will reject any project that uploads metadata 1.2 with a
    non-PEP 386 version. And this is already activated at PyPI.
-   The Distutils2 installer recognizes development releases and let you
    decide which one to pick.&lt;/p&gt;
&lt;p&gt;Granted, it's going to take a while before all installers use the new
standards, and all projects out there make the jump to Metadata 1.2, but
at least we know the problem and implemented the solution.. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;And&lt;a href="http://us.pycon.org/2011/schedule/presentations/81/"&gt;Pycon should be an important milestone&lt;/a&gt; since the first beta of
Distutils2 will be released at... PyPI and will be usable by any
project.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 15 Feb 2011 17:15:00 +0100</pubDate><guid>http://blog.ziade.org/2011/02/15/do-not-upload-dev-releases-at-pypi/</guid></item><item><title>Mozilla at Pycon</title><link>http://blog.ziade.org/2011/02/14/mozilla-at-pycon/</link><description>&lt;p&gt;Mozilla folks will be giving a few talks at Pycon US 2011, the biggest
Python conference. That's happening in one month in Atlanta. &lt;br /&gt;
-   Kumar McMillan on [Supporting all version of Python All the Time
    with Tox][]
-   Ian Bicking on &lt;a href="http://us.pycon.org/2011/schedule/presentations/41/"&gt;Javascript for People who know Python&lt;/a&gt;.
-   Me on &lt;a href="http://us.pycon.org/2011/schedule/presentations/83/"&gt;Firefox Sync&lt;/a&gt; and [Packaging, from Distutils1 to
    Distutils2][]
-   Any talk that I've missed (please let me know)&lt;/p&gt;
&lt;p&gt;Brian &lt;a href="http://pycon.blogspot.com/2011/02/pycon-2011-interview-with-tarek-ziade.html"&gt;asked me a few questions about my talks&lt;/a&gt; on the Pycon Blog if
you want to have more details on them. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you like Python or Mozilla, you should definitely consider going to
Pycon. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;And hey, Mozilla is a proud Lanyard sponsor of Pycon this year !&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Mon, 14 Feb 2011 15:52:00 +0100</pubDate><guid>http://blog.ziade.org/2011/02/14/mozilla-at-pycon/</guid></item><item><title>A light Description Language for REST web services ?</title><link>http://blog.ziade.org/2011/02/14/a-light-description-language-for-rest-web-services/</link><description>&lt;p&gt;&lt;strong&gt;tldr&lt;/strong&gt;: I have this idea that comes back and forth in my head since a
few months now, working on the Sync, Easy Setup and Identity servers. I
want to create a light DL to describe what REST web services an
application implements, and use it to automate the request dispatching
but also the documenting and testing of the services. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;When we create web services, there's a lot of things we do that are
quite systematic: &lt;br /&gt;
1.  Describe the web services in a document.
2.  Define what code should be executed.
3.  Validate and transform incoming requests and outgoing responses.
4.  Run functional and security tests on the web services.&lt;/p&gt;
&lt;h3&gt;1. Describe the web services (=API)&lt;/h3&gt;
&lt;p&gt;Documenting the web services the application needs to offer is the
first step to build it. A document needs to list the URL paths, the
methods to use, what goes it and comes out. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For the Easy Setup server (a.k.a the J-Pake server), I've documented an
initial design of the web services here:
[https://wiki.mozilla.org/Services/Sync/SyncKey/J-PAKE#Server_API][]
and we worked with Stefan and Philipp to refine the design iteratively.&lt;/p&gt;
&lt;p&gt;Once the application is built, that's a non implementation-specific
description that anyone can use to build a new client that interacts
with our server. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;2. What code should be executed ?&lt;/h3&gt;
&lt;p&gt;The next step is to build the code that does the job. On a request, we
need to define what piece of code should be called to build a response
and execute it in order to return a response. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It can be a set of regexps managed by dispatcher tool, which output --
That's &lt;a href="http://routes.groovie.org/"&gt;Routes&lt;/a&gt;. It can also be a simple function decorator -- That's
&lt;a href="http://bottle.paws.de/docs/dev/index.html"&gt;Bottle&lt;/a&gt;. Some other approaches are consisting of using the code
namespace to dispatch the request, like implementing the
&lt;em&gt;Root.get_index&lt;/em&gt; method for a GET on the /index url. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;At Services, we use Routes and feed it with a list that contains a
description of the URLs. See this example:
[http://hg.mozilla.org/services/server-storage/file/78762deede5d/syncstorage/wsgiapp.py#l65][].&lt;/p&gt;
&lt;h3&gt;3. Validate &amp;amp; Filter Requests and Responses&lt;/h3&gt;
&lt;p&gt;Beside the feature code itself, the application usually do a series of
validation or/and transformation on the incoming request -- like
checking its headers, or extracting objects from a JSON body. This can
also be done on the outgoing responses. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Those steps are usually generic enough to be reused for all web
services. And those steps, most of the time, should not be implemented
as WSGI Middlewares -- &lt;em&gt;one simple question to ask yourself to know if
you should create /use a middleware: does your application breaks if you
remove that middleware ? If so, it means that your application may not
work without the middleware, thus it should become a library on which
the application has a hard dependency.&lt;/em&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Some Examples: &lt;br /&gt;
-   an authorization function that checks the &lt;em&gt;Authorization&lt;/em&gt; header and
    set in the execution context a user name.
-   a function that controls that the body contains parseable JSON, and
    unserialize it in the execution context.
-   a function that serialize all response bodies into JSON&lt;/p&gt;
&lt;h3&gt;4. Create functional and security tests&lt;/h3&gt;
&lt;p&gt;Like I said in &lt;em&gt;1.&lt;/em&gt;, building a new client against the server should be
possible simply by reading the documentation. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;That's how we build our functional test suite, which runs for each web
service a series of requests, and checks that the responses are the ones
expected. These tests are simply validating that the server acts as
documented. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Depending on the security expectations, another series of tests can
check for the server behavior when it receives unexpected responses. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;A light, implementation-specific, DL&lt;/h3&gt;
&lt;p&gt;So, the idea would be to describe everything in a static file, that can
be used: &lt;br /&gt;
-   by the &lt;strong&gt;application&lt;/strong&gt; to: &lt;br /&gt;
   -   automatically dispatch the requests to some callable
    -   execute some functions before and after the main callable, to
        transform and filter data&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;by the &lt;strong&gt;testers&lt;/strong&gt; to use a generic HTTP client powered by the DL
    file.&lt;/li&gt;
&lt;li&gt;by the &lt;strong&gt;documentation&lt;/strong&gt; to generate an HTML or Wiki version of that
    DL file.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After investigation, I found &lt;a href="http://en.wikipedia.org/wiki/Web_Application_Description_Language"&gt;WADL&lt;/a&gt; and got scared. That's probably
the XML effect :D &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;WADL is very close to what I am looking for, see an &lt;a href="http://www.w3.org/Submission/wadl/#x3-40001.3"&gt;example&lt;/a&gt;. But
while less complex than WSDL, it still seems a bit overkill for what I
want to do. I am not sure for instance, that I want to fully describe
the structure of the responses. And well, WADL is only documenting the
web services, and not pointing the code that implements them. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I want to do both things in the DL, and keep implementation-specific
parts light enough that they will not really annoy anyone. But they're
still useful when your not in the application itself: for example, a
wiki output could link to an online view of the code that's used. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here's a &lt;em&gt;(very quick)&lt;/em&gt; draft in pseudo-YAML out of my head: &lt;br /&gt;
   POST "/the/webservice/is/here":&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;  &lt;span class="n"&gt;id:&lt;/span&gt; &lt;span class="n"&gt;cool_service&lt;/span&gt;

 &lt;span class="err"&gt; &lt;/span&gt;&lt;span class="n"&gt;description:&lt;/span&gt; &lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;cool&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="n"&gt;does&lt;/span&gt; &lt;span class="n"&gt;this&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;that&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

  &lt;span class="n"&gt;request:&lt;/span&gt;

    &lt;span class="n"&gt;headers:&lt;/span&gt;

      &lt;span class="n"&gt;Authorization:&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;supported&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

    &lt;span class="n"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;explains&lt;/span&gt; &lt;span class="n"&gt;here&lt;/span&gt; &lt;span class="n"&gt;what&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="n"&gt;contains&lt;/span&gt;

  &lt;span class="n"&gt;response:&lt;/span&gt;

    &lt;span class="n"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;explains&lt;/span&gt; &lt;span class="n"&gt;what&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="n"&gt;contains&lt;/span&gt;

    &lt;span class="n"&gt;headers:&lt;/span&gt;

       &lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;that&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt;

    &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;type:&lt;/span&gt; &lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;

    &lt;span class="n"&gt;codes:&lt;/span&gt;

      &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;explains&lt;/span&gt; &lt;span class="n"&gt;here&lt;/span&gt; &lt;span class="n"&gt;what&lt;/span&gt; &lt;span class="n"&gt;getting&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;success&lt;/span&gt; &lt;span class="n"&gt;means&lt;/span&gt;

      &lt;span class="mi"&gt;503&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;explains&lt;/span&gt; &lt;span class="n"&gt;here&lt;/span&gt; &lt;span class="n"&gt;when&lt;/span&gt; &lt;span class="n"&gt;you&lt;/span&gt; &lt;span class="n"&gt;might&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="mi"&gt;503&lt;/span&gt;

      &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;explains&lt;/span&gt; &lt;span class="n"&gt;here&lt;/span&gt; &lt;span class="n"&gt;when&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;bad&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;

  &lt;span class="n"&gt;implemented_by:&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

  &lt;span class="n"&gt;pre&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;hooks:&lt;/span&gt;

      &lt;span class="n"&gt;authenticate&lt;/span&gt;

      &lt;span class="n"&gt;extract_json_from_body&lt;/span&gt;

  &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;hook:&lt;/span&gt;

      &lt;span class="n"&gt;jsonify&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The first part is really, documenting your web service. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But it's also detailed enough to be able to automatically create an
HTTP client that can be the basis for tests. e.g.: &lt;br /&gt;
   def post_cool_service(body, authorization):&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;    &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;The cool service does this and that.&lt;/span&gt;

&lt;span class="s"&gt;   Arguments:&lt;/span&gt;

&lt;span class="s"&gt;       Authorization: description of the supported token etc.&lt;/span&gt;

&lt;span class="s"&gt;       body: explains here what the body should contains&lt;/span&gt;

&lt;span class="s"&gt;   Returns:  code, body, headers&lt;/span&gt;

&lt;span class="s"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;generic&lt;/span&gt; &lt;span class="n"&gt;curl&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Not sure about the generative aspect though, because it's hard to
maintain. Maybe a dynamic introspection of the DL file is a better idea
here... &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The second part tells the server what code should be run, something
that would be similar than: &lt;br /&gt;
   def cool_service(request):&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;    &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;The cool service&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

   &lt;span class="n"&gt;authenticate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

   &lt;span class="n"&gt;extract_json_from_body&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

   &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;In other word, I could strip all the boiler-plate code I have around
the code that implements the features themselves, and just combine them
with a few helper functions via the DL. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This approach is quite similar to Zope's ZCML glue, but without the ZCA
layer --which I tend to find heavy and overkill--&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;hg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mozilla&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/services/s&lt;/span&gt;&lt;span class="n"&gt;erver&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="sr"&gt;/file/&lt;/span&gt;&lt;span class="mi"&gt;78762&lt;/span&gt;&lt;span class="n"&gt;deede5d&lt;/span&gt;&lt;span class="sr"&gt;/syncstorage/&lt;/span&gt;&lt;span class="n"&gt;wsgiapp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="c1"&gt;#l65&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Mon, 14 Feb 2011 01:07:00 +0100</pubDate><guid>http://blog.ziade.org/2011/02/14/a-light-description-language-for-rest-web-services/</guid></item><item><title>Flake8 now Hg friendly</title><link>http://blog.ziade.org/2011/02/12/flake8-now-hg-friendly/</link><description>&lt;p&gt;I apologize in advance to the authors of &lt;a href="http://pypi.python.org/pypi/pep8"&gt;pep8&lt;/a&gt; and &lt;a href="http://pypi.python.org/pypi/pyflakes"&gt;Pyflakes&lt;/a&gt;, as
&lt;a href="http://pypi.python.org/pypi/flake8/"&gt;Flake8&lt;/a&gt; is a horrible hack on the top of those two tools. But it's
really what we needed at Mozilla Services to check that anything that
lands into our Python repositories follow these simple rules: &lt;br /&gt;
-   PEP 8 compliant
-   No unused imports
-   No undefined names&lt;/p&gt;
&lt;p&gt;So Flake8 is a simple script that calls pep8 and pyflakes and merges
the output, so you get all warnings grouped by files, instead of having
to run both tools one after the other. I've stripped all options for now
and you just call the script over a single Python file, over a directory
or by using a *.py glob-style pattern. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I added a few features on the top of it: &lt;br /&gt;
-   Python files that starts with this header are skipped: &lt;strong&gt;&lt;em&gt;# flake8:
    noqa&lt;/em&gt;&lt;/strong&gt;
-   Lines that contains a &lt;strong&gt;&lt;em&gt;# NOQA&lt;/em&gt;&lt;/strong&gt; comment at the end will be
    skipped&lt;/p&gt;
&lt;p&gt;In the 0.4 version I have added a Mercurial hook you can use to
automatically control your code when you call the &lt;strong&gt;&lt;em&gt;push&lt;/em&gt;&lt;/strong&gt; or the
&lt;strong&gt;&lt;em&gt;qrefresh&lt;/em&gt;&lt;/strong&gt; command. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here's how you configure it in your&lt;em&gt; .hgrc&lt;/em&gt; once Flake8 is installed: &lt;br /&gt;
   [hooks]&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;commit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;python:flake8&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hg_hook&lt;/span&gt;

&lt;span class="n"&gt;qrefresh&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;python:flake8&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hg_hook&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;flake8&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;strict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;If &lt;em&gt;strict&lt;/em&gt; option is set to &lt;strong&gt;1&lt;/strong&gt;, any warning will block the commit.
When &lt;em&gt;strict&lt;/em&gt; is set to &lt;strong&gt;0&lt;/strong&gt;, warnings are just displayed in the
standard output. I use it in non-strict mode, so I just get a display of
the issues my code has. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Stuff I might add in the future: &lt;br /&gt;
-   A cleaner merge of pep8 and PyFlakes options
-   &lt;strong&gt;&lt;em&gt;EDIT: Added in 0.5&lt;/em&gt;&lt;/strong&gt; ~~A&lt;a href="http://en.wikipedia.org/wiki/Cyclomatic_complexity"&gt;cyclomatic complexity metric&lt;/a&gt; so I
    can print out "Hey what's up with all those loops ? flat is better
    than nested!"~~&lt;/p&gt;
&lt;p&gt;A more ambitious feature would be to detect similar code patterns and
warn the developer that the function he's adding looks a lot like
function Foo in module Boo. This implies using a tool like
&lt;a href="http://clonedigger.sourceforge.net/"&gt;CloneDigger&lt;/a&gt;, but also keeping in a local, centralized database, some
kind of index of every piece of code that gets committed. This is
mandatory to speed up the comparison work and avoid introducing huge
lags as the code base grows. Mmmm that would be a good GSOC topic !&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sat, 12 Feb 2011 13:40:00 +0100</pubDate><guid>http://blog.ziade.org/2011/02/12/flake8-now-hg-friendly/</guid></item><item><title>A simple self-upgrade build pattern</title><link>http://blog.ziade.org/2011/02/10/a-simple-self-upgrade-build-pattern/</link><description>&lt;p&gt;Bootstrapping is useful. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For example, we use it to build in-place our applications at Mozilla
Services, to develop them. When the developer runs "make build", a
Python script is invoked, and makes sure all the Python packages we are
developing are checked out locally, and tied to the execution
environment. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In the Python community you have similar tools like zc.buildout
combined with &lt;a href="http://pypi.python.org/pypi/mr.developer"&gt;mrdeveloper&lt;/a&gt;, or Pip's &lt;a href="http://pip.openplans.org/requirement-format.html"&gt;requirements&lt;/a&gt;. Although, our
script does Mozilla-specific things, like allowing to update the
dependencies at specific tags via command-line options, or at the
&lt;em&gt;"latest stable tag"&lt;/em&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Anyways, back to bootstrapping. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Having such scripts is great, but maintaining them across all projects
is painful. Since the script is the first thing the build runs, it
cannot have an external dependency so it has to be completely
independent. So you have to copy it around, in all your projects, and
that leads to maintenance hell. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;One solution is to add a self-upgrade feature to the script, so it
tries to get the latest version online before it is run. zc.buildout has
such a self-update feature, that will make sure it's at the latest
version. Although, the implementation of this feature is a bit odd. 1/
you cannot run the bootstrap script if you're offline. 2/ on upgrades,
zc.buildout restarts itself. I don't think 2/ a robust approach and I
recall that it had some bugs under Windows in the past (updating files
that your own program is using might fail). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So, since I was working in the tools-for-Mozilla-Services area, I added
a self-upgrade feature and tried to isolate a simple mechanism for this.
A pattern that would let us publish fresh versions of some scripts in
our server and make it easy for our tools to use them. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;An efficient bootstrap script should: &lt;br /&gt;
-   be able to run offline, so the app can be built even if it's unable
    to check for the latest online version
-   be able to upgrade itself with a newer version
-   upgrade itself without having to restart itself
-   be completely independent -- besides a vanilla Python&lt;/p&gt;
&lt;p&gt;The pattern I have used is as follows: &lt;br /&gt;
-   The bootstrap script is an empty shell that is just responsible to
    update a second script that contains the real bootstrapping code,
    then dynamically import it via an &lt;em&gt;__import__&lt;/em&gt;, and run it.
-   The server that publishes the real bootstrap script returns an ETag
    header with it, and supports the If-None-Match header.
-   The bootstrap script is able to ask the server if the online version
    is newer, by using the ETag value it has from the previous update in
    the If-None-Match header. If the online version is unchanged, the
    client receives a 412 code (Precondition failed).&lt;/p&gt;
&lt;p&gt;The code has no dependencies besides Python. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Extract from the client-side code: &lt;br /&gt;
   import os&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;

&lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urllib2&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;

    &lt;span class="c1"&gt;# getting the file age&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;._build.etag&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;._build.etag&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f:&lt;/span&gt;

            &lt;span class="n"&gt;current_etag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;If-None-Match&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;current_etag&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

        &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

        &lt;span class="n"&gt;current_etag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;None&lt;/span&gt;

    &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;http://moz.ziade.org/_build.py&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

                              &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# checking the last version on our server&lt;/span&gt;

    &lt;span class="n"&gt;try:&lt;/span&gt;

        &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;etag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;ETag&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;except&lt;/span&gt; &lt;span class="n"&gt;urllib2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTPError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e:&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getcode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;412&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

            &lt;span class="n"&gt;raise&lt;/span&gt;

        &lt;span class="c1"&gt;# we&amp;#39;re up-to-date (precondition failed)&lt;/span&gt;

        &lt;span class="n"&gt;etag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_etag&lt;/span&gt;

    &lt;span class="n"&gt;except&lt;/span&gt; &lt;span class="n"&gt;urllib2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;URLError:&lt;/span&gt;

        &lt;span class="c1"&gt;# timeout error&lt;/span&gt;

        &lt;span class="n"&gt;etag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;None&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;etag&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;current_etag&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;etag:&lt;/span&gt;

        &lt;span class="c1"&gt;# we need to update our version&lt;/span&gt;

        &lt;span class="n"&gt;_rename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;_build.py&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;_build.py&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;w&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f:&lt;/span&gt;

            &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;._build.etag&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;w&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f:&lt;/span&gt;

            &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;etag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# we&amp;#39;re good, let&amp;#39;s import the file and run it&lt;/span&gt;

    &lt;span class="n"&gt;mod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;__import__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;_build&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;project_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;deps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;dep&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;dep&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;,&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;

    &lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;project_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

    &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Extract from the server-side code: &lt;br /&gt;
   import os&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;mimetypes&lt;/span&gt;

&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;md5&lt;/span&gt;

&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;wsgiref&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;simple_server&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;make_server&lt;/span&gt;

&lt;span class="c1"&gt;# where the served files are located ?&lt;/span&gt;

&lt;span class="n"&gt;_DIR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;.&amp;#39;&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start_response&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;if_none_match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;HTTP_IF_NONE_MATCH&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;PATH_INFO&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lstrip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;/&amp;#39;&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;path:&lt;/span&gt;

        &lt;span class="c1"&gt;# looks like a bad path -- or a potential security thread&lt;/span&gt;

        &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;400 Bad Request&amp;#39;&lt;/span&gt;

        &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Content-type&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;text/plain&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;

        &lt;span class="n"&gt;start_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Invalid Request&amp;quot;&lt;/span&gt;

    &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_DIR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="c1"&gt;# unknown file&lt;/span&gt;

        &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;404 Not Found&amp;#39;&lt;/span&gt;

        &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Content-type&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;text/plain&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;

        &lt;span class="n"&gt;start_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;File not found&amp;quot;&lt;/span&gt;

    &lt;span class="c1"&gt;# we&amp;#39;re good, let&amp;#39;s look at the file age&lt;/span&gt;

    &lt;span class="n"&gt;etag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;quot;%s&amp;quot;&amp;#39;&lt;/span&gt; &lt;span class="nv"&gt;%&lt;/span&gt; &lt;span class="nv"&gt;md5&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;st_mtime&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;etag&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;if_none_match:&lt;/span&gt;

        &lt;span class="c1"&gt;# same file&lt;/span&gt;

        &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;412 Precondition failed&amp;#39;&lt;/span&gt;

        &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Content-type&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;text/plain&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;

        &lt;span class="n"&gt;start_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;

    &lt;span class="n"&gt;content_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mimetypes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;guess_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;200 OK&amp;#39;&lt;/span&gt;

    &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Content-type&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;

               &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;ETag&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;etag&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;

    &lt;span class="n"&gt;start_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f:&lt;/span&gt;

        &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

    &lt;span class="n"&gt;httpd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;make_server&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Listening on port 5000....&amp;quot;&lt;/span&gt;

    &lt;span class="n"&gt;httpd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;serve_forever&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The server is a plain Python wsgi script that is easy to hook in Apache
or Nginx, to serve a directory of files. It should be safe enough as
long as the served directory is isolated from the rest of the system. I
did not try that hard to secure it, but it makes sure you can't leave
the directory. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;*EDIT&lt;/strong&gt;: as noted in the comments, Nginx and Apache provides such
ETag features, so you can just configure it to serve static files this
way. I did a wsgi script as an initial prototype and to demonstrate what
the server does, and I kept it because it's easy to run or hook into any
web server. And I will probably add more features next there.* &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Now the bootstrap script we use can live in its own repository, and get
bug fixes and features, that will be automatically propagated to all our
applications. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Do you solve this issue differently ? I am curious to get some feedback
on this.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 10 Feb 2011 17:21:00 +0100</pubDate><guid>http://blog.ziade.org/2011/02/10/a-simple-self-upgrade-build-pattern/</guid></item><item><title>Distutils2 - Logilab Sprint Report</title><link>http://blog.ziade.org/2011/02/07/distutils2-logilab-sprint-report/</link><description>&lt;p&gt;We had a great sprint at &lt;a href="http://www.logilab.org/"&gt;Logilab&lt;/a&gt; the other week-end, and this was by
far the most productive sprint on Distutils ever. That's partially
because all the core work we did in the last year starts to surface !
That's also because we had a good team of hackers, focused on specific
tasks. I did not merge all the work yet but it should be done this week
and an alpha released right after. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Now to the list of goodness.. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Data files&lt;/h3&gt;
&lt;p&gt;We can now express data files in &lt;em&gt;setup.cfg&lt;/em&gt;, and use a specific API to
work with them. The goal is to let OS Packagers configure where those
files should land at installation time, without breaking the code that
uses them. The brainstorming we did last year at Pycon is here :
&lt;a href="http://hg.python.org/distutils2/file/tip/docs/design/wiki.rst"&gt;http://hg.python.org/distutils2/file/tip/docs/design/wiki.rst&lt;/a&gt; and
it's now a reality. This is a good step torwards a cleaner packaging
system. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Setup.cfg completion&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;setup.cfg&lt;/em&gt; lacked a few things to be able to fully replace a
&lt;em&gt;setup.py&lt;/em&gt;, like defining extensions to be compiled. This is now done
and we're going to maintain a page were we list all projects that were
successfully converted. The page lives here for now:
[https://bitbucket.org/tarek/distutils2/wiki/Deployments_using_setup.cfg][]
and I will announce it widely once the alpha is released, so people can
start to convert their own projects. We did convert more projects, and I
need to update that page later this week. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Installer / Uninstaller&lt;/h3&gt;
&lt;p&gt;A first version that installs a project and its dependencies, as well
as an uninstaller, was completed. Among other things, it will rollback
the installation in case it encounters any issue. This tool exists
mainly as a implementation reference to exercise all the tools that are
provided in Distutils2 to browse an installation, build a depgraph or
query PyPI. There's still a bit of backward compatibility work to do in
order to deal with Setuptools-based installations and projects, but most
things are done. I expect this to be finished and included in the alpha.&lt;/p&gt;
&lt;h3&gt;From Distutils1 to Distutils2 and vice-versa&lt;/h3&gt;
&lt;p&gt;Since&lt;em&gt; setup.cfg&lt;/em&gt; completely replaces &lt;em&gt;setup.py&lt;/em&gt; -- which is ignored by
Distutils2, you can make your project compatible with both. This implies
that you have to maintain two files that contain duplicate information,
like the project name or the version. We created a tool during the
sprint to avoid this: a simple function that you can use in your
&lt;em&gt;setup.py&lt;/em&gt; file to convert &lt;em&gt;setup.cfg&lt;/em&gt; options into Distutils1 options.&lt;/p&gt;
&lt;p&gt;Of course, to avoid adding a Distutils2 dependency into that setup.py
file, we inject that function into the module, and we'll provide a
command in &lt;em&gt;pysetup&lt;/em&gt;. to generate it: &lt;br /&gt;
   $ pysetup createD1setup&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; &lt;span class="n"&gt;successfully&lt;/span&gt; &lt;span class="n"&gt;created&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;And if you want to do it the other way around, we offer a new feature
in the wizard that will run your &lt;em&gt;setup.py&lt;/em&gt; file and generate a
&lt;em&gt;setup.cfg&lt;/em&gt;. This is the tool we'll promote in order to make it easy for
people to add Distutils2 support. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Stuff left to do&lt;/h3&gt;
&lt;p&gt;Besides the merges, before I release the next alpha there's one big
task I need to do: remove the &lt;em&gt;fancygetopt.py&lt;/em&gt; file, a layer on the top
of &lt;em&gt;getopt&lt;/em&gt;, and add a simpler, isolated module to process the command
line. This will clean up a lot the code base, and make the &lt;em&gt;dist.py&lt;/em&gt;
module stop dealing directly with this. It will only implement the
&lt;em&gt;Distribution&lt;/em&gt; class, and consume command line options without having to
be the one that generates them. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I am not sure yet what I'll use, since both &lt;em&gt;optparse&lt;/em&gt; and &lt;em&gt;argparse&lt;/em&gt;
are unable to meet my requirements here. A custom command-line parser is
not the best thing to maintain though.. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Thank you Logilab &amp;amp; Bearstech&lt;/h3&gt;
&lt;p&gt;Logilab is awesome. We had many people from their teams sprinting with
us, and they paid our meals every day and covered Alexis' travel costs.
Bearstech was awesome as well, as they covered several snacks expenses.&lt;/p&gt;
&lt;p&gt;Thanks to all sprinters that made it there, or worked online. We should
do these more often.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;https:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;bitbucket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/tarek/&lt;/span&gt;&lt;span class="n"&gt;distutils2&lt;/span&gt;&lt;span class="sr"&gt;/wiki/&lt;/span&gt;&lt;span class="n"&gt;Deployments_using_setup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Mon, 07 Feb 2011 01:43:00 +0100</pubDate><guid>http://blog.ziade.org/2011/02/07/distutils2-logilab-sprint-report/</guid></item><item><title>Mozilla Services - Weeks 3-4</title><link>http://blog.ziade.org/2011/02/04/mozilla-services-weeks-3-4/</link><description>&lt;p&gt;What's this ? Read &lt;a href="http://tarekziade.wordpress.com/2010/11/30/rsync-mozillaservices-community-week-47/"&gt;this post&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;What happened&lt;/h3&gt;
&lt;p&gt;The two last weeks where more about fixing small issues here and there
and thinking about the tools we need to build good server-side
applications. We finalized with Stefan the work on the &lt;a href="https://wiki.mozilla.org/Services/Sync/SyncKey/J-PAKE"&gt;Easy Setup
protocol&lt;/a&gt;(Pake). The protocol is now able to work under flaky wifi
condition. Typically, if your device is in a wifi environment that drops
the connection often, instead of starting the transaction back again
every time it drops. we've added a way to resume it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The latest Firefox Home release includes this change, and the server is
compatible with the hardened protocol as well as the previous one. That
way it still works with older clients and with Firefox 4 itself, which
do not have this improvement yet (yeah, Richard &amp;amp; Phillip are quite busy
on fixing blockers, so they'll come to it later). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This flaky wifi issue, a few bugs we had with the Sync server when the
LDAP or the MySQL server dropped, and the blog post about the &lt;a href="http://techblog.netflix.com/2010/12/5-lessons-weve-learned-using-aws.html"&gt;Netflix
Chaos Monkey&lt;/a&gt; gave me a idea of a project we should do at some point
at Mozilla: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;We need to make sure all our apps are robust when a back-end server
(==ldap, memcached, mysql, etc) gets down, and in particular make sure:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;all requests to our server return 503 and logs errors correctly, and
    nothing bad happens on client-side&lt;/li&gt;
&lt;li&gt;once the back-end is back, the server should return to a normal
    behavior&lt;/li&gt;
&lt;li&gt;the whole system should stay stable during the back-end blackout,
    and after&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I've started to write down some ideas about this. Basically, I want
each piece of our dev and stage servers infrastructure to get very slow
or entirely down for a bit, at random times. Then, I want to observe how
the application reacts during and &lt;strong&gt;after&lt;/strong&gt; the event. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;The Seism Project&lt;/h3&gt;
&lt;p&gt;There are many low-level tools that allow injecting delays and errors
at the TCP level. Some of them can require BSD systems, like dummynet.
Since Linux 2.4 TC can be used to do traffic shaping. But using those
tools requires setting up a dedicated infrastructure/configuration every
time we want to use such a tool for an app. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Since all our server pieces are TCP-based, another way of doing it is
to write a TCP proxy that is able to manage the delays, shutdowns and
errors, and to some extent simulate HTTP, LDAP or SQL specific issues
when it makes sense. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I call this project &lt;strong&gt;"Seism"&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The front server that is being tested can run one proxy per back-end
and be configured to use them with no extra work. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;One important feature is to log the behavior of the infrastructure when
a perturbation happens, to see if the application behaves as expected. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;One idea to perform this is to create yet another specialized proxy in
front of the application to be tested to record what happens when a
perturbation is simulated. The&lt;em&gt; "back proxy"&lt;/em&gt; can send a signal to the
&lt;em&gt;"front proxy"&lt;/em&gt;, describing what kind of perturbation it just did, and
the front proxy can record how the application reacted by logging the
response it sent (or the lack of response if the app dies). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The final log will tell us how the app is behaving depending on the
perturbations. One nice feature would be a dashboard to display this.
e.g. all green if the client receives 503s and 200s, red if it receives
a 500 etc. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The behavior logger should also log the next N requests to check that
the server is behaving normally after the perturbation. It can also log
the CPU and Memory to check for stability. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I think this can be really useful to bullet-proof automatically every
Server application we're building, in cunjunction with Hudson. A
continuous integration system can run in a hostile, Seism-enabled
infrastructure, and report failures. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Docs &amp;amp; Templates&lt;/h3&gt;
&lt;p&gt;Another thing I've been doing is starting to build a "Developer Guide"
for the server-side, that would make it easy for people to get started
in building server applications that are using our tools and
environment. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;We have this awesome tool in the Python community called &lt;a href="http://sphinx.pocoo.org/"&gt;Sphinx&lt;/a&gt;.
It's used to document Python itself, but also tons of projects out
there. It's based on a light text format (&lt;a href="http://sphinx.pocoo.org/rest.html"&gt;reStructuredText&lt;/a&gt;) and
allow you to treat documents like code. In other words, everything you
write is pushed in a repository, has an history, patches etc.. And
Sphinx is able to generate a static HTML site out of it or a PDF. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So, the dev guide code is here:
&lt;a href="http://hg.mozilla.org/services/server-devguide/"&gt;http://hg.mozilla.org/services/server-devguide/&lt;/a&gt; and an HMTL
rendering, update hourly, lives here: &lt;a href="http://sync.ziade.org/doc/"&gt;http://sync.ziade.org/doc/&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It's an early draft, full of &lt;em&gt;TODO&lt;/em&gt; and &lt;em&gt;XXX&lt;/em&gt;. But you get the idea: I
am trying to document everything a developer that wants to work with our
code should know. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;And one important thing I have started there is to build &lt;a href="http://sync.ziade.org/doc/code.html#paster-template"&gt;Applications
Templates&lt;/a&gt;, so people that want to build a new app on the top of our
tools, an app that follows our standards, can get started in seconds. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;What's planned&lt;/h3&gt;
&lt;p&gt;So, the focus in the upcoming weeks for me is to make sure the Python
Sync server gets into production, and second level tasks include writing
more doc and polishing our server-side tools.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 04 Feb 2011 10:13:00 +0100</pubDate><guid>http://blog.ziade.org/2011/02/04/mozilla-services-weeks-3-4/</guid></item><item><title>Data files in Distutils2</title><link>http://blog.ziade.org/2011/01/23/data-files-in-distutils2/</link><description>&lt;p&gt;I am preparing &lt;a href="http://wiki.python.org/moin/Distutils/SprintParis"&gt;next week's sprint at Logilab&lt;/a&gt;, trying to isolate
several interesting tasks people will be able to achieve without having
to dig in the whole code base. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;One task we need to achieve in Distutils2 is a better description of
the data files in a project, and in particular a way to categorize them
so an OS packager knows how to make a difference between a documentation
file and a configuration file. Also, we need to let the packager decide
where these files need to land in the system at installation time,
without breaking the developer's code. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So far in Python, people tend to include all files within their Python
packages and access them using the &lt;em&gt;__file__ &lt;/em&gt;variable: &lt;br /&gt;
   import os.path&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;here&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__file__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;images&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;here&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;images_dir&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This technique has its limits: &lt;br /&gt;
-   If you use &lt;em&gt;easy_install&lt;/em&gt; to install your projects, you might end
    up with a zipped egg, meaning that the directory is in a zip file.
    So this code will not work unless you've installed the project with
    the -Z option, or you use the zipimport helper to work with your
    files.
-   Installing data files in site-packages is not a best practice in
    most Linux distributions, and if the OS packager wants to move them
    in the "right" place, he needs to review all the code and patch it
    everywhere it makes an assumption on the location of the file.
-   If the developer does the right thing for &lt;em&gt;Debuntu&lt;/em&gt; with his data
    files, he'd need to do it for every other Linux distribution, but
    also define a specific behaviour for Mac OS X and Windows.&lt;/p&gt;
&lt;p&gt;Last year at Pycon we've worked on that topic and came up with an
elegant design on the paper: &lt;br /&gt;
-   Python will provide in the &lt;em&gt;sysconfig&lt;/em&gt; module a list of default
    categories for data files, with a default location for each target
    system
-   The developer will define its data files in categories in setup.cfg,
    and add if needed new categories
-   Linux distributions will be able to change the paths for each
    categories via a configuration file global to Python --
    sysconfig.cfg
-   Distutils2 will look at the paths of each categories at installation
    time, and install the files there.
-   The developer will use a special open() API to be able to locate the
    file no matter on what system the code runs, or even if the code
    runs on a development tree.&lt;/p&gt;
&lt;p&gt;The draft of this feature is here:
&lt;a href="http://hg.python.org/distutils2/file/tip/docs/design/wiki.rst"&gt;http://hg.python.org/distutils2/file/tip/docs/design/wiki.rst&lt;/a&gt; and
starting to code this design for real is a perfect task for this sprint
because it does not require a lot of knowledge about the Distutils2 code
base. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you are in Paris, or available online, and want to sprint with us,
make sure you register to the wiki page !&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;Sprint@Logilab&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sun, 23 Jan 2011 17:00:00 +0100</pubDate><guid>http://blog.ziade.org/2011/01/23/data-files-in-distutils2/</guid></item><item><title>The UI of my dreams, part 2</title><link>http://blog.ziade.org/2011/01/23/the-ui-of-my-dreams-part-2/</link><description>&lt;p&gt;I had a lot of feedback on &lt;a href="http://tarekziade.wordpress.com/2011/01/14/the-ui-of-my-dreams-firefox-terminal-thunderbird-unified"&gt;my previous blog post about the UI of my
dreams&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Some people said that a tiling windows manager, like &lt;a href="http://xmonad.org/" title="XMonad"&gt;xmonad&lt;/a&gt; would
solve this problem if every application like Firefox, Thunderbird, or
the Terminal was managed like a regular window. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So I gave xmonad a shot during a week. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I really liked it ! It basically does with all windows what I am
already doing within VIM in split mode: when a new window is open, it's
added vertically or horizontally besides an existing window. It's easy
to move the focus from one window to the other, and adapt the size of
each window. The whole screen is effective and you don't waste time
anymore by rearranging your windows with the mouse. And you can have up
to nine desktops (==workspaces). Granted, you cannot save their states,
but it seems that this will be the case in the future. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So the question is: can xmonad across 9 desktops replace Panorama ? &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Greg commented by blog post: &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;To me, Panorama groups are equivalent to other workspaces; and tabs in
other groups are equivalent to windows/documents on other workspaces.&lt;/p&gt;
&lt;p&gt;Gnome are working on making the contents of workspaces persist, which
matches your use of tab groups as persistent identifiers. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Scale (and presumably Exposé) can be used to show just “this”
workspace, and it would be this mode you’d normally use to see just
the tabs/documents you’re interested in right now.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;But my own usage of tabs goes beyond this. For one workspace --
understand one working context --, I can have more tabs opened than the
screen can fit. I don't want to have them all open in my screen. Tabs
are more like &lt;strong&gt;&lt;em&gt;active bookmarks&lt;/em&gt;&lt;/strong&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For example, when I am working I can have: &lt;br /&gt;
-   one vim session that displays 2 windows: the code and its test
-   sometime a second vim session for another pending work
-   two terminals, one to run the tests and one to manage my mercurial
    queues
-   up to 10 opened tabs in Firefox. Bugzilla bugs, technical doc, etc.
-   some Thunderbird tabs&lt;/p&gt;
&lt;p&gt;By using xmonad, I can organize the screen to have one vim session, and
one or two terminals to &lt;strong&gt;&lt;em&gt;code&lt;/em&gt;&lt;/strong&gt;. But as soon as I need to &lt;strong&gt;read&lt;/strong&gt;
documentation or bug descriptions, I need to display Firefox and browse
some tabs. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;And I don't want to use another Desktop for this. I have one for my
personal stuff like managing my servers, I have one for Python
development, one for organizing my trips, one for music etc. And they
all use Firefox, VIM sessions, Terminals etc. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So switching from &lt;strong&gt;&lt;em&gt;coding&lt;/em&gt;&lt;/strong&gt; to &lt;strong&gt;&lt;em&gt;reading&lt;/em&gt;&lt;/strong&gt; when I work on
something, should be done within the same desktop. In other words, I
could use something like Panorama to display different organizations for
the same desktop. I could organize windows in Panorama categories like
"&lt;em&gt;coding"&lt;/em&gt; and "&lt;em&gt;reading"&lt;/em&gt; and define a desktop as &lt;strong&gt;work&lt;/strong&gt; and another
one a &lt;strong&gt;travels&lt;/strong&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So the UI of my dream could be: &lt;br /&gt;
-   xmonad or any tiling windows manager
-   One Panorama-like tool instance &lt;em&gt;per workspace&lt;/em&gt;.
-   A global way to configure shortcuts. (VIM mode for me)&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;Previous blog post&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sun, 23 Jan 2011 13:58:00 +0100</pubDate><guid>http://blog.ziade.org/2011/01/23/the-ui-of-my-dreams-part-2/</guid></item><item><title>The UI of my dreams : Firefox / Terminal / Thunderbird unified</title><link>http://blog.ziade.org/2011/01/14/the-ui-of-my-dreams-firefox-terminal-thunderbird-unified/</link><description>&lt;p&gt;I work in three environments the whole day: &lt;br /&gt;
-   Firefox (now mostly Minefield)
-   Linux terminals
-   Thunderbird&lt;/p&gt;
&lt;p&gt;[gallery] &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Yeah you can tell I am not good at screenshots ;)&lt;/em&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I switch from one screen to the other using &amp;lt;Alt+Tab&gt; and the
underlying Operating System does not really matter. While I am under
Ubuntu these days on my MacBook, I'd say that if I had a Bash or ZSH
under Windows or Mac OSX, I would not care that much about the OS --
Well I do because Linux offers the best environment when you develop
server apps but that's not the point--. In the shell, 90% of my windows
are displaying a Vim and most of the time Vim displays itself several
files in split mode. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Every time I switch the context, I have this tiny &lt;em&gt;ergonomic fracture&lt;/em&gt;
in my head. I try desperately my Vim shortcuts in Firefox. Thanks to
&lt;a href="http://vimperator.org/vimperator"&gt;Vimperator&lt;/a&gt;, I was able to reduce this fracture and I'd love to see
something similar in Thunderbird. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But there's still something Firefox has, I wish I'd find in all my
contexts. Everyone of them has Tabs - But Firefox has ~~TabCandy~~
&lt;a href="http://www.azarask.in/blog/post/designing-tab-candy/"&gt;Panorama&lt;/a&gt;, which let you organize tabs in space. I'd also like to
&lt;a href="http://lifehacker.com/5396929/pin-tab-offers-chrome+like-pinned-tabs-in-firefox"&gt;"Pin as App"&lt;/a&gt;some of my terminals, or do something similar in
Thunderbird. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So the UI of my dreams would consist of having the same behavior for
these three environments: &lt;br /&gt;
-   Panorama
-   App Tabs
-   Same shortcuts everywhere (Vim mode for me ;))
-   Eventually, have a Fusion mode where all tabs from all contexts are
    at the same level&lt;/p&gt;
&lt;p&gt;I don't think it's that crazy and I think that's where we tend to go to
in the future. So, who's in for a Desktop Panorama ?&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 14 Jan 2011 11:00:00 +0100</pubDate><guid>http://blog.ziade.org/2011/01/14/the-ui-of-my-dreams-firefox-terminal-thunderbird-unified/</guid></item><item><title>Mozilla Services - Weeks 01-02</title><link>http://blog.ziade.org/2011/01/11/mozilla-services-weeks-01-02/</link><description>&lt;p&gt;What's this ? read &lt;a href="http://tarekziade.wordpress.com/2010/11/30/rsync-mozillaservices-community-week-47/"&gt;this post&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I did not post a weekly update last week because things slowed down
during vacations. I've also decided: &lt;br /&gt;
-   to change the title to a less geeky one, that is easy to understand
    :D
-   to publish the post every two weeks instead of every week. It seems
    to be a more natural pace. We'll see&lt;/p&gt;
&lt;h3&gt;What happened&lt;/h3&gt;
&lt;p&gt;My talk at Pycon about Firefox Sync was &lt;a href="http://us.pycon.org/2011/schedule/sessions/83/"&gt;accepted&lt;/a&gt; ! I've added a
Wiki page containing the outline so you can help me if you see some
missing things or want me to talk about -
&lt;a href="https://wiki.mozilla.org/Services/Sync/Pycon2010"&gt;https://wiki.mozilla.org/Services/Sync/Pycon2010&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Hudson now runs all kinds of tests over the Sync and Pake servers. The
functional tests are running on a real Sync infrastructure, and that
helped me find bugs that are happening only when the server has been
running for a while. I could fix for instance a bug that was filling the
pool of LDAP connectors without freeing them properly. We'll work on
having such CI setups for every Services projects in the future. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The RPM-based release process for all the Python apps is working like a
charm and I need to take it to the next stage. We will set up a in-house
PyPI proxy to avoid calling pypi.python.org on every build. We will
probably use &lt;a href="http://pypi.python.org/pypi/collective.eggproxy"&gt;collective.eggproxy&lt;/a&gt;. That'll be helpfull for the tests
as well. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I also started to work with Stefan on hardening the &lt;a href="https://wiki.mozilla.org/Services/Sync/SyncKey/J-PAKE#Server_API"&gt;J-Pake protocol&lt;/a&gt;
for the Sync easy setup. The goal is to make sure it works well when the
device is in a flaky network/wifi environment. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;What's planned&lt;/h3&gt;
&lt;p&gt;I'll focus on finishing small bits in the Sync Server and make sure the
Pake server meets all the requirements for the grand Firefox 4 release.
Beside the work with Stefan, we need to add more logs in the server to
make it easier to see what going on: successfull transaction, failures
with reasons etc. I'll work with Richard on this. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Globally, the next two weeks are about "serrer les vis" :D&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 11 Jan 2011 17:41:00 +0100</pubDate><guid>http://blog.ziade.org/2011/01/11/mozilla-services-weeks-01-02/</guid></item><item><title>Distutils2 sprint !</title><link>http://blog.ziade.org/2011/01/11/distutils2-sprint/</link><description>&lt;p&gt;Thanks to Logilab, our host for this, we will have a Distutils2 sprint
in Paris from the 27th to the 30st January. I won't be sprinting the
27th but I will be, starting the 28th until the end. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The sprint will focus on: &lt;br /&gt;
-   the install/remove scripts
-   polishing Distutils2 for the next alpha release
-   many other things...&lt;/p&gt;
&lt;p&gt;I'll do a rehearsal of my Pycon 2011 talk, as an introduction to
packaging and Distutils2. The sprint is open to anyone, but their will
be no introduction to Python, TDD or sprinting and we expect
participants to know those. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you are interested, add your name here:
&lt;a href="http://wiki.python.org/moin/Distutils/SprintParis"&gt;http://wiki.python.org/moin/Distutils/SprintParis&lt;/a&gt;. You can sprint
with us online if you can't make it to Paris. If you want to do so, add
you name with that precision (online).&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 11 Jan 2011 09:24:00 +0100</pubDate><guid>http://blog.ziade.org/2011/01/11/distutils2-sprint/</guid></item><item><title>2010 recap, 2011 plans</title><link>http://blog.ziade.org/2011/01/02/2010-recap-2011-plans/</link><description>&lt;p&gt;Instead of the weekly recap I've been doing lately, I will do a year
recap and list my plans for 2011. Happy new year to all of you ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;tldr: 2010 was quite an important year for me. The most important event
was &lt;a href="http://picasaweb.google.com/ziade.tarek/Christmas2010Turcey#5555816919628036034"&gt;Suki&lt;/a&gt; of course, six months ago ! In Python I am still working on
packaging matters and I've joined Mozilla 6 months ago, working in the
Services team. 2011 should be the &lt;em&gt;Year of Packaging&lt;/em&gt; and more fun at
Mozilla ! &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;2010 recap&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;January&lt;/strong&gt;: My work on PEP 386 and 345 is waiting for the final
approval from Guido and &lt;a href="http://tarekziade.wordpress.com/2010/01/07/possible-new-features-for-distutils-2-7/"&gt;I am back at work on PEP 376 and Distribute&lt;/a&gt;.
PEP work involves politics and time. It also means making sure the
solutions are good enough for a smooth evolution of the existing
ecosystem. It was frustrating at first but after a while I got used to
it. A PEP will eventually gets accepted when there's a clear need for
it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In &lt;strong&gt;February&lt;/strong&gt; &lt;a href="http://tarekziade.wordpress.com/2010/02/20/pycon-slides-answers-to-gm-questions/"&gt;I gave a talk at PyCon&lt;/a&gt; and &lt;a href="http://tarekziade.wordpress.com/2010/01/11/pycon-packaging-sprint-topics/"&gt;sprinted there&lt;/a&gt;. My
talk was well received (it made the top #5 of all talks at PyCon with
the token voting system). I think it was both because I was passionate
about it, and because part of the audience followed the work we did in
the last months. The&lt;a href="http://tarekziade.wordpress.com/2010/02/18/python-language-summit-summary-of-the-packaging-track/"&gt;language summit&lt;/a&gt; was held there as well, and was
both frustrating and great. &lt;em&gt;Frustrating&lt;/em&gt; because I was told to revert
all changes to Distutils and do the work outside the stdlib for a while.
That was the right decision but was a bit hard to accept at first.
&lt;em&gt;Great&lt;/em&gt; because Packaging was yet one of the main focus of the summit.
In February, &lt;a href="http://tarekziade.wordpress.com/2010/02/10/pep-345-and-386-accepted-summary-of-changes/"&gt;some of my PEPs were accepted&lt;/a&gt;, which was a very
important milestone. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In &lt;strong&gt;March&lt;/strong&gt; &lt;a href="http://tarekziade.wordpress.com/2010/03/03/the-fate-of-distutils-pycon-summit-packaging-sprint-detailed-report/"&gt;we started to develop Distutils2&lt;/a&gt;, which was basically
the trunk of Distutils right before the Big Revert. Yannick and other
fine folks from Montreal became &lt;a href="http://tarekziade.wordpress.com/2010/03/16/montreal-packaging-sprint-wrapup/"&gt;regular contributors&lt;/a&gt; to the project.
I've also started in March to think hard about my future, and found out
I was really happy &lt;a href="http://tarekziade.wordpress.com/2010/03/18/4-simple-tips-for-wannabe-remote-workers/"&gt;working remotely&lt;/a&gt;. I also started to focus on GSOC
and build proposals for &lt;a href="http://tarekziade.wordpress.com/2010/03/18/distutils2-proposal-for-gsoc/"&gt;Distutils2&lt;/a&gt; and other &lt;a href="http://tarekziade.wordpress.com/2010/03/21/another-gsoc-idea-a-pypi-testing-infrastructure/"&gt;topics&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In &lt;strong&gt;April&lt;/strong&gt; I was&lt;a href="http://tarekziade.wordpress.com/2010/04/06/hello-psf/"&gt;nominated at the PSF&lt;/a&gt;. I also started to get some
interest in Mozilla and in particular &lt;a href="http://tarekziade.wordpress.com/2010/04/06/python-weave-released/"&gt;Sync&lt;/a&gt; (formely Weave). Beta
versions of Distutils2 started &lt;a href="http://tarekziade.wordpress.com/2010/04/08/a-small-distutils2-foretaste/"&gt;to hit the shelve&lt;/a&gt;. I also added &lt;a href="http://tarekziade.wordpress.com/2010/04/21/stdlibs-shutil-improvements/"&gt;new
features in shutil&lt;/a&gt;. Next, &lt;a href="http://tarekziade.wordpress.com/2010/04/26/pep-376-is-accepted-what-it-means/"&gt;PEP 376 was accepted&lt;/a&gt;. That's the master
piece for packaging interoperability ! Last, &lt;a href="http://tarekziade.wordpress.com/2010/04/26/a-distutils2-google-summer-of-code/"&gt;my GSOC proposal was
accepted&lt;/a&gt; and we got no less than 5 students for Distutils2 alone ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In &lt;strong&gt;May&lt;/strong&gt;, I &lt;a href="http://tarekziade.wordpress.com/2010/05/31/distutils2-vs-pip/"&gt;tried and failed&lt;/a&gt; to merge Pip and Distutils2 efforts
;) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In &lt;strong&gt;June&lt;/strong&gt;, &lt;a href="http://tarekziade.wordpress.com/2010/06/19/suki-mozilla-and-japanese-book/"&gt;Suki was born and I got a new job at Mozilla&lt;/a&gt;. Those
events considerably slowed down my work on Packaging and my blogging of
course. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The &lt;a href="http://tarekziade.wordpress.com/2010/08/19/distutils-2-summary-of-the-gsoc/"&gt;GSOC ended&lt;/a&gt; in &lt;strong&gt;August&lt;/strong&gt;. We managed to keep a few
contributors, which is the whole point. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In &lt;strong&gt;September&lt;/strong&gt; I &lt;a href="http://tarekziade.wordpress.com/2010/09/21/firefox-sync-server-in-python-take-2/"&gt;worked hard on Firefox Sync&lt;/a&gt;, and we had&lt;a href="http://tarekziade.wordpress.com/2010/09/22/afpy-camp-python-sprint-wrap-up/"&gt;our
annual sprint&lt;/a&gt; at my house. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In &lt;strong&gt;October&lt;/strong&gt;, while everything which is not related to work was
slowed down because of the new-born baby, I was still finding a bit of
time to &lt;a href="http://tarekziade.wordpress.com/2010/10/03/a-quick-glimpse-at-distutils2-alpha3-part-2/"&gt;work on Distutils2&lt;/a&gt;. But compared to Q1 and Q2, the activity
on the project was very low. I was just hoping then that it wouldn't
kill the momentum on Packaging. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;November&lt;/strong&gt; and &lt;strong&gt;December&lt;/strong&gt; were mainly focused on work. See my
&lt;a href="http://tarekziade.wordpress.com/2010/11/30/rsync-mozillaservices-community-week-47/"&gt;weekly&lt;/a&gt; reports. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;2011 plans&lt;/h3&gt;
&lt;p&gt;2011 will be the Year of Packaging, I am telling you ! Pycon will be a
milestone because Distutils2 should be in a state where it's pretty much
usable. The next tasks will be to promote it and try to gain
contributors. I am also planning move it back to the stdlib once Python
3.2 final is out. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;2011 will also be the &lt;em&gt;Year of Python&lt;/em&gt; for Mozilla Services ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Firefox Sync will be running using my Python implementation in Q1, and
Firefox 4 betas are already using the J-Pake server I've designed with
the help of my favorite client developers Stefan and Phillip. I wrote it
using the same stack than Sync. Other projects written in Python are
coming in the pipe and we are building a set of reusable tools for this
purpose. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I'll also try to pursue the MoPy (Mozilla in Python) effort. That is,
sharing Python knowledge, tools etc between Mozilla projects. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;All in all, the work I am doing in packaging for Python is helping us a
lot at Mozilla: Distutils2 tools are now used to automatically create
RPM releases of all our dependencies by fetching them at PyPI. I hope my
Pycon talk about Firefox Sync will be accepted so I can explain how I
built it !&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sun, 02 Jan 2011 11:53:00 +0100</pubDate><guid>http://blog.ziade.org/2011/01/02/2010-recap-2011-plans/</guid></item><item><title>$ rsync mozilla/services community #week 49</title><link>http://blog.ziade.org/2010/12/10/rsync-mozillaservices-community-week-49/</link><description>&lt;p&gt;What's this ? read &lt;a href="http://tarekziade.wordpress.com/2010/11/30/rsync-mozillaservices-community-week-47/"&gt;this post&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;h2&gt;Week 49&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://bugzilla.mozilla.org/show_bug.cgi?id=601644"&gt;Easy Setup on new devices&lt;/a&gt;:&lt;/strong&gt; I worked on doing distributed tests
and &lt;a href="http://tarekziade.wordpress.com/2010/12/09/funkload-fabric-quick-and-dirty-distributed-load-system/"&gt;blogged about it&lt;/a&gt;. Turns out using gevent does not speed up the
application because it uses a memcached lib that works with sockets and
it looks like it's locking Gevent. So I'll just use the regular worker
and change some of the design later to be able to use several workers. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Firefox Sync in Python&lt;/strong&gt;: Integrated some reviews from Ian and did
some minor cleanup. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;OpenID / Identity&lt;/strong&gt;: I passed the project to JR, our new team member.
(Welcome ;)) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;QA&lt;/strong&gt;: I set up a Hudson server that runs the tests for the Sync and
the Pake server and also builds the RPMs. This way the packages are
continuously built and any error related to that part is also detected.&lt;/p&gt;
&lt;h2&gt;Next Week Plans&lt;/h2&gt;
&lt;p&gt;This week-end and next week, I plan to: &lt;br /&gt;
-   Write some script to automate the deployment of the RPM built by
    Hudson
-   Start some documentation
-   Have an intensive work week in Moutain View
-   Prepare &lt;a href="https://wiki.mozilla.org/Services/AllSnakes"&gt;the 1/2 Python gathering we will have Tuesday there&lt;/a&gt;.
-   etc.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 10 Dec 2010 20:11:00 +0100</pubDate><guid>http://blog.ziade.org/2010/12/10/rsync-mozillaservices-community-week-49/</guid></item><item><title>Funkload + Fabric = quick and dirty distributed load system</title><link>http://blog.ziade.org/2010/12/09/funkload-fabric-quick-and-dirty-distributed-load-system/</link><description>&lt;p&gt;We're currently using &lt;a href="http://grinder.sourceforge.net/"&gt;The Grinder&lt;/a&gt; for our Sync load tests at Mozilla
Services. Grinder will let you write the tests using &lt;a href="http://www.jython.org/"&gt;Jython&lt;/a&gt; and run
a distributed load test. Despite the Java and Jython environment layer
which is always painful to set up, Grinder works well. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But do not expect it to produce nice reports, you're on your own for
this. You need to work on the raw results to produce nice diagrams. Do
not expect either to perform some operations on the test nodes before
and after the distributed load test is executed. Tests are driven via an
UI and while it's probably possible to script them via Java... it's
Java. And well, the feedback when the tests are running is nearly
inexistant. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The distributed load system of my dreams must: &lt;br /&gt;
-   let me run arbitrary commands on every node before and after it runs
    the test. And retrieve results then merge them in a consolidated
    result.
-   let me write complex unittest-based functional tests that I can also
    use to check the health of a server.
-   provide basic reporting features
-   be written in Python ;)&lt;/p&gt;
&lt;p&gt;Oh.. hold on.. I have it : Funkload and Fabric. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href="http://funkload.nuxeo.org"&gt;Funkload&lt;/a&gt; let you write complex functional tests, does load testing
with complex cycles and produces nice reports. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;And then there's &lt;a href="http://fabfile.org/"&gt;Fabric&lt;/a&gt;, which allows you to run commands via SSH
on a pool of servers. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Funkload is not distributed but it's fairly simple to drive it via
Fabric. And when all nodes have finished the execution --whom you can
watch via the Fabric stdout-- you can get them back and merge them in a
single file. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So basically, the fabric script looks like this: &lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fabric&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;runtest&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;

    &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;fl-run-bench testmodule TestClass.test_function&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;/path/to/results.xml&amp;#39;&lt;/span&gt;

    &lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;host_string&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;-results.xml&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This will run the command on the distant server, then download the xml
file. All through SSH. It can be launched on every server with: &lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;fab&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;H&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt; &lt;span class="n"&gt;runtest&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Fabric does not provide parallel execution yet, so you have to create a
batch to run the script on every server in parallel processes. But this
feature should be included in Fabric 1.1. There's a branch for this:
&lt;a href="https://github.com/goosemo/fabric/commits/multiprocessing"&gt;https://github.com/goosemo/fabric/commits/multiprocessing&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Then you can merge the files in a single XML file like this: &lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;funkload&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MergeResultFiles&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MergeResultFiles&lt;/span&gt;

&lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;merge_results&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;

    &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;file_&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;listdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HERE&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;file_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;endswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;-results.xml&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

            &lt;span class="k"&gt;continue&lt;/span&gt;

        &lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;MergeResultFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;results.xml&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;And finally, create an HTML output: &lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;fl&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;report&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Granted, there's no control of the nodes during the tests, that feature
would require a small TCP server that drives Funkload on each server.
And that would obsoletes the need for Fabric I guess. But for setting up
a quick distributed test, I am good with SSH for now. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I should also mention &lt;a href="http://pypi.python.org/pypi/benchmaster/"&gt;Benchmaster&lt;/a&gt;, which do something similar but
seems complex to configure compared to a simple Fabric script. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Funbric&lt;/strong&gt; ? :D &lt;br /&gt;
&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 09 Dec 2010 00:05:00 +0100</pubDate><guid>http://blog.ziade.org/2010/12/09/funkload-fabric-quick-and-dirty-distributed-load-system/</guid></item><item><title>$ rsync mozilla/services community #week 48</title><link>http://blog.ziade.org/2010/12/04/rsync-mozillaservices-community-week-48/</link><description>&lt;p&gt;What's this ? read &lt;a href="http://tarekziade.wordpress.com/2010/11/30/rsync-mozillaservices-community-week-47/"&gt;this post&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;h2&gt;Week 48&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://bugzilla.mozilla.org/show_bug.cgi?id=601644"&gt;Easy Setup on new devices&lt;/a&gt;:&lt;/strong&gt; I have benched the server on stage
using Funkload to make sure everything works nicely, and realized that
the Nginx + Gunicorn setup is not currently optimal using a single
worker. I need to use a single worker because I have coded an IP
filtering system that relies on a memory queue for speed reasons. In
other words, all requests on one server need to share the same memory.
The solution is to use an async worker like &lt;a href="http://pypi.python.org/pypi/gevent/"&gt;gevent&lt;/a&gt; that can be used
without having to change the application code (Thanks Benoit!). GEvent
uses libevent and greenlets and will allow a single process to run an
event loop to handle requests, ala Twisted. So that's my next task. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Firefox Sync in Python&lt;/strong&gt;: We finally landed the Python Sync server in
dev servers, using our full environment. The server itself works since a
while now, and is successfully used by people in the community, but I
didn't have the chance to get it running with our fully-fledge
infrastructure yet at Mozilla. So I had a few bugs to fix this week,
related to that. For instance the ACLs on the Ldap are set so the bind
user cannot read all fields on users entries. Anyways, this is now
looking good. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;OpenID / Identity&lt;/strong&gt;: The bench script is ready. There's not much left
to do on the server at this point. Maybe promote the server a little bit
to see if some people in the community have an interest in such a thing.&lt;/p&gt;
&lt;h2&gt;Next Week Plans&lt;/h2&gt;
&lt;p&gt;Next week I plan to: &lt;br /&gt;
-   Test the gevent worker with GUnicorn
-   Start some documentation
-   Integrate code reviews for Sync, hopefully
-   Prepare the 1/2 Python gathering I have planned mid-december in
    Moutain View Offices
-   etc.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sat, 04 Dec 2010 16:16:00 +0100</pubDate><guid>http://blog.ziade.org/2010/12/04/rsync-mozillaservices-community-week-48/</guid></item><item><title>$ rsync mozilla/services community week #47</title><link>http://blog.ziade.org/2010/11/30/rsync-mozillaservices-community-week-47/</link><description>&lt;h2&gt;What's that ?&lt;/h2&gt;
&lt;p&gt;The various weekly updates the Mozilla projects are a good way to
follow, from a ten thousands foot point of view, what's going on &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I'd like to do my own contribution and launch my own weekly update,
about the work we are doing in my team (Mozilla Services) and in
particular everything related to Python. I'll try to keep it biased and
say good things about Python and bad things about PHP and Perl ;). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;More seriously, I think that's a good way to sync with the community
for the ones who cares about Python and Mozilla, and get some feedback.
Especially since the services we build are not direct end-user products
for people out there. Although, they play an important role in the
background. For instance, to store and retrieve your encrypted bookmarks
via the Firefox Sync add-on, you are calling our Services. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I'll keep those reports short and synthetic and I'll leave it up to the
readers to ask for more details on specific points. I will try not to
use internal technical jargon. &lt;br /&gt;
&lt;/p&gt;
&lt;h2&gt;Week 47&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt; &lt;a href="https://bugzilla.mozilla.org/show_bug.cgi?id=601644"&gt;Easy Setup on new devices&lt;/a&gt; &lt;/strong&gt;: &lt;br /&gt;
-   The &lt;a href="http://hg.mozilla.org/services/server-key-exchange/"&gt;server-side&lt;/a&gt;, using memcached &amp;amp; Python is ready. It's now
    deployed on stage using &lt;a href="http://nginx.org"&gt;Nginx&lt;/a&gt; and &lt;a href="http://gunicorn.org/"&gt;Gunicorn&lt;/a&gt;, and the security
    team is now reviewing it.
-   I worked with Richard to deploy it through rpms. The &lt;a href="http://pypi.python.org/pypi/pypi2rpm"&gt;pypi2rpm&lt;/a&gt;
    script I wrote now works well and creates a RPM out of any project
    released at PyPI, with the right options for CentOS.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Firefox Sync in Python&lt;/strong&gt;: &lt;br /&gt;
-   Rysiek (from Poland) now successfully use the server with his LDAP
    and Postgres setup, after we worked on adding options for him.
-   Ian reviewed the code of &lt;a href="http://hg.mozilla.org/services/server-core/"&gt;server-core&lt;/a&gt; and I followed up with a
    few fixes.
-   Toby is now using it as well in the new project he works on, and
    gave me some feedback on the provided APIs. We fixed a few things to
    make his life easier&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;OpenID &lt;/strong&gt;: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I have started a &lt;a href="http://bitbucket.org/tarek/server-openid"&gt;prototype&lt;/a&gt; of an OpenID server for Services. It
works now with all websites I know of, that consume Openid. It uses
&lt;a href="http://code.google.com/p/redis/"&gt;Redis&lt;/a&gt; to store the association handles and the sites tokens. For the
protocol implementation, I've first used &lt;a href="http://pypi.python.org/pypi/python-openid/"&gt;python-openid&lt;/a&gt; but I ended
up doing custom code. python-openid is great but a bit over-engineered
for what I need to do on server-side. Although, one really useful
feature in this project is in its examples/ folder: a client application
you can use to test your server, and vice-versa. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The client part in Firefox is a&lt;a href="http://bitbucket.org/tarek/weave-identity"&gt;custom weave-identity&lt;/a&gt; add-on that
automatically performs the authentication on the openid server, and
removes all manual steps you need to authorize a site. It replaces the
input text where you usually type your open id identity, with a "sign
in" button. &lt;br /&gt;
&lt;/p&gt;
&lt;h2&gt;Next Week Plans&lt;/h2&gt;
&lt;p&gt;Well, this week as of yesterday :) &lt;br /&gt;
-   &lt;strong&gt;Easy Setup&lt;/strong&gt;: Will follow Richard and Michael work on deployment
    and reviews and help them in case there's an issue
-   &lt;strong&gt;Sync&lt;/strong&gt;: more reviews planned, need to write doc for sync-core
-   &lt;strong&gt;OpenId&lt;/strong&gt;: need to write the script for benchmarking the server
    using &lt;a href="http://funkload.nuxeo.org/"&gt;Funkload&lt;/a&gt;.
-   more things !&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 30 Nov 2010 14:49:00 +0100</pubDate><guid>http://blog.ziade.org/2010/11/30/rsync-mozillaservices-community-week-47/</guid></item><item><title>[personal] [fr] Comment je me suis fait avoir par Virgin Mobile</title><link>http://blog.ziade.org/2010/11/19/personal-fr-comment-je-me-suis-fait-avoir-par-virgin-mobile/</link><description>&lt;p&gt;J'en reviens pas. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Il y a un an, j'ai pris le Forfait Paradyse 2h SL avec un HTC Hero.
(L'offre a changé et a plusieurs limitations maintenant) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Mon objectif était clair: avoir un smartphone qui me pernette de faire
du web sans me soucier de la bande passante que je consomme. Ce forfait
était parfait, malgré une petite limitation pas trop gênante: au bout de
500 mo je recois un SMS me stipulant que je suis "trop gourmand" et que
ma bande passante allait être réduite pour la fin du mois. Je trouvais
ce message mignon, mais plus maintenant. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Tous les possesseurs de smart phone à base d'Android ou d'iPhone le
savent bien: à mois d'être ultra-vigilant et de passer en wifi aussi
souvent que possible, c'est assez facile de dépasser cette limite, sans
pour autant avoir un usage extravagant du téléphone comme du tethering.
Et justement, un forfait illimité c'est fait pour ne plus avoir ce souci
de... limite... &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Ce mois-ci j'ai fait pas mal de déplacements et mon téléphone ne
passait pas trop en wifi. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Mais là ce matin grosse surprise: Virgin m'a suspendu ma ligne. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;J'appelle le service client et j'ai d'abord le droit à cette
explication fumeuse: &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Nous avons coupé votre ligne pour raison de sécurite, car vous aviez
consommé beaucoup de data et nous pensions que peut-être votre
téléphone avait été volé. Nous attendions que vous nous appeliez pour
rétablir la ligne... Elle le sera sous 3h.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Si mon télephone est volé la procédure normale est d'appeler
l'opérateur pour qu'il bloque la ligne. Un operateur ne va pas faire du
zèle de sécurité dans ce cas de figure ?! Au pire, il essaye de
contacter le client pour vérifier. Bon, les techniciens au bout du fil
ne sont pas très fiables parfois, alors je rappele et ce coup ci
l'explication change un peu: &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Vous avez consommé trop de data, et nous avons le droit de couper la
ligne conformément à l'article 10.5. Votre ligne sera retabli d'ici
12h&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Mince, j'ai pris 8 heures en rappelant.. et le fameux article 10.5 (que
j'ai accepté me dit la dame) est assez incroyable. Je vous laisse
savourer le titre et les passages que j'ai mis en gras. &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;10.5 Le Client s’engage à utiliser le Service en bon père de
famille.&lt;/strong&gt; &lt;br /&gt;
 Lorsque le Client souscrit à une offre de service comprenant une
offre d’abondance ou un nombre illimité de communications, de SMS,
d’emails, de messages instantanés et de navigation internet via
internet mobile et/ou via une application mobile illimités, le type
des communications, de SMS, de messages instantanés, d’emails et de
navigation illimités concernés, la destination et/ou la plage horaire
sont précisés dans les documents commerciaux relatifs à cette offre.
Lorsque les communications, les SMS, les messages instantanés ou les
emails en abondance ou illimités sont possibles, ils ne sont autorisés
exclusivement qu’entre deux personnes physiques et &lt;strong&gt;pour un usage
personnel non lucratif direct&lt;/strong&gt;. &lt;br /&gt;
&lt;strong&gt;Virgin Mobile se réserve le droit&lt;/strong&gt;, en cas d’utilisation
inappropriée ou non-conforme aux indications figurant dans la
documentation commerciale d’une offre de services et/ou dans la fiche
tarifaire, comprenant des communications, des SMS, des emails, des
messages instantanés et de la navigation internet via internet mobile
et/ou via une application mobile en abondance ou illimités, &lt;strong&gt;de
facturer les communications&lt;/strong&gt;, SMS, emails, messages ou navigation
internet interdits jusqu’à la suspension au prix hors forfait figurant
dans la brochure tarifaire, &lt;strong&gt;et/ou de suspendre puis de résilier
l’offre, conformément aux dispositions de l’article 11&lt;/strong&gt;. Constituent
notamment des cas d’utilisation inappropriée d’une offre de service
comprenant des offres d’abondance ou des communications, des SMS,
d’emails, des messages instantanés et/ou de la navigation internet via
internet mobile et/ou via une application mobile illimités : &lt;br /&gt;
 - détournement des offres, &lt;br /&gt;
 - l’utilisation des offres d’abondance, des communications, des SMS,
des emails, des messages instantanés et/ou de la Data via internet
mobile et/ou via une application mobile illimités à des fins autres
que personnelles (notamment aux fins d’en faire commerce), &lt;br /&gt;
&lt;strong&gt;- une consommation anormale de communications, de SMS, d’emails, de
messages instantanés et/ou d’un usage internet via internet mobile
et/ou via une application mobile anormal dans le cadre des offres
d’abondance ou d’offres illimitées par rapport à la consommation
moyenne enregistrée parmi les clients titulaires d’offres d’abondance
ou d’offres illimitées, &lt;/strong&gt; &lt;br /&gt;
 - l’utilisation à titre gratuit ou onéreux d’une telle offre de
service en tant que passerelle de ré acheminement de communications,
les appels depuis un boitier radio, &lt;br /&gt;
 - l’utilisation ininterrompue du forfait par le biais notamment d’une
composition automatique et en continu de numéros sur la ligne, &lt;br /&gt;
 - la cession ou la revente, totale ou partielle, des communications,
des SMS, des emails, messages instantanés et/ou de la Data via
internet mobile et/ou via une application mobile illimités, &lt;br /&gt;
 - l’envoi en masse ou groupé de communications, de SMS, d’emails de
façon automatisée ou non, &lt;br /&gt;
 - l’utilisation partagée du service Data et notamment d’associer la
carte SIM à un équipement de type routeur à des fins de partage de
l’accès entre plusieurs utilisateurs. Les usages de type peer to peer
; newsgroups (partage de ressources entre utilisateurs) sont
interdits, &lt;br /&gt;
 - l’utilisation à des fins de streaming concernant les offres Data
via internet mobile et/ou via une application mobile, &lt;br /&gt;
 - l’utilisation de la carte SIM avec un forfait navigation internet
illimitée dans un terminal en tant que modem. Afin de pouvoir
permettre une utilisation optimale à l’ensemble des Clients titulaires
d’une offre illimitée de navigation internet via internet mobile et/ou
via une application mobile, emails, messagerie instantanée ou
d’abondance, une limitation du débit de l’accès au réseau internet
au-delà d’un usage mensuel indiqué dans les conditions particulières
des offres pourra être mise en œuvre. &lt;br /&gt;
 Le Client reconnaît et accepte que dans la mesure où Internet n’est
pas un réseau sécurisé, il lui incombe de prendre toutes mesures
utiles pour se prémunir contre les virus ou les intrusions de tiers.
Toute transmission par ce biais de données qu’il estime
confidentielles se fera à ses risques et périls.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Donc je résume: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Si par rapport à la moyenne des utilisateurs de cette offre illimité,
je suis trop au dessus, Virgin à le droit de me facturer un surplus ou
de couper ma ligne. J'aimerais bien connaitre l'algorithme, mais en gros
les plus gros consommateurs qui avant voyaient leur bande passante
limitée jusqu'à la fin du mois, peuvent maintenant avoir leur ligne
automatiquement coupée (=on ne peut plus vous appeler, le "numéro n'est
pas attribué"). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Donc l'offre de Virgin&lt;strong&gt; "internet illimité"&lt;/strong&gt; est en fait &lt;strong&gt;"limité
aux bon père de famille"&lt;/strong&gt; selon les critères vagues de Virgin. Et moi,
pauvre Geek naif, j'ai une consommation apparament anormale alors on me
coupe la ligne... donc je suis puni de téléphone pour avoir cru a cette
offre illimité il y a un an. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;J'ai presque envie de contacter des associations de consommateurs ou la
DGCCRF. Car il me parait évident que Virgin ne rentabilise pas ces
anciens forfaits et essaye de limiter la casse avec des moyens pas très
corrects. C'est vraiment dommage: impossible de récupérer le meme
article 10.5 d'il y a un an pour voir s'il a été modifié, je serais
curieux de voir ca. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Après, c'est pas forcément rose chez les autres opérateurs, mais je
pars de Virgin dès que possible. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Si vous êtes dans le même cas ou un cas similaire, merci de commenter.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 19 Nov 2010 16:37:00 +0100</pubDate><guid>http://blog.ziade.org/2010/11/19/personal-fr-comment-je-me-suis-fait-avoir-par-virgin-mobile/</guid></item><item><title>Packaging talk at Pycon 2011</title><link>http://blog.ziade.org/2010/11/13/packaging-talk-at-pycon-2011/</link><description>&lt;p&gt;I have submitted a talk for Distutils at Pycon U.S. 2011 under the
"Extreme Talks" section. The idea is to do a deep-dive talk on how
Distutils2 works and how to use it in your projects. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here's the summary of what I am planning to present: &lt;br /&gt;
   -  Distutils2 presentation and goals&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Framework&lt;/span&gt;

    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;driven&lt;/span&gt; &lt;span class="n"&gt;packaging&lt;/span&gt; &lt;span class="nb"&gt;system&lt;/span&gt;

&lt;span class="o"&gt;-&lt;/span&gt;  &lt;span class="n"&gt;Changes&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;Distutils1&lt;/span&gt;

    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;R&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;I&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;

    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PEP&lt;/span&gt; &lt;span class="mi"&gt;345&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;versions&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PEP&lt;/span&gt; &lt;span class="mi"&gt;386&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;PyPI&lt;/span&gt; &lt;span class="n"&gt;goodies&lt;/span&gt;

     &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;browsing&lt;/span&gt;

     &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;uploading&lt;/span&gt; &lt;span class="n"&gt;docs&lt;/span&gt;

    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;What&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;installed&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;what&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PEP&lt;/span&gt; &lt;span class="mi"&gt;376&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

     &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;Dependency&lt;/span&gt; &lt;span class="n"&gt;graph&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt;

    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Extensibility&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;

     &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;commands&lt;/span&gt;

     &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;compilers&lt;/span&gt;

&lt;span class="o"&gt;-&lt;/span&gt;  &lt;span class="n"&gt;Pysetup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;one&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;rule&lt;/span&gt; &lt;span class="n"&gt;them&lt;/span&gt; &lt;span class="n"&gt;all&lt;/span&gt;

    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;

    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;remove&lt;/span&gt;

    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt; &lt;span class="n"&gt;things&lt;/span&gt;

&lt;span class="o"&gt;-&lt;/span&gt;  &lt;span class="n"&gt;Examples&lt;/span&gt;

    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Example&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="n"&gt;simple&lt;/span&gt; &lt;span class="n"&gt;Distutils2&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt;

    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Example&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Porting&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;Distutils2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;keep&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="n"&gt;working&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;Distutils&lt;/span&gt;&lt;span class="sr"&gt;/Setuptools/&lt;/span&gt;&lt;span class="n"&gt;zc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;buildout&lt;/span&gt; &lt;span class="n"&gt;environments&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

   &lt;span class="err"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Example&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Creating&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;releasing&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="n"&gt;own&lt;/span&gt; &lt;span class="n"&gt;commands&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;compilers&lt;/span&gt;

    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Example&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Developement&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt; &lt;span class="n"&gt;made&lt;/span&gt; &lt;span class="n"&gt;simple&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;Distutils2&lt;/span&gt;

&lt;span class="o"&gt;-&lt;/span&gt;  &lt;span class="n"&gt;Conclusion&lt;/span&gt;

    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Roadmap&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;If you are thinking about something related to Distutils that you would
like to see discussed during this talk, let me know here !&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sat, 13 Nov 2010 11:47:00 +0100</pubDate><guid>http://blog.ziade.org/2010/11/13/packaging-talk-at-pycon-2011/</guid></item><item><title>Distutils 2 alpha 4 - work in progress</title><link>http://blog.ziade.org/2010/11/07/distutils-2-alpha-4-work-in-progress/</link><description>&lt;p&gt;Time flies, and we did not hit the first beta of Distutils2 yet. It's
quite clear now that we won't make it for Python 3.2. But that's not a
big issue because people will be able to install Distutils2 from Python
2.4 to 3.x and use it until it gets back into Python standard library. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;We are going to have a sprint next week-end and try to wrap up a new
alpha. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It should contain: &lt;br /&gt;
-   The work done by &lt;strong&gt;Eric&lt;/strong&gt; on the configure command during the GSOC.
    The&lt;strong&gt; configure command&lt;/strong&gt; let you create a config file that can be
    used by the &lt;strong&gt;build&lt;/strong&gt; or the &lt;strong&gt;install&lt;/strong&gt; command. This fixes one
    issue people have: the install command will not have to run the
    build command again, just to get the options that were used to build
    the project.
-   The work done by &lt;strong&gt;Alexis&lt;/strong&gt; on the installation script. We are
    adding &lt;strong&gt;a 'pysetup' script&lt;/strong&gt; people can use to drive installations,
    un-installations and running any command or feature Distutils2 has.
    One script to rule them all.
-   The work done by &lt;strong&gt;Zubin&lt;/strong&gt; to make Distutils2
    &lt;strong&gt;Python3-compatible&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;Other stuff I've added&lt;/h3&gt;
&lt;p&gt;You can now configure the sub command list the &lt;strong&gt;&lt;em&gt;install&lt;/em&gt;&lt;/strong&gt; or the
&lt;strong&gt;&lt;em&gt;build&lt;/em&gt;&lt;/strong&gt; command uses in distutils.cfg, or setup.cfg. -- Or any other
subcommands in fact -- This means that people will be able to provide a
new sub command that extends &lt;strong&gt;install&lt;/strong&gt; or &lt;strong&gt;build&lt;/strong&gt;, without having to
override the hardcoded list of subcommands they contain. They will also
be able to define the ordering of all subcommands. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;sdist&lt;/strong&gt; command has a new option called &lt;strong&gt;manifest_builders&lt;/strong&gt;,
where you can provide hooks that will be called &lt;strong&gt;&lt;em&gt;just after&lt;/em&gt;&lt;/strong&gt; the
sdist command has created the list of files to include in the
distribution, and &lt;strong&gt;&lt;em&gt;just before&lt;/em&gt;&lt;/strong&gt; the manifest is written. In other
words, you can use it to add or remove files through code. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Other stuff I am working on&lt;/h3&gt;
&lt;p&gt;The compilers in Distutils are still messy and did not get any
attention yet. I'd like to clean them up, to make it easier to register
a new compiler, and to add a tutorial on how to write a new compiler. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Compilers are something I am not very familiar with. Jeremy Kloth, who
worked on 4Suite and on Distutils2 should be of a good help in this
area. I wished we would get some help from other folks from the Python
scientific community since they have specific compiling needs. But
according to David Cournapeau, [&lt;em&gt;"Several extremely talented people in
the scipy community have indepedently attempted to improve this in the
last decade or so, without any success"&lt;/em&gt;][] .. reading his whole blog I
am quite surprised because part of the problems he described with
Distutils are getting solved right now in Distutils2 --some of them
during the GSOC after his criticisms on python-dev-- and other problems
don't sound like unsolvable issues. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Oh well, I can understand that people got fed up with Distutils, it's a
very long process to improve it. And it's a good thing that other people
build other tools. The Python community can have as many build tool as
it wants. The more tools the better. &lt;strong&gt;As long as we all share the same
standards when it comes to publish things at PyPI or install projects in
Python. Interoperability matters. &lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;And that's where Distutils2 has an important role to play. Besides the
command system it provides to build and package stuff, it also provides
implementation of all the latest packaging standards we have built
through PEPs. The one that will be used in the future at all levels in
PyPI and Python. So if you plan on building yet another build tool, you
should have a look at them. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Help us !&lt;/h3&gt;
&lt;p&gt;Distutils2 support can be added &lt;strong&gt;today&lt;/strong&gt; in your project without
interfering with the existing Distutils1/Setuptools/Distribute support.
You can add in your project's &lt;em&gt;setup.cfg&lt;/em&gt; new sections that are specific
to Distutils2 and that will make your project Distutils2 compatible
without any harm. This would be of a great help for us to get some
feedback from real-world projects on Distutils2. If you are interested
and want to help, let me know. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;And, if you have any frustration with distutils, or feature request, we
want to hear them. You can drop by #distutils on Freenode or drop a
mail in our &lt;a href="http://groups.google.com/group/the-fellowship-of-the-packaging"&gt;mailing list&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;[&lt;em&gt;"Several extremely talented people in the scipy community have
  indepedently attempted to improve this in the last decade or so,
  without any success"&lt;/em&gt;]: http://cournape.wordpress.com/2010/10/13/271/&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sun, 07 Nov 2010 23:53:00 +0100</pubDate><guid>http://blog.ziade.org/2010/11/07/distutils-2-alpha-4-work-in-progress/</guid></item><item><title>Firefox Sync : deploying Python apps on RPM-based systems</title><link>http://blog.ziade.org/2010/11/04/firefox-sync-deploying-python-apps-on-rpm-based-systems/</link><description>&lt;p&gt;The Python Firefox Sync server is going to be deployed at Mozilla in
production early next year, on RHEL-based servers. Working on packaging
matters in Python, I focused a lot on the best way to deploy our Python
applications and libraries, and make every involved party happy. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Our environment&lt;/h3&gt;
&lt;p&gt;Unlike a fully-featured website, our various web applications for
Firefox Sync don't have a lot of dependencies besides Python itself.
This means that we don't have a huge list of dependencies to deploy to
set up every part of the infrastructure. Another important point is that
unlike smaller projects where a single server/VM might manage several
applications whose dependencies can conflict, we have dedicated
environments for each application we are deploying. Hey, I am happily
running 5 small websites with 5 different environments on my own server,
thanks to zc.buildout, but for Sync it's the other way around: many
servers for the same app :) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In other words, tools like zc.buildout or virtualenv -- which provide
application-level isolated environments -- are not really needed. We can
happily install the different bits in the same Python environment. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Repository-oriented vs Release-oriented&lt;/h3&gt;
&lt;p&gt;One approach I have seen is to deploy applications in a directory using
Git or Mercurial and just pull the code from a repository on a specific
tag or branch. Dependencies can be fetch the same way by using a vendor
repository, or simply installed locally using Pip or easy_install. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The biggest caveat of this approach is that there's no more static
archive that freezes everything into a single object that can be
manipulated by usual packaging systems the system provide -- The Python
packaging system or the OS packaging system -- &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;OS Packaging systems provide a lot of automation for sysadmins and more
features for pre- and post- installation steps for the packager. Instead
of using custom recipes to upgrade the application, they can use what
they are using for everything else on the system, and with much more
features like the ability to mark some files in the metadata as
configuration files and tell the installer about it to avoid overriding
existing files, etc. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The Python packaging system is also simpler than a repository-based
system when you want to run the application in any Operating System. You
just install the application that was released, and don't bother with
source repositories. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Distutils-based and RPM-based&lt;/h3&gt;
&lt;p&gt;Sync is packaged in RPMs and in Distutils source archives. Every
distribution has a .spec file containing the RPM metadata and also a
setup.py file. Releasing the project into Distutils-based archive is
done so people in the community that wish to deploy the server in a
non-RPM based system can do it. In a near future, a simple "pip install
SyncServer" will do the trick for them. They'll have extra manual steps,
like hooking the app in the web server of their choice, but they won't
have to checkout many repositories and create a dedicated environment. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For the RPM based distribution, I was a bit annoyed by the
&lt;strong&gt;bdist_rpm&lt;/strong&gt; command Python/Distutils provides. It's not good enough
to easily tweak the RPM creation process and you always end-up writing
your own &lt;em&gt;.spec&lt;/em&gt; file. But build_rpm do provide a nice automation: it
calls rpmbuild with good enough options, and for some distributions I
don't need to write a custom .spec file as long as I am able to define a
custom name for the RPM project. For instance, I prefix all my
distributions with &lt;strong&gt;&lt;em&gt;python26-&lt;/em&gt;&lt;/strong&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Since we've removed build_rpm from Distutils2, I ended up writing an
enhanced version in a project called pypi2rpm, that provides two new
options: --name and --spec-file. I use them to build RPM on projects
that have custom .spec files, or when I want to force the name of the
RPM with a direct command-line option. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The pypi2rpm project also provide a nice command-line script that uses
the power of Distutils2 to generate RPMs for the latest versions of any
project, as long as it is published at PyPI. That's useful to complete
my RPM collection when I cannot find a RPM for a project. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So, if I want to create a RPM for the latest WebOb release, I do: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;$ pypi2rpm.py webob &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I've pushed pypi2rpm at PyPI if you want to play with it. It's a work
in progress and lacks of documentation, but is good enough for me for
what I need to do right now. If you are interested in this small project
let me know !&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 04 Nov 2010 18:43:00 +0100</pubDate><guid>http://blog.ziade.org/2010/11/04/firefox-sync-deploying-python-apps-on-rpm-based-systems/</guid></item><item><title>MoPy = Mozilla + Python</title><link>http://blog.ziade.org/2010/11/04/mopy-mozilla-python/</link><description>&lt;p&gt;Python is gaining a lot of traction in many Mozilla projects. From the
&lt;a href="http://support.mozilla.com/"&gt;SUMO&lt;/a&gt; and &lt;a href="https://addons.mozilla.org/"&gt;AMO&lt;/a&gt; websites, to &lt;a href="http://blog.mozilla.com/webdev/2010/05/19/socorro-mozilla-crash-reports/"&gt;Socorro&lt;/a&gt; and the next-gen &lt;a href="http://www.mozilla.com/firefox/sync/"&gt;Firefox
Sync&lt;/a&gt; server. I can fairly say that it becomes a mainstream language
for building server-side software in our eco-system. This is making me
really happy, since I am involved in the Python project. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;[caption id="" align="alignright" width="381" caption="The MoPy fish -
a 1997 cyberpet :)"]&lt;img alt="image" src="http://upload.wikimedia.org/wikipedia/en/3/39/MOPy&amp;amp;Plant.JPG" /&gt;[/caption] &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I won't blog here about the pro's and con's of Python versus PHP or
Java etc., there are already a lot of good articles out there about it.
I'd be biased anyways ;). The least I can say is that it makes a lot of
sense for us at all levels, to use Python and its great libs and
frameworks. To name a few: &lt;br /&gt;
-   Django, used by AMO and SUMO
-   WebOb and Paste, used by Sync (next-gen) to build wsgi-based Web
    Services
-   SQLAlchemy, used by Sync (next-gen) to work with our databases.&lt;/p&gt;
&lt;p&gt;When I joined Mozilla, one thing that I really wanted to do was to
start a "Python in Mozilla" informal user group. A space where people
working on the various Mozilla projects could share about Python. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I started a mailing list here : &lt;a href="http://groups.google.com/group/mozilla-python"&gt;MoPy Mailing List&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;We also had a small meeting at Mountain View HQ. The WebDev team was in
town so it was a good opportunity to do it. We talked about our various
approaches to handle configuration files in our projects and that was
quite interesting to share. We had people from WebDev, Labs and
Services. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I'd love to share ideas about Python with the whole Mozilla community,
and maybe try to organise more meetings in the future. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you are involved in a Mozilla project, and use Python -- or simply
curious about Python -- please join the mailing list and don't be shy to
post. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Semi-related: I have submitted a talk for the next Pycon in Atlanta
about Firefox Sync, and will try to organize a Mozilla BoF there as
well.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;The MoPy fish - a 1997 cyberpet :)&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 04 Nov 2010 15:28:00 +0100</pubDate><guid>http://blog.ziade.org/2010/11/04/mopy-mozilla-python/</guid></item><item><title>A quick glimpse at Distutils2 alpha3 - part 2</title><link>http://blog.ziade.org/2010/10/03/a-quick-glimpse-at-distutils2-alpha3-part-2/</link><description>&lt;p&gt;It seems that doing a little bit of teasing on the next release
generates valuable feedback. So I'll do more :) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In the next version we have this really useful module called
&lt;strong&gt;depgraph&lt;/strong&gt; (&lt;a href="http://distutils2.notmyidea.org/library/distutils2.depgraph.html#"&gt;full doc&lt;/a&gt;). It creates a dependency graph to study
installed distribution, but also to-be-installed distributions. This is
the basis of any installer script but is also useful for users who want
to know what's installed.&lt;img alt="image" src="http://distutils2.notmyidea.org/_images/depgraph_output.png" /&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Setuptools has a module called &lt;strong&gt;pkg_resource&lt;/strong&gt;s that would allow to
create such a feature, but distutils2 used an enhanced version of the
stdlib module &lt;strong&gt;pkgutil&lt;/strong&gt; which now supports &lt;a href="http://python.org/dev/peps/pep-0376"&gt;PEP 376&lt;/a&gt; but also offers
a compatibility mode to be able to browse packages that were installed
by pre-PEP 376 installers like Pip or easy_install. The new &lt;strong&gt;pkgutil&lt;/strong&gt;
module lives in a _backport package in distutils2 but will be pushed
back to the stdlib as soon as we reach a stable version for distutils2.
It will make pkg_resources obsolete for the part that let you iterate
on installed projects and will provide more. (Note that pkg_resources
contains much more features besides.) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But enough talking, try to build your depgraph yourself ! depgraph can
be called as a script to generate dependency graphs in the stdout, or as
.dot (graphviz) files. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here's a small demo: &lt;br /&gt;
   # installing the latest tip&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;sudo&lt;/span&gt; &lt;span class="n"&gt;easy_install&lt;/span&gt; &lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;bitbucket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/tarek/&lt;/span&gt;&lt;span class="n"&gt;distutils2&lt;/span&gt;&lt;span class="sr"&gt;/get/&lt;/span&gt;&lt;span class="n"&gt;bdfaec90d665&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gz&lt;/span&gt;

&lt;span class="n"&gt;Downloading&lt;/span&gt; &lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;bitbucket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/tarek/&lt;/span&gt;&lt;span class="n"&gt;distutils2&lt;/span&gt;&lt;span class="sr"&gt;/get/&lt;/span&gt;&lt;span class="n"&gt;bdfaec90d665&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gz&lt;/span&gt;

&lt;span class="n"&gt;Processing&lt;/span&gt; &lt;span class="n"&gt;bdfaec90d665&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gz&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;# what do we have installed ?&lt;/span&gt;

&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;python&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="n"&gt;distutils2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;depgraph&lt;/span&gt;

&lt;span class="n"&gt;Dependency&lt;/span&gt; &lt;span class="n"&gt;graph:&lt;/span&gt;

 &lt;span class="n"&gt;PasteDeploy&lt;/span&gt; &lt;span class="mf"&gt;1.3.3&lt;/span&gt;

 &lt;span class="n"&gt;virtualenv&lt;/span&gt; &lt;span class="mf"&gt;1.4.9&lt;/span&gt;

 &lt;span class="n"&gt;pyflakes&lt;/span&gt; &lt;span class="mf"&gt;0.4.0&lt;/span&gt;

 &lt;span class="o"&gt;...&lt;/span&gt;

 &lt;span class="n"&gt;Distutils2&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="n"&gt;a3&lt;/span&gt;

 &lt;span class="n"&gt;ropemode&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;rc2&lt;/span&gt;

   &lt;span class="n"&gt;rope&lt;/span&gt; &lt;span class="mf"&gt;0.9.3&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;rope&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.9.2&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;# let&amp;#39;s create a dot file of the dependencies&lt;/span&gt;

&lt;span class="c1"&gt;# if a distribution don&amp;#39;t have a dependency it&amp;#39;s not added - so you don&amp;#39;t get crazy graphs&lt;/span&gt;

&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;python&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="n"&gt;distutils2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;depgraph&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;

&lt;span class="n"&gt;Dot&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="n"&gt;written&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;depgraph.dot&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;# let&amp;#39;s create an image&lt;/span&gt;

&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;dot&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Tpng&lt;/span&gt; &lt;span class="n"&gt;depgraph&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dot&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;depgraph&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;png&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Try it out and let us know how it worked for you !&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;Depgraph example&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sun, 03 Oct 2010 13:37:00 +0200</pubDate><guid>http://blog.ziade.org/2010/10/03/a-quick-glimpse-at-distutils2-alpha3-part-2/</guid></item><item><title>A quick glimpse at Distutils2 1.0 alpha3</title><link>http://blog.ziade.org/2010/10/02/a-quick-glimpse-at-distutils2-10-alpha3/</link><description>&lt;p&gt;We're busy preparing the third alpha of Distutils2 1.0. I am pretty
excited about it because all of the hard work that was done during the
GSOC is starting to surface. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The major feature in alpha3 will be the fact that&lt;strong&gt; setup.py is gone&lt;/strong&gt;.
Everything is now driven from scripts you can run using -m. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;To describe your project you can now use &lt;strong&gt;setup.cfg&lt;/strong&gt;, or better : use
the &lt;strong&gt;mkpkg&lt;/strong&gt; wizard which will ask you a few questions and create it
for you. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In the demo below, I am installing distutils2 in a virtualenv and I am
creating a setup.cfg for a project that contains the 'mycode' package.
Then I am creating a distribution, installing it and testing it. &lt;br /&gt;
   # creating a temp dir with a virtualenv in it&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;cd&lt;/span&gt; &lt;span class="sr"&gt;/tmp/&lt;/span&gt;&lt;span class="n"&gt;demo&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;

&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;virtualenv&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nb"&gt;no&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;packages&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;New&lt;/span&gt; &lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="n"&gt;executable&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="sr"&gt;/bin/&lt;/span&gt;&lt;span class="n"&gt;python2&lt;/span&gt;&lt;span class="mf"&gt;.6&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;# installing Distutils2 from the tip&lt;/span&gt;

&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;bin&lt;/span&gt;&lt;span class="sr"&gt;/easy_install http://bitbucket.org/&lt;/span&gt;&lt;span class="n"&gt;tarek&lt;/span&gt;&lt;span class="sr"&gt;/distutils2/g&lt;/span&gt;&lt;span class="n"&gt;et&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tip&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gz&lt;/span&gt;

&lt;span class="n"&gt;Downloading&lt;/span&gt; &lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;bitbucket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/tarek/&lt;/span&gt;&lt;span class="n"&gt;distutils2&lt;/span&gt;&lt;span class="sr"&gt;/get/&lt;/span&gt;&lt;span class="n"&gt;tip&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gz&lt;/span&gt;

&lt;span class="n"&gt;Processing&lt;/span&gt; &lt;span class="n"&gt;tip&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gz&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;# creating a Python package to include to the project&lt;/span&gt;

&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;mkdir&lt;/span&gt; &lt;span class="n"&gt;mycode&lt;/span&gt;

&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;echo&lt;/span&gt; &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;I am alive\&amp;quot; &amp;gt; mycode/__init__.py&lt;/span&gt;

&lt;span class="s"&gt;# running the wizard that creates a setup.cfg for me&lt;/span&gt;

&lt;span class="s"&gt;# Thanks god it takes care of the Trove classifiers for me now !&lt;/span&gt;

&lt;span class="s"&gt;$ bin/python -m distutils2.mkpkg&lt;/span&gt;

&lt;span class="s"&gt;Project name [demo]:&lt;/span&gt;

&lt;span class="s"&gt;Current version number: 1.0&lt;/span&gt;

&lt;span class="s"&gt;Package description:&lt;/span&gt;

&lt;span class="s"&gt;   &amp;gt; short demo&lt;/span&gt;

&lt;span class="s"&gt;Author name [Tarek]: Tarek&lt;/span&gt;

&lt;span class="s"&gt;Author e-mail address [tarek@ziade.org]:&lt;/span&gt;

&lt;span class="s"&gt;Project Home Page: http://bitbucket.org/tarek/distutils2/wiki/Home&lt;/span&gt;

&lt;span class="s"&gt;Do you want to add a package ? (y/n): y&lt;/span&gt;

&lt;span class="s"&gt;Package name: mycode&lt;/span&gt;

&lt;span class="s"&gt;Do you want to add a package ? (y/n): n&lt;/span&gt;

&lt;span class="s"&gt;Do you want to set Trove classifiers? (y/n): y&lt;/span&gt;

&lt;span class="s"&gt;Please select the project status:&lt;/span&gt;

&lt;span class="s"&gt;1 - Planning&lt;/span&gt;

&lt;span class="s"&gt;2 - Pre-Alpha&lt;/span&gt;

&lt;span class="s"&gt;3 - Alpha&lt;/span&gt;

&lt;span class="s"&gt;4 - Beta&lt;/span&gt;

&lt;span class="s"&gt;5 - Production/Stable&lt;/span&gt;

&lt;span class="s"&gt;6 - Mature&lt;/span&gt;

&lt;span class="s"&gt;7 - Inactive&lt;/span&gt;

&lt;span class="s"&gt;Status: 3&lt;/span&gt;

&lt;span class="s"&gt;What license do you use: GPL&lt;/span&gt;

&lt;span class="s"&gt;Matching licenses:&lt;/span&gt;

&lt;span class="s"&gt;   1) License :: OSI Approved :: GNU General Public License (GPL)&lt;/span&gt;

&lt;span class="s"&gt;   2) License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)&lt;/span&gt;

&lt;span class="s"&gt;Type the number of the license you wish to use or ? to try again:: 1&lt;/span&gt;

&lt;span class="s"&gt;Do you want to set other trove identifiers (y/n) [n]: n&lt;/span&gt;

&lt;span class="s"&gt;Wrote &amp;quot;&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;.&lt;/span&gt;

&lt;span class="s"&gt;# checking the result&lt;/span&gt;

&lt;span class="s"&gt;$ more setup.cfg&lt;/span&gt;

&lt;span class="s"&gt;[metadata]&lt;/span&gt;

&lt;span class="s"&gt;name = demo&lt;/span&gt;

&lt;span class="s"&gt;version = 1.0&lt;/span&gt;

&lt;span class="s"&gt;author = Tarek&lt;/span&gt;

&lt;span class="s"&gt;author_email = tarek@ziade.org&lt;/span&gt;

&lt;span class="s"&gt;description = short demo&lt;/span&gt;

&lt;span class="s"&gt;home_page = http://bitbucket.org/tarek/distutils2/wiki/Home&lt;/span&gt;

&lt;span class="s"&gt;classifier = Development Status :: 3 - Alpha&lt;/span&gt;

&lt;span class="s"&gt;    License :: OSI Approved :: GNU General Public License (GPL)&lt;/span&gt;

&lt;span class="s"&gt;[files]&lt;/span&gt;

&lt;span class="s"&gt;packages = mycode&lt;/span&gt;

&lt;span class="s"&gt;# trying to build a source distribution&lt;/span&gt;

&lt;span class="s"&gt;$ bin/python -m distutils2.run sdist&lt;/span&gt;

&lt;span class="s"&gt;running sdist&lt;/span&gt;

&lt;span class="s"&gt;# installing the distribution in the virtualenv&amp;#39;ed Python&lt;/span&gt;

&lt;span class="s"&gt;$ bin/python -m distutils2.run install&lt;/span&gt;

&lt;span class="s"&gt;running install&lt;/span&gt;

&lt;span class="s"&gt;...&lt;/span&gt;

&lt;span class="s"&gt;# is my project properly installed ?&lt;/span&gt;

&lt;span class="s"&gt;$ bin/python&lt;/span&gt;

&lt;span class="s"&gt;Python 2.6.5 (r265:79063, Apr 16 2010, 13:57:41)&lt;/span&gt;

&lt;span class="s"&gt;[GCC 4.4.3] on linux2&lt;/span&gt;

&lt;span class="s"&gt;Type &amp;quot;&lt;/span&gt;&lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;, &amp;quot;&lt;/span&gt;&lt;span class="n"&gt;copyright&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;, &amp;quot;&lt;/span&gt;&lt;span class="n"&gt;credits&lt;/span&gt;&lt;span class="s"&gt;&amp;quot; or &amp;quot;&lt;/span&gt;&lt;span class="n"&gt;license&lt;/span&gt;&lt;span class="err"&gt;&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;more&lt;/span&gt; &lt;span class="n"&gt;information&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;mycode&lt;/span&gt;

&lt;span class="n"&gt;I&lt;/span&gt; &lt;span class="n"&gt;am&lt;/span&gt; &lt;span class="n"&gt;alive&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;I'm hoping to have alpha3 released this week-end or early next week. &lt;br /&gt;
&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sat, 02 Oct 2010 15:44:00 +0200</pubDate><guid>http://blog.ziade.org/2010/10/02/a-quick-glimpse-at-distutils2-10-alpha3/</guid></item><item><title>Twisted rocks !</title><link>http://blog.ziade.org/2010/09/30/twisted-rocks/</link><description>&lt;p&gt;When your LDAP server or your MySQL server starts to get really slow for
any reason, you better make sure your web application don't wait too
much for them. Otherwise you will just have more and more requests
piling up, waiting for your HTTP server to kill them when they reach the
server timeout. Apache default timeout is 300 seconds by the way, so
depending on how many users your application serves, you might end up
with a big bottleneck in your application and a potential disaster on
the server resources. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;One solution is to make sure your web application handles itself the
problem, by configuring a tight timeout for every third party server it
calls, like a SQL server or a LDAP Server. So whenever things gets too
slow, you can return immediately a 503 to the client and free the
thread. A &lt;a href="http://webee.technion.ac.il/labs/comnet/netcourse/CIE/RFC/2068/201.htm"&gt;Retry-Later&lt;/a&gt; header can also be added to inform the client.&lt;/p&gt;
&lt;p&gt;For Sync, we have another header that is specifically looked up when
things gets bad on server side, which is X-Weave-Backoff. But that
header is used only on successful operations to gently ask the client to
back off for some time. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Anyways, in theory it's quite simple to add some&lt;em&gt; try..except timout:&lt;/em&gt;
code in your application but in practice you better test it for real by
benching your application with slow third party servers and check that
the server does not melt in that case. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Not all servers (LDAP, SQL, etc) provide a way to make things slower
and not all Operating Systems provide a simple way to slow down the
network between two applications. &lt;a href="http://www.linuxfoundation.org/collaborate/workgroups/networking/netem"&gt;netem&lt;/a&gt; is a nice tool but you might
need to recompile your kernel to use it, and you have to be on some
flavor of BSD or Linux for that. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Twisted excels for such tasks. I could write a port-forwarding script
to simulate delays in less than 15 lines (that would take probably 100
lines using plain socket/asyncore). &lt;br /&gt;
   import time&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;twisted&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;internet&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;reactor&lt;/span&gt;

&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;twisted&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;protocols&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;portforward&lt;/span&gt;

&lt;span class="n"&gt;class&lt;/span&gt; &lt;span class="n"&gt;LoggingProxyServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;portforward&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ProxyServer&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;dataReceived&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;portforward&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ProxyServer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dataReceived&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;class&lt;/span&gt; &lt;span class="n"&gt;LoggingProxyFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;portforward&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ProxyFactory&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;protocol&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LoggingProxyServer&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

    &lt;span class="n"&gt;fwd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LoggingProxyFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;localhost&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;389&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;reactor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;listenTCP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;390&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fwd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;reactor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Once this script is launched, my application can use the port 390 to
connect to LDAP, and deal with the timeouts. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;From there, I am &lt;a href="http://bitbucket.org/tarek/sync-server/src/tip/tests/delay/delay.py"&gt;exploring different ways&lt;/a&gt; to delay the calls in a
realistic manner. Like making the delay get bigger at every call until
it reaches a max, then reducing it, etc. It's also a good way to log all
the TCP conversations between the apps without having to set up a
dedicated app like &lt;a href="http://www.wireshark.org/"&gt;WireShark&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I already knew this, since Twisted was the framework we used in my
previous company, but let me say it again: when it comes to write little
network tools like that, &lt;strong&gt;Twisted just rocks&lt;/strong&gt;.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 30 Sep 2010 18:14:00 +0200</pubDate><guid>http://blog.ziade.org/2010/09/30/twisted-rocks/</guid></item><item><title>Afpy Camp - Python Sprint Wrap-up</title><link>http://blog.ziade.org/2010/09/22/afpy-camp-python-sprint-wrap-up/</link><description>&lt;p&gt;The Afpy Computer Camp was great as usual. We were fifteen of us
sprinting at my house and we had a lot of fun watching zombies movies
(Shawn of the Dead. Zombie Land) and eating that suckling Pig I bought.&lt;/p&gt;
&lt;p&gt;[caption id="" align="alignright" width="315" caption="The packaging
pig is dead "]&lt;img alt="image" src="http://farm5.static.flickr.com/4109/5000775607_c8b6f9673d.jpg" /&gt;[/caption] &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;On the coding side I must admit I did not do as much as I wanted, which
is a shame since I had around me great folks to work on testing,
packaging etc. I had to take care of the catering and the train station
shuttling. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I did find some time to brainstorm with Michael on plugins and some
code should follow this week. The idea is to provide a entry-point like
set of API in the version of pkgutil we have in distutils2, which
complements PEP 376 and is supposed to update the module we have in the
stdlib. Michael will use it for unittest2 once I ship a working version.&lt;/p&gt;
&lt;p&gt;I also did quite some work with Holger, who helped me with is Tox tool
to set up a Continuous Integration server for Distutils2 that tests
multiple Python version. Yay! Its up and running but I need to add some
ACLs before I publish it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Next we talked about the best approach for people to start using
Distutils2 amd Holger came up with great ideas. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;We will provide as planned an alpha version that has a run module that
can be run to execute distutils2 on a project. No more setup.py here,
the script will just read the setup.cfg and read all options from there,
including a section where all metadata are described. &lt;br /&gt;
   $ python -m distutils2.run install&lt;/p&gt;
&lt;p&gt;That's already committed and I have one more feature to add before I
released Distutils2 1.0a3 : a small hook to allow people to run
arbitrary code in the process. This hook won't be able to change the
metadata so we don't break the "static metadata" paradigm we want to
provide -- see PEP 345 --. It will be useful though to work on build
options etc.&lt;img alt="image" src="http://farm5.static.flickr.com/4152/5005266723_3f9db72e71.jpg" /&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Projects will be able to keep their setup,py file for Distutils1 and
Setuptools (that's the new idea :D) , and add sections in their
setup.cfg to make the project Distutils2 compatible in the same time.
IOW each Distutils version will not step on the other version toes. I am
more concerned about the adoption of distutils2 in the various
installers like Pip or easy_install but that's a good complement to
have. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I had a great time with great folks, thanks to the PSF / Jesse Noller
for the $250 grant we are going to use for paying part of the travel
expenses for Michael and Holger. Thanks also to Logilab who sent us
Pierres-Yves. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I am looking forward to read other people wrap-up, and also to next
year Camp ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.afpy.org/photos/afpy_barcamp_3"&gt;More Pictures&lt;/a&gt;&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;Packaging Pig&amp;quot;&lt;/span&gt;
&lt;span class="s"&gt;&amp;quot;Debian FTW&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Wed, 22 Sep 2010 21:39:00 +0200</pubDate><guid>http://blog.ziade.org/2010/09/22/afpy-camp-python-sprint-wrap-up/</guid></item><item><title>Firefox Sync Server in Python - Take 2</title><link>http://blog.ziade.org/2010/09/21/firefox-sync-server-in-python-take-2/</link><description>&lt;p&gt;It's been more than a month since the last update on my work on Firefox
Sync. Time for a quick update. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;The Code&lt;/h3&gt;
&lt;p&gt;The application grew quite well and was splitted in four separate
projects: &lt;br /&gt;
-   &lt;strong&gt;SyncCore&lt;/strong&gt;: contains the authentication back-ends and various
    utilities like the CEF logger or various WSGI helpers.
-   &lt;strong&gt;SyncReg&lt;/strong&gt;: that's the User application. Implements:
    &lt;a href="https://wiki.mozilla.org/Labs/Weave/User/1.0/API"&gt;https://wiki.mozilla.org/Labs/Weave/User/1.0/API&lt;/a&gt;. Can be used as
    a standalone WSGI application
-   &lt;strong&gt;SyncStorage&lt;/strong&gt;: Contains the storage back-ends and implements
    &lt;a href="https://wiki.mozilla.org/Labs/Weave/Sync/1.0/API"&gt;https://wiki.mozilla.org/Labs/Weave/Sync/1.0/API&lt;/a&gt; (and the
    upcoming 1.1.) Can be used as a standalone WSGI application.
-   &lt;strong&gt;SyncServer&lt;/strong&gt;: This is just a glue application that can be used to
    run in the same server both Reg and Storage servers. By default this
    application will run sqlite back-ends for storage and
    authentication, which means it can be launched with a zero-config
    environment.&lt;/p&gt;
&lt;p&gt;I moved the code to bitbucket, and will clone it back to hg.mozilla.org
once we set dedicated repositories for the Python server there. If you
want to run your own Sync server, it's still very simple. Make sure you
have the latest virtualenv installed, Mercurial and Make, then run: &lt;br /&gt;
   $ hg clone http://bitbucket.org/tarek/sync-server Sync&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;cd&lt;/span&gt; &lt;span class="n"&gt;Sync&lt;/span&gt;

&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;make&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Then you can run your server on port 5000 by using the built-in web
server: &lt;br /&gt;
   $ bin/paster serve development.ini&lt;/p&gt;
&lt;p&gt;Of course, a real setup should be done using SSL, a real web server
like Apache/mod_wsgi and MySQL for the DB. But the default setup is
useful and can replace the minimal-server Toby wrote. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Benching&lt;/h3&gt;
&lt;p&gt;One thing I want to make sure is that the Python server is as fast as
possible, and faster than the PHP application. Since a Python web
application can reuse the same interpreter in memory, there's a lot of
room for improvements like connection pooling and light memory caching.
I also wanted to bench out various configurations for the DB, like using
postgresql instead of mysql etc. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The team is currently working on stress testing our Sync infrastructure
and the tool that we use is &lt;a href="http://grinder.sourceforge.net/"&gt;Grinder&lt;/a&gt;. Grinder is a Java tool that
uses Jython for writing tests, and provides a simple console to drive
it. The results Grinder return are raw results, and there's quite some
work left to do if you want to generate nice reports. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I used another tool to bench the server called &lt;a href="http://funkload.nuxeo.org/"&gt;Funkload&lt;/a&gt;. It's a
Python tool that uses unittest classes to run benches, and provides a
functional test tool to query a web server and do some assertions like
&lt;a href="http://pythonpaste.org/webtest/"&gt;WebTest&lt;/a&gt;. It produces HTML reports that are containing a lot of
metrics. Some I don't use because they are specific to web sites. But
it's good enough to stress-test the Sync server and compare PHP and
Python speed. One caveat is that it cannot be distributed. There's a
project called &lt;a href="http://pypi.python.org/pypi/benchmaster"&gt;BenchMaster&lt;/a&gt; that adds this feature, that I need to
try. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The stress test is the same than the Grinder one, and here are some
reports using various configuration :
&lt;a href="http://sync.ziade.org/funkload/"&gt;http://sync.ziade.org/funkload&lt;/a&gt;/ &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;[&lt;img alt="image" src="http://sync.ziade.org/funkload/python-postgres/requests_rps.png" /&gt;][]While Python already appears to be slightly faster than
PHP, those were done on my MacBook with 100 users loaded in the DB, 6000
objects each, so don't mean a lot. Just that the Python application is
not borked :D . &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I'll probably run Funkload in the same environment we run Grinder at
Mozilla, where we have a realistic setup. I also want to have this kind
of reports generated every day, so I can keep an eye on how the Python
server performs. Making sure the app does not slow down when it grows is
one important part of continuous integration. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Caching: Redis vs Memcached&lt;/h3&gt;
&lt;p&gt;I used &lt;a href="http://code.google.com/p/redis/"&gt;Redis&lt;/a&gt; to do a bit of caching in the Python app, instead of
Memcached like the PHP app. See my previous post for the rational. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Redis was very stable during my benches, but I have heard from some
other projects that they had quite a few problems with it in production
[I might post more details here in another blog post]. I still think
this is the tool we should use in Sync, and I also want to experiment
writing a full back-end for Sync using it. But the first version of the
Sync server we will deploy on our servers will probably use Memcached
since it's proven to work well right now and since I don't really need
all the extra features Redis offers if the usage is restricted to
volatile caching. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Continuous integration&lt;/h3&gt;
&lt;p&gt;&lt;img alt="image" src="https://hudson.mozilla.org/plugin/chucknorris/images/alert.jpg" /&gt;I am still working alone on the Python app, but a continuous
integration server is something we really want to have. I am a big fan
of buildbot but I wanted to give a try at Hudson. The management
interface is brilliant and I could set up a Hudson server for Sync in an
hour. I eventually moved it at &lt;a href="https://hudson.mozilla.org/job/Sync/"&gt;https://hudson.mozilla.org/job/Sync&lt;/a&gt;
with other Mozilla projects from the WebDev team. It contains Pylint
reports, test coverage report, and of course Chuck Norris keeps the code
safe. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;What's next ?&lt;/h3&gt;
&lt;p&gt;The Python app is mostly done, besides a few things to clean up. The
next big step will be to bench it alongside the PHP application on
realistic data, fix any problem that will rise, then work on pushing it
in production. The production switch will probably happen gradually
since every node is standalone. And since the rest of the team is quite
busy to make sure everything is ready for the upcoming Firefox 4 final
release which includes Sync natively, switching to Python is not the #1
priority right now. I expect it to happen before the end of the year
though. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In the meantime once the benches are done and the code is rock-solid.
I'll start to play with different back-ends. A full Redis back-end and
maybe something based on Riak or Cassandra.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;Funkload Report&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;[&lt;img alt="image" src="http://sync.ziade.org/funkload/python-postgres/requests_rps.png" /&gt;]: http://sync.ziade.org/funkload
    "Chuck Norris"&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 21 Sep 2010 17:11:00 +0200</pubDate><guid>http://blog.ziade.org/2010/09/21/firefox-sync-server-in-python-take-2/</guid></item><item><title>Pycon France next week-end</title><link>http://blog.ziade.org/2010/08/23/pycon-france-next-week-end/</link><description>&lt;p&gt;&lt;a href="http://www.pycon.fr/conference/edition2010"&gt;Pycon France&lt;/a&gt; will be starting Saturday in Paris. Make sure you're
there if you are in Paris. It's Free. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here are a list of talks I don't want to miss (I want to see them all
more of course): &lt;br /&gt;
-   GUnicorn, the wsgi server
-   MongoDB and MongoKit
-   Python in Debian&lt;/p&gt;
&lt;p&gt;The full program: [http://www.pycon.fr/#talksschedule][]&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Mon, 23 Aug 2010 16:48:00 +0200</pubDate><guid>http://blog.ziade.org/2010/08/23/pycon-france-next-week-end/</guid></item><item><title>Distutils 2 - Summary of the GSOC</title><link>http://blog.ziade.org/2010/08/19/distutils-2-summary-of-the-gsoc/</link><description>&lt;p&gt;Man this has been an amazing summer. We had 5 students working on
Distutils 2 and the work done was really great. Most important, I think
we managed to find new contributors for the future. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Thanks to Josip, Alexis, Konrad, Eric and Zubin ! Also, thanks to their
respective mentors: Michael, Fred, Titus and Lennart. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Let's have a very high-level summary of the tasks that were done. As a
reminder, Distutils2 aims to provide two things: a toolbox for third
party projects that want to provide packaging features, and a
full-featured package installer/manager for Python. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;What was done&lt;/h3&gt;
&lt;p&gt;Distutils2 has now an &lt;strong&gt;index&lt;/strong&gt; package, containing tools to work with
PyPI (or any PyPI-like server). This package contains classes to work
with the Simple Index protocol as well as the XML-RPC APIs PyPI has. We
think that the latter should be replaced by static REST calls, but
that's another topic. If you want to build a tool that uses PyPI, that's
the package you want to use. See &lt;a href="http://distutils2.notmyidea.org/library/distutils2.index.html"&gt;this doc&lt;/a&gt; (temporary location) for
more info. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Another thing we added is an installer script. This script is a very
light script that uses the index package I've mentioned before, but also
a dependency graph builder we now have. The graph builder allows you to
analyze relations between installed projects, but also any project you
want to install. See &lt;a href="http://bitbucket.org/josip/distutils2/src/tip/docs/source/depgraph.rst"&gt;this doc&lt;/a&gt;. This installation script will be
completed by an uninstallation feature very soon hopefully. You might
wonder why we did this since there are existing scripts like Pip or
easy_install. The reasons are quite simple: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;1) we wanted to exercise all the modules Distutils2 provides to work
with PyPI, metadata and installed projects in a high level script. The
long-term goal is to have projects like Pip or Distribute use
Distutils2-the-toolbox since they'll use the same standards in the
future. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;2) Distutils2-the-package-manager wants to provide a basic
installer/uninstaller. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Oh by the way, the depgraph tool is PEP 376 compatible, as we have now
a new version of pkgutil that supports PEP 376. See &lt;a href="http://bitbucket.org/josip/distutils2/src/tip/docs/source/pkgutil.rst"&gt;this doc&lt;/a&gt;. The
next task will be to put this new version in Python 3.2, and that should
happen fairly soon. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Speaking of which, we are not sure yet if we are going to include an
plugin system like the entry points in Distutils2. Discussions on this
has been controversial on Python-dev. In the meantime you can enjoy the
&lt;a href="http://bitbucket.org/tarek/extensions"&gt;extensions&lt;/a&gt; project, which is a quick hack to have entry points
without depending on Setuptools or Distribute --and it's now Distutils2
and PEP 376 compatible--. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Distutils2 is &lt;em&gt;almost&lt;/em&gt; Python 3 compatible. The student in charge is
working hard to finish this task. See his &lt;a href="http://bitbucket.org/zubin71/distutils2-py3"&gt;repo&lt;/a&gt;. As usual, the last
problems to be solved are about unicode, strings, bytes, you named it.
But I expect to include this work in one of the alpha version of
Distutils2 1.0. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Last but not least, we worked on the commands front of Distutils2: &lt;br /&gt;
-   &lt;strong&gt;test&lt;/strong&gt; was added -- a command to run the project tests, inspired
    from Setuptools
-   &lt;strong&gt;upload_docs&lt;/strong&gt; was added -- a command to upload docs at
    packages.python.org. Originally created by Jannis Leidel and present
    in Distribute
-   &lt;strong&gt;check&lt;/strong&gt; was added and improved -- a command that I created to
    check a project before releasing/uploading it. It checks its
    metadata, its description reST compliancy etc.
-   A command post/pre hook system -- similar to what RPM has so you can
    point code to be run before and after a Distutils command is run.
    Very powerful and useful -- but to be used with caution. You
    wouldn't want to add to the install process a code dependency you
    are not sure to have. But in the meantime, being able to run a code
    every time a project is installed on your system is sweet ;)&lt;/p&gt;
&lt;p&gt;I am forgetting a million things, but I guess its not important, since
all our students are blogging about it ;) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Thanks to Google, the GSOC is a great program. Distutils2 just got a
really serious boost, we are very close to a good, useful release. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;As a conclusion, to make things clear for the few people that still see
all these efforts as reinventing the wheel since packaging has been
kind-of-solved already elsewhere: Every extensible system, whether its
an OS like Debian or Fedora, or a language like Python, will &lt;em&gt;always&lt;/em&gt;
provide its own custom packaging system, that is built on the top of the
community experience and needs. The only thing that really matters is to
have this work based on standards so inter-operability is manageable.
That's the goal of some of the PEP we have written, like PEP 386 and PEP
345 and that will make OS packagers life easier in the future. An
universal packaging system is an utopia.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 19 Aug 2010 09:35:00 +0200</pubDate><guid>http://blog.ziade.org/2010/08/19/distutils-2-summary-of-the-gsoc/</guid></item><item><title>Firefox Sync Server in Python -- Take 1</title><link>http://blog.ziade.org/2010/08/10/firefox-sync-server-in-python-take-1/</link><description>&lt;p&gt;I have been working for a bit more than a month now on the next
generation of the Firefox Sync server in Python and while the project is
still in its early stages and subject to a lot of changes, I think it's
a good idea to share now about what we are building here at Mozilla.
Maybe that'll attract contributors ! &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;About Sync&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.mozilla.com/en-US/firefox/sync/"&gt;Firefox Sync&lt;/a&gt; (formerly Weave) let you synchronize your Firefox
bookmarks, history, passwords, opened tabs etc. so you can have them on
any computer, or even use them from your iPhone &lt;a href="http://www.mozilla.com/en-US/mobile/home/"&gt;by using Firefox
Home&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Clients that are syncing work with our servers at Mozilla by using the
Sync and the User APIs defined in these documents: &lt;br /&gt;
-   &lt;a href="https://wiki.mozilla.org/Labs/Weave/User/1.0/API"&gt;https://wiki.mozilla.org/Labs/Weave/User/1.0/API&lt;/a&gt;
-   &lt;a href="https://wiki.mozilla.org/Labs/Weave/Sync/1.0/API"&gt;https://wiki.mozilla.org/Labs/Weave/Sync/1.0/API&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The User APIs manage the users accounts and tell the client which
server holds the data of a given user. In other words, each user is
tightly coupled to a single server when reading or writing data. This
natural sharding is great for scaling Sync, and is possible because
users don't share data (yet... ;)) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Another important point is that the data are encrypted on client side
before they are sent over. That's because one of the key concept of Sync
is that your data should not be known by our servers, to protect your
privacy. Well, we could probably still know &lt;em&gt;how many&lt;/em&gt; bookmarks you
have by counting the number of entries in the DB, or &lt;em&gt;how often&lt;/em&gt; you use
your browser. But as soon as you use a service like that you have to
give away these kind of information, most of the time just because they
are useful to make the service faster or understand any potential
problem. &lt;a href="https://services.mozilla.com/privacy-policy/Privacy_Policy.pdf"&gt;Read our privacy policy here&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;And the good news is that you can set up your &lt;em&gt;own&lt;/em&gt; Sync server and
even implement it yourself if you want. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So, a Sync server a pretty passive storage server, that is quite easy
to scale while keeping &lt;a href="http://en.wikipedia.org/wiki/Data_consistency"&gt;data consistency&lt;/a&gt; across clients. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;About the code&lt;/h3&gt;
&lt;p&gt;The current implementation uses Apache, PHP, LDAP, MySQL and Memcached.
For various reasons I won't detail in this post --that might be another
post-- , it has been decided to switch the Sync server to Python &lt;br /&gt;
&lt;/p&gt;
&lt;h4&gt;Python libraries&lt;/h4&gt;
&lt;p&gt;The Sync server is composed of web services and a few screens used for
the password reset process, so using a web framework would have been
overkill. Although, writing a wsgi-enabled server made a lot of sense
since it allows people to run our implementation on their laptop, or on
any wsgi-compatible web server they wish to use. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So, I've picked : &lt;br /&gt;
-   &lt;a href="http://routes.groovie.org"&gt;Routes&lt;/a&gt;, to dispatch requests to a few classes (controllers)
-   &lt;a href="http://pythonpaste.org/webob/"&gt;WebOb&lt;/a&gt; to process incoming requests and build responses
-   &lt;a href="http://pythonpaste.org/"&gt;Paste. PasteScript, PasteDeploy&lt;/a&gt;, to group the configuration in
    an ini file and make it easy to run the application with a built-in
    server.&lt;/p&gt;
&lt;p&gt;There are alternative routing systems, but &lt;strong&gt;Routes&lt;/strong&gt; really fits my
brain and make the dispatching quite simple. I really like the fact that
you can &lt;em&gt;optionally&lt;/em&gt; use regular expressions to validate URLs. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;WebOb&lt;/strong&gt; is quite a standard library and make our life simple to read
requests and write responses. The code in our controllers stays KISS
with WebOb when you have to read incoming data: they're all available in
simple mappings. The response is also built by WebOb and you can forget
about all the wsgi protocol details. We mainly return JSON dumps that
WebOb wraps into responses. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Last, &lt;strong&gt;Paste&lt;/strong&gt; is very handy to run the server locally, to initialize
data, and handle multiple configurations. I should also say that my
colleague&lt;a href="http://blog.ianbicking.org/"&gt;Ian Bicking&lt;/a&gt; is behind the Paste and WebOb libs, and
involved in the Sync project. So those were quite natural choices. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The authentication process is a custom function that reads a basic
authentication header and checks it using an authentication plugin (more
on plugins later in this post.) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For the storage, I've picked &lt;strong&gt;SQLAlchemy&lt;/strong&gt; and &lt;strong&gt;python-ldap&lt;/strong&gt;. I
don't really use the ORM part of SQLAlchemy and write pretty raw SQL
queries to avoid any extra overhead. The benefit of the ORM was null
here anyways, since all storage I/O are contained in a storage class
that outputs simple mappings. I have created the mappers though, as they
are useful to initialize a DB on a first run. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But when the server runs, SQLAlchemy is mainly used for: &lt;br /&gt;
-   its connection pooling abilities.
-   the nice parameters binding
-   the ability to switch to any DB system via configuration (as long as
    the SQL is compatible of course)&lt;/p&gt;
&lt;p&gt;As for&lt;strong&gt; python-ldap&lt;/strong&gt; (I didn't implement the LDAP part yet), it's the
standard connector I have always used with various flavors of LDAP
servers (OpenLDAP, ActiveDirectories, etc.). I don't think there is any
competitor for this anyways. &lt;br /&gt;
&lt;/p&gt;
&lt;h4&gt;Caching&lt;/h4&gt;
&lt;p&gt;The caching is currently done using Memcached. For instance, when
clients are often asking for specific collection items, they end up in
memcached to lower the number of queries made to MySQL. For the Python
implementation though, I've decided to use &lt;a href="http://code.google.com/p/redis/"&gt;&lt;strong&gt;Redis&lt;/strong&gt;&lt;/a&gt; instead. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In terms of speed, Redis and Memcached are quite similar. Redis though
has interesting extras: &lt;br /&gt;
-   The data is saved to the disk, so you don't lose your cache. The
    speed stays almost the same as memcached since the disk syncs are
    done asynchronously from time to time. Since a Sync user is tightly
    coupled to a storage server, that's an interesting feature to have.
    And, hey, you can move data from a Redis DB to another, so migrating
    the cache to another server is even possible.
-   Redis provides built-in APIs to work with sets and lists, which
    authorizes more complex caching without extra code. This will allow
    us to do more caching in the future.&lt;/p&gt;
&lt;h4&gt;Storage&lt;/h4&gt;
&lt;p&gt;The storage itself will stay on MySQL but we will probably explore
alternative storages systems in the future. One requirement of Sync is
to be able to &lt;em&gt;write&lt;/em&gt; data as fast as possible so all clients can have
access to them as soon as possible. Right now, Sync provides &lt;a href="http://en.wikipedia.org/wiki/Immediate_consistency"&gt;immediate
consistency&lt;/a&gt;, since all writes are done synchronously on a single
server. &lt;br /&gt;
&lt;/p&gt;
&lt;h4&gt;Plugins&lt;/h4&gt;
&lt;p&gt;The PHP application was built with extensibility in mind: the way
Mozilla stores the data and authenticates users (a mix of LDAP and
MySQL) might not work if the code is used by someone else. That's why
the code was built using abstractions for the storage and the
authentication part, and the Python version took back this good idea. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Basically, you can write a new authentication or storage class, and
configure Sync to use it. See the documentation I am building on this:
&lt;a href="http://sync.ziade.org/doc/storage.html"&gt;http://sync.ziade.org/doc/storage.html&lt;/a&gt; (temporary location) &lt;br /&gt;
&lt;/p&gt;
&lt;h4&gt;Web server&lt;/h4&gt;
&lt;p&gt;The web server that runs the Python application will stay Apache (with
[mod_wsgi][]) since it has proven to work very well with the current
implementation. I might bench other servers in the future though, like
&lt;a href="http://www.google.com/url?sa=t&amp;amp;source=web&amp;amp;cd=1&amp;amp;ved=0CBgQFjAA&amp;amp;url=http://gunicorn.org/&amp;amp;ei=ojlhTNSzG9W6jAfVweiwCQ&amp;amp;usg=AFQjCNH4886vZhhVrpKRkAak1Ja0k68d3g"&gt;Gunicorn&lt;/a&gt; + nGninx or &lt;a href="http://projects.unbit.it/uwsgi/"&gt;uWSGI&lt;/a&gt; + nGninx. We now have a nice
&lt;a href="http://grinder.sourceforge.net/"&gt;Grinder&lt;/a&gt; script that realistically mimics Sync users, so.. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Doc and Code&lt;/h3&gt;
&lt;p&gt;I've started a documentation, the temporary location is at
&lt;a href="http://sync.ziade.org/doc"&gt;http://sync.ziade.org/doc&lt;/a&gt; and you can grab the code we are building
at [http://hg.mozilla.org/users/telliott_mozilla.com/sync-server][].
You can already use the server with your Firefox / Firefox Home, but
this is still at development stage, so use at your own risks. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I would love to get some feedback on that work !&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 10 Aug 2010 15:07:00 +0200</pubDate><guid>http://blog.ziade.org/2010/08/10/firefox-sync-server-in-python-take-1/</guid></item><item><title>Afpy computer camp 17/18 September</title><link>http://blog.ziade.org/2010/08/05/afpy-computer-camp-1718-september/</link><description>&lt;p&gt;I &lt;a href="http://tarekziade.wordpress.com/2010/04/15/afpy-computer-camp-2-345-september/"&gt;have announced it already&lt;/a&gt;, and the dates have changed a bit. But
let's say it again since the date is approaching: we will have a sprint
at my house the second week-end of September (17/18), focusing on
packaging and testing (and other topics if people want). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This sprint will be a special event like last year: we will enjoy wines
directly from the producers place (Gevrey-Chambertin anyone ?) and do
some restaurants in the area. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Now its a good time to tell me if you are coming, by adding your name
here:
&lt;a href=""&gt;http://www.coactivate.org/projects/afpy-computer-camp-2010/project-home&lt;/a&gt;
(make sure you are registered) &lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;coactivate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/projects/&lt;/span&gt;&lt;span class="n"&gt;afpy&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;computer&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;camp&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2010&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 05 Aug 2010 22:29:00 +0200</pubDate><guid>http://blog.ziade.org/2010/08/05/afpy-computer-camp-1718-september/</guid></item><item><title>plugins system: thoughts for an entry points replacement</title><link>http://blog.ziade.org/2010/07/25/plugins-system-thoughts-for-an-entry-points-replacement/</link><description>&lt;p&gt;&lt;em&gt;This blog entry was inspired by the discussion I just had with Michael
on IRC, as he just added plugins in unittest2.&lt;/em&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Setuptools' entry points feature is &lt;em&gt;hated&lt;/em&gt; and &lt;em&gt;loved&lt;/em&gt; by developers.
If you are not familiar with them, you can read &lt;a href="http://lucumr.pocoo.org/2006/7/30/setuptools-plugins"&gt;this post from
Armin&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hated&lt;/strong&gt; because when you install a project that contains entry points
(let's call them &lt;em&gt;plugins&lt;/em&gt;), they can be used in another application
without letting you know. So basically if a plugin sucks, it can break
another application just by being installed in your Python. And it's not
easy to have an overview of what plugins are installed and potentially
active.The worst is that projects that provide entry points, usually
provide many other things. But if you want to deactivate the plugin, you
have to remove the whole project... Note that plugins are not loaded at
Python startup. What happens is that any application can iterate over
the metadata of installed projects, looking for plugins, and eventually
loading them if wanted. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Loved&lt;/strong&gt;, because from a developer point of view you can have a new
feature added in a program with no extra configuration at all. Take
Nose. Thanks to entry points, it's dead easy to create a plugin for this
test runner, and tell people to pip-install this new project. Zero
config. Nice. Another great thing is that it's global to Python. Any
application can consume any entry point. Entry points are &lt;strong&gt;implicit
plugins&lt;/strong&gt; I guess. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Distutils&lt;/em&gt; has a plugin system as well: you can add new commands by
adding in distutils.cfg the path to the Python package containing the
command. That's an explicit plugin system since the end-user has to
configure it manually so Distutils uses it. &lt;em&gt;Mercurial&lt;/em&gt; uses the same
technique: activating a plugin is done in .hgrc. I would call these
&lt;strong&gt;explicit plugins&lt;/strong&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I think we can get the benefits of entry points without their caveats
really simply. And provide a generic plugin system for all. Let's
summarize what we want: &lt;br /&gt;
-   being able to list all installed plugins for every Python
    application
-   being able to remove a plugin or deactivate it. Without being forced
    to uninstall the project that provided it
-   have a plugin automatically installed and activated when the project
    that provides it is installed&lt;/p&gt;
&lt;p&gt;Here's how we can do. That's a brain dump, please give me some feedback
! &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Global plugin registry&lt;/h3&gt;
&lt;p&gt;Let's have a &lt;em&gt;.python-plugins.cfg&lt;/em&gt; file in the user's home &lt;em&gt;(and one
global to Python. The user cfg is merged with the global one at startup,
and overrides the values -- thanks Mongoose_Q for mentioning this on
Twitter)&lt;/em&gt;. It's a simple ini-like file like &lt;em&gt;.hgrc&lt;/em&gt;, where each section
represents a python application and a group name. A group is just a
family of plugins. For instance 'commands' can be a group for the
'distutils' application. In this section, each line is a plugin,
represented by a pointer to the module or class, followed by a label as
the value. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here's an example for a distutils 'i18n' command. It's a MyClass class,
located in the foo package, in the bar module: &lt;br /&gt;
     [distutils:commands]&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bar:MyClass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i18n&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The link to the code comes first because some plugins could have no
name: &lt;br /&gt;
     [app:group]&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;  &lt;span class="nb"&gt;package&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;module:Class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;Accessing the registry&lt;/h3&gt;
&lt;p&gt;distutils can provide an API to read the file, iterate and load the
plugins: &lt;br /&gt;
       &amp;gt;&amp;gt;&amp;gt; from distutils2 import plugins&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;    &lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;plugins&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;distutils&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;command&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="sr"&gt;&amp;lt;iterator&amp;gt;&lt;/span&gt;

    &lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;plugins&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;distutils&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;command&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Plugin&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;i18n&amp;quot;&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bar:MyClass&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;plugin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;plugins&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;distutils&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;command&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;    &lt;span class="c1"&gt;# gets the code and loads it&lt;/span&gt;

    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MyClass&lt;/span&gt; &lt;span class="n"&gt;Instance&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;Installing the plugins&lt;/h3&gt;
&lt;p&gt;Last, distutils could provide a mechanism to automatically register a
plugin. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Projects could describe their plugins in their setup.cfg: &lt;br /&gt;
     [plugins]&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;  &lt;span class="n"&gt;distutils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;commands&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;i18n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bar:MyClass&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Then distutils would automatically inject them at installation time in
&lt;em&gt;.python-plugins.cfg&lt;/em&gt; &lt;strong&gt;only if the end user agrees&lt;/strong&gt;: &lt;br /&gt;
     $ python setup.py install&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;  &lt;span class="n"&gt;distutils&lt;/span&gt; &lt;span class="n"&gt;has&lt;/span&gt; &lt;span class="n"&gt;detected&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;i18n&amp;quot;&lt;/span&gt; &lt;span class="n"&gt;plugin&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;distutils:commands&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Do&lt;/span&gt; &lt;span class="n"&gt;you&lt;/span&gt; &lt;span class="n"&gt;want&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;activate&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Y&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sun, 25 Jul 2010 02:29:00 +0200</pubDate><guid>http://blog.ziade.org/2010/07/25/plugins-system-thoughts-for-an-entry-points-replacement/</guid></item><item><title>Random notes on Mercurial queues</title><link>http://blog.ziade.org/2010/06/30/random-notes-on-mercurial-queues/</link><description>&lt;p&gt;Working on the various parts of the Mozilla project requires some patch
fu. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Basically, everything happens in &lt;a href="http://bugzilla.mozilla.org"&gt;bugzilla.mozilla.org&lt;/a&gt;, where you
upload a patch and ask for a review. Once you have started to work on
several patches, maintaining several mercurial clones can be tedious.
That's where queues are helping a lot. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Mozilla has a &lt;a href="https://developer.mozilla.org/en/Mercurial_queues"&gt;nice document about queues&lt;/a&gt;. I've also found this
&lt;a href="http://docs.sympy.org/spt-printable.html"&gt;Sympy tutorial&lt;/a&gt;quite useful. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here are my random notes about queues so far: &lt;br /&gt;
-   a queue is a directory of patch files (in .hg/patches). You can
    qpush or qpull them in your local repo. qpush will apply the current
    patch and add an entry in the commit log. qpop undoes it. pushing
    and poping will move you up or down in the stack.
-   I enabled 'color' in mercurial and use "hg qseries" to know where I
    am in the stack
-   I reorder patches by editing .hg/patches/series. Pretty rough but
    good enough. how come there are no q* command for that ?
-   to delete a patch, I make sure there are no pending changes, then I
    do "hg qpop -a; hg qdelete the_patch"
-   to import a patch from bugzilla, I use "hg qimport -n xxxx.patch
    https://bugzilla.mozilla.org/attachment.cgi?id=xxxx" where xxxx is
    the bug number&lt;/p&gt;
&lt;p&gt;How do &lt;em&gt;you&lt;/em&gt; work with queues ?&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Wed, 30 Jun 2010 17:21:00 +0200</pubDate><guid>http://blog.ziade.org/2010/06/30/random-notes-on-mercurial-queues/</guid></item><item><title>Suki, Mozilla and Japanese book</title><link>http://blog.ziade.org/2010/06/19/suki-mozilla-and-japanese-book/</link><description>&lt;p&gt;June is a very intense month for me, and the rest of the year should be
way more intense. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For once, this entry is not about packaging, and a bit personal. But I
want to share this with the Python community because that's my second
family. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;First of all, Suki was born two days ago on June 17th ! That's my
lovely little girl, my second child. I am so happy about this (you bet).
Her aunt, &lt;a href="http://www.amelelkamel.com/"&gt;who is creating short movies for a living&lt;/a&gt;, has made a
small video to welcome Suki to the world. &lt;a href="http://www.youtube.com/watch?v=GrTnRctKYR8"&gt;Check it out, she's done an
amazing job&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I was looking for a job and I have found one. I have accepted a
position at &lt;a href="https://mozillalabs.com/"&gt;Mozilla Labs&lt;/a&gt;and I'll start next Monday. I am very
excited about this for many reasons. The projects I'll be working on are
great, and the Mozilla people &amp;amp; values match perfectly with how I feel
about open source and the web. I'll start talking about this here in a
few weeks I guess, once things will be really started. Thanks to all the
people that helped me in my job seeking during these last months ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Last, my latest book "Expert Python Programming" was&lt;a href="http://www.amazon.co.jp/エキスパートPythonプログラミング-Tarek-Ziade/dp/4048686291/ref=sr_1_2?ie=UTF8&amp;amp;s=books&amp;amp;qid=1276973189&amp;amp;sr=8-2"&gt;translated in
Japanese&lt;/a&gt;. I know some authors are translated in other languages quite
often, but for me it was a big deal, because I have worked around with
the translation team and the Japanese book is actually better than the
original book, thanks to them, their feedback, passion about Python,
ideas and work. Now I need to go to Japan to meet them :D&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sat, 19 Jun 2010 21:28:00 +0200</pubDate><guid>http://blog.ziade.org/2010/06/19/suki-mozilla-and-japanese-book/</guid></item><item><title>Distutils2 vs Pip</title><link>http://blog.ziade.org/2010/05/31/distutils2-vs-pip/</link><description>&lt;p&gt;&lt;em&gt;Note: if you are not familiar with &lt;a href="http://www.python.org/dev/peps/pep-0345/"&gt;PEP 345&lt;/a&gt;, you might want to read
it to understand this entry.&lt;/em&gt; &lt;em&gt;It adds for instance "Requires-Dist" that
is similar to setuptools' install_requires and provides a standard for
dependencies description.&lt;/em&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The GSOC has started and we are already working on a lot of tasks about
packaging. The main difficulty is to make sure each student works
without overlapping with others, and never get blocked. That's why we
will have weekly meetings with (almost) everyone. In parallel, the nice
posse from the Montreal user group is organizing Distutils sprints quite
often now. That means that we now have an important manpower for
Distutils and things are starting to speed up. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;There's one controversial topic though, that we need to straighten up :
do we want to add an installer in Distutils2 ? And since Distutils2 goal
is to be back in the stdlib for Python 3.2, that means: do we want to
add an installer in the stdlib ? &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;My answer so far is &lt;strong&gt;Yes&lt;/strong&gt;. And that's what I'll be working on unless
someone is able to change my mind :) &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;What is Distutils2 ?&lt;/h3&gt;
&lt;p&gt;Let me explain first what is the Distutils2 project, and what we want
it to provide. Like its predecessor, Distutils2 wants to provide two
things: &lt;br /&gt;
1.  &lt;strong&gt;a toolbox for third packaging tools&lt;/strong&gt;, whether they are &lt;em&gt;simple&lt;/em&gt;
    installers or full featured package managers (PyPM, Pip, Enthought
    Installer etc..). This toolbox will include (if not already)
    reference implementation of PEP 345, PEP 376, PEP 386. In other
    words, if you want to create the next killer packaging system, you
    can use modules like &lt;em&gt;distutils2.version&lt;/em&gt; (PEP 386) or
    &lt;em&gt;distutils2.metadata&lt;/em&gt; (PEP 345) to build it, without depending on
    the "everything is a command" philosophy of Distutils.
2.  &lt;strong&gt;a standalone tool that can be used to install or remove
    distributions&lt;/strong&gt;. That's what Distutils is and that's what we want to
    provide in the future in Distutils2. The ability to install projects
    (and therefore its dependencies since this is a new metadata field
    we added in PEP 345).&lt;/p&gt;
&lt;p&gt;The controversy is about 2. It's controversial to provide a script that
installs dependencies via PyPI into distutils2 because some projects
like Pip already provides this feature. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Our current packaging ecosystem explained&lt;/h3&gt;
&lt;p&gt;A few years ago, before Setuptools added the ability to install
dependencies via &lt;strong&gt;&lt;em&gt;easy_install&lt;/em&gt;&lt;/strong&gt;, installing a distribution of a
given project was as simple as running a &lt;strong&gt;&lt;em&gt;python setup.py install&lt;/em&gt;&lt;/strong&gt;.
This was installing the distribution in the target system, in proper
locations defined by the &lt;strong&gt;&lt;em&gt;install&lt;/em&gt;&lt;/strong&gt; command. That's it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Setuptools grew organically on the top of Distutils to provide new
metadata like the "install_requires" field, that lists dependencies.
Setuptools provided two things: &lt;br /&gt;
-   A new&lt;strong&gt; install command&lt;/strong&gt; that triggers the installation of
    dependencies, by reading the setuptools-specific "install_requires"
    metadata, and fetching dependencies at PyPI and installing them
    recursively.
-   An &lt;strong&gt;easy_instal&lt;/strong&gt;l script that can be used to install a
    distribution located at PyPI. That's just a bootstrap on the top of
    the new install command. In other words, it grabs the archive at
    PyPI, unpack it, and run "python setup.py install" on it.&lt;/p&gt;
&lt;p&gt;In other words, &lt;strong&gt;your Python project setup.py is the installer
itself&lt;/strong&gt; because when you use setuptools, it calls its specific install
command and triggers the installation chain. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;That's when the mess started: people that didn't have setuptools
installed couldn't install projects that was using it of course. So the
solution that was provided was to propose an &lt;strong&gt;ez_setup.py&lt;/strong&gt; script
that you have to include in your project and to run when setup.py is
used, to be able to run your installation. In other words, your setup.py
is bootstrapping the utilization/installation of setuptools. And that
turned out to be really messy since Setuptools has its own way for
installing things. I hope I don't sound harsh here, Setuptools is the
best thing that happened to packaging in years. And a lot of our current
work is to bring back its features into the "main stream". &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The result is that you, as a end user, do not control what installer is
going to be used, and you end up with a site-packages that has projects
installed differently, and that uses different installers. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I am strongly against this behavior because of the mess it creates.
&lt;strong&gt;In my opinion a python source distribution should not embed an
installer and force its usage like this&lt;/strong&gt;. We need to separate concerns:
a python source project should be a dumb container with the code, and
with some metadata. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Then Pip showed up. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Pip is an installer script that grabs the project you want to install
and run "python setup.py install" on it. That's all it does when the
project is a plain Distutils one. When it encounter Setuptools projects,
it blocks the installation of the project's dependencies I have
described earlier, and installs it like a simple Distutils project.
Then, it analyzes its dependencies and installs each one of them
separately. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;That's really the way to go because it breaks what setuptools is
enforcing: projects are not installing other projects in the process
anymore. And in the long term, it will allow us to get rid of setup.py
(but that's another blog post). And I hope Pip will soon be able to
install Distutils2 projects because it is providing unifi ed metadata
(distutils+setuptools -&gt; PEP 345). &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Distutils2 vs Pip&lt;/h3&gt;
&lt;p&gt;So as I said before: it's controversial to provide a script that
installs dependencies via PyPI into distutils2 because Pip already
provides this feature. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But one Distutils2 goal (like Distutils) is to provide a command to
install a Distribution of your system so it works. And the concept of
"Distribution" has evolved, thanks to PEP 345. this means that it needs
to install dependencies now, exactly the way Pip does. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;We could just tell people to install Pip on the top of the stdlib. But
the goal is to provide in the stdlib a working packaging environment,
that provides a minimum set of features. The goal is to have something
that works when you install Python 3.2, like what was provided when
distutils was brought in (eg &lt;em&gt;batteries included&lt;/em&gt;). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Mac OS X includes easy_install, I don't see any good reason not to
include a package installer in the Python stdlib itself. At least, we
will be able to have a control on what script gets installed by default
with Python. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;That's why I have proposed to include Pip in Distutils2 but Ian and
Carl seems a bit reluctant for various reasons. One of them is that
having Pip included in the stdlib will slow down their work. I don't
think this is true as long as it's included carefully. If Distutils2
allows its installer to be replaced through configuration by another
one, then Pip can have new releases independently from the version
included in the stdlib and people can upgrade their system without
having to wait for the next Python release. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In any case, we are working on the various bits that are composing an
installer in Distutils2 during GSOC since one of the goal of the project
as I said earlier, is to provide a toolbox. So if the merge does not
occur, it's likely that we will start a installer/uninstaller script in
Distutils2, and it will look a lot like Pip I guess. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;EDIT: to make things clearer, when I am saying that both projects
should merge, I am only referring to the raw "install with dependencies"
features in Pip, and not all the other features.&lt;/em&gt;&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Mon, 31 May 2010 12:21:00 +0200</pubDate><guid>http://blog.ziade.org/2010/05/31/distutils2-vs-pip/</guid></item><item><title>Faking a server for client-side tests</title><link>http://blog.ziade.org/2010/05/10/faking-a-server-for-client-side-tests/</link><description>&lt;p&gt;Distutils makes some call to the PyPI server to register and upload
projects. Distutils2 will also make some calls to
&lt;a href="http://packages.python.org"&gt;packages.python.org&lt;/a&gt; to automate the upload of documentation. This
feature was added by Janis a while ago in Distribute and is being
backported in Distutils2 during the GSOC. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;To test all these features, what I usually did in Distutils was to
monkey-patch the code that calls the server and record in memory the
exchanges to check them. The problem with this approach is that you have
to be careful in the way you patch the APIs the client code use. A
typical bug that can happen is to get a slightly different behavior
making your code buggy or broken when it interacts with the real server.
Of course you can run your test once with the real server then use some
mock or stub techniques. But this work can be rather tedious and complex
in my opinion. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;What I tend to do these days is drop completely this client-side test
fixture approach, and just run a local server that implements partially
or fully the real server API. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This blog post is just to demonstrate how easy it can be to run your
own test server. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For HTTP protocols, the standard library provides everything needed to
write such a server in a few lines. The &lt;a href="http://packages.python.org"&gt;wsgiref&lt;/a&gt;
module for instance, is great to get a web server up and running during
your tests. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here's a full example (working for Python &gt;= 2.6 and Python 3): &lt;br /&gt;
   import threading&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="err"&gt;try:&lt;/span&gt;

    &lt;span class="err"&gt;from&lt;/span&gt; &lt;span class="err"&gt;urllib.request&lt;/span&gt; &lt;span class="err"&gt;import&lt;/span&gt; &lt;span class="err"&gt;urlopen&lt;/span&gt;

&lt;span class="err"&gt;except&lt;/span&gt; &lt;span class="err"&gt;ImportError:&lt;/span&gt;

    &lt;span class="err"&gt;from&lt;/span&gt; &lt;span class="err"&gt;urllib2&lt;/span&gt; &lt;span class="err"&gt;import&lt;/span&gt; &lt;span class="err"&gt;urlopen&lt;/span&gt;

&lt;span class="err"&gt;import&lt;/span&gt; &lt;span class="err"&gt;time&lt;/span&gt;

&lt;span class="err"&gt;from&lt;/span&gt; &lt;span class="err"&gt;wsgiref.simple_server&lt;/span&gt; &lt;span class="err"&gt;import&lt;/span&gt; &lt;span class="err"&gt;make_server,&lt;/span&gt; &lt;span class="err"&gt;demo_app&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppRunner&lt;/span&gt;&lt;span class="err"&gt;(threading.Thread):&lt;/span&gt;

    &lt;span class="err"&gt;&amp;quot;&amp;quot;&amp;quot;Thread&lt;/span&gt; &lt;span class="err"&gt;that&lt;/span&gt; &lt;span class="err"&gt;wraps&lt;/span&gt; &lt;span class="err"&gt;a&lt;/span&gt; &lt;span class="err"&gt;wsgi&lt;/span&gt; &lt;span class="err"&gt;app&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="err"&gt;def&lt;/span&gt; &lt;span class="err"&gt;__init__(self,&lt;/span&gt; &lt;span class="err"&gt;wsgiapp=demo_app):&lt;/span&gt;

        &lt;span class="err"&gt;threading.Thread.__init__(self)&lt;/span&gt;

        &lt;span class="err"&gt;self.httpd&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="err"&gt;make_server(&amp;#39;&amp;#39;,&lt;/span&gt; &lt;span class="err"&gt;0,&lt;/span&gt; &lt;span class="err"&gt;wsgiapp)&lt;/span&gt;

        &lt;span class="err"&gt;self.address&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="err"&gt;self.httpd.server_address&lt;/span&gt;

    &lt;span class="err"&gt;def&lt;/span&gt; &lt;span class="err"&gt;run(self):&lt;/span&gt;

        &lt;span class="err"&gt;self.httpd.serve_forever()&lt;/span&gt;

    &lt;span class="err"&gt;def&lt;/span&gt; &lt;span class="err"&gt;stop(self):&lt;/span&gt;

        &lt;span class="err"&gt;self.httpd.shutdown()&lt;/span&gt;

        &lt;span class="err"&gt;self.join()&lt;/span&gt;

        &lt;span class="err"&gt;time.sleep(0.2)&lt;/span&gt;

&lt;span class="err"&gt;_SERVER&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="err"&gt;None&lt;/span&gt;

&lt;span class="err"&gt;def&lt;/span&gt; &lt;span class="err"&gt;run_server():&lt;/span&gt;

    &lt;span class="err"&gt;&amp;quot;&amp;quot;&amp;quot;Runs&lt;/span&gt; &lt;span class="err"&gt;the&lt;/span&gt; &lt;span class="err"&gt;server.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="err"&gt;global&lt;/span&gt; &lt;span class="err"&gt;_SERVER&lt;/span&gt;

    &lt;span class="err"&gt;if&lt;/span&gt; &lt;span class="err"&gt;_SERVER&lt;/span&gt; &lt;span class="err"&gt;is&lt;/span&gt; &lt;span class="err"&gt;not&lt;/span&gt; &lt;span class="err"&gt;None:&lt;/span&gt;

        &lt;span class="cp"&gt;# we suppose it&amp;#39;s running&lt;/span&gt;

        &lt;span class="err"&gt;return&lt;/span&gt; &lt;span class="err"&gt;_SERVER.address&lt;/span&gt;

    &lt;span class="err"&gt;_SERVER&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="err"&gt;AppRunner()&lt;/span&gt;

    &lt;span class="err"&gt;_SERVER.start()&lt;/span&gt;

    &lt;span class="err"&gt;return&lt;/span&gt; &lt;span class="err"&gt;_SERVER.address&lt;/span&gt;

&lt;span class="err"&gt;def&lt;/span&gt; &lt;span class="err"&gt;stop_server():&lt;/span&gt;

    &lt;span class="err"&gt;&amp;quot;&amp;quot;&amp;quot;Stops&lt;/span&gt; &lt;span class="err"&gt;the&lt;/span&gt; &lt;span class="err"&gt;server.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="err"&gt;global&lt;/span&gt; &lt;span class="err"&gt;_SERVER&lt;/span&gt;

    &lt;span class="err"&gt;if&lt;/span&gt; &lt;span class="err"&gt;_SERVER&lt;/span&gt; &lt;span class="err"&gt;is&lt;/span&gt; &lt;span class="err"&gt;None:&lt;/span&gt;

        &lt;span class="err"&gt;return&lt;/span&gt;

    &lt;span class="err"&gt;_SERVER.stop()&lt;/span&gt;

    &lt;span class="err"&gt;_SERVER&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="err"&gt;None&lt;/span&gt;

&lt;span class="err"&gt;if&lt;/span&gt; &lt;span class="err"&gt;__name__&lt;/span&gt; &lt;span class="err"&gt;==&lt;/span&gt; &lt;span class="err"&gt;&amp;#39;__main__&amp;#39;:&lt;/span&gt;

    &lt;span class="cp"&gt;# 1. set up&lt;/span&gt;

    &lt;span class="err"&gt;print(&amp;#39;Launching&lt;/span&gt; &lt;span class="err"&gt;the&lt;/span&gt; &lt;span class="err"&gt;test&lt;/span&gt; &lt;span class="err"&gt;server&amp;#39;)&lt;/span&gt;

    &lt;span class="err"&gt;url,&lt;/span&gt; &lt;span class="err"&gt;port&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="err"&gt;run_server()&lt;/span&gt;

    &lt;span class="err"&gt;print(&amp;#39;Test&lt;/span&gt; &lt;span class="err"&gt;server&lt;/span&gt; &lt;span class="err"&gt;running&lt;/span&gt; &lt;span class="err"&gt;at&lt;/span&gt; &lt;span class="err"&gt;%s:%d&amp;#39;&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt; &lt;span class="err"&gt;(url,&lt;/span&gt; &lt;span class="err"&gt;port))&lt;/span&gt;

    &lt;span class="cp"&gt;# 2. test&lt;/span&gt;

    &lt;span class="err"&gt;try:&lt;/span&gt;

        &lt;span class="err"&gt;print(&amp;#39;Testing..&amp;#39;)&lt;/span&gt;

        &lt;span class="err"&gt;res&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="err"&gt;urlopen(&amp;#39;http:&lt;/span&gt;&lt;span class="c1"&gt;//%s:%d&amp;#39; % (url, port))&lt;/span&gt;

        &lt;span class="err"&gt;assert&lt;/span&gt; &lt;span class="err"&gt;b&amp;#39;Hello&lt;/span&gt; &lt;span class="err"&gt;world!&amp;#39;&lt;/span&gt; &lt;span class="err"&gt;in&lt;/span&gt; &lt;span class="err"&gt;res.read()&lt;/span&gt;

    &lt;span class="err"&gt;finally:&lt;/span&gt;

        &lt;span class="cp"&gt;# 3. tear down&lt;/span&gt;

        &lt;span class="err"&gt;print(&amp;#39;Stopping&lt;/span&gt; &lt;span class="err"&gt;the&lt;/span&gt; &lt;span class="err"&gt;test&lt;/span&gt; &lt;span class="err"&gt;server&amp;#39;)&lt;/span&gt;

        &lt;span class="err"&gt;stop_server()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;em&gt;run_server() &lt;/em&gt;and &lt;em&gt;stop_server()&lt;/em&gt; are driving a thread that runs a
wsgi application using the &lt;em&gt;wsgiref&lt;/em&gt; helpers. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In the main section you can see an example of a test fixture, and of
running a client test on the &lt;em&gt;demo_app&lt;/em&gt; wsgi application &lt;em&gt;wsgiref&lt;/em&gt;
provides. It should return a "Hello world!" page. These functions are
making sure there's only one server running even if &lt;em&gt;run_server()&lt;/em&gt; is
called several times, so it's dead easy to use it from a unittest class.&lt;/p&gt;
&lt;p&gt;From there, writing a test server is just a matter of implementing a
wsgi application that will replace the &lt;em&gt;demo_app&lt;/em&gt; one. I find this
technique superior to all the stub/mockup work required when you don't
run your own server. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For other protocols than HTTP, this work can be longer and more complex
of course, unless the sdtlib or a third party project already has a
server implementation you can use (like &lt;a href="http://www.doughellmann.com/PyMOTW/smtpd/index.html"&gt;smtpd&lt;/a&gt;).&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Mon, 10 May 2010 11:59:00 +0200</pubDate><guid>http://blog.ziade.org/2010/05/10/faking-a-server-for-client-side-tests/</guid></item><item><title>A Distutils2 Google Summer of Code</title><link>http://blog.ziade.org/2010/04/26/a-distutils2-google-summer-of-code/</link><description>&lt;p&gt;The GSOC list of accepted students for the PSF &lt;a href="http://socghop.appspot.com/gsoc/org/home/google/gsoc2010/python"&gt;was released&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The &lt;a href="http://hg.python.org/distutils2"&gt;Distutils2&lt;/a&gt; project has 5 students that will work on various
packaging tasks this summer. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Congratulations to : &lt;br /&gt;
-   Konrad Delong
-   Alexis Metaireau
-   Eric Araujo
-   Zubin Mithra
-   Josip Djolonga&lt;/p&gt;
&lt;p&gt;Also, a big thanks to the people that are going to mentor them: &lt;br /&gt;
-   Fred Drake
-   Titus Brown
-   Lennart Regebro
-   Michael Mulich&lt;/p&gt;
&lt;p&gt;Last, a second important project will be done this summer, to create a
PyPI Testing infrastucture. Congrats to Mouad Benchchaoui and Thanks to
Jesse Noller ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I'll be mentoring Alexis, and I'll help other mentors and make sure we
all synchronize our efforts. I'm pretty excited about this upcoming GSOC
because it's a true opportunity to speed up the work we need to do, in
order to built the new packaging tools we all want to have.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Mon, 26 Apr 2010 22:46:00 +0200</pubDate><guid>http://blog.ziade.org/2010/04/26/a-distutils2-google-summer-of-code/</guid></item><item><title>PEP 376 is accepted -- What it means</title><link>http://blog.ziade.org/2010/04/26/pep-376-is-accepted-what-it-means/</link><description>&lt;p&gt;&lt;a href="http://www.python.org/dev/peps/pep-0376"&gt;PEP 376&lt;/a&gt; has just been accepted. This is a very important step in the
packaging work we have been doing during the last year. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This PEP introduces a database of installed distributions, and
therefore a standard that allows interoperability among all tools. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;To summarize, a distribution that gets installed will have to create a
&lt;strong&gt;&lt;em&gt;.dist-info&lt;/em&gt;&lt;/strong&gt; directory in Python, containing these files: &lt;br /&gt;
-   &lt;strong&gt;METADATA&lt;/strong&gt;: contains the project metadata, as described in [PEP
    345][], &lt;a href="http://www.python.org/dev/peps/pep-0314"&gt;PEP 314&lt;/a&gt; and &lt;a href="http://www.python.org/dev/peps/pep-0241"&gt;PEP 241&lt;/a&gt;.
-   &lt;strong&gt;RECORD&lt;/strong&gt;: records the list of installed files
-   &lt;strong&gt;INSTALLER&lt;/strong&gt;: records the name of the tool used to install the
    project
-   &lt;strong&gt;REQUESTED&lt;/strong&gt;: the presence of this file indicates that the project
    installation was explicitly requested (i.e., not installed as a
    dependency).&lt;/p&gt;
&lt;p&gt;Python will provide in the &lt;strong&gt;&lt;em&gt;pkgutil&lt;/em&gt;&lt;/strong&gt; module, &lt;a href="http://www.python.org/dev/peps/pep-0376/#new-functions-and-classes-in-pkgutil"&gt;a set of APIs&lt;/a&gt;that
can be used to query installed projects. This ressembles a lot to what
the Setuptools project currently provides with its &lt;strong&gt;pkg_resources&lt;/strong&gt;
module. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;An interesting side-effect of the &lt;strong&gt;&lt;em&gt;RECORD&lt;/em&gt;&lt;/strong&gt; file is that package
managers will be able to uininstall projects. As a matter of fact
Distutils2 will provide a basic uninstall feature on the top of the
&lt;strong&gt;&lt;em&gt;pkgutil&lt;/em&gt;&lt;/strong&gt; APIs, and I hope tools like Pip (that already provide this
feature) will adopt the new standard. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This small PEP is the basis to a new PEP that is coming next, which
will define a standard to describe and consume resource files in Python
projects. The ultimate goal will be to be able to get rid of
&lt;strong&gt;&lt;em&gt;setup.py&lt;/em&gt;&lt;/strong&gt; and describe everything in static configuration files in
your project. But this is another story ;) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Thanks to all people involved in this PEP, and in particular, thanks to
Philip J. Eby for his help (PEP 376 is massively based on what he has
created in Setuptools.)&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Mon, 26 Apr 2010 12:30:00 +0200</pubDate><guid>http://blog.ziade.org/2010/04/26/pep-376-is-accepted-what-it-means/</guid></item><item><title>stdlib&amp;#039;s shutil improvements</title><link>http://blog.ziade.org/2010/04/21/stdlib039s-shutil-improvements/</link><description>&lt;p&gt;I have contributed &lt;a href="http://tarekziade.wordpress.com/2008/07/08/shutilcopytree-small-improvement/"&gt;a small feature to &lt;strong&gt;shutil&lt;/strong&gt; in the past&lt;/a&gt;, and in
my work on distutils I used it really intensively. So I took over its
maintenance a while ago. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I had some feedback from the community that shutil shouldn't be used,
because of some negative comments like this one in copytree: "&lt;em&gt;consider
this example code rather than the ultimate tool&lt;/em&gt;". Well guess what ? I
removed that comment because this is a darn lie ! ;) . &lt;em&gt;copytree()&lt;/em&gt; for
instance, is really powerful and avoid you to write yet-another-os-walk
loop. Use it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The only limitation of shutil APIs are related to the fact that it's
unable to copy all the file metadata when you move/copy files around.
Those are too specific to each platform and it's impossible/very hard to
come up with a portable metadata reader/writer. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But for most needs you have in your Python applications, shutil is
perfect. Have a look at tools like &lt;em&gt;Distribute&lt;/em&gt; or &lt;em&gt;zc.buildout&lt;/em&gt;, they
are using shutil all over the place. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Besides a few bug fixes I did, here's a list of major features I have
added or I am currently working on, with their Python version
availability: &lt;br /&gt;
-   &lt;strong&gt;make_archive()&lt;/strong&gt;: this function will let you create any kind of
    archive, given a tree of files. You can create tarballs, zip files,
    or anything you want as long as you register a function that knows
    how to create one. See the &lt;a href="http://docs.python.org/dev/library/shutil.html#archives-operations"&gt;doc&lt;/a&gt; for more details.&lt;strong&gt; (Python
    2.7)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;unpack_archive()&lt;/strong&gt;: this function will let you unpack any kind of
    archive. It has a registery like &lt;em&gt;make_archive&lt;/em&gt;(). It's almost
    ready but too late for 2.7 inclusion. &lt;strong&gt;(Python 3.2)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;copytree()&lt;/strong&gt;: Two new options for this one (see &lt;a href="http://docs.python.org/dev/py3k/library/shutil.html#shutil.copytree"&gt;doc&lt;/a&gt;).
    &lt;strong&gt;(Python 3.2)&lt;/strong&gt; &lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;&lt;em&gt;copy_function&lt;/em&gt;: allows you to provide your own &lt;em&gt;copy&lt;/em&gt;
        function. Used by &lt;em&gt;copytree() &lt;/em&gt;it wants to copy a file.&lt;ul&gt;
&lt;li&gt;&lt;em&gt;ignore_dangling_symlinks&lt;/em&gt;: if &lt;em&gt;copytree()&lt;/em&gt; encounter a
    dangling symlink, and &lt;em&gt;symlinks&lt;/em&gt; is set to False, it will ignore
    it and not raise an error anymore.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;shutil&lt;/strong&gt; groups &lt;em&gt;all high-level files operations&lt;/em&gt;, that's why I have
added new functions to work with archives. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you have any feature suggestion, any file operation function that
you think should be included there, please let me know !&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Wed, 21 Apr 2010 13:21:00 +0200</pubDate><guid>http://blog.ziade.org/2010/04/21/stdlib039s-shutil-improvements/</guid></item><item><title>Afpy Computer Camp #2 - 3/4/5 september</title><link>http://blog.ziade.org/2010/04/15/afpy-computer-camp-2-345-september/</link><description>&lt;p&gt;Last year at the end of the summer, we had in my country house (I am
renovating) near Dijon/France, a nice week-end consisting of tasting
Burgundy wines and coding, demoing etc. &lt;br /&gt;
[&lt;img alt="image" src="http://lh6.ggpht.com/_BBU4XN71nJo/Sq0gFczXAvI/AAAAAAAAAbE/zHL3BGdUrWo/s720/IMGP5543.JPG" /&gt;][]&lt;/p&gt;
&lt;p&gt;You can see some more pictures [here][&lt;img alt="image" src="http://lh6.ggpht.com/_BBU4XN71nJo/Sq0gFczXAvI/AAAAAAAAAbE/zHL3BGdUrWo/s720/IMGP5543.JPG" /&gt;]. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;We had a great time, and I'd like to organize this event again this
year. I am blogging very early about it to make sure that interested
people will save the date ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you are interested in joining the camp, join this CoActivate page :
&lt;a href="http://www.coactivate.org/projects/afpy-computer-camp-2010"&gt;http://www.coactivate.org/projects/afpy-computer-camp-2010&lt;/a&gt; and add
your name in the &lt;a href="http://www.coactivate.org/projects/afpy-computer-camp-2010/project-home"&gt;wiki&lt;/a&gt; page once I have activated your account.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;Afpy Camp #1&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;[&lt;img alt="image" src="http://lh6.ggpht.com/_BBU4XN71nJo/Sq0gFczXAvI/AAAAAAAAAbE/zHL3BGdUrWo/s720/IMGP5543.JPG" /&gt;]: http://picasaweb.google.com/ziade.tarek/AfpyComputerCamp2009Turcey30Aout&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 15 Apr 2010 07:45:00 +0200</pubDate><guid>http://blog.ziade.org/2010/04/15/afpy-computer-camp-2-345-september/</guid></item><item><title>A Firefox plugin experiment. XUL, Bottle and Redis</title><link>http://blog.ziade.org/2010/04/14/a-firefox-plugin-experiment-xul-bottle-and-redis/</link><description>&lt;p&gt;I am experimenting Mozilla's XUL to write Firefox plugins. I never had a
chance to give it a shot before, so let's roll. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;To try it out, I decided to implement a very simple &lt;strong&gt;Twitter Firefox
plugin&lt;/strong&gt; that works a bit like TweetDeck: The plugin adds a new action
in the contextual menu labeled "Twitt it!". When you click on it, a
window pops, with a text box containing a short url for the current
page. The user can complete the text and send a twitter about the page.&lt;/p&gt;
&lt;p&gt;[&lt;img alt="image" src="http://tarekziade.files.wordpress.com/2010/04/picture-36.png?w=1024" /&gt;][] &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I am pretty sure there are hundreds of similar plugins out there, but
it's a pretty simple use case to learn XUL :) &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;The shortener&lt;/h3&gt;
&lt;p&gt;Before I could start to write the Firefox plugin, I needed a url
shortener. I had two options: a shortener local to the plugin or an
online service. A local shortener is probably more efficient, but an
online service is more fun and more interesting to code: it can be
shared across clients and it's a first step to a more complex plugin
that works with online services. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Services likes &lt;a href="http://code.google.com/p/bitly-api/wiki/ApiDocumentation"&gt;bit.ly provide such API&lt;/a&gt;, but you have to register of
course, and you have a limited number of calls to the service. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It's not like I am going to make a zillions calls to such a service,
but it's so simple to implement that I decided to write my own. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;To store the URLs I used a Redis storage. This is of course overkill
for an experiment, but it was a good excuse to try it out and see how
Redis fits my brain. &lt;br /&gt;
&lt;/p&gt;
&lt;h4&gt;Redis&lt;/h4&gt;
&lt;p&gt;Setting up a Redis server on my MacBook was done in a matter of
minutes. You just have to compile the source and you get a redis-server
binary you can launch and eventually daemonize: &lt;br /&gt;
   $ ./redis-server&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="n"&gt;Apr&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;07&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;28&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Warning:&lt;/span&gt; &lt;span class="nb"&gt;no&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="n"&gt;specified&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;using&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;specify&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;redis-server /path/to/redis.conf&amp;#39;&lt;/span&gt;

&lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="n"&gt;Apr&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;07&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;28&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Server&lt;/span&gt; &lt;span class="n"&gt;started&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Redis&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="mf"&gt;1.2.6&lt;/span&gt;

&lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="n"&gt;Apr&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;07&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;28&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;DB&lt;/span&gt; &lt;span class="n"&gt;loaded&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;disk&lt;/span&gt;

&lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="n"&gt;Apr&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;07&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;28&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="n"&gt;ready&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nb"&gt;accept&lt;/span&gt; &lt;span class="n"&gt;connections&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="mi"&gt;6379&lt;/span&gt;

&lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="n"&gt;Apr&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;07&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;28&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;DB&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="nb"&gt;keys&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;volatile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="n"&gt;slots&lt;/span&gt; &lt;span class="n"&gt;HT&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="n"&gt;Apr&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;07&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;28&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;clients&lt;/span&gt; &lt;span class="n"&gt;connected&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;slaves&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;294928&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;shared&lt;/span&gt; &lt;span class="n"&gt;objects&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;From there, you can use the redis Python client, which provides all
Redis commands. The great thing about this client (I didn't try all of
them though) is that it is self-documented. All you have to do is read
the Redis Commands Reference here :
&lt;a href="http://code.google.com/p/redis/wiki/CommandReference"&gt;http://code.google.com/p/redis/wiki/CommandReference&lt;/a&gt;. Lower-case
them and you have the Python methods. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Redis can be used as a classical key/value storage, but also has
convenient APIs to work with lists and sets. In other words Redis can
handle &lt;a href="http://www.paperplanes.de/2010/2/16/a_collection_of_redis_use_cases.html"&gt;many use cases&lt;/a&gt; out of the box. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;To store URLs in Redis, I used the simple set/get APIs and stored two
key/value per URL: long_url/short_url and short_url/long_url. This
takes more room but makes all lookups efficient (that is, 0(1)). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here's the code:
&lt;a href=""&gt;http://bitbucket.org/tarek/urltotwit/src/tip/shortener/shortener/urlstore.py&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I am pretty new to Redis, so if you think this is not optimal, let me
know ! &lt;br /&gt;
&lt;/p&gt;
&lt;h4&gt;The web service&lt;/h4&gt;
&lt;p&gt;The web service is just composed of two methods: &lt;br /&gt;
1.  &lt;strong&gt;/urls/short_id&lt;/strong&gt; : when reached, redirect to the page
    corresponding to the short_id
2.  &lt;strong&gt;/shortener&lt;/strong&gt;: a GET method that returns a short url, given an url&lt;/p&gt;
&lt;p&gt;This is a perfect use case for the &lt;a href="http://bottle.paws.de/"&gt;&lt;strong&gt;Bottle&lt;/strong&gt;&lt;/a&gt; micro-framework ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Nothing particular here, besides the fact that it took me 30 lines or
so to write it ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The code is here :
&lt;a href=""&gt;http://bitbucket.org/tarek/urltotwit/src/tip/shortener/shortener/main.py&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;One thing I have to add is a security layer once this is published on
my server. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;The Plugin&lt;/h3&gt;
&lt;p&gt;To start things out, I used the Extension Wizard located at
&lt;a href="http://ted.mielczarek.org/code/mozilla/extensionwiz"&gt;http://ted.mielczarek.org/code/mozilla/extensionwiz&lt;/a&gt; that generated a
skeleton of the plugin. The Firefox versions in &lt;em&gt;install.rdf&lt;/em&gt; are a bit
outdated, so I had to change the &lt;strong&gt;em:maxVersion&lt;/strong&gt; value to &lt;strong&gt;3.5.*&lt;/strong&gt;
so it would work with my 3.5.9 version. I am not 100% sure the layout is
still fully compliant with the best practices but it worked. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;From there, I created a new XUL file called &lt;em&gt;send_twitter.xul&lt;/em&gt; in
&lt;em&gt;content&lt;/em&gt;. This file contains the window that is displayed by the
plugin. It uses jQuery so its simple to call the shortener via Json. &lt;br /&gt;
   &amp;lt;window id="twitit-window"&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt; title=&amp;quot;Twit it !&amp;quot;

 orient=&amp;quot;horizontal&amp;quot;

 xmlns:html=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot;

 xmlns=&amp;quot;http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul&amp;quot;

 onload=&amp;quot;centerWindowOnScreen();&amp;quot;&amp;gt;

 &lt;span class="nt"&gt;&amp;lt;script&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;chrome://global/content/dialogOverlay.js&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

 &lt;span class="nt"&gt;&amp;lt;script&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;jquery-1.4.2.min.js&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

 &lt;span class="nt"&gt;&amp;lt;script&lt;/span&gt; &lt;span class="na"&gt;language=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;javascript&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;![CDATA[&lt;/span&gt;

&lt;span class="cp"&gt; function initTwit() {&lt;/span&gt;

&lt;span class="cp"&gt;   var current_url = window.opener.gBrowser.contentDocument.location.href;&lt;/span&gt;

&lt;span class="cp"&gt;   $.getJSON(&amp;quot;http://localhost:8080/shorten&amp;quot;, {url: current_url},.&lt;/span&gt;

&lt;span class="cp"&gt;   function(json) {&lt;/span&gt;

&lt;span class="cp"&gt;     document.getElementById(&amp;#39;twit-text&amp;#39;).value = json.result;&lt;/span&gt;

&lt;span class="cp"&gt;   });&lt;/span&gt;

&lt;span class="cp"&gt; }&lt;/span&gt;

&lt;span class="cp"&gt; function send(twit) {&lt;/span&gt;

&lt;span class="cp"&gt;   /* TO BE DONE */&lt;/span&gt;

&lt;span class="cp"&gt; }&lt;/span&gt;

&lt;span class="cp"&gt; initTwit();&lt;/span&gt;

&lt;span class="cp"&gt; ]]&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

 &lt;span class="nt"&gt;&amp;lt;box&amp;gt;&lt;/span&gt;

 &lt;span class="nt"&gt;&amp;lt;vbox&amp;gt;&lt;/span&gt;

 &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Enter your Twitter message for this page:&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

 &lt;span class="nt"&gt;&amp;lt;spring&lt;/span&gt; &lt;span class="na"&gt;flex=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;3&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;max-height: 5px;&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

 &lt;span class="nt"&gt;&amp;lt;textbox&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;twit-text&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;min-width: 100px;&amp;quot;&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;

 &lt;span class="na"&gt;rows=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;4&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;cols=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;80&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;multiline=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;true&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;maxlength=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;140&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

 &lt;span class="nt"&gt;&amp;lt;/textbox&amp;gt;&lt;/span&gt;

 &lt;span class="nt"&gt;&amp;lt;hbox&amp;gt;&lt;/span&gt;

 &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ok&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;label=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;OK&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;default=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;true&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;accesskey=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;t&amp;quot;&lt;/span&gt;

 &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;send($(&amp;#39;twit-text&amp;#39;));&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

 &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;cancel&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;label=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Cancel&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;default=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;false&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;accesskey=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;c&amp;quot;&lt;/span&gt;

 &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;window.close();&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

 &lt;span class="nt"&gt;&amp;lt;/hbox&amp;gt;&lt;/span&gt;

 &lt;span class="nt"&gt;&amp;lt;/vbox&amp;gt;&lt;/span&gt;

 &lt;span class="nt"&gt;&amp;lt;/box&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/window&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;em&gt;initTwit()&lt;/em&gt; calls the shortener via Json, and fills the text box with
the short url. This version points to my local Shortener instance. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;What stroke me when building this window was the need to rebuild the
plugin and relaunch Firefox everytime I changed it. And working with XUL
windows is not obvious because of the way the layout works (v/hboxes). I
ended up working with a Live Xul editor:
&lt;a href="http://ted.mielczarek.org/code/mozilla/xuledit/xuledit.xul"&gt;http://ted.mielczarek.org/code/mozilla/xuledit/xuledit.xul&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The part that I still miss is TDD: how to set up a healthy Javascript
TDD environment to work with Firefox Plugins ? Investigating.. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The (unfinished) code is here:
&lt;a href=""&gt;http://bitbucket.org/tarek/urltotwit/src/tip/urltotwit%40tarek.ziade.org&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you are a XUL coder, please comment ! &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;I need to finish the work on the part that sends the twitter via the
user account. So far I found XUL pretty nice. It feels like working in
an enhanced HTML language. I also need to read some about the security
model and the development best practices. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;On Python side, I am pretty happy with the Redis+Bottle small stack,
and I'll probably add a small web UI and make it available on ziade.org.
I'll be the third one at my Python User group to have my own url
shortener ;) (&lt;a href="http://a.pwal.fr/"&gt;Gawel&lt;/a&gt;, D&lt;a href="http://code.welldev.org/bgk/src"&gt;avid'Bgk&lt;/a&gt;)&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;Twitter Plugin example&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;[&lt;img alt="image" src="http://tarekziade.files.wordpress.com/2010/04/picture-36.png?w=1024" /&gt;]: http://tarekziade.files.wordpress.com/2010/04/picture-36.png
    http://bitbucket.org/tarek/urltotwit/src/tip/shortener/shortener/urlstore.py
    http://bitbucket.org/tarek/urltotwit/src/tip/shortener/shortener/main.py
    http://bitbucket.org/tarek/urltotwit/src/tip/urltotwit@tarek.ziade.org/&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Wed, 14 Apr 2010 22:36:00 +0200</pubDate><guid>http://blog.ziade.org/2010/04/14/a-firefox-plugin-experiment-xul-bottle-and-redis/</guid></item><item><title>a small Distutils2 foretaste</title><link>http://blog.ziade.org/2010/04/08/a-small-distutils2-foretaste/</link><description>&lt;p&gt;&lt;em&gt;Notice: If you didn't follow what's going on in the packaging world,
you can read &lt;a href="http://guide.python-distribute.org/introduction.html#current-state-of-packaging"&gt;this&lt;/a&gt;.&lt;/em&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The first public version of Distutils2 will be &lt;strong&gt;1.0a1&lt;/strong&gt;, and is
scheduled for &lt;strong&gt;April 30th&lt;/strong&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;We are working on implementing the various PEPs, removing the bad bits
inherited from Distutils, keeping the good ones, and adding the work
that was done in Distribute, like the Python 3 work (and grabbing the
good bits of Setuptools as well, like the &lt;em&gt;test&lt;/em&gt; command.) I have also
proposed a GSOC project, and I hope it will be picked by the PSF, to
boost the work. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In any case, lots of tasks will not be ready for &lt;strong&gt;1.0a1&lt;/strong&gt;, and will be
shipped later. But I think its important to release what we currently
have since it's &lt;em&gt;also&lt;/em&gt; a result of over 1 year of work I have been doing
within the stdlib. As a reminder: I reverted this work in Python, making
Distutils there pretty similar to 2.6's, and I've forked this work to
create distutils2. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Anyways, this blog entry is focusing on describing some of the features
you will find in Distutils2 &lt;em&gt;today&lt;/em&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The code is located in the new Python Mercurial repository
(&lt;a href="http://hg.python.org/distutils2"&gt;http://hg.python.org/distutils2&lt;/a&gt;). &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Distutils2 philosophy&lt;/h3&gt;
&lt;p&gt;One of the key point of Distutils2 is to try to provide tools for
third-party projects like Pip, PyPM, etc. without forcing them to adopt
the &lt;em&gt;everything-is-a-distutils-command&lt;/em&gt; philosophy. People have suffered
from this in the past, and this problem was probably one of the reason
the Setuptools project has to patch Distutils' code all over the place.&lt;/p&gt;
&lt;p&gt;I am therefore trying to think about Distutils2 as two distinct things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;a python packaging framework with tools anyone can use&lt;/li&gt;
&lt;li&gt;a python package manager based on CLI commands people can use to
    build, upload, install packages.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The way 2. is working today in Distutils has been very controversial,
and they are other tools out there based on other philosophies. I'll
focus on making 1. as useful as possible for all tools, without forcing
them to adopt 2. But also to enhance/fix 2. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;There are key features to add in Distutils2 to make the CLI command
system more useful. For instance, adding a &lt;strong&gt;&lt;em&gt;configure&lt;/em&gt;&lt;/strong&gt; command that
will take all compiling options and generate a configuration file that
can be consumed by other commands (build or install for instance) will
probably make people life easier. This has been done by 4Suite, and this
will be done in Distutils2. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In the Framework part, we already have two interesting modules you can
already use: &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;The version module&lt;/h3&gt;
&lt;p&gt;The &lt;strong&gt;&lt;em&gt;version&lt;/em&gt;&lt;/strong&gt; module is the implementation of PEP 376. It provides
among other things: &lt;br /&gt;
-   The &lt;strong&gt;NormalizedVersion&lt;/strong&gt; class you can use to work with PEP 376
    version
-   The &lt;strong&gt;suggest_normalized_version()&lt;/strong&gt; function you can use to try
    to transform any version into a PEP 376-compatible version. This
    function is based on what we have learned by looking at the projects
    released at PyPI.&lt;/p&gt;
&lt;p&gt;So, if you need to compare versions, you can create instances of
&lt;strong&gt;NormalizedVersion&lt;/strong&gt; using the string representation of the version,
and use any comparison operator. &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;from distutils2.version import NormalizedVersion&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;NormalizedVersion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;1.2a1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;NormalizedVersion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;1.2&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;True&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;In case the version is not PEP 376 compatible, the class will throw an
exception &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;NormalizedVersion('1.2-a1')&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;Traceback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="k"&gt;last&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="n"&gt;distutils2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IrrationalVersionError:&lt;/span&gt; &lt;span class="mf"&gt;1.2&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;a1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;And for these "irrational versions" ;), you can use
&lt;strong&gt;suggest_normalized_version&lt;/strong&gt; to try to convert them: &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;from distutils2.version import suggest_normalized_version&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;suggest_normalized_version&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;1.2-a1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="s"&gt;&amp;#39;1.2a1&amp;#39;&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;NormalizedVersion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;suggest_normalized_version&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;1.2-a1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;NormalizedVersion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;1.2a1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;The metadata module&lt;/h3&gt;
&lt;p&gt;The &lt;strong&gt;&lt;em&gt;metadata&lt;/em&gt;&lt;/strong&gt; module implements PEP 345 (the new metadata) but
also works with old metadata versions. It provides a
&lt;strong&gt;DistributionMetadata&lt;/strong&gt; class you can use to read or write metadata
files (those PKG-INFO file you can find in any release). This class is
compatible with all versions of Metadata: &lt;br /&gt;
-   1.0 : PEP 241
-   1.1 : PEP 314
-   1.2 : PEP 345&lt;/p&gt;
&lt;p&gt;The PEP 345 implementation supports the micro-language for the
environment markers, and displays warnings when versions that are
supposed to be PEP 386 are violating the scheme. &lt;br /&gt;
&lt;/p&gt;
&lt;h4&gt;Reading metadata&lt;/h4&gt;
&lt;p&gt;The &lt;strong&gt;DistributionMetadata&lt;/strong&gt; class can be instanciated with the path of
the metadata file, and provides a dict-like interface to the values: &lt;br /&gt;
    &amp;gt;&amp;gt;&amp;gt; from distutils2.metadata import DistributionMetadata&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DistributionMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;PKG-INFO&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

 &lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()[:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

 &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Metadata-Version&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Version&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Platform&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Supported-Platform&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

 &lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

 &lt;span class="s"&gt;&amp;#39;CLVault&amp;#39;&lt;/span&gt;

 &lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Version&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

 &lt;span class="s"&gt;&amp;#39;0.5&amp;#39;&lt;/span&gt;

 &lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Requires-Dist&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

 &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;pywin32; sys.platform == &amp;#39;win32&amp;#39;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Sphinx&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The fields that supports environment markers can be automatically
ignored if the object is instanciated using the
&lt;strong&gt;&lt;em&gt;platform_dependant&lt;/em&gt;&lt;/strong&gt; option. &lt;strong&gt;DistributionMetadata&lt;/strong&gt; will
interpret in the case the markers and willautomatically remove the
fields that are not compliant with the running environment. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here's an example under Mac OS X. The win32 dependency we saw earlier
is ignored: &lt;br /&gt;
    &amp;gt;&amp;gt;&amp;gt; from distutils2.metadata import DistributionMetadata&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DistributionMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;PKG-INFO&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;platform_dependant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

 &lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Requires-Dist&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

 &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Sphinx&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;If you want to provide your own execution context, let's say to test
the Metadata under a particular environment that is not the current
environment, you can provide your own values in the
&lt;strong&gt;execution_context&lt;/strong&gt; option, which &lt;br /&gt;
is the dict that may contain one or more keys of the context the
micro-language expects. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here's an example, simulating a win32 environment: &lt;br /&gt;
    &amp;gt;&amp;gt;&amp;gt; from distutils2.metadata import DistributionMetadata&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;sys.platform&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;win32&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DistributionMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;PKG-INFO&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;platform_dependant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

 &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="err"&gt;                                &lt;/span&gt; &lt;span class="n"&gt;execution_context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

 &lt;span class="o"&gt;...&lt;/span&gt;

 &lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Requires-Dist&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;pywin32; sys.platform == &amp;#39;win32&amp;#39;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

 &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="err"&gt;                             &lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Sphinx&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

 &lt;span class="o"&gt;...&lt;/span&gt;

 &lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Requires-Dist&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

 &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;pywin32&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Sphinx&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h4&gt;Writing metadata&lt;/h4&gt;
&lt;p&gt;Writing metadata can be done using the &lt;strong&gt;write&lt;/strong&gt; method: &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;metadata.write('/to/my/PKG-INFO')&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;The class will pick the best version for the metadata, depending on the
values provided. If all the values provided exists in all versions, the
class will used &lt;strong&gt;&lt;em&gt;metadata.PKG_INFO_PREFERRED_VERSION&lt;/em&gt;&lt;/strong&gt;. It is set
by default to &lt;strong&gt;"1.0"&lt;/strong&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;h4&gt;Conflict checking and best version&lt;/h4&gt;
&lt;p&gt;Some fields in PEP 345 have to follow a version scheme in their
versions predicate. When the scheme is violated, a warning is emited: &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;from distutils2.metadata import DistributionMetadata&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DistributionMetadata&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Requires-Dist&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Funky (Groovie)&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="s"&gt;&amp;quot;Funky (Groovie)&amp;quot;&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;valid&lt;/span&gt; &lt;span class="n"&gt;predicate&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Requires-Dist&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Funky (1.2)&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Stay tuned for some more fortastes of Distutils2 ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;And if you want to contribute, join us in #distutils (freenode)&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 08 Apr 2010 10:52:00 +0200</pubDate><guid>http://blog.ziade.org/2010/04/08/a-small-distutils2-foretaste/</guid></item><item><title>python-weave released</title><link>http://blog.ziade.org/2010/04/06/python-weave-released/</link><description>&lt;p&gt;I did more experiments on Weave last week-end, and released
&lt;a href="http://pypi.python.org/pypi/python-weave/"&gt;python-weave&lt;/a&gt; at PyPI, which is basically Mozilla's Python client for
Weave, plus some work I did to make it a Distutils-based project so it
can be installed easily. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you want to play with it, make sure you have Swig installed, then
run: &lt;br /&gt;
   $ pip install python-weave&lt;/p&gt;
&lt;p&gt;You can also use easy_install or a plain &lt;em&gt;setup.py install&lt;/em&gt; call. Once
this is done, you will have a &lt;strong&gt;&lt;em&gt;weave&lt;/em&gt;&lt;/strong&gt; package in your Python
installation, containing the Weave client. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;My first goal with this client was to publish my Firefox bookmarks in
an RSS feed online in my Pylons website (ziade.org), so I could share
them with others. I have now a cron job on my server that gets the
bookmarks on my Weave account every hour, and creates a RSS2 feed out of
it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The result is here : &lt;a href="http://ziade.org/bookmarks.xml"&gt;http://ziade.org/bookmarks.xml&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It simplifies my bookmarking workflow and make it dead simple for other
people to grab them (I can drop delicious and use exclusively FF to mark
pages). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I have added this script as a small example in the source release, see:
&lt;a href=""&gt;http://bitbucket.org/tarek/python-weave-client/src/tip/examples/bookmarks2rss.py&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The next steps in my experiments will be: &lt;br /&gt;
1.  display the xml feed in a Pylons view, sorting the bookmarks with
    their tags/keywords
2.  have a way to mark some bookmarks as private, and some as public
    (maybe through folder organization)
3.  add some social features on the top of it. (enable comments, etc.)
4.  let my friends register their own Weave accounts (pointing to any
    Weave server, not necessarily Mozilla's), so they can publish their
    own bookmarks.&lt;/p&gt;
&lt;p&gt;I realize that 4. is potentially dangerous because it can allow a third
party server to retrieve and store data that was protected by Mozilla's
Weave server. But one of the goal of Weave is to allow several devices
to get the users' data and Firefox is just one client. There's a lot of
potential to build applications outside Firefox too. I have seen that
Weave has an &lt;a href="https://wiki.mozilla.org/Labs/Weave/OAuth"&gt;OAuth&lt;/a&gt; service (not sure yet if its implemented), so I
need to experiment that. But I'd also need to display other people'
bookmarks. So maybe the best solution for this would be to have a single
Weave account all people share. And allow a single Firefox to manage
multiple accounts: a personal one and a shared one accessed by several
people. That would resolve 2. :) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Anyways, this whole experiment is about building something similar to
what delicious provides, but with a tighter integration in Firefox and
on an open standard.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;bitbucket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/tarek/&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;weave&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="sr"&gt;/src/&lt;/span&gt;&lt;span class="n"&gt;tip&lt;/span&gt;&lt;span class="sr"&gt;/examples/&lt;/span&gt;&lt;span class="n"&gt;bookmarks2rss&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 06 Apr 2010 15:42:00 +0200</pubDate><guid>http://blog.ziade.org/2010/04/06/python-weave-released/</guid></item><item><title>Hello PSF !</title><link>http://blog.ziade.org/2010/04/06/hello-psf/</link><description>&lt;p&gt;I was nominated then accepted as a member of the Python Software
Foundation. Thanks Marc-André and others. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Anyone can contribute to the Python community without being a PSF
member of course, so what's the point ? &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It's quite simple: being a member means that you can vote and
participate in the decisions made by the PSF for Python. This means a
lot to me since I am devoting a lot of my free time to contribute to
Python. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;And... well... I am just proud ! :)&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 06 Apr 2010 13:57:00 +0200</pubDate><guid>http://blog.ziade.org/2010/04/06/hello-psf/</guid></item><item><title>Playing with Weave from Mozilla Labs</title><link>http://blog.ziade.org/2010/03/27/playing-with-weave-from-mozilla-labs/</link><description>&lt;p&gt;Mozilla Labs has launched an interesting project called Weave some times
ago. The idea is to provide an online service where your Firefox
instance can save personal data, like bookmarks or passwords. You can
grab a Firefox Add-On here and get started :
&lt;a href="http://mozillalabs.com/weave/"&gt;http://mozillalabs.com/weave/&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;[&lt;img alt="Weave Logo" src="https://services.mozilla.com/images/weave-logo.png" /&gt;][] &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The personal data is encrypted by Firefox using a scheme based on AES
256 and private/public keys. See
&lt;a href="https://wiki.mozilla.org/Labs/Weave/Developer/Crypto"&gt;https://wiki.mozilla.org/Labs/Weave/Developer/Crypto&lt;/a&gt; for more
details. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The Weave protocol is quite simple and is documented here
:&lt;a href="https://wiki.mozilla.org/Labs/Weave/API"&gt;https://wiki.mozilla.org/Labs/Weave/API&lt;/a&gt;, and I have seen some
Python implementations around for the server-side. Although Mozilla's
implementation for their Firefox service looks like it's in PHP right
now. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Mozilla also provides &lt;a href="http://hg.mozilla.org/labs/weaveclient-python"&gt;a small Python client&lt;/a&gt; for the protocol, which
can be used to get and set data to a Weave server: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I was curious about Weave so I started to play around with the Python
client, and ended up &lt;a href="https://bitbucket.org/tarek/python-weave-client/"&gt;cloning it on Bitbucket&lt;/a&gt;, to add a few things to
try it out. The original code in Mozilla's repository is not packaged
(yet) and you just get a few Python modules. Although it has a command
line that will let you grab some data from the server, but I wanted to
try it out from my code. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So I've made it an installable Python package in my clone so I could
install the client with Pip or easy_install. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Then I wrote a small class to try it to get my Firefox bookmarks: &lt;br /&gt;
   import json&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;weave&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;WeaveStorageContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WeaveCryptoContext&lt;/span&gt;

&lt;span class="n"&gt;_DEFAULT_SERVER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;https://auth.services.mozilla.com&amp;#39;&lt;/span&gt;

&lt;span class="n"&gt;class&lt;/span&gt; &lt;span class="n"&gt;WeaveClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;passphrase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;_DEFAULT_SERVER&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;storage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WeaveStorageContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;crypto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WeaveCryptoContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;passphrase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;_deserialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;crypto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

    &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;get_bookmarks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;bookmark&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_items&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;bookmarks&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

            &lt;span class="n"&gt;bookmark&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_deserialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;payload&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;bookmark&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;type&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;bookmark&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

                &lt;span class="k"&gt;continue&lt;/span&gt;

            &lt;span class="n"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;bookmark&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;title&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;bookmark&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;bmkUri&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WeaveClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;tarek.ziade&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;password&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;passphrase&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_bookmarks&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;

        &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Here, a storage context class and a crypto context class are created
with my login information, and the get_items() API gives me back a list
of encrypted bookmarks. Weave organizes its data into collections, and
bookmarks is just a label for one collection. There are other
collections ([see
https://wiki.mozilla.org/Labs/Weave/Sync/1.0/API#Collections][]), but
Weave could be used to store and retrieve any kind of data. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you want to replay my example, or play with the client, you can
install it using my bitbucket repository like this: &lt;br /&gt;
   $ pip install http://bitbucket.org/tarek/python-weave-client/get/tip.gz&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="ow"&gt;or&lt;/span&gt;

&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;easy_install&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;bitbucket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/tarek/&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;weave&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="sr"&gt;/get/&lt;/span&gt;&lt;span class="n"&gt;tip&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gz&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Beware that this is not a release, just my custom clone :). But I'll
probably ping the author to see if he has any interest in packaging his
modules like I did, so we get it at PyPI. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;What I am really looking forward about Weave now is how the project is
going to grow and securely handle millions of user's data. This will
probably end up in a very interesting infrastructure, with some
similarities with projects like Dropbox or &lt;a href="http://allmydata.org/~warner/pycon-tahoe.html"&gt;Tahoe&lt;/a&gt;.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;Weave Logo&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;[&lt;img alt="Weave Logo" src="https://services.mozilla.com/images/weave-logo.png" /&gt;]: https://services.mozilla.com/&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sat, 27 Mar 2010 23:12:00 +0100</pubDate><guid>http://blog.ziade.org/2010/03/27/playing-with-weave-from-mozilla-labs/</guid></item><item><title>Another GSOC Idea : a PyPI testing infrastructure</title><link>http://blog.ziade.org/2010/03/21/another-gsoc-idea-a-pypi-testing-infrastructure/</link><description>&lt;p&gt;Last year I've proposed &lt;a href="http://wiki.python.org/moin/SummerOfCode/2009"&gt;a GSOC project&lt;/a&gt; that no student picked up. I
think the fact that it was titled "A Distutils Regression System" was
not really appealing. I should have called it a "PyPI testing
infrastructure" because that's what it is really. And I know today
exactly how the system should work, so I can make a clearer proposal.
The proposal page is here:
&lt;a href="http://wiki.python.org/moin/SummerOfCode/PyPITestingInfrastructure"&gt;http://wiki.python.org/moin/SummerOfCode/PyPITestingInfrastructure&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So basically, the idea is to run a service that will receive update
notifications everytime someone uploads a distribution at PyPI, to
perform a few tests on it and send a report to the author by email (or
display it on a website). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The tests we want to do are quite simple: &lt;br /&gt;
-   does "&lt;em&gt;python setup.py install"&lt;/em&gt; works or throw out some errors ?
-   what files are modified on the system upon installation ?
-   does the tests pass ?&lt;/p&gt;
&lt;p&gt;For the latter, it supposes that we are able to detect and run the
tests. They are no conventions yet (but the TIP list is working on this)
but we can try out a few things on the source tree. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;EDIT: for all the tests we want to run on a distribution, we should
look at the &lt;a href="http://pycheesecake.org"&gt;CheeseCake&lt;/a&gt; project (Thanks Richard for mentioning it in
the comments)&lt;/em&gt;&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Getting update notifications is fairly simple now that Martin added a
&lt;a href="http://en.wikipedia.org/wiki/PubSubHubbub"&gt;PubSubHubBub&lt;/a&gt; service at PyPI. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The core part of the project is to make sure the commands are run on a
clean system. The easiest way to do it is to use Virtual Machines
through a service like EC2: we can run the test, then rollback the VM
state so it's back in a clean state. There are a few libs in Python to
drive systems like EC2. We also need to secure the VM so the code that
is run doesn't make network calls. If the project tests are relying on a
network resource, those are bad tests anyways. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Last, but not least, if we want to get back interesting reports, we
need to probe what's going on. It's simple at Python level, but we never
know what can happen in a &lt;em&gt;setup.py&lt;/em&gt; module. If a C module or an
external program is called somehow in there, it could perform operations
on the system we wouldn't be aware of. I think &lt;a href="http://sourceware.org/systemtap/"&gt;SystemTap&lt;/a&gt; is a great
tool to probe for all operations at the OS level. I am not sure how to
do it under Win32 though.. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But having a working story under Linux would be already a great thing,
so I am not worrying about that at this point. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So, I've entered the topic in the GSOC list, and I am willing to mentor
this if a student wants to do this. I know Titus is also very interested
in this topic, so we might work together on this, as co-mentors, which
would be great. That's a topic we've discussed several times in the past
and I think Titus wants a similar system. Mmm.. wait, we all do, don't
we ? ;)&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sun, 21 Mar 2010 21:38:00 +0100</pubDate><guid>http://blog.ziade.org/2010/03/21/another-gsoc-idea-a-pypi-testing-infrastructure/</guid></item><item><title>Distutils2 proposal for GSOC</title><link>http://blog.ziade.org/2010/03/18/distutils2-proposal-for-gsoc/</link><description>&lt;p&gt;&lt;strong&gt;EDIT 2010-03-28 : Started a detailed list here
&lt;a href="http://wiki.python.org/moin/SummerOfCode/Distutils2"&gt;http://wiki.python.org/moin/SummerOfCode/Distutils2&lt;/a&gt;&lt;/strong&gt; &lt;br /&gt;
The Google Summer of Code will be happening again this year, and I am
going to propose a project under the PSF : Distutils2. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you are a student and you are interested in building with me the
replacer of Distutils, this is an opportunity to contribute to an Open
Source project ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;A finite and detailed list of tasks will be defined just before the
proposals deadline, to avoid proposing a project that is too vague or to
make it impossible to rate the student at mid-term. This list will be
built at the last moment, because I am unable right now to know exactly
what will be the state of Distutils2 this summer. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But here's a taste of what the student will work on: &lt;br /&gt;
-   work on the implementation of PEP 376
-   finish the implementation of the static metadata (that is the "no
    more setup.py" subproject ;) )
-   work on a "configure" command that will generate a configuration
    file other commands can read back and use.
-   write a tutorial on how to use Distutils2
-   promote and evangelize Distutils2 - This will consist of helping
    python project to switch/adopt Distutils2. We will define a list of
    targets and the student will be in charge of helping them switching
    to Distutils2 if they want.&lt;/p&gt;
&lt;p&gt;If you are interested, be sure to join the&lt;a href="http://mail.python.org/mailman/listinfo/soc2010-general"&gt;mailing list&lt;/a&gt; and stay
tuned !&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 18 Mar 2010 15:24:00 +0100</pubDate><guid>http://blog.ziade.org/2010/03/18/distutils2-proposal-for-gsoc/</guid></item><item><title>4 simple tips for wannabe remote workers</title><link>http://blog.ziade.org/2010/03/18/4-simple-tips-for-wannabe-remote-workers/</link><description>&lt;p&gt;I've been working remotely for the past 6 years now. I can't believe it
has been so long already. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Quick explanation. I've started to work remotely for good reasons : I
have a 7 years old kid that lives with his mother in Dijon, France.
Since I am not with her anymore, I need to stick around Dijon in order
to see my boy as often as possible. Until he's old enough to visit me
elsewhere by himself ;) &lt;br /&gt;
&lt;/em&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;6 years earlier, working remotely was not really possible in France.
Companies were reluctant to do it because they thought they were unable
to control their employees if they were not physically present. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But in the software industry, a well-organized company has no reasons
to be reluctant to remote workers. As long as the proper tools and the
proper processes are set, a remote developer (and under some
circumstances a manager) is more efficient if he works from home. And it
makes the company save some money in most cases (less office space to
rent, etc.) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Some XP/Lean methodologies are organized around the fact that the whole
team is physically present but it's quite simple to adapt the rules to
integrate remote people. Skype and other tools are working fine these
days. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Of course, I don't think a 100% remote work is doable: you have to meet
people physically on a regular basis. That's important because a meeting
in person is better in specific phases in software projects. (launch and
design phases) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Anyways, here's a condensed list of tips if you have never worked
remotely and might do it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I hope you will find them useful. And if you are a remote worker and
have other tips, please comment this blog post. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Tip #1 - Everything you do, should be done in a VCS and an issue&lt;/h3&gt;
&lt;p&gt;tracker&lt;/p&gt;
&lt;p&gt;For all aspects of a software project, it's important to use a Version
Control System (I use Mercurial) even for your design notes or
documentation. Trust me, that's the only efficient way for other people
that need to work on the same parts you are working on to stay informed.
Listing the history of a file in a (D)VCS gives the reader a good
understanding of what's going on. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The only problem with this approach is that everyone in the team needs
to know how to use a VCS. Marketers and other people that are not used
to developers tools might be reluctant with this, but there are GUI
tools now on the top of every good VCS system (like Tortoise). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Like the VCS, using a good issue tracker to track every task that is
done is very important. And issues should be linked to VCS changesets
and vice-versa. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I use for instance Bitbucket with Mercurial, and they have a nice
plugin that will add a comment to an issue with a link to a changeset,
if you use the proper tag in the changeset comment when you push your
change. All issues trackers do this these days: Trac, Jira, etc. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Tip #2 - A daily over-the-phone quick meeting is a good idea&lt;/h3&gt;
&lt;p&gt;The best way to follow what other people are doing in the project is to
have quick meetings every day. I don't think it's a good idea to have
the meeting in the morning, because people don't start their work at the
same time and it's better to discuss what's hot (==what you have done
during the day.). So I like evening meetings better. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;A short meeting on Skype or over the phone, that doesn't last more than
ten minutes, is perfect. It will "force" you to briefly summarize what
you have done during the day before the meeting starts. Doing these
brief summaries is a great exercise for yourself too: it helps you
understand where you spend your time. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;And don't do these meetings over IRC or chat: a voice meeting is
faster, and is a good opportunity to "humanize" your work day a little
bit. Spending a whole day at home working alone can be depressing
sometimes, so hearing your colleague voices feels good. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Tip #3 - Your new office open-space is an IRC room&lt;/h3&gt;
&lt;p&gt;The best way to communicate instantly with your colleagues is a room in
the Internet Relay Chat or any other IM system that allows several
people to talk. I've also discovered lately &lt;a href="http://etherpad.com"&gt;Etherpad&lt;/a&gt;, which is the
perfect IRC companion : a real-time collaborative text editor. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Tip #4 - Separate your private life from your work&lt;/h3&gt;
&lt;p&gt;Some people think that working at home means you work less, but that's
a myth. Most of the time, you work more because it's so easy to finish
something you have started, when you are at home. It's not fun to work
alone in the evening in a big open space once other people have left . &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But working late at home is a very dangerous habit for your private
life. Your wife and kids will start to hate your work. So be sure to
have office hours and to separate physically your office from the rest
of the house.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 18 Mar 2010 01:08:00 +0100</pubDate><guid>http://blog.ziade.org/2010/03/18/4-simple-tips-for-wannabe-remote-workers/</guid></item><item><title>Montreal Packaging sprint wrapup</title><link>http://blog.ziade.org/2010/03/16/montreal-packaging-sprint-wrapup/</link><description>&lt;p&gt;After the Confoo.ca conference, &lt;a href="http://ygingras.net/"&gt;Yannick Gingras&lt;/a&gt; from the Montreal
Python user group organized two &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;[caption id="" align="alignright" width="343" caption="Sprinting on
TurboGears 2"][&lt;img alt="Les Brasseurs Numérique - Sprinting on TurboGears
2" src="http://lh5.ggpht.com/_BBU4XN71nJo/S5_Ly74ivlI/AAAAAAAAAoM/00wkIxnidaY/s512/IMGP7327.jpg" /&gt;][][/caption] &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;small sprints at the Brasseurs Numériques headquarters (Digital
Brewers). Yannick's company is called like that because he brews his own
beers ! As a matter of fact, he prepared a beer for the sprints, that
was really good (and quite strong.). His beer would probably beat some
good Belgium beers on a blind test. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Sprint #1 : Turbogears&lt;/h3&gt;
&lt;p&gt;&lt;a href="http://percious.com/blog"&gt;Chris Perkins&lt;/a&gt; led a Turbogears sprint Saturday. I am not very
familiar with this framework but I am using Pylons a lot, and its the
basis of Turbogears. &lt;a href="http://www.gothcandy.com/blog"&gt;Alice Bevan-McGregor&lt;/a&gt; was present, and could
confront his ideas on web frameworkery with Chris since he created his
own tool : &lt;a href="http://www.web-core.org"&gt;WebCore&lt;/a&gt;. I've heard that they worked on making the
Turbogears dispatcher a standalone library so it could be used by both
frameworks. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I worked on my side on small packaging issues TG has. We fixed the
latest TG 2.x beta custom package index that was broken (it was
generating incomprehensible errors when installing TG, and I found out
that the index pages in the TG PyPI were broken) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Next, I've added to easy_install a &lt;em&gt;--no-find-links&lt;/em&gt; option to prevent
links added by projects in their setup.cfg. This will prevent projects
like Pylons to implicitely add links that easy_install reads. The
effect is that some old version of some packages like nose were
installed. This is not released yet. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you were present to the sprint, please comment to tell us what
you've done ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;EDIT: Chris added more details on the TG sprint&lt;/strong&gt; -- &lt;a href="http://tarekziade.wordpress.com/2010/03/16/montreal-packaging-sprint-wrapup/#comment-9959"&gt;see the
comments&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Sprint #2 : Packaging&lt;/h3&gt;
&lt;p&gt;Monday evening we worked on packaging issues. I started the sprint by
presenting the current state of packaging on a board. That took quite a
while because it is not obvious to understand the packaging eco-system
(distutils, setuptools, distutils2 and pip.). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Then I've listed possible tasks and people started to work. &lt;br /&gt;
-   Yannick Gingras worked a bit on the Hitchicker's Guide to Packaging
    then worked on Distribute on Issue #133.
-   Ahmed Al-Saadi was pretty new to packaging so he worked on the guide
    and tried to catch up with the state of packaging (that's a real
    work :) )
-   Alexandre Vassaloti worked on porting distutils2 into Python3. So
    basically, like Distribute, Distutils2 will be installable on Python
    2 and 3, using the same source tree, and a 2to3 call upon
    installation.
-   Nicolas Cadou worked on PEP 345 support. He created a sample project
    that will be used in a functional test to validate that everything
    works. He eventually fixed some code in Distutils2 so it works with
    the PEP 345 DistributionMetadata class I've built during Pycon. I
    need to merge his work asap.
-   Matthieu Leduc-Hammel worked with me on PEP 345 support for PyPI.
    I've changed the postgres database to add the new fields, and
    Matthieu worked on PyPI UI. For instance, you will have a nice box
    on the project pages now that displays links from the Project-URL
    metadata field. I need to merge his work asap.&lt;/p&gt;
&lt;p&gt;As a global note: Mercurial was the perfect tool for this sprint. I am
able to merge people work in Distutils2 and other projects without all
the repository access issues we usually get when we start a sprint. I am
looking forward for a full Mercurial switch of Python, because this will
boost contributions. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Thanks Yannick, Nicolas for the Sprint and the Beer ! Thanks &lt;a href="http://ubity.com"&gt;Ubity&lt;/a&gt;
for sponsoring the Packaging sprint with pizzas ! (they are looking for
developers btw)&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;Les Brasseurs Numérique - Sprinting on TurboGears 2&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;[&lt;img alt="Les Brasseurs Numérique - Sprinting on TurboGears 2" src="http://lh5.ggpht.com/_BBU4XN71nJo/S5_Ly74ivlI/AAAAAAAAAoM/00wkIxnidaY/s512/IMGP7327.jpg" /&gt;]: http://lh5.ggpht.com/_BBU4XN71nJo/S5_Ly74ivlI/AAAAAAAAAoM/00wkIxnidaY/s512/IMGP7327.jpg&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 16 Mar 2010 19:42:00 +0100</pubDate><guid>http://blog.ziade.org/2010/03/16/montreal-packaging-sprint-wrapup/</guid></item><item><title>The fate of Distutils - Pycon Summit + Packaging Sprint detailed report</title><link>http://blog.ziade.org/2010/03/03/the-fate-of-distutils-pycon-summit-packaging-sprint-detailed-report/</link><description>&lt;h3&gt;The summit&lt;/h3&gt;
&lt;p&gt;I quickly &lt;a href="http://tarekziade.wordpress.com/2010/02/18/python-language-summit-summary-of-the-packaging-track/"&gt;posted an entry&lt;/a&gt; right after the &lt;a href="http://www.google.com/url?sa=t&amp;amp;source=web&amp;amp;ct=res&amp;amp;cd=1&amp;amp;ved=0CAYQFjAA&amp;amp;url=http://us.pycon.org/2009/about/summits/language/&amp;amp;ei=TNWOS9WfNMHM4gbx0vitDQ&amp;amp;usg=AFQjCNG3uKSR65UU2riqyQDEAgFng5b2eg"&gt;Language Summit&lt;/a&gt; we
held before Pycon in Atlanta. Basically, all the work I have being doing
in Distutils and the PEPs we've prepared for the "big refactoring" will
not be done in the standard library. Distutils in the stdlib trunk will
be reverted to its current 2.6.x state. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I was quite despaired right after the summit. All the work we did
during in the past year would not land into the standard library for
2.7, and all the pre- refactoring work I did, like making the test
coverage decent, was going to be useless for the stdlib. Having that
work included in 2.7 was one of my goal and I worked hard on making sure
most of the important PEPs would be accepted before the feature freeze
for 2.7 happened (the first beta, freezing new features, is in 4 weeks.)&lt;/p&gt;
&lt;p&gt;I was even more depressed because I started to pull out of Distutils
the "sysconfig" module and simplified the code in distutils, while
making sure that the backward compatibility was kept. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I had a twenty minutes meeting with Guido after the Summit to clarify
the situation and he helped me understand why this was the right path
and worked with me on what to do next in the stdlib front and outside
the stdlib. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Basically, &lt;strong&gt;a package that comes in the standard library has a foot in
the grave&lt;/strong&gt; (I am paraphrasing Guido here.). Its APIs is frozen, and
people don't really expect nothing from it, but small new features and
bug fixes. Refactorings are dangerous, if not impossible. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I have hit that problem in the past, in one of the 2.6 bug fix release,
where I broke Setuptools compatibility because of an internal change I
have made in a private method. The breakage was partly because
Setuptools overrides a private method and partly because a public method
that was not clearly documented was affected. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;A few weeks ago the problem happened again : someone complained on
python-dev because a declaration (an exception class) was missing from
Distutils. An exception class was imported from the &lt;em&gt;errors&lt;/em&gt; module into
another Distutils module, but not used anymore there. And the module it
was imported in didn't have an &lt;em&gt;__all__&lt;/em&gt; attribute. A third-party
tool was importing the exception from the wrong module, so when I
cleaned it up the third party module was broken. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So basically, any change I make in Distutils, even a simple cleaning,
and worse, even a private method change, potentially breaks third-party
tools. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;You could argue that they should be careful in how they use Distutils,
and never patch it or change its internal etc., and for edge cases like
missing imports, just fix them. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But hey, Python 2.7 is out of the door in five weeks, and the user
experience will be that &lt;em&gt;Python has broken third-party libraries&lt;/em&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;And the worse part of it : some of these libraries like Setuptools are
not really maintained anymore and expect Distutils not to evolve
anymore. But Setuptools is used nevertheless since it solves some
problems Distutils doesn't. So the end user is the one that will suffer
from those regressions. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In other words, project like Setuptools slows down the work we want to
do in packaging because the current eco-system depends on a big,
monolithic, messy pile of code that is located in different projects
with different maintainers. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;At this point, I understood that the easiest way for Distutils to
evolve was to get away from this pile and grow on another namespace
called &lt;strong&gt;distutils2&lt;/strong&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Welcome Distutils2&lt;/h3&gt;
&lt;p&gt;If you have followed what is going on with packaging since last year,
you might think: "distutils, setuptools, distribute and now distutils2
?, oh no!!!" &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But that is going to be for the benefit of everyone. See the roadmap in
image below. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;[caption id="" align="alignnone" width="385" caption="State of
packaging"][&lt;img alt="State of packaging" src="http://guide.python-distribute.org/_images/state_of_packaging.jpg" /&gt;][][/caption] &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So basically, I have forked Distutils and renamed its package into
&lt;strong&gt;Distutils2&lt;/strong&gt;. The project is located in
&lt;a href="http://hg.python.org/distutils2"&gt;http://hg.python.org/distutils2&lt;/a&gt; and the goal is to put it back into
the standard library as soon as it reaches a state where it starts to be
used by the community. Distutils will just die slowly, probably pulling
Setuptools and Distribute with it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The Distribute project is still important because it can help us
releasing bug fixes or Python 3 support things &lt;strong&gt;&lt;em&gt;today&lt;/em&gt;&lt;/strong&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Distutils2 will be 2.4 to 3.2 compatible and will get back from
Distribute the good bits and implement the PEPs that were accepted
lately &lt;a href="http://tarekziade.wordpress.com/2010/02/10/pep-345-and-386-accepted-summary-of-changes/"&gt;PEP 345 and PEP 386&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;And I am happily removing old code we don't want/need anymore without
worrying about backward compatibility. Yeah ! &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;The packaging sprint&lt;/h3&gt;
&lt;p&gt;After the conferences, we started a packaging sprint and I was
surprised because many people showed up and worked on the topic. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;[caption id="" align="alignnone" width="484" caption="Brainstorming on
PEP 376"][&lt;img alt="Brainstorming on PEP 376" src="http://lh3.ggpht.com/_BBU4XN71nJo/S45d_jRywdI/AAAAAAAAAl4/ggtwGaVkKwU/s720/IMGP7237.jpg" /&gt;][][/caption] &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;We created a few teams to work on PEP 376, mkpkg, the Hitchicker's
Guide to Packaging (HHGP), and Distribute. I won't say the name of each
person, I am too scared to forget someone :D. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;PEP 376&lt;/h3&gt;
&lt;p&gt;Like last year, people from various distributions (Fedora, Ubuntu,
Debian) and I worked on packaging issues. They worked on PEP 386 last
year mainly (the versioning scheme) and focused on &lt;a href="http://www.python.org/dev/peps/pep-0376"&gt;PEP 376&lt;/a&gt; this
year. This PEP is about setting up a standard for installed packages,
and an installation index that allows to query what packages are
installed, and get their metadata. In extend, it provides an uninstall
feature. The goal is to have a standard for all package managers of
course. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;One part of the PEP is about describing the data files that are
installed with the project (like configuration files or documentation)
so they can be removed and maybe relocated. The group focused on
describing the files a project contains in a static way (in setup.cfg)
with variables that can be expanded an installation time (which values
are provided by Python, but globally configurable by the OS packagers.)&lt;/p&gt;
&lt;p&gt;We did quite some work and brainstorming on this, and even focused on
removing setup.py ! A fully static description of a project
(metadata+file list) is the key to a better packaging tool ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Expect a proposal soon on distutils-SIG, for PEP 376. If you want to
have a look, the draft proposal is here: &lt;a href="http://hg.python.org/distutils2/file/243df45d7f6f/docs/design/wiki.rst"&gt;draft&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;mkpkg and Distribute&lt;/h3&gt;
&lt;p&gt;We had two one-member teams at some point, so I can name them without
being scared of forgetting someone ;) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Sean worked on a nice add-on for Distutils2, a script that builds a
setup.py file after asking you a few questions. &lt;a href="http://www.tummy.com/journals/entries/jafo_20100302_003614"&gt;He blogged about it&lt;/a&gt;.
so I don't need to get into further details :) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Noufal worked on fixing some bugs in &lt;a href="http://bitbucket.org/tarek/distribute"&gt;Distribute&lt;/a&gt;. We should do a
release at some point. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;The HitchHicker's Guide to Packaging&lt;/h3&gt;
&lt;p&gt;Another group worked on the guide. The goal is to provide some help for
people that want to package things &lt;strong&gt;today&lt;/strong&gt; and are despaired with the
sparse documentation they can find. Which tool to use ? how ? when ? &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The work done was quite amazing, look at it :
&lt;a href="http://guide.python-distribute.org"&gt;http://guide.python-distribute.org&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I have spoken with Georg Brandl to see how we could move it to
docs.python.org and make it grow there. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Distutils2 coding&lt;/h3&gt;
&lt;p&gt;Besides PEP 345, I worked on making Distutils2 work for 2.2, 2.5, 2.6
and this is now over. I have also almost fully implemented PEP 345 in
there. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;There's now a metadata module with a dict-like &lt;a href="http://hg.python.org/distutils2/file/243df45d7f6f/src/distutils2/metadata.py"&gt;DistributionMetadata&lt;/a&gt;
class that knows how to read and write PKG-INFO files. It also knows how
to interpret the micro-language we've defined: the &lt;a href="http://www.python.org/dev/peps/pep-0345/#environment-markers"&gt;environment
markers&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Last, I've added the &lt;a href="http://www.python.org/dev/peps/pep-0386"&gt;PEP 386&lt;/a&gt; version module : &lt;a href="http://hg.python.org/distutils2/file/243df45d7f6f/src/distutils2/version.py"&gt;version.py&lt;/a&gt;. This
one is used now by the metadata class to control versions. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;More to come ! &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Next sprint at Confoo.ca&lt;/h3&gt;
&lt;p&gt;The &lt;a href="http://www.montrealpython.org/2010/02/upcoming-sprints/"&gt;next packaging sprint&lt;/a&gt; will happen in Montreal, where I am going
as a speaker next week. We will continue the worked started, so stay
tuned.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;State of packaging&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;[&lt;img alt="State of packaging" src="http://guide.python-distribute.org/_images/state_of_packaging.jpg" /&gt;]: http://guide.python-distribute.org/introduction.html#current-state-of-packaging
    "Brainstorming on PEP 376"
  [&lt;img alt="Brainstorming on PEP 376" src="http://lh3.ggpht.com/_BBU4XN71nJo/S45d_jRywdI/AAAAAAAAAl4/ggtwGaVkKwU/s720/IMGP7237.jpg" /&gt;]: http://lh3.ggpht.com/_BBU4XN71nJo/S45d_jRywdI/AAAAAAAAAl4/ggtwGaVkKwU/s720/IMGP7237.jpg&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Wed, 03 Mar 2010 23:27:00 +0100</pubDate><guid>http://blog.ziade.org/2010/03/03/the-fate-of-distutils-pycon-summit-packaging-sprint-detailed-report/</guid></item><item><title>Pycon slides + answers to GM questions</title><link>http://blog.ziade.org/2010/02/20/pycon-slides-answers-to-gm-questions/</link><description>&lt;p&gt;I did my presentation yesterday, and it seems like people enjoyed it,
from what I've heard in the halls and seen online. I am very glad about
this feedback because packaging is not the sexiest topic in programming
conferences in general. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Anyways, here are my slides :
&lt;a href="http://ziade.org/slides/pycon-2010-state-of-packaging.pdf"&gt;http://ziade.org/slides/pycon-2010-state-of-packaging.pdf&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;EDIT: And here's the video on blip : &lt;a href="http://bit.ly/dlPB2X"&gt;http://bit.ly/dlPB2X&lt;/a&gt;&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;And as promised, I've answered to all the questions people asked in the
google moderator :[http://www.google.com/moderator/#16/e=4395
.][]Thanks to everyone that has participated. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I am now going to enjoy the rest of the Pycon event and I am looking
forward to the sprints.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sat, 20 Feb 2010 14:18:00 +0100</pubDate><guid>http://blog.ziade.org/2010/02/20/pycon-slides-answers-to-gm-questions/</guid></item><item><title>Python Language Summit -- Summary of the packaging track</title><link>http://blog.ziade.org/2010/02/18/python-language-summit-summary-of-the-packaging-track/</link><description>&lt;p&gt;Here are quick wrapup on what has been said during the packaging session
in the language summit that is happening today at Pycon. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The four major points are: &lt;br /&gt;
1.  The implementation of the accepted PEPs that have been done lately
    will not happen in Distutils but in a new package in the Distribute
    project (so logically in a "distribute" package). This resolves
    backward compatibility issues: new features will be under the
    "distribute" namespace.
2.  Distribute will stay a third-party package and will be integrated in
    the standard library once it has enough support and feedback from
    the community. So this could happen in 3.3 (or 2.8 ;) ). Some part
    that are useful for the existing distutils package might be added in
    the stdlib today. But the idea is to stop adding features in
    distutils and focus on distribute.
3.  The Hitchhicker's guide to packaging is going to be moved into the
    Python repository, so it becomes part of docs.python.org. It's not
    finished yet but it'll grow there.
4.  Ian threw the idea to have virtualenv as a core feature, but he's
    not sure how yet. Some brainstroming on this should happen during
    Pycon.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 18 Feb 2010 19:03:00 +0100</pubDate><guid>http://blog.ziade.org/2010/02/18/python-language-summit-summary-of-the-packaging-track/</guid></item><item><title>Ask questions about packaging</title><link>http://blog.ziade.org/2010/02/11/ask-questions-about-packaging/</link><description>&lt;p&gt;I have started a Google Moderator series to gather questions people
would like to ask about Python packaging. I will answer to the most
popular questions at the end of my talk at Pycon. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here's the link : [http://www.google.com/moderator/#16/e=4395][] &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Please, add some questions, and vote !&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 11 Feb 2010 22:20:00 +0100</pubDate><guid>http://blog.ziade.org/2010/02/11/ask-questions-about-packaging/</guid></item><item><title>PEP 345 and 386 accepted -- Summary of changes</title><link>http://blog.ziade.org/2010/02/10/pep-345-and-386-accepted-summary-of-changes/</link><description>&lt;p&gt;Several PEPs were accepted this month by Guido, and among them PEP 345
and PEP 386, which are about Python packaging. I am summarizing in this
entry the main changes we've made. &lt;br /&gt;
&lt;/p&gt;
&lt;h2&gt;PEP 345 - Metadata v1.2&lt;/h2&gt;
&lt;p&gt;This PEP is about the Metadata that gets added in your project when you
build a distribution using Distutils or a Distutils-based tool. Those
are fields like "name" or "version" you pass as options in your setup.py
file. They eventually land in a PKG-INFO file then at PyPI and in each
Python's site-packages where the project is installed. They are also
useful when your project gets repackaged by an OS packager. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;New fields&lt;/h3&gt;
&lt;p&gt;PEP 345 adds some new fields : &lt;br /&gt;
-   &lt;strong&gt;Maintainer&lt;/strong&gt; : a string containing the maintainer's name
-   &lt;strong&gt;Maintainer-email&lt;/strong&gt; : a string containing the maintainer's e-mail
-   &lt;strong&gt;Requires-Python&lt;/strong&gt; : Python version(s) that the distribution is
    guaranteed to be compatible with.
-   &lt;strong&gt;Requires-External&lt;/strong&gt; : Describes some dependencies in the system
    that the distribution is to be used
-   &lt;strong&gt;Requires-Dist&lt;/strong&gt; : String naming some other distutils project
    required by this distribution.
-   &lt;strong&gt;Provides-Dist&lt;/strong&gt; : String naming a Distutils project which is
    contained within this distribution.
-   &lt;strong&gt;Obsoletes-Dist&lt;/strong&gt; : String describing a distutils project's
    distribution which this distribution renders obsolete
-   &lt;strong&gt;Project-URL&lt;/strong&gt; : String containing a browsable URL for the project
    and a label for it, separated by a comma.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Maintainer&lt;/em&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;em&gt;Maintainer-email&lt;/em&gt;&lt;/strong&gt; were added because people
were confused about the &lt;em&gt;maintainer&lt;/em&gt; and &lt;em&gt;maintainer_email&lt;/em&gt; options
they have in Distutils. Before PEP 345, if you used the &lt;em&gt;author&lt;/em&gt;
&lt;strong&gt;&lt;em&gt;and&lt;/em&gt;&lt;/strong&gt; the &lt;em&gt;maintainer&lt;/em&gt; fields, one was dropped and one was kept to
fill the &lt;em&gt;Author&lt;/em&gt; metadata field. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Requires-Python&lt;/em&gt;&lt;/strong&gt; was added so people could list the Python
versions their project is compatible with. We do have classifiers
already for that in the Trove classifier, but this new field is more
than a simple field : the version string that is used supports a syntax
that makes it possible to describe any set of Python versions. See the
&lt;a href="http://www.python.org/dev/peps/pep-0345/#version-specifiers"&gt;version specifier&lt;/a&gt; of the PEP. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Remember the &lt;em&gt;requires.txt&lt;/em&gt; metadata Setuptools introduced together
with the &lt;em&gt;install_requires&lt;/em&gt; option ? &lt;strong&gt;&lt;em&gt;Requires-Dist&lt;/em&gt;&lt;/strong&gt; is comparable
to this and will let you define dependencies on other Python projects.
Distutils had the &lt;strong&gt;&lt;em&gt;Requires&lt;/em&gt;&lt;/strong&gt; field, but it was defining dependencies
at the &lt;strong&gt;module&lt;/strong&gt; level and this was never really used by the community.
So &lt;strong&gt;&lt;em&gt;Requires&lt;/em&gt;&lt;/strong&gt; is gone in 1.2. Last, &lt;strong&gt;&lt;em&gt;Requires-Dist&lt;/em&gt;&lt;/strong&gt; is using a
version comparison scheme described in details in &lt;a href="http://www.python.org/dev/peps/pep-0345/#version-specifiers"&gt;the version specifier
section&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Provides-Dist&lt;/em&gt;&lt;/strong&gt; gives you the ability among other things to
reorganize your project names or to distribute a subproject in several
projects. For instance, the project called &lt;em&gt;ZODB&lt;/em&gt; used to include the
project &lt;em&gt;transaction&lt;/em&gt; that is also distributed as a standalone project
now. If &lt;em&gt;ZODB&lt;/em&gt; is installed, and if you need &lt;em&gt;transaction&lt;/em&gt;, you won't
have to install it again. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Obsoletes-Dist&lt;/em&gt;&lt;/strong&gt; will let you make sure a project that is
incompatible with your project is not installed at the same time. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Project-URL&lt;/em&gt;&lt;/strong&gt; is useful to provide a list of URLs for your project
like a browsable repository or a tracker. The goal will be to add a
small box on PyPI project pages for these URLs, so developers can
emphasize them. This will hopefully address the complaints PyPI had in
these past months when the comment system was added. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Some small changes were made on existing fields. One important change
is on the &lt;strong&gt;&lt;em&gt;Description&lt;/em&gt;&lt;/strong&gt; field (&lt;em&gt;long_description&lt;/em&gt; in setup.py).
Before PEP 345, once this value was written in the metadata file, its
empty lines and spaces at the beginning of lines were truncated. In
other words, if a tool was reading back this value, its reSTrucured
syntax was broken. This is no longer the case. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Once I've finished implementing PEP 345, you will be able to read back
a project's metadata usig the &lt;em&gt;DistributionMetadata&lt;/em&gt; class. See an
&lt;a href="http://docs.python.org/dev/distutils/examples.html#reading-the-metadata"&gt;example&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Environment Markers&lt;/h3&gt;
&lt;p&gt;When Pip wants to install all the dependencies a project requires, it
has to follow these steps: &lt;br /&gt;
1.  download the project from PyPI
2.  execute a Distutils command on setup.py, like egg_info, to get the
    metadata. And in particular the list of requirements&lt;/p&gt;
&lt;p&gt;This is mandatory because the metadata are not statically defined and
the developer might need to run some code in his setup.py to know what
dependencies are required, depending on the target platform. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For example: &lt;br /&gt;
       if sys.platform == 'win32':&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="err"&gt;   &lt;/span&gt;     &lt;span class="n"&gt;install_requires&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;pywin32&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt;     &lt;span class="n"&gt;install_requires&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;In other words, there's no way to get the metadata without running
third-party code. Environment markers fix this issue in most cases by
providing a micro-language that can be used at the field level. At the
end, you can write things like that in the metadata: &lt;br /&gt;
       Requires-Dist: pywin32 (&amp;gt;1.0); sys.platform == 'win32'&lt;/p&gt;
&lt;p&gt;And Distutils will provide a tool to parse and execute this expression,
so you know if your target platform have to use this metadata field.
Meaning that Pip or other tools will be able to read metadata of a
project without running any third party code. For example to get all the
dependencies of a project depending on the target platform just by
querying PyPI. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;See all the details &lt;a href="http://www.python.org/dev/peps/pep-0345/#environment-markers"&gt;in the section in the PEP&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;h2&gt;PEP 386&lt;/h2&gt;
&lt;p&gt;Throughout all the PEP 345, we had to compare versions. To be able to
perform this, we need to have a common standard for versions numbers.
That's what PEP 386 was written for. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The idea is not to force people to version their project using this
version scheme, but rather to provide a scheme that is good enough for
interoperability and that is human readable. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;PEP 386 provides this scheme (in pseudo-format) : &lt;br /&gt;
     N.N[.N]+[{a|b|c|rc}N[.N]+][.postN][.devN]&lt;/p&gt;
&lt;p&gt;The corresponding regular expression is: &lt;br /&gt;
       expr = r"""^&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;    &lt;span class="p"&gt;(?&lt;/span&gt;&lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="sr"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;+\.\&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;         &lt;span class="c1"&gt;# minimum &amp;#39;N.N&amp;#39;&lt;/span&gt;

    &lt;span class="p"&gt;(?&lt;/span&gt;&lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="sr"&gt;&amp;lt;extraversion&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(?:&lt;/span&gt;&lt;span class="o"&gt;\.\&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# any number of extra &amp;#39;.N&amp;#39; segments&lt;/span&gt;

    &lt;span class="p"&gt;(?:&lt;/span&gt;

        &lt;span class="p"&gt;(?&lt;/span&gt;&lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="sr"&gt;&amp;lt;prerel&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;abc&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;rc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;         &lt;span class="c1"&gt;# &amp;#39;a&amp;#39; = alpha, &amp;#39;b&amp;#39; = beta&lt;/span&gt;

                                     &lt;span class="c1"&gt;# &amp;#39;c&amp;#39; or &amp;#39;rc&amp;#39; = release candidate&lt;/span&gt;

        &lt;span class="p"&gt;(?&lt;/span&gt;&lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="sr"&gt;&amp;lt;prerelversion&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="p"&gt;(?:&lt;/span&gt;&lt;span class="o"&gt;\.\&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="p"&gt;)?&lt;/span&gt;

    &lt;span class="p"&gt;(?&lt;/span&gt;&lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="sr"&gt;&amp;lt;postdev&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;\.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(?&lt;/span&gt;&lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="sr"&gt;&amp;lt;post&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="p"&gt;))?(&lt;/span&gt;&lt;span class="o"&gt;\.&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="p"&gt;(?&lt;/span&gt;&lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="sr"&gt;&amp;lt;dev&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="p"&gt;))?)?&lt;/span&gt;

    &lt;span class="vg"&gt;$&amp;quot;&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The scheme handles these cases: &lt;br /&gt;
-   pre-releases
-   development versions
-   final versions
-   post-release versions
-   development versions of post-release versions&lt;/p&gt;
&lt;p&gt;Here are some examples: &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;from verlib import NormalizedVersion as V&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;1.0a1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;1.0a2.dev456&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;1.0a2&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;1.0a2.1.dev456&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;1.0a2.1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;1.0b1.dev456&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;1.0b2&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;1.0b2.post345&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;1.0c1.dev456&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;1.0c1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;1.0.dev456&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;1.0&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;1.0.post456.dev34&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;1.0.post456&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;True&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This can look utterly complex to you, but in fact there are good
chances that your version scheme is already compatible with this one.
We've tested in on PyPI, and 88 % of the projects' distributions were
recognized. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;The suggest_rational_version function&lt;/h3&gt;
&lt;p&gt;Let's face it: there are hundreds of valid versionning schemes that are
not compatible with what we've done. So we are adding a function called
&lt;em&gt;suggest_rational_version&lt;/em&gt; that can be used to transform a version
that is not "PEP-386" compliant into one that is compliant. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This does a number of simple normalizations to the given string, based
on an observation of versions currently in use on PyPI. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Given a dump of those versions on February 10th 2010, the function has
given those results out of the 9066 distributions PyPI had: &lt;br /&gt;
-   8058 (88.88%) already match &lt;code&gt;NormalizedVersion&lt;/code&gt; without any change
-   795 (8.77%) match when using this suggestion method
-   213 (2.35%) don't match at all.&lt;/p&gt;
&lt;p&gt;And here's an extract of the 2.35% unrecognized scheme: &lt;br /&gt;
-   0.2-grigoropoulos
-   0.1-alphadev
-   working proof of concept
-   bzr14
-   1 (first draft)&lt;/p&gt;
&lt;p&gt;In other words, they are unusable anyways. If you want to try this on
your own versions, grab the code at
&lt;a href="http://bitbucket.org/tarek/distutilsversion/"&gt;http://bitbucket.org/tarek/distutilsversion/&lt;/a&gt;. And if you version
doesn't match at all and you think its a mistake, let me know so we can
work your case. &lt;br /&gt;
&lt;/p&gt;
&lt;h2&gt;Conclusion + my rant on packaging&lt;/h2&gt;
&lt;p&gt;I have started working seriously on packaging issues a year ago. And I
kept on hearing complaints on how packaging sucks hard. That was
frustrating since some folks and I were working hard to change things
(and we still do). I kept seeing the same people ranting about
packaging, and most of the time they were not willing to help around. I
guess that's just me being naive, but let me say it one more time :) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;If you don't like how packaging works in Python, stop complaining and
come in the Distutils mailing list, or in #distutils on freenode and
help us !&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I am now really glad that these PEPs were accepted by Guido because
they are the first milestone we had to reach to improve things for real
in packaging. Python 2.7 is going to make a big jump forward in this
area ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Next milestone we are trying to reach for 2.7 is to finish PEP 376
(uninstall feature, querying installed packages, etc)&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Wed, 10 Feb 2010 10:24:00 +0100</pubDate><guid>http://blog.ziade.org/2010/02/10/pep-345-and-386-accepted-summary-of-changes/</guid></item><item><title>improving Python&amp;#039;s getpass module</title><link>http://blog.ziade.org/2010/02/06/improving-python039s-getpass-module/</link><description>&lt;p&gt;&lt;strong&gt;UPDATED see the end.&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The Python standard library has a module called &lt;a href="http://docs.python.org/library/getpass.html"&gt;getpass&lt;/a&gt; you can use
to get a password from the prompt: &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;import getpass&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; password &lt;span class="o"&gt;=&lt;/span&gt; getpass.getpass&lt;span class="p"&gt;()&lt;/span&gt;

Password:          &lt;span class="o"&gt;&amp;lt;--&lt;/span&gt; non&lt;span class="o"&gt;-&lt;/span&gt;echoed typing here

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; print password

worked
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;That's nice, and Distutils uses it to ask for your password when you
register or upload a release at PyPI, if it's not found in your
&lt;a href="http://tarekziade.wordpress.com/2009/01/09/distutils-improved-pypirc-for-python-27-and-31/"&gt;pypirc&lt;/a&gt; file. But this is annoying to type and type again your
password, so you end up saving it in &lt;strong&gt;clear text&lt;/strong&gt; in pypirc. Thats
sucks. And the getpass module gets pretty useless if you want to store
and retrieve passwords from other places than the user brain. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But wait... we have the &lt;a href="http://pypi.python.org/pypi/keyring"&gt;Keyring&lt;/a&gt; project now.. what about making
getpass use Keyring so you can safely read a password from your favorite
keyring (Keychain, KWallet, etc..) ? &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I've started to write a new getpass module that could do this. But
instead of adding a keyring dependency in it and struggling for months
(years) to get the addition of Keyring into the stdlib, I have made
getpass pluggable. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In my improved version, you can define in a small configuration file
(getpass.cfg) an arbritrary function that will be used by getpass for
the &lt;em&gt;getpass.getpass&lt;/em&gt; API. Here's such a file: &lt;br /&gt;
&lt;a href="http://docs.python.org/library/getpass.html"&gt;getpass&lt;/a&gt;&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;  &lt;span class="n"&gt;getpass&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;backend&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;keyring:get_pass_get_password&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Here I am configuring get pass to use the &lt;em&gt;get_pass_get_password&lt;/em&gt;
function from the &lt;em&gt;keyring&lt;/em&gt; package. That's a function that gets
installed in your Python once Keyring is installed. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This function has the same interface than the default &lt;em&gt;getpass.getpass&lt;/em&gt;
API and calls keyring. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The modified getpass module is here:
&lt;a href="http://bitbucket.org/tarek/getpass/"&gt;http://bitbucket.org/tarek/getpass/&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;And works against the current trunk of Keyring. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;What I would like to do now is to propose the small changes I've made
in Python's getpass for inclusion in the stdlib. They are backward
compatible changes and offers a simple, yet powerfull way to extend
getpass without adding any other module in the stdlib. And maybe adding
a setpass in there too would make sense. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Update from python-ideas&lt;/h3&gt;
&lt;p&gt;So I brought up the idea in the mailing lists and it turns out (thanks
to the folks at Python-ideas) that the way I want to introduce this
feature is not good for these reasons : &lt;br /&gt;
1.  &lt;em&gt;getpass&lt;/em&gt; is just a function that is used to get a password from the
    prompt. you can consider it as a potential, dummy backend for
    Keyring for example. Trying to make it extendable just denaturates
    its original purpose.
2.  the only use case right now in the stdlib is for Distutils, so it
    doesn't really make sense to have a keyring in there. People can
    just use the Keyring project directly.
3.  Now if other parts of the stdlib have the same need, it will be time
    to think about how it could be included in the stdlib level rather
    than in Distutils.&lt;/p&gt;
&lt;p&gt;So, I'll work for its inclusion at Distutils level rather thah on
getpass level.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sat, 06 Feb 2010 23:51:00 +0100</pubDate><guid>http://blog.ziade.org/2010/02/06/improving-python039s-getpass-module/</guid></item><item><title>Simple command-line vault : CLVault</title><link>http://blog.ziade.org/2010/02/01/simple-command-line-vault-clvault/</link><description>&lt;p&gt;I am pretty happy with the &lt;a href="http://pypi.python.org/pypi/keyring"&gt;Keyring&lt;/a&gt; project. I use it now everywhere
in my Mercurial-based projects, thanks to [mercurial_keyring][]. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;There's one other place I've started to use it: I needed a simple
command-line based tool to save passwords and read them. The tool I've
used so far was &lt;a href="http://keepass.info/"&gt;KeePass&lt;/a&gt;, but I need to run it then click on its UI.
This is time consuming when I simply want to push a password in the
clipboard to use it to unlock something. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So I've wrote these two very simple scripts that use Keyring to store
and retrieve passwords &lt;br /&gt;
   $ clvault-set blog&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;Set&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="n"&gt;password:&lt;/span&gt;

&lt;span class="n"&gt;Password&lt;/span&gt; &lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;clvault&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="n"&gt;blog&lt;/span&gt;

&lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="n"&gt;has&lt;/span&gt; &lt;span class="n"&gt;been&lt;/span&gt; &lt;span class="n"&gt;copied&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="n"&gt;clipboard&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The code that copy the passwords in the clipboard was tested under Mac
OS with its Keychain, but should work under Windows and Linux as well. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I think these scripts can be useful for people like me who spend most
of their time in a bash prompt when they are not in Vim or Emacs. So I
created a project called CLVault.. You can grab it at the PyPI: &lt;a href="http://pypi.python.org/pypi/CLVault"&gt;CLVault
PyPI page&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;or install it like this with Pip: &lt;br /&gt;
   $ pip install clvault&lt;/p&gt;
&lt;p&gt;Let me know if it's useful to you !&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Mon, 01 Feb 2010 17:26:00 +0100</pubDate><guid>http://blog.ziade.org/2010/02/01/simple-command-line-vault-clvault/</guid></item><item><title>Pycon packaging sprint topics</title><link>http://blog.ziade.org/2010/01/11/pycon-packaging-sprint-topics/</link><description>&lt;p&gt;Pycon is coming soon. Here's a list of possible topics I would like to
work on during the sprint: &lt;br /&gt;
1.  adding the features in Distutils I've mentioned in my [earlier
    post][].
2.  work on the standalone release of Distutils, and make sure it works
    with 2.4, 2.5, etc so it can be distributed at PyPI. There are
    already &lt;a href="http://python-distribute.org/"&gt;installable nightly builds&lt;/a&gt; by the way.
3.  Finish the buildbot work so Distutils is tested with more projects
    from PyPI
4.  Continue the work on Distribute, and specifically the work on 0.7: &lt;br /&gt;
   1.  finish the develop command that would work with non-eggs formats
    2.  finish the configure/build/install command where all options are
        computed and saved by the configure command&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Fix plenty of issues in the tracker&lt;/li&gt;
&lt;li&gt;work on a geolocalisation feature for Pip, so the nearest mirror can
    be picked&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Anyone interested in packaging sprinting at Pycon ? Let me know !&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Mon, 11 Jan 2010 22:20:00 +0100</pubDate><guid>http://blog.ziade.org/2010/01/11/pycon-packaging-sprint-topics/</guid></item><item><title>Fixing packaging terminology confusion</title><link>http://blog.ziade.org/2010/01/07/fixing-packaging-terminology-confusion/</link><description>&lt;p&gt;&lt;em&gt;Edit: the discussion is still going on, so I've probably blogged that a
little bit early (I was excited about it ;)). Stay tuned for the final
output.&lt;/em&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Brad Allen launched a thread in &lt;a href="http://mail.python.org/pipermail/distutils-sig/2010-January/015232.html"&gt;Distutils-SIG&lt;/a&gt; about packaging
terminology confusion. In particular the usage of the word "&lt;strong&gt;package&lt;/strong&gt;"
in our community. Part of the confusion is because of the meaning of
this word in Python (that is, a directory containing one of several
Python modules, with a special one named __init__.py) and in some
systems like Debian (there, a package is a distribution file for a
library or an application). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This confusion was present in PEP 345 (which was started years ago, so
that explains it) - and is present in Distutils documentation and also
in PyPI (That is: Python &lt;strong&gt;Package&lt;/strong&gt; Index). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I really like Tres Seaver's definitions, because they match prefectly
the reality: &lt;br /&gt;
-   &lt;strong&gt;package&lt;/strong&gt; means a Python package, (directory intended to be on
    sys.path, with an __init__.py. We *never* mean a distributable
    or installable archive, except when "impedance matching" with folks
    who think in terms of operating system distributions.
-   &lt;strong&gt;distribution&lt;/strong&gt; is such a distributable / installable archive:
    either in source form (an 'sdist'), or one of the binary forms
    (egg., etc.). Any distribution may contain multiple packages (or
    even no packages, in the case of standalone scripts).
-   &lt;strong&gt;project&lt;/strong&gt; is the process / community which produces releases of a
    given set of software, identified by a name unique within PyPI's
    namespace. PyPI manages metadata about projects (names, owners) and
    their releases. Every real project has at least one release.
-   &lt;strong&gt;release&lt;/strong&gt; is a set of one or more distributions of a project, each
    sharing the same version. Some PyPI metadata is specific to a
    release, rather than a project. Every release has at least one
    distribution.&lt;/p&gt;
&lt;p&gt;And I really like Martin's proposal in the thread (in Catalog-SIG since
it was cross-posted): "PyPI would then be the &lt;strong&gt;Python Project Index&lt;/strong&gt;."&lt;/p&gt;
&lt;p&gt;I'll fix Distutils documentation on my side accordingly, as well as the
&lt;a href="http://guide.python-distribute.org"&gt;guide&lt;/a&gt; we are building. Let's promote these definitions :)&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 07 Jan 2010 21:30:00 +0100</pubDate><guid>http://blog.ziade.org/2010/01/07/fixing-packaging-terminology-confusion/</guid></item><item><title>Possible new features for Distutils 2.7</title><link>http://blog.ziade.org/2010/01/07/possible-new-features-for-distutils-27/</link><description>&lt;p&gt;While PEP 345 and PEP 386 are waiting for &lt;a href="http://mail.python.org/pipermail/python-dev/2010-January/094771.html"&gt;the final approval&lt;/a&gt;, I am
back at work on Distutils code work, PEP 376, Distribute, and the
HitchHicker guide to Packaging. The latter is growing faster than I have
expected, thanks to the contributions of John Gabriele. &lt;a href="http://guide.python-distribute.org"&gt;It has quite
some content already&lt;/a&gt;. I think the guide is an important task, and
I'll try to focus on it in this first trimester. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Distutils 2.7 new features&lt;/h3&gt;
&lt;p&gt;Python 2.7 first beta version is &lt;a href="http://www.python.org/dev/peps/pep-0373/#release-schedule"&gt;around the corner&lt;/a&gt;, and once it's
reached we can't add new features. So, besides the code that will be
changed if the PEPs we worked on at Distutils-SIG are accepted, here's a
list of small features I'd like to introduce in Distutils: &lt;br /&gt;
-   &lt;strong&gt;a test command&lt;/strong&gt;, that just uses the new [unittest discovery
    script][] to run unittest-compatible tests.
-   &lt;strong&gt;a new option for sdist called 'extra_files'&lt;/strong&gt;, that will allow to
    list extra files to be included in the distribution. These files
    will not be installed by 'install', just be part of the
    distribution. This will allow including files like CHANGELOG, etc..
    without having to use a MANIFEST template.
-   &lt;strong&gt;a very basic pre/post commit hook for the install command&lt;/strong&gt;. These
    hooks will be deactivated when any bdist_* command runs install to
    create the binary tree. Now for bdist_rpm own hooks, I guess the
    best way would be to make install consumes the same two options than
    bdist_rpm (pre-install, post-install) so a project will be able to
    define a hook that is used by RPM and/or &lt;em&gt;python setup.py install&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If you think about something that should be added in 2.7, speak up !&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 07 Jan 2010 06:46:00 +0100</pubDate><guid>http://blog.ziade.org/2010/01/07/possible-new-features-for-distutils-27/</guid></item><item><title>New year&amp;#039;s Python meme</title><link>http://blog.ziade.org/2009/12/28/new-year039s-python-meme/</link><description>&lt;p&gt;Here's a short, 5 questions, 2009 Python meme. Copy-paste the questions,
and blog your answers !&lt;strong&gt; &lt;br /&gt;
&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. What's the coolest Python application, framework or library you
have discovered in 2009 ?&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Twisted&lt;/strong&gt;. I have used Twisted in small occasions in the past, but
since I have changed my job, a fair amount of my daily work is around
the Twisted framework. I also program in plain Python for some needs,
and I use Pylons to build our website, but Twisted is the biggest part
of our current application. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. What new programming technique did you learn in 2009 ?&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Network programming&lt;/strong&gt;. I was a web programmer before, and now I am
learning how to write programs that need to scale, to work using several
computers. It's pretty exciting, and sometimes pretty exhaustive because
when some bugs occur, they are often leading to &lt;a href="http://en.wikipedia.org/wiki/Snowball_effect"&gt;snowball effects&lt;/a&gt;.
The code needs to be more rigourous, and I need to know exactly what is
going on when interacting with memory or I/O. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. What's the name of the open source project you contributed the
most in 2009 ? What did you do ? &lt;br /&gt;
&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Python&lt;/strong&gt;. I am a commiter since December 24th 2008 (my birthday ;) ).
And I have been working almost exclusively on Distutils. I did around
200 commits in the trunk (not counting the backports and forwardports on
the branches), and I have been mainly correcting bugs and improving
Distutils code coverage, which I raised from 18% to up to 75%. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I have also added some minor new features. The most important ones are
or will be added in the What's new file hopefully. I also did a lot of
work in the PEPs to prepare the future of packaging in Python, and wait
for that work to be finished, to start to change Distutils more. While I
did more commits in Distribute, Distutils was the most time-consuming
project. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. What was the Python blog or website you read the most in 2009 ?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Python Reddit. And I think I am not alone in that case. 90% of my blog
hits come from Reddit :-) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. What are the three top things you want to learn in 2010 ?&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I'd like to learn &lt;strong&gt;how to program the C part of the CPython core&lt;/strong&gt;,
and eventually to try to contribute some patches for CPython. I would
also like to &lt;strong&gt;write my own toy programming language&lt;/strong&gt;, just for fun.
Last, I would like to &lt;strong&gt;learn Japanese&lt;/strong&gt;. And all this comes after
Distutils, Distribute, etc, so I am pretty sure I won't find the time...&lt;/p&gt;
&lt;p&gt;Read other people meme now (If you did one, let me know, I'd like to
link it here): &lt;br /&gt;
-   &lt;a href="http://pydanny.blogspot.com/2009/12/new-years-python-meme.html"&gt;Daniel Greenfeld&lt;/a&gt;
-   &lt;a href="http://regebro.wordpress.com/2009/12/28/new-year’s-python-meme/"&gt;Lennart Regebro&lt;/a&gt;
-   &lt;a href="http://faassen.n--tree.net/blog/view/weblog/2009/12/28/0"&gt;Martijn Faassen&lt;/a&gt;
-   &lt;a href="http://jorgenmodin.net/index_html/archive/2009/12/28/new-years-python-meme"&gt;Jorgen Modin&lt;/a&gt;
-   &lt;a href="http://plope.com/Members/chrism/2009_meme"&gt;Chris McDonough&lt;/a&gt;
-   &lt;a href="http://arcriley.blogspot.com/2009/12/python-meme-2009.html"&gt;Arc Riley&lt;/a&gt;
-   &lt;a href="http://www.protocolostomy.com/2009/12/29/2009-python-meme/"&gt;Brian Jones&lt;/a&gt;
-   &lt;a href="http://coreygoldberg.blogspot.com/2009/12/new-years-python-meme.html"&gt;Corey Goldberg&lt;/a&gt;
-   &lt;a href="http://aclark.net/team/aclark/blog/new-years-python-meme"&gt;Alex Clark&lt;/a&gt;
-   &lt;a href="http://teebes.com/blog/21/new-years-python-meme"&gt;Thibaud "Teebes" Morel l'Horset&lt;/a&gt;
-   &lt;a href="http://www.circulartriangle.com/blog/?p=10"&gt;Matthew Wilkes&lt;/a&gt;
-   &lt;a href="http://blog.garbas.si/2010/1/4/python-2009-meme"&gt;Rok Garbas&lt;/a&gt;
-   &lt;a href="http://www.voidspace.org.uk/python/weblog/arch_d7_2010_01_02.shtml#e1145"&gt;Michael Foord&lt;/a&gt;
-   ...&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Mon, 28 Dec 2009 16:33:00 +0100</pubDate><guid>http://blog.ziade.org/2009/12/28/new-year039s-python-meme/</guid></item><item><title>Starting The Hitchhiker&amp;#039;s Guide to Packaging</title><link>http://blog.ziade.org/2009/12/13/starting-the-hitchhiker039s-guide-to-packaging/</link><description>&lt;p&gt;&lt;strong&gt;DON'T PANIC !&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Following-up various discussions we had about the need to provide a
guide for people that want to understand our rather complex packaging
eco-system, and how they can use it, &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I've decided to start &lt;em&gt;The Hitchhiker's Guide to Packaging&lt;/em&gt;. The goals
are: &lt;br /&gt;
-   consolidate in one single Sphinx-based powered website, tutorials
    and documentation on how to use the various packaging tools and how
    to package, release and distribute your Python application.
-   provide guidelines to the community that are up-to-date with the
    latest work done in packaging
-   see if it can be added in the Python documentation itself at some
    point when its mature, as Georg Brandl suggested.&lt;/p&gt;
&lt;p&gt;If you want to join this documentation effort, by adding your existing
documentation to this guide, by writing new one, or by helping on copy
editing or reviewing, let me know (#distutils on Freenode, or by mail)&lt;/p&gt;
&lt;p&gt;The project repository is hosted on bitbucket:
&lt;a href="http://bitbucket.org/tarek/hitchhiker-guide-packaging/"&gt;http://bitbucket.org/tarek/hitchhiker-guide-packaging/&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I've also created a google group:
&lt;a href="http://groups.google.com/group/packaging-guide"&gt;http://groups.google.com/group/packaging-guide&lt;/a&gt;&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sun, 13 Dec 2009 13:35:00 +0100</pubDate><guid>http://blog.ziade.org/2009/12/13/starting-the-hitchhiker039s-guide-to-packaging/</guid></item><item><title>Distutils and Distribute status (part #1)</title><link>http://blog.ziade.org/2009/11/18/distutils-and-distribute-status-part-1/</link><description>&lt;p&gt;Someone told me on IRC that it's currently hard to follow what's going
on in the packaging front. The truth is that it's almost impossible if
you don't read &lt;strong&gt;all&lt;/strong&gt; mails posted in Distutils-SIG. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So here's a quick wrap-up that can save you some time if you are not
reading Distutils-SIG. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;PEP 345 - Metadata 1.2&lt;/h3&gt;
&lt;p&gt;We are almost done with the update of &lt;a href="http://www.python.org/dev/peps/pep-0345"&gt;PEP 345&lt;/a&gt;. This PEP is
describing the Metadata fields for a distribution, that get added in the
file named PKG-INFO. This file is inserted in all your distribution and
also published at PyPI. It's the ID card of your project. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;We are adding these fields in the metadata: &lt;br /&gt;
-   Maintainer : the maintainer's name
-   Maintainer-email : the maintainer's email
-   Requires-Python : What Python versions are compatible with this
    distribution
-   Requires-External : A list of external dependencies, like "libpng",
    "libxslt"
-   Requires-Dist : A list of Python dependencies, from the names
    founded at PyPI. like "zope.interface"
-   Provides-Dist : A list of additional distribution names this
    distribution provides (as a complement to the one provided in
    "Name")
-   Obsoletes-Dist : A list of Python dependencies that are incompatible
    with the current distribution&lt;/p&gt;
&lt;p&gt;Another important change is &lt;strong&gt;environment markers&lt;/strong&gt;. An &lt;strong&gt;environment
marker&lt;/strong&gt; is a marker that can be added at the end of a field after a
semi-colon (';'), to add a condition about the execution environment. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Examples: &lt;br /&gt;
   Requires-Dist: pywin32, bar &amp;gt; 1.0; sys.platform == 'win32'&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;Obsoletes&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Dist:&lt;/span&gt; &lt;span class="n"&gt;pywin31&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;platform&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;win32&amp;#39;&lt;/span&gt;

&lt;span class="n"&gt;Requires&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Dist:&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;machine&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;i386&amp;#39;&lt;/span&gt;

&lt;span class="n"&gt;Requires&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Dist:&lt;/span&gt; &lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;python_version&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;2.4&amp;#39;&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;python_version&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;2.5&amp;#39;&lt;/span&gt;

&lt;span class="n"&gt;Requires&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;External:&lt;/span&gt; &lt;span class="n"&gt;libxslt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;linux&amp;#39;&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;platform&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This will allow developers to define different conditions depending on
the target platform. Moreover, this will allow tools like Pip to get a
list of all dependencies for a given project and a given platform just
by querying PyPI, and with no downloads or build required ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Last, for all the fields that manipulates versions, PEP 345 will use
the version scheme described in PEP 386. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;PEP 386 - Version scheme&lt;/h3&gt;
&lt;p&gt;We've designed in &lt;a href="http://www.python.org/dev/peps/pep-0386/"&gt;PEP 386&lt;/a&gt; a version scheme that works with most
Python software we know about. This version scheme comes with a new
version comparison algorithm that will be provided by Distutils. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The scheme is in pseudo-regexpr (read the PEP for more details): &lt;br /&gt;
   N.N[.N]+[abc]N[.N]+[.postN+][.devN+]&lt;/p&gt;
&lt;p&gt;Don't be afraid ! It looks complex but it's not. The apparent
complexity is due to the fact that we need to be able to work with
development versions and post-release versions. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;There are good chances that your project already works with this
version scheme. If you want to give it a shot, there's a prototype you
can play with in an external repo here:
&lt;a href="http://bitbucket.org/tarek/distutilsversion/"&gt;http://bitbucket.org/tarek/distutilsversion/&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;PEP 376 - Installation standard&lt;/h3&gt;
&lt;p&gt;&lt;a href="http://www.python.org/dev/peps/pep-0376/"&gt;PEP 376&lt;/a&gt; is quite completed now. We have our "standard" for
site-packages, we know how to query installed projects, and how to
remove them. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The discussions are now focusing on the "data" problem. Which is : how
to describe in Distutils, in a more elegant way, the data files you are
using, such as images, man files etc. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This is required to provide to developers more control on how their
data files are installed on the target system, and to the packagers more
tools to re-package a Python distribution. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Wolodja Wentland has been doing a lot of work in this area and leads
this "data" effort. You can follow the discussion on this work in the
Python wiki, starting at:
&lt;a href="http://wiki.python.org/moin/Distutils/DiscussionOverview"&gt;http://wiki.python.org/moin/Distutils/DiscussionOverview&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;PEP 382 -Namespaces packages&lt;/h3&gt;
&lt;p&gt;Distribute comes with a namespace package system, that allows you to
have packages under the same namespace, spread into several
distributions. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;That's what Plone and Zope use to be able to release all those plone.*
and zope.* distributions. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Martin von Loewis proposed to implement it in Python, and this is
described in &lt;a href="http://www.python.org/dev/peps/pep-0382/"&gt;PEP 382&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;We are now waiting for Martin to implement it, and are ready to drop in
Distribute 0.7.x the namespace feature in favor of supporting the PEP
382 one. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Distutils redesign discussions&lt;/h3&gt;
&lt;p&gt;One thing that makes Distutils a bit hard to work with, is how commands
are designed. David Cournapeau (from the Numpy project) gave us an
example of a use case that makes it hard. He basically needs to run the
"build" command knowing the finalized options from the "install"
command. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In other words, when you call something like : &lt;br /&gt;
   $ python setup.py install --prefix=/some/place&lt;/p&gt;
&lt;p&gt;The install command will use the prefix option to cook some other
options. The build command that needs all the options needs in that case
to look over the install command to get the values. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This is not optimal because it means that a &lt;strong&gt;build&lt;/strong&gt; command depends
on an &lt;strong&gt;install&lt;/strong&gt; command to run. It also makes options redundants from
one command to the other. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The solution we are going to try is to create a new command, called
&lt;strong&gt;configure&lt;/strong&gt;, that will be in charge of building a file with all the
options that are required by the build command and the install command.&lt;/p&gt;
&lt;p&gt;This is not new. It has been implemented years ago in 4suite, and it's
the philosophy behind tools like scons, etc: a configure/make/make
install principle applied to a Python project. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This redesign is going to occur in Distribute 0.7. Once it's ready, if
the community has tried it and gave us positive feedback, I'll push it
in Distutils. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It might happen before Python 2.7 is out, it might not. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Other topics&lt;/h3&gt;
&lt;p&gt;There are many other topics, like PyPI mirroring (&lt;a href="http://www.python.org/dev/peps/pep-0381/"&gt;PEP 381&lt;/a&gt;) etc.
I'll write a blog entry later for these.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Wed, 18 Nov 2009 09:36:00 +0100</pubDate><guid>http://blog.ziade.org/2009/11/18/distutils-and-distribute-status-part-1/</guid></item><item><title>virtualenv and zc.buildout now with Distribute included</title><link>http://blog.ziade.org/2009/11/07/virtualenv-and-zcbuildout-now-with-distribute-included/</link><description>&lt;p&gt;We are still actively working in fixing all the remaining bugs in
&lt;a href="http://pypi.python.org/pypi/distribute"&gt;Distribute&lt;/a&gt; (our Setuptools fork). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But we have reached an important milestone this week: both
&lt;a href="http://pypi.python.org/pypi/virtualenv/1.4"&gt;virtualenv&lt;/a&gt; and &lt;a href="http://pypi.python.org/pypi/zc.buildout"&gt;zc.buildout&lt;/a&gt; now comes with an option to switch to
Distribute. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In virtualenv: &lt;br /&gt;
   $ virtualenv --distribute ENV&lt;/p&gt;
&lt;p&gt;In zc.buildout, using its bootstrap.py file: &lt;br /&gt;
   $ python bootstrap.py --distribute&lt;/p&gt;
&lt;p&gt;Enjoy ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For those who may wonder why they should switch to Distribute over
Setuptools, it's quite simple: &lt;br /&gt;
-   Distribute 0.6.x is a drop-in replacement for Setuptools
-   Distribute is actively maintained, and has over 10 commiters
-   Distribute 0.6.x offers Python 3 support !&lt;/p&gt;
&lt;p&gt;And if you still struggle with packaging issues, the place to hang
around to get some help is the #distutils IRC channel on Freenode.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sat, 07 Nov 2009 01:36:00 +0100</pubDate><guid>http://blog.ziade.org/2009/11/07/virtualenv-and-zcbuildout-now-with-distribute-included/</guid></item><item><title>First Distribute mini-sprint (online)</title><link>http://blog.ziade.org/2009/10/16/first-distribute-mini-sprint-online/</link><description>&lt;p&gt;While we are working at fixing bugs in 0.6.x, we are organizing a first
online coding sprint for the 0.7.x series, and for various community
tasks. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;What are we going to do ?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;We need to finalize the 0.7.x renaming and splitting work&lt;/li&gt;
&lt;li&gt;We also need to define a more detailed roadmap for each splitted
    package (new features, deprecations, etc.)&lt;/li&gt;
&lt;li&gt;We need to build a better test environment, and see if we can set up
    a buildbot for our work&lt;/li&gt;
&lt;li&gt;We need to write a tutorial explaining how Distribute can be used in
    a project. (Python 3 support, Moving from Setuptools to Distribute,
    etc)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Who can join ?&lt;/h3&gt;
&lt;p&gt;There are no particular level required to participate, but we won't be
able in this session to teach to participants how to work with
Mercurial, Python, etc. But if you want to help in the Documentation
part or testing, it's as important and useful as the code and we are
welcoming you. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Last, if you are just interested in evaluating Distribute for your
project, you are welcome to join, we will help you. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Where ?&lt;/h3&gt;
&lt;p&gt;Online, on IRC - Freenode - #distutils channel &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;When, how long and how ?&lt;/h3&gt;
&lt;p&gt;Possible times (all CEST +0200 UTC) : &lt;br /&gt;
-   Sunday October 18th - 6 pm
-   Tuesday October 20th - 7 pm
-   Wenesday October 21th - 7 pm
-   Friday October 23th - 7 pm&lt;/p&gt;
&lt;p&gt;The sprint will be held for a minimum of 3 hours, and people will be
free to leave when they want. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The most important thing is to be present on time when the sprint
starts, so we can make groups and kick it off. If you want to join, add
your name here: &lt;a href="http://doodle.com/bfgv3yi3pi48buuv"&gt;http://doodle.com/bfgv3yi3pi48buuv&lt;/a&gt;&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 16 Oct 2009 19:36:00 +0200</pubDate><guid>http://blog.ziade.org/2009/10/16/first-distribute-mini-sprint-online/</guid></item><item><title>top-posting, mobile devices, mail threads and semantics</title><link>http://blog.ziade.org/2009/10/11/top-posting-mobile-devices-mail-threads-and-semantics/</link><description>&lt;p&gt;There's an interesting discussion on python-dev about how hard it is to
follow a thread when people are starting to top-post, meaning they are
answering by quoting the whole text and putting their answers at the
top. I even got bitten by someone once because I was top-posting (don't
get me wrong, he was right about it, I was just not fully aware of the
problem) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;First of all, if you use a mobile device, there are good chances that
the mail application you are using doesn't give you the choice : it will
quote the text for you and will let you answer at the top. That's how it
works on my android (HTC) phone and I couldn't find a way to change it.
I am expecting mail apps on mobile devices to improve on this. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But this problem reflects how hard it is to follow a thread with +100
answers. Worse, depending on the way people are quoting to provide an
answer for a specific part of a mail, some people will just stop reading
it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Gmail is doing a pretty good job to reduce this problem, because it
will automagically hide old content and you will only see new content on
every new mail in the thread. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But some people are not using Gmail for good reasons. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The other problem with gigantic threads at python-dev is that they
often end up in a tree of several sub-threads, making it very hard to
follow what's going on if you don't sort mails by threads in your
client. And again, this is not possible in some mail clients. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I was very frustrated about this problem on gigantic threads about
Distutils because I was seeing people "lost" in a branch of the thread,
asking questions that were answered at the other end of the tree. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So how could we improve on this ? &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Imho, mail threads are not suited for design discussions. I think a way
to improve the situation could be to link mails by keywords. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Everytime you answer a mail, instead of quoting some of its text, you
provide some keywords related to the topic you want to discuss, and you
just type a plain answer. Your answer and the original mail will then be
linked through a semantical relation, like a RDF triplet. These keywords
could be new headers in the mail that is sent. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Let's imagine all mail client are able to sort and browse the threads
by keywords, and to list the used keywords on the side of the thread.
Meaning that everytime you send a mail, you can pick one of the keyword
that was already used, to limit the number of keywords. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Does that make any sense ?&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sun, 11 Oct 2009 10:55:00 +0200</pubDate><guid>http://blog.ziade.org/2009/10/11/top-posting-mobile-devices-mail-threads-and-semantics/</guid></item><item><title>Distribute 0.6.4 released - zc.buildout support</title><link>http://blog.ziade.org/2009/10/10/distribute-064-released-zcbuildout-support/</link><description>&lt;p&gt;We've juste released &lt;a href="http://pypi.python.org/pypi/distribute"&gt;Distribute 0.6.4&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This version is now fully compatible with &lt;a href="http://pypi.python.org/pypi/zc.buildout"&gt;zc.buildout&lt;/a&gt;, meaning that
you can use Distribute in your buildout transparently as long as you use
our special zc.buildout bootstrap file located here :
&lt;a href="http://nightly.ziade.org/bootstrap.py"&gt;http://nightly.ziade.org/bootstrap.py&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;We've also added in [Jannis' upload_docs command][], that allows a
project to upload its Sphinx based documentation to PyPI. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;As a matter of fact, Distribute is using it now and you can reach its
documentation at : &lt;a href="http://packages.python.org/distribute"&gt;http://packages.python.org/distribute&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you have any feedback, or want to help around, drop us a mail at
&lt;a href="http://mail.python.org/mailman/listinfo/distutils-sig/"&gt;distutils-sig&lt;/a&gt; or come in our IRC channel in #distutils (freenode)&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sat, 10 Oct 2009 22:53:00 +0200</pubDate><guid>http://blog.ziade.org/2009/10/10/distribute-064-released-zcbuildout-support/</guid></item><item><title>Python 2.6.3 and Distribute.</title><link>http://blog.ziade.org/2009/10/03/python-263-and-distribute/</link><description>&lt;p&gt;&lt;a href="http://python.org/download/"&gt;Python 2.6.3&lt;/a&gt; is out, will a lot of bugs fixed. I had my share with
Distutils and fixed quite a few, and 2.6.3 is looking very good so far !&lt;/p&gt;
&lt;p&gt;Just a quick note for Setuptools users: you might bump into a problem
if you provide a C extension. The setuptools code makes some assumptions
on &lt;em&gt;how&lt;/em&gt; and in &lt;em&gt;which order&lt;/em&gt; the Distutils &lt;em&gt;build_ext&lt;/em&gt; API are called.
It also overrides some of these API to do some internal extra work. In
other words, the way Setuptools patches Distutils makes it very
sensitive to any internal Distutils code changes. In this particulare
case you might have this bug: &lt;br /&gt;
   File "...setuptools/command/build_ext.py", line 85, in get_ext_filename   KeyError: 'xxx'&lt;/p&gt;
&lt;p&gt;The fix is quite simple, it can be done by the end-user or in your
package (which is better of course). &lt;br /&gt;
-   In your package : use "&lt;a href="http://ttp://pypi.python.org/pypi/distribute"&gt;Distribute&lt;/a&gt; &gt;= 0.6.3" distribution
    instead of the usual "Setuptools == 0.6c9" distribution in you
    dependencies list. The code remain unchanged and you can still
    "import setuptools" and have it working fine.
-   As an end-user: just do a Distribute installation and your fine
    "(sudo) easy_install Distribute"&lt;/p&gt;
&lt;p&gt;Hang in &lt;em&gt;#distutils&lt;/em&gt; on Freenode, or drop a mail in &lt;a href="http://mail.python.org/mailman/listinfo/distutils-sig"&gt;distutils-SIG&lt;/a&gt;
in case you have a problem. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Just to make things clear: The Distribute 0.6.x series is a mirror of
Setuptools 0.6c9 code, with bug fixes.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sat, 03 Oct 2009 14:20:00 +0200</pubDate><guid>http://blog.ziade.org/2009/10/03/python-263-and-distribute/</guid></item><item><title>Got Python 3 support ?</title><link>http://blog.ziade.org/2009/09/26/got-python-3-support/</link><description>&lt;p&gt;One thing that slows down the adoption of Python 3 is the low number of
available third party projects. If your project depends on some other
projects, you are pretty lucky if they are all available under Python 3.&lt;/p&gt;
&lt;p&gt;I don't think that the problem comes from the Python 3 syntax adoption,
because Python provides a pretty powerful tool to convert your Python 2
code into Python 3 code, called &lt;a href="http://docs.python.org/library/2to3.html"&gt;2to3&lt;/a&gt;. (notice that the backward
process is also available since this summer : &lt;a href="http://pypi.python.org/pypi/3to2"&gt;3to2&lt;/a&gt;). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The biggest issue in my opinion is the lack of packaging support.
Distutils itself works fine on Python 3, but I am talking about
Setuptools, which is widely used in the community and doesn't work under
Python 3. So if your project, or one of its dependency uses Setuptools,
you can't switch to Python 3. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Well, I am glad to say that this is not true anymore, thanks to Martin
von Löwis, &lt;a href="http://regebro.wordpress.com/2009/09/25/setuptools-on-python-3-status-pretty-damn-good/"&gt;Lennart Regebro&lt;/a&gt; and Alex Grönholm that have been working
on Distribute's Python 3 support lately. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The &lt;a href="http://pypi.python.org/pypi/distribute"&gt;Distribute&lt;/a&gt; project is a fork of the Setuptools project and is
now fully compatible with Python 3. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you are using Setuptools, install Distribute 0.6.3, read its
docs/python3.txt file and add Python 3 support to your project. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;And if you need help in porting your distribution to Python 3, drop in
&lt;a href="http://mail.python.org/mailman/listinfo/distutils-sig/"&gt;Distutils-SIG&lt;/a&gt;, we will help you.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sat, 26 Sep 2009 20:06:00 +0200</pubDate><guid>http://blog.ziade.org/2009/09/26/got-python-3-support/</guid></item><item><title>static metadata for distutils</title><link>http://blog.ziade.org/2009/09/12/static-metadata-for-distutils/</link><description>&lt;p&gt;In Distutils, every package has some metadata fields, defined in &lt;a href="http://www.python.org/dev/peps/pep-0314"&gt;PEP
314&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;setup.py&lt;/em&gt; script is the place where you provide them, by calling
the &lt;em&gt;setup&lt;/em&gt; function, located in &lt;em&gt;distutils.core&lt;/em&gt;. Each argument passed
to the function can be one of these metadata. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So basically, you can describe your distribution in the &lt;em&gt;setup.py&lt;/em&gt; file
like this: &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;distutils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;setup&lt;/span&gt;

&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;MyDistribution&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;0.1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;cool&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

      &lt;span class="n"&gt;packages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;my_package&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;ext_modules&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Extension&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;foo&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;foo.c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;/blockquote&gt;
&lt;p&gt;Notice that &lt;em&gt;packages&lt;/em&gt; and &lt;em&gt;ext_modules&lt;/em&gt; in this example are not part
of the Metadata fields. They are extra fields used by some commands. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;From there, various &lt;em&gt;distutils&lt;/em&gt; commands can be called using this
script. They will get these options and act upon. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For instance, the &lt;em&gt;sdist&lt;/em&gt; command will build a source distribution and
create a static &lt;em&gt;PKG-INFO&lt;/em&gt; file that contains the metadata fields. It
will extract them from the arguments you've passed to &lt;em&gt;setup.py&lt;/em&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;install&lt;/em&gt; command will install this &lt;em&gt;PKG-INFO&lt;/em&gt; file in your Python
installation alongside your packages and modules (since Python 2.5) and
some tools like Distribute or Setuptools will let you read these
information once the distribution is installed. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;You can even get the metadata fields values by asking for them directly
through &lt;em&gt;setup.py&lt;/em&gt;: &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;python&lt;/span&gt; &lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;

&lt;span class="n"&gt;MyDistribution&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;/blockquote&gt;
&lt;p&gt;Another example : the &lt;em&gt;register&lt;/em&gt; command can send the metadata or your
distribution to PyPI. They will be made available on PyPI website and
also through its XML-RPC interface: &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;xmlrpclib&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;xmlrpclib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;http://pypi.python.org/pypi&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;release_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;distribute&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;0.6&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;author&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="s"&gt;&amp;#39;The fellowship of the packaging&amp;#39;&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;release_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;distribute&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;0.6&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;keywords&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="s"&gt;&amp;#39;CPAN PyPI distutils eggs package management&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;/blockquote&gt;
&lt;h3&gt;Limitations of metadata&lt;/h3&gt;
&lt;p&gt;Metadata are pretty handy, but there are some obstructing limitations
we bumped into when we started to work on packaging matters during last
Pycon. &lt;br /&gt;
&lt;/p&gt;
&lt;h4&gt;Platform-dependant metadata&lt;/h4&gt;
&lt;p&gt;We wanted to extend the Metadata fields list in order to add a
"&lt;em&gt;require&lt;/em&gt;s" field that can be used to list the requirements (in term
others python packages or modules). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For instance, if you want to define that your project depends on
&lt;em&gt;simplejson&lt;/em&gt;, you could write: &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;distutils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;setup&lt;/span&gt;

&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;MyDistribution&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;0.1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;cool&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

      &lt;span class="n"&gt;packages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;my_package&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;ext_modules&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Extension&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;foo&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;foo.c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;

      &lt;span class="n"&gt;requires&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;simplsjon&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;/blockquote&gt;
&lt;p&gt;This is not a new proposal. It was proposed in &lt;a href="http://www.python.org/dev/peps/pep-0345/"&gt;PEP 345&lt;/a&gt;, but never
really used. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Since then, Setuptools provided a similar field, called
"&lt;em&gt;install_requires&lt;/em&gt;" together with &lt;em&gt;easy_install &lt;/em&gt;script that acts a
bit like a package manager. &lt;em&gt;easy_install&lt;/em&gt; reads the requirements and
install them when you install a distribution. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But the limitation of those requirement fields is that they might be
platform-dependant. For example, you don't need to install &lt;em&gt;simplejson&lt;/em&gt;
anymore under 2.6 since a json library was included in the standard
library. In other cases you might have different dependencies depending
if you run under windows or linux, and so on. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So to be able to get the metadata right, you have to work a little bit
in your &lt;em&gt;setup.py &lt;/em&gt;file: &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;distutils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;setup&lt;/span&gt;

&lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;version_info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;version_info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

    &lt;span class="n"&gt;requires&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;simplejson&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

    &lt;span class="n"&gt;requires&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt;

&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;MyDistribution&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;0.1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;cool&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

      &lt;span class="n"&gt;packages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;my_package&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;ext_modules&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Extension&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;foo&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;foo.c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;

      &lt;span class="n"&gt;requires&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;requires&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;/blockquote&gt;
&lt;p&gt;But the metadata will only be available at install time, when the
&lt;em&gt;install&lt;/em&gt; command will execute the code of &lt;em&gt;setup.py&lt;/em&gt; on the target
system. &lt;br /&gt;
&lt;/p&gt;
&lt;h4&gt;Code-dependant metadata&lt;/h4&gt;
&lt;p&gt;In other words, once a field like &lt;em&gt;requires&lt;/em&gt; is added in the Metadata,
you will not know for sure if it's reliable when you look at the project
page at PyPI. That's because the metadata you will see there will be the
one created by the person that called the &lt;em&gt;register&lt;/em&gt; command and sent
the result. This result is tighted to his environment, not yours. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;To be able to get the metadata for your environment you will need to
run that code again, by downloading the package, then running a
&lt;em&gt;setup.py&lt;/em&gt; command. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Let's try to do it with the &lt;em&gt;lxml&lt;/em&gt; source distribution. Let's try to
get the &lt;em&gt;name&lt;/em&gt; field : &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;python&lt;/span&gt; &lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;

&lt;span class="n"&gt;Building&lt;/span&gt; &lt;span class="n"&gt;lxml&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="mf"&gt;2.2.2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;NOTE:&lt;/span&gt; &lt;span class="n"&gt;Trying&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt; &lt;span class="n"&gt;without&lt;/span&gt; &lt;span class="n"&gt;Cython&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pre&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;generated&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;src/lxml/lxml.etree.c&amp;#39;&lt;/span&gt; &lt;span class="n"&gt;needs&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt; &lt;span class="n"&gt;available&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;Using&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;libxslt&lt;/span&gt; &lt;span class="mf"&gt;1.1.12&lt;/span&gt;

&lt;span class="n"&gt;Building&lt;/span&gt; &lt;span class="n"&gt;against&lt;/span&gt; &lt;span class="n"&gt;libxml2&lt;/span&gt;&lt;span class="sr"&gt;/libxslt in the following directory: /&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;

&lt;span class="n"&gt;lxml&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;/blockquote&gt;
&lt;p&gt;What happened here ? Frankly I am not sure. But asking for the name
(that appears on the last line) called a bunch of code located in the
distribution. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I could probably ask the lxml team to fix this output, and make sure
&lt;em&gt;setup.py&lt;/em&gt; can still be used to work with the metadata, but this was
just to demonstrate a flaw in the way Distutils works : you need to run
third party code just to get the metadata of a distribution you're not
even sure you are going to install on your system. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;The setup.cfg file&lt;/h3&gt;
&lt;p&gt;Part of the problem can be resolved by putting the metadata in a static
file alongside &lt;em&gt;setup.py&lt;/em&gt;. As a matter of fact, the &lt;em&gt;setup.cfg&lt;/em&gt; file is
already used by distutils to store some options. There's even a &lt;em&gt;global&lt;/em&gt;
section that can be used to set the metadata into the &lt;em&gt;Distribution&lt;/em&gt;
object Distutils creates when you run &lt;em&gt;setup()&lt;/em&gt;. Using the global
section that way is not documented and probably not intended. What's
intended is to be able to set some global options like "verbose" or such
things. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;See
[http://docs.python.org/install/index.html#syntax-of-config-files][] &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But the code is a generic setter, that allows you to pass any field (so
the metadata). Call it a bug if you want, but I was pretty excited to
see that I could pass my metadata to Distutils through it. Unfortunately
these values are not passed to the &lt;em&gt;DistributionMetadata&lt;/em&gt; subobject in
Distutils, so it doesn't work exactly like the arguments passed to
&lt;em&gt;setup()&lt;/em&gt;. Too bad ;-)&lt;em&gt; &lt;br /&gt;
&lt;/em&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I could change this right away in the code, but we have better plans I
think. &lt;br /&gt;
&lt;/p&gt;
&lt;h4&gt;A new setup section&lt;/h4&gt;
&lt;p&gt;Instead of working in the &lt;em&gt;global&lt;/em&gt; section which should stay specific
to running options, let's create a new section and put the metadata in
them. &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;[setup]&lt;/span&gt;

&lt;span class="err"&gt;name:&lt;/span&gt; &lt;span class="err"&gt;MyDistribution&lt;/span&gt;

&lt;span class="err"&gt;version:&lt;/span&gt; &lt;span class="err"&gt;0.1&lt;/span&gt;

&lt;span class="err"&gt;description:&lt;/span&gt; &lt;span class="err"&gt;cool&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;/blockquote&gt;
&lt;p&gt;The &lt;em&gt;setup.py&lt;/em&gt; script stays, but is now not containing any metadata
field, and does only contain what I would call "working arguments". e.g.
argument used by commands that are not part of the Metadata: &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;distutils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;setup&lt;/span&gt;

&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;packages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;my_package&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;ext_modules&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Extension&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;foo&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;foo.c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;/blockquote&gt;
&lt;h4&gt;What about platform-dependant fields ?&lt;/h4&gt;
&lt;p&gt;In order not to require any third party code to read the metadata, we
need a way to express platform-dependant fields in the&lt;em&gt; setup.cfg&lt;/em&gt; file.&lt;/p&gt;
&lt;p&gt;The proposed way is to have platform-dependant sections : &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;[setup]&lt;/span&gt;

&lt;span class="err"&gt;name:&lt;/span&gt; &lt;span class="err"&gt;MyDistribution&lt;/span&gt;

&lt;span class="err"&gt;version:&lt;/span&gt; &lt;span class="err"&gt;0.1&lt;/span&gt;

&lt;span class="err"&gt;description:&lt;/span&gt; &lt;span class="err"&gt;cool&lt;/span&gt;

&lt;span class="err"&gt;conditional-sections:&lt;/span&gt; &lt;span class="err"&gt;py25&lt;/span&gt;

&lt;span class="k"&gt;[py25]&lt;/span&gt;

&lt;span class="na"&gt;condition: python_version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;= &amp;#39;2.5&amp;#39;&lt;/span&gt;

&lt;span class="err"&gt;requires:&lt;/span&gt; &lt;span class="err"&gt;simplejson&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;/blockquote&gt;
&lt;p&gt;The &lt;em&gt;py25&lt;/em&gt; section is read only if the expression is true. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Another example: &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;[setup]&lt;/span&gt;

&lt;span class="err"&gt;name:&lt;/span&gt; &lt;span class="err"&gt;MyDistribution&lt;/span&gt;

&lt;span class="err"&gt;version:&lt;/span&gt; &lt;span class="err"&gt;0.1&lt;/span&gt;

&lt;span class="err"&gt;description:&lt;/span&gt; &lt;span class="err"&gt;cool&lt;/span&gt;

&lt;span class="err"&gt;conditional-sections:&lt;/span&gt; &lt;span class="err"&gt;py25,&lt;/span&gt; &lt;span class="err"&gt;py26&lt;/span&gt;

&lt;span class="k"&gt;[py25]&lt;/span&gt;

&lt;span class="na"&gt;condition: python_version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;= &amp;#39;2.5&amp;#39; or python_version == &amp;#39;2.4&amp;#39;&lt;/span&gt;

&lt;span class="err"&gt;requires:&lt;/span&gt; &lt;span class="err"&gt;simplejson&lt;/span&gt;

&lt;span class="k"&gt;[py26]&lt;/span&gt;

&lt;span class="na"&gt;condition: python_version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;= &amp;#39;2.6&amp;#39; and sys_platform == &amp;#39;win32&amp;#39;&lt;/span&gt;

&lt;span class="err"&gt;requires:&lt;/span&gt; &lt;span class="err"&gt;bar&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;/blockquote&gt;
&lt;p&gt;here, "&lt;em&gt;bar&lt;/em&gt;" will be installed under Python 2.6 under Windows, and
"&lt;em&gt;simpljson&lt;/em&gt;" under Python 2.5 or 2.4 on any platform. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Distutils will provide a new function that is able to interpret the
expressions provided in the condition, and calculate the metadata
depending on the platform. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;That's still some code we are running here, but: &lt;br /&gt;
-   We are restricting the execution context to the bare minimum:
    &lt;em&gt;python_version&lt;/em&gt;, &lt;em&gt;sys_platform&lt;/em&gt;, &lt;em&gt;os_name&lt;/em&gt;, and all values
    returned by &lt;em&gt;os.uname()&lt;/em&gt;
-   The function will be vanilla Python: you will be able to extract the
    metadata without running a third party code, and knowing that the
    execution is restricted to a few string comparisons.
-   The code can be executed at PyPI without any potential security
    issue, meaning that the XML-RPC functions will be able to send you
    back the metadata of a packages depending on your environment. In
    other word, a package manager would be able to list all the
    dependency of a distribution for the target platform without
    downloading any of these distribution.&lt;/p&gt;
&lt;h4&gt;There will always be edge cases&lt;/h4&gt;
&lt;p&gt;For the 1% of distributions that need more work to calculate the
metadata,&lt;em&gt; setup.py&lt;/em&gt; will still be present and any option passed as an
argument will override a value provided by &lt;em&gt;setup.cfg&lt;/em&gt;. They'll just
have to add a flag in the &lt;em&gt;setup.cfg&lt;/em&gt; file, indicating that it does not
provides all the metadata, and that running &lt;em&gt;setup.py&lt;/em&gt; is required: &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;[setup]&lt;/span&gt;

&lt;span class="err"&gt;name:&lt;/span&gt; &lt;span class="err"&gt;MyDistribution&lt;/span&gt;

&lt;span class="err"&gt;version:&lt;/span&gt; &lt;span class="err"&gt;0.1&lt;/span&gt;

&lt;span class="err"&gt;description:&lt;/span&gt; &lt;span class="err"&gt;cool&lt;/span&gt;

&lt;span class="err"&gt;static-metadata:&lt;/span&gt; &lt;span class="err"&gt;false&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;/blockquote&gt;
&lt;p&gt;If this flag is present, people will now that running setup.py is
mandatory to get the full set of metadata. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For example, if the web service provided at PyPI to get the metadata,
will be able to return a platform specific set if we provide the target
environment. Let's say we add in distutils an '&lt;em&gt;execution_environment&lt;/em&gt;'
that returns the environment used to interpret the &lt;em&gt;setup.cfg&lt;/em&gt; file: &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;import xmlrpclib&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;xmlrpclib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;http://pypi.python.org/pypi&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;distutils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;util&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;execution_environment&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;execution_environment&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;os_version&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Darwin Kernel Version 9.8.0: Wed Jul 15 16:55:01 PDT 2009; root:xnu-1228.15.4~1/RELEASE_I386&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

 &lt;span class="s"&gt;&amp;#39;os_name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;posix&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

 &lt;span class="s"&gt;&amp;#39;python_version&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;2.6&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

 &lt;span class="s"&gt;&amp;#39;os_release&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;9.8.0&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

 &lt;span class="s"&gt;&amp;#39;os_sysname&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Darwin&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

 &lt;span class="s"&gt;&amp;#39;os_nodename&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;MacZiade&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

 &lt;span class="s"&gt;&amp;#39;os_machine&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;i386&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

 &lt;span class="s"&gt;&amp;#39;sys_platform&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;darwin&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;release_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;MyDistribution&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;0.6&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;execution_environment&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;requires&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;foo&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;bar&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;release_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;MyDistribution&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;0.6&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;execution_environment&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;static-metadata&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;True&lt;/p&gt;
&lt;p&gt;PyPI will be able to generate the metadata by interpreting the
&lt;em&gt;setup.cfg&lt;/em&gt; file with the &lt;em&gt;execution_environment&lt;/em&gt; info. &lt;br /&gt;
&lt;/p&gt;
&lt;h4&gt;What happens now ?&lt;/h4&gt;
&lt;p&gt;I won't write a PEP for this. I don't think it's necessary because this
feature is backward compatible, and if people don't use it in Python 2.7
and 3.2, it will just fade away, like other things in Distutils. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But we need to reach a consensus at Distutils-SIG then inform about it
at Python-dev. I just hope we will have this consensus real quickly,
unlike most topics we are working on for a year ;) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Or maybe I should be a bit of a dictator for this feature and just go
ahead and add it ? Because as &lt;a href="http://sayspy.blogspot.com/"&gt;Brett&lt;/a&gt; told me several times, it's
impossible to make everyone happy about everything. And I'd like to see
Distutils move on. There's so much left to do... &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;What do you think ? How do you like that feature ?&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sat, 12 Sep 2009 00:25:00 +0200</pubDate><guid>http://blog.ziade.org/2009/09/12/static-metadata-for-distutils/</guid></item><item><title>Gsoc is over, we have a Python Keyring lib</title><link>http://blog.ziade.org/2009/08/25/gsoc-is-over-we-have-a-python-keyring-lib/</link><description>&lt;p&gt;The Google Summer of Code is over and the&lt;a href="http://pypi.python.org/pypi/keyring"&gt;first version of the keyring
library was released&lt;/a&gt; last week by Kang at PyPI. &lt;br /&gt;
&lt;/p&gt;
&lt;h2&gt;How Keyring works, the big picture&lt;/h2&gt;
&lt;p&gt;This library implements a simple plugin system. Each plugin has to
implement a set of methods described in an abstract class and can wrap
any underlying Keyring system. We called those plugins "&lt;strong&gt;&lt;em&gt;backends&lt;/em&gt;&lt;/strong&gt;".
The nice thing about it is that you can implement your own custom
backend and make it available through the Keyring configuration file. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Kang has coded various Keyring backends in C and C++ extensions, for
KWallet, Keychain, and Gnome. We also have added a Keyring
implementation that uses the Win32Crypto API so windows users can use
the lib. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;When the Keyring lib is used, all declared plugins, whether they are
provided by the lib itself or by a third party package, will be loaded.
Then they will be asked a simple question: &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;"Can you run in this environment ?"&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The backend can answer one of these: &lt;br /&gt;
-   "Yes, I could work in this environment"
-   "No, I can't"
-   "Yes and you should use me !"&lt;/p&gt;
&lt;p&gt;The library filters out backends that can't work on the target, sort
the remaining ones, and get one of the best backend. This doesn't
happens of course if you explicitely define which backend you want to
use, which is possible. &lt;br /&gt;
&lt;/p&gt;
&lt;h2&gt;What's next&lt;/h2&gt;
&lt;p&gt;Keyring 0.1 is out and there will probably be 1 or 2 releases to
stabilize the code. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The next steps will be : &lt;br /&gt;
-   to use it in Distutils, with a soft dependency : Distutils will let
    you use it through configuration if it detects Keyring is installed.
-   to promote its usage and in particular see if projects like
    Mercurial could use it
-   to work on a PEP for its integration in Python stdlib, in the
    getpass module&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 25 Aug 2009 12:04:00 +0200</pubDate><guid>http://blog.ziade.org/2009/08/25/gsoc-is-over-we-have-a-python-keyring-lib/</guid></item><item><title>Gsoc Keyring project - post-midterm quick status</title><link>http://blog.ziade.org/2009/08/05/gsoc-keyring-project-post-midterm-quick-status/</link><description>&lt;p&gt;&lt;a href="http://kangzhang-en.blogspot.com/"&gt;Kang&lt;/a&gt; (my Gsoc student) work is going very well on the Python
Universal Keyring Library. He's basically finished to implement back
ends for major keyring systems: &lt;br /&gt;
-   Apple KeyChain
-   Gnome Keyring
-   KDE Wallet&lt;/p&gt;
&lt;p&gt;And for Windows users, there's an extra back end based on
Win32CryptoKeyring, which stores a crypted version of the password in a
file. Last, there's another File-based crypted back end based on the
PyCrypto lib. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The library is extensible and there's an abstract base class that
provides an interface you can use to write your own back end. Each back
end has to return a "recommandation" value depending on the execution
context (the platform, and anything the back end wants to check). 0
means the back end is not compatible, 1 means it will work, 2 means it's
recommended to use it. This allows us to pick the best back end
automatically depending on the execution context. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Check the code here : &lt;a href="http://bitbucket.org/kang/python-keyring-lib/"&gt;http://bitbucket.org/kang/python-keyring-lib/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Kang is tweaking the API names, modules names etc, and then will: &lt;br /&gt;
-   add some documentation
-   write a patch for getpass.getpass
-   start a web page for the lib&lt;/p&gt;
&lt;p&gt;For the latter we are brainstorming on the project name before we
release it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I've asked on Twitter, here are some proposals so far (thanks guys!): &lt;br /&gt;
-   @nightlybuild: &lt;strong&gt;Vercotti&lt;/strong&gt; ? ( the recurring Sicilian connected
    gangster, played by Michael Palin - &lt;a href="http://tiny.cc/luigi"&gt;http://tiny.cc/luigi&lt;/a&gt;
-   @pmclanahan: I like "&lt;strong&gt;Zoot&lt;/strong&gt;". From The Holy Grail, the name of the
    naughty sister in the perilous castle Anthrax.
-   @tpherndon: &lt;strong&gt;Bridge Keeper&lt;/strong&gt; - "Answer me these questions three!"
-   @gurneyalex: you could consider &lt;strong&gt;creosote&lt;/strong&gt; too
-   @jessenoller: Call it &lt;strong&gt;bucket&lt;/strong&gt;. From meaning of life.
-   @regebro: "&lt;strong&gt;Biggles&lt;/strong&gt;". Because he chains an old woman to a wall.
    &lt;a href="http://www.ibras.dk/montypython/episode15.htm"&gt;http://www.ibras.dk/montypython/episode15.htm&lt;/a&gt;
-   @gurneyalex: &lt;strong&gt;BrightSide&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;And you, what's your proposal ? &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Once the name is picked, we will start promoting the lib. I'll work on
distutils integration, and we will propose it to projects like Mercurial
(it makes sense to allow the removal of those clear-text passwords from
your hgrc when you are doing http auth). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Have I said it already ? being a GSoc mentor is a great experience,
especially when you have the chance to pick students like Kang.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Wed, 05 Aug 2009 21:24:00 +0200</pubDate><guid>http://blog.ziade.org/2009/08/05/gsoc-keyring-project-post-midterm-quick-status/</guid></item><item><title>Words on : Distribute, Distutils, PEP 376, PEP 386, PEP 345</title><link>http://blog.ziade.org/2009/07/24/words-on-distribute-distutils-pep-376-pep-386-pep-345/</link><description>&lt;p&gt;I am taking a break for a week starting tomorrow morning. My wife made
it clear : &lt;strong&gt;my laptop and internet are totally forbidden during 7
days&lt;/strong&gt;. So I thought it was a good opportunity to make a status of all
the projects that are going on in the packaging world. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;[&lt;img alt="image" src="http://farm2.static.flickr.com/1327/1068561077_dc14096ad3_o.gif" /&gt;][]Distribute&lt;/h3&gt;
&lt;p&gt;The setuptools fork is doing good. We've discussed the roadmap strategy
with some people at Distutils-SIG and came up with that plan: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0.6 will be released the first week of August&lt;/strong&gt;. It will support
Python 2.x only and will provide a bootstrap to replace an existing
version of setuptools, as described in my &lt;a href="http://tarekziade.wordpress.com/2009/07/22/preparing-to-release-distribute-0-6/"&gt;previous entry&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0.7 will support Python 2.x and 3.x&lt;/strong&gt; and will refactor the code in
three distributions. It will also rename all parts under new names, so
they will not compete anymore with setuptools. Its development will
start right after 0.6 is out. If you have ideas or feature requests,
please add them in our &lt;a href="http://bitbucket.org/tarek/distribute/issues/"&gt;issue tracker at bitbucket&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Distutils&lt;/h3&gt;
&lt;p&gt;They are &lt;a href="http://bugs.python.org/issue?@columns=title,id,activity,status&amp;amp;@sort=-activity&amp;amp;@group=priority&amp;amp;@filter=components,status&amp;amp;@pagesize=10&amp;amp;@startwith=0&amp;amp;status=1&amp;amp;components=3&amp;amp;@dispname=Distutils"&gt;some patches waiting&lt;/a&gt; for my attention, I need to work on
when I am back. But globally, if you open Distutils code, you will
notice that it has been PEP8-fied (besides a few places I need to
finishe) and test covered. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Some small refactorings have been done, like removing duplicate code
and using the tar module of the stdlib instead of the tar command on the
system, thus allowing building tar archive under win32 without having to
install the tar program for example. Another nice change was done on the
upload command: it uses urllib2 now, meaning that you can use an http
proxy with a environment variable transparently. Other changes are on
their way, like fixing the mess with "compiler" option, and cleaning up
some remaining duplicate behaviors. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But nothing is deeply changing yet on how Distutils works. It waits for
the PEPs we are working on to be accepted... &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;PEP 376&lt;/h3&gt;
&lt;p&gt;After I thaught this PEP was ready to be accepted, I sent it from
Distutils-SIG to Python-dev. Then we had very long threads on Python-dev
about it, showing that it was not ready at all... &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Man, I don't think I've picked the easiest topic ;) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Anyways, people gave a lot of thaught on the topic, and Paul Moore
helped building a PEP 302-compatible version of the prototype, you can
see at &lt;a href="http://bitbucket.org/tarek/pep376/"&gt;bickbucket&lt;/a&gt;. From there, we still need to fix the problem of
the absolute/relative paths in the RECORD file, and try to have real
world uses cases, with various package managers applications. But we
will eventually have an acceptable PEP, I hope, within a few months. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This pep will add query APIs in pkgutils in the stdlib, so you know
what's installed in your Python. It will also provide tools to uninstall
installed ditributions. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It's a hot topic because we are dealing with the fuzzy boundary between
&lt;em&gt;Python the extendable language&lt;/em&gt;, where you can install distributions
using distutils-based tools, and &lt;em&gt;Python the interpreter&lt;/em&gt;, that gets
extended with packages managed by an OS-based package manager like APT
under Debian for example. So for the latter, all our tool are getting
too much in the way and should not make it hard for system packagers to
extract the metadata they need to re-package distutils-based
distributions. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;PEP 386&lt;/h3&gt;
&lt;p&gt;Man, this PEP would make our life easier. It's all the work we started
during Pycon, to find an &lt;em&gt;acceptable&lt;/em&gt; way to compare versions. By
acceptable I mean that could be used in our community &lt;strong&gt;and&lt;/strong&gt; workable
by OS packagers. I said I would drop it, because there were too much
controversy, but quite a few people would like to see it accepted. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I doubt Guido will accept a PEP that would enforce a version scheme
though. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I had created this PEP, so we could move forward on &lt;strong&gt;PEP 345&lt;/strong&gt;, where
we want to introduce "install_requires", the field used in setuptools
to list dependencies. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The prototype for PEP 386 is in bitbucket too : &lt;a href="http://bitbucket.org/tarek/distutilsversion/"&gt;verlib&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Whatever happens, I'll probably publish it. Its useful to build a
package system, and it has a function to translate most
distutils/setuptools version schemes. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Now about my addiction&lt;/h3&gt;
&lt;p&gt;I know that sounds stupid, but I don't know what is going to happen
next week. It's been years since I've not been fully offline for a week.
I already have right now so many threads to catch up on various mailing
lists that I am scared of how it's going to be in 7 days... &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But it's probably a good thing for me to do this :) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I am off, see you in 7 days !&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;Internet Addicted&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;[&lt;img alt="image" src="http://farm2.static.flickr.com/1327/1068561077_dc14096ad3_o.gif" /&gt;]: http://www.flickr.com/photos/9009139@N08/1068561077/sizes/o/&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 24 Jul 2009 22:56:00 +0200</pubDate><guid>http://blog.ziade.org/2009/07/24/words-on-distribute-distutils-pep-376-pep-386-pep-345/</guid></item><item><title>Vacations + a new job</title><link>http://blog.ziade.org/2009/07/24/vacations-a-new-job/</link><description>&lt;p&gt;I am officialy on vacations ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I've just finished my time at Alterway / Ingeniweb, where I was CTO for
the last two years. Well CTO was a translation for "Directeur technique"
in French, which is more like "head of developments". So I was like some
kind of lead developer, but without a code project :) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;These two years were a great time for me. It's time for me to summarize
what I have done, what went right, and what went wrong. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Summary of my work at Ingeniweb&lt;/h3&gt;
&lt;p&gt;I won't talk about customer projects, but about what I did that served
both for our projects and for the community. Ingeniweb is a company that
does (did in fact) Plone websites. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I was hired at Ingeniweb with some clear goals (I had more, but these
are the most interesting): &lt;br /&gt;
1.  improve the release process
2.  help Ingeniweb get back into the community
3.  QA work&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Improve the release process &lt;/strong&gt;: when I started, Ingeniweb was not
using zc.buildout, the de-facto Plone standard, so one of the biggest
task I achieved was to make them switch to zc.buildout, and eventually
make some minor contributions in zc.buildout. That was a hard work
because the average developer was against such a big change in his
habits. I eventually succeeded. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I've also worked on plone.org side to make it "pypi compatible" and
eventually became a Python commiter to improve distutils, the mother of
all packaging tools in Python. That was very positive for me and for the
community, I think. And I am now involved in Python development for the
future, trying to write some useful PEPs and so on. This is going to be
one of the major community task I'll be working on for the upcoming
years I think. &lt;br /&gt;
&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;strong&gt;Help Ingeniweb get "back into the Plone community"&lt;/strong&gt;: this was quite&lt;/dt&gt;
&lt;dt&gt;an interesting task. The people that hired me were frustrated because&lt;/dt&gt;
&lt;dt&gt;their developers were not (besides a few exceptions) part of the Plone&lt;/dt&gt;
&lt;dt&gt;community. So I worked on this by trying to understand why it happened.&lt;/dt&gt;
&lt;dt&gt;It's quite simple in fact : that's because of the lack of communication&lt;/dt&gt;
&lt;dd&gt;people in our company were not talking enough with the community and
were trying to solve and work on their problems on their side. No
judgment or disrespect on this : this is mainly because of the language
barrier I think. And also because the people in charge were not sending
our developers in conferences anymore. Being part of an OSS community
requires from the company managers to send their developers in
conferences from time to time. &lt;br /&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;The output of this task was not positive for Ingeniweb, but in the
meantime very positive for me : I was able to become part of the Plone
community and meet great people. Although I was unable to push my
co-workers to follow me in this, neither to make the company contribute
more on this. We organized one sprint, but given the size of the company
at that time (20+) this was not enough in my opinion. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The worst part I think is were I realized that the people that asked me
to do it, didn't care much about it anymore. The boss left us, and the
"second" left right after :) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;QA in customer projects&lt;/strong&gt; : The third main task was to raise the QA
in all projects, starting from scratch : beside a very few exceptions,
people were not making tests neither buying the Test-Driven approach.
The problem with TDD is that it cannot work if it's not done everywhere
by everyone in the company. When this is the case, someone who doesn't
practice TDD looks like he's trying to break a window of the house we
are all building together. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But when you are working with developers that don't practice TDD, you
need to first convince the project managers that it's better, otherwise
it doesn't take. I have convinced some co-workers, but at the end, TDD
didn't work out because some managers were just telling their developers
to drop TDD to go "faster" in the customer projects. (faster... into
troubles for big applications) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;At the end, I think it's very hard to raise the QA in companies that
sells development services, because it implies a lot of commitment from
all people involved, from sells to managment. In an software editor
company it's easier because you can put some guards around your software
code base and keep the QA high. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But along the way I did some interesting projects to set up continuous
integration. collective.buildbot is one of those. It allows you to set
up automatically a buildbot into a zc.buildout-based project. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Don't get me wrong if you feel that this is a pessimistic summary :
everything else was just great ! &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;What's next ?&lt;/h3&gt;
&lt;p&gt;When I am back from my vacation, I am starting a new work in a startup
in Paris, providing a &lt;a href="http://en.wikipedia.org/wiki/Software_as_a_service"&gt;Saas&lt;/a&gt; in the cloud fully written in Python. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I'll blog about it when I am back :)&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 24 Jul 2009 16:38:00 +0200</pubDate><guid>http://blog.ziade.org/2009/07/24/vacations-a-new-job/</guid></item><item><title>Preparing to release Distribute 0.6</title><link>http://blog.ziade.org/2009/07/22/preparing-to-release-distribute-06/</link><description>&lt;p&gt;According to the &lt;a href="http://doodle.com/4eyxzrwgwq4a6t9s"&gt;poll&lt;/a&gt;, The name of the fork will be &lt;strong&gt;Distribute&lt;/strong&gt; !&lt;/p&gt;
&lt;p&gt;The code should not be changed anymore at this point, and I am working
on the bootstraping so installing Distribute will work with an existing
Setuptools installation and will replace it for the applications that
requires it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This is done by detecting an installation of Setuptools, and replacing
it with a &lt;em&gt;fake&lt;/em&gt; installation. This means that once you've installed
Distribute, applications and especially installers will think that
&lt;em&gt;setuptools 0.6c9&lt;/em&gt; is installed. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;That's pretty strong and intrusive, but required for a simple switch :
even if the programs you are using have a setuptools dependency, they
will work without requiring any change on the code or in their setup.py
files. Same goes for zc.buildout apps. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I still have a lot of work for this part : &lt;br /&gt;
-   I don't detect and patch properly single-version-externally-managed
    setuptools installation yet (required for pip)
-   I need to fix a bug on a first run under jython
-   I need to fix a bug when virtualenv is not used with
    --no-site-packages
-   I need to do extensive tests on zc.buildout to see if it behaves
    correctly&lt;/p&gt;
&lt;p&gt;If you want to help: &lt;br /&gt;
1.  download [http://nightly.ziade.org/install_test.py][]
2.  run it with the Python interpreter of your choice (possibly a
    virtualenv-ed one)&lt;/p&gt;
&lt;p&gt;To uninstall, follow the Uninstallation instructions here :
&lt;a href="http://bitbucket.org/tarek/distribute/src/tip/README.txt"&gt;http://bitbucket.org/tarek/distribute/src/tip/README.txt&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;*Disclaimer: it might break your installation**** &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If the test end up with this line: **** Test is OK. It worked.
Otherwise, please let me know ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The next "big" step will happen with Distribute 0.7 because the plan is
to split the code in several distributions with renamed modules: &lt;br /&gt;
-   &lt;strong&gt;Distribute&lt;/strong&gt; : the core (setuptools package but renamed)
-   &lt;strong&gt;DistributeInstall&lt;/strong&gt; : easy_install on its own, and renamed.
-   &lt;strong&gt;DistributeResource&lt;/strong&gt; : will contain the pkg_resources.py module,
    renamed&lt;/p&gt;
&lt;p&gt;I'll post more details on it in the upcoming days.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Wed, 22 Jul 2009 17:30:00 +0200</pubDate><guid>http://blog.ziade.org/2009/07/22/preparing-to-release-distribute-06/</guid></item><item><title>The strange world of packaging - forking setuptools</title><link>http://blog.ziade.org/2009/07/19/the-strange-world-of-packaging-forking-setuptools/</link><description>&lt;p&gt;Again, like a year ago, people had enough of the fact that the
setuptools project is not maintained since 9 months. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Phillip Eby explained that he doesn't have time to do it unless someone
would pay him for that. But in the meantime, he doesn't bless anyone to
do it. Well, he has blessed some people to do it (Ian Bicking and Jim
Fulton), but unfortunately these people are not willing to do it because
they have a lot of other projects going on. Other people that could
maintain it, including me, fail in his "unqualified people" category :)&lt;/p&gt;
&lt;p&gt;So we are all locked in a strange situation where tons of patches are
ready to be commited in the setuptools tracker but are not making it.
Several non-public forks have started to appear around of course. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So again, I decided with some other people to create a fork called
"Distribute". It's a real fork located here :
&lt;a href="http://bitbucket.org/tarek/distribute"&gt;http://bitbucket.org/tarek/distribute&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;By &lt;em&gt;real&lt;/em&gt; I mean that this fork was not created with the purpose of
forcing Phillip to do a release like we did last year for the 0.6c9
release, but with the intention to free us from that strange situation
where we all depend on his wills and (lack of) time. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The plan is to release a first version next week, that corresponds to
the setuptools 0.6 branch, with some patches applied. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Next, we are planning to start a 0.7 version where the code will be
splitted in several distributions: &lt;br /&gt;
-   a distribution for pkg_resources
-   a distribution for the setuptools package itself
-   a distribution for easy_install&lt;/p&gt;
&lt;p&gt;A little bit of bikeshedding is going on to pick a name for that fork,
and we ended up running a &lt;a href="http://doodle.com/4eyxzrwgwq4a6t9s"&gt;poll&lt;/a&gt;. (vote!) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Now, right after &lt;a href="http://mail.python.org/pipermail/distutils-sig/2009-July/012665.html"&gt;I have announced this plan on Distutils-SIG&lt;/a&gt;,
Phillip reacted by annoucing a similar plan, e.g. splitting the
setuptools project in several distributions. But since he previously
said that he didn't have the time to do it, I doubt that it'll work out
unless he's opening its development to a wider range of developers and
maintainers. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;That's the strange world of packaging...&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sun, 19 Jul 2009 09:59:00 +0200</pubDate><guid>http://blog.ziade.org/2009/07/19/the-strange-world-of-packaging-forking-setuptools/</guid></item><item><title>Distutils nighlty builds</title><link>http://blog.ziade.org/2009/07/03/distutils-nighlty-builds/</link><description>&lt;p&gt;If you want to work (or try to find some bugs in it to help out) with
the latest bleeding-edge Distutils trunk version that will be shipped
with Python 2.7 and Python 3.2, you can do it now ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I am creating a nighlty build every day here now :
&lt;a href="http://nightly.ziade.org/"&gt;http://nightly.ziade.org/&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This distribution can be installed in your existing Python 2.6
installation, and will probably work with 2.5. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I'm not planning to publish standalone versions yet, except nighlty
build, at least until 2.7 and 3.2 are starting to be released as alphas.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 03 Jul 2009 13:57:00 +0200</pubDate><guid>http://blog.ziade.org/2009/07/03/distutils-nighlty-builds/</guid></item><item><title>Dropping PEP 386 (versions comparison)</title><link>http://blog.ziade.org/2009/07/03/dropping-pep-386-versions-comparison/</link><description>&lt;p&gt;As you might know, we are working hard on Distutils side for Python 2.7
and 3.2 upcoming releases. The biggest work is on &lt;a href="http://www.python.org/dev/peps/pep-0376/"&gt;PEP 376&lt;/a&gt;, that will
introduce among other things a uninstaller function and functions to
query installed distributions. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The other "big" work is on &lt;a href="http://www.python.org/dev/peps/pep-0345/"&gt;PEP 345&lt;/a&gt;. We want to introduce a new
metadata field called "install_requires" to be able to express
requirements. That's from the setuptools project and is quite used by
the community. Notice that there were several attempts to define
requirements in the past in Distutils, but none of them really made it
through. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For instance, if you want to define docutils as a dependency for your
distribution but a version less or equal to 0.4, you can say : &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;docutils &amp;lt;= 0.4&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;But as long as you want to work with such dependencies and provide a
way to express them with operators, you have to be able to compare
versions. For instance if you want to compare an installed docutils
distribution to see of it is compatible with 0.4. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;That's another big topic we have been working on for the last few
months, with people from various communities (Fedora, Ubuntu, etc). And
I have started to write down &lt;a href="http://www.python.org/dev/peps/pep-0386/"&gt;PEP 386&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But comparing version appears to be a topic that cannot be generic. It
seems that Distutils, therefore Python, shouldn't enforce any rule on
this. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Furthermore, since we have said that Distutils should be a lighter
package, it will not implement a complete package managment system, like
setuptools or zc.buildout does. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So I've decided to propose to drop PEP 386, and stick on a very simple
rule in PEP 345, saying that requirements can be defined, with : &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;distribution_name OPERATOR version&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;where OPERATOR is in &gt;, &amp;lt;, ==, !=, &gt;= or &amp;lt;=. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Last, so the work done at Pycon and in Distutils-SIG is not lost, I
will publish the library we wrote. This could be a very good basis for
packaging managment systems out there.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 03 Jul 2009 13:44:00 +0200</pubDate><guid>http://blog.ziade.org/2009/07/03/dropping-pep-386-versions-comparison/</guid></item><item><title>mod_ntlm in FreeBSD</title><link>http://blog.ziade.org/2009/06/30/mod_ntlm-in-freebsd/</link><description>&lt;p&gt;We had to install [mod_ntlm][] under a FreeBSD 6.4 server with a
colleague, but the port for this package seems broken at least for
FreeBSD 6.2 and 6.4. It just doesn't work when it initiates an NTLM
session through SMB, and it seems to be compiled without the log support
so the last log you get doesn't give any useful hint on what's wrong. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So we had to recompile it to activate the log and try understand what
the problem was. But Apache doesn't not support threaded mode under
FreeBSD 6.x (or I couldn't manage to make it work howsoever), so
mod_ntlm failed to compile since it uses Apache's thread mutexes to
perform some locks on some operations. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;We've deactivated them and recompiled the module, and now it works now
like a charm. It's weird because it means that the binary package for
this module is completely broken. I couldn't find any place mentioning
this. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Anyways, I know this is an old software combination, but since Google
doesn't give any hint on this problem, I wanted to blog about it and
join the diff I made out of mod_ntlm2 at
&lt;a href="https://modntlm.svn.sourceforge.net/svnroot/modntlm/trunk"&gt;https://modntlm.svn.sourceforge.net/svnroot/modntlm/trunk&lt;/a&gt; here:&lt;a href="http://www.afpy.org/Members/tarek/mod_ntlm2.diff"&gt;diff
file&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So if you bump into this problem, you will hopefully reach this page
and save a few hours.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 30 Jun 2009 13:39:00 +0200</pubDate><guid>http://blog.ziade.org/2009/06/30/mod_ntlm-in-freebsd/</guid></item><item><title>Smoke testing in Distutils</title><link>http://blog.ziade.org/2009/05/28/smoke-testing-in-distutils/</link><description>&lt;p&gt;I am not really sure &lt;strong&gt;smoke testing&lt;/strong&gt; is the best name to describe what
I am trying to set up. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Wikipedia says: &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In computer programming and software testing, smoke testing is a
preliminary to further testing, which should reveal simple failures
severe enough to reject a prospective software release. In this case,
the smoke is metaphorical.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;But I think it describes well the need I have : being able to test the
output of several releases of Distutils, without relying on unit tests
for that. Simply because they did not exist on early versions of
Distutils. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Why do I need "smoke tests"&lt;/h3&gt;
&lt;p&gt;Lately, I was working on Distutils, removing some old bugs and adding
some tests. Working on an under-tested package is like opening a can of
worms : when you change something you can introduce some regressions.
It's hard to avoid for sure, because even if you add some tests
subsequently for some features, you are not really doing proper TDD,
where the tests and the code grow together. So there might be some
uncovered cases left even if the coverage is improving slowly. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Regression also happens when you correct a behavior that was broken for
years : if third party tools used a broken behavior without knowing it,
fixing the behavior at some point becomes a regression from them. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But these pitfalls are unavoidable. Hopefully they don't last for too
long in a big project like Python. They are quickly reported, either by
buildbots, either by someone from the community. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The last behavior problem I had was on the &lt;strong&gt;build_ext command&lt;/strong&gt;.
There's a string option called &lt;em&gt;"compiler"&lt;/em&gt;, were you can force the
compiler type. You can put for instance &lt;strong&gt;'linux&lt;/strong&gt;' and build_ext will
use the &lt;em&gt;UnixCompiler&lt;/em&gt; compiler class. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Although this option name is misleading. It should have been called
&lt;em&gt;"compiler_type"&lt;/em&gt; in the first place. And the &lt;em&gt;build_ext&lt;/em&gt; command adds
another problem : it turns the &lt;em&gt;compiler&lt;/em&gt; attribute of the class where
the option value is stored, into a compiler instance. No need to say a
class attribute value should not have several types. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The first application to suffer from this is Python itself. It uses
&lt;em&gt;build_ext.compiler&lt;/em&gt;, as a compiler instance to build its modules. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This means that "compiler" is now doomed to be both an option and a
compiler attribute. It's not easy to fix and will require a deprecation
step. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So the idea of the smoke test is to make sure Distutils still knows how
to produce releases of existing projects. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;The Smoke testing buildbot&lt;/h3&gt;
&lt;p&gt;I have built a buildbot instance using &lt;a href="http://pypi.python.org/pypi/collective.buildbot"&gt;collective.buildbot&lt;/a&gt;. The
script the slaves run is quite simple: it gets some projects source and
run their setup.py script, using various versions of the Python
interpreter, then the trunk itself. The commands run so far are
&lt;strong&gt;sdist&lt;/strong&gt; and &lt;strong&gt;bdist&lt;/strong&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Then it compares every file contained in the archive created to make
sure produced archives are similar. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I will encouter some exceptions, when the package I am testing includes
different files in its distribution depending on the Python version, but
those exception will be managed. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Last, I cannot run this test on every package out there, for security
reasons. Until I have the proper virtual environment to do that, I'll
work on a white list of packages I "trust". So far, there's just a
package of mine, and NumPy. But this list will grow. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you are curious to know if your package builds under Python trunk,
get in touch with me, I'll probably add it if I know I can trust it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The last problem I have with this system yet is the fact that
setuptools is not working anymore with the Distutils trunk, so I am
unable to test setuptools-based packages. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The buildbot is located here : &lt;a href="http://buildbot.ziade.org/waterfall"&gt;http://buildbot.ziade.org/waterfall&lt;/a&gt;&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 28 May 2009 15:38:00 +0200</pubDate><guid>http://blog.ziade.org/2009/05/28/smoke-testing-in-distutils/</guid></item><item><title>Monitoring a Zope 2 application</title><link>http://blog.ziade.org/2009/05/19/monitoring-a-zope-2-application/</link><description>&lt;p&gt;We have a simple need for a customer project that runs a Zeo server and
a few Zeo clients : being able to check the status of every Zeo client,
and monitor what they are doing. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href="http://plone.org/products/deadlockdebugger"&gt;DeadlockDebugger&lt;/a&gt; almost provides this feature since it is able to
produce a dump of the execution stack for every thread a Zope instance
is running. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Based on this tool, I have developed &lt;a href="http://pypi.python.org/pypi/ZopeHealthWatcher/"&gt;ZopeHealthWatcher&lt;/a&gt;, that
provides a console script to query a Zope instance, and get back a
status for every running thread. It tells you if the thread is idling or
if it's running some code. The script also returns an exit code
depending on the number of busy threads, so it can be used in tools like
Nagios. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;When there are 4 or more busy threads, the script will return the
execution stacks for every busy thread and some extra info like the
system load and memory info. The returned info will be extendable
through plug-ins in the next version, but right now the provided info
are enough for our needs. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I have also created an HTML version, so when the dump is requested from
another tool than the console script (e.g. a browser), it displays a
nice human-readable interface (check the PyPI page for more info and a
screenshot). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Notice that DeadLockDebugger is hackish since it patches the Zope
publisher at startup. But we won't change this part: we need this tool
to run from the oldest to the newest Zope 2 version. And the patch just
works fine, so... &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The provided version should run out of the box in a buildout-based
Plone 3 application, but requires manual installation steps on older
Plone or CPS versions. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I didn't mention these manual steps in the documentation. I think I am
the only person in the world interested in running this tool on the
dead-but-still-in-production-in-many-places Nuxeo CPS. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;By the way: kudos goes to Marc-Aurèle Darche, who is maintaining CPS
for years now, making it one of the most bug-free and stable CMS
solution out there. Ok it's probably easier to reach this level of
quality since the platform is very stable and only evolves very slowly
thanks to &lt;a href="http://www.racinet.fr/index.php?pages/Nuxeo-CPS"&gt;Georges Racinet&lt;/a&gt;.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 19 May 2009 09:29:00 +0200</pubDate><guid>http://blog.ziade.org/2009/05/19/monitoring-a-zope-2-application/</guid></item><item><title>Distutils state</title><link>http://blog.ziade.org/2009/05/10/distutils-state/</link><description>&lt;p&gt;Since Pycon, a lot of discussion and work has been going on.&lt;/p&gt;
&lt;p&gt;During the summit, we made a few decisions (see
&lt;a href="http://mail.python.org/pipermail/python-dev/2009-March/087834.html"&gt;http://mail.python.org/pipermail/python-dev/2009-March/087834.html&lt;/a&gt;)
but this topic is &lt;strong&gt;so wide and so complex&lt;/strong&gt; that a lot of discussion
still needs to be done to have a clear and complete picture of where we
want to go and how we are going to do it&lt;/p&gt;
&lt;p&gt;But we have made significant progress and reached consensus on some key
points. This entry is a short list of these key points.&lt;/p&gt;
&lt;h3&gt;Being able to compare project versions&lt;/h3&gt;
&lt;p&gt;If you take a look in Distutils code, there's a &lt;em&gt;version&lt;/em&gt; and a
&lt;em&gt;versionpredicate&lt;/em&gt; module that provides a way to compare versions. This
feature is barely used by Distutils itself, and a very few number of
projects out there are using it (If you do so, please let me know).&lt;/p&gt;
&lt;p&gt;This is probably because Distutils provides two different ways to
compare versions: a "strict" one and a "loose" one. In other words
Distutils clearly states that it is unable to provide a unique version
comparison algorithm that can be used by anyone out there. Anyone means
here : Python developers, OS packagers, etc.&lt;/p&gt;
&lt;p&gt;Setuptools did a better job by providing an algorithm that covers most
cases, but suffers from this universality: it's too heuristic.&lt;/p&gt;
&lt;p&gt;So one of the main topic during Pycon was to try to find a version
comparison algorithm that would just work for everyone and in the
meantime would be strict enough to be useful. That's a pretty tough
task, but I think we have reached something that is "good enough" for
everybody. We had the chance to have people from Ubuntu, RedHat during
Pycon to work on this task, and Trent Mick took the lead during the
sprints to come up with a prototype.&lt;/p&gt;
&lt;p&gt;It's described here :
&lt;a href="http://wiki.python.org/moin/Distutils/VersionComparison"&gt;http://wiki.python.org/moin/Distutils/VersionComparison&lt;/a&gt; and I have
put the code here &lt;a href="http://bitbucket.org/tarek/distutilsversion"&gt;http://bitbucket.org/tarek/distutilsversion&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Two more things to take care of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Philip Eby came up with an edge-case : being able to do a&lt;em&gt;
    development version of a post release&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Jean-Paul Calderone proposed to add a constructor that would take
    explicit arguments to describe the version, rather than a string
    representation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There's a branch (tarek-postdev) on bitbucket to work on these two
cases. But basically, it seems that we have a consensus on a unique way
to compare versions in Python ! This is a great step forward.&lt;/p&gt;
&lt;p&gt;Another point that&lt;em&gt; Toshio&lt;/em&gt; Kuratomi raised during a hall discussion was
the fact that some Python developers are not having good practices when
versioning their releases. So we agreed that a good &lt;em&gt;"How to properly
use version numbers for your projects releases"&lt;/em&gt; document will have to
be delivered in Distutils documentation, besides the new algorithm. I am
pretty confident that people will eventually follow it.&lt;/p&gt;
&lt;h3&gt;APIs to access installed project metadata, and an egg-info standard&lt;/h3&gt;
&lt;p&gt;Once a project is installed in Python, you don't have a way to get its
metadata and to answer to basic questions like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;what are the installed packages ?&lt;/li&gt;
&lt;li&gt;what is the version ?&lt;/li&gt;
&lt;li&gt;how is the author ?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Of course you can dig in your installation and get back most of these,
but depending how you installed your package (manual, easy_install,
pip) it might be located in different places.&lt;/p&gt;
&lt;p&gt;So we agreed on a standard for this, described in PEP 376 :
&lt;a href="http://www.python.org/dev/peps/pep-0376"&gt;http://www.python.org/dev/peps/pep-0376&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;An API will be introduced to be able to get the info for a installed
package, as long as it was installed using PEP 376 standard.&lt;/p&gt;
&lt;h3&gt;An uninstall feature !&lt;/h3&gt;
&lt;p&gt;A feature that is claimed for a long time now should be introduced in
Distutils : uninstallation !&lt;/p&gt;
&lt;p&gt;Distutils will provide a basic, reference &lt;em&gt;uninstall&lt;/em&gt; feature that will
remove all files that where previously installed for a project. This
will be doable as long as this list of files is recorded at installation
time, and not used by another project.&lt;/p&gt;
&lt;p&gt;See the details in PEP 376.&lt;/p&gt;
&lt;h3&gt;Standardize PKG-INFO&lt;/h3&gt;
&lt;p&gt;New fields will be added in the metadata of a project. The most
important one is&lt;strong&gt; install_requires&lt;/strong&gt; from setuptools, which let you
list the projects your project depend on and their versions.&lt;/p&gt;
&lt;p&gt;This is for informational purpose, and Distutils will not provide an
install feature that will fetch and install dependencies. This can be
done by third-party tools like easy_install as long as they use the
installation standard described earlier.&lt;/p&gt;
&lt;p&gt;We started this work with Jim Fulton at Pycon, and Tres Seaver took the
lead on this task since then. The plan is to update PEP 345 . The work
is done in a branch here :
&lt;a href=""&gt;http://svn.python.org/view/peps/branches/jim-update-345/pep-0345.txt?view=markup&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;But the remaining work is focusing on practical details. Just remember
that PKG-INFO will evolve and install_requires will be integrated
amongst other changes.&lt;/p&gt;
&lt;h3&gt;Distutils code cleanup, test coverage&lt;/h3&gt;
&lt;p&gt;The test coverage is starting to look good and most modules are covered
around 80%. I guess this is the average in most Test-Driven Development
projects out there. So Distutils is becoming a good citizen of the
standard library ;)&lt;/p&gt;
&lt;p&gt;The remaining work for a good test coverage is mostly on compilers side
and os specific commands.&lt;/p&gt;
&lt;p&gt;My black list of untested (or nearly untested) modules :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;bcppcompiler&lt;/li&gt;
&lt;li&gt;cygwinccompiler&lt;/li&gt;
&lt;li&gt;emxccompiler&lt;/li&gt;
&lt;li&gt;msvc9compiler&lt;/li&gt;
&lt;li&gt;msvccompiler&lt;/li&gt;
&lt;li&gt;command.bdist_msi&lt;/li&gt;
&lt;li&gt;command.bdist_rpm&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I need to figure out how to properly test them. Last, I need to set up a
buildbot that will try out Distutils trunk on a list of projects out
there, like numpy for example.&lt;/p&gt;
&lt;h3&gt;Other topics&lt;/h3&gt;
&lt;p&gt;Check this ! &lt;a href="http://wiki.python.org/moin/Distutils"&gt;http://wiki.python.org/moin/Distutils&lt;/a&gt;&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//s&lt;/span&gt;&lt;span class="n"&gt;vn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/view/&lt;/span&gt;&lt;span class="n"&gt;peps&lt;/span&gt;&lt;span class="sr"&gt;/branches/&lt;/span&gt;&lt;span class="n"&gt;jim&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;345&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;pep&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;0345&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;txt&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;markup&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sun, 10 May 2009 15:30:00 +0200</pubDate><guid>http://blog.ziade.org/2009/05/10/distutils-state/</guid></item><item><title>Gsoc : Keyring library work started !</title><link>http://blog.ziade.org/2009/05/03/gsoc-keyring-library-work-started/</link><description>&lt;p&gt;I am very proud to be a &lt;a href="http://socghop.appspot.com/org/home/google/gsoc2009/python"&gt;Gsoc&lt;/a&gt; mentor this year on a very interesting
topic : an universal keyring library for Python. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;About the topic&lt;/h3&gt;
&lt;p&gt;In Distutils, if you want to interact with PyPI, you have to register
in the website and you get a login/password so you can register and/or
upload your packages. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Before Python 2.6, the only way you could interact with PyPI was by
storing these info into your .pypirc file in &lt;strong&gt;clear text&lt;/strong&gt;. This was
not the best solution. For example we have in my company some staging
servers we share, and from whom we upload packages to various PyPI-like
servers. So we have to store PyPI login/password info in them. This
means that if Bob wants to push his package from that server, he has to
put his password into a clear text file which is most of the time
readable by everyone. It's not such a big deal in our company since we
can trust each other, but it's a very bad practice. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So I ended up changing this in Python 2.6 so people could type their
password in a prompt when working with packages, using
&lt;strong&gt;getpass.getpass&lt;/strong&gt;. So they wouldn't have to store them anymore. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But this is not enough : we need to provide something better. We need
&lt;strong&gt;getpass.getpass&lt;/strong&gt; to be able to interact with keyring libraries like
&lt;a href="http://en.wikipedia.org/wiki/Keychain_(Mac_OS)"&gt;KeyChain&lt;/a&gt;, &lt;a href="http://en.wikipedia.org/wiki/GNOME_Keyring"&gt;Gnome Keyring&lt;/a&gt;, etc. So the login/password info are
safely stored and can be reused. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This service will be useful for Distutils, but also for any Python
application. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The idea of the GSOC task is to provide an unified keyring library for
Python, and it's harder that it sounds. For instance, we need to find a
way to provide something that works under Windows. So the whole work is
quite challenging and interesting, and the goal is to end up with a
keyring library we can use into Distutils and propose for inclusion in
getpass. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;About Kang&lt;/h3&gt;
&lt;p&gt;Kang Zhang is the student that was selected for this work. Congrats !
He has started to work on it. You can follow his work in his &lt;a href="http://kangzhang-en.blogspot.com/"&gt;blog&lt;/a&gt;. I
have a strong feeling that he will succeed in this work and come up with
something good. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Take a look at the &lt;a href="http://soc.python.org/"&gt;Python Soc planet&lt;/a&gt; too, where all students
involved in Python GSOC are blogging about their ongoing work.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sun, 03 May 2009 10:49:00 +0200</pubDate><guid>http://blog.ziade.org/2009/05/03/gsoc-keyring-library-work-started/</guid></item><item><title>Basic plugin system using ABCs and the &amp;quot;extensions&amp;quot; package</title><link>http://blog.ziade.org/2009/05/01/basic-plugin-system-using-abcs-and-the-quotextensionsquot-package/</link><description>&lt;p&gt;I need a very simple plugin system for one of my projects. The project
is a small WSGI application called &lt;strong&gt;mysysadmin&lt;/strong&gt; that allows you to
launch some commands on your system to manage some applications. It also
allows you to view log files in your web browser. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It's similar in some ways to &lt;a href="http://www.webmin.com/"&gt;WebMin&lt;/a&gt;, &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So in my application, every tab is a plugin that manages one
application. I have a plugin for Apache, another one for MySQL, and so
one. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Back to my plugin system. So every plugin that is registered becomes a
tab in the WSGI application, as long as it provides all the methods my
web application needs to interact with it. So I want to check that each
plugin &lt;strong&gt;strictly&lt;/strong&gt; provides the API needed by the main program. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The first tools that came in mind were : &lt;br /&gt;
-   &lt;a href="http://pypi.python.org/pypi/zope.interface"&gt;zope.interface&lt;/a&gt;, to be able to provide that each plugin meets the
    requirements.
-   &lt;a href="http://peak.telecommunity.com/DevCenter/setuptools#extensible-applications-and-frameworks"&gt;setuptools entry points&lt;/a&gt;, so it's easy for a third party code to
    implement a plugin.&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;But I find both projects a bit complex to implement such a simple&lt;/dt&gt;
&lt;dt&gt;plugin system.I could use the standalone Plugins package Phillip&lt;/dt&gt;
&lt;dt&gt;provides instead of setuptools, but it still does too much things imho.&lt;/dt&gt;
&lt;dt&gt;That's someting I am currently learning by working on packaging matters&lt;/dt&gt;
&lt;dd&gt;one library should not provide too many features. &lt;br /&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;h3&gt;Extensions : a simple plugin system&lt;/h3&gt;
&lt;p&gt;So I have started to implement a light-weight plugin system called
&lt;a href="http://pypi.python.org/pypi/extensions"&gt;extensions&lt;/a&gt;, which reuses setuptools entry points principles but is
more simple to use. The goal of this project is to provide very simple
APIs to handle plugins, and to make it work without introducing a new
argument into the setup.py &lt;em&gt;setup&lt;/em&gt; method, like setuptools does. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For instance, if you want to define an &lt;strong&gt;apache&lt;/strong&gt; function in your
&lt;strong&gt;modules&lt;/strong&gt; module, in your &lt;strong&gt;myapp&lt;/strong&gt; package, you just call the
&lt;strong&gt;register&lt;/strong&gt; function : &lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;extensions&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;register&lt;/span&gt;

&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;mysysadmin.modules&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;apache&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;myapp.modules:apache&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;That's it ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;And to use it, the &lt;strong&gt;mysysadmin&lt;/strong&gt; application can use a simple API
called &lt;strong&gt;get&lt;/strong&gt;, that iterates over all plugins defined for
&lt;em&gt;"mysysadmin.modules"&lt;/em&gt; : &lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;extensions&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;plugin&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;mysysadmin.modules&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="c1"&gt;# do something with the plugin&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The magic is done by writing in the &lt;em&gt;.egg-info&lt;/em&gt; directory installed for
the package that contains each plugin, a &lt;em&gt;PLUGIN&lt;/em&gt; file that contains the
list of registered elements. It's an idea borrowed from setuptoools
entry points. So &lt;em&gt;get&lt;/em&gt; iterates over all &lt;em&gt;.egg-info&lt;/em&gt; directories in your
path and load the &lt;em&gt;PLUGIN&lt;/em&gt; files it finds. Nothing new here. That's how
setuptools does, and that's perfect. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you have any feedback on &lt;a href="http://pypi.python.org/pypi/extensions"&gt;extensions&lt;/a&gt;, let me know ! &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Strict plugins&lt;/h3&gt;
&lt;p&gt;The other need is to strictly check that every plugin provides the API
needed, e.g. fulfill the requirements. This is what we could call
&lt;a href="http://en.wikipedia.org/wiki/Design_by_contract"&gt;Design by contract&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;You can provide a base class for this, and ask the plugins to inherit
from it. Or you can ask the Plugin to provide a marker to specify it
implements a given behavior. &lt;em&gt;zope.interface&lt;/em&gt; can do a nice job for the
latter,and let you check that a given object implements an interface. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But I wanted to give a shot to the brand new Python ABCs and make sure
anyone can write a plugin in plain Python, without having to rely on any
kind of marker system. ABCs will let you check that a class implements
some methods without requiring it to inherit from a specific class, to
implement a specific interface or provide a custom marker. Pure duck
typing ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So let's define for our application a &lt;strong&gt;Plugin&lt;/strong&gt; class, that gives the
signature every plugin will need to provide. It uses &lt;strong&gt;ABCMeta&lt;/strong&gt; as its
meta class, and the &lt;strong&gt;abstractmethod&lt;/strong&gt; for every method that should be
implemented by every plugin. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here's an extract : &lt;br /&gt;
   from abc import ABCMeta, abstractmethod&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;class&lt;/span&gt; &lt;span class="n"&gt;Plugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

   &lt;span class="n"&gt;__metaclass__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ABCMeta&lt;/span&gt;

   &lt;span class="nv"&gt;@abstractmethod&lt;/span&gt;

    &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;get_command_list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt;

    &lt;span class="nv"&gt;@abstractmethod&lt;/span&gt;

    &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;run_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="n"&gt;pass&lt;/span&gt;

    &lt;span class="nv"&gt;@classmethod&lt;/span&gt;

    &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;__subclasshook__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cls&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;Plugin:&lt;/span&gt;

            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__abstractmethods__:&lt;/span&gt;

                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__dict__&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__mro__&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

                    &lt;span class="k"&gt;continue&lt;/span&gt;

                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;NotImplemented&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;True&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;NotImplemented&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The &lt;strong&gt;__subclasshook__&lt;/strong&gt; method is a class method that will be
called everytime a class is tested using &lt;strong&gt;issubclass(klass, Plugin)&lt;/strong&gt;.
In that case, it will check that every method marked with the
&lt;strong&gt;abstractmethod&lt;/strong&gt; decorator is provided by the class. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So basically, the application can discover and use the plugins, with: &lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;extensions&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;plugin&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;mysysadmin.modules&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;klass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;issubclass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Plugin&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;sorry, not a suitable plugin&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;continue&lt;/span&gt;

    &lt;span class="c1"&gt;# do something with the plugin&lt;/span&gt;

    &lt;span class="n"&gt;xxx&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Abstract Base Classes are one honking great idea -- let's do more of
those!&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 01 May 2009 20:59:00 +0200</pubDate><guid>http://blog.ziade.org/2009/05/01/basic-plugin-system-using-abcs-and-the-quotextensionsquot-package/</guid></item><item><title>URLs in books</title><link>http://blog.ziade.org/2009/04/19/urls-in-books/</link><description>&lt;p&gt;I received some complaints about the fact that some links in my books
were dead by the time they were printed. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For the next book I am working on, I have proposed to my editor to set
up a website to keep track of all references mentioned. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;By using unique short ascii references throughout the book, it's easy
to provide a simple redirect service to the target URL, and to fix it
when it changes (just by setting up a mail alert if your redirect
reaches a 404). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For example, if I am referring to &lt;strong&gt;mod_wsgi&lt;/strong&gt; in my book, I can write
this reference: &lt;strong&gt;&lt;em&gt;#mod_wsgi&lt;/em&gt;&lt;/strong&gt;, and provide a redirection to
&lt;strong&gt;&lt;em&gt;http://code.google.com/p/modwsgi&lt;/em&gt;&lt;/strong&gt; into my website, through a
unique, mnemotechnic permanent URL :
&lt;strong&gt;&lt;em&gt;http://ziade.org/urls/mod_wsgi&lt;/em&gt;&lt;/strong&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This small service, à la Tiny URL is not a burden for the reader imho :
he is using his computer anyway when he visits an URL mentioned in a
book. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It's a simple idea I am sure a lot of people have thaught about before,
but I fail to see it applied in the books I am buying these days. Is
there any good reason I fail to see ?&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sun, 19 Apr 2009 14:42:00 +0200</pubDate><guid>http://blog.ziade.org/2009/04/19/urls-in-books/</guid></item><item><title>new blog location, new design, update your bookmarks</title><link>http://blog.ziade.org/2009/04/15/new-blog-location-new-design-update-your-bookmarks/</link><description>&lt;p&gt;I was thinking about doing this change for a while, and I took the time
to do it last week-end: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;My personal website at &lt;a href="http://ziade.org"&gt;http://ziade.org&lt;/a&gt; is now powered by
&lt;a href="http://www.pylonshq.com/"&gt;Pylons&lt;/a&gt; and &lt;a href="http://atomisator.ziade.org/"&gt;Atomisator&lt;/a&gt;. It's both in French and English (the urls
are translated too). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It contains: &lt;br /&gt;
-   a home page with my latest twitter entries
-   a blog (this one)
-   some other pages like a résumé, a list of the books I wrote, a
    projects page, etc.&lt;/p&gt;
&lt;p&gt;I am still using Wordpress to write my blog entries but they are now
processed with Atomisator and displayed at &lt;a href="http://ziade.org/blog"&gt;http://ziade.org/blog&lt;/a&gt;.
The feed is now located at &lt;a href="http://ziade.org/blog/xml"&gt;http://ziade.org/blog/xml&lt;/a&gt; and I will make
sure all Planets use this URL for now on. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Next I'll probably : &lt;br /&gt;
-   move to another blogging tool, and my readers will not suffer from
    the change as long as they use the ziade.org feed url.
-   add some portlets into the new site, like the ones I have on
    Wordpress.&lt;/p&gt;
&lt;p&gt;How do you like this new look ? Any comment / advice ? :)&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Wed, 15 Apr 2009 15:51:00 +0200</pubDate><guid>http://blog.ziade.org/2009/04/15/new-blog-location-new-design-update-your-bookmarks/</guid></item><item><title>Distutils: introducing the check command (reStructuredText control)</title><link>http://blog.ziade.org/2009/04/10/distutils-introducing-the-check-command-restructuredtext-control/</link><description>&lt;p&gt;I am introducing the &lt;strong&gt;check&lt;/strong&gt; command in Distutils. This command will
check your package metadata, like the &lt;strong&gt;sdist&lt;/strong&gt; and the &lt;strong&gt;register&lt;/strong&gt;
command already do (they display warnings). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But the new thing is that it will also allow you to check if
&lt;em&gt;long_description&lt;/em&gt; is reStructuredText compliant. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Its usage will be: &lt;br /&gt;
   $ python setup.py check --restructuredtext&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;running&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;

&lt;span class="n"&gt;warning:&lt;/span&gt; &lt;span class="n"&gt;check:&lt;/span&gt; &lt;span class="n"&gt;Title&lt;/span&gt; &lt;span class="n"&gt;underline&lt;/span&gt; &lt;span class="n"&gt;too&lt;/span&gt; &lt;span class="n"&gt;short&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;warning:&lt;/span&gt; &lt;span class="n"&gt;check:&lt;/span&gt; &lt;span class="n"&gt;Title&lt;/span&gt; &lt;span class="n"&gt;underline&lt;/span&gt; &lt;span class="n"&gt;too&lt;/span&gt; &lt;span class="n"&gt;short&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;warning:&lt;/span&gt; &lt;span class="n"&gt;check:&lt;/span&gt; &lt;span class="n"&gt;Could&lt;/span&gt; &lt;span class="n"&gt;finish&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;parsing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="n"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;And there's also a &lt;em&gt;strict&lt;/em&gt; mode, that raises an error in case
something is wrong &lt;br /&gt;
   $ python setup.py check --restructuredtext --strict&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;running&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;

&lt;span class="n"&gt;warning:&lt;/span&gt; &lt;span class="n"&gt;check:&lt;/span&gt; &lt;span class="n"&gt;Title&lt;/span&gt; &lt;span class="n"&gt;underline&lt;/span&gt; &lt;span class="n"&gt;too&lt;/span&gt; &lt;span class="n"&gt;short&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;warning:&lt;/span&gt; &lt;span class="n"&gt;check:&lt;/span&gt; &lt;span class="n"&gt;Title&lt;/span&gt; &lt;span class="n"&gt;underline&lt;/span&gt; &lt;span class="n"&gt;too&lt;/span&gt; &lt;span class="n"&gt;short&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;warning:&lt;/span&gt; &lt;span class="n"&gt;check:&lt;/span&gt; &lt;span class="n"&gt;Could&lt;/span&gt; &lt;span class="n"&gt;finish&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;parsing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="n"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;error:&lt;/span&gt; &lt;span class="n"&gt;Please&lt;/span&gt; &lt;span class="n"&gt;correct&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="nb"&gt;package&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Last, this command will be used by &lt;strong&gt;register&lt;/strong&gt;, and &lt;strong&gt;sdist&lt;/strong&gt;, so you
can stop the process in case the metadata are not correct. This is
useful to make sure your PyPI home page is not broken for example, since
it parses &lt;em&gt;long_description&lt;/em&gt; to build it. So a good practice will be to
use &lt;em&gt;strict&lt;/em&gt; when registering a package: &lt;br /&gt;
   $ python setup.py register --strict&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;running&lt;/span&gt; &lt;span class="n"&gt;register&lt;/span&gt;

&lt;span class="n"&gt;running&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;

&lt;span class="n"&gt;warning:&lt;/span&gt; &lt;span class="n"&gt;check:&lt;/span&gt; &lt;span class="n"&gt;Title&lt;/span&gt; &lt;span class="n"&gt;underline&lt;/span&gt; &lt;span class="n"&gt;too&lt;/span&gt; &lt;span class="n"&gt;short&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;warning:&lt;/span&gt; &lt;span class="n"&gt;check:&lt;/span&gt; &lt;span class="n"&gt;Title&lt;/span&gt; &lt;span class="n"&gt;underline&lt;/span&gt; &lt;span class="n"&gt;too&lt;/span&gt; &lt;span class="n"&gt;short&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;warning:&lt;/span&gt; &lt;span class="n"&gt;check:&lt;/span&gt; &lt;span class="n"&gt;Could&lt;/span&gt; &lt;span class="n"&gt;finish&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;parsing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="n"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;error:&lt;/span&gt; &lt;span class="n"&gt;Please&lt;/span&gt; &lt;span class="n"&gt;correct&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="nb"&gt;package&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This feature will land in Python 2.7 (I am working on it and it should
be commited this week end). Of course, it will not introduce a hard
dependency on docutils in Python, neither it will change the current
default behavior. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Until then, you can use &lt;a href="http://pypi.python.org/pypi/collective.dist"&gt;collective.dist 0.2.3&lt;/a&gt;, that provides this
feature for Python 2.4 to 2.6&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 10 Apr 2009 14:11:00 +0200</pubDate><guid>http://blog.ziade.org/2009/04/10/distutils-introducing-the-check-command-restructuredtext-control/</guid></item><item><title>Pycon hallway session #2: thoughts for multiple versions in Python</title><link>http://blog.ziade.org/2009/03/30/pycon-hallway-session-2-thoughts-for-multiple-versions-in-python/</link><description>&lt;p&gt;We had an excellent brainstorming session today in the hall, with
Toshio, Georg, Martin, Thomas, etc.. (sorry we were so many I don't have
the full list) with some insights from Guido and Brett. We tried to
think about a way to handle multiple versions of a same package. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here's the two most important concepts : &lt;br /&gt;
-   &lt;strong&gt;Unicity: &lt;/strong&gt;There should be one and only one instance of a Python
    package at a given version on a system
-   &lt;strong&gt;Combination:&lt;/strong&gt; One Python application combines several packages to
    run&lt;/p&gt;
&lt;h2&gt;About unicity&lt;/h2&gt;
&lt;p&gt;A Python package is a component that can be installed on a system. If
you use the standard Distutils approach, it will end up in the Python
site-packages directory and be importable by the interpreter. This
package comes with a version number and is unique. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This unicity is important for security and maintainability. For
instance, if there's a security hole in a package, the fix is applied in
one place and the system maintainer knows it can't be present elsewhere
on the system. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This is the system administrator point of view&lt;/em&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;h2&gt;About Combination&lt;/h2&gt;
&lt;p&gt;What defines a Python application is the fact that is selects a list of
packages it needs to run. And this varies for every application. So two
Python applications might need a different version of a given package
and that is normal. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;What's important is to have the right list of packages when an
application loads. Tools like zc.buildout or virtualenv are perfect for
this need : they create an isolated environment for your application to
run with the right set of packages. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So the simplest way to release an application is to ship it with
everything required, regardless the unicity. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This is the application developer point of view&lt;/em&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;h2&gt;The idea&lt;/h2&gt;
&lt;p&gt;zc.buildout and virtualenv are a blast for developers, and another
thing system packagers might dislike. This is because they break the
unicity by allowing developers to ship their applications as black
boxes. Of course one may say that this is perfectly fine since what's
inside an application is not the problem of the system packager. But
since this application is made of packages that may be shared on the
system by other applications, that is redondant. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Forget &lt;em&gt;site-packages&lt;/em&gt; for a moment. And let's think about a new
loading system for packages. This approach is similar in some ways to
setuptools' multiple version system. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Storing multiple versions&lt;/h3&gt;
&lt;p&gt;First of all, let's store the packages in a directory, and for each
version of the package, under a sub-directory which name is the version.&lt;/p&gt;
&lt;p&gt;For example: &lt;br /&gt;
-   SQLAlchemy &lt;br /&gt;
   -   0.4 &lt;br /&gt;
       -   package code is here
        -   package egg-info here&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;0.5 &lt;br /&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;package code is here&lt;ul&gt;
&lt;li&gt;package egg-info here&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;jsonlib &lt;br /&gt;
&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;1.2.6 &lt;br /&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;package code is here&lt;ul&gt;
&lt;li&gt;package egg-info here&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;1.3.10 &lt;br /&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;package code is here&lt;ul&gt;
&lt;li&gt;package egg-info here&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Given this structure, some mechanism can provide to the interpreter the
latest available version of a package, as long as Distutils knows how to
handle version comparisons correctly (&lt;a href="http://wiki.python.org/moin/DistutilsVersionFight"&gt;which will be the case in a near
future&lt;/a&gt;) &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Choosing specific versions&lt;/h3&gt;
&lt;p&gt;Back to our application. Let's call it MyApp, version 1.0. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It needs specific versions for some packages. Forget &lt;em&gt;zc.buildout&lt;/em&gt; and
&lt;em&gt;pip&lt;/em&gt; for a moment. And let's think about a different way to express the
packages (and versions) its needs. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Let's make a Python package for this application and let's make a few
assumptions on some features in Distutils: &lt;br /&gt;
-   setuptools' install_requires has made it into Distutils, as part of
    metadata.
-   metadata are defined &lt;strong&gt;statically&lt;/strong&gt; in a package, apart from
    setup.py.&lt;/p&gt;
&lt;p&gt;So basically, the application is mainly a static list of dependencies
defined into &lt;em&gt;install_requires&lt;/em&gt;. For example: &lt;br /&gt;
-   SQLAlchemy &gt; 0.4
-   jsonlib == 1.2.6&lt;/p&gt;
&lt;p&gt;When MyApp will get installed by Distutils, it will be added in the
packages tree. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;When it is used, it will need to load the versions of SQLAlchemy and
jsonlib it needs. The ones that are inside its metadata. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;To make it possible, the script that launches the application calls a
built-in function called read_deps, that takes the metadata and reads
them to know which versions fit: &lt;br /&gt;
   # the script&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;read_deps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;PKG-INFO&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SQLAlchemy&lt;/span&gt;

&lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;jsonlib&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This call will load the right versions in the packages tree: &lt;br /&gt;
-   SQLAlchemy &lt;br /&gt;
   -   0.4
    -   0.5&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;jsonlib &lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;1.2.6&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1.3.10&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;MyApp &lt;br /&gt;
&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;1.0&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;What's next&lt;/h3&gt;
&lt;p&gt;I just dropped here the rough idea, and a lot of details are missing.
But I think it's a good thing to share this idea here in its early
stage. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;We have decided we would try to write a prototype using &lt;em&gt;importlib&lt;/em&gt; and
&lt;em&gt;sys.meta_path&lt;/em&gt;. Maybe Georg will start it during the Pycon sprint, and
start to digg into the details. He was working on this when I left the
open session at Pycon. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Stay tuned&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Mon, 30 Mar 2009 07:16:00 +0200</pubDate><guid>http://blog.ziade.org/2009/03/30/pycon-hallway-session-2-thoughts-for-multiple-versions-in-python/</guid></item><item><title>Pycon hallway session #1: a keyring library for Python</title><link>http://blog.ziade.org/2009/03/27/pycon-hallway-session-1-a-keyring-library-for-python/</link><description>&lt;p&gt;Before I sit down and clean up my summit notes to send them to
python-dev, I wanted to post an entry about a small project which I
think could be a great task for a student at the Summer of Code (I doubt
it can fill 4 months of work but it could be done amongst other tasks).&lt;/p&gt;
&lt;p&gt;Yesterday, we did a late session with Martin von Loewis, Jim Fulton and
Georg Brandl about PyPI and the fact that it needed a better way to
handle passwords on client side. That is, the fact that you have to
store your password in the .pypirc file if you want to upload your
package to PyPI. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This is unsafe and unwanted. A few months ago, I have made a small
change in Distutils so it would prompt for the password using the
getpass module if it doesn't find it in the .pypirc file. (This was a
contrbution of Nathan Van Gheem). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Anyways, this is not enough. Jim suggested to set up a SSH server on
PyPI using Paramiko, so we could use a standard ssh connection and
benefit from ssh-agent. But this is unfortunately not universal. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So let me get back to the idea I sent some time ago :
&lt;a href=""&gt;http://mail.python.org/pipermail/python-ideas/2009-January/002465.html&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;What&lt;/span&gt; &lt;span class="n"&gt;about&lt;/span&gt; &lt;span class="n"&gt;having&lt;/span&gt; &lt;span class="n"&gt;an&lt;/span&gt; &lt;span class="n"&gt;option&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;getpass&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;reuse&lt;/span&gt; &lt;span class="n"&gt;passwords&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt;

&lt;span class="nb"&gt;system&lt;/span&gt; &lt;span class="n"&gt;keyrings&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt;

    &lt;span class="n"&gt;getpass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;[,&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;would&lt;/span&gt; &lt;span class="n"&gt;become:&lt;/span&gt;

    &lt;span class="n"&gt;getpass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;[,&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keyring&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;where&lt;/span&gt; &lt;span class="n"&gt;keyring&lt;/span&gt; &lt;span class="n"&gt;would&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;callable&lt;/span&gt; &lt;span class="n"&gt;that&lt;/span&gt; &lt;span class="n"&gt;can&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt; &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;retrieve&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt;

&lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;keyring&lt;/span&gt; &lt;span class="nb"&gt;system&lt;/span&gt;

&lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;getpass&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt; &lt;span class="n"&gt;could&lt;/span&gt; &lt;span class="n"&gt;provide&lt;/span&gt; &lt;span class="n"&gt;some&lt;/span&gt; &lt;span class="n"&gt;keyring&lt;/span&gt; &lt;span class="n"&gt;support&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="n"&gt;under&lt;/span&gt; &lt;span class="n"&gt;Linux&lt;/span&gt;

&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;keychain&lt;/span&gt; &lt;span class="n"&gt;under&lt;/span&gt; &lt;span class="n"&gt;Mac&lt;/span&gt; &lt;span class="n"&gt;OS&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt;

&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="n"&gt;ss&lt;/span&gt;

&lt;span class="n"&gt;And&lt;/span&gt; &lt;span class="n"&gt;let&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;developers&lt;/span&gt; &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="n"&gt;their&lt;/span&gt; &lt;span class="n"&gt;own&lt;/span&gt; &lt;span class="n"&gt;keyring&lt;/span&gt; &lt;span class="nb"&gt;system&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt; &lt;span class="n"&gt;providing&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;callable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;/blockquote&gt;
&lt;p&gt;As Greg Smith said in the thread, the first task is to create a library
that supports all standard keyring systems out there, including things
like KWallet, Internet Explorer, Fireforx and so on... &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I'll mentor this project if any student would like to do it.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//m&lt;/span&gt;&lt;span class="n"&gt;ail&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/pipermail/&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ideas&lt;/span&gt;&lt;span class="sr"&gt;/2009-January/&lt;/span&gt;&lt;span class="mo"&gt;002465&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 27 Mar 2009 14:55:00 +0100</pubDate><guid>http://blog.ziade.org/2009/03/27/pycon-hallway-session-1-a-keyring-library-for-python/</guid></item><item><title>Packaging Survey first results</title><link>http://blog.ziade.org/2009/03/26/packaging-survey-first-results/</link><description>&lt;p&gt;Around 570 people answered the survey, which is a great number I didn't
expect. Thanks again to Massimo for his help on this. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I have a lot of work to read all the open question answers, and all the
comments that goes with the "other" answer, but I wanted to publish the
results of the closed questions before the summit. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I don't want to comment the results yet. I will after I have studied
all answers, so it'll be a little while ;) &lt;br /&gt;
&lt;/p&gt;
&lt;h2&gt;Who are you ?&lt;/h2&gt;
&lt;p&gt;Professional developer using Python exclusively.&lt;/p&gt;
&lt;p&gt;283&lt;/p&gt;
&lt;p&gt;Professional developer using Python unable to use Python "at work".&lt;/p&gt;
&lt;p&gt;34&lt;/p&gt;
&lt;p&gt;Professional developer using Python sometimes.&lt;/p&gt;
&lt;p&gt;196&lt;/p&gt;
&lt;p&gt;Hobbyist using Python.&lt;/p&gt;
&lt;p&gt;116&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Where are you located ?&lt;/h2&gt;
&lt;p&gt;USA&lt;/p&gt;
&lt;p&gt;212&lt;/p&gt;
&lt;p&gt;Western Europe&lt;/p&gt;
&lt;p&gt;268&lt;/p&gt;
&lt;p&gt;Eastern Europe&lt;/p&gt;
&lt;p&gt;42&lt;/p&gt;
&lt;p&gt;Asia&lt;/p&gt;
&lt;p&gt;18&lt;/p&gt;
&lt;p&gt;Africa&lt;/p&gt;
&lt;p&gt;9&lt;/p&gt;
&lt;p&gt;Other&lt;/p&gt;
&lt;p&gt;70&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;If you are a web programmer, what is the framework you use the most ?&lt;/h2&gt;
&lt;p&gt;Pylons&lt;/p&gt;
&lt;p&gt;55&lt;/p&gt;
&lt;p&gt;TG 2&lt;/p&gt;
&lt;p&gt;14&lt;/p&gt;
&lt;p&gt;TG 1&lt;/p&gt;
&lt;p&gt;15&lt;/p&gt;
&lt;p&gt;Django&lt;/p&gt;
&lt;p&gt;184&lt;/p&gt;
&lt;p&gt;Zope (including Plone)&lt;/p&gt;
&lt;p&gt;137&lt;/p&gt;
&lt;p&gt;Other&lt;/p&gt;
&lt;p&gt;207&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;How do you organize your application code most of the time ?&lt;/h2&gt;
&lt;p&gt;I put everything in one package&lt;/p&gt;
&lt;p&gt;171&lt;/p&gt;
&lt;p&gt;I create several packages and use a tool like zc.buildout or Paver to
distribute the whole application&lt;/p&gt;
&lt;p&gt;137&lt;/p&gt;
&lt;p&gt;I create several packages and use a main package or script to launch the
application&lt;/p&gt;
&lt;p&gt;198&lt;/p&gt;
&lt;p&gt;I use my own mechanism for aggregating packages into a single install.&lt;/p&gt;
&lt;p&gt;67&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;For libraries you don't distribute publicly, do you you create a&lt;/h2&gt;
&lt;p&gt;setup.py script ?&lt;/p&gt;
&lt;p&gt;Yes&lt;/p&gt;
&lt;p&gt;321&lt;/p&gt;
&lt;p&gt;No&lt;/p&gt;
&lt;p&gt;249&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;What is the main tool or combination of tools you are using to&lt;/h2&gt;
&lt;p&gt;package and distribute your Python application ?&lt;/p&gt;
&lt;p&gt;None&lt;/p&gt;
&lt;p&gt;80&lt;/p&gt;
&lt;p&gt;setuptools&lt;/p&gt;
&lt;p&gt;150&lt;/p&gt;
&lt;p&gt;distutils&lt;/p&gt;
&lt;p&gt;127&lt;/p&gt;
&lt;p&gt;zc.buildout and distutils&lt;/p&gt;
&lt;p&gt;10&lt;/p&gt;
&lt;p&gt;zc.buildout and setuptools&lt;/p&gt;
&lt;p&gt;107&lt;/p&gt;
&lt;p&gt;Paver and setuptools&lt;/p&gt;
&lt;p&gt;9&lt;/p&gt;
&lt;p&gt;Paver and Distutils&lt;/p&gt;
&lt;p&gt;3&lt;/p&gt;
&lt;p&gt;Other&lt;/p&gt;
&lt;p&gt;64&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;How do you install a package that does not provide a standalone&lt;/h2&gt;
&lt;p&gt;installer (but provides a standard setup.py script) most of the time ?&lt;/p&gt;
&lt;p&gt;I use easy_install&lt;/p&gt;
&lt;p&gt;241&lt;/p&gt;
&lt;p&gt;I download it and manually run the python setup.py install command&lt;/p&gt;
&lt;p&gt;139&lt;/p&gt;
&lt;p&gt;I use pip&lt;/p&gt;
&lt;p&gt;34&lt;/p&gt;
&lt;p&gt;I move files around and create symlinks manually.&lt;/p&gt;
&lt;p&gt;7&lt;/p&gt;
&lt;p&gt;I use the packaging tool provided in my system (apt, yum, etc)&lt;/p&gt;
&lt;p&gt;81&lt;/p&gt;
&lt;p&gt;Other&lt;/p&gt;
&lt;p&gt;33&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;How do you remove a package ?&lt;/h2&gt;
&lt;p&gt;manually, by removing the directory and fixing the .pth files&lt;/p&gt;
&lt;p&gt;275&lt;/p&gt;
&lt;p&gt;I use one virtualenv per application, so the main python is never
polluted, and only remove entire environments.&lt;/p&gt;
&lt;p&gt;154&lt;/p&gt;
&lt;p&gt;using the packaging tool (apt, yum, etc)&lt;/p&gt;
&lt;p&gt;178&lt;/p&gt;
&lt;p&gt;I don't know / I fail at uninstallation&lt;/p&gt;
&lt;p&gt;79&lt;/p&gt;
&lt;p&gt;I change PYTHONPATH to include a directory of the packages used by my
application, then remove just that directory&lt;/p&gt;
&lt;p&gt;31&lt;/p&gt;
&lt;p&gt;Other&lt;/p&gt;
&lt;p&gt;10&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;How do you manage using more than one version of a library on a&lt;/h2&gt;
&lt;p&gt;system ?&lt;/p&gt;
&lt;p&gt;I don't use multiple versions of a library&lt;/p&gt;
&lt;p&gt;217&lt;/p&gt;
&lt;p&gt;I use virtualenv&lt;/p&gt;
&lt;p&gt;203&lt;/p&gt;
&lt;p&gt;I use Setuptools' multi-version features&lt;/p&gt;
&lt;p&gt;46&lt;/p&gt;
&lt;p&gt;I build fresh Python interpreter from source for each project&lt;/p&gt;
&lt;p&gt;16&lt;/p&gt;
&lt;p&gt;I use zc.buildout&lt;/p&gt;
&lt;p&gt;109&lt;/p&gt;
&lt;p&gt;I set sys.path in my scripts&lt;/p&gt;
&lt;p&gt;48&lt;/p&gt;
&lt;p&gt;I set PYTHONPATH to select particular libraries&lt;/p&gt;
&lt;p&gt;49&lt;/p&gt;
&lt;p&gt;Other&lt;/p&gt;
&lt;p&gt;23&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Do you work with setuptools' namespace packages ?&lt;/h2&gt;
&lt;p&gt;Yes&lt;/p&gt;
&lt;p&gt;178&lt;/p&gt;
&lt;p&gt;No&lt;/p&gt;
&lt;p&gt;344&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Has PyPI become mandatory in your everyday work (if you use&lt;/h2&gt;
&lt;p&gt;zc.buildout for example) ?&lt;/p&gt;
&lt;p&gt;Yes&lt;/p&gt;
&lt;p&gt;228&lt;/p&gt;
&lt;p&gt;No&lt;/p&gt;
&lt;p&gt;294&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;If you previously answered Yes, did you set up an alternative&lt;/h2&gt;
&lt;p&gt;solution (mirror, cache..) in case PyPI is down ?&lt;/p&gt;
&lt;p&gt;Yes&lt;/p&gt;
&lt;p&gt;77&lt;/p&gt;
&lt;p&gt;N/A&lt;/p&gt;
&lt;p&gt;277&lt;/p&gt;
&lt;p&gt;No&lt;/p&gt;
&lt;p&gt;166&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Do you register your packages on PyPI ?&lt;/h2&gt;
&lt;p&gt;Yes&lt;/p&gt;
&lt;p&gt;239&lt;/p&gt;
&lt;p&gt;No&lt;/p&gt;
&lt;p&gt;281&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Do you upload your package on PyPI ?&lt;/h2&gt;
&lt;p&gt;Yes&lt;/p&gt;
&lt;p&gt;205&lt;/p&gt;
&lt;p&gt;No&lt;/p&gt;
&lt;p&gt;314&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;If you previously answered No, how do you distribute your packages ?&lt;/h2&gt;
&lt;p&gt;One my own website, using simple links&lt;/p&gt;
&lt;p&gt;139&lt;/p&gt;
&lt;p&gt;One my own website, using a PyPI-like server&lt;/p&gt;
&lt;p&gt;50&lt;/p&gt;
&lt;p&gt;On a forge, like sourceforge&lt;/p&gt;
&lt;p&gt;N/A&lt;/p&gt;
&lt;p&gt;251&lt;/p&gt;
&lt;p&gt;Other&lt;/p&gt;
&lt;p&gt;56&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 26 Mar 2009 03:35:00 +0100</pubDate><guid>http://blog.ziade.org/2009/03/26/packaging-survey-first-results/</guid></item><item><title>Pycon Language Summit is tomorrow</title><link>http://blog.ziade.org/2009/03/26/pycon-language-summit-is-tomorrow/</link><description>&lt;p&gt;Tomorrow is the &lt;a href="http://us.pycon.org/2009/about/summits/language/"&gt;Language Summit&lt;/a&gt;, yeepee. :) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;package and distribution&lt;/em&gt; part of the Summit is going to be great
since we have key people coming up. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;zc.buildout and pip leaders (Jim Fulton and Ian Bicking) will be
present, and many others. I'll be representing Distutils, since I am its
current maintainer. Unfortunately Philip Eby (setuptools) can't make it,
but he should be reachable via IRC (I am trying to set something up for
tomorrow). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Anyway, one of the goal of the Summit is to validate the new features
and enhancements we want to introduce in Distutils and PyPI. It's
important to make sure they play well with third-party tools like
zc.buildout, setuptools and pip. We also need to make sure these tools
will evolve in the same direction in the future. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;We have reached a point in Python where we need to concentrate all the
packaging effort to build a common standard in the standard library,
because it is badly needed. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here's a draft of the slides I will present tomorrow, during the first
5/10 minutes, as a session leader: &lt;br /&gt;
-   Packaging Survey results overview
-   Topics to discuss &lt;br /&gt;
   -   setting up an organized network of mirrors (see &lt;a href="http://www.python.org/dev/peps/pep-0381"&gt;PEP 381)&lt;/a&gt;
    -   discuss about other PyPI enhancements
    -   improve the package installation / uninstallation (see [PEP
        376)][]
    -   discuss the package dependencies problem and see if we can come
        up with a PEP
    -   discuss the multiple version problem and see if we can come up
        with a PEP
    -   discuss the isolated environment vs the OS vendor approach and
        see what can be done to improve their coexistence.&lt;/p&gt;
&lt;p&gt;Summit schedule: &lt;br /&gt;
-   13:20 -&gt; 13:30 : presentation
-   13:30 -&gt; 14:30 : discussions/work in small groups
-   14:30 -&gt; 14:50 : "tour de table"
-   14:50 -&gt; 15:10 : break
-   15:10 -&gt; onwards: sprint !&lt;/p&gt;
&lt;p&gt;I am not sure about the 'work in small group' part yet, because I don't
know how many people will show up, and what people will want to focus
on.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 26 Mar 2009 02:38:00 +0100</pubDate><guid>http://blog.ziade.org/2009/03/26/pycon-language-summit-is-tomorrow/</guid></item><item><title>How to upload your package to Plone.org</title><link>http://blog.ziade.org/2009/03/15/how-to-upload-your-package-to-ploneorg/</link><description>&lt;p&gt;We have been working hard in the past few months with my Plone friends
to make it happen. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It's now available : &lt;a href="http://plone.org"&gt;Plone.org&lt;/a&gt; is acting like PyPI in its
&lt;a href="http://plone.org/products"&gt;Products&lt;/a&gt; section, which means that you can use &lt;a href="http://docs.python.org/distutils/"&gt;Distutils&lt;/a&gt; to
register and upload your packages in both places now. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I have written a small tutorial on plone.org to explain how to use it:
&lt;a href=""&gt;http://plone.org/documentation/tutorial/how-to-upload-your-package-to-plone.org&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I will also explain during my Pycon presentation
(&lt;a href="http://us.pycon.org/2009/conference/schedule/event/44/"&gt;http://us.pycon.org/2009/conference/schedule/event/44&lt;/a&gt;) how this new
feature at Plone.org will help people that are working with Plone and
Python packages.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;plone&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/documentation/&lt;/span&gt;&lt;span class="n"&gt;tutorial&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;how&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;upload&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;your&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;package&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;plone&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sun, 15 Mar 2009 13:35:00 +0100</pubDate><guid>http://blog.ziade.org/2009/03/15/how-to-upload-your-package-to-ploneorg/</guid></item><item><title>Take the Python Packaging Survey</title><link>http://blog.ziade.org/2009/03/09/take-the-python-packaging-survey/</link><description>&lt;p&gt;The &lt;a href="http://us.pycon.org/2009/about/summits/language/"&gt;Python Langage Summit&lt;/a&gt; is coming up. To prepare this event, I
have put online a survey you can take to tell us a bit more about you
and how you package your Python applications. &lt;br /&gt;
-   &lt;strong&gt;Who should take the survey&lt;/strong&gt; : any Python developer that packages
    and distributes his code, no matter how.
-   Take the survey: &lt;a href="http://tinyurl.com/packaging-survey"&gt;&lt;strong&gt;http://tinyurl.com/packaging-survey&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Thanks to all the people that helped building the survey, and a special
thanks to Massimo Di Pierro who created the application that runs the
Survey and helped me set up the survey. It runs under &lt;a href="http://mdp.cti.depaul.edu/"&gt;web2py&lt;/a&gt;.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Mon, 09 Mar 2009 05:41:00 +0100</pubDate><guid>http://blog.ziade.org/2009/03/09/take-the-python-packaging-survey/</guid></item><item><title>Raising Distutils test coverage : half-way</title><link>http://blog.ziade.org/2009/02/23/raising-distutils-test-coverage-half-way/</link><description>&lt;p&gt;After the next commit I will make in Distutils (that adds tests for
bdist_rpm), the test coverage of this Python standard library package
will be at 41%. This means that I have doubled the test coverage over
the past few months, from &lt;strong&gt;18% to 41%&lt;/strong&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;My goal is to double it again, and reach 80% in the next 6 months. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This also means I am just half an idiot now ! (since &lt;a href="http://ivory.idyll.org/blog/feb-09/people-who-dont-use-code-coverage-are-idiots"&gt;people who don't
have 100% code coverage are idiots&lt;/a&gt; ;)). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So does it make Distutils more robust ? &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It would have probably made the latest Python 3 release looks better
for this package, since we had a &lt;a href="http://mail.python.org/pipermail/python-dev/2009-February/086156.html"&gt;uncovered cmp() call left in
Distutils&lt;/a&gt; by the time the release was made. In the meantime, &lt;a href="http://tarekziade.wordpress.com/2009/02/08/a-distutils-regression-test-system/"&gt;as I
said before&lt;/a&gt;, the "real" Distutils regression test suite is held by
all the packages out there in the community, that are built and
installed everyday. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Python trunk Distutils test coverage : 41% &lt;br /&gt;
&lt;/strong&gt; &lt;br /&gt;
   Name               Stmts   Exec  Cover&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;--------------------------------------&lt;/span&gt;

&lt;span class="n"&gt;__init__&lt;/span&gt;&lt;span class="err"&gt;              &lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;archive_util&lt;/span&gt;&lt;span class="err"&gt;         &lt;/span&gt; &lt;span class="mi"&gt;77&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;61&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;79&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;bcppcompiler&lt;/span&gt;&lt;span class="err"&gt;        &lt;/span&gt; &lt;span class="mi"&gt;185&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;ccompiler&lt;/span&gt;&lt;span class="err"&gt;           &lt;/span&gt; &lt;span class="mi"&gt;453&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;211&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;46&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;cmd&lt;/span&gt;&lt;span class="err"&gt;                 &lt;/span&gt; &lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;134&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;74&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;config&lt;/span&gt;&lt;span class="err"&gt;               &lt;/span&gt; &lt;span class="mi"&gt;73&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;59&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;core&lt;/span&gt;&lt;span class="err"&gt;                 &lt;/span&gt; &lt;span class="mi"&gt;93&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;53&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;cygwinccompiler&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;161&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;debug&lt;/span&gt;&lt;span class="err"&gt;                 &lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="err"&gt;  &lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;dep_util&lt;/span&gt;&lt;span class="err"&gt;             &lt;/span&gt; &lt;span class="mi"&gt;43&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;dir_util&lt;/span&gt;&lt;span class="err"&gt;            &lt;/span&gt; &lt;span class="mi"&gt;109&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;76&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;69&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;dist&lt;/span&gt;&lt;span class="err"&gt;                &lt;/span&gt; &lt;span class="mi"&gt;581&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;386&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;66&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;emxccompiler&lt;/span&gt;&lt;span class="err"&gt;        &lt;/span&gt; &lt;span class="mi"&gt;118&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;errors&lt;/span&gt;&lt;span class="err"&gt;               &lt;/span&gt; &lt;span class="mi"&gt;49&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;extension&lt;/span&gt;&lt;span class="err"&gt;            &lt;/span&gt; &lt;span class="mi"&gt;97&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;fancy_getopt&lt;/span&gt;&lt;span class="err"&gt;        &lt;/span&gt; &lt;span class="mi"&gt;233&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;126&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;54&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;file_util&lt;/span&gt;&lt;span class="err"&gt;           &lt;/span&gt; &lt;span class="mi"&gt;124&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;77&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;62&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;filelist&lt;/span&gt;&lt;span class="err"&gt;            &lt;/span&gt; &lt;span class="mi"&gt;161&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;102&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;63&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;log&lt;/span&gt;&lt;span class="err"&gt;                  &lt;/span&gt; &lt;span class="mi"&gt;46&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;msvc9compiler&lt;/span&gt;&lt;span class="err"&gt;       &lt;/span&gt; &lt;span class="mi"&gt;408&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;msvccompiler&lt;/span&gt;&lt;span class="err"&gt;        &lt;/span&gt; &lt;span class="mi"&gt;370&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;spawn&lt;/span&gt;&lt;span class="err"&gt;                &lt;/span&gt; &lt;span class="mi"&gt;93&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;sysconfig&lt;/span&gt;&lt;span class="err"&gt;           &lt;/span&gt; &lt;span class="mi"&gt;323&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;51&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;text_file&lt;/span&gt;&lt;span class="err"&gt;           &lt;/span&gt; &lt;span class="mi"&gt;112&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;61&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;54&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;unixccompiler&lt;/span&gt;&lt;span class="err"&gt;       &lt;/span&gt; &lt;span class="mi"&gt;160&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;util&lt;/span&gt;&lt;span class="err"&gt;                &lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;157&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;61&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;version&lt;/span&gt;&lt;span class="err"&gt;              &lt;/span&gt; &lt;span class="mi"&gt;68&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;62&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;91&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;versionpredicate&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;61&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;51&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;83&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;__init__&lt;/span&gt;&lt;span class="err"&gt;              &lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="err"&gt;  &lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;bdist&lt;/span&gt;&lt;span class="err"&gt;                &lt;/span&gt; &lt;span class="mi"&gt;61&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;57&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;bdist_dumb&lt;/span&gt;&lt;span class="err"&gt;           &lt;/span&gt; &lt;span class="mi"&gt;57&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;82&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;bdist_msi&lt;/span&gt;&lt;span class="err"&gt;           &lt;/span&gt; &lt;span class="mi"&gt;322&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;bdist_rpm&lt;/span&gt;&lt;span class="err"&gt;           &lt;/span&gt; &lt;span class="mi"&gt;252&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;198&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;78&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;bdist_wininst&lt;/span&gt;&lt;span class="err"&gt;       &lt;/span&gt; &lt;span class="mi"&gt;170&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;build&lt;/span&gt;&lt;span class="err"&gt;                &lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;54&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;build_clib&lt;/span&gt;&lt;span class="err"&gt;           &lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;build_ext&lt;/span&gt;&lt;span class="err"&gt;           &lt;/span&gt; &lt;span class="mi"&gt;334&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;160&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;build_py&lt;/span&gt;&lt;span class="err"&gt;            &lt;/span&gt; &lt;span class="mi"&gt;213&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;178&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;83&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;build_scripts&lt;/span&gt;&lt;span class="err"&gt;        &lt;/span&gt; &lt;span class="mi"&gt;78&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;65&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;83&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;clean&lt;/span&gt;&lt;span class="err"&gt;                &lt;/span&gt; &lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;config&lt;/span&gt;&lt;span class="err"&gt;              &lt;/span&gt; &lt;span class="mi"&gt;185&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;install&lt;/span&gt;&lt;span class="err"&gt;             &lt;/span&gt; &lt;span class="mi"&gt;251&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;156&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;62&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;install_data&lt;/span&gt;&lt;span class="err"&gt;         &lt;/span&gt; &lt;span class="mi"&gt;44&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;install_egg_info&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;install_headers&lt;/span&gt;&lt;span class="err"&gt;      &lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;install_lib&lt;/span&gt;&lt;span class="err"&gt;          &lt;/span&gt; &lt;span class="mi"&gt;97&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;51&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;install_scripts&lt;/span&gt;&lt;span class="err"&gt;      &lt;/span&gt; &lt;span class="mi"&gt;33&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;29&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;87&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;register&lt;/span&gt;&lt;span class="err"&gt;            &lt;/span&gt; &lt;span class="mi"&gt;173&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;82&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;sdist&lt;/span&gt;&lt;span class="err"&gt;               &lt;/span&gt; &lt;span class="mi"&gt;228&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;78&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;upload&lt;/span&gt;&lt;span class="err"&gt;              &lt;/span&gt; &lt;span class="mi"&gt;112&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;33&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="err"&gt;--------------------------------------&lt;/span&gt;

&lt;span class="nv"&gt;TOTAL&lt;/span&gt;&lt;span class="err"&gt;              &lt;/span&gt; &lt;span class="mi"&gt;7502&lt;/span&gt;&lt;span class="err"&gt;  &lt;/span&gt; &lt;span class="mi"&gt;3126&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;strong&gt; &lt;br /&gt;
Python 2.5.4 Distutils test coverage : 18%&lt;/strong&gt; &lt;br /&gt;
   Name               Stmts   Exec  Cover&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;--------------------------------------&lt;/span&gt;

&lt;span class="n"&gt;__init__&lt;/span&gt;&lt;span class="err"&gt;              &lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;archive_util&lt;/span&gt;&lt;span class="err"&gt;         &lt;/span&gt; &lt;span class="mi"&gt;78&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;bcppcompiler&lt;/span&gt;&lt;span class="err"&gt;        &lt;/span&gt; &lt;span class="mi"&gt;185&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;ccompiler&lt;/span&gt;&lt;span class="err"&gt;           &lt;/span&gt; &lt;span class="mi"&gt;453&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;cmd&lt;/span&gt;&lt;span class="err"&gt;                 &lt;/span&gt; &lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;79&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;43&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;core&lt;/span&gt;&lt;span class="err"&gt;                 &lt;/span&gt; &lt;span class="mi"&gt;93&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;cygwinccompiler&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;160&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;debug&lt;/span&gt;&lt;span class="err"&gt;                 &lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="err"&gt;  &lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;dep_util&lt;/span&gt;&lt;span class="err"&gt;             &lt;/span&gt; &lt;span class="mi"&gt;43&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;dir_util&lt;/span&gt;&lt;span class="err"&gt;            &lt;/span&gt; &lt;span class="mi"&gt;106&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;dist&lt;/span&gt;&lt;span class="err"&gt;                &lt;/span&gt; &lt;span class="mi"&gt;578&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;342&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;59&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;emxccompiler&lt;/span&gt;&lt;span class="err"&gt;        &lt;/span&gt; &lt;span class="mi"&gt;118&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;errors&lt;/span&gt;&lt;span class="err"&gt;               &lt;/span&gt; &lt;span class="mi"&gt;49&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;extension&lt;/span&gt;&lt;span class="err"&gt;            &lt;/span&gt; &lt;span class="mi"&gt;97&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;fancy_getopt&lt;/span&gt;&lt;span class="err"&gt;        &lt;/span&gt; &lt;span class="mi"&gt;233&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;121&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;51&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;file_util&lt;/span&gt;&lt;span class="err"&gt;           &lt;/span&gt; &lt;span class="mi"&gt;121&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;filelist&lt;/span&gt;&lt;span class="err"&gt;            &lt;/span&gt; &lt;span class="mi"&gt;162&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;log&lt;/span&gt;&lt;span class="err"&gt;                  &lt;/span&gt; &lt;span class="mi"&gt;46&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;msvccompiler&lt;/span&gt;&lt;span class="err"&gt;        &lt;/span&gt; &lt;span class="mi"&gt;365&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;mwerkscompiler&lt;/span&gt;&lt;span class="err"&gt;      &lt;/span&gt; &lt;span class="mi"&gt;140&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;spawn&lt;/span&gt;&lt;span class="err"&gt;                &lt;/span&gt; &lt;span class="mi"&gt;93&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;sysconfig&lt;/span&gt;&lt;span class="err"&gt;           &lt;/span&gt; &lt;span class="mi"&gt;296&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;text_file&lt;/span&gt;&lt;span class="err"&gt;           &lt;/span&gt; &lt;span class="mi"&gt;146&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;unixccompiler&lt;/span&gt;&lt;span class="err"&gt;       &lt;/span&gt; &lt;span class="mi"&gt;159&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;util&lt;/span&gt;&lt;span class="err"&gt;                &lt;/span&gt; &lt;span class="mi"&gt;235&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;69&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;29&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;version&lt;/span&gt;&lt;span class="err"&gt;              &lt;/span&gt; &lt;span class="mi"&gt;68&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;versionpredicate&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;61&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;51&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;83&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;__init__&lt;/span&gt;&lt;span class="err"&gt;              &lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="err"&gt;  &lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;bdist&lt;/span&gt;&lt;span class="err"&gt;                &lt;/span&gt; &lt;span class="mi"&gt;59&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;bdist_dumb&lt;/span&gt;&lt;span class="err"&gt;           &lt;/span&gt; &lt;span class="mi"&gt;57&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;bdist_msi&lt;/span&gt;&lt;span class="err"&gt;           &lt;/span&gt; &lt;span class="mi"&gt;320&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;bdist_rpm&lt;/span&gt;&lt;span class="err"&gt;           &lt;/span&gt; &lt;span class="mi"&gt;248&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;bdist_wininst&lt;/span&gt;&lt;span class="err"&gt;       &lt;/span&gt; &lt;span class="mi"&gt;159&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;build&lt;/span&gt;&lt;span class="err"&gt;                &lt;/span&gt; &lt;span class="mi"&gt;52&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;build_clib&lt;/span&gt;&lt;span class="err"&gt;           &lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;build_ext&lt;/span&gt;&lt;span class="err"&gt;           &lt;/span&gt; &lt;span class="mi"&gt;304&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;build_py&lt;/span&gt;&lt;span class="err"&gt;            &lt;/span&gt; &lt;span class="mi"&gt;213&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;143&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;67&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;build_scripts&lt;/span&gt;&lt;span class="err"&gt;        &lt;/span&gt; &lt;span class="mi"&gt;78&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;82&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;clean&lt;/span&gt;&lt;span class="err"&gt;                &lt;/span&gt; &lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;config&lt;/span&gt;&lt;span class="err"&gt;              &lt;/span&gt; &lt;span class="mi"&gt;185&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;install&lt;/span&gt;&lt;span class="err"&gt;             &lt;/span&gt; &lt;span class="mi"&gt;220&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;54&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;install_data&lt;/span&gt;&lt;span class="err"&gt;         &lt;/span&gt; &lt;span class="mi"&gt;44&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;install_egg_info&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;install_headers&lt;/span&gt;&lt;span class="err"&gt;      &lt;/span&gt; &lt;span class="mi"&gt;26&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;install_lib&lt;/span&gt;&lt;span class="err"&gt;          &lt;/span&gt; &lt;span class="mi"&gt;96&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;install_scripts&lt;/span&gt;&lt;span class="err"&gt;      &lt;/span&gt; &lt;span class="mi"&gt;33&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;29&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;87&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;register&lt;/span&gt;&lt;span class="err"&gt;            &lt;/span&gt; &lt;span class="mi"&gt;171&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;sdist&lt;/span&gt;&lt;span class="err"&gt;               &lt;/span&gt; &lt;span class="mi"&gt;204&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="nv"&gt;upload&lt;/span&gt;&lt;span class="err"&gt;              &lt;/span&gt; &lt;span class="mi"&gt;118&lt;/span&gt;&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;

&lt;span class="err"&gt;--------------------------------------&lt;/span&gt;

&lt;span class="nv"&gt;TOTAL&lt;/span&gt;&lt;span class="err"&gt;              &lt;/span&gt; &lt;span class="mi"&gt;7026&lt;/span&gt;&lt;span class="err"&gt;  &lt;/span&gt; &lt;span class="mi"&gt;1283&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Mon, 23 Feb 2009 13:06:00 +0100</pubDate><guid>http://blog.ziade.org/2009/02/23/raising-distutils-test-coverage-half-way/</guid></item><item><title>What&amp;#039;s new in Distutils ?</title><link>http://blog.ziade.org/2009/02/15/what039s-new-in-distutils/</link><description>&lt;p&gt;Since Python 3.0.1 was released this week, here's a quick wrapup of what
is going on in Distutils. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Code work (since one month)&lt;/h3&gt;
&lt;h4&gt;New features&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Issue 2563 : now the manifest is embed in windows extensions&lt;/li&gt;
&lt;li&gt;Issue 4394 : the storage of the password in .pypirc file is optional
    now&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Fixed bugs&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Issue 4524: distutils was failing to build scripts with the
    '--with-suffix=3'&lt;/li&gt;
&lt;li&gt;Issue 5132 : build_ext command was failing under Solaris with
    '--enabled-shared'&lt;/li&gt;
&lt;li&gt;Issue 5075 : bdist_wininst was depending on the vc runtime&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Refactoring&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Issue 2461 : added test coverage for util.py&lt;/li&gt;
&lt;li&gt;Issue 3986 : removed string and type usage from distutils.cmd&lt;/li&gt;
&lt;li&gt;Issue 3987 : removed type usage from distutils.core&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Documentation&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Issue 5158 : added documentation for depends option for extensions&lt;/li&gt;
&lt;li&gt;Issue 4987: updated README info&lt;/li&gt;
&lt;li&gt;Issue 4137 : SIG web pages were updated&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Design work&lt;/h3&gt;
&lt;p&gt;The main topics that are being discussed are: &lt;br /&gt;
-   Improving the console script story. &lt;a href="http://mail.python.org/pipermail/distutils-sig/2009-February/010980.html"&gt;thread starts here&lt;/a&gt;.
-   Publishing a Survey on Distutils before the Language Summit. &lt;a href="http://mail.python.org/pipermail/distutils-sig/2009-January/010782.html"&gt;thread
    starts here&lt;/a&gt;.
-   Adding an uninstall command to unistall packages in the sdtlib :
    &lt;a href="http://mail.python.org/pipermail/distutils-sig/2009-January/010866.html"&gt;thread starts here&lt;/a&gt;
-   Adding a get_metadata API in pkgutil to get the metadata of an
    arbritary package The same code can also be used for various lookups
    (uninstall, console script) : &lt;a href="http://mail.python.org/pipermail/distutils-sig/2009-January/010711.html"&gt;thread starts here&lt;/a&gt;
-   Having a new PEP to make egg.info a directory and clearly define
    PKG-INFO and its match with metdata. &lt;a href="http://mail.python.org/pipermail/distutils-sig/2009-February/010968.html"&gt;thread starts here&lt;/a&gt;&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sun, 15 Feb 2009 10:35:00 +0100</pubDate><guid>http://blog.ziade.org/2009/02/15/what039s-new-in-distutils/</guid></item><item><title>A Distutils Regression Test System ?</title><link>http://blog.ziade.org/2009/02/08/a-distutils-regression-test-system/</link><description>&lt;p&gt;I am making some progress in Distutils. I closed something like 10 bugs
last week, and I am reaching issues that were added 8 months ago. Not
that everything is entirely cleaned up in the newest issues, but they're
almost all being processed. Every commit comes with at least a test, to
get the code base back into a state were it is easier to make things
evolve without the risk of breaking it up. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It comes through tiny little changes, with tests and an eye on the
coverage. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Now I am facing an unpleasant situation : since the test coverage is
still low, I am always scared of breaking something in Distutils when I
am fixing a bug or making a change.Buildbots are watching, and I run
some of my own packaging work with the current trunk. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But still, this is an unpleasant situation, and I don't want to cause
the package to be broken in the next Python version... &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But the regression tests exists ! They are there, hidden, in the
community. It's everyone package. &lt;br /&gt;
1.  Joe adds an issue in the Python bug tracker, because Distutils
    didn't work as expected on his package because of a bug
2.  At some point the bug is (was) fixed.
3.  The test to make sure the bug is fixed is "Joe is running Distutils
    over his package again, and makes sure it is properly installed,
    compiled, etc".
4.  The bug is closed.&lt;/p&gt;
&lt;p&gt;So how can I get back this test to make sure Joe's package is still
working properly, so he doesn't hate us at the next major Python release
? &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;A Distutils Regression Test Server&lt;/h3&gt;
&lt;p&gt;If Joe's package is on PyPI, we can set something up. A dedicated
server that watches the PyPI changelog and triggers a buildbot when: &lt;br /&gt;
-   a new release of Joe's Package comes out
-   we change something in Distutils code&lt;/p&gt;
&lt;p&gt;The precise test to be run is still unclear to me but, I am thinking
about some generic strategies and I think it's possible. Let's call this
test &lt;strong&gt;a distutils regression test&lt;/strong&gt;. (If you have a better name, I'll
buy it) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Of course it doesn't have to be on all the packages that are uploaded
out there at PyPI. Just Joe's one, because he came up with a problem we
fixed. And we would be ashamed if the bug comes back on Joe's package. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This requires of course a server, and probably a vmware-like system if
Joe runs Windows or Solaris, to make buildbot slaves etc. It also
requires that Joe uses the right metadata in his package so we know if
it works under Python 2, Python 3, etc. &lt;a href="http://mail.python.org/pipermail/distutils-sig/2008-October/010419.html"&gt;MvL added enough classifiers
lately for this&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;A Distributed Distutils Regression Test&lt;/h3&gt;
&lt;p&gt;But some package are not on PyPI, for privacy or conveniency in the
packaging process of the person in charge. So, what if the &lt;strong&gt;distutils
regression test&lt;/strong&gt; is provided in a Distutils command ? It can run the
same test the server runs, and come up with a report that is sendable or
sent by mail to a special mailing list or so. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This supposes that the developer is cooperative. So maybe it can even
be automatically triggered in case of any failure on any Distutils
command, and ask the user if he would like to send a report ? &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The good thing here is that it doesn't require CPU power on the test
server, and that anyone can run that test. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;So what ?&lt;/h3&gt;
&lt;p&gt;Well I am just throwing an idea here, because I am really concerned
about the potential regression problems. Even if Distutils is 100%
covered with tests, it's not possible to test all combinations. The real
world environment is the only test that can be trusted at the end in the
packaging area. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I'll throw this idea at the Language Summit in March, and if it catches
people interest, maybe a Google Summer of Code task could be done for
that topic ? Can't implement it myself, I am overwhelmed already in
Distutils maintenance :D &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Just out of curiosity, how do *you* test your packages to make sure
they get installed correctly ? &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt; &lt;br /&gt;
&lt;/strong&gt;&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sun, 08 Feb 2009 17:18:00 +0100</pubDate><guid>http://blog.ziade.org/2009/02/08/a-distutils-regression-test-system/</guid></item><item><title>Building a survey for Distutils (Pycon&amp;#039;s Python Language Summit)</title><link>http://blog.ziade.org/2009/01/28/building-a-survey-for-distutils-pycon039s-python-language-summit/</link><description>&lt;p&gt;I have the opportunity to lead a session at the &lt;a href="http://us.pycon.org/2009/about/summits/language/"&gt;Python Language
Summit&lt;/a&gt; at Pycon. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In order to prepare this event, I am currently building a survey
because we need to know how people use Distutils, what is wrong with it,
and so on... &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The draft is here : &lt;a href="http://wiki.python.org/moin/Packaging%20Survey"&gt;http://wiki.python.org/moin/Packaging%20Survey&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;When it's ready (hopefully soon), I'll post it in a SurveyMonkey-like
system online for people to take it, and synthetize the results, so what
I am saying at the summit reflects hopefully the reality. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you are a Python developer, an OS packager, come to the
&lt;a href="http://mail.python.org/mailman/listinfo/distutils-sig/"&gt;Distutils-SIG mailing list&lt;/a&gt; and help us build the best survey
possible ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;(while the draft is in a wiki, it's better to discuss the changes in
the mailing list before it is applied)&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Wed, 28 Jan 2009 11:48:00 +0100</pubDate><guid>http://blog.ziade.org/2009/01/28/building-a-survey-for-distutils-pycon039s-python-language-summit/</guid></item><item><title>Singletons (and Borg) are unpythonic (well.. imvho)</title><link>http://blog.ziade.org/2009/01/22/singletons-and-borg-are-unpythonic-well-imvho/</link><description>&lt;p&gt;Alex Martelli wrote a review about my latest book (can't find a
permanent link on this, just look at Amazon.com you'll find it). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Amongst the negative parts there's one noticeable part I'd like to
discuss in my blog, because I disagree with Alex's analysis. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Alex says: &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This also holds for the chapter on design patterns, with such
egregious claims as "Singletons should not have several levels of
inheritance" -- they should have as few as practical and feasible,
*exactly like any other class*; the desire to limit the number of
distinct instances (which is mostly about STATE) is quite orthogonal
to the issues with subclassing (which is mostly about BEHAVIOR). From
this original "totally missing the point" follows a classic howler
(which I've seen repeated in a review above): "why not use a module?".
I have news for you, Tarek: a module supports *ZERO* inheritance --
which is quite a bit stricter than even the unjustified "should not
have several level" claim above. Having to completely give up the
usefulness of inheritance just because you want to limit instantiation
would be a very limiting engineering tradeoff! If there's no need for
inheritance then *of course* you want to use a module - DOH! - but
if there IS (or if special methods can really help you) then it's not
an option.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I think that the Singleton (and Borg) pattern is totally useless in
fact. That's not the philosophy of Python in my humble opinion. And I
think my book is right to advise people not to use this pattern. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I don't see the point of bending down a class so it only has one
instance, where you can simply create an instance of that class in a
module, add a "_" prefix to that class, and tell the world that this
instance is your singleton. I don't see why a class should deal with
that kind of STATE. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Frankly, I doubt that this singleton/borg pattern is really used in the
community. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The only place where I really had to use singleton classes was in Zope.
But that was more like a marker than anything else, and the class was
registered under a "single" name in a global mapping (eg. its id in its
container, in the ZODB tree). And in that case, we were creating one
mixin class that used a singleton class, and we called it a "tool", with
all the desired BEHAVIOR inside of it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;And well, if we would had several instance of it, for sure nothing bad
could really happen, because the real unicity was provided by the id of
the object. And nowadays those "tools" are going away and they are now
called "utilities", and I don't think any singleton class is still
really present or used. Just simply because a class is not the right
place to enforce this. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I'd also say that there's an architectural problem when you enforce
things like this in Python. If a programmer tells me that he wants to
use a Singleton on his class because it holds a DB connector he wants to
be instanciated once in his application, I am asking him right away to
review the way the program is structured. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So what is the closest element in Python that will let you mark an
object as unique ? what is the most convenient way to mark an object
with an id in a container ? &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;A simple variable in a module&lt;/em&gt;. (or a simple declaration in a zcml
file if you are a zopish guy) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I love Python for this because it's multiparadigm unlike Java : you
don't have to set up over-engineered OOP stuff for this kind of needs
(and it is surely not a tradeoff to use well engineered OOP besides). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Last, when I am claiming in Frenglish, that "Singletons should not have
several levels of inheritance". This is just to warn people that, since
these patterns are trying to break the way classes work, &lt;em&gt;you might get
screwed at some point&lt;/em&gt; when singletons are subclassed. A descriptor or a
metaclass or whatever can just break your singleton stuff because Python
was not meant to be used like that. It's not robust.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 22 Jan 2009 23:19:00 +0100</pubDate><guid>http://blog.ziade.org/2009/01/22/singletons-and-borg-are-unpythonic-well-imvho/</guid></item><item><title>Blog title changed - Fetchez le Python mes amis</title><link>http://blog.ziade.org/2009/01/22/blog-title-changed-fetchez-le-python-mes-amis/</link><description>&lt;p&gt;I have too many search hits about &lt;a href="http://en.wikipedia.org/wiki/Carpet_python"&gt;Carpet Pythons&lt;/a&gt; on my blog, and too
many book reviews on how bad my English is. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Welcome to "Fetchez le Python", the technical blog on Python
programming language, in a pure Frenglish style !&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 22 Jan 2009 21:49:00 +0100</pubDate><guid>http://blog.ziade.org/2009/01/22/blog-title-changed-fetchez-le-python-mes-amis/</guid></item><item><title>Python standard lib : give me more withs !</title><link>http://blog.ziade.org/2009/01/20/python-standard-lib-give-me-more-withs/</link><description>&lt;p&gt;I used to write in files like this : &lt;br /&gt;
   open('somefile', 'w').write(content)&lt;/p&gt;
&lt;p&gt;It's ugly for sure, and a more proper way is : &lt;br /&gt;
   f = open('somefile', 'w')&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;try:&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;finally:&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;But since Python 2.6, the &lt;strong&gt;with&lt;/strong&gt; statement is superior for this code
pattern: &lt;br /&gt;
   with open('somefile', 'w') as f:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This is so natural in fact that I am always thinking about &lt;strong&gt;with&lt;/strong&gt;
when I work with classes that have a start/stop or open/close behavior.&lt;/p&gt;
&lt;p&gt;So, what about adding this behavior into imaplib.IMAP4, ftplib.FTP and
smtplib.SMTP ? &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So we can write things like this : &lt;br /&gt;
       &amp;gt;&amp;gt;&amp;gt; from ftplib import FTP&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;    &lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;FTP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;ftp.somewhere.com&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ftp:&lt;/span&gt;

    &lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;ftp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;someone&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;pass&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;some&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;I am working on a &lt;a href="http://bugs.python.org/issue4972"&gt;series of patches&lt;/a&gt; for this, and wondering if some
other classes in the standard library could benefit from this as well..&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 20 Jan 2009 22:11:00 +0100</pubDate><guid>http://blog.ziade.org/2009/01/20/python-standard-lib-give-me-more-withs/</guid></item><item><title>Distutils : improved .pypirc for Python 2.7 and 3.1</title><link>http://blog.ziade.org/2009/01/09/distutils-improved-pypirc-for-python-27-and-31/</link><description>&lt;p&gt;When you launch such a command: &lt;br /&gt;
   $ python setup.py register sdist upload&lt;/p&gt;
&lt;p&gt;There's no way to give to Distutils your PyPI password in the prompt,
so you distribution is uploaded to the server. You have to store your
password in the &lt;em&gt;.pypirc&lt;/em&gt; file: &lt;br /&gt;
       [distutils]&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;    &lt;span class="nb"&gt;index&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;servers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;

        &lt;span class="n"&gt;pypi&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pypi&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;username:&lt;/span&gt; &lt;span class="sr"&gt;&amp;lt;username&amp;gt;&lt;/span&gt;

    &lt;span class="n"&gt;password:&lt;/span&gt; &lt;span class="sr"&gt;&amp;lt;password&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The password is stored in clear text, so it can be used by Distutils to
authenticate. This is rather unsecure, since anyone who has a read
access to your home can get your password. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I have detected this problem this summer while listing the possible
enhancements in Distutils. Nathan Van Gheem sent me a mail a month ago
to ask for that same feature in &lt;a href="http://pypi.python.org/pypi/collective.dist#what-is-collective-dist"&gt;collective.dist&lt;/a&gt;; which is a port of
the latest Distutils features into Python 2.4 so Zope can use them. So
before having it into collective.dist, the first step was to introduce
it into Python itself. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The idea is to be able to remove from &lt;em&gt;.pypirc&lt;/em&gt; the password so it's
asked at the prompt. Nothing fancy here : the Distribution object that
is created before you launch any command is the place where you can
share a context between commands. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So when you launch: &lt;br /&gt;
   $ python setup.py register sdist upload&lt;/p&gt;
&lt;p&gt;Here's what is happening: &lt;br /&gt;
1.  &lt;strong&gt;register&lt;/strong&gt; looks into &lt;em&gt;.pypirc&lt;/em&gt;, if no password is found, it asks
    it to the user using &lt;em&gt;getpass&lt;/em&gt;
2.  &lt;strong&gt;register&lt;/strong&gt; use it then store it in the Distribution instance
3.  &lt;strong&gt;upload&lt;/strong&gt; look into the Distribution instance to see if the
    password was stored, and use it&lt;/p&gt;
&lt;p&gt;This is now available in Python 2.7 and 3.1, and &lt;a href="http://svn.python.org/view?rev=68415&amp;amp;view=rev"&gt;heavily tested&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I'd like to go further and to think about a ssh-agent like system, so
there's no need to enter the pasword everytime you work with PyPI in the
same session. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Does anyone knows what would be the way to do it properly ? I think a
ssh-agent like mechanism in Python's getpass would be a great feature
itself.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 09 Jan 2009 08:30:00 +0100</pubDate><guid>http://blog.ziade.org/2009/01/09/distutils-improved-pypirc-for-python-27-and-31/</guid></item><item><title>2009 plans, part #1 : Distutils</title><link>http://blog.ziade.org/2009/01/04/2009-plans-part-1-distutils/</link><description>&lt;p&gt;Happy New year all ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I am going to make a few posts on the things I would like to achieve in
2009. Each entry will focus on a topic. This one is about Distutils. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I was granted a commit privilege in Python, specifically to work on
Distutils maintenance. This is a huge privilege, and I try will do my
best in this job. I have worked on a few tickets already and closed
some. I learnt the Python development process, which requires to
backport and to forward-port changesets in various Python versions.
While this can be taken care of automatically by someone else if you
don't do it, it's better that every commiter takes the time to merge his
own work. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So what's next ? &lt;br /&gt;
-   There are &lt;a href="http://bugs.python.org/issue?@sort0=activity&amp;amp;@sort1=&amp;amp;@group0=&amp;amp;@group1=&amp;amp;@columns=id,activity,title,creator,assignee,status&amp;amp;@filter=status&amp;amp;status=-1,1,3&amp;amp;@search_text=distutils&amp;amp;@pagesize=50&amp;amp;@startwith=0"&gt;132 tickets&lt;/a&gt; that are open in the Python tracker, that
    match the word distutils, and some of them are 5 years old !
-   There's a &lt;a href="http://mail.python.org/pipermail/python-dev/2008-December/083819.html"&gt;Python language summit&lt;/a&gt; to be held in Chicago right
    before Pycon, and I volunteered to champion the task about
    Distutils, PyPI and packaging matters.&lt;/p&gt;
&lt;p&gt;I am planning to : &lt;br /&gt;
-   review and classify all the tickets in the tracker;
-   fix the maximum amount of them before the summit;
-   make Distutils a first class citizen in test coverage;
-   make Distutils code more modern.&lt;/p&gt;
&lt;p&gt;Besides, I will try to build a roadmap for Distutils I will present in
Chicago. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;To build this roadmap, I will ask for input in the &lt;a href="http://mail.python.org/mailman/listinfo/distutils-sig/"&gt;distutils-SIG
mailing list&lt;/a&gt; in the coming days, and see what people will come up
with. There's no crowd in this list these days but sometimes some
threads are hot when it comes to the future of packaging in Python. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The roadmap I am planning to build will not address all the issues
people have when it comes to distribute a Python application, since
there is no consensus yet on the best practices. It will rather try to
see if the current version of Distutils can be enhanced to adress some
problems, and at least be the bridge to something new in the future.
Maybe by including some best practices from third-party tools (the
pre-condition for all of this imho is to make the Distutils code base
healthier). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Anyway, I hope that the lead developers of: &lt;a href="http://pypi.python.org/pypi/zc.buildout"&gt;zc.buildout&lt;/a&gt;, &lt;a href="http://pypi.python.org/pypi/pip"&gt;pip&lt;/a&gt;,
&lt;a href="http://pypi.python.org/pypi/setuptools/"&gt;setuptools&lt;/a&gt;, &lt;a href="http://pypi.python.org/pypi/Paver"&gt;paver&lt;/a&gt; (and those projects I forget about right now)
will participate in this discussion, and that we will be able to find
pragmatic enhancements.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sun, 04 Jan 2009 19:52:00 +0100</pubDate><guid>http://blog.ziade.org/2009/01/04/2009-plans-part-1-distutils/</guid></item><item><title>Pycon 2009 talks</title><link>http://blog.ziade.org/2008/12/16/pycon-2009-talks/</link><description>&lt;p&gt;I have 2 accepted talks at Pycon, that is great. I would like to say
that the Pycon review system is awesome because you can see what the
reviewers have said, and understand why your talk was accepted or
declined. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I was a bit frustrated that my Atomisator talk was declined, but I
think it makes sense : this is a new tool, and beside my user group and
a few people, it is not really used yet. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;One reviewer said that it had to be picked, and another one answered :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I agree that PyCon should not restrict itself to well-known projects,
but it should definitely restrict itself to projects that are (a) in
production use, (b) under active development, and (c) likely to still
be so in a year. There are so many projects meeting these criteria
that for me, the bar is very high indeed to spend a talk slot on one
that does not.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Ok, fair enough : I will present this talk at Pycon 2010 and they won't
have any argument to decline it ;) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The talks that made it: &lt;br /&gt;
-   How AlterWay releases web applications using zc.buildout
-   On the importance of PyPI in delivering and building Python
    softwares - mirroring, fail-over and third-party package indexes&lt;/p&gt;
&lt;p&gt;I will get into greater details later on.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 16 Dec 2008 23:32:00 +0100</pubDate><guid>http://blog.ziade.org/2008/12/16/pycon-2009-talks/</guid></item><item><title>Python Isolated Environment (PIE)</title><link>http://blog.ziade.org/2008/12/15/python-isolated-environment-pie/</link><description>&lt;p&gt;Here's a proposal I will send to the python-dev. What do you think ? &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(Disclaimer : this proposal is highly inspired from the work done by
people in various tools, it does not reinvent anything)&lt;/em&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;The problem&lt;/h3&gt;
&lt;p&gt;Python developers distribute and deploy their packages using myriads of
dependencies. Some of them are not yet available as official OS python
packages. Even sometimes one package conflicts with the &lt;em&gt;official&lt;/em&gt;
version of a package installed in a given OS. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In any case, the cycle of development of most Python applications is
shorter than the release cycle of Linux distributions, so it is
impossible for application Foo to wait that Bar 5.6 is officialy
available in Debian 4.x. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Therefore, there's a need to provide or describe a specific list of
dependencies for their application to work. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;And this list of dependency might conflict with the existing list of
packages installed in Python. In other words, even if this is not a
wanted behavior from an os packager point of view, an application might
need to provide its own execution context. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Right now, when Python is loaded, it uses the site module to browse the
site-packages directory to populate the path with packages it find
there. &lt;em&gt;.pth&lt;/em&gt; files are also parsed to provide extra paths. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Python 2.6 has introduced per-user &lt;em&gt;site-packages&lt;/em&gt; directory, where you
can define an extra directory, which is added in the path like the
central one. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But both will append new paths to the environment without any rule of
exclusion or version checking. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;The workarounds&lt;/h3&gt;
&lt;p&gt;A few workarounds exist to be able to express what packages (and
version) an application needs to run, or to set up an isolated
environment for it: &lt;br /&gt;
-   &lt;a href="http://pypi.python.org/pypi/setuptools/"&gt;&lt;em&gt;setuptools&lt;/em&gt;&lt;/a&gt; provides the &lt;em&gt;install_requires&lt;/em&gt; mechanism where
    you can define dependencies directly inside the package, as a new
    metadata. It also provides a way to install two different versions
    of one package and let you pick by code or when the program starts,
    which one you want to activate.
-   &lt;a href="http://pypi.python.org/pypi/virtualenv"&gt;&lt;em&gt;virtualenv&lt;/em&gt;&lt;/a&gt; will let you create an isolated Python environment,
    where you can define your own site-packages. This allows you to make
    sure you are not conflicting with a incompatible version of a given
    package.
-   &lt;a href="http://pypi.python.org/pypi/zc.buildout"&gt;&lt;em&gt;zc.buildout&lt;/em&gt;&lt;/a&gt; relies on setuptools and provides an isolated
    environment a bit similar in some aspects to virtualenv.
-   &lt;a href="http://pypi.python.org/pypi/pip"&gt;pip&lt;/a&gt; provides a way to describe requirements in a file, which can
    be used to define &lt;em&gt;bundles&lt;/em&gt;, which are very similar to what
    zc.buildout provides.&lt;/p&gt;
&lt;p&gt;But they all aim at the same goal : define a specific execution context
for a specific application, and declare dependencies with no respect to
other applications or to the OS environment. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This proposal describes a solution that can be added to Python to
provide that feature. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;The solution&lt;/h3&gt;
&lt;p&gt;A &lt;em&gt;isolated environment&lt;/em&gt; file that describes dependencies is added.
This file can be tweaked by the application packager, or later by the OS
packager if something goes wrong. &lt;br /&gt;
&lt;/p&gt;
&lt;h4&gt;The isolated environment file&lt;/h4&gt;
&lt;p&gt;A new file called a &lt;em&gt;Python Isolated Environment&lt;/em&gt; file (PIE file) can
be provided by any application to define the list of dependencies and
their versions. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It is a simple text file with a first line that provides : &lt;br /&gt;
-   a list of paths, separated by ':', on line 1
-   then one package per line, starting at line 2. each package can be
    prefixed by a `!`&lt;/p&gt;
&lt;p&gt;For example: &lt;br /&gt;
   /var/myapp/myenv&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;lxml&lt;/span&gt;

&lt;span class="n"&gt;sqlite&lt;/span&gt;

&lt;span class="n"&gt;sqlalchemy&lt;/span&gt;

&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;sqlobject&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This list of packages might or might not be installed in Python. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Versions can be provided as well in this file : &lt;br /&gt;
   /var/myapp/myenv:/var/myapp/myenv2&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;lxml&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.9&lt;/span&gt;

&lt;span class="n"&gt;sqlite&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;1.8&lt;/span&gt;

&lt;span class="n"&gt;sqlalchemy&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt;

&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;sqlobject&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mf"&gt;0.6&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The file is saved with the &lt;em&gt;pie&lt;/em&gt; extension, &lt;br /&gt;
&lt;/p&gt;
&lt;h4&gt;Loading an isolated environment file&lt;/h4&gt;
&lt;p&gt;A new function called &lt;em&gt;load_isolated_environment&lt;/em&gt; is added in
&lt;em&gt;site.py&lt;/em&gt;, that let you load a PIE file. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Loading a PIE file means: &lt;br /&gt;
-   for each package defined, starting at line 2,
    &lt;em&gt;load_isolated_environment&lt;/em&gt; will look into the environment if the
    package with the particular version exists. The version is given by
    the &lt;em&gt;package.__version__&lt;/em&gt; value or the &lt;em&gt;PKG-INFO&lt;/em&gt; one when
    available. If the package exists but the version is not available,
    the version 0.0 is used.
-   for packages without the ! prefix: &lt;br /&gt;
   -   if the package is not found, it will scan each path provided on
        line 1 of the file, using the &lt;em&gt;site-packages&lt;/em&gt; method, looking
        for that package.
    -   if the package is found, it is added in the path.
    -   if the package is not found, a &lt;em&gt;PackageMissing&lt;/em&gt; error is raised.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;for packages starting with the ! prefix: &lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;if the package is found, it is removed from the path&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This function can be called by code like this: &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;from site import load_isolated_environment&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;load_isolated_environment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;/path/to/context.pie&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;From there, &lt;em&gt;sys.path&lt;/em&gt; meets the requirements and the code that is
executed after this call will benefit from this context. &lt;br /&gt;
Another context can be loaded in the same process : &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;load_isolated_environment('/path/to/another_context.pie')&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;Limitations: &lt;br /&gt;
-   if the new context brakes other programs in the process. It's up to
    the application packager to fix the context file.
-   it's not the job of &lt;em&gt;load_isolated_environment&lt;/em&gt; to resolve
    dependencies issues : if the &lt;em&gt;foo&lt;/em&gt; package needs the &lt;em&gt;bar&lt;/em&gt; package,
    it won't complain.
-   it is not the job of &lt;em&gt;load_isolated_environment&lt;/em&gt; to get missing
    dependencies.&lt;/p&gt;
&lt;h4&gt;Using an isolated environment file&lt;/h4&gt;
&lt;p&gt;Typically, an isolated environment file can be used into high-level
Python scripts. For example, any script an application provides to be
launched : &lt;br /&gt;
   # this script runs zope&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;site&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_isolated_environment&lt;/span&gt;

&lt;span class="n"&gt;load_isolated_environment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;zope-3.4.pie&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;zope&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;zope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Mon, 15 Dec 2008 10:24:00 +0100</pubDate><guid>http://blog.ziade.org/2008/12/15/python-isolated-environment-pie/</guid></item><item><title>Looking for beta testers for Atomisator</title><link>http://blog.ziade.org/2008/12/14/looking-for-beta-testers-for-atomisator/</link><description>&lt;p&gt;I am looking for beta testers, interested in customized rss feeds or
email alerts experimentations. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here's a list of services Atomisator can provide : &lt;br /&gt;
-   You run a project and you would like to receive a daily summary in
    your mailbox on what is being said about it in blogs, tweets, etc
-   You have a list of feeds you want to aggregate, with specific
    filters and you can't manage to do with Yahoo pipes or any tools out
    ther, because it is too specific.
-   You want to annotate entries in a feed with extra information
-   etc..&lt;/p&gt;
&lt;p&gt;What you get as a beta-tester: &lt;br /&gt;
-   a custom Atomisator configuration that fills your needs
-   I am hosting the service, and you get &lt;br /&gt;
   -   either an url on my server to an xml file you can read in your
        aggregator
    -   either one mail per day&lt;/p&gt;
&lt;p&gt;What you are not getting as a beta tester: &lt;br /&gt;
-   you don't get any guarantee on the output or the reliability, these
    are just experimentations.
-   if it's down I can't promise when it will be up again&lt;/p&gt;
&lt;p&gt;Let me know by mail if you are interested&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sun, 14 Dec 2008 13:57:00 +0100</pubDate><guid>http://blog.ziade.org/2008/12/14/looking-for-beta-testers-for-atomisator/</guid></item><item><title>Pycon 2009 proposals</title><link>http://blog.ziade.org/2008/12/12/pycon-2009-proposals/</link><description>&lt;p&gt;The proposal acceptance date is in a few days. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here are the four proposals I have made: &lt;br /&gt;
-   &lt;strong&gt;The state of packaging in Python&lt;/strong&gt;. This discussion resumes the
    current options when it comes to distribute your packages. It also
    explains the pitfalls and the gap between the Python developers and
    the OS vendors and packagers. I think this talk will not be picked
    because the topic is wide and vague. So I proposed to transform it
    into a panel where lead developers from various framework could
    explain their usage of distutils and what is missing to make them
    happy. No feedback yet on this.
-   &lt;strong&gt;Atomisator, the agile data processing framework&lt;/strong&gt;. This tool is
    starting to be useful, and I think it can be useful to others. Check
    &lt;a href="http://atomisator.ziade.org"&gt;http://atomisator.ziade.org&lt;/a&gt; for a quick overview.
-   &lt;strong&gt;How AlterWay releases web applications using zc.buildout&lt;/strong&gt;. That
    is the same talk I gave at the Plone conf but I present it in a way
    people understand zc.buildout is not tied to Zope and Plone and can
    be used with any other application. As a matter of fact, it has
    become a standard here, and we use it for Pylons, etc..
-   &lt;strong&gt;On the importance of PyPI in delivering and building Python
    softwares - mirroring, fail-over and third-party package indexes&lt;/strong&gt;.
    That's a long title. It presents &lt;a href="http://tarekziade.wordpress.com/2008/11/26/python-package-distribution-my-current-work/"&gt;my work on PyPI&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Last, I will go to the Python Language Summit the day before Pycon. I
volunteered to be a "champion" on distutils matters.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 12 Dec 2008 08:18:00 +0100</pubDate><guid>http://blog.ziade.org/2008/12/12/pycon-2009-proposals/</guid></item><item><title>How to make binary distribution of buildouts</title><link>http://blog.ziade.org/2008/12/09/how-to-make-binary-distribution-of-buildouts/</link><description>&lt;h3&gt;The Problem&lt;/h3&gt;
&lt;p&gt;I need to distribute pre-compiled buildouts because some projects don't
allow us to have gcc installed on the production system for security
reasons. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Fair enough, we need to provide a pre-compiled buildout. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you want to distribute your buildout-based Plone application in a
binary form, so it can be installed without requiring any compiler on
the platform, you need to compile all .c modules before you provide a
tarball of your buildout folder. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This is easy : just run your buildout and all .so files will be created
in the zope 2 installation. (.pyd under windows) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But this will work only if you compile in a directory that is located
within the same path on the target machine, because zc.buildout uses
absolute paths when it builds scripts. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Furthermore, if the python interpreter is not located in the same
place, your buildout script itself is screwed. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Last but not least, plone.recipe.zope2install is not clever enough. It
will remove your zope2 installation when it detects that the path has
changed. This is pretty annoying even if you have gcc : what is the
point of compiling the c extension again since they &lt;br /&gt;
are statically compiled in-place ? &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;The solution&lt;/h3&gt;
&lt;p&gt;I have changed plone.recipe.zope2install and added a new option called
`smart-recompile` (in trunk right now, not released). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you use it, the recipe will check for .so or .pyd files before
trying to ditch your zope 2 installation and recompile it. Even if you
don't use it to build binary distributions, it will make your buildout
build faster if you already have zope compiled in there. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Next, I have created a special bootstrap.py, who is clever enough to
rebuild the buildout script with the right path to the used interpreter,
and with offline-mode capabilities. To make it short : boostrap.py works
no matter if you have an internet connection or not. Grab it here :
&lt;a href="http://ziade.org/bootstrap.py"&gt;http://ziade.org/bootstrap.py&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So now, basically you can compile your buildout and deploy it on any
system, on any path, without any internet connection, like this: &lt;br /&gt;
   $ python bootstrap.py    # will rebuild the buildout script    &lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;buildout&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Of course this doesn't work if you have dynamically compiled extensions
like python-ldap. For theses, the best pick is to rely on the system
ones. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href="http://ziade.org/bootstrap.py"&gt;&lt;/a&gt;&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 09 Dec 2008 01:26:00 +0100</pubDate><guid>http://blog.ziade.org/2008/12/09/how-to-make-binary-distribution-of-buildouts/</guid></item><item><title>A PostRank plugin for Atomisator</title><link>http://blog.ziade.org/2008/12/07/a-postrank-plugin-for-atomisator/</link><description>&lt;p&gt;Yesterday, I bumped into &lt;a href="http://www.postrank.com/"&gt;PostRank&lt;/a&gt;. This system is collecting data
from various social systems like Twitter and provides a service where
you can type in an url of a blog post or a entire blog. You get a
PostRank depending on the popularity of the URL. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I wrote a plugin for Atomisator and ran it on my own blog. Here's the
result: &lt;a href="http://ziade.org/afpy/"&gt;http://ziade.org/afpy/&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;And the Atomisator configuration for this is : &lt;br /&gt;
&lt;a href="http://atomisator.ziade.org/"&gt;atomisator&lt;/a&gt;&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;sources&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;rss&lt;/span&gt; &lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;tarekziade&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wordpress&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="sr"&gt;/feed/&lt;/span&gt;&lt;span class="n"&gt;atom&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;

&lt;span class="n"&gt;database&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqlite:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;carpet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;

&lt;span class="n"&gt;outputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;rss&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;public&lt;/span&gt;&lt;span class="sr"&gt;/rss.xml &amp;quot;http://tarekziade.wordpress.com/&lt;/span&gt;&lt;span class="n"&gt;feed&lt;/span&gt;&lt;span class="sr"&gt;/atom/&lt;/span&gt;&lt;span class="s"&gt;&amp;quot; &amp;quot;&lt;/span&gt;&lt;span class="n"&gt;Carpet&lt;/span&gt; &lt;span class="n"&gt;Python&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;PR&lt;/span&gt;&lt;span class="s"&gt;&amp;quot; &amp;quot;&lt;/span&gt;&lt;span class="n"&gt;Powered&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt; &lt;span class="n"&gt;Atomisator&lt;/span&gt;&lt;span class="err"&gt;&amp;quot;&lt;/span&gt;

&lt;span class="n"&gt;enhancers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;postrank&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;How PostRank works&lt;/h3&gt;
&lt;p&gt;PostRank works with urls you provide, on their web interface or through
their web services. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;As long as these url are present in their big cloud-computing based
system, they provide a rank that is calculated with the number of
comments related to the blog, the number of tweet messages that refers
to it, and so on. The complete algorithm they used is secret but this is
not the point. I have secret algorithms too ;). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The point is that they are trying to categorize blog entries using
social networks as indicators, and that they have a huge database. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Social indicators in Atomisator&lt;/h3&gt;
&lt;p&gt;This is one of the approach I have with &lt;a href="http://atomisator.ziade.org/"&gt;Atomisator&lt;/a&gt;, when it is used
to build a planet. For instance I have a Digg plugin that will inject in
each entry the comments found on Digg if the entry was digged. It also
present the number of Digg. Of course this is done live because I don't
have a cloud-computing based system where I store data. I use Digg
webservice on the fly. (On the fly here doesn't mean Atomisator make the
calls to Digg from the Planet application of course. It means Atomisator
calls them when it creates the merged feed on the system) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The benefit of this approach is that I can provide a social indicator
on a post immediatly. Systems like PostRank will not work on entries
that are too recent because their spiders have a lag of one week or so.&lt;/p&gt;
&lt;p&gt;The pitfall of my approach is that I am unable to calculate trends
because I don't store the indicators as they vary. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But if someone wanted to build a BtoC application using Atomisator,
they could implement a set of plugins based on Amazon tools to make them
store data in a more scalable way and in time. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Next steps&lt;/h3&gt;
&lt;p&gt;So I have this new PostRank plugin, and this is awesome because I have
added a treshold parameter in it. Basically if a post has a high
PostRank value, it will appear in the Planet. If it's low, it can be
automatically removed. The fact that PostRanks are lagging for new
entries is not a problem: interesting posts will eventually pop after a
few days in the Planet. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This is perfect to reduce the number of entries in an aggregator. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But I do want to write my own PostRank that works live, with no storage
at all. &lt;strong&gt;Because the whole point of Atomisator is to provide a
framework where anyone can try out various filtering combinations&lt;/strong&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So to be able to provide this power, it needs to work just by
collecting data directly from the social services, like the PostRank
plugin does with this PostRank "meta-service". The next step is
therefore to see if I can query services like Twitter to list the twits
related to an url, without having to store the twitter feed myself. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In any case, if my talk on Atomisator at Pycon 2009 is selected, the
PostRank plugin will be shown besides the Digg plugin.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sun, 07 Dec 2008 11:54:00 +0100</pubDate><guid>http://blog.ziade.org/2008/12/07/a-postrank-plugin-for-atomisator/</guid></item><item><title>Expert Python Programming Book : typo sprint tonight !</title><link>http://blog.ziade.org/2008/11/27/expert-python-programming-book-typo-sprint-tonight/</link><description>&lt;p&gt;I love Packt. As soon as I have told them that some people liked the
book but &lt;a href="http://techblog.ironfroggy.com/2008/11/how-to-be-dissappointed-in-something.html"&gt;complained&lt;/a&gt; about the typos, they proposed to go ahead and
launch a new print cycle. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Basically it means that the next buyers will have a typo-free book. At
least for all the typos that were &lt;a href="http://atomisator.ziade.org/wiki/Errata"&gt;reported on my Trac here,&lt;/a&gt; or at
Packt's. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I am currently processing all the typos reported at Packt so I have a
full list on my wiki, and will provide them the final list tomorrow. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Then, they will re-print it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So if you already own the book, and you see a typo that is not listed,
please let me know.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 27 Nov 2008 21:03:00 +0100</pubDate><guid>http://blog.ziade.org/2008/11/27/expert-python-programming-book-typo-sprint-tonight/</guid></item><item><title>Python package distribution - my current work</title><link>http://blog.ziade.org/2008/11/26/python-package-distribution-my-current-work/</link><description>&lt;p&gt;I found a bit of time to work on distribution matters. Here's a status
of what I am doing there. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;There are two topics I am focusing on right now. &lt;br /&gt;
-   clean up and enhance Python's distutils package
-   implement the mirroring infrastructure at PyPI&lt;/p&gt;
&lt;h3&gt;distutils work&lt;/h3&gt;
&lt;p&gt;Nathan Van Gheem proposed a cool patch in &lt;a href="http://pypi.python.org/pypi/collective.dist/0.2.0#what-is-collective-dist"&gt;collective.dist&lt;/a&gt;, (this
package is a port of the new features I have added in distutils so they
are available in 2.4 and 2.5). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Nathan proposed a patch to be able to avoid the storage of the password
in the .pypirc file. The prompt is used in that case. This is something
that was in my pile for a long time. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I have added a few things to Nathan's patch, and a test, and proposed
it to Python. I am now waiting for its integration in 2.7 trunk:
&lt;a href="http://bugs.python.org/issue4394"&gt;http://bugs.python.org/issue4394&lt;/a&gt;. If it's accepted, I will backport
it to collective.dist. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;There are some other tickets I am waiting to be accepted: &lt;br /&gt;
-   &lt;a href="http://bugs.python.org/issue4400"&gt;http://bugs.python.org/issue4400&lt;/a&gt;
-   &lt;a href="http://bugs.python.org/issue2461"&gt;http://bugs.python.org/issue2461&lt;/a&gt;
-   &lt;a href="http://bugs.python.org/issue3992"&gt;http://bugs.python.org/issue3992&lt;/a&gt;
-   &lt;a href="http://bugs.python.org/issue3985"&gt;http://bugs.python.org/issue3985&lt;/a&gt;
-   &lt;a href="http://bugs.python.org/issue3986"&gt;http://bugs.python.org/issue3986&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I am not sure when those will be integrated. The average time for the
integration of tickets in distutils in Python is between 6 months and 8
months. hihihi. :D &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;PyPI mirroring&lt;/h3&gt;
&lt;p&gt;The job I am doing in PyPI will be in three phase : &lt;br /&gt;
-   &lt;strong&gt;Phase 1&lt;/strong&gt;: implement the mirroring infrastructure in PyPI
-   &lt;strong&gt;Phase 2&lt;/strong&gt;: promote it, and propose patches for the mirroring tools
    out there so they use the protocol
-   &lt;strong&gt;Phase 3&lt;/strong&gt;: promote and propose patches for &lt;a href="http://pypi.python.org/pypi/pip"&gt;pip&lt;/a&gt; so it can use
    the mirrors efficiently (fail-over and nearest mirror
    infrastructure).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Phase 1: so far, so good. &lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;With some insights from &lt;a href="http://www.mechanicalcat.net/richard/log/Python"&gt;Richard Jones&lt;/a&gt; and Martin von Löwis, I am
currently implementing the mirroring infrastructure for PyPI we have
defined during the D.C. sprint (I still owe a blog entry about this
sprint). The code lives &lt;a href="https://svn.python.org/packages/branches/tarek-pypi/pypi/"&gt;in a branch on the python svn folder&lt;/a&gt;
dedicated to PyPI. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The idea of the mirroring infrastructure is to be able to get a list of
official mirrors for PyPI, that can be used as alternatives sources .
(It is described here: [http://wiki.python.org/moin/PEP_374][]). A
great behavior could be that the client application interacts with the
nearest mirror location automatically, and switch to another if it goes
down. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So, a list of mirrors will be made available at &lt;em&gt;/mirrors&lt;/em&gt;, and the
client applications will be able from there to use an alternative
location for every package. The hardest part concerns the stats : we
want to display in PyPI the download counts for each package by summing
downloads from every mirror. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So every mirror will have to provide its "local stats" that can be
visited by PyPI. That's the biggest part of the work I am doing. It will
build the stats for PyPI by parsing its Apache log file. And hopefully,
this code should be reusable by the mirrors themselve so they can build
their stats the same way. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Of course this infrastructure could be used for any PyPI-compatible
server even if is not a mirror of PyPI (like a private PyPI server)&lt;/em&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Phase 2&lt;/strong&gt; will consist in promoting the infrastructure to the
mirroring softwares out there. Maybe Pycon will be a good place for
that. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Phase 3&lt;/strong&gt; is the most interesting one : make sure the client
applications use the mirrors ! I think Ian Bicking's pip project could
be the right place for these innovations. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Next topics in the pile: &lt;br /&gt;
-   &lt;strong&gt;index-merging&lt;/strong&gt;: describe in a PEP-like document the index-merging
    feature that would allow clients to merge several indexes with a
    content that differe. For example: PyPI + a private PyPI server. I
    have written a first draft of such a patch in setuptools in the past
    (&lt;a href="http://bugs.python.org/setuptools/issue32"&gt;http://bugs.python.org/setuptools/issue32&lt;/a&gt;) but I have lost all
    my hopes to see this project moving forward lately.
-   &lt;strong&gt;Brainstorming&lt;/strong&gt;: try to understand the &lt;strong&gt;Python Packaging
    Paradox&lt;/strong&gt;. That is = how come the community, which is composed of
    many briliant people, is unable to move forward in packaging
    matters.
-   &lt;strong&gt;&lt;em&gt;Distribute the return&lt;/em&gt;&lt;/strong&gt; :D&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Wed, 26 Nov 2008 00:16:00 +0100</pubDate><guid>http://blog.ziade.org/2008/11/26/python-package-distribution-my-current-work/</guid></item><item><title>How to be disappointed with the &amp;quot;printed&amp;quot; in &amp;quot;printed book&amp;quot;</title><link>http://blog.ziade.org/2008/11/22/how-to-be-disappointed-with-the-quotprintedquot-in-quotprinted-bookquot/</link><description>&lt;p&gt;I feel really bad about this comment on my book : &lt;a href="http://techblog.ironfroggy.com/2008/11/how-to-be-dissappointed-in-something.html"&gt;How To Be
Dissappointed in Something You Recommend&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Just a quick word about the try, return finally code pattern, since I
had some feedback about it. I would like to mention that this code
pattern is perfectly right: &lt;br /&gt;
   def function():&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;try:&lt;/span&gt;

&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;something&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;finally:&lt;/span&gt;

&lt;span class="err"&gt;     &lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="n"&gt;something&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;I should have explained it better, because this pattern is not used a
lot by people, so you can think that "do something" is called after the
return of the function, which is not the case. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For the typos now: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The first thing I did wrong&lt;/strong&gt;: when I started the book, I wanted, as
I did in &lt;a href="https://www.amazon.fr/dp/2100508830"&gt;my previous book&lt;/a&gt;, to run unit tests on the book itself to
avoid those mistakes. That said, the previous one was in Latex, which is
quite simple to interact with, and this one is in OpenOffice, because
that is how the editor works. I had to write a script to extract the
Python code from the Ooo file, to unit test it. I didn't. I simply ran
out of time, as usual when you have deadlines on books. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The second thing I did wrong&lt;/strong&gt;: I should have told the editor to wait
a bit, I didn't. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But Packt does Print On Demand, so I know that the Errata page I am
maintaining here : &lt;a href="http://atomisator.ziade.org/wiki/Errata"&gt;http://atomisator.ziade.org/wiki/Errata&lt;/a&gt;, is being
processed by the editor, and that the typos will be removed from the
book at some point, without having to wait for a second edition. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I'll update this blog entry as soon as I know the status on this. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I am really sorry &lt;a href="http://techblog.ironfroggy.com"&gt;Calvin&lt;/a&gt;, and all the people that are suffering
from these typos.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sat, 22 Nov 2008 17:23:00 +0100</pubDate><guid>http://blog.ziade.org/2008/11/22/how-to-be-disappointed-with-the-quotprintedquot-in-quotprinted-bookquot/</guid></item><item><title>How to receive email alerts when someone talks about something - 6 steps tutorial using Atomisator</title><link>http://blog.ziade.org/2008/11/08/how-to-receive-email-alerts-when-someone-talks-about-something-6-steps-tutorial-using-atomisator/</link><description>&lt;p&gt;I like Google Alert, the idea of receiving a mail every day that
summarizes all articles related to a given topic is really helpfull when
you need to focus on a specific subject for a while. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But this is not enough. I want to receive a mail that points me to any
mailing list or planet feed or blogs out there as well, that talks about
the topic. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;You can't do it with Google Alerts as far as I know. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Let's take an example: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I want to receive a daily mail that points me to any mail thread or
blog entry, that is related to the word "buildout" or to the word
"pycon".&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Basically, to do it manually, I need to read Planet Python, Planet
Zope, then take a look at the Python, Zope and Plone mailing lists. It
takes at least 10 minutes, and more if you want to read all entries to
make sure you won't miss anything. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Since online systems like &lt;a href="http://www.nabble.com/"&gt;Nabble&lt;/a&gt; provides RSS feed for mailing
lists (don't find yours ? just add it there !), it is easy to read them
as they where regular feeds. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;From there, a script that reads all the selected feeds and sends a mail
pointing to the entries that match the selected words is simple to write
as well, and fill the need. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But don't code it : &lt;a href="http://atomisator.ziade.org"&gt;Atomisator&lt;/a&gt; will let you do this with a few
lines of configuration. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here's a step-by-step tutorial. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 1 - install easy_install&lt;/strong&gt; &lt;br /&gt;
-   download ez_setup :
    [http://peak.telecommunity.com/dist/ez_setup.py][]
-   run it with your Python interpreter&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 2 - install Atomisator and SQLite &lt;br /&gt;
&lt;/strong&gt; &lt;br /&gt;
-   run the command : &lt;em&gt;easy_install Atomisator&lt;/em&gt;
-   make sure SQLite is installed on your system. If not, install it:
    &lt;a href="http://www.sqlite.org/download.html"&gt;http://www.sqlite.org/download.html&lt;/a&gt;* &lt;br /&gt;
    *&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 3 - create an "atomisator.cfg" file&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The content of the file has to be: &lt;br /&gt;
&lt;a href="http://atomisator.ziade.org"&gt;atomisator&lt;/a&gt;&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;entries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;false&lt;/span&gt;

&lt;span class="n"&gt;sources&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;

  &lt;span class="n"&gt;rss&lt;/span&gt; &lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nabble&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;Python&lt;/span&gt;&lt;span class="o"&gt;---&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;f2962&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt;

  &lt;span class="n"&gt;rss&lt;/span&gt; &lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;n2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nabble&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;Plone&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;f293351&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt;

  &lt;span class="n"&gt;rss&lt;/span&gt; &lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nabble&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;Zope&lt;/span&gt;&lt;span class="o"&gt;---&lt;/span&gt;&lt;span class="n"&gt;General&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;f6715&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt;

  &lt;span class="n"&gt;rss&lt;/span&gt; &lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;planet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;rss10&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt;

  &lt;span class="n"&gt;rss&lt;/span&gt; &lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;zope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/Planet/&lt;/span&gt;&lt;span class="n"&gt;planet_rss10&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;filters =&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;buzzwords&lt;/span&gt; &lt;span class="n"&gt;words&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;txt&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;outputs =&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This file will look into Planet Python, Planet Zope and various mailing
lists (Python, Plone, Zope). Of course you can add or remove feeds in
the &lt;strong&gt;sources&lt;/strong&gt; option. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 4 - Create the words.txt file&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This file contains regular expressions, one per line, that will be used
to match the entries. The file has to be saved besides&lt;em&gt; atomisator.cfg&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;For our example: &lt;br /&gt;
   buildout&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;pycon&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;You can put any expression you want in this file, as long as you have
one matching expression per line. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 5 - add an email.cfg configuration file. &lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This is where you define the target emails that will receive the alerts
(&lt;strong&gt;tos&lt;/strong&gt; option). You can also specify the &lt;strong&gt;from&lt;/strong&gt; email, or the smtp
server location. The file has to be saved besides&lt;em&gt; atomisator.cfg&lt;/em&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In our case it can be: &lt;br /&gt;
   [email]&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;tos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tarek&lt;/span&gt;&lt;span class="nv"&gt;@ziade&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;

&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tarek&lt;/span&gt;&lt;span class="nv"&gt;@ziade&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;

&lt;span class="n"&gt;smtp_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;smtp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;neuf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Step 6 - Run it !&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The command to be called is &lt;strong&gt;atomisator&lt;/strong&gt; (installed by easy_install)
followed by the configuration file: &lt;br /&gt;
   $ atomisator atomisator.cfg&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;Reading&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;Launching&lt;/span&gt; &lt;span class="n"&gt;worker&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;rss&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;http://www.nabble.com/Python---python-list-f2962.xml&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt;

&lt;span class="n"&gt;Launching&lt;/span&gt; &lt;span class="n"&gt;worker&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;rss&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;http://n2.nabble.com/Plone-f293351.xml&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt;

&lt;span class="n"&gt;Launching&lt;/span&gt; &lt;span class="n"&gt;worker&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;rss&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;http://www.nabble.com/Zope---General-f6715.xml&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt;

&lt;span class="n"&gt;Launching&lt;/span&gt; &lt;span class="n"&gt;worker&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;rss&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;http://planet.python.org/rss10.xml&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt;

&lt;span class="n"&gt;Launching&lt;/span&gt; &lt;span class="n"&gt;worker&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;rss&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;http://www.zope.org/Planet/planet_rss10.xml&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt;

&lt;span class="n"&gt;Retrieving&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;rss&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;http://www.nabble.com/Python---python-list-f2962.xml&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt;

&lt;span class="n"&gt;Retrieving&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;rss&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;http://www.nabble.com/Zope---General-f6715.xml&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt;

&lt;span class="n"&gt;Retrieving&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;rss&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;http://n2.nabble.com/Plone-f293351.xml&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt;

&lt;span class="n"&gt;Retrieving&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;rss&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;http://planet.python.org/rss10.xml&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt;

&lt;span class="n"&gt;Retrieving&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;rss&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;http://www.zope.org/Planet/planet_rss10.xml&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt;

&lt;span class="o"&gt;.................................................................................................................................................&lt;/span&gt;

&lt;span class="n"&gt;Writing&lt;/span&gt; &lt;span class="n"&gt;outputs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;Data&lt;/span&gt; &lt;span class="n"&gt;ready&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Check your mails. This call can be put in a daily cron. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Tested under Mac OS X and Linux.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sat, 08 Nov 2008 12:38:00 +0100</pubDate><guid>http://blog.ziade.org/2008/11/08/how-to-receive-email-alerts-when-someone-talks-about-something-6-steps-tutorial-using-atomisator/</guid></item><item><title>Plone Conference 2008 in Washington D.C. - summary</title><link>http://blog.ziade.org/2008/11/06/plone-conference-2008-in-washington-dc-summary/</link><description>&lt;p&gt;I am back from the Plone Conference in D.C., and the jetlag is gone. The
jetlag is gone for weeks now but it's hard to find the time to blog
these days :/ &lt;br /&gt;
&lt;/p&gt;
&lt;h2&gt;On the talks I have seen and topics I have chatted about&lt;/h2&gt;
&lt;p&gt;There were a lot of great talks in D.C., and it was hard to decide
which one to look at. In any case it was easy to meet the speaker if I
had missed the talk, because the Plone Conference, unlike big
conferences like OSCON, is a place where everyone hangs around the same
spot after a talk is over. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here's a list of some topics I have seen or I have talked about with
some people. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Deliverance - Ian Bicking&lt;/h3&gt;
&lt;p&gt;If you look at what Ian has produced in the past 5 years, he is one of
the most prolific contributor of tools that become standards in the
Python web development web community. Think about Python Paste or
virtualenv, and many others. Deliverance might be the next big one. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Take a bunch of micro web applications you want to join to build a full
web system, for historical reasons or just because you believe a
particular feature just won't fit in Plone but will do great in Pylons.&lt;/p&gt;
&lt;p&gt;Now ask a designer to glue everything together under the same look. He
(or the guy that integrates his design) will probably hates you: he will
have to learn how to integrate in heterogeneous environments. This is
easy under some systems that let you stick a layout and a css in a
simple way. This is not easy under Plone, unless you learn how to do it
(but this will be improved in the future). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Deliverance is a proxy that let you skin any application that spits
html content, by running some XPATH rules on the content and applying
some changes to produce a new output. Basically, you have a simple html
page that just provides the layout you want to have, without any
content, and a xml file that explains how to extract some content from
the page produced by the third-party application and where to inject it
in your empty html page. The great thing is that you can call different
third-party servers given the path you are in, and even call several
servers to build one single page. This opens a lot of perspectives. &lt;br /&gt;
&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;The first caveat of this approach is that you have to provide a&lt;/dt&gt;
&lt;dt&gt;Single-Sign On feature to avoid people having to connect several times.&lt;/dt&gt;
&lt;dt&gt;This can be a problem sometimes with some applications if they are not&lt;/dt&gt;
&lt;dt&gt;open enough to let you do it. But most of the time, it is not a problem&lt;/dt&gt;
&lt;dd&gt;if the users are all located in a LDAP it is easiy. &lt;br /&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;Furthermore, if you use only Python-based applications, you can use a
WSGI envrionment and a middleware like repoze.who to glue together let's
say, a Plone app and a Pylons app. Products.oopas is the PAS plugin that
can be used for that on Plone side to grab the authentication context
and use it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The second problem I can see is about response headers. One example: if
a page is composed of elements that comes from several pages, and if the
page has a Last-Modifier header, I don't think Deliverance handles this
correctly yet, to make sure to present the newest Last-Modified header
from all third-party servers that where called to build that page. But
this more likely to be a detail compared to the single authentication
problem. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In any case this is a very promising tool ! &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Content Mirror - Kapil Thangavelu&lt;/h3&gt;
&lt;p&gt;I didn't see that talk, but I have talked about this tool with a few
people. The idea is to serialize the content of a Plone instance into a
relational database (eg Postgresql), as it happens, using events. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I need to give a try and check it deeper, to see how the overhead is
dealt, and how the aggregator I have read about is doing (it collects
mirorring operations to perform in a transaction, and optimize the calls
at the end of the transaction to avoid redudant calls if I understood
correctly). I don't know yet for example if there's a pool of jobs for
the mirroring tasks to avoid a point of failure. But I am pretty sure
this is taking care of. The other point I need to see if there's a round
trip. e.g. if there's a way to apply a relational database change back
into Plone. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But in any case I can already see various use cases for my customers.
For instance, having a plone instance as a back office, with complex
workflows for editors and contributors, and a lightweight Pylons
application as the front application, that concentrates into displaying
the relational database as fast as possible, makes a lot of sense in big
environments. It just scales better. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So this is a interesting tool as well. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;repoze.bfg - Chris McDonough&lt;/h3&gt;
&lt;p&gt;Chris gave a talk about repoze.bfg, which is a new web framework that
takes back the good bits from Zope and push them into a WSGI world,
using the Pylons approach I would say. That is : "here's the template
engine you can use in repoze, but really, use the one you like". &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Frankly, I am really seeing this new effort as one of the most
promising one in the Zope community. Already, repoze.auth is a major
middleware in WSGI : Zope's Pluggable Authentication Service outside
Zope, usable with any WSGI application. This is a blast ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;And people are starting to contribute a lot of interesting middlewares
under the repoze namespace. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Now I didn't really try repoze.bfg itself yet, but given the people
that are behind it, I am pretty sure this framework will meet success in
the future. Having a MVC framework ala Pylons that let you use Zope
packages with a "this zope package is repoze/wsgi compliant" label on
each one of them is very cool. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;collective.indexing - Andreas Zeidler and al&lt;/h3&gt;
&lt;p&gt;At the snow sprint, we worked with the Enfold crew that did a great
work in integrating the Solr/Lucene system so it can be used from Plone.
We replaced a few fields like the searchable text and indexed it on Solr
side, just to give it a try. The snow work was really focusing on
providing a buildout, a few recipes and a bench to say : "Hey, Plone
community, this is a blast ! let's do more of it" &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Later Andreas Zeidler and a few other guys continued the work on
indexing matter and they delivered collective.indexing, which provides
two things: &lt;br /&gt;
-   a queue that collects all indexing to be done, and optimize the call
    to the catalog
-   a bridge to use collective.solr&lt;/p&gt;
&lt;p&gt;I didn't follow the latest development and I didn't know how far the
guys went, but I had the chance to hang around with Andreas and Tom
Lazar in D.C., so now I know that this package is production ready :D &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So in other words : I'll probably use it as a mandatory package for all
the big plones out there. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The queuing part imho, should go into the catalog itself because
there's no other way to make sure a third-party product is not calling
the catalog during the transaction wile another product does the same. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Server-Side Include (SSI)&lt;/h3&gt;
&lt;p&gt;Tom Lazar worked during the Snow Sprint on lovely.remoteinclude to make
Plone portlets accessible via unique URLs. From there, it is possible to
push a page that contains a list of urls rather than the calculated
page, to a front server that knows how to read SSI directive, and builds
the page. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This is great for performances, and is a lot like ESI (Edge Side
Include) we use to have in CPSSkins. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I am wondering if both could be implemented in the same tool in fact. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Tom told me that he will try to continue this work at the performance
sprint in Bristol in december, so let's keep an eye on this ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;I have seen many other talks and topics, but these few ones where the
ones I really needed to talk about.&lt;/em&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;h2&gt;On the conference organization&lt;/h2&gt;
&lt;p&gt;I am helping in the organization of Pycon FR in Paris since 2 years
now. I know what is means to organize such events : it is a LOT OF WORK.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;You know when an event is well organized when you don't feel it is
organized. &lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;That was the case in D.C. Bravo Alex, Amy and all the others ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The only problem (wifi) was not the organizers fault, and I have never
been to any event where it is not cahotic at some point (besides OSCON)
so... :) &lt;br /&gt;
&lt;/p&gt;
&lt;h2&gt;On the community&lt;/h2&gt;
&lt;p&gt;I love you all guys. It is an amazing community.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 06 Nov 2008 16:54:00 +0100</pubDate><guid>http://blog.ziade.org/2008/11/06/plone-conference-2008-in-washington-dc-summary/</guid></item><item><title>PloneConf&amp;#039;08 slides + screencasts : delivering applications with zc.buildout and a distributed model</title><link>http://blog.ziade.org/2008/10/28/ploneconf03908-slides-screencasts-delivering-applications-with-zcbuildout-and-a-distributed-model/</link><description>&lt;p&gt;I was totally drowned into some customer projects since I came back from
the Plone Conference. But things are looking better now, so I can take a
bit of time to start blogging about the conference. I'll probably do
three blog posts: this one about my tutorial, the next one about the
conference itself and last, an entry about the sprints. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So I gave a tutorial about zc.buildout. The length was a bit
challenging, since I had 90 minutes. Enough time to explain more things
than in a regular talk, but not enough time to get into great details,
as a tutorial should be. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The other thing was about the topic: two talks were covering
zc.buildout, Clayton Parker's one and mine. So my goal was to make sure
they were not overlapping too much. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I had the chance to meet Clayton before we both gave our talk, since
the Six Feet Up crew gave me shelter in the house they rented (nicest
guys in the block). Even if we didn't exchange a lot on the slides
themselves, I could figure what Clayton was going to present. So I....
started my slides from scratch two days before my tutorial and worked
carefully on their scope :D &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Alex Clark came and backed me up during the talk, since we are working
together one plone.org for months now and since the talk presented the
new plone.org that is coming up. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I think I did quite well during the talk, because we had a pause half
way, and when we started back, the room stayed full ;) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This is the second time I record all the console work in small
screencasts, to avoid live problems, and I think this is the best way to
go if you need to do some demos, so I'll keep on doing it. Plus, it's
nice to provide them to people after the talk. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Anyway, the talk was videotaped so you can to judge by yourself: &lt;br /&gt;
-   &lt;a href="http://209.222.144.36/plone2008/fri_tarek_ziade.asf"&gt;The video&lt;/a&gt;
-   &lt;a href="http://broadcasturban.net/webcast/plone2008/fri_ziade.htm"&gt;The&lt;/a&gt;&lt;a href="http://www.slideshare.net/tarek.ziade/delivering-applications-with-zcbuildout-and-a-distributed-model-plone-conference-2008-presentation/"&gt;slides&lt;/a&gt;&lt;a href="http://broadcasturban.net/webcast/plone2008/fri_ziade.htm"&gt;.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And everytime you see a "get the screencast at
&lt;a href="http://ziade.org/ploneconf"&gt;http://ziade.org/ploneconf&lt;/a&gt;" in there, well get them :) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In detail: &lt;br /&gt;
1.  &lt;a href="http://ziade.org/ploneconf/01-distutils.mov"&gt;Distutils demo&lt;/a&gt;
2.  &lt;a href="http://ziade.org/ploneconf/02-setuptools.mov"&gt;setuptools demo&lt;/a&gt;
3.  &lt;a href="http://ziade.org/ploneconf/03-collective.eggproxy.mov"&gt;collective.eggproxy demo&lt;/a&gt;
4.  &lt;a href="http://ziade.org/ploneconf/04-psc-install.mov"&gt;PloneSoftwareCenter installation&lt;/a&gt;
5.  &lt;a href="http://ziade.org/ploneconf/05-collective.dist.mov"&gt;collective.dist demo&lt;/a&gt;
6.  &lt;a href="http://ziade.org/ploneconf/06-new.plone.org.mov"&gt;new.plone.org demo&lt;/a&gt;
7.  &lt;a href="http://ziade.org/ploneconf/07-collective.releaser.mov"&gt;collective.releaser demo&lt;/a&gt;
8.  &lt;a href="http://ziade.org/ploneconf/08-plone3_buildout.mov"&gt;plone 3 buildout demo&lt;/a&gt;
9.  &lt;a href="http://ziade.org/ploneconf/09-multiple-releasing.mov"&gt;Multiple target releasing demo&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;What's next ?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;We need to finish the work with Alex on plone.org. It's not hard, it
    just takes time, and we both are quit busy in our jobs :)&lt;/li&gt;
&lt;li&gt;I need to polish collective.releaser and collective.eggproxy. They
    are &lt;em&gt;brut de fonderie&lt;/em&gt;, and the code suck a bit. If you are using
    them and want to help, or have some feedback/issues, please, pretty
    please, let me know.&lt;/li&gt;
&lt;/ul&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 28 Oct 2008 15:37:00 +0100</pubDate><guid>http://blog.ziade.org/2008/10/28/ploneconf03908-slides-screencasts-delivering-applications-with-zcbuildout-and-a-distributed-model/</guid></item><item><title>Distribute sprint has started in D.C. (setuptools, distutils, PyPI)</title><link>http://blog.ziade.org/2008/10/11/distribute-sprint-has-started-in-dc-setuptools-distutils-pypi/</link><description>&lt;p&gt;Just a quick note: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;We have started the sprint in D.C. on packaging matters. Our tasks for
the moment: &lt;br /&gt;
-   re-read all the threads in &lt;a href="http://mail.python.org/pipermail/distutils-sig/"&gt;distutils-ML&lt;/a&gt; since mid-september.
-   write a short list of actions that can be done NOW in distutils,
    setuptools and PyPI
-   write a short list of writing that can be done to define a "new"
    tool&lt;/p&gt;
&lt;p&gt;Please, join at any time in #distutils in freenode, and ping us, if
you want to get involved.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sat, 11 Oct 2008 15:05:00 +0200</pubDate><guid>http://blog.ziade.org/2008/10/11/distribute-sprint-has-started-in-dc-setuptools-distutils-pypi/</guid></item><item><title>Plone shirt for my funders</title><link>http://blog.ziade.org/2008/10/02/plone-shirt-for-my-funders/</link><description>&lt;p&gt;[caption id="" align="alignnone" width="400" caption="JJ shirt"]&lt;img alt="JJ
shirt" src="http://1.bp.blogspot.com/_rrgRZwGYA4M/SOMQZfDyinI/AAAAAAAAAEY/GCAzH0Ys-JM/s400/ipv6.jpg" /&gt;[/caption] &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So I promised I would create a shirt for the top #5 contributors of my
fund raising. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I think I found an idea. &lt;a href="http://jjinux.blogspot.com/2008/09/ipv6-t-shirt.html"&gt;My friend JJ is wearing a shirt&lt;/a&gt; I made on
the picture you see. That's the phrase we wanted me to put on the shirt:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;There's no place like 127.0.0.1 &lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;(except maybe ::1)&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That is one of the geekiest shirt I know of :D &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Anyway, I want to borrow the idea for the plone shirt, and write,
beside the Plone logo: &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;There's no place like 127.0.0.1 &lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;(except maybe #plone on freenode)&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That is, to express the fact that the Plone community is a really
friendly one, and it's great to be part of it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;EDIT: I have better proposals from the two Alex: &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;$ whois plone&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;127.0.0.1&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;and: &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;There is no place like Plone&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;and: &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Plone, I wish I knew how to quit you&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;and: &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Did you mean Plone ?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I am hesitating now.... :)&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;JJ shirt&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 02 Oct 2008 01:22:00 +0200</pubDate><guid>http://blog.ziade.org/2008/10/02/plone-shirt-for-my-funders/</guid></item><item><title>Simplifying the commenting system</title><link>http://blog.ziade.org/2008/09/26/simplifying-the-commenting-system/</link><description>&lt;p&gt;They are many commenting systems out there for Plone, Zope 3, etc. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But let's take this simple statement : why a content provider like any
Zope-based application should deal with comments ? A comment is a simple
piece of text, that refers to a page and maybe to another comment. There
are no workflow needed in most cases. Maybe an authorization required to
add the comment but that's it. And how do you provide a comment on a
page that is not generated by your web server, but that is displayed
somewhere to your users nevertheless ? (like a static page) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So, looking at how &lt;a href="http://www.openplans.org/projects/deliverance/introduction"&gt;Deliverance&lt;/a&gt; works, and because I want to have
comments on Sphinx pages, I thaught of a simple way to deal with
comments: A WSGI Middleware/Proxy. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The comment application stores comments in a SQL DB and use the pages
id (the full URL) as references. I can even use services like Akismet to
avoid spams. Then when the page is displayed, I can inject the comments
associated to it on-the-fly, and provide a JS-enabled form for people to
add comments (which can be caught when submited by the middleware too I
guess, or sent through Ajax to a small JSON-enabled service that
listens) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This is perfect for an hybrid environment where you have pages
generated by various web applications. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Of course there are some caveats, like if the page is gone, or has
moved. But dealing with orphan comments should be doable. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;We might try that out soon for &lt;a href="http://afpy.org"&gt;Afpy.org&lt;/a&gt; :)&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 26 Sep 2008 16:05:00 +0200</pubDate><guid>http://blog.ziade.org/2008/09/26/simplifying-the-commenting-system/</guid></item><item><title>Distribute, end of the fork -- or the start of a new hope</title><link>http://blog.ziade.org/2008/09/26/distribute-end-of-the-fork-or-the-start-of-a-new-hope/</link><description>&lt;p&gt;&lt;em&gt;(This post is a work in progress, as things still evolve&lt;/em&gt;) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In a way, I am glad I have made a fork, and I am glad this is going to
be the shortest fork ever. A lot of people reacted on my proposal and I
could get a very clear picture of what is wrong in the
distutils/setuptools world, and how I could help on this. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This is how I interpret it: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Guido explained that the mistake made was not to integrate setuptools
in the core from the very begining. The current distutils code did not
evolve in the meantime and Phillip Eby worked on setuptools to make it
evolve. setuptools now reaches a point where it needs other contributors
to fit all the needs and solve more problems. It had one contributor in
the past, (Jim Fulton) but it needs more. It also became a mandatory
package for many folks in the Python world because Phillip did a great
work on it : it solves most problems. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;From what I could see, all the people that gets frustrated at some
point with setuptools, either don't use it, either try to see how it
could be changed. But when they try that, they take the same paths
others have taken before because there is a lack of info and
documentation on setuptools current status and future. And these paths
are quite long for the brain before you are able to provide a
well-thaught idea or patch. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I have tried to help myself on setuptools, then I have started to blame
Phillip saying that he did not have enough time to make setuptools
evolve. But that was the wrong approach, because the only problem
really, is a lack of visibility in setuptools development. And this is
something that can be fixed quite easily I believe. The bug tracker
added some months ago helped a lot. A roadmap and one or two page around
it and that should be it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Guido also stated that it was merely impossible to work on the next
generation of distutils outside Python core. But in the meantime this
means that we need some people from the core to help us in there. And
they are really busy (I can understand that) on other matters in Python
code. We can write patches for some fixes for sure, and there are
hundreds of them to do in distutils. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But to boost it we would need a core developer that gives some love to
that package. I mean, for example &lt;a href="http://bugs.python.org/issue2461"&gt;I still have a patch waiting for
reviewing&lt;/a&gt;, wich only adds some unit tests that are lacking. There is
no code there, just unit tests, and it is pending since 6 months...
Guido said that I could try to become a core developer, that this was
not impossible, if I started to contribute patches often to other parts
of Python as well. But that's quite a challenge. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;What frustrated me is that some people like Jim Fulton said they were
willing to work on a new distutils from scratch, and in the meantime I
understand what Guido says. he pointed us to Joel entry on &lt;a href="http://www.joelonsoftware.com/articles/fog0000000069.html"&gt;rewriting
from scratch&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;My Plans&lt;/h3&gt;
&lt;p&gt;I don't have the brain to drive a fork of setuptools at this time. I
knew it from the beginning, I just wanted to make people react. I think
that setuptools can be the laboratory where we can experiment things,
and distutils the place where we can apply them at some point, with
Phillip help because he has a pretty big overview of all that. It is
going to take years, but well, we are young. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;To help on this I will: &lt;br /&gt;
-   try to gather people at the PloneConf to talk about it, maybe even
    sprint
-   try to document setuptools and distutils at my level, and write a
    roadmap for people to know what is going on. Either through a Sphinx
    site either on Python.org wiki.
-   work on patches for distutils, and try to point things in setuptools
    that might be brought into distutils
-   try to see if I have the brain to understand and work on other parts
    of Python.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 26 Sep 2008 08:22:00 +0200</pubDate><guid>http://blog.ziade.org/2008/09/26/distribute-end-of-the-fork-or-the-start-of-a-new-hope/</guid></item><item><title>Expert Python Programming book, more details + sample chapter</title><link>http://blog.ziade.org/2008/09/24/expert-python-programming-book-more-details-sample-chapter/</link><description>&lt;p&gt;It seems that my book is now officialy available, so I should give more
details about its content. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Who should get this book ?&lt;/h3&gt;
&lt;p&gt;I think Shannon described the targetship of my book quite well in the
foreword: &lt;br /&gt;
*If you're looking to progress from knowing Python to mastering Python,
this is the &lt;br /&gt;
book for you. In fact, this is exactly the type of book I wish I had
had ﬁve years ago. &lt;br /&gt;
What took me years to discover by steadfastly attending talks at PyCon
and my local &lt;br /&gt;
Python users' group is now available in a succinct book form. * &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;What is means is that this book does not only focus on Python syntax,
but also covers how to use Python in a professional environment. Beyond
writing a program that works, a good Python developer uses continous
integration principles and tries to think about the maintainability of
his code. Taking care of choosing good names for instance, will
naturally make the code better. Test-Driven Development make the code
better too. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The book tries to synthesize these good practices and explain why they
are good. So if you are using Python and know how to write a program,
and want to push it further, this is a book for you. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;On the code&lt;/h3&gt;
&lt;p&gt;All the code of the book lives here:&lt;a href="http://atomisator.ziade.org"&gt;http://atomisator.ziade.org&lt;/a&gt;. So
it can evolve. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;On my English&lt;/h3&gt;
&lt;p&gt;I am French. My english is far from being perfect. The Packt team did a
great work on improving it, but you will probably feel my french touch
in the book. I hope you won't mind. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;On the structure&lt;/h3&gt;
&lt;p&gt;Depending on your needs, you might feel that the ordering of the
chapters is not what you were expecting. The current ordering is the one
I would use when writing an application, but this is my own vision. If
you are a manager you probably have your own way. I worked this out with
one concern in mind : every chapter is independant. So feel free to jump
to any chapter when you have finished one. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Sample chapter&lt;/h3&gt;
&lt;p&gt;I am giving away Chapter 10, as an appetizer: &lt;a href="http://tarekziade.files.wordpress.com/2008/09/chapter-10.pdf"&gt;chapter 10 of "Expert
Python Programming"&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This chapter is about documentation. It gives principles and good
practices to document your Python projects. It is an invitation to use
Sphinx and reStructuredText, together with a set of good practices. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;More details on each chapter&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Chapter 1&lt;/strong&gt;, Getting Started. I took me a long time to decide whether
I should drop or not this chapter. It describes how to install an
environment to work with Python. Since the book is for people that
already knows how to use Python a bit, it seemed out of topic. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;A few things convinced me to let it in: I have a friend that works with
Python under Windows for years, but don't know anything about distutils
or setuptools and how to set the proper MinGW environment to be able to
install packages without having any trouble. Secondly: I am making some
assumptions there for the rest of the book examples to work and I am a
setuptools fan. Last but not least: this is a small chapter compared to
the size of the book (less than 10%) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Chapter 2, Syntax Best Practices—Below the Class Level&lt;/strong&gt;, I had a lot
of fun there. I am talking about topics like coroutines and contextlib.
Pure Python joy ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Chapter 3&lt;/strong&gt;, &lt;strong&gt;Syntax Best Practices—Above the Class Level&lt;/strong&gt;, This
chapter explains amongst other topics why super() is dangerous, how the
MRO works, meta-programming, etc. Lots of fun too. I think people will
enjoy it a lot. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Chapter 4&lt;/strong&gt;, &lt;strong&gt;Choosing good names&lt;/strong&gt;, Writing a program that works is
a good thing. Writing a program that can evolve and that is
comprehensible by other developers is harder. This chapter will give you
some clues on choosing good names, beyond the PEP8 guide, and how to
organize and build a modular application by working out the API. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Chapter 5&lt;/strong&gt;, &lt;strong&gt;Writing a package&lt;/strong&gt;. The main ideas here are: write
and distribute all your packages the same way, so use templates and
distutils. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Chapter 6&lt;/strong&gt;, &lt;strong&gt;Writing an application&lt;/strong&gt;. Same as chapter 5, but at
the application level. This field is very different depending on what
frameworks you use and what community you are part of. I tried to come
up with something that seems to be the way all developers are tending to
take. Maybe I am a bad visonnary, maybe I am wrong. I don't think I am.
If you disagree, you will still find interesting stuff in there.I am
presenting a micro case study to make things clearerild a modular
application by working out the API. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Chapter 7&lt;/strong&gt;,&lt;strong&gt; Working with zc.buildout&lt;/strong&gt;. Buildout is widely use in
Plone and Zope. There are other tools out there of course. But buildout
rocks imho. This chapter show how it work and how to use it to work and
distributeyour application. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Chapter 8&lt;/strong&gt;,&lt;strong&gt; Managing code&lt;/strong&gt;. Its starts with a state-of-the-art of
version control systems and continuous integration principles. I am
explaining how you can work using mercurial and buildbot. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Chapter 9&lt;/strong&gt;,&lt;strong&gt; Managing lifecyle&lt;/strong&gt;. Same as chapter 8, but focusing
on software lifecycles, and why the iterative approach rocks. Also show
how trac can be used. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Chapter 10&lt;/strong&gt;,&lt;strong&gt; Documenting your projects, &lt;/strong&gt; Just read it :) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Chapter 11&lt;/strong&gt;,&lt;strong&gt; Test-Driven Development, &lt;/strong&gt;This is my TDD manifesto
:) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Chapter 12 and 13&lt;/strong&gt;,&lt;strong&gt; Optimisation,&lt;/strong&gt; Ever wondered how to calculate
the complexity of the code ? how to benchmark and optimize ? &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Chapter 14,&lt;/strong&gt; &lt;strong&gt;Useful design patterns,&lt;/strong&gt; Design patterns revisited,
because the GoF did not know enough about Python :) &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Feedback !&lt;/h3&gt;
&lt;p&gt;It is really frustrating in some way to write a printed book on a topic
like Python. By the time I have finished the book and gave it back to
the editor, I was already thinking on some changes and some improvments
I could make. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I created a website for the readers : &lt;a href="http://atomisator.ziade.org"&gt;http://atomisator.ziade.org&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;You can add a ticket there for the v2 !&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Wed, 24 Sep 2008 16:26:00 +0200</pubDate><guid>http://blog.ziade.org/2008/09/24/expert-python-programming-book-more-details-sample-chapter/</guid></item><item><title>Distribute, a setuptools fork.</title><link>http://blog.ziade.org/2008/09/24/distribute-a-setuptools-fork/</link><description>&lt;p&gt;&lt;a href="http://tarekziade.wordpress.com/2008/09/26/distribute-end-of-the-fork/"&gt;&lt;strong&gt;EDIT: Read this new entry&lt;/strong&gt;&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;That's enough ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Setuptools is now a big part of our Python development and release
infrastructure. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But we have been struggling with the setuptools development process for
months around here. The project is run by one single man, who is really
busy on other things as well. I am not blaming him, he's doing a great
job. I am just saying that setuptools needs to be more open. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Yesterday, I had, like the week before, some developers complaining
about some incompatibility between setuptools and Subversion 1.5. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It is not like it is a hard to fix, and as a matter of fact it is fixed
now in the trunk of the project. But not released since quite a long
time now. So I have to ask my developers to checkout the trunk on their
buildout, and use the ==dev tag elsewhere. I proposed some help to do
the release, but my mails were just ignored. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;There are also a lot of improvments to do in this tool, so Python gets
the distribution tool it deserves. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But having one single person with the commiter rights is not possible
on such an important package for the community. It has to be
community-driven. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Enough talking, I am launching a setuptools fork, which will be
community-driven, and wich will remain 100% compatible with setuptools
at this point. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;You can react, of follow the reaction on the distutils-SIG I started
here :
&lt;a href=""&gt;http://mail.python.org/pipermail/distutils-sig/2008-September/010031.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Maybe my attempt to make such a tool belong to the Python community
will fail, I don't know. Maybe people will not like seeing someone
forking like that and will continue to sleep and to unsubsribe
themselves from the distutils-SIG. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But at least I am trying something because Python is suffering from a
lack of a good, robust distribution tool at this point and there are a
lot of people that could contribute if the development process is more
open and the maintainer(s) more available and reactive. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I have contributed a bit in distutils in the past year, but the Python
core development team has a lack of interest in this part of Python at
this time. Thanks to some of the core team members, I could have some
patch applied in the trunk, like the multiple servers pypirc. But we
won't be able to create what I would call 'distutils 2' in Python itself
at this point. And setuptools seems to be the best candidate for a
distutils replacement in the future. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;As long as the community can work on it of course ! We are the
community, we are distributing large application to our customers. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So that's what "Distribute" is about.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//m&lt;/span&gt;&lt;span class="n"&gt;ail&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/pipermail/&lt;/span&gt;&lt;span class="n"&gt;distutils&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;sig&lt;/span&gt;&lt;span class="sr"&gt;/2008-September/&lt;/span&gt;&lt;span class="mo"&gt;010031&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Wed, 24 Sep 2008 13:10:00 +0200</pubDate><guid>http://blog.ziade.org/2008/09/24/distribute-a-setuptools-fork/</guid></item><item><title>Annoucing collective.eggproxy, the smart PyPI mirror</title><link>http://blog.ziade.org/2008/09/24/annoucing-collectiveeggproxy-the-smart-pypi-mirror/</link><description>&lt;p&gt;I just wanted to announce the release of collective.eggproxy
(&lt;a href="http://pypi.python.org/pypi/collective.eggproxy/0.2.0"&gt;http://pypi.python.org/pypi/collective.eggproxy/0.2.0&lt;/a&gt;) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;collective.eggproxy is a smart mirror for PyPI, thaught and coded by my
colleague &lt;a href="http://zebert.blogspot.com/"&gt;Bertrand Mathieu&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It will collect packages on PyPI only when a program like easy_install
or zc.buildout asks for it. In other words, unlike some mirrors that act
like rsync and get the whole PyPI base (more than 5 gigas)
collective.eggproxy will only get what you need. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;At first run collective.eggproxy downloads pypi index and builds a page
of links. When a software asks for a specific package, version, etc.
collective.eggproxy downloads it if needed and stores it locally. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Want to give a try ? try it in two lines with easy_install: &lt;br /&gt;
   easy_install collective.eggproxy&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;eggproxy_run&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;That's it !&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Wed, 24 Sep 2008 09:15:00 +0200</pubDate><guid>http://blog.ziade.org/2008/09/24/annoucing-collectiveeggproxy-the-smart-pypi-mirror/</guid></item><item><title>The WSGI era is here</title><link>http://blog.ziade.org/2008/09/19/the-wsgi-era-is-here/</link><description>&lt;p&gt;This is probably obvious for the people that uses &lt;a href="http://repoze.org/"&gt;Repoze&lt;/a&gt; or
&lt;a href="http://pylonshq.com/"&gt;Pylons&lt;/a&gt;, or early adopters in the &lt;a href="http://plone.org/"&gt;Plone&lt;/a&gt; world, but from a Plone
or a Zope developer perspective, you could live without it until now. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Now &lt;a href="http://www.wsgi.org/wsgi/"&gt;WSGI&lt;/a&gt; is everywhere. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I remember when Martijn Faassen brought the idea in 2006, of &lt;a href="http://faassen.n--tree.net/blog/view/weblog/2006/11/29/0"&gt;hooking
Grok and Zope 3 into the WSGI&lt;/a&gt;. Maybe someone else talked about it
before but that was the first time I could picture what WSGI could
bring. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Now with the work done by people like: &lt;br /&gt;
-   The repoze team, that made it easier to run a Plone-based
    application in WSGI
-   The &lt;a href="http://pythonpaste.org/script/"&gt;Paste Script / Paste Deploy&lt;/a&gt; team, that provided a simple way
    to describe a WSGI chain&lt;/p&gt;
&lt;p&gt;And major WSGI middlewares like : &lt;br /&gt;
-   &lt;a href="http://static.repoze.org/whodocs/"&gt;repoze.who&lt;/a&gt; which allows you to deal with authentication
    separately
-   &lt;a href="http://www.openplans.org/projects/deliverance/project-home"&gt;Deliverance&lt;/a&gt;, which let you theme any application and let this
    application focus on delivering a content
-   Things like &lt;a href="http://pypi.python.org/pypi/Beaker"&gt;Beaker&lt;/a&gt;, which let you use memcached for instance, to
    store session data and cache arbitrary things&lt;/p&gt;
&lt;p&gt;From a CTO point of view, a WSGI environment brings me the ability to
think about a web application and build it without having to stick into
one framework and try to bend all technologies inside it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For instance: &lt;br /&gt;
-   I can write a Plone application and use Beaker to deal with
    sessions, without having to wrap Memcached into a custom plone
    package.
-   I can ask a graphic designer to work on a CSS and a layout without
    having to do it into Plone. It's not that Plone design tools are
    bad, but the learning curve of writing a rule file in Deliverance
    and apply it to any piece of application makes the designer more
    productive than becoming a specialist of one skinning tool.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If my customer use &lt;a href="http://moinmo.in/"&gt;moinmoin&lt;/a&gt; as a Wiki, I can put it into my
    Plone site transparently by defining a composite section in my Paste
    configuraton file.&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You could do all the mentioned thing without WSGI, just by importing
the packages and/or dealing with proxies at Apache level. But that is
not the point. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The point is that WSGI brought the idea of making all web frameworks
and libraries interact together to build one web application. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It is not the silver bullet of course, but my gut feeling is that this
will create some kind of reunification in Python Web development
communities: people are starting to look at a wider range of package,
beyond the framework they use everyday.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 19 Sep 2008 11:30:00 +0200</pubDate><guid>http://blog.ziade.org/2008/09/19/the-wsgi-era-is-here/</guid></item><item><title>Paris Bobun Sprint</title><link>http://blog.ziade.org/2008/09/16/paris-bobun-sprint/</link><description>&lt;p&gt;I don't have time to talk in detail about it at this time, but I wanted
to say that the &lt;a href="http://www.openplans.org/projects/paris-bobun-sprint"&gt;Bobun Paris Sprint&lt;/a&gt; was a great event. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Together with the &lt;a href="http://www.openplans.org/projects/plone-3-paris-sprint/project-home"&gt;sprint&lt;/a&gt; we have done a few month before here at
AlterWay, this a great momentum for the French Plone community. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Those events will probably be recurrent from now on (the one we
organized at AlterWay will be organize every year for sure, and I think
the Bobun team wants to do it again as well). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Maybe we will be able at some point in a few years to organize the
PloneConf in Paris ? &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;That would be a blast :D&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 16 Sep 2008 09:30:00 +0200</pubDate><guid>http://blog.ziade.org/2008/09/16/paris-bobun-sprint/</guid></item><item><title>zc.buildout recipe to build your Sphinx doc</title><link>http://blog.ziade.org/2008/09/11/zcbuildout-recipe-to-build-your-sphinx-doc/</link><description>&lt;p&gt;&lt;a href="http://sphinx.pocoo.org/"&gt;Sphinx&lt;/a&gt; is now used by quite a few communities : &lt;a href="http://docs.python.org/dev/"&gt;Python&lt;/a&gt;,
&lt;a href="http://docs.djangoproject.com/en/dev/"&gt;Django&lt;/a&gt;, &lt;a href="http://docs.pylonshq.com/"&gt;Pylons&lt;/a&gt;, Grok (not sure about the current status), ... &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;No wonder, it's a blast. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;We are now starting to use it to produce customer documentation for our
buildout-based applications. Basically, a Sphinx structure is created in
the buildout using sphinx-quickstart, and a few tweaks are made so the
HTML and PDF outputs have a custom look. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Managing the documentation like the code makes life easier. This is one
of the basic rule of &lt;a href="http://www.agilemodeling.com/essays/agileDocumentation.htm"&gt;agile documentation&lt;/a&gt; : separate the content from
the layout, so you can provide documentation in any shape (html, pdf)
with a single source. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;To make things easier, I have released
&lt;a href="http://pypi.python.org/pypi/collective.recipe.sphinxbuilder/"&gt;collective.recipe.sphinxbuilder&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This recipe: &lt;br /&gt;
-   creates for you a Sphinx-based documentation in your buildout
-   creates a single script in the bin folder to build the documentation
    with one command
-   provides &lt;a href="http://pypi.python.org/pypi/collective.recipe.sphinxbuilder/#supported-options"&gt;an extensive set of options&lt;/a&gt; to drive Sphinx from
    buildout&lt;/p&gt;
&lt;p&gt;Adding it in your buildout is as simple as : &lt;br /&gt;
   [buildout]&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;parts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;

  &lt;span class="n"&gt;sphinx&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sphinx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;recipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;collective&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recipe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sphinxbuilder&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;I have also customized the look and feel of the output so it uses the
Plone logo and a custom css. This is configurable from the buildout
configuration file of course. By the way, if someone from the Plone
community wants to improve the CSS, please dot it ! (I am not good at
this :) ) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;[gallery] &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Notice that if you use LaTex or PDF rendering, you will need to install
pdflatex. Furthermore, the recipe script will not work under Windows
unless you install a Linux-like environment, since it uses the Makefile
provided by Sphinx. I guess MSYS+MinGW should make it work, but I didn't
try.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 11 Sep 2008 11:41:00 +0200</pubDate><guid>http://blog.ziade.org/2008/09/11/zcbuildout-recipe-to-build-your-sphinx-doc/</guid></item><item><title>Yet another Planet</title><link>http://blog.ziade.org/2008/09/04/yet-another-planet/</link><description>&lt;p&gt;&lt;a href="http://atomisator.ziade.org/"&gt;Atomisator&lt;/a&gt; is a framework so it is hard to get an idea of its
features until a real application uses it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;That is why I wrote a small application in Pylons called &lt;a href="https://svn.afpy.org/misc/atomisator.afpy.org/packages/Yap/trunk/"&gt;Yap&lt;/a&gt; (Yet
Another Planet), that is basically displaying the XML file produced by
an Atomisator instance. Since Atomisator does all the work, the Pylons
apps is really small (one or two controllers, that's it). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;My first use case was to produce a nice, smart Planet for our user
group &lt;a href="http://afpy.org"&gt;Afpy&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here's a first draft: &lt;a href="http://ziade.org/afpy/"&gt;http://ziade.org/afpy/&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;You can play with 'j', 'k' and arrows to open and close posts, but I am
still working on this, so it will also scrolling the window when you are
on a post. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Anyways, it grabs various French sources for Python and uses these
plugins from Atomisator: &lt;br /&gt;
-   filter : reddit
-   filter : delicious
-   filter : doublons
-   enhancer : related
-   enhancer : digg&lt;/p&gt;
&lt;p&gt;The result is basically following reddit and delicious links to display
an extract of the page linked, and display digg comments as well.
Duplicate are removed as well. A list of related entry are also added to
each entry. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It is based on this configuration file, Atomisator uses to generate an
XML file for Yap in a cron: &lt;br /&gt;
&lt;a href="http://atomisator.ziade.org/"&gt;atomisator&lt;/a&gt;&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;sources&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;rss&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;del&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;icio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;us&lt;/span&gt;&lt;span class="sr"&gt;/rss/&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;Delicious&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;rss&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;afpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;search_rss&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="n"&gt;portal_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;AFPYNews&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;sort_on&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Date&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;sort_order&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;reverse&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;review_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;published&lt;/span&gt; &lt;span class="n"&gt;Afpy&lt;/span&gt; &lt;span class="n"&gt;News&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;rss&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;feeds&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;feedburner&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="sr"&gt;/Baderlog/&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="n"&gt;Bader&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;rss&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;biologeek&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="sr"&gt;/journal/&lt;/span&gt;&lt;span class="n"&gt;rss&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;php&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Python&lt;/span&gt; &lt;span class="n"&gt;Biologeek&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;rss&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gawel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/weblog/&lt;/span&gt;&lt;span class="n"&gt;rss&lt;/span&gt;&lt;span class="sr"&gt;/python/&lt;/span&gt;&lt;span class="n"&gt;afpy&lt;/span&gt;&lt;span class="sr"&gt;/zope/&lt;/span&gt;&lt;span class="n"&gt;zope3&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;rss&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;Gawel&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;rss&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;haypocalc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="sr"&gt;/blog/&lt;/span&gt;&lt;span class="n"&gt;rss&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;php&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Python&lt;/span&gt;&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;Haypo&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;rss&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;jehaisleprintemps&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;net&lt;/span&gt;&lt;span class="sr"&gt;/blog/&lt;/span&gt;&lt;span class="n"&gt;rss&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;No&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;rss&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;programmation&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/sections/&lt;/span&gt;&lt;span class="n"&gt;blog&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;exportrss&lt;/span&gt; &lt;span class="n"&gt;Tarek&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;rss&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt; &lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blogmarks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;net&lt;/span&gt;&lt;span class="sr"&gt;/rss/&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;Blogmarks&lt;/span&gt;

&lt;span class="c1"&gt;# put here the database location&lt;/span&gt;

&lt;span class="n"&gt;database&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqlite:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;afpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;

&lt;span class="c1"&gt;# this is the file that will be generated&lt;/span&gt;

&lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/home/&lt;/span&gt;&lt;span class="n"&gt;tarek&lt;/span&gt;&lt;span class="sr"&gt;/www/&lt;/span&gt;&lt;span class="n"&gt;packages&lt;/span&gt;&lt;span class="sr"&gt;/Yap/&lt;/span&gt;&lt;span class="n"&gt;trunk&lt;/span&gt;&lt;span class="sr"&gt;/yap/&lt;/span&gt;&lt;span class="n"&gt;public&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;afpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt;

&lt;span class="c1"&gt;# infos that will appear in the generated feed.&lt;/span&gt;

&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Planet&lt;/span&gt; &lt;span class="n"&gt;Python&lt;/span&gt; &lt;span class="n"&gt;Francophone&lt;/span&gt;

&lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Le&lt;/span&gt; &lt;span class="n"&gt;planet&lt;/span&gt; &lt;span class="n"&gt;de&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;Association&lt;/span&gt; &lt;span class="n"&gt;Python&lt;/span&gt; &lt;span class="n"&gt;Francophone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;et&lt;/span&gt; &lt;span class="n"&gt;des&lt;/span&gt; &lt;span class="n"&gt;gens&lt;/span&gt; &lt;span class="n"&gt;heureux&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="nb"&gt;link&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;afpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/planet/&lt;/span&gt;

&lt;span class="n"&gt;filters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;reddit&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;delicious&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;doublons&lt;/span&gt;

&lt;span class="n"&gt;enhancers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;related&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;digg&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;What's Next ?&lt;/h3&gt;
&lt;p&gt;Since now, there were no attempt to try to automatically classify
entries. The next plugin I am working on will provide a Naive Bayesian
filter to classify entries, together with a way to train it through the
Yap web interface. basically a 'keep'/'ditch' button. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I will also set an english Planet Python to see how things go with more
sources.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 04 Sep 2008 21:20:00 +0200</pubDate><guid>http://blog.ziade.org/2008/09/04/yet-another-planet/</guid></item><item><title>Thank you, Plone community !</title><link>http://blog.ziade.org/2008/08/29/thank-you-plone-community/</link><description>&lt;p&gt;Wow. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I have been really involved in Plone for about one year now. This
community just rocks. I met incredible guys, and did tons of work and
experiments with them. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I have &lt;a href="http://tarekziade.wordpress.com/2008/08/26/help-me-go-to-the-plone-conference/"&gt;asked&lt;/a&gt; the community for help to go to the Plone Conf with a
fund raising. I raised the funds in 3 days... I am astonished, this is a
marvelous gift from you guys. I alsmot cried when I saw all those people
donating. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I am so lucky, and so proud to be part of the Plone community and to
know all of you. I will work hard to produce the best tutorial I can for
the Plone Conf, and continue of course my work in the community tasks I
am currently involved in. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I would like to say a big, warm thank to (the order is chronological,
from the latest donation to the first one): &lt;br /&gt;
-   Andreas Jung
-   Veda Williams
-   David Glick
-   Jeff Kowalczyk
-   Youenn Boussard
-   Christian Klinger
-   Jesse Snyder
-   Alec Mitchell
-   John Habermann
-   Maurits van Rees
-   Jean-François Roche
-   Martin Aspeli
-   Alain Meurant
-   Aleksandr Vladimirskiy
-   Jon Stahl
-   Alexander Limi
-   Stephen McMahon&lt;/p&gt;
&lt;p&gt;And a very special thanks to Calvin Hendryx-Parker. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This conference will be a &lt;a href="http://plone.org/events/conferences/2008-washington-dc/agenda"&gt;blast&lt;/a&gt; for sure, if you can go there, do
it ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Now I better find a good idea for those T-Shirts I have promised ! ...
:)&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 29 Aug 2008 17:25:00 +0200</pubDate><guid>http://blog.ziade.org/2008/08/29/thank-you-plone-community/</guid></item><item><title>Plone development: Nose or zope.testing ?</title><link>http://blog.ziade.org/2008/08/28/plone-development-nose-or-zopetesting/</link><description>&lt;p&gt;Python needs a better testing tool, hopefully this will happen &lt;a href="http://www.mail-archive.com/python-dev@python.org/msg31411.html"&gt;some
days&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Until then we, Plone developers use &lt;a href="http://pypi.python.org/pypi/zope.testing"&gt;zope.testing&lt;/a&gt; for the best and
the worst. &lt;br /&gt;
-   the best because zope.testing provides &lt;a href="http://pypi.python.org/pypi/zope.testing#layers"&gt;layers&lt;/a&gt; and the Zope/Plone
    stack provides really nice things to work with them.
-   the worst because sometimes it just gives me some headaches to
    figure out how to set my test fixtures correctly.
-   the worst because I cannot easy-install zope.testing and just call
    it from the prompt to run some tests (this should get better
    sometimes see &lt;a href="http://mail.zope.org/pipermail/zope-dev/2008-May/031851.html"&gt;here&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;That said, when you work with a zc.buildout based environment, it is
easy to use zope.testing, thanks to &lt;a href="http://pypi.python.org/pypi/zc.recipe.testrunner/1.1.0"&gt;zc.recipe.testrunner&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But since the eggification of Zope, we write more and more code that
would benefit from a lighter test framework. &lt;a href="http://www.somethingaboutorange.com/mrl/projects/nose/"&gt;Nose&lt;/a&gt; has this lighter
approach, I love it to write simple tests without the Java-like heavy
UnitTest framework. I can just write: &lt;br /&gt;
   def test_something():&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;   &lt;span class="n"&gt;assert&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;And call &lt;em&gt;nosetests&lt;/em&gt;. Test fixtures are easy to set at all levels as
well, using the &lt;em&gt;with_setup&lt;/em&gt; decorator. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I tried to write some tests that could be launchable from both
frameworks, by making some bridges, but I came up to the conclusion that
a package should fully use either zope.testing, either Nose. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So now, I use two test scripts in my zc.buildout environments, and
decide depending on the package. I have written another recipe for
buildout to bind Nose: &lt;a href="http://pypi.python.org/pypi/pbp.recipe.noserunner"&gt;pbp.recipe.noserunner&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;From there, I create two sections in my buildout.cfg file: &lt;br /&gt;
   [buildout]&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;parts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;nose&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;ztest&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;nose&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;recipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pbp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recipe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;noserunner&lt;/span&gt;

&lt;span class="n"&gt;eggs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;egg1&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;egg2&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ztest&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;recipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;zc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recipe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;testrunner&lt;/span&gt;

&lt;span class="n"&gt;eggs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;egg3&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;egg4&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;These will generate two test scripts &lt;br /&gt;
-   nose to run nosetests with egg1 and egg2 in sys.path
-   ztest to run zope.testing with egg3 and egg4 in sys.path&lt;/p&gt;
&lt;p&gt;Here's my grid of choices when I am coding: &lt;br /&gt;
-   zc.buildout recipes : zope.testing (doctests using zc.buildout
    testing framework)
-   Plone packages: zope.testing
-   Python packages and 'everything' else: Nose&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 28 Aug 2008 15:25:00 +0200</pubDate><guid>http://blog.ziade.org/2008/08/28/plone-development-nose-or-zopetesting/</guid></item><item><title>Atomisator, visiting links</title><link>http://blog.ziade.org/2008/08/27/atomisator-visiting-links/</link><description>&lt;p&gt;I am writing a plugin for &lt;a href="http://tarekziade.wordpress.com/2008/08/20/atomisator-a-framework-to-build-custom-rss-feeds/"&gt;Atomisator&lt;/a&gt; that detects when a post is a
&lt;a href="http://www.reddit.com/"&gt;Reddit&lt;/a&gt; or a Delicious entry, and adds a sample from the page it
links to. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;On a Reddit feed for example, you will basically get a meaningful
title, and summary that will look like this: &lt;br /&gt;
   [link] [comments]&lt;/p&gt;
&lt;p&gt;This is not really useful in a feed... &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So basically, the plugin I am writing is detecting this kind of entries
and is grabbing a sample out of the linked page, so the entry is
transformed like this: &lt;br /&gt;
   Extract from the link:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;   &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;blablablba&lt;/span&gt;

   &lt;span class="n"&gt;blablabla&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;[link] [comments]&lt;/p&gt;
&lt;p&gt;The extracted text is a pure text, extracted using &lt;a href="http://www.crummy.com/software/BeautifulSoup/"&gt;BeautifulSoup&lt;/a&gt;
and the SGML parser from the standard library. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This is quite useful as long as the begining of the web page is
meaningful, which is rarely the case... Most of the time the web page
starts with a gibberish text, like a menu bar content for instance. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So I am trying to detect what is the "best" part of the web page
pointed by the link. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;To do so, I am using the title of the entry, which is suppose to make
sense. Since there are good chances the text will contain the words used
in the title, I am looking for them into the page. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I have tried several combinations, even by trying to find the smallest
sample where I get the maximum number of words from the title by doing
some cartesian products. But this was too slow. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Instead, I am trying to detect the real beginning of the post by trying
some common patterns : most of the time the body of the post is a div
tag with a class attribute like body, content, article-content, etc.. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I am running it now over the &lt;a href="http://www.reddit.com/r/Python.rss"&gt;Python feed at Reddit&lt;/a&gt;, and the results
start to look nice so far (==you can understand what the page talks
about most of the time). See
here:&lt;a href="http://www.ziade.org/atomisator/filtered.xml"&gt;http://www.ziade.org/atomisator/filtered.xml&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Now I will try to run it over a fair amount of entries and with various
sources to try to tune up the extractor. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This code will also be a useful base to visit links of any kind of
entry, but it needs a lot of cleanup: I have spotted some quadratic
complexity parts I need to get ridd of. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Try it yourself :&lt;/strong&gt; &lt;br /&gt;
1.  make sure you have SQLite installed
2.  install atomisator with easy_install atomisator
3.  create your atomisator.cfg file with the content below
4.  then run it by calling 'atomisator' in the folder atomisator.cfg
    lives&lt;/p&gt;
&lt;p&gt;atomisator.cfg content : &lt;br /&gt;
&lt;a href="http://tarekziade.wordpress.com/2008/08/20/atomisator-a-framework-to-build-custom-rss-feeds/"&gt;atomisator&lt;/a&gt;&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;sources&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;rss&lt;/span&gt; &lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reddit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="sr"&gt;/r/&lt;/span&gt;&lt;span class="n"&gt;Python&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rss&lt;/span&gt; &lt;span class="n"&gt;reddit&lt;/span&gt;

&lt;span class="n"&gt;database&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqlite:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;atomisator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;

&lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;atomisator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt;

&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Meta&lt;/span&gt; &lt;span class="n"&gt;feed&lt;/span&gt;

&lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Automatic&lt;/span&gt; &lt;span class="n"&gt;feed&lt;/span&gt; &lt;span class="n"&gt;created&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt; &lt;span class="n"&gt;Atomisator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="nb"&gt;link&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;

&lt;span class="n"&gt;filters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="n"&gt;reddit&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Wed, 27 Aug 2008 19:50:00 +0200</pubDate><guid>http://blog.ziade.org/2008/08/27/atomisator-visiting-links/</guid></item><item><title>Help me go to the Plone Conference !</title><link>http://blog.ziade.org/2008/08/26/help-me-go-to-the-plone-conference/</link><description>&lt;p&gt;I have been asked to be a "champion" last winter at the &lt;a href="http://plone.org/events/2008-summit/next-actions-and-champions"&gt;Plone
summit&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;My task was : &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Improve release procedures for add-ons on plone.org: document a
release process, and create release tools for packaging and uploading
products from the command line.&lt;/em&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Since then, the major steps that I have done for it were : &lt;br /&gt;
-   change Python's &lt;em&gt;.pypirc&lt;/em&gt; format so it can be used to interact with
    PyPI and Plone. &lt;a href="http://tarekziade.wordpress.com/2008/05/12/the-new-pypirc-format-in-python-distutils/"&gt;DONE&lt;/a&gt;
-   provide a package to make the new .pypirc format available under
    Python 2.4 and 2.5. &lt;a href="http://pypi.python.org/pypi/collective.dist"&gt;DONE&lt;/a&gt;
-   make PloneSoftwareCenter PyPI-compatible. &lt;a href="http://tarekziade.wordpress.com/2008/05/06/plone-paris-sprint-wrapup-3-newploneorg-collectivedist-released/"&gt;DONE&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The steps left are: &lt;br /&gt;
-   continue the help on Plone.org Plone 3 migration, so it becomes
    reality
-   document the release process, based on PyPI, Plone.org and
    collective.dist&lt;/p&gt;
&lt;p&gt;I am planning to go at the &lt;a href="http://plone.org/events/conferences/2008-washington-dc"&gt;Plone Conference&lt;/a&gt; and to give a tutorial
on all this work to deliver to the community the releasing process
documentation. So these two remaining steps have to happen &lt;em&gt;before&lt;/em&gt; the
conference. I am also planning to sprint with people there. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;My &lt;a href="http://alterway.fr/"&gt;company&lt;/a&gt; contributes a lot to the Plone community. For instance
we have organized the Paris Plone Sprint and we try to contribute as
often as we can into the collective. But it has its limits and my
airfare cannot be covered at this time. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This is the first time I am asking something to the community : please,
help me go to the Plone conference ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I have created &lt;a href="http://tarek.chipin.com/plone-conference"&gt;a page at ChipIn&lt;/a&gt;, and I am trying to raise the money
to go to D.C. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;What you get if you help me: &lt;br /&gt;
-   You will contribute to plone.org and release procedures enhancements
-   Since I can &lt;a href="http://tarekziade.wordpress.com/2008/05/21/diy-pycon-fr-shirts/"&gt;create t-shirts&lt;/a&gt;, I will create a unique Plone'08 DC
    logo T-Shirt that I will ship to the 5 biggest donators.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;EDIT : It seems that Paypal won't take your browser language settings
into account. If you can't read french you can try this hack I have
found to switch to english :&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;*when the paypal page is loaded add "locale.x=en_US&amp;amp;" into the url
after "websrc?=" * &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;so it looks like this: &lt;br /&gt;
https://www.paypal.com/fr/cgi-bin/webscr?locale.x=en_US&amp;amp;cmd=...&lt;/em&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Reload that link and it should switch in english.&lt;/em&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;EDIT 2: don't worry if Paypal tells you &lt;em&gt;Amina El Kamel &lt;/em&gt;received the
payment. This is my girlfriend Paypal account ;) .&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Click on the cat : &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href="http://tarek.chipin.com/plone-conference"&gt;&lt;img alt="image" src="http://ziade.org/atomisator/shrek_cat.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 26 Aug 2008 21:52:00 +0200</pubDate><guid>http://blog.ziade.org/2008/08/26/help-me-go-to-the-plone-conference/</guid></item><item><title>Visual profiling with Nose and gprof2dot</title><link>http://blog.ziade.org/2008/08/25/visual-profiling-with-nose-and-gprof2dot/</link><description>&lt;p&gt;&lt;a href="http://www.somethingaboutorange.com/mrl/projects/nose/"&gt;Nose&lt;/a&gt; comes with a handy option to generate profiling stats. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;To profile your code, create a test dedicated to this purpose and run
it with the right options: &lt;br /&gt;
   $ nosetests --with-profile --profile-stats-file stats.pf test_performance&lt;/p&gt;
&lt;p&gt;This will run the tests that corresponds to the &lt;em&gt;test_performance&lt;/em&gt;
name and generate a stats.pf file. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Nose uses &lt;a href="http://docs.python.org/lib/module-hotshot.html"&gt;hotshot&lt;/a&gt;, so if you want to generate a file that can be
read directly by the &lt;a href="http://docs.python.org/lib/module-profile.html"&gt;pstats&lt;/a&gt; module and all the statistics tools out
there, you need to convert it using the &lt;a href="http://docs.python.org/lib/module-hotshot.stats.html"&gt;hotshot.stats&lt;/a&gt; module. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;From there, there is plenty of tools that can transform such a file
into a visual graph. Most of the time, they use &lt;a href="http://www.graphviz.org/"&gt;Graphviz&lt;/a&gt; to render a
graph, by generating a file &lt;a href="http://www.graphviz.org/cgi-bin/man?dot"&gt;dot&lt;/a&gt; can read. This software is most of
the time easy to install through a binary distribution on your system.
If you need to compile it... good luck.. ;) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Anyway, from there, I use &lt;a href="http://code.google.com/p/jrfonseca/wiki/Gprof2Dot"&gt;gprof2dot&lt;/a&gt;, which renders a nice graph
with meaningful colors. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;From the author: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href="http://code.google.com/p/jrfonseca/wiki/Gprof2Dot"&gt;&lt;img alt="image" src="http://jrfonseca.googlecode.com/svn/wiki/gprof2dot.png" /&gt;&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The color of the nodes and edges varies according to the total time %
value. In the default temperature-like color-map, functions where most
time is spent (hot-spots) are marked as saturated red, and functions
where little time is spent are marked as dark blue.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;/li&gt;
&lt;li&gt;&lt;/li&gt;
&lt;li&gt;&lt;/li&gt;
&lt;li&gt;&lt;/li&gt;
&lt;li&gt;&lt;/li&gt;
&lt;li&gt;&lt;/li&gt;
&lt;li&gt;&lt;/li&gt;
&lt;li&gt;&lt;/li&gt;
&lt;li&gt;&lt;/li&gt;
&lt;li&gt;&lt;/li&gt;
&lt;li&gt;&lt;/li&gt;
&lt;li&gt;&lt;/li&gt;
&lt;li&gt;&lt;/li&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;If &lt;/em&gt;you want to use it, I have created some console scripts for
conveniency, you can install using [easy_install][]: &lt;br /&gt;
   $ easy_install pbp.scripts&lt;/p&gt;
&lt;p&gt;It creates a &lt;em&gt;gprof2dot&lt;/em&gt; script you can use, following the author
documentation, but also a &lt;em&gt;hotshot2dot&lt;/em&gt; script that will convert
automatically a statistics file and pass it to &lt;em&gt;gprof2dot&lt;/em&gt;: &lt;br /&gt;
   $ hotshot2dot /path/to/my/hotshot/file&lt;/p&gt;
&lt;p&gt;This will print in the output a dot file, you can send to the dot
program, using a pipe: &lt;br /&gt;
~~~~ {style="text-align:left;"}
$ hotshot2dot /path/to/my/hotshot/file | dot -Tpng -o output.png
~~~~&lt;/p&gt;
&lt;p&gt;You will get the visual result in &lt;em&gt;output.png&lt;/em&gt;.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Mon, 25 Aug 2008 09:19:00 +0200</pubDate><guid>http://blog.ziade.org/2008/08/25/visual-profiling-with-nose-and-gprof2dot/</guid></item><item><title>Atomisator, a framework to build custom RSS feeds</title><link>http://blog.ziade.org/2008/08/20/atomisator-a-framework-to-build-custom-rss-feeds/</link><description>&lt;p&gt;We are all overwhelmed by the amount of data in our feed readers. While
this problem is unavoidable if you keep on adding new feeds in it, they
could be automatically filtered and categorized to reduce the flow of
data. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I wanted for a long time to try out some custom filters over my feeds
to find for example related entries, by trying to understand the meaning
of the posts, using tools like &lt;a href="http://nltk.sourceforge.net/"&gt;NLTK&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So I needed a playground for this, where I could play with feeds. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I think the closest tool for this is to use &lt;a href="http://pipes.yahoo.com/pipes/"&gt;Yahoo Pipes&lt;/a&gt; but as far
as I know, the only way to create custom filters is to run a web service
and call it from Yahoo Pipes. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Anyways, I started to code a framework (at first it was an example for
&lt;a href="http://tarekziade.wordpress.com/2008/08/08/a-new-python-book-expert-python-programming/"&gt;my latest book&lt;/a&gt;) that looks a lot like Yahoo Pipes in its principles.
I don't have any User Interface at this time of course, but a simple
plugin-based tool that will let me combine my code snippets with feeds.&lt;/p&gt;
&lt;p&gt;It is called Atomisator (see &lt;a href="http://atomisator.ziade.org"&gt;http://atomisator.ziade.org&lt;/a&gt;). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;[caption id="" align="alignnone" width="539" caption="The big
picture"][&lt;img alt="The big picture" src="http://www.ziade.org/atomisator/atomisator.png" /&gt;]&lt;a href="http://www.ziade.org/atomisator/atomisator.png"&gt;The big picture&lt;/a&gt;[/caption] &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The process is quite simple: &lt;br /&gt;
1.  &lt;strong&gt;Readers&lt;/strong&gt; are plugins that know how to read a source and provide
    entries out of it.
2.  &lt;strong&gt;Filters&lt;/strong&gt; are plugins that know how to remove unwanted entries, or
    enhance them (change their title, summary, etc.). They can be
    combined.
3.  the entries are then pushed in a database. This is useful to avoid
    doublons, and to keep track of past entries.
4.  to create the feed, the entries are read from the database
5.  &lt;strong&gt;Enhancers&lt;/strong&gt; are plugins that will add to entries extra info.
    Typically info that can't be stored, like Digg comments if the entry
    is detected on Digg, or Google related searches, and so on
6.  The feed is then generated.&lt;/p&gt;
&lt;p&gt;Right now I am focusing on making it fast, which is not simple because
the plugins can play with all entries in the database. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It is in early stage and undertested, but it kinda works. I pushed it
at PyPI to see of it meets interest. If it does, I will document the
process of writing plugins. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Make sure you have SQlite installed, and give it a try : &lt;br /&gt;
   $ easy_install atomisator.main&lt;/p&gt;
&lt;p&gt;$ atomisator -c atomisator.cfg&lt;/p&gt;
&lt;p&gt;$ atomisator&lt;/p&gt;
&lt;p&gt;You will have an atomisator.xml feed created. You can add other feeds
in atomisator.cfg as well and try them. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Now with this environment, I can start to try out custom algorithms
over my feeds. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I've been told the name doesn't sound right in Ehglish, but it does in
French so I keep it ;)&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Wed, 20 Aug 2008 16:56:00 +0200</pubDate><guid>http://blog.ziade.org/2008/08/20/atomisator-a-framework-to-build-custom-rss-feeds/</guid></item><item><title>iw.eggproxy, a smart PyPI mirror</title><link>http://blog.ziade.org/2008/08/12/iweggproxy-a-smart-pypi-mirror/</link><description>&lt;p&gt;Mirroring PyPI becomes a recurrent need for Zope development, because
&lt;a href="http://pypi.python.org/pypi/zc.buildout/"&gt;zc.buildout&lt;/a&gt;makes a lot of package downloads to build one
application. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This is useful when you are working in an intranet with limited web
access or when you want to speed up download times. It also makes things
safer: if PyPI is down and if developers computers don't have caches,
having a mirror will save your day. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;While PyPI has proven its robustness (it is 100% up for months now as
far as I can see), having mirrors makes a lot of sense. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;We have created a small mirror application here at Ingeniweb, that we
use for our buildouts needs. This work was thaught and created by my
colleague &lt;a href="http://zebert.blogspot.com/"&gt;Bertrand Mathieu&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It is a smart proxy that will download packages at PyPI everytime they
have been asked by a buildout or an easy_install client. When the
package is downloaded, it is kept in the proxy side for any new
requests. This means that after a while, the proxy has its own
collection of packages that corresponds to the real needs and will not
query PyPI anymore. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This approach avoids having to download and synchronize PyPI with
crons, which is a heavy process since PyPI weight several gigas. The
caveat of course, is that it won't be able to get a new package if PyPI
is down. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Take a look ! &lt;a href="http://pypi.python.org/pypi/iw.eggproxy"&gt;http://pypi.python.org/pypi/iw.eggproxy&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;By the way there is an interesting sprint coming up on all these
topics, in Germany : &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.openplans.org/projects/black-forest-sprint/project-home"&gt;http://www.openplans.org/projects/black-forest-sprint/project-home&lt;/a&gt;&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 12 Aug 2008 09:04:00 +0200</pubDate><guid>http://blog.ziade.org/2008/08/12/iweggproxy-a-smart-pypi-mirror/</guid></item><item><title>A new Python book : &amp;quot;Expert Python Programming&amp;quot;</title><link>http://blog.ziade.org/2008/08/08/a-new-python-book-quotexpert-python-programmingquot/</link><description>&lt;p&gt;The &lt;a href="http://www.packtpub.com/"&gt;Packt team&lt;/a&gt; added &lt;a href="http://www.packtpub.com/expert-python-programming/book"&gt;a page about my new book&lt;/a&gt;, which will be out
sometimes in September, so I guess it is the right time for me to
announce it here ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So here comes "Expert Python Programming", where I explain how we work
with Python every day to create software. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img alt="image" src="http://images.packtpub.com/images/full/184719494X.jpg" /&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This is my first book in English, and writing in another language was
quite challenging ;) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Anyway, this book is intended for developers that already have a
background in Python and covers only advanced topics (&lt;a href="http://www.packtpub.com/expert-python-programming/book#indetail"&gt;see the editor
details&lt;/a&gt;). But as I said, it explains how we develop our applications
in Python so topics like continuous integration, documentation, testing,
releasing, refactoring, etc. are covered. &lt;br /&gt;
Managers will also have a good overview of how a Python project can be
run and managed, using modern tools like Distributed Version Control
Systems (Mercurial for instance) or Buildbot. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Even if I am working on Zope and Plone these days, I have focused on
writing a book that is only about Python, to make it useful to any
developer. The fact that my friend &lt;a href="http://jjinux.blogspot.com/"&gt;Shannon&lt;/a&gt; did the technical
reviewing helped on this : he doesn't use Zope too much so he was close
to the target readership. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I will take more time when it comes out to announce it in the various
mailing lists, and to get into greater details about the content that
will be available online besides the book (some Python packages, etc) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I can't wait to see it out and hold it :D&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 08 Aug 2008 14:57:00 +0200</pubDate><guid>http://blog.ziade.org/2008/08/08/a-new-python-book-quotexpert-python-programmingquot/</guid></item><item><title>OSCON report #1: the city of Portland</title><link>http://blog.ziade.org/2008/07/29/oscon-report-1-the-city-of-portland/</link><description>&lt;p&gt;Covering &lt;a href="http://en.oreilly.com/oscon2008/public/content/home"&gt;OSCON&lt;/a&gt; when you are a speaker is quite hard. Until my talk
was done, I was more in a mood of reviewing my slides and not really
thinking about other talks or blogging. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;[caption id="attachment_136" align="alignnone" width="225"
caption="Convention center from the Bridge (the two twin
towers)"][&lt;img alt="image" src="http://tarekziade.files.wordpress.com/2008/07/img_3591.jpg?w=225" /&gt;][][/caption] &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I will try to write a few reports now that everything is over. This
first report is about &lt;a href="http://en.wikipedia.org/wiki/Portland,_Oregon"&gt;Portland&lt;/a&gt;, the city and is not technical at
all. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This is the first non-technical entry in my blog, but let's this it for
once. So &lt;strong&gt;if you are looking for technical content, skip this entry&lt;/strong&gt;
:) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Portland is an amazing place. The weather was nice throughout the whole
week: sunny and not too hot. Some evenings were a bit chilly but that
felt nice to me. The convention center where OSCON took place was
located on the other side of the Willamette river, in front of the city
center. It is a bit far from the center, but finding places were you can
put so many people must be hard I guess. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The top six things I have noticed about Portland: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Local beers are great&lt;/strong&gt;. You can find many places where they brew
their own beers, that are comparable to Belgian beers in quality and
taste, like the &lt;a href="http://www.luckylab.com/html/story.html"&gt;Lucky Lab&lt;/a&gt; (Where Jon, a member of the local Plone
user group took us, thanks!). Some place have samples so you can taste
several kinds of beers. I also went to the &lt;a href="http://www.oregonbrewfest.com/"&gt;Oregon Brewers Festival&lt;/a&gt;
right after OSCON was over, where you could taste beers from many
places. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;[caption id="attachment_137" align="alignnone" width="225"
caption="Oregon Brewers Festival"][&lt;img alt="Beer Festival" src="http://tarekziade.files.wordpress.com/2008/07/img_3585.jpg?w=225" /&gt;][][/caption] &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Parking a car is a pain&lt;/strong&gt;. I think the best way to travel in Portland
is a mix of Tram, Bus and Bike. You can put your bike on the nose of the
buses or inside the trams. Smart thing. This would be useful in Paris.
We ended up parking our car far from the center and used the tram
(called Max, and free in the center). Portland should set up a bike
rental system like in Paris (Velib'), I am sure this would rock. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Organics&lt;/strong&gt;: people here seem to be really concerned about
sustainability and organics. I've been told there are a lot of organics
farm around Portland. Supermarkets have a nice amount of organics stuff
as well. This is nice. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Starbucks Coffee owns the streets&lt;/strong&gt;: this is scary. there's a
Starbucks almost on each block here. Anyway, local coffee shops have
better coffees and they provide nice places to chill out, read a book or
talk with people. I think this is where you can feel the real Portland
way of living. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The food is good&lt;/strong&gt; : I have to admit, when we, french people, travel,
we feel a bit superior to some countries on food matters. We tend to
show off about it :). But Portland has great places to eat. If you like
donuts, the place you must go to is called &lt;a href="http://voodoodoughnut.com/"&gt;Voodoo Doughnut&lt;/a&gt;. They are
creative ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;[caption id="attachment_138" align="alignnone" width="300"
caption="Voodoo Doughnut"][&lt;img alt="Voodoo Donuts" src="http://tarekziade.files.wordpress.com/2008/07/img_3614.jpg?w=300" /&gt;][][/caption] &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Big open spaces&lt;/strong&gt;: I went to Mount Hood where people are skying in...
July. I wanted to visit this place because it is where Stanley Kubrick
did the outside shots of the famous motel in &lt;a href="http://en.wikipedia.org/wiki/The_Shining_(film)"&gt;The Shining&lt;/a&gt;. I also had
a walk at the Hood river sandbar, where people do kite surfing. One tip:
don't walk there with shorts on, the wind is so strong that you get
slapped by the sand. So if you like big open spaces and sports, Portland
is the place to be: all those place are one or two hours drive from the
city center. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;[caption id="attachment_139" align="alignnone" width="300" caption="Mt
Hood"][&lt;img alt="Mount Hood" src="http://tarekziade.files.wordpress.com/2008/07/img_3402.jpg?w=300" /&gt;][][/caption] &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;End of the aparté ! The next entry will focus on OSCON&lt;/p&gt;
&lt;p&gt;[&lt;img alt="image" src="http://tarekziade.files.wordpress.com/2008/07/img_3591.jpg?w=225" /&gt;]: http://tarekziade.files.wordpress.com/2008/07/img_3591.jpg
  [&lt;img alt="Beer Festival" src="http://tarekziade.files.wordpress.com/2008/07/img_3585.jpg?w=225" /&gt;]: http://tarekziade.files.wordpress.com/2008/07/img_3585.jpg
  [&lt;img alt="Voodoo Donuts" src="http://tarekziade.files.wordpress.com/2008/07/img_3614.jpg?w=300" /&gt;]: http://tarekziade.files.wordpress.com/2008/07/img_3614.jpg
  [&lt;img alt="Mount Hood" src="http://tarekziade.files.wordpress.com/2008/07/img_3402.jpg?w=300" /&gt;]: http://tarekziade.files.wordpress.com/2008/07/img_3402.jpg&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 29 Jul 2008 05:20:00 +0200</pubDate><guid>http://blog.ziade.org/2008/07/29/oscon-report-1-the-city-of-portland/</guid></item><item><title>My OSCON slides online (zc.buildout)</title><link>http://blog.ziade.org/2008/07/25/my-oscon-slides-online-zcbuildout/</link><description>&lt;p&gt;After a few attempts to make my screencast look nice under Google Video,
I decided I would just upload the original ones (.mov and .m4v files)
and a PDF export of my presentation. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So everything is available here: &lt;a href="http://ziade.org/oscon"&gt;http://ziade.org/oscon&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Slides: &lt;a href="http://ziade.org/oscon/oscon.pdf"&gt;http://ziade.org/oscon/oscon.pdf&lt;/a&gt; &lt;br /&gt;
-   Screencast #1, playing with distutils:
    &lt;a href="http://ziade.org/oscon/demo1.m4v"&gt;http://ziade.org/oscon/demo1.m4v&lt;/a&gt;
-   Screencast #2, playing with setuptools:
    &lt;a href="http://ziade.org/oscon/demo2.m4v"&gt;http://ziade.org/oscon/demo2.m4v&lt;/a&gt;
-   Screencast #3, playing with zc.buildout:
    &lt;a href="http://ziade.org/oscon/demo3.mov"&gt;http://ziade.org/oscon/demo3.mov&lt;/a&gt;
-   Screencast #4, creating a recipe:
    &lt;a href="http://ziade.org/oscon/demo4.mov"&gt;http://ziade.org/oscon/demo4.mov&lt;/a&gt;
-   Screencast #5, creating a plone buildout and adding a develop
    package in it: &lt;a href="http://ziade.org/oscon/demo5.mov"&gt;http://ziade.org/oscon/demo5.mov&lt;/a&gt;
-   Screencast #6, collective.buildbot demo (no coding, just showing
    buildbot.ingeniweb.com): &lt;a href="http://ziade.org/oscon/demo6.mov"&gt;http://ziade.org/oscon/demo6.mov&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The .mov files are streamed automatically in your browser I believe,
not the m4v ones. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Thanks to Jim Fulton for the quick feedback.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 25 Jul 2008 00:46:00 +0200</pubDate><guid>http://blog.ziade.org/2008/07/25/my-oscon-slides-online-zcbuildout/</guid></item><item><title>Going to OSCON</title><link>http://blog.ziade.org/2008/07/18/going-to-oscon/</link><description>&lt;p&gt;I am leaving tomorrow, heading to Portland, OR, to &lt;a href="http://en.oreilly.com/oscon2008/public/content/home"&gt;OSCON&lt;/a&gt;. My talk on
&lt;a href="http://en.oreilly.com/oscon2008/public/schedule/detail/2712"&gt;zc.buildout and Plone&lt;/a&gt; will be thurdsay the 24th, and I'll be there
the whole week. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you are going there and want to meet leave me a note, I am looking
forward to meet other geeks there. :D&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 18 Jul 2008 12:00:00 +0200</pubDate><guid>http://blog.ziade.org/2008/07/18/going-to-oscon/</guid></item><item><title>shutil.copytree small improvement</title><link>http://blog.ziade.org/2008/07/08/shutilcopytree-small-improvement/</link><description>&lt;p&gt;When you have to work with directories and files, there's a very common
pattern: copying a directory into another, but filtering out a few files
and directories. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For instance, if you want to copy a directory that contains source
code, you will probably remove .pyc files and .svn directories if you
work with Subversion. In that case, &lt;em&gt;shutil.copytree&lt;/em&gt; cannot be used, so
&lt;em&gt;os.walk&lt;/em&gt; is the usual way to go (rough example): &lt;br /&gt;
   import os&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="nb"&gt;join&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;splitext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;split&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;exists&lt;/span&gt;

&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;shutil&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;copyfile&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;copy_directory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

&lt;span class="err"&gt;       &lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="err"&gt;   &lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dirs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

&lt;span class="err"&gt;       &lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;.svn&amp;#39;&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;dirs:&lt;/span&gt;

&lt;span class="err"&gt;           &lt;/span&gt; &lt;span class="n"&gt;dirs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;.svn&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="c1"&gt;# don&amp;#39;t visit .svn directories&lt;/span&gt;

&lt;span class="err"&gt;       &lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;files:&lt;/span&gt;

&lt;span class="err"&gt;           &lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;splitext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;.pyc&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;.pyo&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;.fs&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

&lt;span class="err"&gt;               &lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;

&lt;span class="err"&gt;           &lt;/span&gt; &lt;span class="n"&gt;from_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="err"&gt;           &lt;/span&gt; &lt;span class="n"&gt;to_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;from_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="err"&gt;           &lt;/span&gt; &lt;span class="n"&gt;to_directory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to_&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="err"&gt;           &lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nb"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to_directory&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

&lt;span class="err"&gt;               &lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;makedirs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to_directory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="err"&gt;           &lt;/span&gt; &lt;span class="n"&gt;copyfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;from_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;to_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This is a lot of boiler-plate code, so I usually create a small
function that accepts more arguments to filter out files and directory.
But Python should provide this pattern in the standard library. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I have proposed a patch for shutil.copytree, to integrate filtering
capability. It has been reviewed and commited in the trunk this week, so
we will have it in Python 2.6. Now copytree comes with an ignore
argument that has to be a callable. If given it will be called on each
visited directory to decide what is copied and what is not. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;There's a default callable in shutil called ignore_patterns, that can
be used to filter out files with glob-style patterns. I have added this
example to Python doc: &lt;br /&gt;
   from shutil import copytree, ignore_patterns&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;copytree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ignore&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ignore_patterns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;*.pyc&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;tmp*&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Pretty straight forward ! :D &lt;br /&gt;
More info and examples here :
[http://docs.python.org/dev/library/shutil.html#shutil.copytree][]&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 08 Jul 2008 08:11:00 +0200</pubDate><guid>http://blog.ziade.org/2008/07/08/shutilcopytree-small-improvement/</guid></item><item><title>plone.org migration</title><link>http://blog.ziade.org/2008/07/01/ploneorg-migration/</link><description>&lt;p&gt;Plone.org migration to Plone 3 is taking a bit longer than expected, but
it should turn into reality soon. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;There will be many improvements on the set of packages the website uses
(I am thinking in particular about &lt;strong&gt;Maurits&lt;/strong&gt;'s work on POI that will
speed up the trackers, but it is just an example), and blobs should be
used for the products section (more than 700 projects are registered
there). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I worked last week-end on the products section, by finishing
&lt;a href="http://pypi.python.org/pypi/collective.psc.mirroring"&gt;collective.psc.mirroring&lt;/a&gt;, which will copy all packages that are
uploaded at plone.org into a file system directory. This directory will
be published directly by Apache so the website will become a new package
location for zc.buildout (find-links section) and easy_install calls
without invoking the Plone instance. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Now I am focusing on PloneSoftwareCenter (PSC) migration. It is a
pretty interesting topic: for every project located in the products
folder with releases, I am going to extract its "distutils ids". These
ids are the name set in the setup.py file for each release. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I will then look at PyPI through XML-RPC if the package is also
released, using the id. In that case, and if the author email is the
same on both side, I will validate that the project on plone.org "owns"
the given distutils id. From there PSC will act like PyPI and will
reject uploads of packages if the user is not the owner of the project
that owns the package id. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Of course there will be errors and some people might feel like their
package has been hijacked if they cannot upload their packages. But this
should be minor and should be OK after a while. A mail will soon be send
to the community to ask people to check that they are synced between
PyPI and plone.org. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I am really excited about this work because plone.org will then be
compatible with distutils &lt;em&gt;register&lt;/em&gt; and &lt;em&gt;upload&lt;/em&gt; commands, which means
that people will be able to update plone.org products section like they
do with PyPI : through a single commande line. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Hey Sidnei, what you thought of several years ago is about to turn
true. ;) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Edit&lt;/strong&gt;: My apologies goes to Maurits, who did the work on POI, not
Reinout, his brother ;)&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 01 Jul 2008 22:03:00 +0200</pubDate><guid>http://blog.ziade.org/2008/07/01/ploneorg-migration/</guid></item><item><title>Python 2.4: tarfile module is buggy, patch it !</title><link>http://blog.ziade.org/2008/06/19/python-24-tarfile-module-is-buggy-patch-it/</link><description>&lt;p&gt;I have encoutered really bad bugs in some Python 2.4 applications (Zope
based) using the &lt;em&gt;tarfile&lt;/em&gt; module. For instance, the
&lt;em&gt;TarFile.getmembers&lt;/em&gt; method that returns the files a tar file contains
will just fail to return all files ... &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Hopefully Zope will work under Python 2.5 sometimes. This is a work in
progress in GSOC if I recall it correclty, but I am not sure of the
current state. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Until then I just stick Python 2.5 module in my packages. It seems to
work fine (tests pass :D) after a few changes as it is isolated from the
rest of Python. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you want to do it as well, there are only two changes to make it
work under Python 2.4, to introduce 3 new constants in &lt;em&gt;os&lt;/em&gt; module and
to get rid of a syntax that does not work under 2.4: &lt;br /&gt;
   $ diff /opt/local/lib/python2.5/tarfile.py tarfile.py&lt;/p&gt;
&lt;p&gt;46a47,49&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SEEK_SET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SEEK_CUR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SEEK_END&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="mi"&gt;1070&lt;/span&gt;&lt;span class="n"&gt;c1073&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1076&lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;         &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abspath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;None&lt;/span&gt;

&lt;span class="o"&gt;---&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;         &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;name:&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;             &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abspath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;         &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;             &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;None&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 19 Jun 2008 00:44:00 +0200</pubDate><guid>http://blog.ziade.org/2008/06/19/python-24-tarfile-module-is-buggy-patch-it/</guid></item><item><title>collective.buildbot mailing list</title><link>http://blog.ziade.org/2008/06/13/collectivebuildbot-mailing-list/</link><description>&lt;p&gt;Interested in &lt;a href="http://pypi.python.org/pypi/collective.buildbot"&gt;collective.buildbot&lt;/a&gt; usage or development ? &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Join us in the dedicated google group :
&lt;a href="http://groups.google.com/group/collectivebuildbot"&gt;http://groups.google.com/group/collectivebuildbo&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Reminders: &lt;br /&gt;
-   collective.buildbot is a set of zc.buildout recipes and support for
    declarative configuration for &lt;a href="http://buildbot.net/trac"&gt;Buildbot&lt;/a&gt;.
-   BuildBot is a system to automate the compile/test cycle required by
    most software projects to validate code changes, to set up a
    &lt;a href="http://en.wikipedia.org/wiki/Continuous_integration"&gt;continuous integration system&lt;/a&gt;.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 13 Jun 2008 07:44:00 +0200</pubDate><guid>http://blog.ziade.org/2008/06/13/collectivebuildbot-mailing-list/</guid></item><item><title>zc.buildout on-going work</title><link>http://blog.ziade.org/2008/06/10/zcbuildout-on-going-work/</link><description>&lt;p&gt;Just a quick post about the work going on in zc.buildout. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Work published lately: &lt;br /&gt;
-   Malthe and Mustapha have added a nice option to be able to add or
    substract values in variables that are inherited from an extended
    cfg file (already available, see the doc &lt;a href="http://pypi.python.org/pypi/zc.buildout/1.0.4#variable-substitutions"&gt;here&lt;/a&gt;)
-   I have added an allow-hosts option, that behaves like easy_install
    one, which can be used to restrict some accesses (see &lt;a href="http://pypi.python.org/pypi/zc.buildout/1.0.4#allow-hosts"&gt;here&lt;/a&gt;)
-   Sidnei fixed some annoying bugs (missing quotes in some process
    calls)&lt;/p&gt;
&lt;p&gt;On-going work: &lt;br /&gt;
-   I am working on a timeout config option, to be able to set the
    socket timeout (see &lt;a href="http://tarekziade.wordpress.com/2008/04/07/zcbuildout-monday-trick/"&gt;my previous post&lt;/a&gt; on this). Andreas added a
    command-line option a bit ago as well, but we need to refactor it a
    bit to have it as a config file option.
-   I work on a python API so the buildout can be driven from the code.
    This useful for instance to create tests that are not using a
    os.popen or os.system call to build a buildout: a separated process
    is hard to debug.
-   We are having some thaughts on having a multiple-index enabled
    buildout. Still brainstorming.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 10 Jun 2008 09:20:00 +0200</pubDate><guid>http://blog.ziade.org/2008/06/10/zcbuildout-on-going-work/</guid></item><item><title>Eggs: releasing procedure and continuous integration</title><link>http://blog.ziade.org/2008/06/08/eggs-releasing-procedure-and-continuous-integration/</link><description>&lt;p&gt;&lt;em&gt;Disclaimer: this post is Subversion-centric&lt;/em&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;When releasing a setuptools based-package to the world, developers will
eventually tag it as a stable version, then upload it to PyPI or to
their own website. So typically they create a &lt;strong&gt;branch&lt;/strong&gt; out of the
&lt;strong&gt;trunk&lt;/strong&gt;, fix the &lt;strong&gt;version&lt;/strong&gt;, then create a &lt;strong&gt;tag&lt;/strong&gt; for the release. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Now for continuous development releases, where people get for example a
daily snapshot from the trunk, having a dev suffix to the egg version
makes it possible to distinguish it from a stable release, so
'my.package-2.4dev' will not superseed 'my.package-2.4'. So a given
package will need a &lt;em&gt;dev&lt;/em&gt; prefix in its trunk, that gets removed in the
branch where the next release occurs. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;From there, easy_install will be able to distinguish them. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The question is: &lt;strong&gt;how should we do this ?&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;The Zope 3 way&lt;/h3&gt;
&lt;p&gt;The Zope 3 egg collection has a simple way to manage it, describe here:
[http://svn.zope.org/*checkout*/Sandbox/philikon/foundation/releasing-software.txt][].
The packages have a dev tag suffix in the version metadata, that is
removed in the subversion branch used to tag and release &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This is simple and straight forward. Although, releasing the trunk has
to be done manually. The problem is that any release of the trunk will
have the same name for a given version (my.package-2.4dev) , and in some
case people won't upgrade their environment because the archive keeps
the same name. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;The setuptools way&lt;/h3&gt;
&lt;p&gt;Setuptools comes with a continuous integration feature that allows
people to push the &lt;em&gt;dev&lt;/em&gt; tag in setup.cfg. Then it adds it automatically
to the archive name when it is generated. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;see:
[http://peak.telecommunity.com/DevCenter/setuptools#managing-continuous-releases-using-subversion][]&lt;/p&gt;
&lt;p&gt;Building a release will then consist of using the same process than
Zope 3 one, except that the tag is removed from the setup.cfg file this
time. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Now if you try to release the trunk using disutils &lt;em&gt;sdist&lt;/em&gt; or
&lt;em&gt;bdist_egg&lt;/em&gt; command you will automatically get a dev suffix and the
Subversion revision number sticked to it. This means that each new
revision can generate a new version that will have a distinct name: &lt;br /&gt;
-   my.package-2.4dev-r1245
-   my.package-2.4dev-r1246
-   ...&lt;/p&gt;
&lt;p&gt;easy_install will grab the latest trunk revision when
"my.package-2.4dev" is required, and handle upgrades the right way. This
is better that a manual dev tag because when you re-release a new
version of the trunk, it will superseeds and therefore upgrades previous
revisions. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Another nice feature is to be able to provide to easy_install a
subversion link directly, as long as you append the egg full name to the
link: &lt;br /&gt;
   easy_install http://my.svn/my.package/trunk#egg=my.package-dev&lt;/p&gt;
&lt;h3&gt;collective.releaser&lt;/h3&gt;
&lt;p&gt;The problem with setup.cfg is that if you forget to remove the dev tag
from it when releasing a stable version (you bad boy), you will get the
dev-r4565 suffix in the egg name. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href="http://pypi.python.org/pypi/collective.releaser/"&gt;collective.releaser&lt;/a&gt; takes care of this with a new setuptools
command called &lt;strong&gt;release&lt;/strong&gt;, by creating a release branch and removing
the dev tag automatically. It upgrades the CHANGES.txt file and
version.txt for Plone products. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It also registers and uploads the package to any PyPI-like server. To
decide where the package should be sent, it looks at the
&lt;em&gt;release-packages&lt;/em&gt; variable into the &lt;em&gt;.pypirc&lt;/em&gt;, to see if the package
name matches it. To release all packages to PyPI a default file would
be: &lt;br /&gt;
   [distutils]&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nb"&gt;index&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;servers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;

  &lt;span class="n"&gt;pypi&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;[pypi]&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="err"&gt;username:tarek&lt;/span&gt;

&lt;span class="err"&gt;password:secret&lt;/span&gt;

&lt;span class="cp"&gt;# regular expression-based variable&lt;/span&gt;

&lt;span class="err"&gt;release-packages&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt;

  &lt;span class="err"&gt;.*&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;From there, making a release is as simple as: &lt;br /&gt;
   $ python2.4 setup.py release&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;running&lt;/span&gt; &lt;span class="n"&gt;release&lt;/span&gt;

&lt;span class="n"&gt;This&lt;/span&gt; &lt;span class="nb"&gt;package&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="mf"&gt;0.1.1&lt;/span&gt;

&lt;span class="n"&gt;Do&lt;/span&gt; &lt;span class="n"&gt;you&lt;/span&gt; &lt;span class="n"&gt;want&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="n"&gt;tests&lt;/span&gt; &lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="n"&gt;releasing&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;

&lt;span class="n"&gt;Do&lt;/span&gt; &lt;span class="n"&gt;you&lt;/span&gt; &lt;span class="n"&gt;want&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;create&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;release&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;If&lt;/span&gt; &lt;span class="nb"&gt;no&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;you&lt;/span&gt; &lt;span class="n"&gt;will&lt;/span&gt; &lt;span class="n"&gt;just&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt; &lt;span class="n"&gt;able&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;deploy&lt;/span&gt; &lt;span class="n"&gt;again&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="n"&gt;release&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;

&lt;span class="n"&gt;Enter&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.1.1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="mf"&gt;0.1.2&lt;/span&gt;

&lt;span class="n"&gt;Commiting&lt;/span&gt; &lt;span class="n"&gt;changes&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="n"&gt;Creating&lt;/span&gt; &lt;span class="n"&gt;branches&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="n"&gt;Running&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;mregister sdist bdist_egg mupload -r pypi&amp;quot;&lt;/span&gt;

&lt;span class="n"&gt;running&lt;/span&gt; &lt;span class="n"&gt;mregister&lt;/span&gt;

&lt;span class="n"&gt;Using&lt;/span&gt; &lt;span class="n"&gt;PyPI&lt;/span&gt; &lt;span class="n"&gt;login&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="sr"&gt;/home/&lt;/span&gt;&lt;span class="n"&gt;tarek&lt;/span&gt;&lt;span class="o"&gt;/.&lt;/span&gt;&lt;span class="n"&gt;pypirc&lt;/span&gt;

&lt;span class="n"&gt;Registering&lt;/span&gt; &lt;span class="n"&gt;iw&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resourcetraverser&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;pypi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;pypi&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="n"&gt;running&lt;/span&gt; &lt;span class="n"&gt;mupload&lt;/span&gt;

&lt;span class="n"&gt;Using&lt;/span&gt; &lt;span class="n"&gt;PyPI&lt;/span&gt; &lt;span class="n"&gt;login&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="sr"&gt;/home/&lt;/span&gt;&lt;span class="n"&gt;tarek&lt;/span&gt;&lt;span class="o"&gt;/.&lt;/span&gt;&lt;span class="n"&gt;pypirc&lt;/span&gt;

&lt;span class="n"&gt;Submitting&lt;/span&gt; &lt;span class="n"&gt;dist&lt;/span&gt;&lt;span class="sr"&gt;/iw.resourcetraverser-0.1.2.tar.gz to http://pypi.python.org/&lt;/span&gt;&lt;span class="n"&gt;pypi&lt;/span&gt;

&lt;span class="n"&gt;Submitting&lt;/span&gt; &lt;span class="n"&gt;dist&lt;/span&gt;&lt;span class="sr"&gt;/iw.resourcetraverser-0.1.2-py2.4.egg to http://pypi.python.org/&lt;/span&gt;&lt;span class="n"&gt;pypi&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="mf"&gt;0.1.2&lt;/span&gt; &lt;span class="n"&gt;released&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;As described on its PyPI page, it has a plugin system to perform extra
steps when a release is made. For instance, it is provided with a mail
hook to be able to send mails everytime a release is made. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The tool is pretty new and needs to be smoothed up. For instance, we
need to add a few extra controls like making sure long_description is
reST-valid. But It works, and is being used by a few fellows in the
Plone community already :). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Last, for continuous releases from the trunk, you can set up an alias:&lt;/p&gt;
&lt;p&gt;$ python setup.py alias devrelease mregister sdist mupload&lt;/p&gt;
&lt;p&gt;You will then be able to upload trunk releases with a single call: &lt;br /&gt;
   $ python setup.py devrelease&lt;/p&gt;
&lt;p&gt;The idea is to provide it as the standard tool to release add-on
Products when Plone.org will be upgraded and able to interact with
distutils commands. I will promote it through the work I am doing for
the PSPS task I am championning (&lt;a href="Improve%20release%20procedures%20for%20add-on%20products"&gt;Improve release procedures for add-on
products&lt;/a&gt;), but it has to live a bit. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Help and opinions welcome !&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//s&lt;/span&gt;&lt;span class="n"&gt;vn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;zope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/*checkout*/&lt;/span&gt;&lt;span class="n"&gt;Sandbox&lt;/span&gt;&lt;span class="sr"&gt;/philikon/&lt;/span&gt;&lt;span class="n"&gt;foundation&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;releasing&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;software&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;txt&lt;/span&gt;
&lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;peak&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;telecommunity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="sr"&gt;/DevCenter/s&lt;/span&gt;&lt;span class="n"&gt;etuptools&lt;/span&gt;&lt;span class="c1"&gt;#managing-continuous-releases-using-subversion&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sun, 08 Jun 2008 11:27:00 +0200</pubDate><guid>http://blog.ziade.org/2008/06/08/eggs-releasing-procedure-and-continuous-integration/</guid></item><item><title>DIY PyCon FR shirts</title><link>http://blog.ziade.org/2008/05/21/diy-pycon-fr-shirts/</link><description>&lt;p&gt;We made the shirts at home for PyCon FR and that was a LOT of fun, even
if we had to work hard to make sure all shirt were ready by the time the
event started. We used a silk-screen technique and the output looks
really professional. The ink used makes the shirt design last longer
than shirts that uses transfer. Although the number of colors is
limited. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img alt="image" src="http://tarekziade.files.wordpress.com/2008/05/2511758387_8da10747a8_b.jpg" /&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Have a look at the pictures :
&lt;a href="http://www.flickr.com/photos/82007723@N00/sets/72157605181699082/"&gt;http://www.flickr.com/photos/82007723@N00/sets/72157605181699082/&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Guess what: some people ordered us some t-shirts for some other events,
so some more fun ahead ! :D&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Wed, 21 May 2008 23:28:00 +0200</pubDate><guid>http://blog.ziade.org/2008/05/21/diy-pycon-fr-shirts/</guid></item><item><title>Google AppEngine sprint at Pycon FR</title><link>http://blog.ziade.org/2008/05/14/google-appengine-sprint-at-pycon-fr/</link><description>&lt;p&gt;Late (but exciting) news: a sprint room will be held at &lt;a href="http://fr.pycon.org/"&gt;Pycon FR&lt;/a&gt;, by
the Logilab team, that published a GPL framework on the top of &lt;a href="http://code.google.com/appengine/"&gt;Google
AppEngine&lt;/a&gt;, called &lt;a href="http://www.logilab.org/lax.html"&gt;Logilab Appengine eXtension&lt;/a&gt; (lax). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Participant will be able to learn how to play with AppEngine
technologies. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;All infos (in french):
&lt;a href="http://fr.pycon.org/programme/sprint-appengine"&gt;http://fr.pycon.org/programme/sprint-appengine&lt;/a&gt;&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Wed, 14 May 2008 21:14:00 +0200</pubDate><guid>http://blog.ziade.org/2008/05/14/google-appengine-sprint-at-pycon-fr/</guid></item><item><title>The new .pypirc format in Python / distutils</title><link>http://blog.ziade.org/2008/05/12/the-new-pypirc-format-in-python-distutils/</link><description>&lt;p&gt;Thanks to the sprinters in Washington D.C., (Andrew who worked on
merging it, Alex, etc.) and people that helped in the discussion (Fred,
Martin), my patch to Python for multiple servers handling in .pypirc
file has been integrated in 2.6 trunk. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This patch will allow using distutils &lt;em&gt;register&lt;/em&gt; and upload &lt;em&gt;commands&lt;/em&gt;
with several servers, using the -r option and storing for each one of
them the appropriate username/password in the configuration file. See
&lt;a href="http://wiki.python.org/moin/EnhancedPyPI"&gt;http://wiki.python.org/moin/EnhancedPyPI&lt;/a&gt; for more details. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The &lt;a href="http://pypi.python.org/pypi/collective.dist/"&gt;collective.dist&lt;/a&gt; package provides the same feature for Python
2.4 and 2.5, through new commands: &lt;em&gt;mregister&lt;/em&gt; and &lt;em&gt;mupload&lt;/em&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The new format is also pretty convenient to store several profiles per
servers. For instance, if you have several accounts on PyPI, one for
your company (acme) and one for your own usage, you can define them like
this: &lt;br /&gt;
   [distutils]&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nb"&gt;index&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;servers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;

    &lt;span class="n"&gt;pypi&lt;/span&gt;

    &lt;span class="n"&gt;acme&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pypi&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;username:user&lt;/span&gt;

&lt;span class="n"&gt;password:password&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;acme&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;username:acme_user&lt;/span&gt;

&lt;span class="n"&gt;password:password&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Then use them depending on the package you work in: &lt;br /&gt;
   $ python setup.py register -r acme&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;python&lt;/span&gt; &lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; &lt;span class="n"&gt;register&lt;/span&gt;    &lt;span class="c1"&gt;# default, which is pypi&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;When plone.org will go Plone 3 (this is happening soon), the pypirc
file can be extended with this new target: &lt;br /&gt;
   [distutils]&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nb"&gt;index&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;servers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;

    &lt;span class="n"&gt;pypi&lt;/span&gt;

    &lt;span class="n"&gt;acme&lt;/span&gt;

    &lt;span class="n"&gt;plone&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pypi&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;username:user&lt;/span&gt;

&lt;span class="n"&gt;password:password&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;acme&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;username:acme_user&lt;/span&gt;

&lt;span class="n"&gt;password:password&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;plone&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;repository:http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;plone&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;products&lt;/span&gt;

&lt;span class="n"&gt;username:plone_user&lt;/span&gt;

&lt;span class="n"&gt;password:password&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;From there, setuptools aliases can be used in each package to simplify
things: &lt;br /&gt;
   $ python setup.py alias plone_org register -r plone sdist bdist_egg upload -r plone&lt;/p&gt;
&lt;p&gt;Which will allow you to upload the package to the website in one simple
command: &lt;br /&gt;
   $ python setup.py plone_org&lt;/p&gt;
&lt;p&gt;The patch aslo corrects a few minor bugs in distutils, such as: &lt;br /&gt;
-   .pypirc was not found on Windows
-   pydistutils.cfg was not found on Windows&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Mon, 12 May 2008 12:45:00 +0200</pubDate><guid>http://blog.ziade.org/2008/05/12/the-new-pypirc-format-in-python-distutils/</guid></item><item><title>PyPI usage statistics, the zc.buildout effect</title><link>http://blog.ziade.org/2008/05/08/pypi-usage-statistics-the-zcbuildout-effect/</link><description>&lt;p&gt;It is interesting to look at the usage statistics at PyPI:
[http://pypi.python.org/webstats/usage_200805.html#DAYSTATS][] &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Besides the front page, the most visited pages are in order (same thing
in April): &lt;br /&gt;
-   zc.buildout
-   setuptools
-   zc.recipe.egg
-   plone.recipe.zope2instance
-   plone.recipe.distros
-   python-openid
-   elementtree
-   plone.recipe.zope2install&lt;/p&gt;
&lt;p&gt;Then the packages in the top 60 are mainly zope eggs. This corresponds
to the sequence done by a Plone or Zope being build in a buildout. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you take a closer look at
[http://pypi.python.org/webstats/url_200804.html][], the next top hits
corresponds to zope.* then plone.* packages.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 08 May 2008 20:59:00 +0200</pubDate><guid>http://blog.ziade.org/2008/05/08/pypi-usage-statistics-the-zcbuildout-effect/</guid></item><item><title>Pycon FR is coming up - may 17/18 - ask the program</title><link>http://blog.ziade.org/2008/05/07/pycon-fr-is-coming-up-may-1718-ask-the-program/</link><description>&lt;p&gt;&lt;a href="http://fr.pycon.org/"&gt;Pycon FR&lt;/a&gt; will take place in Paris, may 17/18, at the &lt;a href="http://fr.pycon.org/presentation"&gt;Cité des
sciences et de la Villette&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;A very rich &lt;a href="http://fr.pycon.org/programme"&gt;program&lt;/a&gt; will be presented: &lt;br /&gt;
-   Learn Python first ! (Eric LEBIGOT)
-   &lt;strong&gt;Graphviz&lt;/strong&gt; made easier with &lt;strong&gt;GvGen&lt;/strong&gt; (Sebastien Tricaud)
-   Genes research with Python (Andre Espaze)
-   &lt;strong&gt;CouchDB&lt;/strong&gt; (Benoit Chesneau)
-   &lt;strong&gt;PyPy&lt;/strong&gt; explained (Victor Stinner)
-   Quality Assurance (Julien Jehannet)
-   &lt;strong&gt;Zope 3&lt;/strong&gt; overview (Christophe Combelles)
-   &lt;strong&gt;Scapy&lt;/strong&gt; in action (Renaud Lifchitz)
-   &lt;strong&gt;E-sonoclaste&lt;/strong&gt;, multimedia annotations (Vincent Rioux)
-   Why &lt;strong&gt;Django&lt;/strong&gt; ? (David Larlet)
-   &lt;strong&gt;PLUIE&lt;/strong&gt; project (Michel Claveau)
-   &lt;strong&gt;WSGI&lt;/strong&gt; in practice using &lt;strong&gt;Paste&lt;/strong&gt; (Gael Pasgrimaud)
-   Distributed Version Control with &lt;strong&gt;Mercurial&lt;/strong&gt; (Michael SCHERER)
-   Create and deploy Python apps with &lt;strong&gt;zc.buildout&lt;/strong&gt; (Tarek Ziadé)
-   &lt;strong&gt;Django&lt;/strong&gt; everyday: quality and performance (David Larlet)
-   &lt;strong&gt;Python 3000&lt;/strong&gt; (Victor Stinner)&lt;/p&gt;
&lt;p&gt;There will be &lt;strong&gt;lightning talks&lt;/strong&gt; as well, and the French speaking user
group (Afpy) will have its annual meeting on sunday morning. A second
room will be available for sprints and BoF sessions as well. All the
talks will be in French this year &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you are around Paris, please join use ! It free ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Otherwise, watch the talks in the live stream:
&lt;a href="http://fr.pycon.org/stream-live"&gt;http://fr.pycon.org/stream-live&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Thanks to our partners, for making this happen: &lt;br /&gt;
-   The Python Software Foundation
-   Logilab
-   Nerim
-   Ingeniweb
-   Resolver Systems
-   Pilot Systems
-   WingWare
-   Makina Corpus
-   Toonux
-   ActiveState
-   Gymglish
-   Eyrolles
-   Cité des Sciences et de la Vilette&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Wed, 07 May 2008 13:24:00 +0200</pubDate><guid>http://blog.ziade.org/2008/05/07/pycon-fr-is-coming-up-may-1718-ask-the-program/</guid></item><item><title>Plone Paris Sprint wrapup #3, new.plone.org, collective.dist released !</title><link>http://blog.ziade.org/2008/05/06/plone-paris-sprint-wrapup-3-newploneorg-collectivedist-released/</link><description>&lt;p&gt;The main task I worked on during the sprint with Alex and Matthew was
making PloneSoftwareCenter ready for the new version of Plone.org. These
guys rock. We did tons of things and the new plone.org website is coming
up. Alex worked for quite a while on migrating plone.org to plone 3, but
let me focus on the software center part. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;First of all, let me explain what is the final goal of the work done in
the software center. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;The future of Plonistas: a 100 % egg-based world and what is means&lt;/h3&gt;
&lt;p&gt;for production&lt;/p&gt;
&lt;p&gt;Since a few years, Zope code base was moved into a set of namespaced
packages. Plone is following closely. From there zc.buildout is
providing a simple way to pick up the right set of packages to build an
application. This set is automatically chosen by recipes at Zope and
Plone level. Then developers add their own packages and dependencies to
provide custom features in their applications. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This mechanism means that each team has to: &lt;br /&gt;
-   make distributions of packages (tarball, eggs, ..)
-   build the application with buildout, then release a source
    distribution
-   and provide a way for other developers to build the application on
    their own, by: &lt;br /&gt;
   -   publishing the buildout configuration files
    -   pushing the packages the buildout uses into to a server that is
        reachable by developers
    -   make sure Plone and Zope packages are also reachable&lt;/p&gt;
&lt;p&gt;This means that each team is responsible at least to release packages.&lt;/p&gt;
&lt;h4&gt;Distutils and Cheeseshop&lt;/h4&gt;
&lt;p&gt;Distutils provides two commands to publish packages to the world:
&lt;strong&gt;register&lt;/strong&gt; and &lt;strong&gt;upload&lt;/strong&gt;. These commands were intended to provide to
the developer a way to push packages to any server that supports the
protocol, by using the &lt;em&gt;--repository&lt;/em&gt; option. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But in reality, the only server that is publicly available is the
Cheeseshop (&lt;a href="http://pypi.python.org"&gt;pypi.python.org&lt;/a&gt;). Furthermore Distutils is not providing
everything needed to work with another server than the Cheeseshop, like
I will explain later in this post. So all Plone and Zope packages are
uploaded there. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Therefore, pypi.python.org became a single point of failure when you
need to build a Plone or Zope application. With the growth of the
community, this means that the repository will get bigger and have to
deal with more and more request. PyPI weight around 4 gigas at this
time, of zip files and tarballs. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;While the actual server is really fast, it makes it a bit hard to work
when it is down. It didn't happen often. Twice as far as I remember. But
when it happens, the crowd that uses zc.buildout is frozen because they
run buildouts several time per day. They can use cache of course, as
long as those are up-to-date. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Some mirrors emerged, like
&lt;a href="http://release.ingeniweb.com/pypi.python.org-mirror"&gt;http://release.ingeniweb.com/pypi.python.org-mirror&lt;/a&gt;. Some enhanced
indexes were created as well, like &lt;a href="http://download.zope.org/simple"&gt;http://download.zope.org/simple&lt;/a&gt;,
which is still referring to pypi.python.org but performs a bit of
crawling to speed things up when a buildout uses it through
easy_install. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But all of these are just enhancement to a cheeseshop-centric model. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Furthermore, in case of a private application, &lt;strong&gt;you do not want to see
packages in the Cheeseshop but you might also want to provide developers
a similar way to manage and use them.&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;h4&gt;Plone.org software center is dying !&lt;/h4&gt;
&lt;p&gt;For public applications, given the fact that Distutils provides a
simple way to push a package to a public location (one shell command),
people have started to stop updating the &lt;a href="http://plone.org/products"&gt;&lt;em&gt;Products&lt;/em&gt;&lt;/a&gt; section of
Plone.org. This happens because updating it means doing a whole lot more
than the simple register+upload call. You need to login into the
website, then manually upload the packages, then change the front page
of the product if needed etc.. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So being able to push packages to plone.org with the same set of
commands, is the right solution for developers. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Pushing a package means uploading a public archive but it also means
updating the front page for the given package, with the register
command. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;That what I worked on, at PloneSoftwareCenter level, continuing
Sidnei's work: making it act like Cheeseshop. Now the feature is ready
and being alpha-tested at &lt;a href="http://www.aclark.net/Members/aclark/plone-org-upgrade-update"&gt;new.plone.org&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;h4&gt;Toward a distributed model&lt;/h4&gt;
&lt;p&gt;&lt;img alt="Playing with several egg-based servers" src="http://tarekziade.files.wordpress.com/2008/05/collective-dist.png" /&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;What we will be able to do from there, is to distribute packages (in
blue) to the Cheeseshop (2) as usual, but also to Plone.org (1). Having
such a tool also allows people to run other cheeseshop-like servers,
wether they are public (3) or private (4). This is useful when you are
working on customer projects and do not wish to make their packages
available to the world, or even if it is a public project, you do not
want to push them to PyPi. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Furthermore, it allows having a bit of redundancy : your packages
become available in several places, which is better. Think about the
mirrors at Sourceforge, same thing here... &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This distributed model is used at Ingeniweb, and in other companies I
am starting to list (If you do please let me know, this is important to
promote the tool). &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;The new Plone Software Center (PSC)&lt;/h3&gt;
&lt;p&gt;If you are extensively working with packages and buildouts, you should
consider trying the new PSC, read
&lt;a href=""&gt;http://tarekziade.wordpress.com/2008/03/20/how-to-run-your-own-private-pypi-cheeseshop-server/&lt;/a&gt;
for this. Each project now in the software center can hold several eggs,
and it makes it possible to provide a nice deployment model for your
teams: developer push packages releases in such a server &lt;strong&gt;with the
distutils standards&lt;/strong&gt;, while customers or deployers can build their
applications by picking them up through their buildouts. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;And it is in Plone, so you can provide extensive features, like a bug
tracker, and all the sweet things Plonecan provide. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The work left to be done in PSC is : &lt;br /&gt;
-   making the storage for archives (tgz, egg) pluggable so new storing
    strategies can be provided in separate packages, under the
    collective.psc namespace, I started this work in a branch.
-   finish the collective.psc.mirroring package, that will be used in
    plone.org to fill a system folder, in order to provide an Apache
    direct view over the archives stored. This will make zc.buildout /
    easy_install use this stream rather than hitting the Plone
    instance. Although the final goal will be to transform it into a
    storage strategy, but time is running and this is useful &lt;strong&gt;now&lt;/strong&gt; for
    plone.org.
-   extensive tests on new.plone.org&lt;/p&gt;
&lt;h3&gt;collective.dist&lt;/h3&gt;
&lt;p&gt;To be able to deal with several servers, I have released
&lt;a href="http://pypi.python.org/pypi/collective.dist"&gt;collective.dist&lt;/a&gt; after the sprint (previously iw.dist). This package
provides two new distutils commands: &lt;strong&gt;mupload&lt;/strong&gt; and &lt;strong&gt;mregister&lt;/strong&gt;,
together with a new &lt;strong&gt;pypirc&lt;/strong&gt; format. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Follow the documentation, and you will be set in a matter of minutes.
This package is already used by many developers to make their life
easier, and will help you when the new plone.org goes live. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;disutils evolution&lt;/h3&gt;
&lt;p&gt;collective.dist is an evolution of distutils I have been working at
Python level for months. It is available as a Python patch here :
&lt;a href="http://bugs.python.org/issue1858"&gt;http://bugs.python.org/issue1858&lt;/a&gt;. mregister and mupload are just
modified register and upload commands that makes it possible to interact
with several PyPi-like servers, that's it. PSC uses Distutils standards
in any case, so it is possible to use the regular register and upload
commands with it of course. It is just not convenient because you will
need to have the same username and password on both CheeseShop and the
third-party egg server (or change your pypirc file everytime :D) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So the patch, basically, just makes the -r option of distutils commands
a 100% operational. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Now my job is to convince Python core developers it should be
integrated into Python 2.6, and I am working on this. It is a logical
evolution, but it sounds overkill to some people that does not have this
kind of need: "Why changing that ? we have only one package server,
which is the cheeseshop". &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It can also sound to some of them like I am (together with the team of
20 developers we have ;) ) the only person on earth that wishes it, but
as soon as the new Plone.org will be online, a lot of people will start
needing such a feature. But having it in 2.6 will make it available as a
standard in Plone/Zope world in... a few years. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In any case, most of them agree in the fact that this change is logical
and that the current pypirc format is not to be kept. So I guess it is
just a matter of time and patience.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;tarekziade&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wordpress&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="sr"&gt;/2008/&lt;/span&gt;&lt;span class="mo"&gt;03&lt;/span&gt;&lt;span class="sr"&gt;/20/&lt;/span&gt;&lt;span class="n"&gt;how&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;your&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;own&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;private&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;pypi&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;cheeseshop&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 06 May 2008 12:49:00 +0200</pubDate><guid>http://blog.ziade.org/2008/05/06/plone-paris-sprint-wrapup-3-newploneorg-collectivedist-released/</guid></item><item><title>Plone Paris Sprint wrapup, part #2: collective.buildbot released !</title><link>http://blog.ziade.org/2008/05/02/plone-paris-sprint-wrapup-part-2-collectivebuildbot-released/</link><description>&lt;p&gt;The &lt;a href="http://tarekziade.wordpress.com/2008/04/01/pimp-my-buildbot/"&gt;Pimp my Buildbot&lt;/a&gt; project that was started here at Ingeniweb some
time ago, to be able to set up a buildbot in a matter of minutes with
zc.buildout, was continued during the sprint, and the guys did a great
job on it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It will be used here in customer projects with a Paster that adds
buildbot support when a project starts, because it is a waste of time
for the developers to set everything everytime. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Jean-Francois Roche, Kai Lautaportti and Gael Pasgrimaud added
extensive configuration options (mail, scheduling), and made the SVN
Poller works. This feature allows for instance to make the buildbot
watch a SVN repository without having to add a hook in the server
(post-commit hook for instance), when you don't own it (SourceForge,
collective, etc) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The tool is released in the &lt;a href="http://svn.plone.org/svn/collective/collective.buildbot/trunk/"&gt;collective&lt;/a&gt;, and available at the
&lt;a href="http://pypi.python.org/pypi/collective.buildbot"&gt;Cheeseshop&lt;/a&gt; in one single package ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you want to set a buildbot &lt;br /&gt;
-   provide for each one of your project a buildout that has a test
    script
-   make sure the test script returns exit code (--with-exit-status with
    zope.testing)
-   create a buildout cfg file using collective.buildbot
-   run buildout, that's it !
-   run the master, slaves scripts, and go to the /waterfall page&lt;/p&gt;
&lt;p&gt;Just try out our own &lt;a href="http://buildbot.ingeniweb.com/waterfall"&gt;buildbot&lt;/a&gt; by running this sequence: &lt;br /&gt;
   $ cd /tmp/&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;mkdir&lt;/span&gt; &lt;span class="n"&gt;my_bot&lt;/span&gt;

&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;cd&lt;/span&gt; &lt;span class="n"&gt;my_bot&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;

&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;svn&lt;/span&gt; &lt;span class="n"&gt;co&lt;/span&gt; &lt;span class="n"&gt;https:&lt;/span&gt;&lt;span class="sr"&gt;//i&lt;/span&gt;&lt;span class="n"&gt;ngeniweb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;svn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sourceforge&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;net&lt;/span&gt;&lt;span class="sr"&gt;/svnroot/i&lt;/span&gt;&lt;span class="n"&gt;ngeniweb&lt;/span&gt;&lt;span class="sr"&gt;/buildbot/&lt;/span&gt;&lt;span class="n"&gt;trunk&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;python&lt;/span&gt; &lt;span class="n"&gt;bootstrap&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;

&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;buildout&lt;/span&gt;

&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;master&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;

&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;linux_debian&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;   &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;that&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="k"&gt;our&lt;/span&gt; &lt;span class="n"&gt;slave&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;You should have a buildbot running then at
http://localhost:9000/waterfall &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The tool, without the polling stuff, also works with Mercurial and Bzr,
but probably needs more tests with these repositories. We also need to
make sure the slaves works fine under Windows, and add a nice front page
to the buildbot. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you use it let us know !&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 02 May 2008 09:24:00 +0200</pubDate><guid>http://blog.ziade.org/2008/05/02/plone-paris-sprint-wrapup-part-2-collectivebuildbot-released/</guid></item><item><title>Plone Paris Sprint wrapup, part #1: How do you use eggs and zc.buildout in your projects ?</title><link>http://blog.ziade.org/2008/04/29/plone-paris-sprint-wrapup-part-1-how-do-you-use-eggs-and-zcbuildout-in-your-projects/</link><description>&lt;p&gt;This is the first post of the wrapups I want to do about the sprint that
we had in Paris last week-end. We had a Bird of Feather about how people
use eggs and zc.buildout in their projects, how they release and deploy
them. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;There were some people from Headnet (Anton, Mustapha) and Infrae (Kit,
Sylvain) and Ingeniweb (Me), and we compared a bit how we are working
with eggs, zc.buildout etc. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;That is what I love in our community: companies can share their
knowledge and grow up all together, technically speaking. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;We all have internal recipes, command-line scripts, and are all
relatively happy with zc.buildout. This discussion was very instructive.&lt;/p&gt;
&lt;p&gt;From there, I thaught it would be a good idea to launch a new project
in the community, on the top of zc.buildout (and maybe
zc.sourcerelease), that would provide a common set of tools and
deploying best practices, for people that works with the buildout, no
matter which framework they use (Silva, Plone, etc.) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The first step for this project is to find the common needs and see if
we can join forces to build common tools. To start it up, I decided to
wrap up and release our internal set of tools into a single package
called &lt;a href="http://pypi.python.org/pypi/iw.releaser/"&gt;iw.releaser&lt;/a&gt; and publish it. This is the work I have done
during the last months with the help of Gael, to help our team to work
with zc.buildout in Plone Projects. It is Subversion dependent at this
time. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I am expecting some feedback from Anton and Sylvain to see if we can
make it a collective tool. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This package provides: &lt;br /&gt;
-   a skeleton to build a project structure (buildout, packages; docs,
    etc.) so all projects have a standardized structure
-   a 'release' distutils command that releases a package, upload it to
    the Cheeseshop or other servers, and send a shout out mail
-   a set of command line tools, that can be used to deploy a buildout.
    These commands are doing many things besides launching a buildout
    building (which is a bit different from zc.sourcerelease)&lt;/p&gt;
&lt;p&gt;This package is used at Ingeniweb for a few months now, and I tried to
summarize how it is used in the &lt;a href="http://pypi.python.org/pypi/iw.releaser/"&gt;docs&lt;/a&gt;. I bet a lot of bugs
will be found if you try it, so consider this package as a non-mature
package yet. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Join us all ! So you will be able to release and deploy your
buildout-based apps with a few command-line calls, :D&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 29 Apr 2008 13:02:00 +0200</pubDate><guid>http://blog.ziade.org/2008/04/29/plone-paris-sprint-wrapup-part-1-how-do-you-use-eggs-and-zcbuildout-in-your-projects/</guid></item><item><title>Plone Paris Sprint going on, quick wrapup</title><link>http://blog.ziade.org/2008/04/27/plone-paris-sprint-going-on-quick-wrapup/</link><description>&lt;p&gt;We are working here at the Paris Sprint, and many things are going on. I
wanted to do a quick wrapup before a longer and final wrapup. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here's all the tasks going on (I might miss some): &lt;br /&gt;
-   &lt;strong&gt;Youenn&lt;/strong&gt; and &lt;strong&gt;Cartsen&lt;/strong&gt; are working on
    &lt;a href="http://svn.plone.org/svn/collective/buildout/plone.recipe.apache/"&gt;&lt;strong&gt;plone.recipe.apache&lt;/strong&gt;&lt;/a&gt;, that will let you build and/or setup
    Apache in a buildout
-   &lt;strong&gt;Michel&lt;/strong&gt; presented a very interested talk yesterday on how
    &lt;strong&gt;insecure&lt;/strong&gt; Plone can be (Man in the Middle attacks, etc), on the
    various existing solutions, and works on things to enhance it
-   &lt;strong&gt;Maik&lt;/strong&gt; leads a &lt;a href="http://www.openplans.org/projects/funittest/project-home"&gt;&lt;strong&gt;Funittest&lt;/strong&gt;&lt;/a&gt; group, teaching the tool to
    people, and enhancing i
-   &lt;strong&gt;Olivier&lt;/strong&gt; and &lt;strong&gt;Christophe&lt;/strong&gt; are working on &lt;a href="http://grok.zope.org/"&gt;&lt;strong&gt;Grok&lt;/strong&gt;&lt;/a&gt; to learn
    it. Christophe worked yesterday on filling content to the new
    zope.org web site
-   &lt;strong&gt;Matthew&lt;/strong&gt;, &lt;strong&gt;Alex&lt;/strong&gt; and &lt;strong&gt;I&lt;/strong&gt; are working on
    &lt;a href="http://pypi.python.org/pypi/Products.PloneSoftwareCenter"&gt;&lt;strong&gt;PloneSoftwareCenter&lt;/strong&gt;&lt;/a&gt; and &lt;strong&gt;plone.org&lt;/strong&gt;, so the website can be
    ready to move to an egg-enabled Plone 3. wOOt !
-   &lt;strong&gt;Gael&lt;/strong&gt;, &lt;strong&gt;Jean-Francois&lt;/strong&gt; (and &lt;strong&gt;Kai&lt;/strong&gt; yesterday) are working on
    "&lt;a href="http://tarekziade.wordpress.com/2008/04/01/pimp-my-buildbot/"&gt;&lt;strong&gt;Pimp my Buildbot&lt;/strong&gt;&lt;/a&gt;": configure and launch a complete buildbot
    system in a matter of minutes, using a simple buildout cfg file and
    running a simple command. I think this could be used for Plone
    repository and the collective as well ! The tool will be move to the
    collective at the end of the sprint.
-   &lt;strong&gt;Gilles&lt;/strong&gt;, &lt;strong&gt;Cyrille&lt;/strong&gt;, and others (sorry, can't keep up with all
    names), are working on &lt;a href="http://products.ingeniweb.com/catalog/iw.fss"&gt;&lt;strong&gt;iw.fss&lt;/strong&gt;&lt;/a&gt; (FileSystemStorage for plone
    3)
-   &lt;strong&gt;Mustapha&lt;/strong&gt; and &lt;strong&gt;Anton&lt;/strong&gt; are making sweet things in
    &lt;a href="http://plone.org/products/zopeskel"&gt;&lt;strong&gt;ZopeSkel&lt;/strong&gt;&lt;/a&gt;
-   &lt;strong&gt;Anton&lt;/strong&gt; worked also on finding the best way to do aysnhronous
    tasks in Plone&lt;strong&gt; &lt;br /&gt;
&lt;br /&gt;
&lt;/strong&gt;
-   There's a big &lt;a href="http://www.plonegov.org/"&gt;&lt;strong&gt;PloneGov&lt;/strong&gt;&lt;/a&gt; team that is working on making the
    tool friendly with various government systems.
-   &lt;strong&gt;Jean-Nicolas&lt;/strong&gt; leads the work on &lt;strong&gt;kss/eventPush&lt;/strong&gt;, so we all get
    a upload widget with live feedback (no flash inside ;))
-   &lt;strong&gt;Godefroid&lt;/strong&gt; and &lt;strong&gt;Lennart&lt;/strong&gt; are &lt;strong&gt;Grok&lt;/strong&gt;-ing as well;
-   &lt;strong&gt;Christophe&lt;/strong&gt; is preparing [&lt;strong&gt;collective.kss.fly_guy&lt;/strong&gt;][], a tool
    that provides drag and drop in folder_contents
-   &lt;strong&gt;Harito&lt;/strong&gt;, &lt;strong&gt;Christian&lt;/strong&gt; and &lt;strong&gt;Wim&lt;/strong&gt; are making a sweet new theme
    for Plone: "&lt;strong&gt;Notre Dame&lt;/strong&gt;". The theme is kept quite generic, with
    all the plone tools/portlets designed in blue tones, kept in mind to
    be easy adjustable and changed to other color schemes. It will be a
    table-less egg for Plone 3.&lt;/p&gt;
&lt;p&gt;More blogging tomorrow, I need to get back to my task :)&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sun, 27 Apr 2008 10:51:00 +0200</pubDate><guid>http://blog.ziade.org/2008/04/27/plone-paris-sprint-going-on-quick-wrapup/</guid></item><item><title>Help Plone.org, help Alex Clark</title><link>http://blog.ziade.org/2008/04/21/help-ploneorg-help-alex-clark/</link><description>&lt;p&gt;Alex is trying to raise money for his travel expenses, to go to the
&lt;a href="http://www.openplans.org/projects/plone-3-paris-sprint/project-home"&gt;Plone Paris Sprint&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Alex will work on making Plone.org go Plone 3, so this is important ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Help him here :
&lt;a href="http://www.aclark.net/Members/aclark/help-me-upgrade-plone.org"&gt;http://www.aclark.net/Members/aclark/help-me-upgrade-plone.org&lt;/a&gt;&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Mon, 21 Apr 2008 12:18:00 +0200</pubDate><guid>http://blog.ziade.org/2008/04/21/help-ploneorg-help-alex-clark/</guid></item><item><title>Pycon FR is not competing with EuroPython</title><link>http://blog.ziade.org/2008/04/13/pycon-fr-is-not-competing-with-europython/</link><description>&lt;p&gt;&lt;strong&gt;edit&lt;/strong&gt;: I have changed the blog title, which was not correct in
English, thanks Maik ;) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I wanted to react on &lt;a href="http://faassen.n--tree.net/blog/view/weblog/2008/04/12/0"&gt;Martinj's blog&lt;/a&gt; to make things clear about
&lt;a href="http://fr.pycon.org"&gt;Pycon FR&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Background&lt;/h3&gt;
&lt;p&gt;First of all here's a little background about Pycon FR. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href="http://afpy.org"&gt;Afpy&lt;/a&gt; (Association Francophone Python) is a french Python user group
created four years ago. The group counts around 100 members and is the
only one of this size in french-speaking countries as far as we know. It
has members from France, Belgium, Canada and North Africa countries. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Many meetings are organized throughout the year, mainly in Paris, and
sometimes in Charleroi in Belgium. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;An important event took place at the "Cité des sciences" in Paris last
summer, called "Journées Python" We had around 80 attendees and a rich
program was given during two days. Topics where presented by french
speaking people from France and Belgium, and we had talks about AI,
Django, Zope, TuxDroid, etc &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Some videos are available on the web, you can probably reach them from
here: &lt;a href="http://video.google.fr/videosearch?q=journees.afpy.org"&gt;http://video.google.fr/videosearch?q=journees.afpy.org&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The conference was 100% free for attendees. We also decided to chose
carefully the dates so the conference would not compete with other
events like Europython. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This event was a *true* success. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This event is now called Pycon FR, to be in the same line of what
happens in some other countries. This makes a lot of sense indeed, to
have national Python events, with standardized names. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Attendees&lt;/h3&gt;
&lt;p&gt;Pycon FR attendees are mainly people that speaks english but for some
of them not enough to easily follow English talks. Most of those people
would not make it to Europython because of this language barrier, and a
national conference held in Paris is something that was quite missing at
this time, for them. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So PyCon FR is just a sign of the growing interest about Python, and
should be seen as a 100% positive and constructive thing, believe me. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Together with Europython and Pycon US, the "local" PyCons are in my
opinion, a very good thing to spread the good word. We will, like last
year, promote Europython there. Maybe we could even do a survey this
year to know exactly who goes to Europython, etc. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Pycon FR expansion plans&lt;/h3&gt;
&lt;p&gt;When I said in my last blog entry that we were working on making it
less french-centric, and having international attendees, I should have
explained it better to make sure no one thinks Pycon FR might be a bad
thing for Europython. &lt;br /&gt;
-   If we do invite international speakers, it will be done only if we
    are able to provide a live translation system.
-   The event date will always be picked carefully, as usual, so it does
    not compete with Europython.&lt;/p&gt;
&lt;h3&gt;Europython and local PyCons expansion&lt;/h3&gt;
&lt;p&gt;Here's my opinion about what Europython strategy could be: since it is
an international english-speaking event that gathers a lot of people. It
could be the seed to set up a local Pycon event in places where it does
not exist yet, using the momentum of the event. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This could be done by working with the local user group this way, by
organizing maybe two years in a row Europython there. So, maybe we could
see "Pycon Lithunia" next year ? Then move EuroPython to Amsterdam and
see "Pycon The Netherlands" in two years ? &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Europython exists to promote the language at an international level, so
that could be a good way to spread imho. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But please, pretty please, don't see local Pycons as Europython
competitors, this is not true, this happens just because Python is
growing. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Everyone should be happy about that I think. I disagree with the fact
that each conference might fight to have the "best international
speakers" for a very simple reason: the number of speakers is growing
too, and I don't think either Pycon either EuroPython will ever miss of
interesting talks. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The next smart move I think is to organize and synchronize all these
events, and that is where the Python Software Foundation has a role to
play in my opinion. They have started anyway, and I am really confident
about what will happen next.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sun, 13 Apr 2008 10:59:00 +0200</pubDate><guid>http://blog.ziade.org/2008/04/13/pycon-fr-is-not-competing-with-europython/</guid></item><item><title>Pycon FR is coming !</title><link>http://blog.ziade.org/2008/04/12/pycon-fr-is-coming/</link><description>&lt;p&gt;&lt;a href="http://fr.pycon.org"&gt;PyCon FR&lt;/a&gt; is coming up ! We published yesterday &lt;a href="http://fr.pycon.org/programme"&gt;the final
program&lt;/a&gt;, and I must say that this second annual meeting of Pythoneers
in Paris, looks very good. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Extracts: &lt;br /&gt;
-   Why you should learn Python
-   GraphViz with GvGen
-   Gene search
-   CouchDB
-   PyPy
-   Quality Assurance
-   Scapy
-   Zope 3
-   zc.buildout
-   Django
-   WSGI
-   etc...&lt;/p&gt;
&lt;p&gt;This is going to be two intense days in Paris, so if you are around,
please join us, it's free. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Paris - &lt;a href="http://fr.pycon.org/presentation"&gt;Cité des Sciences et de la Vilette&lt;/a&gt; - 16/17 May . &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The talks will be in french, but the organization team works hard to
have english talks as well next year. Anyway, you don't need to speak
french to join us and have a good time ;)&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sat, 12 Apr 2008 07:31:00 +0200</pubDate><guid>http://blog.ziade.org/2008/04/12/pycon-fr-is-coming/</guid></item><item><title>zc.buildout monday trick :)</title><link>http://blog.ziade.org/2008/04/07/zcbuildout-monday-trick/</link><description>&lt;p&gt;When a site used by your buildout is not responding, you can stare at it
for ... ever &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Add these two lines in your bin/buildout script: &lt;br /&gt;
   import socket&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nb"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setdefaulttimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;With this, the buildout will go to the next link after ten seconds.
This trick made my day :)&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Mon, 07 Apr 2008 16:01:00 +0200</pubDate><guid>http://blog.ziade.org/2008/04/07/zcbuildout-monday-trick/</guid></item><item><title>Doctests: specify the target readership</title><link>http://blog.ziade.org/2008/04/05/doctests-specify-the-target-readership/</link><description>&lt;p&gt;Ben Bangert was &lt;a href="http://groovie.org/articles/2008/04/04/sacrificing-readability-for-automated-doc-tests"&gt;reacting&lt;/a&gt; about &lt;a href="http://pypi.python.org/pypi/zc.buildout"&gt;zc.buildout&lt;/a&gt; doctests, saying that
they are hard to read from the PyPI page, and the examples hard to use
and follow. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I agree with Ben as these doctests are very hard to read when you are
not familiar with zc.buildout testing modules, which provides a set of
API the doctests relies on. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But from a developer point of view, adding a feature to such a package
is best done through doctests, using zc.buildout.testing goodies. And a
developer that is familiar with this package, will find this doctest
very useful. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;zc.buildout in any case, is trying to structurize its PyPI front page,
and push a maximum amount of doc for users, so ... kudos ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I think the problem is more about &lt;strong&gt;specifying the target readership&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;I would like to point another example that comes in zc.buildout:
dependency-links. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The main doctest that appears at PyPI as a light, human-readable
section: [http://pypi.python.org/pypi/zc.buildout#dependency-links][] &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;And the very same section is continued in a specific doctest that does
not appear on the main page:
&lt;a href=""&gt;http://svn.zope.org/zc.buildout/trunk/src/zc/buildout/dependencylinks.txt?rev=81182&amp;amp;view=markup&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;So what happened here is that the developer specified two kind of
readers: &lt;br /&gt;
-   people that will reach the package through its PyPI page
-   people that will go deeper in how the package works, through recipes
    or tutorials&lt;/p&gt;
&lt;p&gt;From there I think there's a simple guideline that could be applied to
enhance the package documentation when adding a feature: &lt;br /&gt;
-   resume the feature in the main page (long_description) with
    examples that does not rely on specific testing API. In other words
    it should be made of plain english and plain python when needed;
-   leave doctest that relies on internal testing API as complementary
    documentation.
-   define for each doctest its nature (recipe, tutorial, etc)&lt;/p&gt;
&lt;p&gt;How could we help people doing such structuration ? &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The distutils metadata could be a good place to do it, by adding an
extra_doctests list for example, that would contain a list of doctests.
From there, PyPI could display the long_description text as usual, and
add a "more information section". &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Let's take an example: &lt;br /&gt;
   def _(f)&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;my.package&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="n"&gt;long_decription&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;README.txt&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;

&lt;span class="n"&gt;extra_description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;recipe&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;create_this.txt&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;

&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;do_that.txt&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;

&lt;span class="s"&gt;&amp;#39;tutorial&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;how_to_use.txt&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;how_to_2.txt&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)]}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;From there, PyPI could provide a Table of content, with a structurized
documentation, and additional pages for the package, grouped by types
(recipe, tutorial) etc. -&gt; Maybe a Sphinx-powered PyPI ? :) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;By the way, I have another post related, which tries to summarized good
pratices in technical writing :
&lt;a href=""&gt;http://tarekziade.wordpress.com/2007/02/23/technical-writing-the-seven-laws&lt;/a&gt;&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//s&lt;/span&gt;&lt;span class="n"&gt;vn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;zope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/zc.buildout/&lt;/span&gt;&lt;span class="n"&gt;trunk&lt;/span&gt;&lt;span class="sr"&gt;/src/&lt;/span&gt;&lt;span class="n"&gt;zc&lt;/span&gt;&lt;span class="sr"&gt;/buildout/&lt;/span&gt;&lt;span class="n"&gt;dependencylinks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;txt&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="n"&gt;rev&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;81182&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;markup&lt;/span&gt;
&lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;tarekziade&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wordpress&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="sr"&gt;/2007/&lt;/span&gt;&lt;span class="mo"&gt;02&lt;/span&gt;&lt;span class="sr"&gt;/23/&lt;/span&gt;&lt;span class="n"&gt;technical&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;writing&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;seven&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;laws&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sat, 05 Apr 2008 13:53:00 +0200</pubDate><guid>http://blog.ziade.org/2008/04/05/doctests-specify-the-target-readership/</guid></item><item><title>Pimp my buildbot !</title><link>http://blog.ziade.org/2008/04/01/pimp-my-buildbot/</link><description>&lt;p&gt;&lt;strong&gt;Edit : this post is a bit deprecated, the project is now called
collective.buildbot. See:
&lt;a href="http://pypi.python.org/pypi/collective.buildbot"&gt;http://pypi.python.org/pypi/collective.buildbot&lt;/a&gt;&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;When a project team use Test-Driven Development to build the code
(everyone should), the next step is to set up automate builds, as
explained in &lt;a href="http://en.wikipedia.org/wiki/Continuous_integration"&gt;continous integration&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This is where &lt;a href="http://buildbot.net/trac"&gt;Buildbot&lt;/a&gt; is great. It is easy and flexible to
install, even more since &lt;a href="http://twistedmatrix.com/trac/"&gt;Twisted&lt;/a&gt; has been eggified. A few
easy_install steps are now sufficient to create a buildbot waterfall.
Configuring a buildbot requires writing a few Python scripts though, and
this has to be done everytime a project starts. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In my work, I need to be able to set buildbots in a matter of minutes,
and they are always similar. They just need a buildmaster, a buildslave,
and a few rules. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;A first attempt to make things easier is to write a &lt;a href="http://pythonpaste.org/"&gt;Python Paste&lt;/a&gt;
script that generates default files. This is not very flexible though,
as upgrades are still a bit tedious. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;A more interesting solution is to provide &lt;a href="http://pypi.python.org/pypi/zc.buildout"&gt;zc.buildout&lt;/a&gt; recipes that
take care of buildmaster and buildslave generation, through a very
simple configuration file. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;We have started this project, in order to be able to launch buildbot
within a buildout. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The project has three parts: &lt;br /&gt;
-   &lt;a href="https://ingeniweb.svn.sourceforge.net/svnroot/ingeniweb/iw.buildbot/trunk/"&gt;&lt;strong&gt;iw.buildbot&lt;/strong&gt;:&lt;/a&gt; a thin layer on the top of Buildbot that allows
    to drive it with configuration files, instead of Python code. In
    other words, it makes buildbot configuration based on declarative
    configuration file and dynamic Python code, instead of declarative
    Python code.
-   &lt;a href="https://ingeniweb.svn.sourceforge.net/svnroot/ingeniweb/iw.recipe.buildmaster/trunk/"&gt;&lt;strong&gt;iw.recipe.buildmaster&lt;/strong&gt;&lt;/a&gt;: a zc.buildout recipe that creates a
    buildbot instance together with configuration files.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://ingeniweb.svn.sourceforge.net/svnroot/ingeniweb/iw.recipe.buildbslave/trunk/"&gt;&lt;strong&gt;iw.recipe.buildslave&lt;/strong&gt;&lt;/a&gt;: a zc.buildout recipe that creates a
    buildslave&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The result is that creating a buildbot is done in a few lines in the
buildout.cfg file: &lt;br /&gt;
   [buildout]&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;parts =

  buildmaster

  linux_debian

[buildmaster]

recipe = iw.recipe.buildmaster

project-name = Ingeniweb Public buildbot

project-url = http://ingeniweb.com

port = 8999

wport = 9000

url = http://buildbot.ingeniweb.com

slaves =

  linux_debian    xxxxx

projects =

  iw.recipes

[linux_debian]

recipe = iw.recipe.buildslave

host = localhost

port = &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;buildmaster&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;

password = xxxx

[iw.recipes]

slave-name=linux_debian

base-url=http://ingeniweb.svn.sourceforge.net

repository=/svnroot/ingeniweb/projects/iw.recipes/

branch=buildout

build-sequence =

    python bootstrap.py

    bin/buildout

test-sequence =

    bin/test -v --exit-with-status
&lt;/pre&gt;&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;buildmaster&lt;/strong&gt; defines the project name and url, the buildbot web
    port, slave port and url, and a list of slaves and projects.&lt;/li&gt;
&lt;li&gt;each project has his own section, where the Subversion path is
    defined, as well as the build sequence and the test sequence.&lt;/li&gt;
&lt;li&gt;each slave is defined in its section&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;From there other buildouts can be created to group packages to be
tested, and to define a script that can be used for tests. For Zope
applications, that would be &lt;em&gt;zopectl&lt;/em&gt; of course, as long as it is used
with &lt;em&gt;--exit-with-status&lt;/em&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This is the case for &lt;a href="https://ingeniweb.svn.sourceforge.net/svnroot/ingeniweb/projects/iw.recipes/buildout/buildout.cfg"&gt;iw.recipes&lt;/a&gt;: it grabs all iw.recipes.* eggs to
hook them to a testrunner. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The result can be see here: &lt;a href="http://buildbot.ingeniweb.com/"&gt;http://buildbot.ingeniweb.com&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The next steps are to make sure everything works fine under Windows,
and see how things goes in various projects, then make it work with all
kinds of VCS and schedulers. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I will probably propose a sprint task on this in &lt;a href="http://www.openplans.org/projects/plone-3-paris-sprint/project-home"&gt;Paris sprint&lt;/a&gt; and
see if the package meets interest.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 01 Apr 2008 12:00:00 +0200</pubDate><guid>http://blog.ziade.org/2008/04/01/pimp-my-buildbot/</guid></item><item><title>Plone Paris sprint D-30</title><link>http://blog.ziade.org/2008/03/25/plone-paris-sprint-d-30/</link><description>&lt;p&gt;This is a reminder if you wish to join us in Paris, April 25-27.
(week-end) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;There will be a Plone sprint, and we expect around 30 people so far. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;All infos are located on the OpenPlans page here:
&lt;a href=""&gt;http://www.openplans.org/projects/plone-3-paris-sprint/project-home&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;There's also a Grok sprint a few days after Paris one :
&lt;a href="http://wiki.zope.org/grok/GrokkerdamSprint"&gt;http://wiki.zope.org/grok/GrokkerdamSprint&lt;/a&gt; &lt;br /&gt;
so this is a good opportunity to do a Python european tour ;)&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;openplans&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/projects/&lt;/span&gt;&lt;span class="n"&gt;plone&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;paris&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;sprint&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 25 Mar 2008 09:02:00 +0100</pubDate><guid>http://blog.ziade.org/2008/03/25/plone-paris-sprint-d-30/</guid></item><item><title>iw.recipe.backup and other zc.buildout recipes</title><link>http://blog.ziade.org/2008/03/25/iwrecipebackup-and-other-zcbuildout-recipes/</link><description>&lt;p&gt;A new recipe has been added to the iw.recipe.* family:
&lt;a href="http://pypi.python.org/pypi/iw.recipe.backup#detailed-documentation"&gt;iw.recipe.backup&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you use zc.buildout to deploy Python applications in production,
this one can be used to backup a buildout folder, before any upgrade is
attempted, or for a daily backup. It creates an archive with a timestamp
on its name, in a defined folder, with the whole folder content. Some
directories can be excluded if needed. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This script is not doing fine-grained backups of the Data.fs like
repozo.py does, but can be combined with it if this finesse is required.
The recipe creates two scripts in the bin folder: &lt;strong&gt;backup&lt;/strong&gt; and
&lt;strong&gt;restore&lt;/strong&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;backup&lt;/strong&gt; creates an archive and takes no parameters, whereas
&lt;strong&gt;restore&lt;/strong&gt; takes an archive name and decompress it into the buildout
folder. The latter does a backup before it decompresses the archive for
more safety. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Last, it will raise an error if the backup folder is located within the
buildout folder ;), and logs all commands into a log file. This is a
tiny recipe, but provides a simple way to backup your buildouts in a
single call, no matter the platform you are in. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Other recipes we provide at this time are: &lt;br /&gt;
-   &lt;a href="http://pypi.python.org/pypi/iw.recipe.cmd"&gt;iw.recipe.cmd&lt;/a&gt;: provides a way to execute shell calls, or inline
    Python calls. This is useful when you need to set a few things when
    a buildout is built, that does not worth creating a new recipe;
-   &lt;a href="http://pypi.python.org/pypi/iw.recipe.fetcher"&gt;iw.recipe.fetcher&lt;/a&gt;: similar to wget. We use it to build
    python-win32.zip archive, to get all installers on the web;
-   &lt;a href="http://pypi.python.org/pypi/iw.recipe.fs"&gt;iw.recipe.fss&lt;/a&gt;: will let you configure FileSystemStorage, so no
    extra step is required;
-   &lt;a href="http://pypi.python.org/pypi/iw.recipe.pound"&gt;iw.recipe.pound&lt;/a&gt;: compiles Pound load balancer, and creates its
    configuration file, so it can be run directly;
-   &lt;a href="http://pypi.python.org/pypi/iw.recipe.sendmail"&gt;iw.recipe.sendmail&lt;/a&gt;: set zope.sendmail as the default mail sender
    in a Zope 2-based application. This is useful if you wish to have a
    fast and robust mailhost service in your Plone sites;
-   &lt;a href="http://pypi.python.org/pypi/iw.recipe.squid"&gt;iw.recipe.squid&lt;/a&gt;: This recipe installs all parts needed to run a
    Squid server dedicated to serve a Zope application, staying friendly
    with Apache and setting things nicely for Plone;
-   &lt;a href="http://pypi.python.org/pypi/iw.recipe.subversion"&gt;iw.recipe.subversion&lt;/a&gt;: recipe to checkout a svn location into a
    part. It is different from &lt;a href="http://pypi.python.org/pypi/infrae.subversion"&gt;infrae.subversion&lt;/a&gt; because it creates
    a tarball out of the subversion checkout, and put it in the
    downloads folder. In other words, it works offline as well as long
    as the tarball was built once. It doesn't have the nice feature
    infrae.subversion provides which is a code checker that will raise
    an error when buildout tries to remove a changed file. But as we use
    the develop feature of buildout to create our code, we didn't need
    that feature. Subversion checkouts are useful to us when we want to
    use a bleeding-edge version of a third-party package that has not
    been released yet;
-   &lt;a href="http://pypi.python.org/pypi/iw.recipe.template"&gt;iw.recipe.template&lt;/a&gt;: this one provides a way to write files based
    on Cheetah templates, like Python Paste does, and is useful for
    micro-needs, like iw.recipe.cmd.&lt;/p&gt;
&lt;p&gt;If you use one of those recipes and whish to make a feature request or
a bug report, I have created a new space on plone.org to have a bug
tracker for all recipes: &lt;a href="http://plone.org/products/iw-recipes/issues"&gt;http://plone.org/products/iw-recipes/issues&lt;/a&gt;&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 25 Mar 2008 07:20:00 +0100</pubDate><guid>http://blog.ziade.org/2008/03/25/iwrecipebackup-and-other-zcbuildout-recipes/</guid></item><item><title>Sphinx as a doc builder for Python projects</title><link>http://blog.ziade.org/2008/03/23/sphinx-as-a-doc-builder-for-python-projects/</link><description>&lt;p&gt;Last year, I worked on a documentation builder based on doctest and
reStructuredText format called &lt;a href="http://hg.programmation-python.org/browser/pycommunity/pycommunity"&gt;PyCommunity&lt;/a&gt;. This tool is collecting
doctests from Python packages and from special places on a source
repository, basing its work on a set of project good practices I had
presented and used in a Pycon 2007 tutorial (&lt;a href="http://programmation-python.org/pycommunity/pycon/PyCon07/slides.html"&gt;slides&lt;/a&gt;). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Basically, it uses the best practices described in Andreas Ruping book,
called &lt;em&gt;Agile Documentation&lt;/em&gt;, applied to Python projects using TDD and
doctests. It can be called Document-Driven Development. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I have never found the time to finish the tool and I was looking
forward to get back to it. As a matter of fact, George Brandl has
released the tool that is used to generate Python documentation, called
&lt;a href="http://sphinx.pocoo.org"&gt;Sphinx&lt;/a&gt;, which does many things I still have in my TODO list, like
&lt;a href="http://pygments.org/"&gt;pygments&lt;/a&gt; integration, and many other things. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;See how Sphinx generates Python doc here:
&lt;a href="http://docs.python.org/dev"&gt;http://docs.python.org/dev&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;From there, it becomes dead simple to generate a website for a Python
project, based on packages doctests and text files and on a specific
doc/ structure. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Sphinx annoucement is really exciting, and it shouldn't be too much
pain to bundle it in a buildout recipe to manage a project
documentation. Since it is based on templates and configuration files, a
default structure can be generated to startup a project documentation
together with a code base. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I have to try and see if Sphinx allows me to set everything up. In
other words, replay my Pycon tutorial with it.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sun, 23 Mar 2008 09:08:00 +0100</pubDate><guid>http://blog.ziade.org/2008/03/23/sphinx-as-a-doc-builder-for-python-projects/</guid></item><item><title>zc.buildout and Plone at OSCON&amp;#039;08</title><link>http://blog.ziade.org/2008/03/21/zcbuildout-and-plone-at-oscon03908/</link><description>&lt;p&gt;Cool, my proposal for &lt;a href="http://en.oreilly.com/oscon2008/public/content/home"&gt;OSCON 2008&lt;/a&gt; has been accepted, and will be
about Plone 3, zc.buildout and the work on creating private PyPI servers
using PloneSoftwareCenter. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It will present how we work at Ingeniweb with both public and private
packages, to deliver Plone applications to customers, using eggs and
buildout. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It's funny because since a few days, there are a lot of discussions
around distutils, PyPI and setuptools, and about making things better at
Python level on how to distribute packages and applications. So it seems
to be a hot topic. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;From my point of view, PyPI brought a lot in the past years in this
area, and being able to deploy a pypi-compatible software center in a
company helps a lot in using the same set of command line tool.
(distutils/setuptools) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So, I am pretty happy nowadays with zc.buildout and setuptools (thanks
to Mr Fulton, Eby and al), despite all the critical that has been made
about setuptools in the last few days, and despite the fact that it is
*so hard* to make a tiny little change make it to distutils trunk :'(
... &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Anyways, if you do Plone or Zope dev, and if you are interested about
software delivery, I'd be glad to exchange about it, to see how you work
and deliver Zope apps, to get other point of views before OSCON. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here's the abstract of my talk: &lt;br /&gt;
Software delivery for complex systems in Python/Zope used to be a little
bit homemade: people usually used custom scripts to deploy their
systems, or relied on generic installation tools. For Plone
applications, most of the time a complex installation guide was provided
to the customer, with a list of dependencies to install and system
changes to take care of.&lt;/p&gt;
&lt;p&gt;The Python Package Index (PyPI), formerly the &lt;strong&gt;Cheeseshop&lt;/strong&gt;, brought a
few years ago a new way to distribute Python applications, together with
&lt;strong&gt;setuptools&lt;/strong&gt;. It made it possible to install a Python library the same
way package systems like &lt;em&gt;apt&lt;/em&gt; or &lt;em&gt;yum&lt;/em&gt; does. From there people started
to deliver their software in separated components, called &lt;strong&gt;eggs&lt;/strong&gt;.
Since most applications in Plone are now egg-based, it is possible to
install a software with a list of eggs.&lt;/p&gt;
&lt;p&gt;zc.buildout provides a descriptive language to list all eggs needed for
a software to run and a plugin system that allows to customize each
steps.This talk will present a case study of a Plone application life
cycle:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;environment building - creating the buildout and its recipes&lt;/li&gt;
&lt;li&gt;continuous integration with buildbot – running the buildout on
    target systems&lt;/li&gt;
&lt;li&gt;deploying – preparing and packaging the buildout for an offline
    installation&lt;/li&gt;
&lt;li&gt;updating – preparing and releasing an update&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And will present a set of extra tool we have built on the top of
zc.buildout to standardize our projects developments and help the
developers: &lt;br /&gt;
-   a set of templates to start a buildout-based project in subversion
-   a tool to create buildbot slaves automatically, given a buildout
-   a diff tool to ease the upgrade of a buildout that is in production.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 21 Mar 2008 18:51:00 +0100</pubDate><guid>http://blog.ziade.org/2008/03/21/zcbuildout-and-plone-at-oscon03908/</guid></item><item><title>how to run your own private PyPI (Cheeseshop) server</title><link>http://blog.ziade.org/2008/03/20/how-to-run-your-own-private-pypi-cheeseshop-server/</link><description>&lt;p&gt;PloneSoftwareCenter 1.5 is heavily developed and not yet released, but
the current trunk code is working well to use it as a PyPI-like server.
It can be really useful for companies that develop Python software and
are looking for a way to centralize their eggs internally. That's what
we use now at Ingeniweb to work on customer projects.&lt;/p&gt;
&lt;p&gt;This tutorial explains how to set a cuttting-edge PloneSoftwareCenter
server, if you want to be an early-adopter of what will be the next
version running on plone.org software center in a few months (but this
code is to be used at your own risks of course ;))&lt;/p&gt;
&lt;h1&gt;Why a private PyPI ?&lt;/h1&gt;
&lt;p&gt;You have some python packages you want to treat the same way the
Cheeseshop does. In other words you want to work with them with
[&lt;em&gt;easy_install&lt;/em&gt;][], &lt;em&gt;&lt;a href="http://pypi.python.org/pypi/zc.buildout"&gt;zc.buildout&lt;/a&gt;&lt;/em&gt;, etc.. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But these packages are private to your company... &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The simplest way is to store your eggs on some network folder. But
&lt;em&gt;distutils&lt;/em&gt; and &lt;em&gt;setuptools&lt;/em&gt; provide a nice set of commands to
automatically build and upload eggs at PyPI or any server that
implements PyPI apis.&lt;/p&gt;
&lt;h1&gt;How ? PloneSoftwareCenter !&lt;/h1&gt;
&lt;p&gt;The Plone community provides a nice tool to manage Python packages, and
it is PyPI compatible. In other words it can act like Cheesehop to
interact with your command line tools. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It also provides an extensive set of features to manage your releases,
run your bug trackers, etc. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;See the &lt;a href="http://plone.org/products/"&gt;plone.org products section&lt;/a&gt;, it is powered by PSC.&lt;/p&gt;
&lt;h1&gt;4 steps to install&lt;/h1&gt;
&lt;p&gt;Thanks to zc.buildout, a Plone Software Center (PSC) is really easy to
setup. There's no binary distribution yet though, so you need to compile
a few things. &lt;br /&gt;
&lt;/p&gt;
&lt;h2&gt;Step 1 - Pre-requests&lt;/h2&gt;
&lt;p&gt;If you are under Windows, grab this archive :
&lt;a href=""&gt;http://release.ingeniweb.com/third-party-dist/python2.4.4-win32.zip&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;decompress it, and run "install.bat". It will install Python 2.4
together with a set of tools, and PATH updates. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you are under Linux, make sure you have gcc, subversion and make
installed. Then install easy_install and PIL, &lt;br /&gt;
like this: &lt;br /&gt;
   $ wget http://peak.telecommunity.com/dist/ez_setup.py&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;python&lt;/span&gt; &lt;span class="n"&gt;ez_setup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;

&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;easy_install&lt;/span&gt; &lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;release&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ingeniweb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="sr"&gt;/third-party-dist/&lt;/span&gt;&lt;span class="n"&gt;PILwoTk&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;1.1.6.4&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gz&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h2&gt;Step 2 - installing PSC&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Make a directory on your system called &lt;em&gt;softwarecenter&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Get into it and grab PSC code with the svn command line:&lt;/p&gt;
&lt;p&gt;$ svn co http://svn.plone.org/svn/collective/Products.PloneSoftwareCenter/buildout/trunk .&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;run the buildout with this set of commands:&lt;/p&gt;
&lt;p&gt;$ python bootstrap.py&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;buildout&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;It will take some time, to grab all elements needed to build PSC.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;run the server &lt;br /&gt;
       $ bin/instance start&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Step 3 - setting up PSC&lt;/h2&gt;
&lt;p&gt;Let's create a Plone website with a PloneSoftwareCenter instance: &lt;br /&gt;
1.  Open a browser and go to &lt;a href="http://localhost:8080/manage"&gt;http://localhost:8080/manage&lt;/a&gt;. The
    login/password is admin/admin.
2.  On the left part, there's a dropbox, select "Plone Site" the hit the
    add button
3.  In the form, set the id to "plone" and hit enter.
4.  Go to [http://localhost:8080/plone/prefs_install_products_form][]
5.  Check "PloneSoftwareCenter" on the left side and hit "Install"
6.  Go to &lt;a href="http://localhost:8080/plone"&gt;http://localhost:8080/plone&lt;/a&gt;
7.  In the "Add new..." menu, Click on "software center"
8.  In the form, in the Title, put "Catalog"
9.  Check Use Classifiers to display Categories (with Topic :: *) under
    Classifiers
10. Hit the Save button&lt;/p&gt;
&lt;p&gt;Your Software Center is ready and available at
&lt;a href="http://localhost:8080/plone/catalog"&gt;http://localhost:8080/plone/catalog&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Step 4 - setting up the client-side&lt;/h2&gt;
&lt;p&gt;Now let's set the client-side so people can use your Software Center: &lt;br /&gt;
1.  &lt;br /&gt;
   install iw.dist:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;   &lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;easy_install&lt;/span&gt; &lt;span class="n"&gt;iw&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dist&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;create a file in your home directory, called .pypirc with this
    content:&lt;/p&gt;
&lt;p&gt;[distutils]&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nb"&gt;index&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;servers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;

  &lt;span class="n"&gt;pypi&lt;/span&gt;

  &lt;span class="nb"&gt;local&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pypi&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;username:YOUR_PYPI_LOGIN&lt;/span&gt;

&lt;span class="n"&gt;password:YOUR_PYPI_PASSWORD&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;local&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;repository:http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;localhost:8080&lt;/span&gt;&lt;span class="sr"&gt;/plone/c&lt;/span&gt;&lt;span class="n"&gt;atalog&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;

&lt;span class="n"&gt;username:admin&lt;/span&gt;

&lt;span class="n"&gt;password:admin&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Of course, the localhost value will differ if you are located on
another machine.. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;iw.dist adds two new commands in distutils: mregister and mupload.
These commands enhance register and upload to make distutils work with
multiple servers. This should be merged hopefully in Python 2.6 very
soon.&lt;/p&gt;
&lt;h1&gt;Let's use it !&lt;/h1&gt;
&lt;p&gt;Now, you will have two new commands in distutils, called '&lt;em&gt;mregister&lt;/em&gt;'
and '&lt;em&gt;mupload&lt;/em&gt;' that will let you use either your PSC either PyPI. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Let's upload an egg into PSC: &lt;br /&gt;
   $ python setup.py mregister sdist bdist_egg mupload -r local&lt;/p&gt;
&lt;p&gt;Let's upload an egg into PyPI: &lt;br /&gt;
   $ python setup.py mregister sdist bdist_egg mupload -r pypi&lt;/p&gt;
&lt;p&gt;if -r is omited, pypi is the default one. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you want to use PSC in zc.buildout or easy_install, you can provide
&lt;a href="http://localhost:8080/plone/catalog/simple"&gt;http://localhost:8080/plone/catalog/simple&lt;/a&gt; as a find-links or index
value: &lt;br /&gt;
   [buildout]  find-links = http://localhost:8080/plone/catalog/simple&lt;/p&gt;
&lt;p&gt;Or: &lt;br /&gt;
   $ easy_install -f http://localhost:8080/plone/catalog/simple my.egg&lt;/p&gt;
&lt;p&gt;That's it !&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;release&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ingeniweb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="sr"&gt;/third-party-dist/&lt;/span&gt;&lt;span class="n"&gt;python2&lt;/span&gt;&lt;span class="mf"&gt;.4.4&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;win32&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;zip&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 20 Mar 2008 11:26:00 +0100</pubDate><guid>http://blog.ziade.org/2008/03/20/how-to-run-your-own-private-pypi-cheeseshop-server/</guid></item><item><title>plone.recipe.cluster, one control script to rule them all</title><link>http://blog.ziade.org/2008/03/06/plonerecipecluster-one-control-script-to-rule-them-all/</link><description>&lt;p&gt;There is something that is a bit missing in a buildout environment to be
able to drive it with no pain : a gobal script that allows starting,
stopping or restarting every software that is bundled in it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For instance, a buildout often has a Zeo server, a Zope instance and
some other servers like Pound, Apache or Squid (just control scripts and
sometimes a whole build. A global script can handle this by running a
suite of commands for each action: start, stop, restart.) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Let's take an example: we want to run a Zeo server, a Zope client, and
a pound load balancer. These actions can be listed in a buildout section
like this: &lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;[buildout] ...

[cluster]

recipe = plone.recipe.cluster

poundctl = &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;buildout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;/pound -f &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;buildout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;/parts/pound/etc/pound.cfg -c &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;buildout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;/var/pound.pid

start =

    &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;buildout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;/zeoserver start

    &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;buildout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;/instance start

    &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;poundctl&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;

stop =

    &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;buildout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;/zeoserver stop

    &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;buildout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;/instance stop

    pid:&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;buildout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;/var/pound.pid

restart =

    &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;buildout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;/zeoserver restart

    &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;buildout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;/instance restart

    &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;poundctl&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The zeoserver and instance scripts are daemons that just need to be
called, and the pound script which is also running as a daemon, do not
have any option to be stopped, but will let the user define a pid file.
So having a way to kill the pid defined by this file is enough in this
case. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The recipe can therefore build a control script that launches the set
of commands for each action: &lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cluster&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;

&lt;span class="n"&gt;Starting&lt;/span&gt; &lt;span class="n"&gt;Cluster&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cluster&lt;/span&gt; &lt;span class="n"&gt;stop&lt;/span&gt;

&lt;span class="n"&gt;Stopping&lt;/span&gt; &lt;span class="n"&gt;cluster&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Let's take another example: a buildout that runs a simple script that
is not daemonized. In other words, it needs to be run in the background
and its pid saved, so the cluster recipe will know how to stop it: &lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;[cluster]&lt;/span&gt;

&lt;span class="na"&gt;recipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;plone.recipe.cluster&lt;/span&gt;

&lt;span class="na"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;

    &lt;span class="err"&gt;background:${buildout:directory}/bin/script&lt;/span&gt;

    &lt;span class="err"&gt;${buildout:bin-directory}/instance&lt;/span&gt; &lt;span class="err"&gt;start&lt;/span&gt;

&lt;span class="na"&gt;stop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;

    &lt;span class="err"&gt;${buildout:bin-directory}/instance&lt;/span&gt; &lt;span class="err"&gt;stop&lt;/span&gt;

&lt;span class="na"&gt;restart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;

    &lt;span class="err"&gt;background:${buildout:directory}/bin/script&lt;/span&gt;

    &lt;span class="err"&gt;${buildout:bin-directory}/instance&lt;/span&gt; &lt;span class="err"&gt;restart&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;In this case, the &lt;em&gt;background:&lt;/em&gt; prefix will indicated the recipe to
execute the command in the background, and to keep its PID in a file.
When the stop command will be called, it will be stopped automatically.&lt;/p&gt;
&lt;p&gt;I think that all cases are covered by this recipe, and I have a
prototype working for Linux/Mac here: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href=""&gt;http://svn.plone.org/svn/collective/buildout/plone.recipe.cluster/trunk&lt;/a&gt;
. I took back and adapted some work done by Blue Dynamics
(&lt;a href="http://svn.plone.org/svn/collective/bda.daemon/trunk/bda/daemon/"&gt;bda.daemon&lt;/a&gt;). System programming is a bit over my head so it needs
some more work in the coming days, but it is working already. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Then, the next step will be to make it win32 compliant, using NT
Services for this, and hopefully releasing a first 0.1.0 version. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you feel that there's some missing use cases, let me know ! (the
full README is &lt;a href="http://svn.plone.org/svn/collective/buildout/plone.recipe.cluster/trunk/plone/recipe/cluster/README.txt"&gt;here&lt;/a&gt;)&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//s&lt;/span&gt;&lt;span class="n"&gt;vn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plone&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/svn/co&lt;/span&gt;&lt;span class="n"&gt;llective&lt;/span&gt;&lt;span class="sr"&gt;/buildout/&lt;/span&gt;&lt;span class="n"&gt;plone&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recipe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cluster&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;trunk&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 06 Mar 2008 22:29:00 +0100</pubDate><guid>http://blog.ziade.org/2008/03/06/plonerecipecluster-one-control-script-to-rule-them-all/</guid></item><item><title>How to add rotatezlogs in your buildout</title><link>http://blog.ziade.org/2008/03/02/how-to-add-rotatezlogs-in-your-buildout/</link><description>&lt;p&gt;&lt;a href="http://pypi.python.org/pypi/iw.rotatezlogs"&gt;iw.rotatezlogs&lt;/a&gt; is a very useful package made by Gilles Lenfant to
rotate log file in Zope. This is quite useful to make sure they don't
get huge. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I have made the necessary changes in &lt;a href="http://pypi.python.org/pypi/plone.recipe.zope2instance/"&gt;plone.recipe.zope2instance&lt;/a&gt; and
&lt;a href="http://pypi.python.org/pypi/plone.recipe.zope2zeoserver/"&gt;plone.recipe.zope2zeoserver&lt;/a&gt; to make it available in a buildout
environment. Since those changes are now released, here's the way to add
the log rotator in your buildout if you wish to use it: &lt;br /&gt;
   [buildout]&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;...

[instance]

...

event-log-custom =

    %import iw.rotatezlogs

    &lt;span class="nt"&gt;&amp;lt;rotatelogfile&amp;gt;&lt;/span&gt;

        path &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;buildout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;folder&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;/var/log/event.log

        max-bytes 1MB

        backup-count 5

    &lt;span class="nt"&gt;&amp;lt;/rotatelogfile&amp;gt;&lt;/span&gt;

access-log-custom =

    %import iw.rotatezlogs

    &lt;span class="nt"&gt;&amp;lt;rotatelogfile&amp;gt;&lt;/span&gt;

        path &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;buildout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;folder&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;/var/log/instance-Z2.log

        max-bytes 1MB

        backup-count 5

    &lt;span class="nt"&gt;&amp;lt;/rotatelogfile&amp;gt;&lt;/span&gt;

eggs =

    ...

    iw.rotatezlogs

[zeo]

...

zeo-log-custom =

    %import iw.rotatezlogs

    &lt;span class="nt"&gt;&amp;lt;rotatelogfile&amp;gt;&lt;/span&gt;

        path &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;buildout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;folder&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;/var/log/zeoserver.log

        max-bytes 1MB

        backup-count 5

    &lt;span class="nt"&gt;&amp;lt;/rotatelogfile&amp;gt;&lt;/span&gt;

eggs =

    ...

    iw.rotatezlogs
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The package is added in the eggs section to make sure the recipes adds
it in the Python path when the script (zope or zeo) is loaded. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Gilles told us that the rotator will be integrated in ZConfig itself
sometimes, so it will be even simpler to use.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sun, 02 Mar 2008 20:43:00 +0100</pubDate><guid>http://blog.ziade.org/2008/03/02/how-to-add-rotatezlogs-in-your-buildout/</guid></item><item><title>plone.recipe.zope2zeoserver and Windows</title><link>http://blog.ziade.org/2008/02/21/plonerecipezope2zeoserver-and-windows/</link><description>&lt;p&gt;The &lt;a href="http://pypi.python.org/pypi/plone.recipe.zope2zeoserver/0.11"&gt;0.11&lt;/a&gt; release of plone.recipe.zope2zeoserver is out. If you are
under Windows you will probably enjoy this upgrade. The &lt;em&gt;zeoctl&lt;/em&gt;
starting script was not working under win32 because it is based on
zdaemon, which is Linux-specific. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I have added two scripts in the recipe, to be able to launch Zeo: &lt;br /&gt;
-   at the command line, with bin\zeo.bat
-   as a service, with bin\zeoservice.exe install/start/stop/remove&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 21 Feb 2008 09:27:00 +0100</pubDate><guid>http://blog.ziade.org/2008/02/21/plonerecipezope2zeoserver-and-windows/</guid></item><item><title>Pylint installation made easier</title><link>http://blog.ziade.org/2008/02/20/pylint-installation-made-easier/</link><description>&lt;p&gt;&lt;strong&gt;EDIT: logilab.pylininstaller is now deprecated, since Logilab made
Pylint easy_installable now.&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;So, just call : easy_install pylint.&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I love &lt;a href="http://www.logilab.org/project/name/pylint"&gt;Pylint&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Correctly configured, it is really useful to raise your code quality.
But it can be really painful to install if you are not under a
Debian-like or OpenSuse-like system, because of its dependencies that
are not namespaced packages (logilab-common and logilab-astng). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In other words, don't try to easy_install the packages that are on
PyPI, it will not work. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;That's why I have created an egg, called &lt;a href="http://pypi.python.org/pypi/logilab.pylintinstaller"&gt;logilab.pylintinstaller&lt;/a&gt;,
that will let you install it as easy as: &lt;br /&gt;
   $  easy_install logilab.pylintinstaller&lt;/p&gt;
&lt;p&gt;It bundles all dependencies and grabs pylint 0.14, then installs
everything. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Thanks to Sylvain Thénault from Logilab for his help on this.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Wed, 20 Feb 2008 17:12:00 +0100</pubDate><guid>http://blog.ziade.org/2008/02/20/pylint-installation-made-easier/</guid></item><item><title>Plone egg developers, please use iw.dist !</title><link>http://blog.ziade.org/2008/02/20/plone-egg-developers-please-use-iwdist/</link><description>&lt;p&gt;This is my first important step in order to fullfill the Strategic
Summit task #7817 (&lt;a href="http://dev.plone.org/plone/ticket/7817"&gt;http://dev.plone.org/plone/ticket/7817&lt;/a&gt;). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I have released the &lt;a href="http://pypi.python.org/pypi/iw.dist"&gt;iw.dist&lt;/a&gt; package which is a replacement for
&lt;em&gt;register&lt;/em&gt; and &lt;em&gt;upload&lt;/em&gt; commands. Theses changes will be pushed in
Python 2.6 hopefully. Until then, iw.dist provides two new commands,
called &lt;em&gt;mregister&lt;/em&gt; and &lt;em&gt;mupload&lt;/em&gt;, which are acting the same way. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Please, use them instead of &lt;em&gt;register&lt;/em&gt; and &lt;em&gt;upload&lt;/em&gt; ! So I can get some
feedback ;) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It is quite simple to set up, see this page:
&lt;a href="http://pypi.python.org/pypi/iw.dist"&gt;http://pypi.python.org/pypi/iw.dist &lt;br /&gt;
&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It will not bather you at all since it does what the regular commands
do, but are the first step to a tool that will let you upload the eggs
to plone.org.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Wed, 20 Feb 2008 14:52:00 +0100</pubDate><guid>http://blog.ziade.org/2008/02/20/plone-egg-developers-please-use-iwdist/</guid></item><item><title>Plone Strategic Summit: Improve releasing procedures for plone.org add-ons</title><link>http://blog.ziade.org/2008/02/13/plone-strategic-summit-improve-releasing-procedures-for-ploneorg-add-ons/</link><description>&lt;p&gt;I have been suggested as a "champion" at the Plone Strategic Summit on
this task: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;# Improve release procedures for add-ons on plone.org: document a
release process, and create release tools for packaging and uploading
products from the command line.&lt;/em&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This is great, because I have been working a lot in this area in the
past month and I think I have a precise idea on what should be done in
the Plone community to improve add-ons products visibility and releasing
process. I am going to expose here the steps I think we should take, and
how to do them, so people can give some feedbacks. Most of them were
already explained on this blog on several entries. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;PyPI vs Plone.org&lt;/h3&gt;
&lt;p&gt;The Cheeseshop (PyPI) is now playing an important role in Plone
development. Everytime a Plone 3 application is built somewhere in the
world, the Cheeseshop is serving hundreds of tarballs and eggs. Since
Zope and Plone has been eggified, and since zc.buildout has been used as
the standard way to build a Plone application, Plone developers are
releasing all their eggs at PyPI. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This releasing process is really convenient, as a package can be
uploaded, and shout out in just one command: &lt;br /&gt;
   $ python setup.py register sdist bdist_egg upload&lt;/p&gt;
&lt;p&gt;And an alias can make it even simpler: &lt;br /&gt;
   $ python setup.py release&lt;/p&gt;
&lt;p&gt;The problem is that many Plone.org add-on products pages that used to
be up-to-date are not upgraded anymore. So Basically, Plone.org Software
Center is dying because of the actual releasing process of eggs.. &lt;br /&gt;
&lt;/p&gt;
&lt;h4&gt;The SPOF problem&lt;/h4&gt;
&lt;p&gt;Another issue with the PyPI-centralized development process is that it
becomes a Single Point of Failure. In other words, if PyPI is down, all
the buildouts out there are stocked, unless you have a up-to date egg
cache on your side. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;PyPI though, together with distutils, was thaught as a distributed
system: you can theorically call register and upload commands to any
server that implements the PyPI Apis. But there are no other PyPI-like
server yet in the community. The PyPI code is open source for sure, and
anyone could take it and run his own PyPI... &lt;br /&gt;
&lt;/p&gt;
&lt;h4&gt;PloneSoftwareCenter features&lt;/h4&gt;
&lt;p&gt;Another risk we have with a PyPI-centric approach is loosing the
features that PSC provides at Plone.org. Those are great, and should be
used by all add-ons out there. Milestones, bug tracker, etc.. Everything
is provided at plone.org for someone to promote and work with his
product. &lt;br /&gt;
&lt;/p&gt;
&lt;h4&gt;The solution we should take&lt;/h4&gt;
&lt;p&gt;To avoid the problems mentioned, we need to: &lt;br /&gt;
-   make PloneSoftwareCenter, therefore Plone.org, PyPI-compatible
-   make distutils command-line tools able to interact with several
    PyPI-compatible servers, besides the official one
-   provide a simple guideline for the Plone community to work with
    these tools&lt;/p&gt;
&lt;h4&gt;The steps&lt;/h4&gt;
&lt;h5&gt;PSC&lt;/h5&gt;
&lt;p&gt;Sidnei has created 2 years ago a branch for PSC with an experimental
PyPI support. I have taken this work and continued it on a branch that
is almost finished. My goal is to finish it at &lt;a href="http://www.openplans.org/projects/plone-3-paris-sprint/project-home"&gt;the Paris Sprint&lt;/a&gt;, so
PSC will be fully PyPI-compliant. I will soon blog on this to describe
the work. &lt;br /&gt;
&lt;/p&gt;
&lt;h5&gt;.pypirc and distutils&lt;/h5&gt;
&lt;p&gt;In order to be able to interact with several PyPI-like server, the
.pypirc file need to evolve. I made a patch and a proposal (see:
&lt;a href="http://wiki.python.org/moin/EnhancedPyPI"&gt;http://wiki.python.org/moin/EnhancedPyPI&lt;/a&gt;) I will try to push in the
next Python Bug Day in two weeks, so it is integrated in Python 2.6. If
it is accepted, I will release a library that implements the same patch,
but for Python 2.4 and 2.5, through specific setuptools commands. &lt;br /&gt;
&lt;/p&gt;
&lt;h5&gt;guideline&lt;/h5&gt;
&lt;p&gt;From there, I guess a guideline can be written, explaining : &lt;br /&gt;
-   how to create a Plone 3 package (through skeletons)
-   how to release it to both PyPI and plone.org&lt;/p&gt;
&lt;p&gt;Another point of interest will be to explain how to deal with several
egg servers in a buildout. &lt;br /&gt;
&lt;/p&gt;
&lt;h4&gt;Proposed calendar&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Mid-March&lt;/em&gt; : provide a package to handle the new .pypirc format&lt;/li&gt;
&lt;li&gt;&lt;em&gt;End of March&lt;/em&gt; : submit the guideline, based on the PSC current
    branch, and a public instance of the new PSC so people can try it.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;End of April &lt;/em&gt;: finalize PSC PyPI support at the Paris Sprint,
    together with Alex Clark, so it is available to Plone.org when it
    goes Plone 3&lt;/li&gt;
&lt;li&gt;After that: submit a guideline on how Plone companies can use PSC to
    create a private PSC, and work together with PyPI, plone.org and
    their own PSC, in a buildout environment&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I have also proposed an OSCON topic on this, in Portland, in July. So
if my talk is accepted, this can be a good place to promote Plone.org's
Plone Software Center.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Wed, 13 Feb 2008 17:51:00 +0100</pubDate><guid>http://blog.ziade.org/2008/02/13/plone-strategic-summit-improve-releasing-procedures-for-ploneorg-add-ons/</guid></item><item><title>Plone Paris Sprint, 25/27 April</title><link>http://blog.ziade.org/2008/02/13/plone-paris-sprint-2527-april/</link><description>&lt;p&gt;Hey, fellow Plone/Zope developer, this is a quick reminder if you would
like to join us at the Paris sprint in April. There will be a lot of
work and fun ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you want to come, please: &lt;br /&gt;
-   join the openplans project here :
    &lt;a href=""&gt;http://www.openplans.org/projects/plone-3-paris-sprint/project-home&lt;/a&gt;
-   send a mail on the project mailing list if you would like to suggest
    a topic
-   send me a mail if you are sure to come, so I can add you in the
    attendee list. If you need a hotel, let me know. Let me also know if
    you are ok to spare a room.&lt;/p&gt;
&lt;p&gt;Hope to see you there !&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;openplans&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/projects/&lt;/span&gt;&lt;span class="n"&gt;plone&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;paris&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;sprint&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Wed, 13 Feb 2008 16:10:00 +0100</pubDate><guid>http://blog.ziade.org/2008/02/13/plone-paris-sprint-2527-april/</guid></item><item><title>Using RelStorage with Plone 3</title><link>http://blog.ziade.org/2008/02/02/using-relstorage-with-plone-3/</link><description>&lt;p&gt;Last week, Shane Hattaway made available &lt;a href="http://wiki.zope.org/ZODB/RelStorage"&gt;RelStorage&lt;/a&gt; on svn.zope.org.
This product replaces PGStorage and allows you to switch from
FileStorage (the Data.fs file) to a Postgresql or Oracle storage for
ZODB pickles. ZEO is not needed anymore in that configuration, and each
Zope instance calls the same Postgresql server. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;According to Shane, RelStorage handles high concurrency better than the
standard combination of ZEO and FileStorage. So I have started to make a
few tests with it to see how we can use it on some projects. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I have made a few changes to plone.recipe.zope2instance to be able to
define a buildout that uses RelStorage, and created a buildout on the
collective that runs a Plone 3 over RelStorage. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The buildout sets a relstorage configuration and patches the ZODB 3.7
code. Instead of a file-storage option in the buildout.cfg file, a
rel-storage option is added: &lt;br /&gt;
   rel-storage =&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;    &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="n"&gt;postgresql&lt;/span&gt;

    &lt;span class="n"&gt;dbname&lt;/span&gt; &lt;span class="n"&gt;zodb&lt;/span&gt;

    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="n"&gt;postgres&lt;/span&gt;

    &lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="n"&gt;localhost&lt;/span&gt;

    &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="n"&gt;postgres&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;If you want to try it with your Postgres or Oracle server, you can get
it from the collective here:
&lt;a href="http://svn.plone.org/svn/collective/collective.relstorage/trunk/"&gt;http://svn.plone.org/svn/collective/collective.relstorage/trunk/&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Notice: the script that patches the ZODB code does a system call
instead of using the difflib module, so you need to have the patch
program available at the prompt. So I guess it will fail under windows.&lt;/p&gt;
&lt;p&gt;Notice 2: The buildout installs psycopg2 egg, but not the oracle one.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sat, 02 Feb 2008 23:10:00 +0100</pubDate><guid>http://blog.ziade.org/2008/02/02/using-relstorage-with-plone-3/</guid></item><item><title>Buildout: plone.org and pypi.python.org are acting like SPOF</title><link>http://blog.ziade.org/2008/01/30/buildout-ploneorg-and-pypipythonorg-are-acting-like-spof/</link><description>&lt;p&gt;Yesterday, plone.org was moved on another server. It was an horrible day
for our people here that didn't have a local cache of eggs to build
their instances. So plone.org was acting like a Single Point Of
Failure(SPOF) for some packages. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;A few developers, that are under windows, were even having permission
denied errors on their buildout because when a package is badly
downloaded is not correctly crushed before a new attempt (I need to add
a ticket about this in setuptools tracker I guess). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Anyway, we decided to create a mirror here, (I am buiding it at
http://release.ingeniweb.com/ this morning hopefully) to avoid such
problems. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This makes me think that zc.buildout should introduce a high-level
mirror mechanism in the find-links variable, that would let someone
explicitely provide a list of mirror. It could look like this: &lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;links&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;

  &lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;pypi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/simple |  http://release.ingeniweb.com/&lt;/span&gt;&lt;span class="n"&gt;pypi&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;mirror&lt;/span&gt;

  &lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;dist&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plone&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;  &lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;release&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ingeniweb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;plone&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;dist&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;mirror&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;It could be used to switch the find-links values sent to setuptools
when the primary url is down by attempting a simple call with a timeout.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Wed, 30 Jan 2008 09:20:00 +0100</pubDate><guid>http://blog.ziade.org/2008/01/30/buildout-ploneorg-and-pypipythonorg-are-acting-like-spof/</guid></item><item><title>Snow sprint report #4 : a new command in zc.buildout + a context-free grammar text generator</title><link>http://blog.ziade.org/2008/01/27/snow-sprint-report-4-a-new-command-in-zcbuildout-a-context-free-grammar-text-generator/</link><description>&lt;p&gt;So this was the last sprint day here at the snowsprint, and a lot of
work was done to wrap-up some of the tasks. On my side I worked on two
topics: &lt;br /&gt;
-   adding a new describe command to &lt;a href="http://pypi.python.org/pypi/zc.buildout"&gt;zc.buildout&lt;/a&gt;
-   coding a random-text generator library&lt;/p&gt;
&lt;h3&gt;Adding a "describe" command to zc.buildout&lt;/h3&gt;
&lt;p&gt;Godefroid came up with a nice idea about buildouts: when you are
working with a recipe, it's sometimes hard to figure out what are the
options it takes, which ones are optionals, what are the default, etc. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It means that you have to digg into the code, or get to the PyPI page.
Hopefully this page will give you the infos, if the long_description
variable was hooked into some reSTructuredText. (see &lt;a href="https://tarekziade.wordpress.com/wp-admin/So%20this%20was%20the%20last%20sprint%20day%20here%20at%20the%20snowsprint,%20and%20a%20lot%20of%20work%20was%20done%20to%20wrapup%20some%20of%20the%20tasks.%20On%20my%20side%20I%20worked%20on%20two%20topics:"&gt;iw.recipe.pound&lt;/a&gt;
for example). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;That make a lot of context changes for the developer, so basically, the
idea of the new describe command is to be able to query for a given
recipe help. This help will be displayed online as long as the recipe
creator fills the Recipe class docstring. We checked with Jim that this
would be a good idea, since he wants (and that's good) to keep the
recipe as simple as possible (basically, any class with an install and
an update commands). Since he liked the idea, we started to code it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So basically, the command is called like this: &lt;br /&gt;
       $ bin/buildout describe my.recipes&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;    &lt;span class="k"&gt;my&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recipes&lt;/span&gt;

        &lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;coolest&lt;/span&gt; &lt;span class="n"&gt;recipe&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;Earth&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

        &lt;span class="n"&gt;Ever&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;It deals with recipe versions and takes care of multiple entry points:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;   &lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;buildout&lt;/span&gt; &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="k"&gt;my&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recipes:default&lt;/span&gt; &lt;span class="k"&gt;my&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recipes:second&lt;/span&gt;

    &lt;span class="k"&gt;my&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recipes:default&lt;/span&gt;

        &lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;coolest&lt;/span&gt; &lt;span class="n"&gt;recipe&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;Earth&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

        &lt;span class="n"&gt;Ever&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

    &lt;span class="k"&gt;my&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recipes:second&lt;/span&gt;

        &lt;span class="n"&gt;No&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="n"&gt;available&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This feature looks quite simple, but was a bit tricky to implement,
since we had to parse the working set of the current buildout to extract
the infos. The version section is also taken care of. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;All that work we did together with Godefroid and Dokai is in a branch,
waiting for Jim's feedback. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Coding a random-text generator&lt;/h3&gt;
&lt;p&gt;When we worked on benchmarking Solr versus Plain catalog on the
indexing task, we created a small script to generate random text, based
on a chomsky algorithm. We were really excited about going deeper in
this topic. Both Dokai and I worked on some generators. I have written
on my side a Python port of &lt;a href="http://nonsense.sourceforge.net/"&gt;nonsense&lt;/a&gt;, and the results were pretty
interesting. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Anyway, we started a fun task for the last day with Dokai and Ethan:
write a random-text generator library and a grok-based web app on the
top of it. I worked on the core part, and we came up with this cool
command line scripts that would generate som random text, given a file
that would provide structure of sentences, and for each part of the
sentence a list of choices. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The command is building the sentences picking the choices randomly. For
example this file adapted from nonsense (extract): &lt;br /&gt;
   [gibberish]&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;default =

    &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;course&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;

name = college

annoucement =

    The &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;university&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt; class &amp;quot;&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;course&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;&amp;quot; has been cancelled due to lack of interest.

    Starting next year, incoming freshman at &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;university&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt; will be required to take &amp;quot;&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;course&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;.&amp;quot;

    &amp;quot;&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;course&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;&amp;quot; will no longer be offered at &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;university&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt; due to lack of interest.

    Due to overwhelming popularity, an additional section of &amp;quot;&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;course&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;&amp;quot; will be offered at &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;university&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt; next semester.

    Not one single student signed up for &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;university&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;&amp;#39;s &amp;quot;&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;course&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;&amp;quot; last semester.

course =

    &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;adjective&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt; &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;noun&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt; &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;suffix&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;

    &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;adjective&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt; &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;noun&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;: &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;ending&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;

    &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;adjective&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt; &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;noun&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt; And &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;adjective&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt; &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;noun&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt; &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;suffix&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;

    &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;noun&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt; &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;noun&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt; &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;suffix&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;

    &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;group1&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt; &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;group2&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt; &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;life&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt; &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;suffix&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;

    &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;group2&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt; &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;noun&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt; &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;life&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt; &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;suffix&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;

    &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;group1&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt; &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;group2&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt; &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;life&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt; Since {#1800-1970}

    &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;group2&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt; &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;life&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;: &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;ending&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;

 event =

    The African Diaspora

    The Harlem Renaissance

    The Civil Rights Movement

    The Italian Renaissance

    Westward Expansion

    Manifest Destiny

    Women&amp;#39;s Suffrage

    World War I

    World War II

    The War Of 1812

    The American Revolution

    The French Revolution

    The Russian Revolution

    The American Civil War

    The Spanish-American War

    The Franco-Prussian War

    The JFK Assasination

action =

    Basketweaving

    Aquatic Ballet

    Synchronized Swimming

    Professional Sports

    The &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;adjective&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt; Pottery Experience

    Home Economics

    Cardplaying

    Birdwatching

noun =

    Diversity

    Globalism

 ...
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;will generate random, domain-specific text. A Grok application has been
built on the top on this, allowing dynamic creation of such files, and
online text generation. Check out &lt;a href="http://blogs.hexagonit.fi/kai/"&gt;Dokai's blog&lt;/a&gt; about this during the
week, as he will present the Grok part. The code is in a Git repo here:
&lt;a href="http://repo.or.cz/w/gibberis.ch.git"&gt;http://repo.or.cz/w/gibberis.ch.git&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Thank you Lovely Systems&lt;/h3&gt;
&lt;p&gt;Thanks to the Lovely team for this sprint !&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sun, 27 Jan 2008 11:06:00 +0100</pubDate><guid>http://blog.ziade.org/2008/01/27/snow-sprint-report-4-a-new-command-in-zcbuildout-a-context-free-grammar-text-generator/</guid></item><item><title>Snow sprint report #3 : ZopeSkel refactoring</title><link>http://blog.ziade.org/2008/01/22/snow-sprint-report-3-zopeskel-refactoring/</link><description>&lt;p&gt;Today, while the zope instances we have prepared for the benchmarks
where suffering from being fed with 40 000 documents, I have worked on a
small task I wanted to do some times ago: refactor a bit ZopeSkel and
add some tests in it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I splitted the template objects that were located in separate modules,
and added for each one of them a doctest that is running the template.
This prevents the template to be broken because when you work in it, it
is not obvious. As a matter of fact, at the time I did it, I found one
template to be broken, so I think this is going to be useful to prevent
regressions. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you work on this package and change or add a template, please,
pretty please, run the following to make sure nothing is broken: &lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;cd&lt;/span&gt; &lt;span class="n"&gt;ZopeSkel&lt;/span&gt;

&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;python&lt;/span&gt; &lt;span class="n"&gt;boostrap&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;

&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;buildout&lt;/span&gt;

&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Also the important thing to do for now on, is to keep accurate doctests
in the docs/ folder. These files are simple to write, as I have added
primitives to simplify the work (well, I have reused what &lt;a href="http://www.gawel.org/weblog/"&gt;Gael&lt;/a&gt; did
in our skels at Ingeniweb). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For instance, let's have a look at the recipe doctest: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href=""&gt;http://dev.plone.org/collective/browser/ZopeSkel/trunk/zopeskel/docs/recipe.txt&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It actually launches the paster over the template, and also launches
the freshly created recipe's own tests. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The next moves on ZopeSkel I can think of would be to: &lt;br /&gt;
-   write more detailed doctests. Each one of them could become a recipe
    on how to use the given template;
-   gather in a top document all the doctest, to provide a detailed
    documentation in Zopeskel's frontal README.txt file, which is parsed
    and displayed at PyPI.&lt;/p&gt;
&lt;p&gt;ZopeSkel is a very important product in my opinion, because it
insuflates a standard way to write Plone code in the community. For that
matter, the &lt;em&gt;recipe&lt;/em&gt; template I have added a while ago was improved here
at the sprint, and you should check on &lt;a href="http://blogs.hexagonit.fi/kai/"&gt;Dokai's blog&lt;/a&gt; about this. He's
writing a wrapup about it right now ;)&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plone&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/collective/&lt;/span&gt;&lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="sr"&gt;/ZopeSkel/&lt;/span&gt;&lt;span class="n"&gt;trunk&lt;/span&gt;&lt;span class="sr"&gt;/zopeskel/&lt;/span&gt;&lt;span class="n"&gt;docs&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;recipe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;txt&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 22 Jan 2008 21:27:00 +0100</pubDate><guid>http://blog.ziade.org/2008/01/22/snow-sprint-report-3-zopeskel-refactoring/</guid></item><item><title>Snow sprint report #2 : benchmarking</title><link>http://blog.ziade.org/2008/01/21/snow-sprint-report-2-benchmarking/</link><description>&lt;p&gt;&lt;em&gt;EDIT: The chomsky was somehow limited, and was creating very similar
documents. Dokai worked on another text generator that generates more
various document. It is based on various file and combine random texts
that are quite nice, check it out ! (same place, but the method is
called random_text() (I have updated the code extract as well))&lt;/em&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Dokai and Tom are working hard on the best way to hook the regular
catalog with the Solr utility. I was a bit aside on this task so I
didn't catch up with it yet. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Anyway, I have prepared the field in order to compare a pure plone 3
with a solr-enabled one. I wanted to generate a Plone instance with many
documents, which content would look realistic. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I found on ASPN a great recipe for a Chomsky-based random text
generator:
&lt;a href="http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/440546"&gt;http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/440546&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So I have just bundled it in a script that can be used to generate
Plone folders with documents in it. When Dokai and Tom work will be
ready, we will use this script to load several thoushands of documents
in the catalogs, to start a few benchmarks. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here's the script (used in an Extension, but straight forward to bundle
in a class), you can also download it from &lt;a href="https://svn.enfoldsystems.com/browse/*checkout*/public/enfold.solr/branches/snowsprint08-buildout/SolrIntegration/Extensions/gen.py?content-type=text/plain&amp;amp;rev=1806"&gt;here&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot; Generates documents with realistic content,&lt;/span&gt;

&lt;span class="s"&gt;    with a Chomsky random generator&lt;/span&gt;

&lt;span class="s"&gt;    taken here : http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/440546&lt;/span&gt;

&lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;Products&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CMFCore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utils&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;getToolByName&lt;/span&gt;

&lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;

&lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;transaction&lt;/span&gt;

&lt;span class="n"&gt;leadins&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;bunch of lines&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

&lt;span class="n"&gt;subjects&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;bunch of lines&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

&lt;span class="n"&gt;verbs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;bunch of lines&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt; &lt;span class="n"&gt;objects&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;bunch of lines&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

&lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;textwrap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;

&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;itertools&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;islice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;izip&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;chomsky&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;times&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;Chomsky method of generating random text.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;part&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

                          &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;part&lt;/span&gt;

                          &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;leadins&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subjects&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verbs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                          &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;xrange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;times&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;gen_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;folder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;numdocs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;root_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;doc_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;wftool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getToolByName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;portal_workflow&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numdocs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;

        &lt;span class="n"&gt;id_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;root_name&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;id_&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;folder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objectIds&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;

            &lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

            &lt;span class="n"&gt;id_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;root_name&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chomsky&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chomsky&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="p"&gt;= chomsky(1)&lt;/span&gt;

&lt;span class="p"&gt;        context.invokeFactory(&amp;#39;Document&amp;#39;, id_, description=desc, title=title,&lt;/span&gt;

&lt;span class="p"&gt;                              subject=sub)&lt;/span&gt;

&lt;span class="p"&gt;        obj = context[id_]&lt;/span&gt;

&lt;span class="p"&gt;        wftool.doActionFor(obj, &amp;#39;publish&amp;#39;)&lt;/span&gt;

&lt;span class="p"&gt;        logging.info(&amp;#39;created document #%d&amp;#39; % i)&lt;/span&gt;

&lt;span class="p"&gt;        if i % 100 == 0:&lt;/span&gt;

&lt;span class="p"&gt;            transaction.savepoint()&lt;/span&gt;

&lt;span class="p"&gt;def gen_folders(context, numfolders=10, numdocs=1000, root_folder_name=&amp;#39;folder_&amp;#39;,&lt;/span&gt;

&lt;span class="p"&gt;                root_name=&amp;#39;doc_&amp;#39;):&lt;/span&gt;

&lt;span class="p"&gt;    wftool = getToolByName(context, &amp;#39;portal_workflow&amp;#39;)&lt;/span&gt;

&lt;span class="p"&gt;    for i in range(numfolders):&lt;/span&gt;

&lt;span class="p"&gt;        root = i&lt;/span&gt;

&lt;span class="p"&gt;        id_ = root_folder_name + str(root)&lt;/span&gt;

&lt;span class="p"&gt;        while id_ in context.objectIds():&lt;/span&gt;

&lt;span class="p"&gt;            root += 1&lt;/span&gt;

&lt;span class="p"&gt;            id_ = root_folder_name + str(root)&lt;/span&gt;

&lt;span class="p"&gt;        context.invokeFactory(&amp;#39;Folder&amp;#39;, id_)&lt;/span&gt;

&lt;span class="p"&gt;        obj = context[id_]&lt;/span&gt;

&lt;span class="p"&gt;        wftool.doActionFor(obj, &amp;#39;publish&amp;#39;)&lt;/span&gt;

&lt;span class="p"&gt;        logging.info(&amp;#39;created folder #%d&amp;#39; % i)&lt;/span&gt;

&lt;span class="p"&gt;        gen_documents(obj, obj, numdocs, root_name)&lt;/span&gt;

&lt;span class="p"&gt;        transaction.savepoint()&lt;/span&gt;

&lt;span class="p"&gt;def gen_sample(portal):&lt;/span&gt;

&lt;span class="p"&gt;    gen_folders(portal)      def random_text(data, num_words=100):&lt;/span&gt;

&lt;span class="p"&gt;    &amp;quot;&amp;quot;&amp;quot;Source: http://www.physics.cornell.edu/sethna/StatMech/ComputerExercises/RandText&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

&lt;span class="p"&gt;    # Read in the file and create a prefix mapping&lt;/span&gt;

&lt;span class="p"&gt;    words = data.split()&lt;/span&gt;

&lt;span class="p"&gt;    prefix = {}&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;xrange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;words&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setdefault&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;words&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;words&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;words&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="n"&gt;current_pair&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

    &lt;span class="n"&gt;random_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_pair&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;&amp;#39; &amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;current_pair&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;xrange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num_words&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="c1"&gt;# last two words in document may not have a suffix&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;current_pair&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;prefix:&lt;/span&gt;

            &lt;span class="n"&gt;break&lt;/span&gt;

        &lt;span class="k"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;current_pair&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

        &lt;span class="n"&gt;random_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random_text&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;&amp;#39; &amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;next&lt;/span&gt;

        &lt;span class="n"&gt;current_pair&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_pair&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="k"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;random_text&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Mon, 21 Jan 2008 14:22:00 +0100</pubDate><guid>http://blog.ziade.org/2008/01/21/snow-sprint-report-2-benchmarking/</guid></item><item><title>Snow sprint report #1 : indexing</title><link>http://blog.ziade.org/2008/01/20/snow-sprint-report-1-indexing/</link><description>&lt;p&gt;So we are here in Austria, sprinting on Zope and Plone (thanks to
&lt;a href="http://www.lovelysystems.com/"&gt;Lovely Systems&lt;/a&gt;). I have proposed a task on building an alternative
indexer system for Plone. So, we worked with Dokai and Tom on this.
Those guys rock, really ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Our goal was to create a plone 3 buildout that provides an out of the
box solution. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Background&lt;/h3&gt;
&lt;p&gt;Let me give you some background about indexing in Zope before
presenting our work. The default indexing system is quite effective, as
long as your instance is not getting too big. Some years ago, we had to
create an alternative indexer for CPS at Nuxeo, that would externalize
the catalog because we figured out that : &lt;br /&gt;
-   50% of the size of the ZODB was the catalog (I am talking about
    gigas here)
-   50% of the time on object creation was taken by indexing tasks, and
    was getting quite slow as the instance was growing.&lt;/p&gt;
&lt;p&gt;Those values are approximate, but quite near the reality back then (I
know some people worked on making indexing better on Zope lately). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Julien then wrote a XML-RPC server that would take care of the indexing
tasks and reply to queries. The software behind it was Lucene, together
with &lt;a href="http://pylucene.osafoundation.org/"&gt;PyLucene&lt;/a&gt;. The overall solution was quite good, beside the pain
we had to install it on some specific Linux back then. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Anyway. What did Julien some years ago exists now and is called
&lt;a href="http://lucene.apache.org/solr/"&gt;Solr.&lt;/a&gt; I also had some experiences a while ago with &lt;a href="http://xapian.org/"&gt;Xapian&lt;/a&gt; (as
Sidnei did too), which is quite efficient too, and easier to use from
Python (see &lt;a href="http://tarekziade.wordpress.com/2007/06/12/indexation-service-with-xapian/"&gt;here&lt;/a&gt;) &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Solr, Xapian&lt;/h3&gt;
&lt;p&gt;So the first task to do was to decide what to use. I called Alan from
&lt;a href="http://www.enfoldsystems.com/"&gt;Enfold Systems&lt;/a&gt; because the guys over there have been working on the
topic for years. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;As a matter of fact, they have created a package for Python that bind a
Solr server. &lt;br /&gt;
They also have a Plone integration that provides an utility to index
content on Solr. &lt;br /&gt;
Since the guys are releasing all of this very soon as open source, we
decided &lt;br /&gt;
to go with this solution for the sprint. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It is not a technological choice (Lucene) because Alan and some guys
from &lt;br /&gt;
Lemur are actually considering a drop-in replacement for Solr based on
Xapian. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In other words, the work done will be compatible with both Lucene and
Xapian technologies. Xapian is pretty interesting since it avoids
deploying Java ;) &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;The sprint task&lt;/h3&gt;
&lt;p&gt;The task was quite "simple" since the Enfold guys did all the hard work
:) &lt;br /&gt;
So we worked on: &lt;br /&gt;
1.  a buildout that builds a Solr server and launches it
2.  a Plone integration to use Solr seamlessly&lt;/p&gt;
&lt;h3&gt;The buildout&lt;/h3&gt;
&lt;p&gt;The buildout done and usable (We tried it under Windows, MacOSX and
Debian) &lt;br /&gt;
It uses new recipe we wrote: &lt;br /&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://svn.plone.org/svn/collective/buildout/collective.recipe.ant"&gt;collective.recipe.an&lt;/a&gt;t : build Java softwares using ant &lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;c&lt;a href="http://svn.plone.org/svn/collective/buildout/collective.recipe.solrinstance/"&gt;ollective.recipe.solrinstance&lt;/a&gt; : builds a Solr instance and
provide a script to launch it &lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you want to try it, here's (roughly) how (comment the blog entry in
case of a problem) &lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="p"&gt;$&lt;/span&gt; svn co https:&lt;span class="o"&gt;//&lt;/span&gt;svn.enfoldsystems.com&lt;span class="o"&gt;/&lt;/span&gt;public&lt;span class="o"&gt;/&lt;/span&gt;enfold.solr&lt;span class="o"&gt;/&lt;/span&gt;branches&lt;span class="o"&gt;/&lt;/span&gt;snowsprint08&lt;span class="o"&gt;-&lt;/span&gt;buildout buildout

&lt;span class="p"&gt;$&lt;/span&gt; cd buildout&lt;span class="o"&gt;/&lt;/span&gt;plone&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;3.0.5&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;

&lt;span class="p"&gt;$&lt;/span&gt; python2.4 bootstrap.py

&lt;span class="p"&gt;$&lt;/span&gt; bin&lt;span class="o"&gt;/&lt;/span&gt;buildout &lt;span class="o"&gt;-&lt;/span&gt;v

&lt;span class="p"&gt;$&lt;/span&gt; bin&lt;span class="o"&gt;/&lt;/span&gt;solr&lt;span class="o"&gt;-&lt;/span&gt;instance &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;     &lt;span class="o"&gt;&amp;lt;--&lt;/span&gt; launches solr &lt;span class="p"&gt;(&lt;/span&gt;python bin\solr&lt;span class="o"&gt;-&lt;/span&gt;instance under Windows

&lt;span class="p"&gt;$&lt;/span&gt; bin&lt;span class="o"&gt;/&lt;/span&gt;instance fg         &lt;span class="o"&gt;&amp;lt;--&lt;/span&gt; launches Zope
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Then, on Zope, install SolrIntegration in the quick_installer. The
next document you will publish will be indexed on Solr side, and
searchable with the search box. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The portal_catalog remains though, so it is indexed twice ;) you can
empty it to check &lt;br /&gt;
Solr is acting right. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Plone integration&lt;/h3&gt;
&lt;p&gt;The last part we need to work on is to make the SearchableText index
100% Solr based. Whit advices us to create a storage for TextIndexNG so
that's where we are heading on (should be done tomorrow hopefully) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;We would also like to do some benchmarks to compare the speed and ZODB
size. We will &lt;br /&gt;
probably use &lt;a href="http://jakarta.apache.org/jmeter/"&gt;Jmeter&lt;/a&gt; for this. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I would like to thank Alan, Leonardo, Sidnei for their work on this
area, and for releasing it as open source: I really believe that it will
become a great indexing solution for Plone in the next months. I was
really waiting for this momentum in indexing in the Plone community.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sun, 20 Jan 2008 23:25:00 +0100</pubDate><guid>http://blog.ziade.org/2008/01/20/snow-sprint-report-1-indexing/</guid></item><item><title>An installer for a buildout-ready Windows</title><link>http://blog.ziade.org/2008/01/20/an-installer-for-a-buildout-ready-windows/</link><description>&lt;p&gt;When you need to run a buildout under Windows, you have to take care of
setting up quite a few things, like installing MinGW and linking it to
Python, and setting up a svn command-line client for most buildouts. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;We created a simple package that contains everything needed to make
your windows buildout-friendly. It is a simple zip file that contains a
batch script and third-party installers. When the batch is run, the
environment variables are set as well, and win32-compatible buildouts
should run without problems from there. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;EDIT: the downloads urls have changed (thanks &lt;a href="http://www.theotheralex.com/"&gt;Sasha&lt;/a&gt;!)&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;You can get it here: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href="http://dl.dropbox.com/u/3265240/python2.4.4-win32.zip"&gt;http://dl.dropbox.com/u/3265240/python2.4.4-win32.zip&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;and another version that adds developing tools like vim and tail: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href="http://dl.dropbox.com/u/3265240/python2.4.4-win32-dev.zip"&gt;http://dl.dropbox.com/u/3265240/python2.4.4-win32-dev.zip&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This zip file is built itself with a buildout that we might publish
soon so you can make a custom zip file.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sun, 20 Jan 2008 20:05:00 +0100</pubDate><guid>http://blog.ziade.org/2008/01/20/an-installer-for-a-buildout-ready-windows/</guid></item><item><title>distutils: multiple servers in .pypirc</title><link>http://blog.ziade.org/2008/01/17/distutils-multiple-servers-in-pypirc/</link><description>&lt;p&gt;Since I am working on PloneSoftwareCenter to make it PyPI-compatible, I
have worked on distutils side to make the &lt;em&gt;register&lt;/em&gt; and &lt;em&gt;upload&lt;/em&gt;
command more friendly when an egg has to be registered to several
servers. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The problem with the actual .pypirc file is that it won't let you
define many username/password for many servers: it is dedicated for one
server. In the meantime, you can specify in a command line option which
server you want to deal with: &lt;br /&gt;
   $ python setup.py register -r http://my.server/pypi&lt;/p&gt;
&lt;p&gt;But this will take the username/password in .pypirc. So if your
username differs from one server to another, it won't work. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I have worked on an enhanced version for this, described here:
&lt;a href="http://wiki.python.org/moin/EnhancedPyPI"&gt;http://wiki.python.org/moin/EnhancedPyPI&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The patch is ready, and comes with new unit tests &lt;em&gt;register&lt;/em&gt; and
&lt;em&gt;upload&lt;/em&gt; commands didn't have yet. The new .pypirc format was shaped
with the help and feedback of catalog-sig people, thanks to Martin v.
Loewis and Fred Drake and others. I am going to submit it for inclusion
today. If it is accepted and integrated we will be able to deal with our
eggs like this: &lt;br /&gt;
   $ python setup.py register sdist upload    # goes to PyPI&lt;/p&gt;
&lt;p&gt;$ python setup.py register sdist upload -r plone.org   # goes to plone.org ;)&lt;/p&gt;
&lt;p&gt;The next step is to provide a patch for a permissive trove classifier
in PyPI. Then all PyPI-like servers will be able to provide the same
service for egg developers, no matter how they deal with classifiers.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 17 Jan 2008 11:06:00 +0100</pubDate><guid>http://blog.ziade.org/2008/01/17/distutils-multiple-servers-in-pypirc/</guid></item><item><title>sys.setdefaultencoding is evil</title><link>http://blog.ziade.org/2008/01/08/syssetdefaultencoding-is-evil/</link><description>&lt;p&gt;I have recently found some &lt;em&gt;UnicodeDecodeError&lt;/em&gt; bugs on some products,
that some people couldn't reproduced. The bug was due to a call to a CMF
API that was doing a &lt;em&gt;str()&lt;/em&gt; over the object, right before using it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This is perfectly fine in that case, because the object is supposed to
be a ZODB id, so it has to be full ASCII. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So the bug looks like this : &lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;éou&amp;#39;&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;Traceback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="k"&gt;last&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

&lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="sr"&gt;&amp;lt;module&amp;gt;&lt;/span&gt;

&lt;span class="n"&gt;UnicodeEncodeError:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;ascii&amp;#39;&lt;/span&gt; &lt;span class="n"&gt;codec&lt;/span&gt; &lt;span class="n"&gt;can&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;t encode character u&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;xe9&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt;

&lt;span class="n"&gt;position&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ordinal&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The people that couldn't reproduced it because they use that ugly hack
which consists of setting Python's default encoding to utf8: &lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setdefaultencoding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;utf8&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;éou&amp;#39;&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="s"&gt;&amp;#39;\xc3\xa9ou&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This will be applied to the whole process, and Python itself
dynamically removes the method from the module at it first use. From the
official doc: &lt;br /&gt;
   setdefaultencoding(name)&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;Set&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt; &lt;span class="n"&gt;used&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;Unicode&lt;/span&gt; &lt;span class="n"&gt;implementation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;If&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="n"&gt;does&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt; &lt;span class="n"&gt;available&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LookupError&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;raised&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;This&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;only&lt;/span&gt; &lt;span class="n"&gt;intended&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt; &lt;span class="n"&gt;used&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;site&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt; &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

&lt;span class="n"&gt;where&lt;/span&gt; &lt;span class="n"&gt;needed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt; &lt;span class="n"&gt;sitecustomize&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Once&lt;/span&gt; &lt;span class="n"&gt;used&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;site&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;removed&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt;

&lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;namespace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;New&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;I can't find the link back, but I have read once that this built-in was
to be removed because it should not be used outside site.py &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The problem is that people tend to add a sitecustomize.py in their
environment, then work with str() and unicode() calls and forget about
doing it right. The result is a major &lt;br /&gt;
misused of strings and unicodes and the code created will be buggy on
other computers. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So never ever use this in your code. If you have a UnicodeDecodeError
it probably means the function is waiting for a string. If you have a
UnicodeEncodeError, it should be unicode. In the same way, do not guess
the encoding in your code. You should work with one type (str or
unicode) and know exactly what is its encoding. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I think this misued is partly due to a lack of warning here:
[http://www.diveintopython.org/xml_processing/unicode.html][] &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Because that's one of the first page a developer finds when he tries to
understand why &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;he has such bugs. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;See a similar entry on the topic 2 years ago here:
&lt;a href="http://faassen.n--tree.net/blog/view/weblog/2005/08/02/0"&gt;http://faassen.n--tree.net/blog/view/weblog/2005/08/02/0&lt;/a&gt;&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 08 Jan 2008 10:45:00 +0100</pubDate><guid>http://blog.ziade.org/2008/01/08/syssetdefaultencoding-is-evil/</guid></item><item><title>Planet and Wordpress buggy title</title><link>http://blog.ziade.org/2008/01/08/planet-and-wordpress-buggy-title/</link><description>&lt;p&gt;I have found out why my entry titles are removed in all planets.
Wordpress recently added in their feeds a new tag in each item of the
feed: &lt;br /&gt;
   &lt;media:title type="html"&gt;tarek&lt;/media:title&gt;&lt;/p&gt;
&lt;p&gt;That's the one which get caught in feedparser, instead of the item real
title. This bug was already noticed and added in the bug tracker:
http://code.google.com/p/feedparser/issues/detail?id=83&amp;amp;q=wordpress. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So I guess it's just a matter of time for Lennart, me, and some other
people, to appear right in all Planets.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 08 Jan 2008 10:13:00 +0100</pubDate><guid>http://blog.ziade.org/2008/01/08/planet-and-wordpress-buggy-title/</guid></item><item><title>PloneSoftwareCenter: news from the PyPI front</title><link>http://blog.ziade.org/2008/01/07/plonesoftwarecenter-news-from-the-pypi-front/</link><description>&lt;p&gt;I have almost finished the work in the pypi branch for
PloneSoftwareCenter PyPI support. There are a few things to polish but
it works. (90% of the work was already done by Sidnei, so I have done
mostly minor refactorings). I guess I'll polish it until Alex Clark
merge it into the trunk. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you want to give it a try, you can use the buildout I have added in
the collective here: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;[http://svn.plone.org/svn/collective/PloneSoftwareCenter/buildout/branches/pypi/&lt;/p&gt;
&lt;p&gt;][] &lt;br /&gt;
I also have a running prototype here: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href="http://products.ingeniweb.com/catalog"&gt;http://products.ingeniweb.com/catalog&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;were we are trying it. This will be our public PSC instance and will
soon contain all our packages and some public packages mirrored or
repackaged as eggs. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Current features&lt;/h3&gt;
&lt;p&gt;The features are: &lt;br /&gt;
-   support of distutils and setuptools register and upload commands
-   automatic creation of projects and releases
-   support of PyPI's simple page, so it can be used by easy_install
    and zc.buildout&lt;/p&gt;
&lt;p&gt;If you want to try it up ask me for a user account. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Trove classification&lt;/h3&gt;
&lt;p&gt;The current default categories uses PyPI to classify the packages, and
everything is hooked to the register command. So when you upload a
package, it will appear in the proper categories in the software center.&lt;/p&gt;
&lt;p&gt;That said, you can change the categories to manage your own. When a
package is uploaded, it will just ignore the unknown categories. I am
working on PyPI side so the Cheeseshop itself works the same way. &lt;br /&gt;
(hopefully, it will be accepted, because the guys from the catalog team
are helping me out in polishing my proposal) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;see my document at : &lt;a href="http://wiki.python.org/moin/EnhancedPyPI"&gt;http://wiki.python.org/moin/EnhancedPyPI&lt;/a&gt; (see
&lt;em&gt;Making PyPI permissive for Trove classification&lt;/em&gt;) &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Dealing with several PyPI-like servers&lt;/h3&gt;
&lt;p&gt;Last but not least, as a Plone developer, the final goal is to be able
to register and upload packages to both PSC and PyPI. This is a bit
tricky with the current distutils implementation and I am working on
this so it can deal with several servers. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The final form will be to be able to do: &lt;br /&gt;
   $ python setup.py register sdist upload -r http://example.com/repository       # registering and uploading at example.com&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;python&lt;/span&gt; &lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; &lt;span class="n"&gt;register&lt;/span&gt; &lt;span class="n"&gt;sdist&lt;/span&gt; &lt;span class="n"&gt;upload&lt;/span&gt;        &lt;span class="c1"&gt;# registering and uploading at PyPI&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;There are some default policies in PSC though, to avoid people
uploading projects and file to easily: if the user is a simple member, a
register command call will create a project and submit it, and the
upload command won't work until the project has been accepted and
published. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Next steps&lt;/h3&gt;
&lt;p&gt;I am really excited about having the same standard everywhere, and to
be able to deploy our packages in the community through a simple command
line: &lt;br /&gt;
-   at the cheeseshop
-   in our private software center
-   in our public software center
-   hopefully, in plone.org when it goes Plone 3.x&lt;/p&gt;
&lt;p&gt;The next steps will be: &lt;br /&gt;
-   to get some feedback from the Plone community, and build a TODO list
    with it (I have to collect Wichert remarks from the ML to start to
    build it)
-   to polish the code
-   to add XML-RPC APIs, like PyPI has&lt;/p&gt;
&lt;p&gt;[http://svn.plone.org/svn/collective/PloneSoftwareCenter/buildout/branches/pypi/&lt;/p&gt;
&lt;p&gt;]: http://svn.plone.org/svn/collective/PloneSoftwareCenter/buildout/branches/pypi/&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Mon, 07 Jan 2008 14:44:00 +0100</pubDate><guid>http://blog.ziade.org/2008/01/07/plonesoftwarecenter-news-from-the-pypi-front/</guid></item><item><title>Plone sprint in Paris, April 25/27</title><link>http://blog.ziade.org/2007/12/27/plone-sprint-in-paris-april-2527/</link><description>&lt;p&gt;I'm really excited about this: we are organizing a Plone sprint in Paris
in April at &lt;a href="http://ingeniweb.com"&gt;Ingeniweb&lt;/a&gt; headquarters ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;No topics are planned yet because it depends a lot on who will be able
to come. This will be done throughout January. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Please join the project and its mailing list if you are interested: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href=""&gt;http://www.openplans.org/projects/plone-3-paris-sprint/project-home&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And do not hesitate to make some topic proposal on it&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;openplans&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/projects/&lt;/span&gt;&lt;span class="n"&gt;plone&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;paris&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;sprint&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 27 Dec 2007 16:19:00 +0100</pubDate><guid>http://blog.ziade.org/2007/12/27/plone-sprint-in-paris-april-2527/</guid></item><item><title>Is __ prefix considered unpythonic ?</title><link>http://blog.ziade.org/2007/12/26/is-__-prefix-considered-unpythonic/</link><description>&lt;p&gt;&lt;strong&gt;EDIT&lt;/strong&gt;: Justus provided in the comments a great link where Jim Fulton
argues that __ should be marked deprecated, folllowed by Neal Norwiz
and Tim Peters answers. This helps a lot understanding what __ should
be used for:
&lt;a href=""&gt;http://mail.python.org/pipermail/python-dev/2005-December/058555.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I am writing on Python OOP best practices and I was wondering what are
the best ways to name attributes in classes. My main concern is about
the distinction between private and protected attributes. &lt;br /&gt;
-   &lt;strong&gt;private attributes&lt;/strong&gt; are attributes that cannot be seen or used
    outside of the class, even buy subclasses. The Python parser calls
    the name mangling algorithm when it finds them to prevent name
    collision;
-   &lt;strong&gt;protected attributes&lt;/strong&gt; are attributes that can be used and seen in
    subclasses and &lt;em&gt;should not be used&lt;/em&gt; outside. Nothing is done on them
    and they can be used like public attributes.&lt;/p&gt;
&lt;h3&gt;When should we use them ?&lt;/h3&gt;
&lt;p&gt;If you read &lt;a href="http://www.python.org/dev/peps/pep-0008/"&gt;PEP8&lt;/a&gt;, it's clearly said that name mangling (using a
'__' prefix) is the best way to protect an attribute from beeing
accessed or overriden. So it should be used for all class internals that
is not intended to be overriden. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But in the biggest open source code bases like Zope or Plone, '__'
usage is very uncommon. The simple '_' prefix is often used instead, to
mark attributes that are private to the class or to the module. So there
are no real distinction between private and protected attributes. It
seems that the 'private' concept is not even used, and people often cut
their class code in two parts: public and protected. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In other languages (like Delphi) that define protected and private
levels though, protected attributes are not used a lot, and people tend
to cut their code in private and public parts and make the protected
layer as slim as possible. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Practical rules&lt;/h3&gt;
&lt;p&gt;Based on these remarks, here's a tentative of '__' and '_' prefixes
best usages in Python, for the use cases I know : &lt;br /&gt;
-   &lt;strong&gt;use __ with property&lt;/strong&gt;. since properties cannot use overriden
    methods and are tied to the class, the methods used with it should
    always be private;
-   &lt;strong&gt;use __ for methods that works with private attributes&lt;/strong&gt;. If your
    methods works for private attributes, make them private too;
-   &lt;strong&gt;use _ on methods when they are clearly intended to be
    overriden;&lt;/strong&gt;
-   &lt;strong&gt;use __ for all module functions and variables that are
    private&lt;/strong&gt;. A protected level is not needed since a module cannot be
    overriden.&lt;/p&gt;
&lt;p&gt;Following these rules would probably make 90% of class attributes
private instead of protected, and change all base code conventions. So I
am wondering: am I a bit unpythonic if I try to follow this standard in
attribute naming ? My guess is that most base code are not clean enough
in that matter. For instance, many of them use both new-style and
old-style classes under Python 2.5, which lead to a MRO algorithm that
differs depending on the classes ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I would love to hear how you people deal with these conventions.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//m&lt;/span&gt;&lt;span class="n"&gt;ail&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/pipermail/&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="sr"&gt;/2005-December/&lt;/span&gt;&lt;span class="mo"&gt;05&lt;/span&gt;&lt;span class="mi"&gt;8555&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="nv"&gt;%20&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Wed, 26 Dec 2007 17:19:00 +0100</pubDate><guid>http://blog.ziade.org/2007/12/26/is-__-prefix-considered-unpythonic/</guid></item><item><title>PloneSoftwareCenter Christmas mini-sprint</title><link>http://blog.ziade.org/2007/12/26/plonesoftwarecenter-christmas-mini-sprint/</link><description>&lt;p&gt;I made a mini-sprint on PSC for Christmas, since everyone around me was
sitting watching christmas movies on TV and trying to digest. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here's a wrapup for comment, and for upcoming work. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Current branch (pypi)&lt;/h3&gt;
&lt;p&gt;I've merged Sidnei's work into a new branch, with the current trunk
since his work was done 2 years ago. I have made a few changes from his
initial implementation: &lt;br /&gt;
-   the PyPI API is now coded in a browser view instead of a persistent
    object, since it has no properties to keep at all;
-   when a release is uploaded, a new release object is created for the
    given version if it doesn't exists instead of raising an error and
    asking the user to manually create it inside the PSC;
-   the doctest was simplified and uses sample tarballs and eggs.&lt;/p&gt;
&lt;p&gt;I need to finish up a few things and to add some features such as: &lt;br /&gt;
-   &lt;strong&gt;automatic project creation&lt;/strong&gt;. When a package is uploaded and no
    project corresponds to it, a new project is created using the egg
    name and provided metadata. This will make the PSC acts like the
    CheeseShop. (an option will be added in PSC to activate/deactivate
    this feature to prevent automatic creation of projects if not
    wanted).
-   &lt;strong&gt;trove web service&lt;/strong&gt; the TROVE.txt file created by Sidnei needs to
    be replaced by a call to the categories; (see next section)
-   &lt;strong&gt;multiple uploads&lt;/strong&gt;. Make sure everything works fine when several
    files are uploaded for one release;
-   &lt;strong&gt;more tests&lt;/strong&gt; I need to write more tests from various clients and
    platform to make sure it works good. (by recording
    setuptools/distutils calls and creating tests with this).&lt;/p&gt;
&lt;p&gt;This work should be done this week if everyone is OK with what I have
proposed. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;About the Trove classification&lt;/h3&gt;
&lt;p&gt;The Cheeseshop provides a Trove classification (see
&lt;a href="http://www.python.org/dev/peps/pep-0301"&gt;http://www.python.org/dev/peps/pep-0301&lt;/a&gt;) which evolves. For instance
the "Django framework" category was added last week IIRC. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Obviously, Plone eggs should follow this classification but when they
are uploaded in a PloneSoftwareCenter they might find specific
categories defined locally (these categories might be specific to the
project). I think we should let people freely define their classifiers
in setup.py and let each server take the ones they have in their list. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The problem with Cheeseshop implementation is that it fails silently
when a item in the 'classifiers' list doesn't exists on the server side.
The package metadata seem to be lost after that. (this looks like a bug
to me, I didn't digg the PyPI code yet). I need to ask over
distutils-sig about this and see if we can come up with a Cheeseshop
that will pick the categories it knows out of the classifier list, and
let the other alone. This would allow PSC to deal with extra categories.&lt;/p&gt;
&lt;p&gt;Then the PSC will have to implement the trove web services and serve
its categories, so the "list-classifiers" option of the register command
works. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Until then I guess we can leave the classification settings manual. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;About the .pypirc file&lt;/h3&gt;
&lt;p&gt;This file that is used with the register command is working just for
one server and will not allow having several set of login/password. This
is not a problem when the login of your plone with your PSC is the same
than the one in PyPI. Otherwise it won't work. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Furthermore, this command is using a hardcoded 'pypi' realm if you look
at distutils/commands/register.py: &lt;br /&gt;
   auth.add_password('pypi', host, username, password)&lt;/p&gt;
&lt;p&gt;The real solution here is to make distutils evolve so the &lt;em&gt;.pypirc&lt;/em&gt;
file allows having several login/password for each server, using the
host url and the realm (the realm can be queried automatically too).
Until then we have to make the PyPI api return a 'pypi' realm on 401
error (and this was done by Sidnei's work). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;To avoid maintaining several &lt;em&gt;.pypirc&lt;/em&gt; files and forcing the realm on
401 errors though, we should create a new register command, that can
work with a enhanced version of the file and allow adding passwords for
several hosts. IMHO, the disutils package code is PyPI-centric but was
primarly intented to work over any release server, so it has to evolve
on that point. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;About sdist and bdist_egg&lt;/h3&gt;
&lt;p&gt;We have discussed in my latest entry about having a new command to
upload the package in the PSC: &lt;br /&gt;
    $ python setup.py plone_upload&lt;/p&gt;
&lt;p&gt;The idea is to be able to upload the release to plone.org and to the
Cheeseshop in one step. People reacted over this because in my example I
used &lt;em&gt;bdist_egg&lt;/em&gt; instead of &lt;em&gt;dist&lt;/em&gt; for the packaging. I think it's a
false debate because it's up to the developer to decide how he releases
his work, using a tarball that is compiled by the target system, and/or
an egg. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So we can just define an enhanced &lt;em&gt;upload&lt;/em&gt; command that replaces the
original one, to automate the upload on several servers, and let the
developer manually call &lt;em&gt;sdist&lt;/em&gt; and/or &lt;em&gt;bdist_egg&lt;/em&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Servers could be picked up by the user at the prompt. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;Schedule &amp;amp; tasks&lt;/h3&gt;
&lt;p&gt;This are my plans in PSF this week and next week: &lt;br /&gt;
-   finish my current work on the branch, so the basic implementation
    works;
-   provide an enhanced version of the register and upload command, for
    multiple servers uploads. This will be done in a new package, since
    it's more like a distutils enhancement;
-   implement the trove webservice using PSC categories.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Wed, 26 Dec 2007 04:06:00 +0100</pubDate><guid>http://blog.ziade.org/2007/12/26/plonesoftwarecenter-christmas-mini-sprint/</guid></item><item><title>Ideas on distributing and promoting Plone packages</title><link>http://blog.ziade.org/2007/12/21/ideas-on-distributing-and-promoting-plone-packages/</link><description>&lt;p&gt;&lt;strong&gt;Edit: &lt;/strong&gt;Just after I posted this, I found &lt;a href="http://awkly.org/2006/01/28/pypi-like-functionality-to-plonesoftwarecenter/"&gt;Sidnei's work&lt;/a&gt;, so it is
basically what I was thinking of. Good job :) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How to promote Plone packages today ?&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Since eggs became a standard in Zope, distribution of Plone packages
can be done directly to the &lt;a href="http://cheeseshop.python.org/pypi"&gt;Cheeseshop&lt;/a&gt;. This is quite nice since
anyone can invoke an upload command like this: &lt;br /&gt;
   $ python setup.py bdist_egg upload&lt;/p&gt;
&lt;p&gt;This makes the package uploaded and available to anyone. This also made
anyone able to promote his work without having to set up a web site or
to study how the community works on that point. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I am pretty new to the Plone community, and I am trying to find the
best way to promote Plone packages we do here. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;My guess is that the Cheeseshop is perfect for Python package but not
enough for Plone packages: in order to promote them, the best place is
the &lt;a href="http://plone.org/products"&gt;plone.org software center&lt;/a&gt;. It brings a tracker, a front page and
a release folder. In other words, anyone who wants to publish a Plone
product that is seen by the whole community has to take care of
uploading its packages into plone.org, or setting a link there, and to
the cheeseshop. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you have a better way to promote your Plone products, let me know !&lt;/p&gt;
&lt;p&gt;I think this process could be enhanced a little bit through automation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Making plone.org pypi-compatible&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The Pypi index has two main features &lt;a href="http://plone.org/products/plonesoftwarecenter"&gt;PloneSoftwareCenter&lt;/a&gt; does not
afaik: &lt;br /&gt;
-   it implements a [file_upload method][], that is called by
    setuptools when the upload command is invoked
-   it provides package index pages that allow [easy_install][] to look
    for a package&lt;/p&gt;
&lt;p&gt;These two features are very simple, and could be added in Plone
Software Center by: &lt;br /&gt;
-   adding a file_upload view on the products page
-   providing an index-compatible view (PSC has a &lt;a href="http://usefulinc.com/doap/"&gt;DOAP&lt;/a&gt; support
    though)&lt;/p&gt;
&lt;p&gt;In other words, that would allow calling the upload command on
plone.org like this: &lt;br /&gt;
   $ python setup.py bdist_egg upload --repository=http://plone.org/products/&lt;/p&gt;
&lt;p&gt;Another command could be added in setuptools to distribute a plone
package automatically: &lt;br /&gt;
   $ python setup.py plone_promote&lt;/p&gt;
&lt;p&gt;plone_promote would invoke bdist_egg then make an upload on
cheeseshop and/or plone.org. In other words, that would allow package
developers to promote their work without pain. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Having such feature would also allow people to create their own
Pypi-compatible private software center when they deal with private
package they want to make available for instance to private project
buildouts. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If people think it's a good idea, I am willing to code it in PSC (I
made a proposal on the bug tracker).&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 21 Dec 2007 11:10:00 +0100</pubDate><guid>http://blog.ziade.org/2007/12/21/ideas-on-distributing-and-promoting-plone-packages/</guid></item><item><title>The Python Papers volume 2, issue 4</title><link>http://blog.ziade.org/2007/12/19/the-python-papers-volume-2-issue-4/</link><description>&lt;p&gt;The next issue of &lt;a href="http://pythonpapers.org/"&gt;The Python Papers&lt;/a&gt; (2, 4) is now available,
containing my blog entry "&lt;a href="http://tarekziade.wordpress.com/2007/09/24/eight-tips-to-start-with-python/"&gt;eight tips to start with Python&lt;/a&gt;", and many
interesting articles, like an introduction to Test-Driven Code
Generation. This article explains, after an introduction to test-driven
development, how to generate skeletons that contains unit tests ready to
run (like ZopeSkel in Plone) . Generated test fixture is a real
advantage in complex frameworks to avoid developers to drop TDD. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Read it, it's free !&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Wed, 19 Dec 2007 08:11:00 +0100</pubDate><guid>http://blog.ziade.org/2007/12/19/the-python-papers-volume-2-issue-4/</guid></item><item><title>iw.* recipes release festival</title><link>http://blog.ziade.org/2007/12/14/iw-recipes-release-festival/</link><description>&lt;p&gt;A lot of buildout recipes have been created lately at Ingeniweb, you can
list them here:
&lt;a href=""&gt;http://pypi.python.org/pypi?%3Aaction=search&amp;amp;term=iw.recipe&amp;amp;submit=search.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Let me present them quickly: &lt;br /&gt;
-   &lt;a href="http://pypi.python.org/pypi/iw.recipe.cmd"&gt;iw.recipe.cmd&lt;/a&gt; : yet another command line recipe. This is useful
    when you need to perform some pre or post installation steps that
    don't worth a recipe.
-   &lt;a href="http://pypi.python.org/pypi/iw.recipe.fetcher"&gt;iw.recipe.fetcher&lt;/a&gt; : this recipe is a wget-like command, that
    knows how to downloads a file given by an url.
-   &lt;a href="http://pypi.python.org/pypi/iw.recipe.fss"&gt;iw.recipe.fss&lt;/a&gt; : this recipe creates folders and configuration
    file when you use &lt;a href="http://ingeniweb.sourceforge.net/Products/FileSystemStorage/"&gt;File System Storage&lt;/a&gt; in your Plone application.
-   &lt;a href="http://pypi.python.org/pypi/iw.recipe.squid"&gt;iw.recipe.squid&lt;/a&gt; : automates the configuration of &lt;a href="http://www.squid-cache.org/"&gt;Squid&lt;/a&gt;, and
    is &lt;a href="http://plone.org/products/cachefu"&gt;Cache-Fu&lt;/a&gt; friendly
-   &lt;a href="http://pypi.python.org/pypi/iw.recipe.pound"&gt;iw.recipe.pound&lt;/a&gt; : compiles and installs &lt;a href="http://www.apsis.ch/pound/"&gt;Pound&lt;/a&gt; in your
    buildout, and creates its configuration file.
-   &lt;a href="http://pypi.python.org/pypi/iw.recipe.subversion"&gt;iw.recipe.subversion&lt;/a&gt; : this one is a bit like
    &lt;a href="http://pypi.python.org/pypi/infrae.subversion/"&gt;infrae.subversion&lt;/a&gt; but it doesn't make controls of modified files
    (which can be very long when you update a buildout), and it
    generates in download-cache when it is given, a tarball of the
    subversion branch. This allow us to perform offline installation
    with subversion branches without having to change the cfg files.
-   &lt;a href="http://pypi.python.org/pypi/iw.recipe.template"&gt;iw.recipe.template&lt;/a&gt; : this recipe knows how to render a cheetah
    template like Paster does for instance. It makes it easy to add a
    few custom files in our buildouts&lt;/p&gt;
&lt;p&gt;If you use them please let us know ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Other recipes are beeing created, like &lt;em&gt;iw.recipe.pen&lt;/em&gt;, which does like
&lt;em&gt;iw.recipe.pound&lt;/em&gt; but for Pen. I will blog sometimes about &lt;a href="http://siag.nu/pen/"&gt;Pen&lt;/a&gt;,
which can be used instead of Pound when you need an universal load
balancer that can work on any platform.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;pypi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;pypi&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;term&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;iw&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recipe&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;submit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 14 Dec 2007 10:02:00 +0100</pubDate><guid>http://blog.ziade.org/2007/12/14/iw-recipes-release-festival/</guid></item><item><title>Using ZopeSkel to raise Plone projects quality</title><link>http://blog.ziade.org/2007/11/30/using-zopeskel-to-raise-plone-projects-quality/</link><description>&lt;p&gt;At Ingeniweb, we have started to define standards for our Plone projects
using &lt;a href="http://plone.org/products/zopeskel"&gt;ZopeSkel&lt;/a&gt;. &lt;a href="http://pypi.python.org/pypi/IngeniSkel"&gt;IngeniSkel&lt;/a&gt; is a thin layer on the top of
ZopeSkel, that was: &lt;br /&gt;
-   injecting Archetype content within existing products
-   defining standard tests skeletons for all elements contained in
    products.
-   providing other templates like the recipe one, that creates a
    skeleton for zc.buildout recipes&lt;/p&gt;
&lt;p&gt;The injection idea was previously &lt;a href="http://martinaspeli.net/articles/a-cool-project-if-you-have-the-time"&gt;proposed by Martin&lt;/a&gt;, and
&lt;a href="http://www.mustap.com/pythonzone_post_234_zopeskel-with-local-commands"&gt;Mustapha&lt;/a&gt; started to implement it in a &lt;a href="https://svn.plone.org/svn/collective/ZopeSkel/branches/"&gt;branch&lt;/a&gt;. Erik F. has added
the archetype content injection yesterday. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It means you can now inject new archetype based content with the Paster
directly with ZopeSkel: &lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;paster&lt;/span&gt; &lt;span class="n"&gt;create&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="n"&gt;archetype&lt;/span&gt; &lt;span class="k"&gt;my&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;package&lt;/span&gt; &lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;cd&lt;/span&gt; &lt;span class="k"&gt;my&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;package&lt;/span&gt;  &lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;paster&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;help&lt;/span&gt;

&lt;span class="n"&gt;usage:&lt;/span&gt; &lt;span class="n"&gt;paster&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;paster_options&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;COMMAND&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;command_options&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;options:&lt;/span&gt;

  &lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="n"&gt;Commands:&lt;/span&gt;

  &lt;span class="n"&gt;create&lt;/span&gt;       &lt;span class="n"&gt;Create&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="n"&gt;layout&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;Python&lt;/span&gt; &lt;span class="n"&gt;distribution&lt;/span&gt;

  &lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="n"&gt;ZopeSkel&lt;/span&gt; &lt;span class="nb"&gt;local&lt;/span&gt; &lt;span class="n"&gt;commands:&lt;/span&gt;

  &lt;span class="n"&gt;addcontent&lt;/span&gt;   &lt;span class="n"&gt;Adds&lt;/span&gt; &lt;span class="n"&gt;plone&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt;  &lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;paster&lt;/span&gt; &lt;span class="n"&gt;addcontent&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;

&lt;span class="n"&gt;Available&lt;/span&gt; &lt;span class="n"&gt;templates:&lt;/span&gt;

  &lt;span class="n"&gt;contenttype:&lt;/span&gt;  &lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="n"&gt;skeleton&lt;/span&gt;

  &lt;span class="n"&gt;portlet:&lt;/span&gt;      &lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="n"&gt;Plone&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;portlet&lt;/span&gt;

  &lt;span class="n"&gt;view:&lt;/span&gt;         &lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="n"&gt;skeleton&lt;/span&gt;

  &lt;span class="n"&gt;zcmlmeta:&lt;/span&gt;     &lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="n"&gt;ZCML&lt;/span&gt; &lt;span class="n"&gt;meta&lt;/span&gt; &lt;span class="n"&gt;directive&lt;/span&gt; &lt;span class="n"&gt;skeleton&lt;/span&gt; &lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;paster&lt;/span&gt; &lt;span class="n"&gt;addcontent&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This is great, now the only thing it misses so we can drop IngeniSkel
in favor of this enhanced version of ZopeSkel is generating tests
modules on all templates and local commands, and the same way everytime.
I started such a work in another branch to backport what we have it and
I will propose it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Why ? Because having tests that are written the same way on all layers
of a Plone project is important to: &lt;br /&gt;
-   automate some QA and documentation tasks
-   make sure a newcomer won't get lost on how to startup a new piece of
    code, with the right test fixture. If he takes too much time to
    prepare the test fixture, he'll probably drop the TDD approach...&lt;/p&gt;
&lt;p&gt;Edit: I have merged the the recipe template into ZopeSkel trunk
already, as I've been asked to&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 30 Nov 2007 11:05:00 +0100</pubDate><guid>http://blog.ziade.org/2007/11/30/using-zopeskel-to-raise-plone-projects-quality/</guid></item><item><title>Controlling Cache headers with zope.testbrowser</title><link>http://blog.ziade.org/2007/11/11/controlling-cache-headers-with-zopetestbrowser/</link><description>&lt;p&gt;On a Plone website, setting the cache headers with &lt;a href="http://plone.org/products/cachefu"&gt;CacheFu&lt;/a&gt; or other
tools is quite easy, but when you need to check on a given page which
headers are inserted, it's not always easy to know what will be called
in the stack of rules you have set. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;You can look at each page properties in you web browser to check this,
but it is a pain and you won't be able to make sure nothing is broken
when you change, for example, your CacheFu settings. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So, having a dedicated functional test to control your website cache
settings is a good way to avoid regressions. &lt;a href="http://cheeseshop.python.org/pypi/zope.testbrowser/3.4.2"&gt;zope.testbrowser&lt;/a&gt; allows
you to write this on a doctest, so it's easy to gather all your cache
headers control in one text file. To install it, you can use &lt;a href="http://peak.telecommunity.com/DevCenter/EasyInstall"&gt;Easy
Install&lt;/a&gt;: &lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;easy_install&lt;/span&gt; &lt;span class="n"&gt;zope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;testbrowser&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Here's an example of such doctest (cache.txt): &lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;=============&lt;/span&gt;

&lt;span class="n"&gt;Testing&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;

&lt;span class="o"&gt;=============&lt;/span&gt;

    &lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;zope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;testbrowser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;browser&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Browser&lt;/span&gt;

&lt;span class="n"&gt;This&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;used&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="nn"&gt;headers::&lt;/span&gt;

    &lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Browser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;login&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;None:&lt;/span&gt;

    &lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Authorization&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="o"&gt;...&lt;/span&gt;                     &lt;span class="s"&gt;&amp;#39;Basic %s:%s&amp;#39;&lt;/span&gt; &lt;span class="nv"&gt;%&lt;/span&gt; &lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;login&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;

&lt;span class="n"&gt;Let&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;s try on the python front page::&lt;/span&gt;

&lt;span class="s"&gt;    &amp;gt;&amp;gt;&amp;gt; headers(&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;)&lt;/span&gt;

&lt;span class="s"&gt;    Date: ...&lt;/span&gt;

&lt;span class="s"&gt;    Server: Apache/2.2.3 (Debian) DAV/2 SVN/1.4.2 mod_ssl/2.2.3 OpenSSL/0.9.8c&lt;/span&gt;

&lt;span class="s"&gt;    Last-Modified: ...&lt;/span&gt;

&lt;span class="s"&gt;    ETag: &amp;quot;60193-3fd1-b811ca00&amp;quot;&lt;/span&gt;

&lt;span class="s"&gt;    Accept-Ranges: bytes&lt;/span&gt;

&lt;span class="s"&gt;    Content-Length: 16337&lt;/span&gt;

&lt;span class="s"&gt;    Connection: close&lt;/span&gt;

&lt;span class="s"&gt;    content-type: text/html; charset=utf-8&lt;/span&gt;

&lt;span class="s"&gt;The page returns an ETag header, which tells the browser if the page has changed.&lt;/span&gt;

&lt;span class="s"&gt;Let&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;try&lt;/span&gt; &lt;span class="n"&gt;Plone&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;s one::&lt;/span&gt;

&lt;span class="s"&gt;    &amp;gt;&amp;gt;&amp;gt; headers(&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;plone&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;Server:&lt;/span&gt; &lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;0.5.26&lt;/span&gt;

    &lt;span class="n"&gt;Date:&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="n"&gt;Connection:&lt;/span&gt; &lt;span class="nb"&gt;close&lt;/span&gt;

    &lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Language:&lt;/span&gt; &lt;span class="n"&gt;en&lt;/span&gt;

    &lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Cache&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Headers&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;By:&lt;/span&gt; &lt;span class="n"&gt;CachingPolicyManager:&lt;/span&gt; &lt;span class="sr"&gt;/plone.org/c&lt;/span&gt;&lt;span class="n"&gt;aching_policy_manager&lt;/span&gt;

    &lt;span class="n"&gt;Expires:&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="n"&gt;Vary:&lt;/span&gt; &lt;span class="n"&gt;Accept&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Encoding&lt;/span&gt;

    &lt;span class="n"&gt;Last&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Modified:&lt;/span&gt; &lt;span class="n"&gt;Sun&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mo"&gt;04&lt;/span&gt; &lt;span class="n"&gt;Dec&lt;/span&gt; &lt;span class="mi"&gt;2005&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;31&lt;/span&gt; &lt;span class="n"&gt;GMT&lt;/span&gt;

    &lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Cache&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Rules&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Applied:&lt;/span&gt; &lt;span class="n"&gt;yes&lt;/span&gt;

    &lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Caching&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Rule&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Id:&lt;/span&gt; &lt;span class="n"&gt;frontpage&lt;/span&gt;

    &lt;span class="n"&gt;Cache&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Control:&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;maxage&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;revalidate&lt;/span&gt;

    &lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Id:&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;hour&lt;/span&gt;

    &lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Length:&lt;/span&gt; &lt;span class="mi"&gt;44947&lt;/span&gt;

    &lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Varnish:&lt;/span&gt; &lt;span class="mi"&gt;783893351&lt;/span&gt; &lt;span class="mi"&gt;783878475&lt;/span&gt;

    &lt;span class="n"&gt;Age:&lt;/span&gt; &lt;span class="mi"&gt;3364&lt;/span&gt;

    &lt;span class="n"&gt;Via:&lt;/span&gt; &lt;span class="mf"&gt;1.1&lt;/span&gt; &lt;span class="n"&gt;varnish&lt;/span&gt;

    &lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Type:&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;utf&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;

    &lt;span class="n"&gt;imagetoolbar:&lt;/span&gt; &lt;span class="nb"&gt;no&lt;/span&gt;

&lt;span class="n"&gt;It&lt;/span&gt; &lt;span class="n"&gt;has&lt;/span&gt; &lt;span class="n"&gt;more&lt;/span&gt; &lt;span class="n"&gt;complex&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="n"&gt;information&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CacheFu&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;used&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt; &lt;span class="n"&gt;Varnish&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="n"&gt;software&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Notice the use of Ellipsis (...) that replaces changing data like
Dates. It can be called using a python module that can look like this: &lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;unittest&lt;/span&gt;

&lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;doctest&lt;/span&gt;

&lt;span class="n"&gt;OPTIONFLAGS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doctest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;REPORT_ONLY_FIRST_FAILURE&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;

                        &lt;span class="n"&gt;doctest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ELLIPSIS&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;

                        &lt;span class="n"&gt;doctest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NORMALIZE_WHITESPACE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;test_suite&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;doctest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DocFileSuite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;basename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;cache.txt&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;

                                         &lt;span class="n"&gt;optionflags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;OPTIONFLAGS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

    &lt;span class="n"&gt;test_suite&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This is not specific to Plone, and can be used on any website. Added to
the stack of tests, this helps a lot on making sure the cache settings
are doing what they are supposed to.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sun, 11 Nov 2007 10:26:00 +0100</pubDate><guid>http://blog.ziade.org/2007/11/11/controlling-cache-headers-with-zopetestbrowser/</guid></item><item><title>Scheduling tasks in Windows with PyWin32</title><link>http://blog.ziade.org/2007/11/01/scheduling-tasks-in-windows-with-pywin32/</link><description>&lt;p&gt;I am posting this small entry because it took me quite a time to compute
all informations to programmatically add tasks in Windows using
&lt;a href="https://sourceforge.net/projects/pywin32/"&gt;pywin32&lt;/a&gt;, so this post should be an helpfull example. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The process to schedule a task is as follows: &lt;br /&gt;
-   create an instance of the COM TaskScheduler interface
-   adding a task within the task scheduler
-   adding a trigger within the task&lt;/p&gt;
&lt;p&gt;Here's how it can look in Python, to create daily tasks: &lt;br /&gt;
   import pythoncom, win32api&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;

&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;win32com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;taskscheduler&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;taskscheduler&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;create_daily_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hour&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;minute&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;creates a daily task&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;split&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pythoncom&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CoCreateInstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;taskscheduler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CLSID_CTaskScheduler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

                                    &lt;span class="n"&gt;pythoncom&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CLSCTX_INPROC_SERVER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

                                    &lt;span class="n"&gt;taskscheduler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IID_ITaskScheduler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;%s.job&amp;#39;&lt;/span&gt; &lt;span class="nv"&gt;%&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Enum&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;

        &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewWorkItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetApplicationName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

        &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetParameters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:]))&lt;/span&gt;

        &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetPriority&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;taskscheduler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;REALTIME_PRIORITY_CLASS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetFlags&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;taskscheduler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TASK_FLAG_RUN_ONLY_IF_LOGGED_ON&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetAccountInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddWorkItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;run_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;localtime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;tr_ind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateTrigger&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="n"&gt;tt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;tr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetTrigger&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="n"&gt;tt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Flags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

        &lt;span class="n"&gt;tt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BeginYear&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;%Y&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;run_time&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

        &lt;span class="n"&gt;tt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BeginMonth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;%m&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;run_time&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

        &lt;span class="n"&gt;tt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BeginDay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;%d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;run_time&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;minute&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;None:&lt;/span&gt;

            &lt;span class="n"&gt;tt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StartMinute&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;%M&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;run_time&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

            &lt;span class="n"&gt;tt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StartMinute&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;minute&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;hour&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;None:&lt;/span&gt;

            &lt;span class="n"&gt;tt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StartHour&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;%H&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;run_time&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

            &lt;span class="n"&gt;tt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StartHour&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hour&lt;/span&gt;

        &lt;span class="n"&gt;tt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TriggerType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;taskscheduler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TASK_TIME_TRIGGER_DAILY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="nb"&gt;tr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetTrigger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;pf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;QueryInterface&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pythoncom&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IID_IPersistFile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;pf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

        &lt;span class="n"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;KeyError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;%s already exists&amp;quot;&lt;/span&gt; &lt;span class="nv"&gt;%&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Activate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;exit_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;startup_error_code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetExitCode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;win32api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FormatMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;startup_error_code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;You can see an example of usage &lt;a href="https://ingeniweb.svn.sourceforge.net/svnroot/ingeniweb/iw.win32/trunk/iw/win32/doctests/scheduler.txt"&gt;here&lt;/a&gt;, in the &lt;a href="https://ingeniweb.svn.sourceforge.net/svnroot/ingeniweb/iw.win32/trunk"&gt;iw.win32&lt;/a&gt; package
we have started to build, to gather all win32 specific Python things.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 01 Nov 2007 12:23:00 +0100</pubDate><guid>http://blog.ziade.org/2007/11/01/scheduling-tasks-in-windows-with-pywin32/</guid></item><item><title>iw.recipe.fss : a recipe to install File System Storage</title><link>http://blog.ziade.org/2007/10/23/iwrecipefss-a-recipe-to-install-file-system-storage/</link><description>&lt;h1&gt;What is FSS ?&lt;/h1&gt;
&lt;p&gt;When you need to work with a lot of static files in your Plone website,
you should consider using &lt;a href="http://ingeniweb.sourceforge.net/Products/FileSystemStorage/"&gt;File System Storage&lt;/a&gt; (FSS). It's an
Archetypes storage that can handle files like PDF, images or small
videos. It prevents the growth of the ZODB. It's not like &lt;a href="http://tarekziade.wordpress.com/2007/09/14/to-blob-or-not-to-blob/"&gt;Blobs&lt;/a&gt;,
because the files are not transactionals. This means you won't have to
worry about network performances when you use ZEO: nothing will be
copied from a node to another. You just have to use a NFS point to make
sure all nodes uses the same files.&lt;/p&gt;
&lt;p&gt;The missing part though, was to be able to easily deploy FSS using the
standard way. &lt;br /&gt;
&lt;/p&gt;
&lt;h1&gt;Deploying FSS&lt;/h1&gt;
&lt;p&gt;In Plone world, &lt;a href="http://cheeseshop.python.org/pypi/zc.buildout/1.0.0b30#detailed-documentation"&gt;zc.buildout&lt;/a&gt; is now the leading project in the
deployment area. Everyone should read &lt;a href="http://plone.org/documentation/tutorial/buildout"&gt;Martin's tutorial&lt;/a&gt; on how to
use it. It makes a typical Plone deployable in a matter of minutes with
no pain. It is based on a configuration reader that instanciate recipes
objects in charge of installing a part of the system. You have recipes
for apache, ldap, etc. &lt;a href="http://cheeseshop.python.org/pypi?:action=search&amp;amp;term=recipe&amp;amp;submit=search"&gt;See existing public recipes at Cheeseshop&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href="http://pypi.python.org/pypi/iw.recipe.fss/0.1c"&gt;iw.recipe.fss&lt;/a&gt; is a recipe that takes care of creating file system
folders and the configuration file used by the Product. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;To use it, just insert a section in your buildout file (it's called a
part in buildout language) that describes each system storage, and the
path to the configuration file: &lt;br /&gt;
   [buildout]&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;parts:

    ...

    fss

    ...

[fss]

recipe = iw.recipe.fss

conf = &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;zopeinstance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;/etc/plone-filesystemstorage.conf

storages =

    storage_name /site/storage_path directory
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;See the recipe's &lt;a href="http://cheeseshop.python.org/pypi/iw.recipe.fss/0.1bdev-r6471"&gt;README.txt&lt;/a&gt; for all options. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;That's it. I love buildout. &lt;br /&gt;
&lt;/p&gt;
&lt;h1&gt;More recipes to come&lt;/h1&gt;
&lt;p&gt;There are a lot of recipe available in the Python Index, but we still
missing some to perform every kind of deployment. Besides the Plone
recipes here are the recipes that I find realy usefull: &lt;br /&gt;
-   &lt;a href="http://cheeseshop.python.org/pypi/infrae.subversion/1.0dev-r26037"&gt;&lt;strong&gt;infrae.subversion&lt;/strong&gt;&lt;/a&gt;: clean, simple way, to checkout a piece of
    code from a Subversion repository. It's a perfect one to create
    developer buildouts.
-   &lt;a href="http://cheeseshop.python.org/pypi/zc.recipe.cmmi/1.0.2"&gt;&lt;strong&gt;zc.recipe.cmmi&lt;/strong&gt;&lt;/a&gt;: the configure-make-make install recipe. Will
    perform a compilation and local installation) of Makefile compatible
    packages under BSD/Linux.
-   &lt;a href="http://cheeseshop.python.org/pypi/zc.recipe.egg/1.0.0b5"&gt;&lt;strong&gt;zc.recipe.egg&lt;/strong&gt;&lt;/a&gt;: this one is very useful if an egg needs a
    special environment. For example if it needs a compiled library
    created with zc.recipe.cmmi.&lt;/p&gt;
&lt;p&gt;We have more recipes that are being coded here at Ingeniweb, to cover
our needs in deploying Plone instances with buildout. I'll try to blog
on them everytime we update the Cheeseshop. If you create recipes
yourself I encourage you to share them on Cheeseshop. Sharing recipes,
in my humble opinion, is an important thing to do in a community: it
helps standards to raise because it shows how people uses software in
real infrastructures.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 23 Oct 2007 07:56:00 +0200</pubDate><guid>http://blog.ziade.org/2007/10/23/iwrecipefss-a-recipe-to-install-file-system-storage/</guid></item><item><title>Unobtrusive benchmark and debug of Python applications</title><link>http://blog.ziade.org/2007/10/18/unobtrusive-benchmark-and-debug-of-python-applications/</link><description>&lt;p&gt;There are many tools available for Python to perform benchmarks and
debugging. For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://docs.python.org/lib/module-hotshot.html"&gt;Hotshot&lt;/a&gt; is bundled in the standard library and provide useful
    data. Maybe you have to install an extra package on some linux
    distribution if I recall it correctly, because it's not GPL;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://ipython.scipy.org/"&gt;iPython&lt;/a&gt; provides a nice interface to perform live debugging,
    like automatic invocation of pdb on exceptions;&lt;/li&gt;
&lt;li&gt;the standard module test provides pystone, that let you benchmark
    the computer in use before the timed tests. This is helpfull to
    bench the code on several computers: the measurements can be
    expressed in in pystones. In other words, you are able to have a
    reproducable measure of a piece of code and work on the code
    complexity to make it faster. In reality, any interference can
    change the results, but this is true as well for time measures.&lt;/li&gt;
&lt;li&gt;all big python frameworks are using the logging module, so it's easy
    to hook in it if extra logging is needed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But when trying to equip an application in order to find out why some
functionalities are slow, or why something goes wrong, it's not always
easy to set up precisely what you want to log if something is slow or
what your want to hook if a bug appears. The simplest way is to call out
all the mentioned tool from the code, but it too obtrusive. Another way
is to use decorators. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;To perform it, you'll have to: &lt;br /&gt;
-   get and install &lt;a href="http://pypi.python.org/pypi/iw.quality"&gt;iw.quality&lt;/a&gt;
-   create the benchmarking or debugging module&lt;/p&gt;
&lt;h1&gt;Get and install iw.quality&lt;/h1&gt;
&lt;p&gt;iw.quality gathers helpers for QA. It has an implementation of the
Levenshtein distance &lt;a href="http://tarekziade.wordpress.com/2007/10/08/make-your-code-base-healthier-the-anti-cheater-pattern/"&gt;discussed earlier&lt;/a&gt;, and now a decorator used for
benchmarking and debugging purpose. Since it's available in PyPi, you
should be able to install it like this: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;$ easy_install iw.quality&lt;/p&gt;
&lt;p&gt;See &lt;a href="http://peak.telecommunity.com/DevCenter/setuptools"&gt;setuptools informations&lt;/a&gt; if you need to install easy_install
itself. &lt;br /&gt;
&lt;/p&gt;
&lt;h1&gt;Preparing the benchmark or the debugging&lt;/h1&gt;
&lt;p&gt;Whether you are about to benchmark or debug your program, you need to
list all the places in your code where you need to hook a log or a pdb.
Then you can create a specialized python module that can be used when
needed. This module will simply decorate the functions you want to work
with. &lt;br /&gt;
&lt;/p&gt;
&lt;h2&gt;Benchmarking&lt;/h2&gt;
&lt;p&gt;Here's an example, let's equip sqlalchemy for benchmarking. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;benchmarking.py file: &lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="c1"&gt;#&lt;/span&gt;

&lt;span class="c1"&gt;# benchmarking queries&lt;/span&gt;

&lt;span class="c1"&gt;#&lt;/span&gt;

&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;iw&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quality&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decorators&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;log_time&lt;/span&gt;

&lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;

&lt;span class="n"&gt;simple_logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;log_time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simple_logger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_engine&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Engine&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simple_logger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Engine&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The log_time decorator comes with a few parameters, like logger wich
is called with the log message. By default it uses logging.info, but you
can use your own like in the example. The chosen functions are then
decorated. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Let's use it: &lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;benchmarking&lt;/span&gt;      &lt;span class="c1"&gt;# applies the decorators&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;create_engine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;sqlite:///:memory:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nn"&gt;log_time::&lt;/span&gt;&lt;span class="n"&gt;2007&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="n"&gt;T21:50:52&lt;/span&gt;&lt;span class="mf"&gt;.352037&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="mf"&gt;0.013&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;create_engine&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;args:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;sqlite:///:memory:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,),&lt;/span&gt; &lt;span class="n"&gt;kw:&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;create table TEST(id int)&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nn"&gt;log_time::&lt;/span&gt;&lt;span class="n"&gt;2007&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="n"&gt;T21:52:13&lt;/span&gt;&lt;span class="mf"&gt;.761085&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="mf"&gt;0.104&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;execute&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args:&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Engine&lt;/span&gt; &lt;span class="n"&gt;object&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mh"&gt;0x12e6e90&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;create tableTEST(id int)&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;kw:&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResultProxy&lt;/span&gt; &lt;span class="n"&gt;object&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mh"&gt;0x12e6ff0&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;insert into TEST (id) values (1)&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nn"&gt;log_time::&lt;/span&gt;&lt;span class="n"&gt;2007&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="n"&gt;T21:52:50&lt;/span&gt;&lt;span class="mf"&gt;.265860&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="mf"&gt;0.000&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;execute&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args:&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Engine&lt;/span&gt; &lt;span class="n"&gt;object&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mh"&gt;0x12e6e90&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;insert into TEST&lt;/span&gt;

&lt;span class="s"&gt;(id) values (1)&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;kw:&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResultProxy&lt;/span&gt; &lt;span class="n"&gt;object&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mh"&gt;0x12f50d0&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;If you need to display more infos on the call, you can use your own
formatter instead of the provoded one. Let's extend the benchmark file:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;formatter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;execution_time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kw&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;%s = %.3f ms&amp;#39;&lt;/span&gt; &lt;span class="nv"&gt;%&lt;/span&gt; &lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;function&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;execution_time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;simple_logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;log_time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;formatter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;formatter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;And rerun some code: &lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;create_engine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;sqlite:///:memory:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="n"&gt;create_engine&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mh"&gt;0x12328b0&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.014&lt;/span&gt; &lt;span class="n"&gt;ms&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;You can add a treshold on the function timing, to log only functions
that are up to this treshold. This is useful to filter a bit.&lt;/p&gt;
&lt;h2&gt;Debugging&lt;/h2&gt;
&lt;p&gt;For debugging purpose, you can use the debug parameter: &lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pdb&lt;/span&gt;

    &lt;span class="n"&gt;pdb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_trace&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;simple_logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;log_time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;formatter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;formatter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

                         &lt;span class="n"&gt;debugger&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;It will be called in case of an Exception: &lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;

&lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;create_engine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;gcckc&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;Return&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;

&lt;span class="sr"&gt;/Users/&lt;/span&gt;&lt;span class="n"&gt;tziade&lt;/span&gt;&lt;span class="sr"&gt;/tests/&lt;/span&gt;&lt;span class="n"&gt;benchmarking&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Pdb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;

&lt;span class="sr"&gt;/Users/&lt;/span&gt;&lt;span class="n"&gt;tziade&lt;/span&gt;&lt;span class="sr"&gt;/tests/&lt;/span&gt;&lt;span class="n"&gt;banchmarking&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Pdb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;

&lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="n"&gt;create_engine&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mh"&gt;0x12328f0&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;3.544&lt;/span&gt; &lt;span class="n"&gt;ms&lt;/span&gt;

&lt;span class="n"&gt;Traceback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="k"&gt;last&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="n"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;esqlalchemy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exceptions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ArgumentError:&lt;/span&gt; &lt;span class="n"&gt;Could&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;parse&lt;/span&gt; &lt;span class="n"&gt;rfc1738&lt;/span&gt; &lt;span class="n"&gt;URL&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;gcckc&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;By using this simple decorator, it's easy to group benchmarking and
debugging in a specialized module, and generate custom reports. The best
practice is to create a module per each use case. I didn't hook it on
Hotshot or other utilities to let people use the tools they like. &lt;br /&gt;
&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 18 Oct 2007 21:00:00 +0200</pubDate><guid>http://blog.ziade.org/2007/10/18/unobtrusive-benchmark-and-debug-of-python-applications/</guid></item><item><title>Make your code base healthier: the anti-cheater pattern</title><link>http://blog.ziade.org/2007/10/08/make-your-code-base-healthier-the-anti-cheater-pattern/</link><description>&lt;p&gt;I gave a few years ago some courses to college students. They had to
write some small C++ programs and send them to me before the end of the
course, so I could correct them and give some grades. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;They were massively cheating :) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I created a anti-cheat tool to try to find the cheaters, mainly for fun
and curiosity. To make it efficient, I tried to understand how people
where cheating. They were cutting and pasting pieces of code from
various people, and where arranging them so they would look original.
They were changing the comments of course, and moving functions around
so I wouldn't recognize the class similarities when I was glancing
through the code. But when they were cheating, 80% of the code would
look similar to the programs they borrowed. When they were composing a
new program out of several sources, the atomic bloc was roughly the
function: it was most of the time coming for one source. In other words,
there were a few students zero (like patient zero in epidemics), that
where the providing a function help-yourself copy-n-paste catalog for 30
fellows. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The Levenshtein distance worked great there in finding the cheaters:
given two strings, it computes the number of permutations needed to get
from one string to the other. A ratio can therefore be calculated
between the two strings. Roughly, when its value is equal or up to 0.7,
we can consider that the two are quite similar. This can be applied to
function code as well. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It was quit funny to use this tool at the end of the course, as I could
point the cheaters immediatly. I hurd that some college use such
programs now to detect if students are doing plagias in their homeworks.&lt;/p&gt;
&lt;p&gt;In software, developers acts the same: in the very same code base, as
long as there is more than one developer involved, you will always find
the same functions duplicated again and again. Sometimes, the
duplication is not done on purpose: &lt;strong&gt;it's a natural use case involved
by the APIs&lt;/strong&gt;. In other words, a refactoring is needed to remove
duplicate code. This is done most of the time by agile developers that
smell the need: why this function is not made generic and moved into the
base class ? &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So it's a good practice to hunt for duplicates, to make the code
smaller, thus more robust. But it's hard to see all duplicates, as it
takes a lot of code reviewing time. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;That's where the anti-cheater pattern is useful&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The pattern can be applied in two steps: &lt;br /&gt;
1.  Parsing the code and applying a bit of filtering.
2.  Calculating the distance, and reporting similarities.&lt;/p&gt;
&lt;h1&gt;Parsing the code&lt;/h1&gt;
&lt;p&gt;The first step is to read the code, using the compiler module. This is
the cleanest way to extract the functions because the module parses the
code and renders an Abstract Syntax Tree (AST), that is browsable
without having to import, thus compile the code. Regular expression
could work, but would be painful to create. In the AST, each node
represents a piece of the program, with a specialized class. For
example, a function is a node of the Function class and a list of
children that represent the content of the function. compiler also
provides a visitor pattern that allows us to set some hook everytime a
function, a module, a class or anything else, gets traversed by the
parser. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Below is the visitor used to parse the code for our duty: &lt;br /&gt;
   registered_code = {}&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;class&lt;/span&gt; &lt;span class="n"&gt;CleanNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;el&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;el&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getChildren&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;el&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;el&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;basestring&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;

        &lt;span class="c1"&gt;# too small&lt;/span&gt;

        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;small&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;

        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;

        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;

        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;hasattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;klass&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;klass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;klass&lt;/span&gt;

&lt;span class="n"&gt;class&lt;/span&gt; &lt;span class="n"&gt;CodeSeeker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;compiles the AST&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;realpath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;compiler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parseFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;compiler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;calculates an unique key for a node&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;hasattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;klass&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;%s %s.%s:%s&amp;#39;&lt;/span&gt; &lt;span class="nv"&gt;%&lt;/span&gt; &lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

                                    &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lineno&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;%s %s:%s&amp;#39;&lt;/span&gt; &lt;span class="nv"&gt;%&lt;/span&gt; &lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lineno&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;_clean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;CleanNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;register the node&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

        &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;

        &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_clean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;small:&lt;/span&gt;

            &lt;span class="n"&gt;registered_code&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;

    &lt;span class="c1"&gt;#&lt;/span&gt;

    &lt;span class="c1"&gt;# compiler walker APIs&lt;/span&gt;

    &lt;span class="c1"&gt;#&lt;/span&gt;

    &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;visitFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;visitClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;subnode&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getChildren&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;subnode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__class__&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt;  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;compiler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stmt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;compiler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

                &lt;span class="k"&gt;continue&lt;/span&gt;

            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;subnode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getChildren&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;

                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

                    &lt;span class="k"&gt;continue&lt;/span&gt;

                &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;klass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;

                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;register_module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;registers a module&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="n"&gt;CodeSeeker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;register_folder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;folder&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;walk a folder and register python modules&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dirs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;folder&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;tests&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

            &lt;span class="k"&gt;continue&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;files:&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;endswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;.py&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;interface.py&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

                &lt;span class="n"&gt;register_module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Each function, inside and outside classes, are registered in
registered_code with a few metadata. There's a few filters as you can
see. Some are Plone specific (like the omission of tests folders, and
interface.py files), and some removes very small functions (when there's
less than 5 nodes in the function, including its name, parameters, etc).
This is the playground for our Levenshtein algorithm. &lt;br /&gt;
&lt;/p&gt;
&lt;h1&gt;Calculating the distance&lt;/h1&gt;
&lt;p&gt;Now we can compare each function to each other, and get a ratio. When
the value is up to 0.7 we can consider that the code is pretty similar.
I have used David Neca's package:
&lt;a href="http://trific.ath.cx/resources/python/levenshtein/"&gt;http://trific.ath.cx/resources/python/levenshtein/&lt;/a&gt; for this, because
it's fast and real simple to use: &lt;br /&gt;
   from Levenshtein import ratio&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;levenshtein&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;entry2&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;returns the ratio&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ratio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;entry2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Using it over our dictionnary will look like this: &lt;br /&gt;
   def search_similarities():&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;    &lt;span class="n"&gt;similar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt;

    &lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;registered_code&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;done&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;items:&lt;/span&gt;

        &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;key2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value2&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;items:&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;key2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;key2&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;done:&lt;/span&gt;

                &lt;span class="k"&gt;continue&lt;/span&gt;

            &lt;span class="n"&gt;code2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="n"&gt;ratio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;levenshtein&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;code2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ratio&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

                &lt;span class="n"&gt;similar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;ratio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="n"&gt;similar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;similar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;similar&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Of course we could do some caching, and use an iterator to optimize the
code (the sorting is not really needed) &lt;br /&gt;
&lt;/p&gt;
&lt;h1&gt;Demo in Plone and Zope&lt;/h1&gt;
&lt;p&gt;Let's run this pattern over Plone and Zope. I have tried it on Plone 3
lib and Zope 3 lib (within Zope 2.10). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For example, addOpenIdPlugin in plone.openid.plugins.oid: &lt;br /&gt;
   def addOpenIdPlugin(self, id, title='', REQUEST=None):&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;    &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;Add a OpenID plugin to a Pluggable Authentication Service.&lt;/span&gt;

&lt;span class="s"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;OpenIdPlugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_setObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getId&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;REQUEST&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;None:&lt;/span&gt;

        &lt;span class="n"&gt;REQUEST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;RESPONSE&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;%s/manage_workspace&amp;quot;&lt;/span&gt;

                &lt;span class="s"&gt;&amp;quot;?manage_tabs_message=OpenID+plugin+added.&amp;quot;&lt;/span&gt; &lt;span class="nv"&gt;%&lt;/span&gt;

                &lt;span class="nv"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;absolute_url&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;was trapped to be similar to manage_addSessionPlugin in
plone.session.plugins.session: &lt;br /&gt;
   def manage_addSessionPlugin(dispatcher, id, title=None, path='/', REQUEST=None):&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;    &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;Add a session plugin.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="n"&gt;sp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;SessionPlugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;dispatcher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_setObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;REQUEST&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;None:&lt;/span&gt;

        &lt;span class="n"&gt;REQUEST&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RESPONSE&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;%s/manage_workspace?&amp;#39;&lt;/span&gt;

                               &lt;span class="s"&gt;&amp;#39;manage_tabs_message=Session+plugin+created.&amp;#39;&lt;/span&gt; &lt;span class="nv"&gt;%&lt;/span&gt;

                               &lt;span class="nv"&gt;dispatcher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;absolute_url&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This tells us that a common API could be written that way (if it
doesn't alreay exists): &lt;br /&gt;
   def addPlugin(container, klass, id, REQUEST=None, **kw):&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;    &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;adds a plugin&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="n"&gt;plugin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_setObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getId&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;REQUEST&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;None:&lt;/span&gt;

        &lt;span class="n"&gt;REQUEST&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RESPONSE&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;%s/manage_workspace?manage_tabs_message&amp;#39;&lt;/span&gt;

                                   &lt;span class="s"&gt;&amp;#39;=Plugin+created&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;%&lt;/span&gt; &lt;span class="nv"&gt;container&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;absolute_url&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;It would avoid having to write such boiler-plate code. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Another example, in Zope 2.10's zope.app folder. The class
SimpleViewClass in zope.app.pagetemplate.simpleviewclass looks very
similar to the one found in zope.app.onlinehelp.onlinehelptopic. Mmmm
&lt;strong&gt;it's exactly the same in fact !&lt;/strong&gt; ;) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Last example. In zope.app.authentication.principalfolder, in
PrincipalFolder class: &lt;br /&gt;
   def search(self, query, start=None, batch_size=None):&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;    &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;Search through this principal provider.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="n"&gt;search&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;search&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;search&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;None:&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt;

    &lt;span class="n"&gt;search&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;values&lt;/span&gt;&lt;span class="p"&gt;()):&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;search&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt;

            &lt;span class="n"&gt;search&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt;

            &lt;span class="n"&gt;search&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()):&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                    &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;batch_size&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;batch_size&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;

                &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

                &lt;span class="n"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This is very similar to GroupFolder's one in
zope.app.authentication.groupfolder: &lt;br /&gt;
   def search(self, query, start=None, batch_size=None):&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;    &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot; Search for groups&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="n"&gt;search&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;search&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;search&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;None:&lt;/span&gt;

        &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

        &lt;span class="n"&gt;search&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;groupinfo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;()):&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;search&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;groupinfo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt;

                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;groupinfo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt;

                 &lt;span class="n"&gt;search&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;groupinfo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;())):&lt;/span&gt;

                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                        &lt;span class="ow"&gt;or&lt;/span&gt;

                        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;batch_size&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;batch_size&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;

                    &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

                    &lt;span class="n"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This should be a common code as well, in the base class. &lt;br /&gt;
&lt;/p&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;My example is not very clean though: &lt;br /&gt;
-   the distance is calculated on the string representation of the tree
    of each function, and this is probably not optimal;
-   the ratio treshold was fixed by trying out the pattern on a few
    source code;
-   the whole thing is quite slow (I'm not Lundh).&lt;/p&gt;
&lt;p&gt;So it's more likely to be a base for a better implementation, or maybe
a CheeseCake addon ? &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But this works for me at this stage, and let me find duplicate code.
I'm thinking of hooking it in a buildbot, to analyze what developers
commit and, when a similarity is found, send a mail with a few
suggestions.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Mon, 08 Oct 2007 19:20:00 +0200</pubDate><guid>http://blog.ziade.org/2007/10/08/make-your-code-base-healthier-the-anti-cheater-pattern/</guid></item><item><title>Testing code that calls third party servers</title><link>http://blog.ziade.org/2007/10/05/testing-code-that-calls-third-party-servers/</link><description>&lt;p&gt;One of the fundamentals of unit testing is that &lt;strong&gt;the unit test should
never depend on any external resource&lt;/strong&gt;. This is true for all data that
might be needed to run the tests, but also for third party servers like
LDAP or SQL: they have to be faked. &lt;br /&gt;
-   &lt;strong&gt;LDAP&lt;/strong&gt; is quite painful to fake. The simplest way is to create all
    tests with a real LDAP server, then replace it with a class that
    returns explicit responses for each explicit request. This is
    managable when the LDAP layer is well done, and easy to patch.
-   &lt;strong&gt;Mailhost&lt;/strong&gt; is also quite easy to patch in the test fixture, and
    printing back the mail sent instead of calling the smtplib will
    allow you to write doctests and unittest without depending on a smtp
    server through telnet.
-   For &lt;strong&gt;SQL&lt;/strong&gt;, the simplest way, as long as you use a library that
    knows how to call different DBs through &lt;a href="http://www.python.org/dev/peps/pep-0249/"&gt;DBAPI&lt;/a&gt;, is to use a flat
    file DB system. I use &lt;a href="http://www.sqlalchemy.org/"&gt;sqlalchemy&lt;/a&gt;, and patching it in my test
    fixtures is easy as patching one line: the sqluri. For example,
    &lt;em&gt;'&lt;strong&gt;mysql://user:pass/server/base'&lt;strong&gt;&lt;em&gt; will become
    **'&lt;/em&gt;&lt;/strong&gt;&lt;/strong&gt;pysqlite:///path/to/package/tests/data/test.db'&lt;/em&gt;&lt;em&gt;. &lt;/em&gt;The
    tests then interact with a &lt;a href="http://www.sqlite.org/"&gt;SQLite&lt;/a&gt; file, and as long as your code
    uses sqlalchemy APIs, everything should work like if the DB was
    MySQL or Postgres. The only difference I can think of is the DB
    unicode settings, that might be different in the production server,
    so be careful in your doctest when you test strings.
-   For other third party elements, &lt;a href="http://theblobshop.com/pymock/"&gt;Mocking&lt;/a&gt; can help !&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 05 Oct 2007 10:03:00 +0200</pubDate><guid>http://blog.ziade.org/2007/10/05/testing-code-that-calls-third-party-servers/</guid></item><item><title>4 tips to keep in mind about the ZODB</title><link>http://blog.ziade.org/2007/10/04/4-tips-to-keep-in-mind-about-the-zodb/</link><description>&lt;p&gt;Plone is a great tool, probably one of the best CMS out there, that let
you rapidly create an advanced web application. But there are some real
pitfalls you might fall into if you are not familiar to the underlying
technology. This post provides a list of tips that can help you out. I
am not pretending to give you a set of solutions to make your website
fast and scalable, but by following those tips, you will think the way
experienced Zope developers think when they code an application. &lt;br /&gt;
1.  &lt;br /&gt;
&lt;strong&gt;Ask you customer what will be the load&lt;/strong&gt;.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;s very important to know it when you are starting a Plone&lt;/span&gt;
&lt;span class="s"&gt;project. That might affect a lot how you are going to work: if the&lt;/span&gt;
&lt;span class="s"&gt;application needs to deal with hundreds of hits per minute, and&lt;/span&gt;
&lt;span class="s"&gt;store gigas of datas, you won&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="n"&gt;organize&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;same&lt;/span&gt; &lt;span class="n"&gt;way&lt;/span&gt; &lt;span class="n"&gt;than&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt;
&lt;span class="n"&gt;load&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;ridiculous&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;like&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;few&lt;/span&gt; &lt;span class="n"&gt;hits&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Project&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="n"&gt;load&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt;
&lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;future&lt;/span&gt; &lt;span class="n"&gt;also:&lt;/span&gt; &lt;span class="n"&gt;how&lt;/span&gt; &lt;span class="n"&gt;big&lt;/span&gt; &lt;span class="n"&gt;will&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;site&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;one&lt;/span&gt; &lt;span class="n"&gt;month&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;one&lt;/span&gt; &lt;span class="n"&gt;year&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt;
&lt;span class="n"&gt;If&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="n"&gt;load&lt;/span&gt; &lt;span class="n"&gt;grows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;you&lt;/span&gt; &lt;span class="n"&gt;might&lt;/span&gt; &lt;span class="n"&gt;need&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="n"&gt;up&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;cyclic&lt;/span&gt; &lt;span class="n"&gt;purge&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt;
&lt;span class="n"&gt;make&lt;/span&gt; &lt;span class="n"&gt;sure&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;stuff&lt;/span&gt; &lt;span class="n"&gt;doesn&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="n"&gt;huge&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Ask yourself what data will be stored in the ZODB&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The ZODB is a great database, and provides a lot of features for
CMS: each object (e.g. Document) is automatically saved on each
change. This persistent layer, à la Hibernate, is nice. But this has
a cost: if hundreds of objects are created by hundreds of users per
minute, it won't work. It will technically work but will become very
slow because the ZODB has to deal with concurrency changes on the
objects. You might argue that this won't be a problem if you take
care of who changes the data and how, but you won't be able to deal
with how base objects work in the ZODB. For example, BTrees objects,
that are supposed to be fast and to be able to deal with many items,
are not really scalable on writes: on my Intel MacBook, on a
BTreeFolder containing 1000 items, running 4 concurrent threads that
are creating objects in a pace of 300 ms, will generate conflict
error on 10% of the requests. You should really ask yourself if you
need ZODB features on some data. Maybe they will fit better in a SQL
database if they don't need versionning or sophisticated workflows.&lt;/p&gt;
&lt;p&gt;The ZCatalog in its classical shape, is also a particular case. It
can weight 40% of the ZODB total size, so if you index a lot of
things, and a lot of features on your websites are based on queries,
it's maybe a good idea to used a specialized database, like
&lt;a href="http://tarekziade.wordpress.com/2007/06/12/indexation-service-with-xapian/"&gt;Xapian&lt;/a&gt; or Lucene. It's faster, and it won't make your ZODB grow.
Hey, why indexes are stored in the ZODB anyway ?
3.  &lt;br /&gt;
&lt;strong&gt;Don't hide your code behind caches&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;SQUID, memcached, CacheFu. All those tools are wonderful and
mandatory when you put your site in production. But you should not
hide your code behind them : that's the best way to code a crappy,
badly architectured application. You should take care on how your
application scales and on your code effectiveness before you think
about caching. Be careful about the complexity of each of your
function, and how they scale.
4.  &lt;br /&gt;
&lt;strong&gt;Be careful of the pound/ZEO mirage&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Making a cluster of ZEO nodes will not make your application
faster, it will just raise the number of concurrent threads your
application can handle simultaneously. Each node needs to be
synchronized behind the scene everytime a data is changed. So the
more nodes you have the more network traffic you will get. This can
damage the overall performance of the application as well.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;There's a lot of work going on in this area. I am looking forward for
what people will do on this sprint for example:
&lt;a href="http://plone.org/events/sprints/copenhagen-performance-sprint"&gt;http://plone.org/events/sprints/copenhagen-performance-sprint&lt;/a&gt;&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 04 Oct 2007 02:26:00 +0200</pubDate><guid>http://blog.ziade.org/2007/10/04/4-tips-to-keep-in-mind-about-the-zodb/</guid></item><item><title>Extending setuptools: adding a new command</title><link>http://blog.ziade.org/2007/09/30/extending-setuptools-adding-a-new-command/</link><description>&lt;p&gt;Before deploying a package with &lt;em&gt;python setup.py install&lt;/em&gt;, it's a good
idea to launch the tests with &lt;em&gt;python setup.py test&lt;/em&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This command can be used as well to quickly launch tests within a
package that is being developed. Since &lt;em&gt;setuptools&lt;/em&gt; can be extended,
other commands can be added to be launched from the &lt;em&gt;setup&lt;/em&gt; script,
while you work in your package. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I have created for example a &lt;em&gt;qa&lt;/em&gt; command that launches &lt;a href="http://divmod.org/trac/wiki/DivmodPyflakes"&gt;&lt;em&gt;pyflakes&lt;/em&gt;&lt;/a&gt;
over the package code, to make sure I don't leave unused import. I could
have used a direct &lt;em&gt;pyflakes&lt;/em&gt; call, but my QAs test are going to grow so
keeping the QA script details under a &lt;em&gt;python setup.py qa&lt;/em&gt; call is a
good practice. This will also make buildbot integration easier, as I can
check for package QA through an unified serie of calls, that plays with
the package &lt;em&gt;setup.py&lt;/em&gt; script. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Commands are simple class that derives from &lt;em&gt;setuptools.Command&lt;/em&gt;, and
define some minimum elements, which are: &lt;br /&gt;
-   description: describe the command
-   user_options: a list of options
-   initialize_options(): called at startup
-   finalize_options(): called at the end
-   run(): called to run the command&lt;/p&gt;
&lt;p&gt;The &lt;a href="http://peak.telecommunity.com/DevCenter/setuptools#adding-commands"&gt;&lt;em&gt;setuptools&lt;/em&gt; doc&lt;/a&gt; is still empty about subclassing &lt;em&gt;Command&lt;/em&gt;,
but a minimal class will look like this: &lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt; &lt;span class="n"&gt;class&lt;/span&gt; &lt;span class="n"&gt;MyCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

     &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;setuptools Command&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

     &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;run my command&amp;quot;&lt;/span&gt;

     &lt;span class="n"&gt;user_options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;initialize_options&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

         &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;init options&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

         &lt;span class="n"&gt;pass&lt;/span&gt;

     &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;finalize_options&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

         &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;finalize options&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

         &lt;span class="n"&gt;pass&lt;/span&gt;

     &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

         &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;runner&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

         &lt;span class="n"&gt;XXX&lt;/span&gt; &lt;span class="n"&gt;DO&lt;/span&gt; &lt;span class="n"&gt;THE&lt;/span&gt; &lt;span class="n"&gt;JOB&lt;/span&gt; &lt;span class="n"&gt;HERE&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The class can then be hook as a command, using an entry point in its
&lt;em&gt;setup.py&lt;/em&gt; file: &lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt; &lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;

     &lt;span class="c1"&gt;# ...&lt;/span&gt;

     &lt;span class="n"&gt;entry_points&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

     &lt;span class="s"&gt;&amp;quot;distutils.commands&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;

     &lt;span class="s"&gt;&amp;quot;my_command = mypackage.some_module:MyCommand&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;

 &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This will add an entry point when the package is installed, so you can
run your new command this way: &lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt; &lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; &lt;span class="n"&gt;my_command&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;You can give a try of my &lt;em&gt;qa&lt;/em&gt; example by installing my &lt;em&gt;eggchecker&lt;/em&gt;
package: &lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt; &lt;span class="n"&gt;easy_install&lt;/span&gt; &lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;programmation&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/pycommunity/&lt;/span&gt;&lt;span class="n"&gt;eggchecker&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tgz&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;That's merely a draft, but will show you how &lt;em&gt;pyflakes&lt;/em&gt; is launched
within the &lt;em&gt;setup.py&lt;/em&gt; script.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sun, 30 Sep 2007 01:51:00 +0200</pubDate><guid>http://blog.ziade.org/2007/09/30/extending-setuptools-adding-a-new-command/</guid></item><item><title>A co-server for Zope</title><link>http://blog.ziade.org/2007/09/28/a-co-server-for-zope/</link><description>&lt;h1&gt;Zasync&lt;/h1&gt;
&lt;p&gt;A few years ago, when we hit with CPS on some big customers intranet
scalability problems, we started to use &lt;a href="http://www.zope.org/Members/poster/zasync"&gt;ZAsync&lt;/a&gt; in order to perform
some tasks in the background. That improved a lot the application
overall performance. What ZAsync does is recording in BTrees within the
ZODB tasks to perform, let's say Python scripts to simplify. Then a
twisted client that runs independantly opens the ZODB to read the BTree
and find the task to perform. It acts like another Zope thread in some
ways. But there's something I never understood: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why the job queue is stored in the ZODB database ?&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;When we talk about scalability, most of time, the infrastructure is
more complex than a simple ZEO. It has Apaches, smtps, load balancers
all over the place. It has cron tasks to perform a variety of things,
link sending mails, creating images, or anything that can be done in the
background. It has most of the time other piece of software that perform
other things. Having a co-server that gives Zope code the ability to
perform background tasks is good. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Having a co-server that gives any software the ability to program a
task is better&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Many applications, many different Zope instances, can benefit from a
centralized task manager. &lt;br /&gt;
&lt;/p&gt;
&lt;h1&gt;Quartz&lt;/h1&gt;
&lt;p&gt;In Java world, I have used a server called &lt;a href="http://www.opensymphony.com/quartz/"&gt;Quartz.&lt;/a&gt; It is an
independant task manager, where you can register tasks and perform jobs,
given a timing. It's like a smart cron. Using the beans technology, it
can run code independantly, or run it within a Java Server application's
context. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why don't we have such a software in Python ?&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Maybe we do, but I have never found it, so i ported part of the idea to
Python in a tool called TaskManager, that I use for example on
&lt;a href="http://fr.luvdit.com/"&gt;fr.luvdit.com&lt;/a&gt; which is a Django application. It sends mails,
calculates neighbourhoods, etc.. Maybe I should release it but that's a
packaging work I didn't find the time to do. Any piece of Python
software can register itself as a task, in order to provide a service.
The jobs are stored into a SQL Database, that is opened through an API
by all the clients that want to perform a task, and by the co-server
that reads the queue and actually perform the tasks. It has three queue
in fact, for different priorities. The client-side APIs are really
simple and are nothing but SQL queries. &lt;br /&gt;
&lt;/p&gt;
&lt;h1&gt;&lt;a href="https://launchpad.net/lovely.remotetask"&gt;lovely.remotetask&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Back to Zope. Lovely systems works on a Zope 3 tool, which seems to be
working a bit like ZAsync: it stills stores the tasks in the ZODB, but
dedicates a Zope application to work as a web service provider if I
understood well. It's the way to go in term of infrastructure but I
think that it's overkill to use a Zope instance for that. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why do we need to deploy a whole Zope stack to have a co-server ?&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;A dedicated, pure Python application, using a SQL database, fits better
because several task runners can work in the same queue, to create a
real producer-consumers queue. In their need to perform tasks on various
platforms, having a centralized job queue and several executors is more
scalable because the producer doesn't deal with several co-servers. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Furthermore, the XML-RPC layer is not a necessity, and not as robust as
SQL: if the co-server is down, the Zope server cannot send jobs anymore,
or check for job states and get them. Working with a SQL table prevent
from this. You might argue that this is the worst scenario, but by
experience, the more application servers an infrastructure has, the more
potential point of failure you get. You might argue that the SQL server
might go down as well, but it's not a code stack, and just holds data to
be processed: all the functionalities, thus the weaknesses, are on the
co-server side. You might also argue that it makes the solution
Python-dependant, but it would be deadly simple to provide a client for
another language. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Anyway, using the ZODB to store such things and a Zope to play with
them is a small mistake in my humble opinion, even if it's based on
PersistentQueue, which looks pretty robust. Let's keep this kind of
database do what it was meant for: storing persistent objects that are
publishable. &lt;br /&gt;
&lt;/p&gt;
&lt;h1&gt;What I would love to have&lt;/h1&gt;
&lt;p&gt;The perfect co-server that I can think of, would be an independant
Python software, like TaskManager that would look like this: &lt;br /&gt;
             -------        -------  &amp;lt;-&amp;gt; co-server instance 1 / win32&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;         &lt;span class="o"&gt;|&lt;/span&gt; zope  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&amp;gt;&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt; sqldb &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&amp;gt;&lt;/span&gt; co&lt;span class="o"&gt;-&lt;/span&gt;server instance &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; linux

          &lt;span class="o"&gt;-------&lt;/span&gt;        &lt;span class="o"&gt;-------&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;-&amp;gt;&lt;/span&gt; co&lt;span class="o"&gt;-&lt;/span&gt;server instance &lt;span class="m"&gt;3&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; linux

                           &lt;span class="o"&gt;^&lt;/span&gt;

 &lt;span class="o"&gt;----------------&lt;/span&gt;          &lt;span class="o"&gt;|&lt;/span&gt;

&lt;span class="o"&gt;|&lt;/span&gt; another server &lt;span class="o"&gt;|&amp;lt;--------&lt;/span&gt;

 &lt;span class="o"&gt;----------------&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;sqldb is a database that store jobs;&lt;/li&gt;
&lt;li&gt;each arrow is provided by a python API, that knows how to interact
    with the database;&lt;/li&gt;
&lt;li&gt;a co-server is an independant, pure Python runner, that picks up
    some work into the DB;&lt;/li&gt;
&lt;li&gt;each co-server instance is able to perform tasks, that are provided
    through a plugin system.&lt;/li&gt;
&lt;li&gt;for zope-dependant tasks, a generic task provides an entry point to
    execute code through XML-RPC calls &lt;em&gt;or&lt;/em&gt; through a direct ZODB
    opening to avoid eating a thread (eg à la ZAsync);&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;OK, this is exactly Quartz :) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In the last five years, most of the scalability problems I bumped into,
were resolved by a good practice: &lt;strong&gt;let's be less Zope-centric when we
talk about infrastructure&lt;/strong&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I would be pleased to have a few comments from Lovely guys on this
topic, and I thank them for their latest post, that helps a lot the
community to think about scalable solutions for Zope.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;lovely-remotetask&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 28 Sep 2007 08:13:00 +0200</pubDate><guid>http://blog.ziade.org/2007/09/28/a-co-server-for-zope/</guid></item><item><title>Using JMeter as a functional test tool</title><link>http://blog.ziade.org/2007/09/28/using-jmeter-as-a-functional-test-tool/</link><description>&lt;p&gt;Today I made an audit on some customer intranet, and I used &lt;a href="http://jakarta.apache.org/jmeter/"&gt;JMeter&lt;/a&gt;
to perform stress tests. This tool is awesome, as you can get a whole
lot of live statistics, and create a powerfull, distributed stress
campaign. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;There are some features to control that the output of HTTP calls are
right, with simple but sufficient patterns (the output contains, the
output doesn't contains, the header has.., etc..) and regular
expressions can be used. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;A high-level functional test is really nothing much more than that: it
performs a user story and check for the result. Ok, maybe some tool like
&lt;a href="http://wiki.openqa.org/display/SEL/Home"&gt;Selenium&lt;/a&gt; have more features, but they are not essential ones, and
JMeter brings some better things. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;What brings JMeter beside functionnal testing are: &lt;br /&gt;
-   a powerfull reporting tool
-   the ability to stress-load your application with the user stories&lt;/p&gt;
&lt;p&gt;A comparable tool, less powerfull though, is &lt;a href="http://funkload.nuxeo.org/"&gt;ben's funkload&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;-&gt; Creating you app through JMeter will give you the opportunity to
tune it without extra work. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It won't make me drop zope.testbrowser tests, because those are merged
within my code and explain how it works in doctests, but it will surely
make my customers feel better with JMeter reporting capabilities, and
myself calmer with its performance analysis: &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;"Hey, look at the screen, that's your functionality #123 running
right now, and you can see its performance through this live performance
graph"&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;[![graph_results.png][]][]&lt;/p&gt;
&lt;p&gt;[![graph_results.png][]]: http://tarekziade.files.wordpress.com/2007/09/graph_results.png
    "graph_results.png"&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 28 Sep 2007 08:05:00 +0200</pubDate><guid>http://blog.ziade.org/2007/09/28/using-jmeter-as-a-functional-test-tool/</guid></item><item><title>Eight tips to start with Python</title><link>http://blog.ziade.org/2007/09/24/eight-tips-to-start-with-python/</link><description>&lt;p&gt;A friend of mine is starting Python. I tried to sum up some tips for
him, that may be useful to others. Don't hesitate to comment it if you
think something important is missing.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Get the best online documentation&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;There are a few online documentation you must read: &lt;br /&gt;
   -   the &lt;a href="http://docs.python.org/tut/tut.html"&gt;official tutorial&lt;/a&gt;, that gives you a quite complete
    overview of Python;
-   the standard library &lt;a href="http://docs.python.org/modindex.html"&gt;module index&lt;/a&gt;. You can download it to
    simplify the search through greps. This is the documentation you
    get through the help command in the prompt.
-   Active State's &lt;a href="http://aspn.activestate.com/ASPN/Python/Cookbook/"&gt;Python Cookbook&lt;/a&gt;. There are thousands of code
    snippets that are created, ranked, categorized and commented by
    developers.
-   &lt;a href="http://www.diveintopython.org"&gt;Dive Into Python&lt;/a&gt; online book, that makes you discover Python
    features through well thought examples.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Read &lt;a href="http://us.pycon.org/2008/about"&gt;PyCon&lt;/a&gt;, &lt;a href="http://europython.org/"&gt;EuroPython&lt;/a&gt; and &lt;a href="http://www.pyconuk.org"&gt;Pycon UK&lt;/a&gt; wrapups and
    slides&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;They are the three main Python events, and a lot of things are
happening there. You'll learn a lot by reading the talks slides. If
you can go there, it's even better: sprints, bird of feathers and
lighting talks are organized. To convince your boss to send you
there, you could make a talk proposal "My first steps in Python" ;)
3.  &lt;br /&gt;
&lt;strong&gt;Suscribe to the right feeds&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The mainstream is &lt;a href="http://planet.python.org/"&gt;Planet Python&lt;/a&gt;. It gathers most of the
        blogs out there, so it is the best place to start.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.reddit.com/r/Python/"&gt;Reddit's Python&lt;/a&gt; is a great place to get the hot topics.&lt;/li&gt;
&lt;li&gt;Pythonware's &lt;a href="http://www.pythonware.com/daily"&gt;Daily Python URL&lt;/a&gt;. Human-filtered feed. It used
    to provide several dozains of links per week, but it seems to
    have slowed down, and provides a few links a week now. I think
    it's better this way.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Learn and use the rising standards&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;There are a few libraries that have a deep impact on the way people
write and distribute their work: &lt;br /&gt;
   -   &lt;a href="http://peak.telecommunity.com/DevCenter/setuptools"&gt;setuptools&lt;/a&gt;: helpers to build and distribute your code
    &lt;a href="http://peak.telecommunity.com/DevCenter/PythonEggs"&gt;eggs&lt;/a&gt;. A public repository à la Perl's CPAN called
    &lt;a href="http://cheeseshop.python.org/pypi/"&gt;Cheeseshop&lt;/a&gt; is wired with this library so people can
    distribute their code there. It's one of the major innovation of
    last years in Python world in my opinion.
-   &lt;a href="http://sqlalchemy.org"&gt;sqlalchemy&lt;/a&gt;: The &lt;a href="http://en.wikipedia.org/wiki/Object-relational_mapping"&gt;ORM&lt;/a&gt; that is now used by the majority of
    Python frameworks. Its flexibility is impressive. I think there
    is no equivalent tool in any other language (please let me know
    if there is);
-   &lt;a href="http://pythonpaste.org/"&gt;Python paster&lt;/a&gt;. This tool allows you to create templates that
    can be used to generate skeletons for your code. It is used by
    many web frameworks to provide people a simple way to generate a
    standardized boiler-plate code canvas when they start up
    something. This is done in Java for quite a long time (you
    cannot do without it in Java, otherwise it would take you years
    to write any program ;)), and tools like &lt;a href="http://sqlalchemy.org"&gt;PyDev&lt;/a&gt; and
    Eclipse would provide the canvas to do similar things. But the
    paster is independant from any IDE;
-   &lt;a href="http://docutils.sourceforge.net/rst.html"&gt;reStructuredText&lt;/a&gt;: learn how to use it. It's our LaTeX. Your
    code documentation should use it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Ask for help&lt;/strong&gt;. The three places you can get some help are: &lt;br /&gt;
&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;the &lt;a href="http://mail.python.org/mailman/listinfo/python-list"&gt;mailing list&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;the irc channel #python on freenode.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.python.org/mailman/listinfo/tutor"&gt;the tutor mailing list&lt;/a&gt;. Mihai Campean says: &lt;em&gt;"This is a list
    specifically for those new to Python and those interested in
    helping people learn the language, and the atmosphere is very
    friendly. It’s probably a better place to start than
    python-list, in my opinion"&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;There are some talented guys that dedicate their free time to help
newcomers.
6.  &lt;br /&gt;
&lt;strong&gt;Try to adapt your successfull code patterns&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;When I started Python, I tried to adapt what I used to do with the
tool I mastered then (Delphi). Since There should be one-- and
preferably only one --obvious way to do it. (try import this in a
prompt), that helped me a lot to learn and understand all the
subtles of Python on use cases I mastered. &lt;br /&gt;
The most pleasant thing about it is that you quickly drop all
Python books and guide to work with the language, unlike Java for
example, where you need to keep many reference books on your desk.
7.  &lt;br /&gt;
&lt;strong&gt;Share on your experience and participate !&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;A newcomer (yeah! fresh blood!) experience is a highly valuable
material for the language advocacy: the discovering state of mind
sometime reveals weaknesses or absurdities experienced users don't
see anymore. Furthermore, fresh new ideas are often brought by
people that comes from other communities. If you feel that something
is absurd, unclear or wrong, you should start a thread on the
language mailing list. If you have an idea onany kind of
enhancement, maybe it worth a &lt;a href="http://www.python.org/dev/peps/"&gt;Python Enhancement Proposal&lt;/a&gt;.
8.  &lt;br /&gt;
&lt;strong&gt;Watch what is being done in Python 3, PyPy and web frameworks&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://en.wikipedia.org/wiki/Python_3"&gt;Python 3&lt;/a&gt; is the next version of Python, &lt;a href="http://codespeak.net/pypy/dist/pypy/doc/news.html"&gt;PyPy&lt;/a&gt; is Python
written in Python. Web frameworks like &lt;a href="http://djangoproject.org"&gt;Django&lt;/a&gt; or &lt;a href="http://zope.org"&gt;Zope&lt;/a&gt; are
large Python codebases. These three sub-communities have something
in common: they form the R&amp;amp;D of the language. &lt;br /&gt;
Zope for example, has enhanced a lot setuptools and doctest through
a massive feedback. Keeping an eye on them even if you don't use
them will make you live and understand what rises in the language. &lt;br /&gt;
PyPy is an amazing project. Even if you don't understand everything
(Python in Python ? what the... ;)), seeing one of Armin Ringo talks
will give you an instructive high level view of Python. Now for
Python 3, even if you cannot read and understand all threads in the
dedicated mailing list, keeping an eye of Guido's wrapups and thread
subjects will help you to do the jump on P3k, and probably make your
Python 2 code look nicer.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Mon, 24 Sep 2007 07:56:00 +0200</pubDate><guid>http://blog.ziade.org/2007/09/24/eight-tips-to-start-with-python/</guid></item><item><title>How to generate proper reStructuredText titles with Python Paste</title><link>http://blog.ziade.org/2007/09/21/how-to-generate-proper-restructuredtext-titles-with-python-paste/</link><description>&lt;p&gt;&lt;a href="http://pythonpaste.org/"&gt;Python Paste&lt;/a&gt; and &lt;a href="http://plone.org/products/zopeskel"&gt;ZopeSkel&lt;/a&gt; are just great, if you don't know them
and you are working on Zope packages, you should really take the time to
look at them. &lt;br /&gt;
-   Python Paste provides, besides other features, a engine to generate
    any kind of package structures based on templates. The templates can
    be written in &lt;a href="http://www.cheetahtemplate.org"&gt;Cheetah&lt;/a&gt; syntax;
-   ZopeSkel is a serie of templates that helps a Zope and/or Plone
    developer to start a Python, Zope or Plone package, taking care of
    all the boiler plate code generation, to make sure the packages are
    done in a egg-compatible, standard way.&lt;/p&gt;
&lt;p&gt;Basing my work on ZopeSkel, I have started to create a serie of custom
templates to speed up and simplify package coding bootstraps. The
documentation in these packages are in &lt;a href="http://docutils.sourceforge.net/rst.html"&gt;reStructuredText&lt;/a&gt;, and I
bumped into a small problem: when you create for example a "README.txt"
in your template, which has a title that uses the project name, for
example: &lt;br /&gt;
   ======================&lt;/p&gt;
&lt;p&gt;$project documentation&lt;/p&gt;
&lt;p&gt;======================&lt;/p&gt;
&lt;p&gt;blablabla&lt;/p&gt;
&lt;p&gt;the result may vary depending on the variable value: &lt;br /&gt;
   ======================&lt;/p&gt;
&lt;p&gt;kool documentation&lt;/p&gt;
&lt;p&gt;======================&lt;/p&gt;
&lt;p&gt;blablabla&lt;/p&gt;
&lt;p&gt;or even: &lt;br /&gt;
   ======================&lt;/p&gt;
&lt;p&gt;i_like_long_names_for_packages documentation&lt;/p&gt;
&lt;p&gt;======================&lt;/p&gt;
&lt;p&gt;blablabla&lt;/p&gt;
&lt;p&gt;It's ugly, and the reST file won't compile if you try to generate html
or PDF with &lt;a href="http://docutils.sourceforge.net/"&gt;docutils&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Since Paste uses Cheetah, we can fix this, by calculating the length of
underlines: &lt;br /&gt;
   #repeat $len($project) + 13&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c1"&gt;#slurp&lt;/span&gt;

&lt;span class="c1"&gt;#end repeat&lt;/span&gt;

&lt;span class="nv"&gt;$project&lt;/span&gt; &lt;span class="n"&gt;documentation&lt;/span&gt;

&lt;span class="c1"&gt;#repeat $len($project) + 13&lt;/span&gt;

&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c1"&gt;#slurp&lt;/span&gt;

&lt;span class="c1"&gt;#end repeat&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;blablabla&lt;/p&gt;
&lt;p&gt;This will create the proper length. You might argue it's a detail, but
that saves me almost ten seconds for each new package now ;)&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 21 Sep 2007 09:54:00 +0200</pubDate><guid>http://blog.ziade.org/2007/09/21/how-to-generate-proper-restructuredtext-titles-with-python-paste/</guid></item><item><title>To blob or not to blob ?</title><link>http://blog.ziade.org/2007/09/14/to-blob-or-not-to-blob/</link><description>&lt;p&gt;Back to the storing discussion: at this time we have a quite complete
tool to handle big files in Plone: File System Storage (&lt;a href="http://ingeniweb.sourceforge.net/Products/FileSystemStorage"&gt;FSS&lt;/a&gt;). &lt;br /&gt;
&lt;/p&gt;
&lt;h1&gt;&lt;a href="#id1" title="fss-current-features"&gt;FSS Current features&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;The idea of this product is to provide a storage with a backup
mechanism, to be able to do some restore. It can handle big file and
store them on server-side with several strategies: &lt;br /&gt;
-   Flat storage: All field values are stored in a flat structure. This
    strategy is the default one.
-   Directory storage strategy: All field values are stored in a
    directory structure.
-   Site storage strategy 1: All field values are stored in a directory
    structure mirroring structure of PloneSite. Backup files are stored
    in a flat structure.
-   Site storage strategy 2: All field values are stored in a directory
    structure mirroring structure of PloneSite. Backup files are stored
    in a flat structure.&lt;/p&gt;
&lt;p&gt;More information on this here : &lt;a href="http://ingeniweb.sourceforge.net/Products/FileSystemStorage/#storage-strategies"&gt;FSS Strategies&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;h1&gt;&lt;a href="#id2" title="a-blob-strategy"&gt;A Blob Strategy ?&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;ZODB Blob, that are beeing integrated into Zope provide similar
features: it stores on the filesystem the file and provide access from
the ZODB. To configure such a storage, the zodb_db section of zope.conf
would look like this: &lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nt"&gt;&amp;lt;zodb_db&lt;/span&gt; &lt;span class="err"&gt;main&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="err"&gt;#&lt;/span&gt; Main FileStorage database

    &lt;span class="nt"&gt;&amp;lt;blobstorage&amp;gt;&lt;/span&gt;

      blob-dir &lt;span class="p"&gt;$&lt;/span&gt;&lt;span class="nv"&gt;INSTANCE&lt;/span&gt;/var/blobs

      &lt;span class="nt"&gt;&amp;lt;filestorage&lt;/span&gt; &lt;span class="na"&gt;base=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;1&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

        path &lt;span class="p"&gt;$&lt;/span&gt;&lt;span class="nv"&gt;INSTANCE&lt;/span&gt;/var/Data.fs

      &lt;span class="nt"&gt;&amp;lt;/filestorage&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;/blobstorage&amp;gt;&lt;/span&gt;

    mount-point /

&lt;span class="nt"&gt;&amp;lt;/zodb_db&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This tells Zope at startup to create a blob-compatible storage and
store blobs in the $INSTANCE/var/blobs folder. Then, the subsection
provide the regular Data.fs. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Using this kind of configuration will let us use ZODB.blob APIs in the
code. To give a shot on blobs, I have created a new strategy on FSS,
that uses blobs. A base class handles blob readings and writings: &lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;class&lt;/span&gt; &lt;span class="n"&gt;ImplicitBlob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Implicit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blob&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Blob&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

                 &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mimetype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;text/plain&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;stores blob metadata&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

        &lt;span class="n"&gt;blob&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Blob&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;

        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;

        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mimetype&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mimetype&lt;/span&gt;

        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;

    &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;get_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;Return the size of the blob.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

        &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;r&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;try:&lt;/span&gt;

            &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;seek&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;tell&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="n"&gt;finally:&lt;/span&gt;

            &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;

    &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;updateMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;fills the metadata&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__dict__:&lt;/span&gt;

                &lt;span class="n"&gt;setattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;writeValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;fills the blob&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

        &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;w&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;try:&lt;/span&gt;

            &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;finally:&lt;/span&gt;

            &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;getValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;returns the blob whole content&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

        &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;r&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;try:&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="n"&gt;finally:&lt;/span&gt;

            &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The real version can be found in the collective repository in a
&lt;a href="http://dev.plone.org/collective/browser/FileSystemStorage/branches/tarek_blob_support"&gt;branch&lt;/a&gt;. This Zope2-style code is not what I would have done in a new
product, but my goal was to quickly add the feature in FSS to make some
tries. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It took me quite a time though, to set up the test fixture to be sure
to run the tests over a BlobStorage instead of a DemoStorage. You must
create a custom_zodb.py file into your tests folder and make sure it's
called before the TestCase imports. Here's my file: &lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;ZODB&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FileStorage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FileStorage&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FileStorage&lt;/span&gt;

&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;ZODB&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MappingStorage&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MappingStorage&lt;/span&gt;

&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;ZODB&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blob&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BlobStorage&lt;/span&gt;

&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;tempfile&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;mkdtemp&lt;/span&gt;

&lt;span class="n"&gt;base_storage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MappingStorage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;test&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;blob_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mkdtemp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;Storage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BlobStorage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blob_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base_storage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;(Thanks ZODB guys for using doctests in your code, that helped me much)&lt;/p&gt;
&lt;p&gt;Once the ImplicitBlob is made available, I just addded a new strategy
in FSS that uses it this way: &lt;br /&gt;
-   the tool becomes a BTreeFolder2 object and holds
    ImplicitBlobinstances in it;
-   each blob id is the file uid;
-   the strategy knows how to return a File object;&lt;/p&gt;
&lt;p&gt;Now I can add FSSItem instances that are stored into blobs. And guess
what, it seems to work ;) &lt;br /&gt;
&lt;/p&gt;
&lt;h1&gt;&lt;a href="#id3" title="what-s-next"&gt;What's next ?&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Using blobs seems to be the future of FSS, because it has all the
features needed to store files. Furthermore blob are transactional, and
this is quite a difference with FSS's regular file storages, because in
that case, it's not necessary anymore to set up a NFS to be
ZEO-compatible. But all FSS strategies have use cases that we need to
keep. For example, the Site Storage Strategy is really sweet: user can
find back their file on the filesystem with the same name, etc. Even
though we know this is not really important technically speaking, it can
be reassuring for the customer. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For large sites though, I wouldn't use blobs and would set up a
specialized co-server. Tramline fills the bill and we are thinking about
providing a tramline-friendly strategy in FSS, in order to provide
direct access to the files. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But all of this is a work in process, and the blob strategy still needs
a lot of testing.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 14 Sep 2007 16:08:00 +0200</pubDate><guid>http://blog.ziade.org/2007/09/14/to-blob-or-not-to-blob/</guid></item><item><title>How to handle large files in Plone ?</title><link>http://blog.ziade.org/2007/09/10/how-to-handle-large-files-in-plone/</link><description>&lt;p&gt;The first time I had to help out a customer (a fifty year old lady that
had no interest whatsoever about computers) on a Plone-based intranet, I
had to explain to her why she couldn't upload really big files. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;That was really tough, as she couldn't understand why the system had
such a limitation. I think she was right. If we drop the technical point
of view, a sophisticated system such as Plone should provide a
transparent way to upload big files and handle them smoothly on the
server side, so the user doesn't feel any difference. Think about it:
what's the functional difference between a big file and a small file ?
Merely none, except that it's longer to put or to get. &lt;br /&gt;
&lt;/p&gt;
&lt;h1&gt;Uploading&lt;/h1&gt;
&lt;p&gt;The first problem when we deal with big files is the upload time.
Browser doesn't provide any feedback, unless you install some Firefox
extension or you use a dedicated protocol, such as FTP. In a web
interface, there's nothing but an animated logo that is moving around
just to say: "Hey, I am not dead !". Ruby On Rails guys came up with a
great feature on this topic: they added in their publiser a few apis
that would let client-side Javascript: &lt;br /&gt;
-   display a progress bar;
-   cancel the upload at anytime.&lt;/p&gt;
&lt;p&gt;I am not a technical guru on HTTP protocols but I am really wondering
why this is not already available in Gmail... I tried myself to
implement it a while ago on Zope 3 publisher, and came up with something
that was working, but never had the time to polish it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The idea is to maintain on server-side, for each ongoing uploads, some
infos that can be read by the client, and a cancel API. The only
important point is to provide a secure mechanism so no other client can
read the infos of another client. The client-side JS code then can call
asynchronously the server to display feedback, and provide a cancel
button. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;At this point, we may consider that it is better to use other piece of
software, like &lt;a href="http://infrae.com/products/tramline"&gt;Tramline&lt;/a&gt;, or some dedicated Apache plugin. But in a
marketing point of view, I think it's really important to be able to
provide this feature with an out-of-the-box Plone. &lt;br /&gt;
&lt;/p&gt;
&lt;h1&gt;Server-side handling&lt;/h1&gt;
&lt;p&gt;OK, so our file is uploaded. My lady at the office now complains that
the system is getting very slow. No wonder, her ZODB weights now several
gigas... Again, functionaly speaking, that's a non-sense. Plone should
be able to work with these big files without making the ZODB so heavy.
The ZODB is a great thing for light objects, but was not meant to hold
big chunk of data. A specific thing has to be done. In Java world, they
have an advanced data storage for Content Managment System, called
&lt;a href="http://jcp.org/en/jsr/detail?id=170"&gt;JSR-170&lt;/a&gt;, and implemented by the &lt;a href="http://en.wikipedia.org/wiki/JSR-170"&gt;JCR&lt;/a&gt;. In this system, a tree of
folders is maintained and big files are stored in blobs (Binar Large
OBjectS), keeping the whole thing scalable, no matter what the users
store in it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;In Zope, we don't have a lot of solutions, there's the File System
Storage (&lt;a href="http://ingeniweb.sourceforge.net/Products/FileSystemStorage"&gt;FSS&lt;/a&gt;) product (and a few similar products) in the Plone
Collective, and that's about it. The FSS provides a smart proxy over the
file system, that let the ZODB breathe and the user handle his big
files. But this is an extra product, created to provide a really missing
core feature. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The &lt;a href="http://www.nabble.com/ZODB-3.8.0b1-is-released-t3903510.html"&gt;3.8 version of ZODB&lt;/a&gt; though, providen this feature now, through
blobs. On Plone side, it seems that the latest &lt;a href="http://plone.org/news/plone4artists-sprint-in-boston-a-success"&gt;Plone4Artists sprint&lt;/a&gt;
in Boston has boosted the work in the topic and blobs will be available
in Plone through &lt;a href="http://dev.plone.org/collective/browser/ATBlobField"&gt;ATBlobField&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This is great news, and I hope we will soon be able to make all files
uses this feature transparently (big file, small file, what the
difference ? they should all be stored out of the ZODB) &lt;br /&gt;
&lt;/p&gt;
&lt;h1&gt;Downloading&lt;/h1&gt;
&lt;p&gt;One of my colleague had a tough issue last week on big files. The File
System Storage was not acting right when the user was downloading PDF
files from the Adobe Browser plugin: the file iterator internally
provided by the Zope publisher was not acting right because it doesn't
support the range feature. This HTTP feature used by the plugin make it
possible to get pieces of files in non-sequential orders. Though, it's
not possible with an iterator to provide pieces of files without having
to rewind. So iterating is of no use there. The solution would be to
provide a smart system that knows who asked the file and how, to smartly
instanciate an iterator or a regular accessor, maybe with a cache system
for called ranges. I need to dig on ZODB blobs, maybe they already
provide such a feature. If you read this and know the answer, please let
me know... &lt;br /&gt;
&lt;/p&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;There's still a lot of work in the topic, and I think the Plone4Artists
project is doing a great job to enhance things. The three topics to work
on are: &lt;br /&gt;
-   cancelable upload with live feedback;
-   transparent use of blobs to store files;
-   smart downloading capabilities.&lt;/p&gt;
&lt;p&gt;This could make a great sprint I guess, to create some kind of
Plone-FSS-NG, so my Lady can upload her movies in her Plone.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Mon, 10 Sep 2007 08:42:00 +0200</pubDate><guid>http://blog.ziade.org/2007/09/10/how-to-handle-large-files-in-plone/</guid></item><item><title>Plone 3 UI : omg !</title><link>http://blog.ziade.org/2007/09/08/plone-3-ui-omg/</link><description>&lt;p&gt;The last time I *really used* Plone was back in the days I used to
have a website called "zopeur.org", a few years ago. I used to work then
with Plone 1.something, and was quite impressed by its features, but
disliked a bit its interface. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I worked then at Nuxeo a few years with CPS, &lt;a href="http://blogs.nuxeo.com/sections/blogs/tarek_ziade/archive_view?category=AJAX"&gt;trying to enhance its
interface&lt;/a&gt; through JS and other tools, until they quit Zope. Now, a
year after, I'm back to Plone for my new work, and I must say that I am
really impressed by the new Plone user interface. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The guys did a great work enhancing the user experience, through clever
layouts and a smooth integration of &lt;a href="http://plone.org/documentation/manual/introducing-kss"&gt;KSS&lt;/a&gt;, a JS framework. I worked a
little bit on it with Godefroid when it started but nothing compared to
what it became: simple, yet powerful. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I didn't write to much on this blog lately, because it's hard to write
for two blogs (one in french, one in english) but i'll try to do it
regularly for now one, to talk about my Plone experience, since it's now
becoming my every day work. I guess my next posts will focus on the
tools I am discovering, and all those things that have evolved a lot in
the past 4 years, focusing onthe UI of course, but also on Zope 3
integration, since I took a little bit part in its development. &lt;br /&gt;
&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;The manual that was published yesterday is a good starting point for me&lt;/dt&gt;
&lt;dd&gt;&lt;a href="http://plone.org/documentation/manual/plone-3-user-manual"&gt;http://plone.org/documentation/manual/plone-3-user-manual&lt;/a&gt;&lt;/dd&gt;
&lt;/dl&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sat, 08 Sep 2007 08:10:00 +0200</pubDate><guid>http://blog.ziade.org/2007/09/08/plone-3-ui-omg/</guid></item><item><title>Indexation service with Xapian</title><link>http://blog.ziade.org/2007/06/12/indexation-service-with-xapian/</link><description>&lt;p&gt;In a previous &lt;a href="http://programmation-python.org/sections/blog/2007_06_07_indexation-facile-rapide"&gt;post&lt;/a&gt; on my french Blog, I was explaining how to use
&lt;a href="http://xapian.org"&gt;Xapian&lt;/a&gt; from its Python binding, to index text content for documents.
The idea was to use the indexing part of Xapian and not to store any
data in the documents.When I started to hook my code into my Django
application, I realized it was not very robust, because I had to open
only one Xapian connector to index content. This was not easy to do and
depends a lot on how the web framework loads the code. Furthermore, the
website had to take care of launching threads to avoid waiting for the
indexation to be finished. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I rewrote the tool to avoid these issues, by decoupling indexation and
queries: when the website needs an indexation, it writes the data into a
special table in a SQL database. On the other side, a thread in a
separated process (eg a worker) reads this table and launch indexations
when it finds lines in it. This producers-consumer pattern makes the
solution very fast and simple. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It is better to perform this kind of pattern than a solution like a
live server, based for example on XML-RPC, because it is more robust: &lt;br /&gt;
-   if any part of the application crashes, the work in process is not
    lost in the SQL database;
-   if the indexer program crashes, the website can still query the
    Xapian database.&lt;/p&gt;
&lt;p&gt;Queries are made with read-only Xapian connectors, which can be used
concurrently with no problems. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;These were the changes I made: &lt;br /&gt;
-   A sqlite database is used to store indexation queries in a table
    (since it's based on &lt;a href="http://www.sqlalchemy.org/"&gt;SQLALchemy&lt;/a&gt;, postgres or mysql can be used
    as well);
-   a thread scans the table to pick its work and delete rows when its
    done;
-   the search API launches standalone and read-only Xapian connectors
    to perform each search.&lt;/p&gt;
&lt;p&gt;When documents are modified and added on the website, a query is made
on the database. This is done through the Django event framework wich
allows to hook code on events like "A document was created". (explained
here: &lt;a href="http://www.mercurytide.com/whitepapers/django-signals/"&gt;http://www.mercurytide.com/whitepapers/django-signals/&lt;/a&gt;) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The package I wrote can be used this way: &lt;br /&gt;
-   The thread in charge of indexations is launched via the &lt;em&gt;run.py&lt;/em&gt;
    script
-   the &lt;em&gt;searcher&lt;/em&gt; and &lt;em&gt;indexer&lt;/em&gt; modules can be used to work with the
    API&lt;/p&gt;
&lt;p&gt;Here's the complete doctest of my package, which provide examples on
how to use these API: &lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;border:1px solid black;background-color:#efefef;font-size:10pt;padding:4px;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;=======&lt;/span&gt;

&lt;span class="n"&gt;indexer&lt;/span&gt;

&lt;span class="o"&gt;=======&lt;/span&gt;

&lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;indexer&lt;/span&gt; &lt;span class="n"&gt;provides:&lt;/span&gt;

&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;side&lt;/span&gt; &lt;span class="n"&gt;modules&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;API&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;ask&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;indexations&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt;

&lt;span class="n"&gt;Xapian&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;When&lt;/span&gt; &lt;span class="n"&gt;an&lt;/span&gt; &lt;span class="n"&gt;indexation&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;asked&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;stored&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt;

&lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;side&lt;/span&gt; &lt;span class="n"&gt;application:&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;standalone&lt;/span&gt; &lt;span class="n"&gt;thread&lt;/span&gt; &lt;span class="n"&gt;that&lt;/span&gt; &lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="n"&gt;what&lt;/span&gt; &lt;span class="n"&gt;has&lt;/span&gt; &lt;span class="n"&gt;been&lt;/span&gt;

&lt;span class="n"&gt;asked&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt; &lt;span class="n"&gt;reading&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;Let&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;s import the modules used by the client-side::&lt;/span&gt;

&lt;span class="s"&gt;&amp;gt;&amp;gt;&amp;gt; import indexer&lt;/span&gt;

&lt;span class="s"&gt;&amp;gt;&amp;gt;&amp;gt; import searcher&lt;/span&gt;

&lt;span class="s"&gt;Let&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="k"&gt;reset&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;SQL&lt;/span&gt; &lt;span class="n"&gt;DB&lt;/span&gt; &lt;span class="nn"&gt;first::&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;indexer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;Let&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;s also reset the Xapian DB::&lt;/span&gt;

&lt;span class="s"&gt;&amp;gt;&amp;gt;&amp;gt; from xapindexer import force_reset&lt;/span&gt;

&lt;span class="s"&gt;&amp;gt;&amp;gt;&amp;gt; force_reset()&lt;/span&gt;

&lt;span class="s"&gt;The Xapian DB should be empty now::&lt;/span&gt;

&lt;span class="s"&gt;&amp;gt;&amp;gt;&amp;gt; searcher.corpus_size()&lt;/span&gt;

&lt;span class="s"&gt;0&lt;/span&gt;

&lt;span class="s"&gt;Indexation&lt;/span&gt;

&lt;span class="s"&gt;==========&lt;/span&gt;

&lt;span class="s"&gt;Each indexable content has a unique id, and a text to index::&lt;/span&gt;

&lt;span class="s"&gt;&amp;gt;&amp;gt;&amp;gt; uid = &amp;#39;&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;

&lt;span class="s"&gt;&amp;gt;&amp;gt;&amp;gt; text = &amp;#39;&lt;/span&gt;&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="n"&gt;taylor&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;rich&lt;/span&gt; &lt;span class="n"&gt;anymore&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;

&lt;span class="s"&gt;Let&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="nb"&gt;index&lt;/span&gt; &lt;span class="nn"&gt;it::&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;indexer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;index_document&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;Another&lt;/span&gt; &lt;span class="nn"&gt;one::&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;indexer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;index_document&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;2&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;pluto is a dog&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;Let&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;s start the worker that is in charge of asynchronous indexation::&lt;/span&gt;

&lt;span class="s"&gt;&amp;gt;&amp;gt;&amp;gt; from xapindexer import start_server&lt;/span&gt;

&lt;span class="s"&gt;&amp;gt;&amp;gt;&amp;gt; start_server()&lt;/span&gt;

&lt;span class="s"&gt;Let&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="nb"&gt;wait&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;bit&lt;/span&gt; &lt;span class="n"&gt;so&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;worker&lt;/span&gt; &lt;span class="n"&gt;has&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;SQL&lt;/span&gt; &lt;span class="n"&gt;Database&lt;/span&gt;

&lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="nn"&gt;work::&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;indexer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_working&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="sb"&gt;`is_working`&lt;/span&gt; &lt;span class="n"&gt;looks&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;SQL&lt;/span&gt; &lt;span class="n"&gt;DB&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;there&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;some&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;Xapian&lt;/span&gt; &lt;span class="n"&gt;DB&lt;/span&gt; &lt;span class="n"&gt;has&lt;/span&gt; &lt;span class="n"&gt;two&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="nn"&gt;now::&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;searcher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;corpus_size&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="mi"&gt;2&lt;/span&gt;

&lt;span class="n"&gt;Searching&lt;/span&gt;

&lt;span class="o"&gt;=========&lt;/span&gt;

&lt;span class="n"&gt;Let&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;s search now, with `searcher`. Operator is AND by default::&lt;/span&gt;

&lt;span class="s"&gt;&amp;gt;&amp;gt;&amp;gt; res = searcher.search(&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;rich&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;)&lt;/span&gt;

&lt;span class="s"&gt;&amp;gt;&amp;gt;&amp;gt; list(res)&lt;/span&gt;

&lt;span class="s"&gt;[&amp;#39;&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;]&lt;/span&gt;

&lt;span class="s"&gt;&amp;gt;&amp;gt;&amp;gt; res = searcher.search(&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;pluto&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;)&lt;/span&gt;

&lt;span class="s"&gt;&amp;gt;&amp;gt;&amp;gt; list(res)&lt;/span&gt;

&lt;span class="s"&gt;[&amp;#39;&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;]&lt;/span&gt;

&lt;span class="s"&gt;&amp;gt;&amp;gt;&amp;gt; res = searcher.search(&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;dog&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;)&lt;/span&gt;

&lt;span class="s"&gt;&amp;gt;&amp;gt;&amp;gt; list(res)&lt;/span&gt;

&lt;span class="s"&gt;[&amp;#39;&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;]&lt;/span&gt;

&lt;span class="s"&gt;&amp;gt;&amp;gt;&amp;gt; res = searcher.search(&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;rich&lt;/span&gt; &lt;span class="n"&gt;dog&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;)&lt;/span&gt;

&lt;span class="s"&gt;&amp;gt;&amp;gt;&amp;gt; list(res)&lt;/span&gt;

&lt;span class="s"&gt;[]&lt;/span&gt;

&lt;span class="s"&gt;Or operator::&lt;/span&gt;

&lt;span class="s"&gt;&amp;gt;&amp;gt;&amp;gt; res = searcher.search(&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;rich&lt;/span&gt; &lt;span class="n"&gt;dog&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;, or_=True)&lt;/span&gt;

&lt;span class="s"&gt;&amp;gt;&amp;gt;&amp;gt; res = list(res)&lt;/span&gt;

&lt;span class="s"&gt;&amp;gt;&amp;gt;&amp;gt; res.sort()&lt;/span&gt;

&lt;span class="s"&gt;&amp;gt;&amp;gt;&amp;gt; res&lt;/span&gt;

&lt;span class="s"&gt;[&amp;#39;&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;, &amp;#39;&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;]&lt;/span&gt;

&lt;span class="s"&gt;We have an API to detect if a document is present::&lt;/span&gt;

&lt;span class="s"&gt;&amp;gt;&amp;gt;&amp;gt; searcher.document_exists(&amp;#39;&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;)&lt;/span&gt;

&lt;span class="s"&gt;True&lt;/span&gt;

&lt;span class="s"&gt;&amp;gt;&amp;gt;&amp;gt; searcher.document_exists(&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;ttt&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;)&lt;/span&gt;

&lt;span class="s"&gt;False&lt;/span&gt;

&lt;span class="s"&gt;And another one to retrieve indexed terms::&lt;/span&gt;

&lt;span class="s"&gt;&amp;gt;&amp;gt;&amp;gt; list(searcher.document_terms(&amp;#39;&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;))&lt;/span&gt;

&lt;span class="s"&gt;[&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;dog&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;, &amp;#39;&lt;/span&gt;&lt;span class="n"&gt;is&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;, &amp;#39;&lt;/span&gt;&lt;span class="n"&gt;pluto&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;]&lt;/span&gt;

&lt;span class="s"&gt;Reindexation&lt;/span&gt;

&lt;span class="s"&gt;============&lt;/span&gt;

&lt;span class="s"&gt;The document can also be reindexed::&lt;/span&gt;

&lt;span class="s"&gt;&amp;gt;&amp;gt;&amp;gt; indexer.index_document(&amp;#39;&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;, &amp;#39;&lt;/span&gt;&lt;span class="n"&gt;pluto&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;)&lt;/span&gt;

&lt;span class="s"&gt;&amp;gt;&amp;gt;&amp;gt; indexer.work_in_process()&lt;/span&gt;

&lt;span class="s"&gt;([2], [])&lt;/span&gt;

&lt;span class="s"&gt;Let&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="nb"&gt;wait&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="nn"&gt;bit::&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;indexer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_working&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;Let&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;s make sure the document has been reindexed::&lt;/span&gt;

&lt;span class="s"&gt;&amp;gt;&amp;gt;&amp;gt; list(searcher.document_terms(&amp;#39;&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;))&lt;/span&gt;

&lt;span class="s"&gt;[&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;, &amp;#39;&lt;/span&gt;&lt;span class="n"&gt;is&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;, &amp;#39;&lt;/span&gt;&lt;span class="n"&gt;pluto&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;]&lt;/span&gt;

&lt;span class="s"&gt;Then check the indexation has changed::&lt;/span&gt;

&lt;span class="s"&gt;&amp;gt;&amp;gt;&amp;gt; res = searcher.search(&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;rich&lt;/span&gt; &lt;span class="n"&gt;dog&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;, or_=True)&lt;/span&gt;

&lt;span class="s"&gt;&amp;gt;&amp;gt;&amp;gt; list(res)&lt;/span&gt;

&lt;span class="s"&gt;[&amp;#39;&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;]&lt;/span&gt;

&lt;span class="s"&gt;Or deleted::&lt;/span&gt;

&lt;span class="s"&gt;&amp;gt;&amp;gt;&amp;gt; res = searcher.search(&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;pluto&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;)&lt;/span&gt;

&lt;span class="s"&gt;&amp;gt;&amp;gt;&amp;gt; list(res)&lt;/span&gt;

&lt;span class="s"&gt;[&amp;#39;&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;]&lt;/span&gt;

&lt;span class="s"&gt;&amp;gt;&amp;gt;&amp;gt; indexer.delete_document(&amp;#39;&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;)&lt;/span&gt;

&lt;span class="s"&gt;&amp;gt;&amp;gt;&amp;gt; while indexer.is_working():&lt;/span&gt;

&lt;span class="s"&gt;...     time.sleep(0.2)&lt;/span&gt;

&lt;span class="s"&gt;&amp;gt;&amp;gt;&amp;gt; res = searcher.search(&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;pluto&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;[]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The complete code is here :
&lt;a href="http://hg.programmation-python.org/browser/xap"&gt;http://hg.programmation-python.org/browser/xap&lt;/a&gt;&lt;a href="http://hg.programmation-python.org/repositories/public/"&gt;&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It is not packaged yet, but can be used to provide an indexation
service for a website or any other application.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 12 Jun 2007 15:39:00 +0200</pubDate><guid>http://blog.ziade.org/2007/06/12/indexation-service-with-xapian/</guid></item><item><title>RuPy is coming up - 14/15 april</title><link>http://blog.ziade.org/2007/04/06/rupy-is-coming-up-1415-april/</link><description>&lt;p&gt;This conference is a great idea: during two days, Python and Ruby
experts will gather in Poznan, 14/15 april, to talk about their favorite
language ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;On Python side we'll have some speaks about: &lt;br /&gt;
-   TurboGears
-   PyPy
-   Django
-   IronPython
-   many other interesting topics&lt;/p&gt;
&lt;p&gt;On Ruby side, well..just take a look here:
[http://rupy.wmid.amu.edu.pl/?page_id=15][], as I cannot say anything
about Ruby (yet ;)) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I'll have the chance to present a topic about Agile Documentation in
Python, which heavily relies on my previous PyCon tutorial, but will
focus on presenting how we, Python coders, play with doctests and enjoy
it. (doctests, reST, Python, Beer = yepee). I have a few days left to
digg on Ruby side to see how they work with tests and documenting, to
conclude my slides. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;There's something I don't understand though, why using Ruby ? Python is
way cooler ! ... ;) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Anyway, I am looking forward to talk to people from the Ruby community
to get an insight, and to see Poland. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;dzień dobry ! &lt;/strong&gt;&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;RuPy&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 06 Apr 2007 22:46:00 +0200</pubDate><guid>http://blog.ziade.org/2007/04/06/rupy-is-coming-up-1415-april/</guid></item><item><title>Slides from Pycon Tutorial</title><link>http://blog.ziade.org/2007/02/24/slides-from-pycon-tutorial/</link><description>&lt;p&gt;Here are the slides I have made for the PyCon tutorial: &lt;br /&gt;
-   S5:
    &lt;a href=""&gt;http://programmation-python.org/pycommunity/pycon/PyCon07/slides.html&lt;/a&gt;
-   reST:
    &lt;a href=""&gt;http://programmation-python.org/pycommunity/pycon/PyCon07/slides.txt&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Don't hesitate to send me feedback about them&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;programmation&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/pycommunity/&lt;/span&gt;&lt;span class="n"&gt;pycon&lt;/span&gt;&lt;span class="sr"&gt;/PyCon07/s&lt;/span&gt;&lt;span class="n"&gt;lides&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;
&lt;span class="s"&gt;&amp;quot;S5 Slides&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;programmation&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/pycommunity/&lt;/span&gt;&lt;span class="n"&gt;pycon&lt;/span&gt;&lt;span class="sr"&gt;/PyCon07/s&lt;/span&gt;&lt;span class="n"&gt;lides&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;txt&lt;/span&gt;
&lt;span class="s"&gt;&amp;quot;reST slides&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sat, 24 Feb 2007 16:18:00 +0100</pubDate><guid>http://blog.ziade.org/2007/02/24/slides-from-pycon-tutorial/</guid></item><item><title>Technical writing, the seven laws</title><link>http://blog.ziade.org/2007/02/23/technical-writing-the-seven-laws/</link><description>&lt;p&gt;I am here at PyCon, enjoying the talks. I will blog about what I have
seen in a few days. Until then, let me drop here the &lt;strong&gt;seven laws&lt;/strong&gt; on
how to write good technical documents.&lt;/p&gt;
&lt;p&gt;These laws are the rules I have synthetized for the tutorial I gave
yesterday, and that can be followed by anyone who write for softwares.&lt;/p&gt;
&lt;p&gt;They are based on advices taken from: &lt;br /&gt;
-   Writing With Power (Peter Elbow)
-   Agile Documentation (Andreas Rüping)
-   My own experience on the topic.&lt;/p&gt;
&lt;p&gt;The seven &lt;em&gt;laws&lt;/em&gt; for technical writing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#the-two-step-writing-process" title="id1"&gt;The Two-step writing process&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#simple-style" title="id2"&gt;Simple style&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#targeted-readership" title="id3"&gt;Targeted readership&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#focused-information" title="id4"&gt;Focused information&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#realistic-examples" title="id5"&gt;Realistic examples&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#light-but-sufficient-approach"&gt;"Light but sufficient" approach&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#structured-documents" title="id7"&gt;Structured documents&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;a href="#id1" title="the-two-step-writing-process"&gt;The Two-step writing process&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Writing should be done in two stages (Elbow, 1980): &lt;br /&gt;
-   a free writing where ideas are written on the paper no matter the
    shape.
-   a review stage where things are structured and reviewed.&lt;/p&gt;
&lt;p&gt;Each stage should take 50% of the time. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;-&gt; Split you writing time in two phases, no one can write it right the
first time (except Mozart) &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;&lt;a href="#id2" title="simple-style"&gt;Simple style&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Use short sentences and simple writing style.&lt;/li&gt;
&lt;li&gt;Use simple vocabulary&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;a href="#id3" title="targeted-readership"&gt;Targeted readership&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Focus on your target when you write a document (Rüping, 2003). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Assume their background knowledge to restrict the scope of the
documentation. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;-&gt; A &lt;em&gt;prerequest&lt;/em&gt; section can help a lot. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;&lt;a href="#id4" title="focused-information"&gt;Focused information&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A document is about a clear focus. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;A precise title for each section and the document itself, and a summary
can help. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;-&gt; If you cannot easily name the document or one of its section,
there's a problem &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;&lt;a href="#id5" title="realistic-examples"&gt;Realistic examples&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Drop the &lt;em&gt;foo&lt;/em&gt; and &lt;em&gt;bar&lt;/em&gt; habits. Examples must be real-life examples,
and usable as-is. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Neh: &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;import graph&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;calculateSquare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;bar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;renderSquare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Better: &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;import graph&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;square&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;calculateSquare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;square_view&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;renderSquare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;square&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;&lt;a href="#id6" title="light-but-sufficient-approach"&gt;"Light but sufficient" approach&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A working software is more important than the best documentation in the
world (Ambler, 2002). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Quality over Quantity&lt;/em&gt; is the best rule. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;-&gt; Spending too much time to find something in the documentation is a
bad sign. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;-&gt; Think documents like code. Always limit the size of sections,
examples, etc. Modularized documentation is the key. &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;&lt;a href="#id7" title="structured-documents"&gt;Structured documents&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Use a clear document portfolio to facilitate the reading. Document
should use: &lt;br /&gt;
-   an abstract with a readers guideline
-   a toc when there are more than one section
-   references
-   glossary
-   tables and diagrams&lt;/p&gt;
&lt;p&gt;-&gt; Never write a document that doesn't have a template &lt;br /&gt;
&lt;/p&gt;
&lt;h3&gt;&lt;/h3&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;id6&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 23 Feb 2007 20:31:00 +0100</pubDate><guid>http://blog.ziade.org/2007/02/23/technical-writing-the-seven-laws/</guid></item><item><title>PyCommunity 0.1 almost ready</title><link>http://blog.ziade.org/2007/02/11/pycommunity-01-almost-ready/</link><description>&lt;p&gt;I've been working a lot on PyCommunity packaging lately, so it can be
ready for my PyCon tutorial. Version 0.1 will be released by then and
will autogenerate html pages for a Python project that bases its
documentation on reST files. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;There are a few mandatory files that have to be present in the project
for the tool to work, but it is not really restrictive. I tried a few
runs on Zope codebase and it works fine, besides a few problems with
epydoc (some Zope files are breaking epydoc, I need to dig on this) and
a few failures on some malformed reST files. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;PyCommunity is based on &lt;a href="http://www.cheetahtemplate.org/" title="chetah"&gt;Cheetah templates&lt;/a&gt;, that can be customized
to smoothly integrate generated pages into an existing website. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Features to complete for 0.1: &lt;br /&gt;
-   &lt;a href="http://sourceforge.net/projects/cheesecake/" title="Cheesecake"&gt;Cheesecake&lt;/a&gt; index report for each package
-   test coverage report for each package, using &lt;a href="http://cheeseshop.python.org/pypi/trace2html/"&gt;trace2html&lt;/a&gt;
-   &lt;a href="http://crunchy.sourceforge.net/" title="Crunchy"&gt;Crunchy frog&lt;/a&gt; integration
-   &lt;a href="http://pygments.pocoo.org/" title="Pygments"&gt;Pygments&lt;/a&gt; integration
-   SVN hook dameon with a queue, so the generation can be done smoothly
-   Folder and file skipping to avoid the process of unwanted content&lt;/p&gt;
&lt;p&gt;The source code is in a Mercurial repository, right here:
&lt;a href="http://hg.programmation-python.org/repositories/public"&gt;http://hg.programmation-python.org/repositories/public&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you &lt;a href="http://programmation-python.org/pycommunity/"&gt;follow this link&lt;/a&gt;, you will see a generated site based on a
fake project used in PyCommunity unit tests.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;Trace2html&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sun, 11 Feb 2007 22:53:00 +0100</pubDate><guid>http://blog.ziade.org/2007/02/11/pycommunity-01-almost-ready/</guid></item><item><title>a Python Developer Room at the FOSDEM</title><link>http://blog.ziade.org/2007/02/09/a-python-developer-room-at-the-fosdem/</link><description>&lt;p&gt;As a young PUG, we've missed the deadline last year. We're in this year
! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The &lt;a href="http://afpy.org/" title="AFPY"&gt;AFPY&lt;/a&gt; (Association Francophone Python) organizes this year, at
&lt;a href="http://www.fosdem.org/2007/" title="FOSDEM"&gt;the annual FOSDEM conference&lt;/a&gt; (24/25 February, Brussels, Belgium) a
Python Developer Room. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The room schedule can be followed here:
&lt;a href="http://www.fosdem.org/2007/schedule/devroom/python"&gt;http://www.fosdem.org/2007/schedule/devroom/python&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;A lot of interesting Python-related conferences (and Zope as well) will
take place, and a few sprints will probably be organized as well. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So if you are in Europe, not attending to &lt;a href="http://us.pycon.org/TX2007/HomePage" title="PyCon"&gt;PyCon&lt;/a&gt;, the FOSDEM is the
place to go to !&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;Python Developer Room&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 09 Feb 2007 18:43:00 +0100</pubDate><guid>http://blog.ziade.org/2007/02/09/a-python-developer-room-at-the-fosdem/</guid></item><item><title>Pycon: detailed outline for documenting projects</title><link>http://blog.ziade.org/2007/01/18/pycon-detailed-outline-for-documenting-projects/</link><description>&lt;p&gt;There's only a few days remaining before tutorial cancellation at Pycon,
if you have interest in some of them, it's time to register ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I have completed my outline for the tutorial I'll be giving. If you
consider to attend, you should bring your laptop and have this elements
installed on it, to maximize your pleasure ;) : &lt;br /&gt;
-   Python 2.4 or 2.5
-   &lt;a href="http://docutils.sourceforge.net"&gt;docutils&lt;/a&gt;
-   &lt;a href="http://peak.telecommunity.com/DevCenter/setuptools"&gt;setuptools&lt;/a&gt;
-   &lt;a href="www.logilab.org/857"&gt;pylint&lt;/a&gt;
-   &lt;a href="http://pycheesecake.org/"&gt;cheesecake&lt;/a&gt;
-   subversion, client and server (optional)&lt;/p&gt;
&lt;p&gt;(PyCommunity will be provided at tutorial day). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Updated outline: &lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;writing&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;document&lt;/span&gt;

  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;mastering&lt;/span&gt; &lt;span class="n"&gt;reStructuredText&lt;/span&gt;

    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;presentation&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;reStructuredText&lt;/span&gt;

    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;distribution&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;cheatsheet&lt;/span&gt;

    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;exercises&lt;/span&gt;

  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;writing&lt;/span&gt; &lt;span class="n"&gt;techniques&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;tips&lt;/span&gt;

    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;presentation&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;ten&lt;/span&gt; &lt;span class="n"&gt;laws&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;examples&lt;/span&gt;

    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;exercise&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;binomial&lt;/span&gt;

  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;team&lt;/span&gt; &lt;span class="n"&gt;writing&lt;/span&gt;

    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;presentation:&lt;/span&gt; &lt;span class="n"&gt;bad&lt;/span&gt; &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;good&lt;/span&gt; &lt;span class="n"&gt;designer&lt;/span&gt; &lt;span class="n"&gt;VS&lt;/span&gt; &lt;span class="n"&gt;good&lt;/span&gt; &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bad&lt;/span&gt; &lt;span class="n"&gt;designer&lt;/span&gt;

    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;exercise:&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="n"&gt;review&lt;/span&gt;

&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;writing&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;Python&lt;/span&gt;

  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;documenting&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt;

    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;presentation&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;which&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;how&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nb"&gt;split&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="n"&gt;into&lt;/span&gt; &lt;span class="n"&gt;docs&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt;

    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;exercise&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;an&lt;/span&gt; &lt;span class="n"&gt;existing&lt;/span&gt; &lt;span class="nb"&gt;package&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;documenting&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="nb"&gt;package&lt;/span&gt;

    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;presentation:&lt;/span&gt; &lt;span class="n"&gt;README&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;txt&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;friends&lt;/span&gt;

    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;exercise:&lt;/span&gt; &lt;span class="n"&gt;writing&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;minimum&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;testing&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;cheesecake&lt;/span&gt;

&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;using&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;driven&lt;/span&gt; &lt;span class="n"&gt;developement&lt;/span&gt;

  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;mastering&lt;/span&gt; &lt;span class="n"&gt;doctests&lt;/span&gt;

    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;presentation:&lt;/span&gt; &lt;span class="n"&gt;unittests&lt;/span&gt; &lt;span class="n"&gt;ain&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;t doctests&lt;/span&gt;

&lt;span class="s"&gt;    - exercise: spliting tests between doctests and unittests&lt;/span&gt;

&lt;span class="s"&gt;  - continuous documentation&lt;/span&gt;

&lt;span class="s"&gt;    - presentation: designing through doctests&lt;/span&gt;

&lt;span class="s"&gt;    - exercise: creating a module with its doctest&lt;/span&gt;

&lt;span class="s"&gt;BREAK&lt;/span&gt;

&lt;span class="s"&gt;- writing documents in a Python Project&lt;/span&gt;

&lt;span class="s"&gt;  - writing tutorials&lt;/span&gt;

&lt;span class="s"&gt;    - presentation: what&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;tutorial&lt;/span&gt;

    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;presentation:&lt;/span&gt; &lt;span class="n"&gt;how&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nb"&gt;write&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;tutorial&lt;/span&gt;

    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;exercice:&lt;/span&gt; &lt;span class="nb"&gt;write&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;mini&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;tutorial&lt;/span&gt;

  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;writing&lt;/span&gt; &lt;span class="n"&gt;cookbook&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;recipes&lt;/span&gt;

    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;presentation:&lt;/span&gt; &lt;span class="n"&gt;what&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;cookbook&lt;/span&gt;

    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;presentation:&lt;/span&gt; &lt;span class="n"&gt;how&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nb"&gt;write&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;recipe&lt;/span&gt;

    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;exercice:&lt;/span&gt; &lt;span class="nb"&gt;write&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;recipe&lt;/span&gt;

  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;writing&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt; &lt;span class="n"&gt;glossary&lt;/span&gt;

    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;exercise:&lt;/span&gt; &lt;span class="nb"&gt;write&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;glossary&lt;/span&gt;

  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;crosslinking&lt;/span&gt; &lt;span class="n"&gt;everything&lt;/span&gt;

    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;exercice:&lt;/span&gt; &lt;span class="nb"&gt;link&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;recipe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tutorial&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;glossary&lt;/span&gt;

&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;website&lt;/span&gt; &lt;span class="n"&gt;autogeneration&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;pycommunity&lt;/span&gt;

  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;presentation:&lt;/span&gt; &lt;span class="n"&gt;pycommunity&lt;/span&gt;

  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;exercise:&lt;/span&gt; &lt;span class="n"&gt;setting&lt;/span&gt; &lt;span class="n"&gt;up&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;subversion&lt;/span&gt; &lt;span class="n"&gt;structure&lt;/span&gt;

  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;exercise:&lt;/span&gt; &lt;span class="n"&gt;setting&lt;/span&gt; &lt;span class="n"&gt;up&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;hook&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;website&lt;/span&gt; &lt;span class="n"&gt;autogeneration&lt;/span&gt;

  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;customize&lt;/span&gt; &lt;span class="n"&gt;pycommunity&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;an&lt;/span&gt; &lt;span class="n"&gt;existing&lt;/span&gt; &lt;span class="n"&gt;website&lt;/span&gt;

&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;conclusions&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 18 Jan 2007 10:13:00 +0100</pubDate><guid>http://blog.ziade.org/2007/01/18/pycon-detailed-outline-for-documenting-projects/</guid></item><item><title>Pycon wiki updates</title><link>http://blog.ziade.org/2007/01/11/pycon-wiki-updates/</link><description>&lt;p&gt;I have updated a bit the PyCon Wiki tonite : &lt;br /&gt;
-   &lt;a href="http://us.pycon.org/TX2007/TutorialsAM6outline"&gt;A more detailed class outline about my tutorial on documenting&lt;/a&gt;
-   &lt;a href="http://us.pycon.org/TX2007/PyJCR"&gt;A BoF about creating a content repository standard in Python&lt;/a&gt;&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;DDD&amp;quot;&lt;/span&gt;
&lt;span class="s"&gt;&amp;quot;PyJCR&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 11 Jan 2007 21:45:00 +0100</pubDate><guid>http://blog.ziade.org/2007/01/11/pycon-wiki-updates/</guid></item><item><title>ZODB vs SGBD ? Give me a standard, it&amp;#039;s about time !</title><link>http://blog.ziade.org/2007/01/09/zodb-vs-sgbd-give-me-a-standard-it039s-about-time/</link><description>&lt;p&gt;I've been reading &lt;a href="http://blog.delaguardia.com.mx/index.php?op=ViewArticle&amp;amp;articleId=72&amp;amp;blogId=1"&gt;Carlos' post about Zope 3&lt;/a&gt;, and his thaugths on the
role of the framework. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I want to react on this and give my opinion on Zope future and on
Python Web developement as well, because I feel that people are not
debating on the right thing. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Since a few months, a lot of people in Zope community are seeing Zope 3
as packages that can be used in Zope 2 or elsewhere. This is probably
true. A lot of people are also using CherryPy, Django, TurboGears, ..,
and dropping Zope for some reasons. These reasons are probably right.
(my reason to drop Zope would be the ZODB). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But beside this reasons, (every framework has pro's and con's) there's
a missing brick in the Python Web Developement ecosystem that exists in
Java world: a standard for document repositories. It's called &lt;a href="http://jcp.org/en/jsr/detail?id=170" title="JSR-170"&gt;JCR or
JSR-170&lt;/a&gt;. Some people in Zope community &lt;a href="http://blogs.nuxeo.com/sections/blogs/fermigier/2005_06_25_jsr_170_java_content"&gt;have talked about it&lt;/a&gt; in
&lt;a href="http://faassen.n--tree.net/blog/view/weblog/2005/07/20/0"&gt;the&lt;/a&gt; &lt;a href="http://palladion.com/home/tseaver/obzervationz/jsr170_doodling_20050711"&gt;past&lt;/a&gt;. Nuxeo did a bind as well with &lt;a href="http://svn.nuxeo.org/trac/pub/browser/nuxeo.jcr/trunk/src/nuxeo/jcr/"&gt;nuxeo.jcr&lt;/a&gt;, then
dropped Zope/Python for Java (which is totally logical given where CPS
software is heading). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I strongly believe that the future and the maturity of all Python web
frameworks relies at the first place on a common document repository
standard, like DB API did with SQL. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Python web developers, it's time for all of us to cooperate and create
such a standard in Python, and an implementation based on SQLAlchemy.
This would let Zope drop the ZODB (yepee) and let every web developer
use the best of all frameworks (the Zope Component Architecture, the
Django approach, etc.) since they would all be able to work with a given
repository. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I would like to see the PSF and all framework core developers support
such a project. &lt;br /&gt;
edit: oups.. I just realized I have used a french acronym in the title
(SGBD). The english one is RDBMS&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;Carlos&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Tue, 09 Jan 2007 14:16:00 +0100</pubDate><guid>http://blog.ziade.org/2007/01/09/zodb-vs-sgbd-give-me-a-standard-it039s-about-time/</guid></item><item><title>Document-Driven Development (DDD) in Python</title><link>http://blog.ziade.org/2007/01/06/document-driven-development-ddd-in-python/</link><description>&lt;p&gt;Test-Driven Development is widely used in the Python community to create
quality software. The benefits of such approach is not to be proved
anymore. It creates better software by : &lt;br /&gt;
-   preventing &lt;em&gt;regression&lt;/em&gt;
-   making the developers actually &lt;em&gt;think&lt;/em&gt; about what they write
-   providing a real help for newcomers : unit and functional tests are
    showing how the code was made and how it has to be used.
-   ...&lt;/p&gt;
&lt;p&gt;Python has brought a real enhancement to test-driven developement with
doctests. They allow developer to write documentation with embed code
examples that can be run for real. This allows a team to integrate
documentation in the development cycle and to replace most of their
tests with documents that are always up to date: documents become tests.&lt;/p&gt;
&lt;p&gt;In the meantime, documentation has always been the black beast of
developers. They hate writing it and most of the time, a software
project website is never up to date if there's no one dedicated to this
task. For open source projects, the website is the most important media
and should always reflect to what is happening in the code base. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;doctests can resolve this problem by automating documentation
creation at all level &lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I have used this principle to create a &lt;em&gt;Document-Driven Developpement&lt;/em&gt;
(DDD) approach, which provides to a project team a way to automate the
update the project website : each commit on the code base calls a script
that generates on the fly html pages for: &lt;br /&gt;
-   the api
-   the glossary for the project
-   documents for all important modules
-   tutorials
-   recipes
-   ...&lt;/p&gt;
&lt;p&gt;This method will be fully explained at my &lt;a href="http://us.pycon.org/TX2007/TutorialsAM#AM6"&gt;PyCon Tutorial (February,
22th)&lt;/a&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I will published by then the script that automates the creation of the
website, using a subversion repository that follows a few guidelines (in
its content and structure). &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Can't wait to be there !&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;pycon&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sat, 06 Jan 2007 23:04:00 +0100</pubDate><guid>http://blog.ziade.org/2007/01/06/document-driven-development-ddd-in-python/</guid></item><item><title>Pycon 2007 tutorial about documenting Python projects</title><link>http://blog.ziade.org/2006/12/02/pycon-2007-tutorial-about-documenting-python-projects/</link><description>&lt;p&gt;&lt;img alt="Babel Tower" src="http://upload.wikimedia.org/wikipedia/commons/1/1b/Image-Harsdörffer-Peristromata_Turcica-detail.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;My tutorial proposal on &lt;a href="http://us.pycon.org/TX2007/TutorialsAM#AM6"&gt;how to document a Python project&lt;/a&gt;, has been
accepted. I have quite a good idea on the things I need to present for
people to understand how to integrate documentation writing in their
development cycle.&lt;a href="http://us.pycon.org/TX2007/TutorialsAM6outline"&gt;See the class outline for a summary&lt;/a&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The main idea behind this tutorial is to explain how documenting can be
done through coding and testing, in order to create lightweight but
sufficient documentation instead of creating documents at the end of the
project. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The technique is based on: &lt;br /&gt;
-   &lt;a href="http://en.wikipedia.org/wiki/Literate_programming"&gt;literate programming&lt;/a&gt;, introduced by the father of Tex, [Donald
    Knuth][]
-   Agile documentation principles, which uses agile techniques for
    document writing . These have been [summarized in a book by Andreas
    Rüping][].&lt;/p&gt;
&lt;p&gt;and provide simple processes to avoid the &lt;em&gt;"programmers hate writing
document"&lt;/em&gt; effect. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For open source projects, the final step of agile documentation is: &lt;br /&gt;
-   to &lt;strong&gt;automate the update of the dedicated website&lt;/strong&gt; everytime
    developers change the base code.
-   to provide a &lt;strong&gt;visible document landscape&lt;/strong&gt; to anyone interested to
    the project
-   to &lt;strong&gt;increase the quality of the code&lt;/strong&gt; base
-   to make &lt;strong&gt;developers become good writers&lt;/strong&gt;, and make them love it&lt;/p&gt;
&lt;p&gt;This tutorial is based on my work on &lt;a href="http://zope-cookbook.org/"&gt;zope-cookbook.org&lt;/a&gt;, which tried
to follow a fast-moving technology : Zope, and some work on several
Python open source projects, books and articles I have written. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Nevertheless, the conference is next February, and I would like to
integrate more feedback on how people feel about documenting their
application. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;If you are a developer, using Python or any language out there, or a
project leader, I would love to get a few words about your experience on
the topic, in order to enhance this tutorial. &lt;br /&gt;
-   What is the place of documentation in your project cycles ?
-   What are the different document types you use ?
-   What kind of formats do you use for you document ? (Word,
    OpenOffice, Latex, reST..)
-   Do you have dedicated people that write documentation ?
-   How do your developers feel about documentation ?
-   ...&lt;/p&gt;
&lt;p&gt;Please don't hesitate to comment this entry, or to senf me an email
(ziade dot tarek at gmail dot com)&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;Pycon Tutoria&amp;quot;&lt;/span&gt;
&lt;span class="s"&gt;&amp;quot;Outiline&amp;quot;&lt;/span&gt;
&lt;span class="s"&gt;&amp;quot;Literate programming&amp;quot;&lt;/span&gt;
&lt;span class="s"&gt;&amp;quot;Donald Knuth&amp;quot;&lt;/span&gt;
&lt;span class="s"&gt;&amp;quot;Agile documentation&amp;quot;&lt;/span&gt;
&lt;span class="s"&gt;&amp;quot;http://zope-cookbook.org/&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sat, 02 Dec 2006 23:06:00 +0100</pubDate><guid>http://blog.ziade.org/2006/12/02/pycon-2007-tutorial-about-documenting-python-projects/</guid></item><item><title>The dark side of the force</title><link>http://blog.ziade.org/2006/11/29/the-dark-side-of-the-force/</link><description>&lt;p&gt;Remember &lt;a href="http://www.python.org/doc/Humor.html#yoda"&gt;the funny post about Python vs Perl on Python mailing list&lt;/a&gt;
? &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I think it fits pretty well to TDD. Here's a slighty modified version:&lt;/p&gt;
&lt;p&gt;EXTERIOR: DAGOBAH -- DAY &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;With Yoda strapped to his back, Luke climbs up one of the &lt;br /&gt;
many thick vines that grow in the swamp until he reaches the &lt;br /&gt;
Dagobah statistics lab. Panting heavily, he continues his &lt;br /&gt;
exercises -- writing test-driven code in Python, making &lt;br /&gt;
fakes, mock objects. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;YODA: Code! Test! Yes. A programmer's strength flows from
non-regression. &lt;br /&gt;
Never stop doing tests. The dark side of code maintainability &lt;br /&gt;
reach you would without tests. Easy to write without them when &lt;br /&gt;
package you create. If once you start down the dark path, &lt;br /&gt;
forever will it dominate your destiny, consume you it will. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;LUKE: Isn't it simpler not to do tests ? &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;YODA: No... no... no. Quicker, easier, more seductive. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;LUKE: So why I should do tests then ? &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;YODA: You will know. When your code you try to correct six months from&lt;/p&gt;
&lt;h2&gt;now.&lt;/h2&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;Yoda about Perl/Python&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Wed, 29 Nov 2006 01:29:00 +0100</pubDate><guid>http://blog.ziade.org/2006/11/29/the-dark-side-of-the-force/</guid></item><item><title>How to prevent drowning in the huge rss daily feed you receive</title><link>http://blog.ziade.org/2006/11/06/how-to-prevent-drowning-in-the-huge-rss-daily-feed-you-receive/</link><description>&lt;p&gt;Feeds, feeds, feeds everywhere. I can't keep up with all incoming data.
&lt;strong&gt;I am drowned&lt;/strong&gt; ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I thought digg and similar services would help me on this, but it just
raised the problem on a meta-level. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It takes me at least 20 minutes per day to: &lt;br /&gt;
-   Remove duplicates entries
-   Look over hundreds of entries to select the one that worth a
    reading, by looking at the title, the origin and the tags. (80% of
    the time)
-   Read my selection, and even though I know this will make it worse,
    add news feeds I've found from my selection.&lt;/p&gt;
&lt;p&gt;I had to automate some of this daily activity. &lt;strong&gt;I had to cut off this
huge amount of data&lt;/strong&gt; and to get closer to what I needed to read. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But how ? &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;First of all, let's analyze a bit what a feed reader wants. It can be
resumed in two things: &lt;br /&gt;
-   She wants to get the best news on her field of interest, out of a
    huge list of rss feeds.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;She wants to keep an eye on what's going on out there, and make her
    field evolve with some indexes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Most of the work can be done by pieces of software. I think this is
called webmining but I am not an expert. Webmining would be a part of
&lt;a href="http://en.wikipedia.org/wiki/Datamining" title="Damamining"&gt;Datamining&lt;/a&gt;. But let's cut off the big words: &lt;strong&gt;a couple of Python
script can do the job&lt;/strong&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;That's &lt;strong&gt;"Atomisator"&lt;/strong&gt; job ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img alt="Atomisator" src="http://programmation-python.org/metafeed/atomisator.jpg" /&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Atomisator&lt;/em&gt; grabs multiple feeds out there, removes duplicate news by
using the distance of &lt;a href="http://en.wikipedia.org/wiki/Levenstein"&gt;Levenshtein distance&lt;/a&gt;, and create a new feed
out of it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;It also provides a way to filter up entries on the fly, by looking at
each entry content. Simple filters for instance, will validate an entry
if it contains certain words. The filtering system is pluggable, and
works as a transformation chain, so new filter can be written to
fine-tune the entries. This makes it possible to create several custom
feeds form the same pile of data. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Another field of investigation was to use a bayesian network to filter
entries but it doesn't work well : pertinency moves too fast on this
kind of news, and an inference mechanism would work on static news
topics (Historic events maybe ?) &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Last but not least, a special filter is used to collect statistics, and
compute &lt;strong&gt;a buzz-o-meter report&lt;/strong&gt;. This report indicates the top 50 most
used words over all sources, and is filtered with an english common
words dictionnary. It doesn't use the tags like most tag clouds, because
people often use the same tags over and over, without really thinking
about it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A scan of the post content is way better: you get the REAL tags&lt;/strong&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;That's how I keep an eye on what's the most talked about, even though
it's not on my custom feed: I can adapt it afterwards. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Ok now here's the feeds I use, updated every 30mn : &lt;br /&gt;
-   &lt;a href="http://programmation-python.org/metafeed/getMetaFeed"&gt;"Meta Feed" Not filtered, but not redundant&lt;/a&gt;
-   &lt;a href="http://programmation-python.org/metafeed/getCustomFeed"&gt;Python/Zope oriented (lots of filtering)&lt;/a&gt;
-   &lt;a href="http://programmation-python.org/metafeed/buzz"&gt;Live buzz-o-meter&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="http://programmation-python.org/metafeed/buzz"&gt;Get it all here in this page&lt;/a&gt;&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="http://programmation-python.org/metafeed/buzz"&gt;&lt;/a&gt;&lt;/strong&gt; &lt;br /&gt;
This is pretty handy for my daily job. The buzz-o-meter is for fun, but
I see from time to time new words that pops in the list, I can
investigate on. It also shows that Ajax and Ruby lead the buzz-o-sphere.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://svn.gna.org/viewcvs/atomisator/"&gt;The source code is GPL and available here&lt;/a&gt;, but still poorly
documented and not packaged, should be better soon. The version running
the feeds is a bit different from the trunk and some merging will be
done soon. This was a project started some months ago I just wrapped and
re-lauched yesterday. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;N.B.: I had a few comments on how poorly written was this blog entry.
If you find some mistakes or badly turned sentences, don't hesitate:
tell me ! (i am french ;) )&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;Levenstein&amp;quot;&lt;/span&gt;
&lt;span class="s"&gt;&amp;quot;Metafeed&amp;quot;&lt;/span&gt;
&lt;span class="s"&gt;&amp;quot;Python feed&amp;quot;&lt;/span&gt;
&lt;span class="s"&gt;&amp;quot;buzz&amp;quot;&lt;/span&gt;
&lt;span class="s"&gt;&amp;quot;Atomisator&amp;quot;&lt;/span&gt;
&lt;span class="s"&gt;&amp;quot;Atomisator code&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Mon, 06 Nov 2006 19:32:00 +0100</pubDate><guid>http://blog.ziade.org/2006/11/06/how-to-prevent-drowning-in-the-huge-rss-daily-feed-you-receive/</guid></item><item><title>Protecting a Python svn code base with the pre-commit hook</title><link>http://blog.ziade.org/2006/11/01/protecting-a-python-svn-code-base-with-the-pre-commit-hook/</link><description>&lt;p&gt;In a community project, opened to various contributors, there are a few
thing to take care of in order not to break the code. I am not talking
about code reviewing but about bad code editing that breaks it all. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Most frequent errors are: &lt;br /&gt;
-   &lt;strong&gt;&amp;lt;tab&gt; insertions&lt;/strong&gt;, that get mixed with space-based indentation
-   &lt;strong&gt;carriage return insertion&lt;/strong&gt;, before line feeds with some weird
    windows editors&lt;/p&gt;
&lt;p&gt;Instead of tracking the developers that commited such things, and send
them an army of white rabbits, an automatic code check is way better. It
is not a good idea though, to clean up incoming code. The best thing to
do is to block unwanted changes and warn the commiters, so they learn
about it. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;This is really easy with Subversion. I have grabbed a script on the
web, and adapted it a bit for this task. Here it is: &lt;br /&gt;
   #!/usr/bin/python2.4&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="c1"&gt;# -*- coding: UTF-8 -*-&lt;/span&gt;

&lt;span class="c1"&gt;# adapted from:&lt;/span&gt;

&lt;span class="c1"&gt;#   http://blog.wordaligned.org/articles/2006/08/09/a-subversion-pre-commit-hook&lt;/span&gt;

&lt;span class="c1"&gt;# by Tarek Ziadé&lt;/span&gt;

&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Popen&lt;/span&gt;

&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PIPE&lt;/span&gt;

&lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;

&lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="n"&gt;re_options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IGNORECASE&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MULTILINE&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DOTALL&lt;/span&gt;

&lt;span class="n"&gt;class&lt;/span&gt; &lt;span class="n"&gt;EOF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;findall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;endswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;\\n&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;\n&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;tab_catcher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;^\\t&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;re_options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;windows_catcher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;\\r\\n$&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;re_options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;testers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;found TAB&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tab_catcher&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;

           &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;found CR/LF&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;windows_catcher&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;

           &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;no new line at the end&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EOF&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;command_output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot; Capture a command&amp;#39;s standard output.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PIPE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;communicate&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;files_changed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;look_cmd&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot; List the files added or updated by this transaction.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;

    &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;added_or_updated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;A&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;U&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt;

            &lt;span class="n"&gt;command_output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;look_cmd&lt;/span&gt; &lt;span class="nv"&gt;%&lt;/span&gt; &lt;span class="err"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;changed&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;).split(&amp;quot;&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;)&lt;/span&gt;

&lt;span class="s"&gt;            if added_or_updated(line)]&lt;/span&gt;

&lt;span class="s"&gt;def file_contents(filename, look_cmd):&lt;/span&gt;

&lt;span class="s"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="n"&gt;Return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;contents&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;this&lt;/span&gt; &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

&lt;span class="s"&gt;    return command_output(&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;%s&lt;/span&gt; &lt;span class="nv"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot; % (look_cmd % &amp;quot;&lt;/span&gt;&lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;, filename))&lt;/span&gt;

&lt;span class="s"&gt;def test_expression(expr, filename, look_cmd):&lt;/span&gt;

&lt;span class="s"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="n"&gt;regexpr&lt;/span&gt; &lt;span class="n"&gt;over&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

&lt;span class="s"&gt;    return len(expr.findall(file_contents(filename, look_cmd))) &amp;gt; 0&lt;/span&gt;

&lt;span class="s"&gt;def check_file(look_cmd):&lt;/span&gt;

&lt;span class="s"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="n"&gt;checks&lt;/span&gt; &lt;span class="n"&gt;Python&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;this&lt;/span&gt; &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

&lt;span class="s"&gt;    def is_python_file(fname):&lt;/span&gt;

&lt;span class="s"&gt;        return os.path.splitext(fname)[1] in &amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;.split()&lt;/span&gt;

&lt;span class="s"&gt;    erroneous_files = []&lt;/span&gt;

&lt;span class="s"&gt;    for file in files_changed(look_cmd):&lt;/span&gt;

&lt;span class="s"&gt;        if not is_python_file(file):&lt;/span&gt;

&lt;span class="s"&gt;            continue&lt;/span&gt;

&lt;span class="s"&gt;        for error_type, tester in testers:&lt;/span&gt;

&lt;span class="s"&gt;            if test_expression(tester, file, look_cmd):&lt;/span&gt;

&lt;span class="s"&gt;                erroneous_files.append((error_type, file))&lt;/span&gt;

&lt;span class="s"&gt;    num_failures = len(erroneous_files)&lt;/span&gt;

&lt;span class="s"&gt;    if num_failures &amp;gt; 0:&lt;/span&gt;

&lt;span class="s"&gt;        sys.stderr.write(&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ERROR&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;please&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="n"&gt;files:n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;)&lt;/span&gt;

&lt;span class="s"&gt;        for error_type, file in erroneous_files:&lt;/span&gt;

&lt;span class="s"&gt;            sys.stderr.write(&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ERROR&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="nv"&gt;%s&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;%sn&lt;/span&gt;&lt;span class="s"&gt;&amp;quot; % (error_type, file))&lt;/span&gt;

&lt;span class="s"&gt;    return num_failures&lt;/span&gt;

&lt;span class="s"&gt;def main():&lt;/span&gt;

&lt;span class="s"&gt;    from optparse import OptionParser&lt;/span&gt;

&lt;span class="s"&gt;    parser = OptionParser()&lt;/span&gt;

&lt;span class="s"&gt;    parser.add_option(&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;, &amp;quot;&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;revision&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;,&lt;/span&gt;

&lt;span class="s"&gt;                        help=&amp;quot;&lt;/span&gt;&lt;span class="n"&gt;Test&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;TXN&lt;/span&gt; &lt;span class="n"&gt;actually&lt;/span&gt; &lt;span class="n"&gt;refers&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;revision&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;,&lt;/span&gt;

&lt;span class="s"&gt;                        action=&amp;quot;&lt;/span&gt;&lt;span class="n"&gt;store_true&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;, default=False)&lt;/span&gt;

&lt;span class="s"&gt;    errors = 0&lt;/span&gt;

&lt;span class="s"&gt;    (opts, (repos, txn_or_rvn)) = parser.parse_args()&lt;/span&gt;

&lt;span class="s"&gt;    look_opt = (&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;, &amp;quot;&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;revision&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;)[opts.revision]&lt;/span&gt;

&lt;span class="s"&gt;    look_cmd = &amp;quot;&lt;/span&gt;&lt;span class="n"&gt;svnlook&lt;/span&gt; &lt;span class="nv"&gt;%s&lt;/span&gt; &lt;span class="nv"&gt;%s&lt;/span&gt; &lt;span class="nv"&gt;%s&lt;/span&gt; &lt;span class="nv"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot; % (&lt;/span&gt;

&lt;span class="s"&gt;        &amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;, repos, look_opt, txn_or_rvn)&lt;/span&gt;

&lt;span class="s"&gt;    errors += check_file(look_cmd)&lt;/span&gt;

&lt;span class="s"&gt;    return errors&lt;/span&gt;

&lt;span class="s"&gt;if __name__ == &amp;quot;&lt;/span&gt;&lt;span class="n"&gt;__main__&lt;/span&gt;&lt;span class="err"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

    &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;

    &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;I've also added a &lt;em&gt;new line at end of file&lt;/em&gt; control. This script has to
be called in the&lt;em&gt; pre-commit hook&lt;/em&gt; script (look up in SVN documentation)&lt;/p&gt;
&lt;p&gt;The call should look like: &lt;br /&gt;
   /chemin/vers/script/svn_check_source.py "$REPOS" "$TXN" || exit 1&lt;/p&gt;
&lt;p&gt;You can then extend the controls made by this script, by controlling
for example the quality of the commited code with tools like
&lt;a href="http://pychecker.sourceforge.net/"&gt;pychecker&lt;/a&gt;. But these extra controls should not block commits and
should be quite light to perform because the commiter waits for the
changeset to be validated. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Sending a mail to the commiter with suggestions when her code doesn't
pass some quality checks is a better idea. Furthermore, for an extensive
QA test, it is simpler to hook a script on a system like &lt;a href="http://buildbot.sourceforge.net/" title="Buildbot"&gt;buildbot&lt;/a&gt;
and create a nighlty digest over the whole code base. &lt;a href="http://www.logilab.org/projects/pylint" title="PyLint"&gt;Pylint&lt;/a&gt; is very
handy on this kind of controls, and can be fine tuned to generate a
useful QA report that buildbot can send to developers.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Wed, 01 Nov 2006 12:29:00 +0100</pubDate><guid>http://blog.ziade.org/2006/11/01/protecting-a-python-svn-code-base-with-the-pre-commit-hook/</guid></item><item><title>Pycon proposal : How to document a Python open source project</title><link>http://blog.ziade.org/2006/10/27/pycon-proposal-how-to-document-a-python-open-source-project/</link><description>&lt;p&gt;I have sent a &lt;a href="http://us.pycon.org/TX2007/CallForTutorials"&gt;proposal for a tutorial&lt;/a&gt; at Pycon, covering the way to
document a project through the developement cycle. It's based on &lt;a href="http://www.agilemodeling.com/essays/agileDocumentation.htm"&gt;Agile
Documentation principles&lt;/a&gt; and provide a way to smoothly integrate
documentation in the daily developer work. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here it is: &lt;br /&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Summary &lt;/em&gt;&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Documentation plays a vital role in open sources projects.&lt;/em&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Well designed, it attracts new people into the project, &lt;br /&gt;
 help the users use it, and raises the quality &lt;br /&gt;
 of the code base and the team work.&lt;/em&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;In other words, it builds the shared knowledge.&lt;/em&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This tutorial is based on a talk i've made &lt;br /&gt;
 at Europython about &lt;a href="http://zope-cookbook.org"&gt;zope-cookbook.org&lt;/a&gt;, and go &lt;br /&gt;
 further by giving a complete set of practical methods &lt;br /&gt;
 that can help a team to create and maintain an up to date &lt;br /&gt;
 documentation with no pain.&lt;/em&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;An example code base will be provided and used for all &lt;br /&gt;
 exercises.&lt;/em&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;By the end of the tutorial, people should be able to &lt;br /&gt;
 integrate documentation in their developement process, &lt;br /&gt;
 the same way they probably did one day with unit testing. &lt;br /&gt;
&lt;strong&gt; &lt;br /&gt;
 Presentation outline&lt;/strong&gt;&lt;/em&gt; &lt;br /&gt;
-   &lt;em&gt;agile documentation principle (short overview)&lt;/em&gt;
-   &lt;em&gt;mastering doctests (overview + exercises)&lt;/em&gt;
-   &lt;em&gt;mastering reStructuredText (overview + exercises)&lt;/em&gt;
-   &lt;em&gt;writing techniques and tips (examples + exercises)&lt;/em&gt;
-   &lt;em&gt;shapes of documents (overview)&lt;/em&gt;
-   &lt;em&gt;building document templates (examples + exercises)&lt;/em&gt;
-   &lt;em&gt;setting up documentation writing in the development cycle:&lt;/em&gt;
-   &lt;em&gt;continuous documentation (examples + exercises)&lt;/em&gt;
-   &lt;em&gt;team writing (exercises)&lt;/em&gt;
-   &lt;em&gt;portfolio (overview)&lt;/em&gt;
-   &lt;em&gt;setting up a help center (overview + exercise)&lt;/em&gt;
-   &lt;em&gt;linking it all together (complete example
    (svn/trac/website/etc..))&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;* Intended audience&lt;/em&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;All, except the doctests exercises that will have a bit of coding&lt;/em&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;the zope-cookbook talk: &lt;br /&gt;
 -
&lt;a href=""&gt;http://indico.cern.ch/contributionDisplay.py?contribId=5&amp;amp;sessionId=53&amp;amp;confId=44&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now let's wait and see if it's accepted :)&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;Pycon&amp;quot;&lt;/span&gt;
&lt;span class="s"&gt;&amp;quot;Agile Documentation&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//i&lt;/span&gt;&lt;span class="n"&gt;ndico&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cern&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;contributionDisplay&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="n"&gt;contribId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;sessionId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;53&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;confId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;44&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Fri, 27 Oct 2006 22:14:00 +0200</pubDate><guid>http://blog.ziade.org/2006/10/27/pycon-proposal-how-to-document-a-python-open-source-project/</guid></item><item><title>Mini-book: a dozen recipes for Zope 3</title><link>http://blog.ziade.org/2006/10/19/mini-book-a-dozen-recipes-for-zope-3/</link><description>&lt;p&gt;I have started a few months ago to write &lt;a href="http://zope-cookbook.org/" title="zope-cookbook.org"&gt;a cookbook for Zope 3&lt;/a&gt;, both
in english and french. The challenge was to be able to: &lt;br /&gt;
-   follow the project code base wich is evolving (too) fast
-   provide transversal knowledge for developers that wants to create
    real world things with zope&lt;/p&gt;
&lt;p&gt;The project was growing and doing well, but I had to stop, because of
Nuxeo's drop of Zope: it became too hard to follow Zope 3 development
exclusively in my spare time. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Nevertheless, &lt;a href="http://kpug.zwiki.org/ZopeCookbook"&gt;Baiju copied the recipes in his wiki&lt;/a&gt;, and hopefully
they will be reinjected in the work done their to construct a mainstream
book. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;However, this experience was really exciting, because I had to set up a
writing process, &lt;a href="http://indico.cern.ch/contributionDisplay.py?contribId=5&amp;amp;sessionId=53&amp;amp;confId=44"&gt;explained last summer at Europython&lt;/a&gt;, based on agile
documentation principles, to be able to provide up-to-date materials,
and examples codes. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;As a souvenir ;), I have generated raw pdfs in both english and french.
They might be useful to people that are discovering Zope 3 out there, as
they also introduces developing methodologies. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Here they are: &lt;br /&gt;
-   &lt;a href="https://tarekziade.files.wordpress.com/2006/10/cookbooken.pdf"&gt;Zope 3 cookbook in english&lt;/a&gt;
-   &lt;a href="https://tarekziade.files.wordpress.com/2006/10/cookbookfr.pdf"&gt;Zope 3 cookbook in french&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Let me know of they are useful to you ! &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Important note: those recipes are not following a book plan, so the pdf
table of content can look a bit strange to readers.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;Baiju wiki&amp;quot;&lt;/span&gt;
&lt;span class="s"&gt;&amp;quot;Eurpython 2006&amp;quot;&lt;/span&gt;
&lt;span class="s"&gt;&amp;quot;Zope 3 cookbook in english&amp;quot;&lt;/span&gt;
&lt;span class="s"&gt;&amp;quot;Zope 3 cookbook in french&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Thu, 19 Oct 2006 09:59:00 +0200</pubDate><guid>http://blog.ziade.org/2006/10/19/mini-book-a-dozen-recipes-for-zope-3/</guid></item><item><title>Marketing Python is urgent, part #1: a Python Certification</title><link>http://blog.ziade.org/2006/10/15/marketing-python-is-urgent-part-1-a-python-certification/</link><description>&lt;p&gt;&lt;strong&gt;Thing changes&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;For about three years, I had one of the best place a Python developer
could dream of in France : one of the most Python-friendly company out
there (Nuxeo), that was promoting the language everytime it was
possible. In this context, even though you've told so, you don't feel
how Python is small compared to Java in the business area. You just
don't want to hear about it because you develop great software, and feel
happy with Python. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;But time are changing, and when your company decides to drop Python for
Java for the main language, a lot of things goes through one developer's
head. &lt;br /&gt;
-   &lt;em&gt;First of all&lt;/em&gt;, I felt like back in college, where people were
    laughing at me and my redhat box, because everybody was under
    Windows and thaught Linux was a piece of crap. I felt like I was
    pushed into some kind of minority. Weird feeling.
-   &lt;em&gt;Secondly&lt;/em&gt;, I felt a little bitter because I understood, while all
    arguments of such a switch are quite understandable, that it's
    mostly a problem of money, because Python can do what Java does. If
    I would've been a billionnaire 10 years ago, Python would be the
    standard nowadays, and all arguments could be flipped ;). But the
    reality is here now, Java is the main stream language in OSS. Java
    and its related technologies are interesting, but I still look at
    them like I look at Ruby: what are their strengths ? How Python can
    learn of them ? This is a pragmatic Pythoneer habit.
-   &lt;em&gt;Last but not least&lt;/em&gt;, with all the evangelism I've been doing these
    past years, in &lt;a href="http://blogs.nuxeo.com/sections/blogs/tarek_ziade"&gt;english&lt;/a&gt; or in &lt;a href="http://programmation-python.org" title="french blog"&gt;french&lt;/a&gt; through &lt;a href="http://www.amazon.fr/exec/obidos/redirect?link_code=as2&amp;amp;path=ASIN/2212116772&amp;amp;tag=programmation-21&amp;amp;camp=1642&amp;amp;creative=6746"&gt;my book&lt;/a&gt; or
    the &lt;a href="http://afpy.org" title="Afpy"&gt;AFPY user group&lt;/a&gt;, I still want to promote Python and be part
    of its community.&lt;/p&gt;
&lt;p&gt;But how ? &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;This blog will throw ideas about marketing Python&lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;All the talks about my company switch makes me feel like marketing
Python is urgent, so it grows in companies, schools, etc. Looking at how
Ruby does its marketing is quite interesting, but this language is still
too young to be able to differenciate the reality from the buzz effect
yet. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Looking at how Linux did, and still does its marketing, is more
interesting because you know what worked and what didn't. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;So this blog will focus on a few things that could be done. And this
first post focuses on &lt;em&gt;Python Certification&lt;/em&gt;. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Important notice: i have digged to find some infos on this topic, but
if I missed something that is beeing done, please forgive me and comment
this post. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A Python Certification &lt;/strong&gt; &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Linux certifications, brought for example by &lt;a href="http://dyork.livejournal.com/tag/certification" title="Dan York"&gt;Dan York&lt;/a&gt; and the
&lt;a href="http://www.lpi.org/" title="LPI"&gt;LPI&lt;/a&gt;, have became great tools to promote Linux in business. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The benefits are quite simple : &lt;br /&gt;
-   Certification centers promote Linux and pay big money in
    advertisment to attract students
-   The LPI is a non-profit organization and can therefore create a
    standard in certification, that is followed by everyone
-   The students became in their own Linux evangelists, and there's a
    crowd of students out there that are just waiting to know the truth
    ;)
-   Companies can rely on it to hire
-   Developers can obtain it to get recognized&lt;/p&gt;
&lt;p&gt;So what about Python ? Take the last chapter I wrote, change "Linux" by
"Python", and "LPI" by "PSF" and you get the idea. &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;I really believe the &lt;a href="http://www.python.org/psf/" title="PSF"&gt;Python Software Foundation&lt;/a&gt; should create a
Python certification. That's probably what we are going to dig on our
side at AFPY and maybe come up with some proposition at the PSF. And
you, readers ? what are your opinion about it ? I'd love to hear it. &lt;br /&gt;
Next time I'll talk about the Python Program Certification, which is
basically the same idea but applies to software standardisation and
quality, and about &lt;a href="http://pycheesecake.org/" title="cheesecake"&gt;cheesecake&lt;/a&gt;, which is a great idea.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;Nuxeo blog&amp;quot;&lt;/span&gt;
&lt;span class="s"&gt;&amp;quot;My book&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tarek Ziadé</dc:creator><pubDate>Sun, 15 Oct 2006 16:45:00 +0200</pubDate><guid>http://blog.ziade.org/2006/10/15/marketing-python-is-urgent-part-1-a-python-certification/</guid></item></channel></rss>
