Gawel's blurbhttp://www.gawel.org/weblog/en/Tue, 16 Dec 2008 20:57:00 -0000Skinning with pyquery and deliverancehttp://www.gawel.org/weblog/en/2008/12/skinning-with-pyquery-and-deliverance<p>I'm using <a class="reference external" href="http://deliverance.openplans.org/">deliverance</a> since a few month now to skin this blog and <a class="reference external" href="http://www.afpy.org">afpy.org</a>. I also contribute to <a class="reference external" href="http://pyquery.org/">pyquery</a> since i like the idea of manipulating xml in python like with <a class="reference external" href="http://jquery.com/">jQuery</a>. So the next step is to use a kind of pyquery rule in deliverance.</p> <p>After looking at the deliverance code it seems that adding new rules is easy. You just need to register it in the deliverance rules. So here is the result:</p> <div class="highlight"><pre><span class="kn">from</span> <span class="nn">deliverance</span> <span class="kn">import</span> <span class="n">rules</span> <span class="kn">from</span> <span class="nn">pyquery</span> <span class="kn">import</span> <span class="n">PyQuery</span> <span class="k">as</span> <span class="n">pq</span> <span class="k">class</span> <span class="nc">PyQuery</span><span class="p">(</span><span class="n">rules</span><span class="o">.</span><span class="n">AbstractAction</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot;PyQuery rule for deliverance&quot;&quot;&quot;</span> <span class="n">name</span> <span class="o">=</span> <span class="s">&#39;pyquery&#39;</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">source_location</span><span class="p">,</span> <span class="n">callback</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">source_location</span> <span class="o">=</span> <span class="n">source_location</span> <span class="bp">self</span><span class="o">.</span><span class="n">callback</span> <span class="o">=</span> <span class="n">callback</span> <span class="k">def</span> <span class="nf">apply</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">content_doc</span><span class="p">,</span> <span class="n">theme_doc</span><span class="p">,</span> <span class="n">resource_fetcher</span><span class="p">,</span> <span class="n">log</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot;apply the rule&quot;&quot;&quot;</span> <span class="bp">self</span><span class="o">.</span><span class="n">callback</span><span class="p">(</span><span class="n">pq</span><span class="p">([</span><span class="n">content_doc</span><span class="p">]),</span> <span class="n">pq</span><span class="p">([</span><span class="n">theme_doc</span><span class="p">]),</span> <span class="n">resource_fetcher</span><span class="p">,</span> <span class="n">log</span><span class="p">)</span> <span class="nd">@classmethod</span> <span class="k">def</span> <span class="nf">from_xml</span><span class="p">(</span><span class="n">cls</span><span class="p">,</span> <span class="n">tag</span><span class="p">,</span> <span class="n">source_location</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot;Parses and instantiates the class from an element&quot;&quot;&quot;</span> <span class="n">use</span> <span class="o">=</span> <span class="n">tag</span><span class="o">.</span><span class="n">attrib</span><span class="p">[</span><span class="s">&#39;use&#39;</span><span class="p">]</span> <span class="n">modname</span><span class="p">,</span> <span class="n">funcname</span> <span class="o">=</span> <span class="n">use</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s">&#39;:&#39;</span><span class="p">)</span> <span class="n">mod</span> <span class="o">=</span> <span class="nb">__import__</span><span class="p">(</span><span class="n">modname</span><span class="p">,</span> <span class="nb">globals</span><span class="p">(),</span> <span class="nb">locals</span><span class="p">(),</span> <span class="p">[</span><span class="s">&#39;&#39;</span><span class="p">])</span> <span class="n">callback</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">mod</span><span class="p">,</span> <span class="n">funcname</span><span class="p">)</span> <span class="k">return</span> <span class="n">cls</span><span class="p">(</span><span class="n">source_location</span><span class="p">,</span> <span class="n">callback</span><span class="p">)</span> <span class="c"># register the new rule</span> <span class="n">rules</span><span class="o">.</span><span class="n">_actions</span><span class="p">[</span><span class="s">&#39;pyquery&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">PyQuery</span> </pre></div> <p>Now i'm able to use my rule. I just need to import the above module before any deliverance one:</p> <div class="highlight"><pre><span class="kn">from</span> <span class="nn">myproject</span> <span class="kn">import</span> <span class="n">pyquery_rule</span> <span class="kn">from</span> <span class="nn">deliverance.middleware</span> <span class="kn">import</span> <span class="n">DeliveranceMiddleware</span> <span class="c"># initialize your middleware</span> </pre></div> <p>Then here is the rules:</p> <div class="highlight"><pre><span class="nt">&lt;ruleset&gt;</span> <span class="nt">&lt;theme</span> <span class="na">href=</span><span class="s">&quot;/theme.html&quot;</span> <span class="nt">/&gt;</span> <span class="nt">&lt;rule</span> <span class="na">class=</span><span class="s">&quot;default&quot;</span><span class="nt">&gt;</span> <span class="nt">&lt;pyquery</span> <span class="na">use=</span><span class="s">&quot;myproject.rules:default&quot;</span> <span class="nt">/&gt;</span> <span class="nt">&lt;/rule&gt;</span> <span class="nt">&lt;/ruleset&gt;</span> </pre></div> <p>Where <cite>myproject/rules.py</cite> look like this:</p> <div class="highlight"><pre><span class="k">def</span> <span class="nf">default</span><span class="p">(</span><span class="n">content</span><span class="p">,</span> <span class="n">theme</span><span class="p">,</span> <span class="n">resource_fetcher</span><span class="p">,</span> <span class="n">log</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot;rule used for testing&quot;&quot;&quot;</span> <span class="n">content</span><span class="p">(</span><span class="s">&#39;title&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">text</span><span class="p">(</span><span class="s">&#39;My site - &#39;</span><span class="o">+</span><span class="n">content</span><span class="p">(</span><span class="s">&#39;title&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">text</span><span class="p">())</span> <span class="n">content</span><span class="p">(</span><span class="s">&#39;#footer&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">remove</span><span class="p">()</span> <span class="n">theme</span><span class="p">(</span><span class="s">&#39;body&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">content</span><span class="p">(</span><span class="s">&#39;body&#39;</span><span class="p">))</span> </pre></div> <p><cite>content</cite> and <cite>theme</cite> are pyquery objects. Feel free to do whatever you want with them. Enjoy !!</p> <p>Notice that this work only with deliverance's <a class="reference external" href="http://codespeak.net/svn/z3/deliverance/trunk/">trunk</a> (or &gt;=0.3).</p> <!-- Links --> Tue, 16 Dec 2008 20:57:00 -00000skinning-with-pyquery-and-deliveranceCombine zc.buildout and pip benefitshttp://www.gawel.org/weblog/en/2008/12/combine-zc.buildout-an-pip-benefits<p>I've just released a new <a class="reference external" href="http://pypi.python.org/pypi/zc.buildout">zc.buildout</a> recipe which allow to install packages with <a class="reference external" href="http://pip.openplans.org//">pip</a>.</p> <p>I am getting really excited about it because it has many advantages taken from both components.</p> <ol class="arabic simple"> <li>The <a class="reference external" href="http://pypi.python.org/pypi/gp.recipe.pip">recipe</a> adds a <a class="reference external" href="http://pypi.python.org/pypi/virtualenv">virtualenv</a> in the <cite>parts/</cite> directory of your buildout then use this binary to generate executable python scripts. So you have a <strong>clean</strong> sandbox.</li> <li>The <a class="reference external" href="http://pypi.python.org/pypi/gp.recipe.pip">recipe</a> is based on <a class="reference external" href="http://pypi.python.org/pypi/zc.recipe.egg#id23">zc.recipe.egg#scripts</a> so you can share your eggs between buildouts as usual.</li> <li>Of course, you can install some <cite>.pybundle</cite> files.</li> <li>You can build package from svn with the <cite>editables</cite> option.</li> </ol> <ol class="arabic simple" start="4"> <li>Each line found in the <cite>install</cite> option is the last part of a <a class="reference external" href="http://pip.openplans.org//">pip</a> command. This allow you to build eggs with dependencies. For example to install <a class="reference external" href="http://codespeak.net/lxml/">lxml</a> in a pure sandbox without libxml2 and libxslt installed you need Cython installed and this command line <cite>python setup.py install --static-deps</cite> to install lxml. This is easy with the <cite>recipe</cite>. Here is a sample configuration file for this case:</li> </ol> <div class="highlight"><pre><span class="k">[buildout]</span> <span class="c"># the cache dir is used by buildout &amp; pip</span> <span class="na">download-cache</span> <span class="o">=</span> <span class="s">download</span> <span class="na">parts</span> <span class="o">=</span> <span class="s">eggs</span> <span class="k">[eggs]</span> <span class="na">recipe</span> <span class="o">=</span> <span class="s">gp.recipe.pip</span> <span class="c"># eggs installed by pip (also add the Deliverance bundle)</span> <span class="na">install</span> <span class="o">=</span> <span class="s">Cython</span> <span class="na">--install-option</span><span class="o">=</span><span class="s">--static-deps lxml==2.2alpha1</span> <span class="err">http://deliverance.openplans.org/dist/Deliverance-snapshot-latest.pybundle</span> <span class="c"># eggs installed by zc.recipe.egg</span> <span class="na">eggs</span> <span class="o">=</span> <span class="s">Paste</span> <span class="err">pyquery</span> </pre></div> <p>That's all !! This works perfectly on my Mac OSX and should works on most system.</p> <p>In fact <a class="reference external" href="http://pypi.python.org/pypi/zc.recipe.egg#id23">zc.recipe.egg</a> will work in most cases but when you need a clean sandbox and some extra options, <a class="reference external" href="http://pypi.python.org/pypi/gp.recipe.pip">gp.recipe.pip</a> is a good alternative.</p> <!-- Links --> Sun, 07 Dec 2008 20:08:00 -00000combine-zc.buildout-an-pip-benefitsGet thumbnails the WSGI wayhttp://www.gawel.org/weblog/en/2008/11/get-thumbnails-the-WSGI-way<p>A few month ago I've created a middleware to generate and cache images thumbnails. I need this to evaluate the image size from the browser's window on the client side and then get the thumbnail at an url based on this size.</p> <p>The result is <a class="reference external" href="http://pypi.python.org/pypi/repoze.bitblt">iw.thumbs</a>. The package provide a highly configurable middleware to serve images thumbnails.</p> <p>The principle is to map an uri to a file system directory. Then if an uri match the specified regexp, a thumbnail is served by the middleware.</p> <p>For example, with the default configuration, the url <cite>http://localhost/thumbs/100x100/image.png</cite> will render a thumbnail of <cite>/var/mapped_dir/image.png</cite>.</p> <p>There is also a &quot;size&quot; uri parser to use named size and prevent potential DoS attacks.</p> <p>That what i'm using here, for my blog. I have two size: blog (490x490) and large (750x750). Here the <a class="reference external" href="http://pythonpaste.org/deploy/">Paste</a> configuration:</p> <div class="highlight"><pre><span class="k">[app:thumbs]</span> <span class="na">use</span> <span class="o">=</span> <span class="s">egg:iw.thumbs</span> <span class="na">url_regexp</span> <span class="o">=</span> <span class="s">^/(?P&lt;size&gt;%s)(?P&lt;path&gt;/.+)</span> <span class="na">url_parser</span> <span class="o">=</span> <span class="s">iw.thumbs.url:size_parser</span> <span class="na">image_dir</span> <span class="o">=</span> <span class="s">%(here)s/var/rst/images</span> <span class="na">cache_dir</span> <span class="o">=</span> <span class="s">%(here)s/data/thumbs</span> <span class="na">sizes</span> <span class="o">=</span> <span class="s">blog = 490x490</span> <span class="na">large</span> <span class="o">=</span> <span class="s">750x750</span> </pre></div> <p>As you can see, the package also provide an application factory in case of you just want to math a specific prefix.</p> <p>Then I just have to map it in my url mapper section:</p> <div class="highlight"><pre><span class="k">[app:main]</span> <span class="na">use</span> <span class="o">=</span> <span class="s">egg:Paste#urlmap</span> <span class="na">/thumbs</span> <span class="o">=</span> <span class="s">thumbs</span> <span class="na">/</span> <span class="o">=</span> <span class="s">pylons # my pylons application</span> </pre></div> <p>Another middleware exist to generate thumbnail with a different approach. <a class="reference external" href="http://pypi.python.org/pypi/repoze.bitblt">repoze.bitblt</a> allow you to generate thumbnail from the size specified in your image tag. It also add a secure part to the image url to prevent DoS attacks. I think both package have their place.</p> <p>What I like with <a class="reference external" href="http://pypi.python.org/pypi/repoze.bitblt">iw.thumbs</a> is that I only need to change a configuration parameter instead of all image tags if the width of my blog column change in the future. The other good point is that you can generate image tags on the client side wich seems not really possible with <a class="reference external" href="http://pypi.python.org/pypi/repoze.bitblt">repoze.bitblt</a>.</p> <p>But well, both middleware are another good reason to use WSGI applications.</p> <p>And as an example, here is Alain, the <a class="reference external" href="http://www.afpy.org/">AFPy</a> mascot render with the <cite>blog</cite> size. If you click on it, you'll get the <cite>large</cite> size in a popup.</p> <div align="center" class="align-center"><img alt="/thumbs/blog/alain_at_rennes.jpg" class="align-center" src="/thumbs/blog/alain_at_rennes.jpg" /></div> <!-- Links --> Tue, 18 Nov 2008 18:17:00 -00000get-thumbnails-the-WSGI-wayWonderful world of WSGIhttp://www.gawel.org/weblog/en/2008/11/wonderful-world-of-wsgi<p>This time it's really winter. When it's cold the only thing i like is geeking at home. So I decide to rewrite my website with a set of WSGI applications.</p> <p>The first thing done was to initialize a <a class="reference external" href="http://pylonshq.com/">Pylons</a> <a class="reference external" href="http://www.gawel.org/projects/browser/GawelOrg/trunk">project</a> for my blog. I got something working after a few hours. <a class="reference external" href="http://pylonshq.com/">Pylons</a> really speed developments.</p> <p>Then I need a skin. I decide to give a try to <a class="reference external" href="http://deliverance.openplans.org/">Deliverance</a>. Skinning with <a class="reference external" href="http://deliverance.openplans.org/">Deliverance</a> is quite easy. After reading the docs, I've create a <a class="reference external" href="http://www.gawel.org/projects/browser/GawelOrg/trunk/themes/layout.html">layout</a> based on my old html code and a set of <a class="reference external" href="http://www.gawel.org/projects/browser/GawelOrg/trunk/themes/rules.xml">rules</a>. Add this to my configuration file:</p> <div class="highlight"><pre><span class="k">[filter:deliverance]</span> <span class="na">use</span> <span class="o">=</span> <span class="s">egg:Deliverance</span> <span class="na">theme_uri</span> <span class="o">=</span> <span class="s">file:///%(here)s/themes/layout.html</span> <span class="na">rule_uri</span> <span class="o">=</span> <span class="s">file:///%(here)s/themes/rules.xml</span> </pre></div> <p>That's it. My <a class="reference external" href="http://pylonshq.com/">Pylons</a> app looks good without any style sheet as you can see ;)</p> <p>Another part of my website is my projects <a class="reference external" href="http://www.gawel.org/docs/">documentations</a> which are generated by <a class="reference external" href="http://sphinx.pocoo.org/">Sphinx</a> and served by a static app. Those pages are now skinned with <a class="reference external" href="http://deliverance.openplans.org/">Deliverance</a> and you can't really know that you browse another application.</p> <p>I also use <a class="reference external" href="http://trac.edgewall.org/">Trac</a> to browse my <a class="reference external" href="http://www.gawel.org/projects/">svn repository</a> so I've try to put it in my WSGI stack. <a class="reference external" href="http://trac.edgewall.org/">Trac</a> now support WSGI except that it do something strange with the output and/or stderr so you'd better not using an error middleware with it else you get a beautiful blank page. You can create a WSGI application with a few lines of code:</p> <div class="highlight"><pre><span class="c"># -*- coding: utf-8 -*-</span> <span class="kn">import</span> <span class="nn">os</span> <span class="kn">from</span> <span class="nn">trac.web.main</span> <span class="kn">import</span> <span class="n">dispatch_request</span> <span class="k">def</span> <span class="nf">make_trac</span><span class="p">(</span><span class="n">conf</span><span class="p">,</span> <span class="n">trac_env</span><span class="p">):</span> <span class="k">def</span> <span class="nf">application</span><span class="p">(</span><span class="n">environ</span><span class="p">,</span> <span class="n">start_request</span><span class="p">):</span> <span class="n">environ</span><span class="p">[</span><span class="s">&#39;trac.env_path&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">trac_env</span> <span class="k">return</span> <span class="n">dispatch_request</span><span class="p">(</span><span class="n">environ</span><span class="p">,</span> <span class="n">start_request</span><span class="p">)</span> <span class="k">return</span> <span class="n">application</span> </pre></div> <p>and skin it with <a class="reference external" href="http://deliverance.openplans.org/">Deliverance</a>. Notice that in my case, <a class="reference external" href="http://trac.edgewall.org/">Trac</a> seems inserting different encoding in the same page so I need to <a class="reference external" href="http://www.gawel.org/projects/browser/GawelOrg/trunk/gawelorg/lib/trac_wsgi.py">hack</a> it a bit.</p> <p>The last thing done is to use a ProxyApp to serve and skin my <a class="reference external" href="http://www.gawel.org/buildbot/">buildbot</a>. Of course, this part is also skinned with <a class="reference external" href="http://deliverance.openplans.org/">Deliverance</a>.</p> <p>I now have only one process to serve all my python apps so I can use <a class="reference external" href="http://static.repoze.org/whodocs/">repoze.who</a> to manage authentication for all apps. I have a ldap server, so I've write a small <a class="reference external" href="http://www.gawel.org/projects/browser/GawelOrg/trunk/gawelorg/auth/ldapauth.py">plugin</a> for it. This work perfect, and I'm now able to log in <a class="reference external" href="http://pylonshq.com/">Pylons</a> and <a class="reference external" href="http://trac.edgewall.org/">Trac</a>.</p> <p>I'm using <a class="reference external" href="http://deliverance.openplans.org/">Deliverance</a> as a WSGI middleware and it seems a bit different than when you use it as a proxy. I encounter a few bugs. I have to only use XPath to find nodes in the DOM. Other expressions just don't work. You also can't remove nodes. But, well, this seems promising.</p> <!-- Links --> Mon, 03 Nov 2008 20:11:00 -00000wonderful-world-of-wsgi