<?xml version="1.0" encoding="utf-8" ?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:syn="http://purl.org/rss/1.0/modules/syndication/" xmlns="http://purl.org/rss/1.0/">




    



<channel rdf:about="http://www.stereoplex.com/search_rss">
  <title>Stereoplex</title>
  <link>http://www.stereoplex.com</link>

  <description>
    
            These are the search results for the query, showing results 1 to 5.
        
  </description>

  

  

  <image rdf:resource="http://www.stereoplex.com/logo.png"/>

  <items>
    <rdf:Seq>
      
        <rdf:li rdf:resource="http://www.stereoplex.com/blog/back-to-plone"/>
      
      
        <rdf:li rdf:resource="http://www.stereoplex.com/blog/buildout-vs-pip-virtualenv-and-requirements-files"/>
      
      
        <rdf:li rdf:resource="http://www.stereoplex.com/blog/migrating-django-mingus"/>
      
      
        <rdf:li rdf:resource="http://www.stereoplex.com/blog/python-unicode-and-unicodedecodeerror"/>
      
      
        <rdf:li rdf:resource="http://www.stereoplex.com/blog/creating-a-python-2-4-plone-and-zope-development-e"/>
      
    </rdf:Seq>
  </items>

</channel>


  <item rdf:about="http://www.stereoplex.com/blog/back-to-plone">
    <title>Back to Plone</title>
    <link>http://www.stereoplex.com/blog/back-to-plone</link>
    <description>Stereoplex is now running Plone again! So why have I moved back?</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>Long-time readers will remember that a while ago, I migrated this blog from its initial Plone 2.5 + SimpleBlog incarnation to a Django-based blog: Kevin Fricovsky's Mingus, to be precise. Well - we've moved back to Plone.</p>
<p>What you're reading now is running on stock Plone 4, with some extra portlets (of types that ship with Plone, no code required), a single custom stylesheet with associated images, and <a class="external-link" href="http://plone.org/products/plone.app.discussion">plone.app.discussion</a> and <a class="external-link" href="http://pypi.python.org/pypi/collective.akismet">collective.akismet</a> for comments.</p>
<p>(OK, OK - there's one other small customisation to replace Plone's favicon, but that doesn't really count, right?)</p>
<h2>Why change at all?</h2>
<p>Plone's editing experience is far superior to what's (easily) obtainable through the Django admin interface - or at least, what was easily available at the time I did the migration. It was simply a TinyMCE editing widget layered over a text field. In particular, image uploading involved going to another screen, uploading, navigating back, and getting the embed stuff right. This was painful. A direct result of this was that I wrote fewer blog posts. And this was a Bad Thing.</p>
<h2>Why Plone?</h2>
<p>Plone 4 was recently released, and it was finally at the point where I was satisfied that it could do everything out of the box that I needed - all I had to do was to theme it. I did consider other options:</p>
<h3>Wordpress</h3>
<p>For most people, Wordpress is the go-to blog engine. I could even have gone with a hosted option. However, hosting implies that I'd have to use a Wordpress theme to get it looking the way I wanted - and I don't really have the time to learn to theme Wordpress. (Remembering how to theme Plone - it's been a while! - took a half day, which was plenty long enough). That meant that I'd need to use Deliverance to theme it, and run the blog on my own server. Running Wordpress myself again wasn't something I wanted to do: it would mean installing (and more importantly, maintaining) a PHP installation, and installing, configuring and maintaining a MySQL install. If I was going to use a third-party blog that requires a relational database, I'd much rather it used PostgreSQL - I run that anyway, I've got backups scheduled, and so on and so forth.</p>
<p>So that pretty much counted out Wordpress.</p>
<h3>Mingus (or any other Django-based solution)</h3>
<p>Of course, I could just have kept on with Mingus, and re-skinned. However, this wouldn't solve the problem of the content editing experience not being great. I would therefore have had to investigate alternate blog platforms - perhaps FeinCMS (which I already have some experience with, as we're using it in the next version of <a class="external-link" href="http://www.swooptravel.co.uk">Swoop</a>) or Django CMS, or something new. Well - I might have done all that, and still wound up with a solution that wasn't as good as Plone's editing environment - and then I <strong>still</strong> would have had to go back and theme the thing.</p>
<h2>So, Plone</h2>
<p>So, I ended up back at Plone.</p>
<p style="padding-left: 0px; ">This was not to say that I didn't have any reservations about taking it for a spin again. The last time I did any serious Plone work was back in the 2.5 and early 3.0 days - just as the technologies in what was then Zope 3, now ZTK, were being introduced. Plone out-of-the-box was pretty slow (and I didn't have much time this time round to spend on performance tuning, not for my little blog, and availabile hardware was pretty minimal) and the default theme was even by then looking dated, with complex markup and styles.</p>
<p style="padding-left: 0px; ">I was also wary of needing to add new content types. Archetypes, though functional, is now old - and shows all the signs of organic code growth. I've been watching Dexterity, lined up as the AT replacement, for some time now; and while it looks very promising, it's not quite there yet. I'm subscribed to the Dexterity bug tracker mailing list, and there are still too many bugs to do with fundamentals coming through for my liking. I've mentally got Plone + Dexterity + Deliverance lined up for very rapid website builds, but I don't think it's quite mature enough yet.</p>
<p style="padding-left: 0px; ">So I tried Plone 4. And was pleasantly surprised.</p>
<p style="padding-left: 0px; ">Firstly, it's faster. Noticeably faster. And even on my little Slicehost VM. This is an OOTB configuration, so no Chameleon for ZPT, but even so - great.</p>
<p style="padding-left: 0px; ">Secondly, Sunburst is a lot easier on the eye. More importantly, it's much easier to go a long way with pure CSS. The only non-CSS presentation customisation I did (portlet rearranging through the Plone UI aside) was to customise the favicon.</p>
<h2>The New Stereoplex</h2>
<p>I'm pretty happy with it (as I was the last time!). The hardware is the same, but Plone itself is much, much faster. It's easier to do simple skinning - with the exception of the favicon customisation, all I did was register a new CSS stylesheet.</p>
<p>I've also allowed myself the luxury of not even trying it in IE. :)</p>
<p>Don't think, incidentally, that I'm moving away from Django - not at all. However, I now have a lot more confidence in using Plone to build web <strong>sites</strong> (and of course, intranets), which are primarily content management problems. I'm sticking with Django for web <strong>applications. </strong>You really can't beat Django for development speed. But if I need a CMS again in the future, I'll feel much more comfortable about Plone.</p>
<p>My take-away from this:</p>
<p style="text-align: center; ">Plone for web sites.</p>
<p style="text-align: center; ">Django for web applications.</p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>Dan Fairs</dc:creator>
    <dc:rights></dc:rights>
    
      <dc:subject>housekeeping</dc:subject>
    
    
      <dc:subject>plone</dc:subject>
    
    <dc:date>2010-10-01T11:50:00Z</dc:date>
    <dc:type>Page</dc:type>
  </item>


  <item rdf:about="http://www.stereoplex.com/blog/buildout-vs-pip-virtualenv-and-requirements-files">
    <title>buildout vs pip, virtualenv and requirements files</title>
    <link>http://www.stereoplex.com/blog/buildout-vs-pip-virtualenv-and-requirements-files</link>
    <description>The Python world is blessed with two mainstream choices for integrating Python packages into an application: buildout and pip. This post looks at the pros and cons of each, to try to help you pick which one is best for you.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>Repeatable software configurations are crucial for reliable software deployments. Applications (particularly web applications based on frameworks) are often made up of dozens, if not hundreds of separate packages. When you deploy your application, you want to be sure that the packages and their versions are the same as those that were tested through development and staging.</p>
<p>Buildout</p>
<p>Buildout, developed originally by Jim Fulton at Zope Corporation, was an attempt to address this problem. zc.buildout 1.0.0, released back in January 2008 (after over a year of betas) has become the accepted way of managing the hundreds of packages that make up Zope, BlueBream and Plone. Buildout isn't restricted to Zope, of course - it's usable for any setuptools-aware Python software.</p>
<p>Buildout is based around configuration files, most commonly a single file called buildout.cfg. Buildout itself doesn't do very much; most functionality is provided by add-on modules. These modules are called 'recipes'. They're easy to write, and there are <a href="http://pypi.python.org/pypi?:action=browse&show=all&c=512">dozens of custom recipes</a> to perform specialised tasks. However, the key piece of functionality that buildout offers is to allow the developer to simply list the packages (eggs) required by their application, and optionally their versions. Minimum, maximum and ranges of acceptable versions can be specified, and buildout will do its best to satisfy version requirements.</p>
<p>Buildout is based on setuptools (which is in turn based upon distutils, part of the Python standard library). It uses setuptools to do the heavy lifting of package search and installation. Buildout environments are isolated from the system Python: packages installed via buildout don't end up anywhere near system Python's site-packages directory.</p>
<p>pip and virtualenv</p>
<p>Pip and virtualenv both come originally from the ever-productive Ian Bicking. pip is a replacement for easy_install, part of setuptools (as indeed pip is now): it offers a number of advantages over easy_install, **expand this**. Like buildout, pip also offers the ability to install a set of Python packages of a given version, using a requirements file. The requirements file lists which eggs to install, their versions, editable eggs checked out of source control, alternative mirrors for the location of packages, and so on.</p>
<p>pip installs into the current Python environment - which by default, will be the system Python. Pip is therefore commonly used with virtualenv, which isolates the Python environment.</p>
<p>buildout dependency resolution - build can fail halfway through due to version conflicts</p>
<p><span style="font-size: small;">A hybrid approach? gp.recipe.pip</span></p>
<p>Would be nice if it could provide data to feed mr.developer [sources], and [versions]</p>
<p>Conclusion</p>
<p>Choose pip - simplicity</p>
<p>Choose buildout - sophistication</p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>Dan Fairs</dc:creator>
    <dc:rights></dc:rights>
    
      <dc:subject>buildout</dc:subject>
    
    
      <dc:subject>plone</dc:subject>
    
    
      <dc:subject>python</dc:subject>
    
    
      <dc:subject>zope</dc:subject>
    
    
      <dc:subject>django</dc:subject>
    
    <dc:date>2010-02-17T22:44:34Z</dc:date>
    <dc:type>Page</dc:type>
  </item>


  <item rdf:about="http://www.stereoplex.com/blog/migrating-django-mingus">
    <title>Migrating to Django Mingus</title>
    <link>http://www.stereoplex.com/blog/migrating-django-mingus</link>
    <description>I've migrated my blog from a creaking Plone 2.5 to a fork of Django Mingus. This is the process I went through.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>I've been running a blog since 2007 (check the archives!). At the time, it made most sense for me to go for a Plone-based blog. Plone was what I was most familiar with, and there was a simple blog product out there that I could use called, sensibly enough, SimpleBlog. In fact, you can still grab it - it's where you'd expect on the <a href="http://plone.org/products/simpleblog">Plone Products section</a>. And as you can see, the release there was the one that I used at the time - SimpleBlog 2.0, for Plone 2.5</p>
<p>Fast-forward to the start of 2010, and things have moved on. Plone's moved on, for sure. Plone 4 is just around the corner, and there's some really, really cool stuff in there: Dexterity, a new content types framework, finally looks like it'll make content type creation as easy as it should be, and simple types and behaviours can be created through the web. Deco, slated (last time I heard) for Plone 5, is quite literally going to make publishers wet themselves. And Deliverance has got great potential in helping to unify the many disparate systems which makes the typical corporate user's daily life such a grind. Thing is, I'm not a big corporate user; a lot of what makes Plone great for that environment is overhead for me and my little blog.</p>
<p>The big change for me personally, though, is that more and more of my work is now Django. About two thirds of 2009 was Zope 2/Five, and a third Django; 2010 is looking like it'll be the other way around. So, having decided that my blog was looking a little tired, and seemed to be a bit of a spam magnet, I decided to bite the bullet and do a complete rebuild in Django.</p>
<p>I didn't want to write yet-another-blog from scratch, so I picked what seemed to have the most buzz around it at the time - <a href="http://github.com/montylounge/django-mingus">Django Mingus</a> - and started from there. And this is how I did it.</p>
<p>Oh, before I get stuck in, all the code for this site is open source. Fortunately, there's not much of it. There's no documentation as such (this is open source, after all) because the target audience is, well, me. I've stayed true to my Zope roots and used buildout; the buildout and Django project can be checked out from GitHub:</p>
<p><a href="http://github.com/danfairs/stereoplex-buildout">Stereoplex buildout and project</a></p>
<p>Onwards.</p>
<h2>Planning</h2>
<p>There's more to moving a blog than just installing your blog software and bashing out new articles. I had to consider a number of migration issues, specifically:</p>
<ul>
<li>Obviously, I need to move all the content over from my old blog. That's not just articles: that's images, comments, the whole shebang.</li>
<li>I needed to either keep all the old article URLs from my previous blog working, or have them redirect to the new URLs.</li>
<li>Similarly, there are lots of RSS URLs out in the wild; I've handed a couple of them out to the Django community aggregator and the Planet Plone aggregator. There's also the main site feed.</li>
<li>I decided not to move user accounts over, as I'd already decided that I wasn't going to require a login to comment. I was going to go with a ReCaptcha integration.</li>
</ul>
<p>So, the first step was obviously going to be to extract all the content from my old blog.</p>
<h2>Getting the content out of the ZODB</h2>
<p>Plone stores data in the ZODB. The ZODB is amazing. It was years ahead of its time, and provides a really natural way to store and interact with data in document-oriented systems. It's only really in the last year or so that we've seen the rise of broadly similar data stores, most of which talk HTTP and don't have the fine-grained transaction control that ZODB provides. The only thing to remember with the ZODB is that you can only access it directly with Python. That meant that I had to write a Python script to dump out all my content into a platform-neutral form. (I could have written a script which read the ZODB directly and created Django models, I guess. Using an XML intermediate format seemed easier though, and was probably quicker to work with - loading up the Plone 2.5 codebase is pretty slow.)</p>
<p>Most Plone applications, SimpleBlog included, stored its data using the Archetypes content type framework. Archetypes (often just AT) is a schema-driven approach to creating content. This is a pretty handy approach (even if AT's implementation was a bit awkward in places) as it's really simple to write code that can introspect that content. This was invaluable for writing an export script.</p>
<p>The result is on github: <a href="http://github.com/danfairs/Simple-AT-XML-Dump">Simple-AT-XML-Dump</a>. I should note that AT does have support for native XML dump and load. Thing is, I've got an ancient version of AT, and SimpleBlog used CMF (a layer underneath Plone) comments. I had no idea if XML marshalling would work properly, so I just did my own custom XML format. It's an instance script. It should work pretty well on any folderish Archetypes types. Invoke it like this:</p>
<pre>bin/instance run Simple-AT-XML-Dump/run.py -p /zodb/path/to/plone/content -o out.xml</pre>
<p>/zodb/path/to/plone/content is the physical path in the ZODB to dump. This needs to be the root folderish AT item. out.xml is the output file.</p>
<p>I won't go into the specifics of how the script works (if there's anything you're interested in especially, mail me or post a comment below). In a nutshell though, it'll dump out AT types by schema (including ImageFields with base64-encoded data) and any CMF discussions that are associated with them.</p>
<h2>Starting with Django Mingus</h2>
<p>Many years of pain have taught me to automate my build. The XML dumper instance script above is pretty much the smallest thing I'll do without a build (and even now, I feel a twinge of guilt at not having packaged it properly.) For me, therefore, the first thing to do was to get a build up and running.</p>
<p>The system du jour seems to be to use pip and virtualenv. Pip (a replacement for easy_install) lets you specify requirements files, which define which Python packages you application uses, what versions of them, and lets you install directly from the major source control systems. (This ability seems to have caused lots of requirements files with github links in them to spring up in 'released' packages, especially in the Django world. I regard this as a Bad Thing; but that's an opinion piece for another time). However, grizzled Zope veterans tend to reach for buildout in such a circumstance so, wanting to restrict New Fangled stuff to learning how Mingus was put together, I stuck with that. Buildout predates pip and virtualenv, and so does stuff that you'd normally just do with virtualenv, like creating an isolated environment. Buildout config files are also a touch more verbose than requirements files. That said, I'm still pretty sure that buildout is the more extensible system, with a vast array of custom recipes; and, in common with a lot of software from the Zope world, the narrative documentation is atrocious.</p>
<p>But I know buildout, and I'm not giving up these scars so easily, so that's what I went with, basing my buildout config on the requirements file that comes with Mingus. This process was actually pretty simple:</p>
<ol>
<li>Set up a standard Django buildout using djangorecipe</li>
<li>Pop mr.developer in as an extension, add [sources] for all of the editable (-e) eggs in the pip requirements file, and add them all as auto-checkout</li>
<li>Put all the non-editable eggs from the requirements file into the eggs section of the buildout config</li>
<li>Use the versions supplied in the requirements file to create a [versions] section in the buildout config</li>
</ol>
<p>This gives me a buildout.cfg file that mirrors the Mingus requirements file, but will also:</p>
<ul>
<li>Create the django management script and django WSGI file automatically</li>
<li>Create a project and select an appropriate settings file for me</li>
</ul>
<p>The end result of this is the GitHub project I linked to above, <a href="http://github.com/danfairs/stereoplex-buildout">stereoplex-buildout</a>.</p>
<h2>Stereoplex</h2>
<p>I created another egg, called <a href="http://github.com/danfairs/stereoplex">stereoplex</a>, to contain all my site-specific customisations and scripts. I used another package of mine, <a href="http://pypi.python.org/pypi/fez.djangoskel/">fez.djangoskel</a>, to create the basic layout. Specifically, it has:</p>
<ul>
<li>A Django management command to import the XML file created with Simple-AT-XML-Dump</li>
<li>A Django ModelAdmin subclass (actually a basic.blog.admin.PostAdmin subclass) to let me use TinyMCE as my editor</li>
<li>A ReCaptcha Django form field, widget, and custom comment form</li>
<li>An single extra view, which returns all items posted on the blog</li>
<li>A URLConf which brought together Mingus' URLs plus those for the extra view, and for TinyMCE</li>
<li>And of course, all the template overrides and CSS, JavaScript and images required for the new Stereoplex look and feel</li>
</ul>
<h2>Importing the Data</h2>
<p>The next step was to write a data importer. This had to do a number of things (data migrations are never simple!):</p>
<ul>
<li>Import all the images in the XML data file, creating basic.media.models.Photo instances for each of them</li>
<li>Rewrite all image links in the body text of posts to contain &lt;inline&gt; elements used by Mingus</li>
<li>Create basic.blog.models.Post instances for each blog post in the file</li>
<li>Create django.contrib.comments.models.Comment instances for every comment, and associate them with the appropriate post </li>
<li>Create django.contrib.redirect.models.Redirect objects for each imported post, to allow existing inbound links to be redirected to the new location.</li>
</ul>
<p>Automated content import is one of those things that you tell clients is usually impossible. And when they're migrating from a legacy CMS platform (or indeed, hand-maintained HTML), then that's usually right. The inevitable gigabytes of hand-rolled HTML are at best poorly formed, and at worse represent content which needs throwing away anyway.</p>
<p>I was more fortunate. I didn't have that much content to migrate - 60-odd posts, and a few images - and Plone 2.5's default editor Kupu is actually pretty good at producing good HTML. I was able to directly use the existing HTML, and only needed to replace the &lt;img&gt; tags with the appropriate &lt;inline&gt; expected by Mingus.</p>
<h2>Changes to Mingus packages</h2>
<p>I did make some modifications to Mingus' packages. These were as follows:</p>
<h3>django-mingus</h3>
<ul>
<li><a href="http://github.com/danfairs/django-mingus/commit/f94ad242fe9f216651333352b982738bcd1bdf2e">Improve the way subscription links are generated</a></li>
<li><a href="http://github.com/danfairs/django-mingus/commit/7c11807bd80c5d8f1b88185aa7f9bddbea0f99dc">On a category page, include the RSS link for that category in the page header</a></li>
<li><a href="http://github.com/danfairs/django-mingus/commit/898874e65a131db2dc2bc6cf8b695655d837290f">Hide teaser-related markup if there's no teaser</a></li>
</ul>
<h3>django-basic-apps</h3>
<ul>
<li><a href="http://github.com/danfairs/django-basic-apps/commit/482daf65b1f372b5cbe518eed5a34d1e1ca72404">Fix a bug where a template tag library was not loaded</a></li>
</ul>
<h3>django-sugar</h3>
<ul>
<li><a href="http://github.com/danfairs/django-sugar/commit/5a748578b0e19a6102e75fa68a2160da80572c2a">Enhance the pygmentize filter to not be restricted to &lt;code&gt;</a></li>
</ul>
<p>All really very minor.</p>
<h2>Server Configuration</h2>
<h3>Apache</h3>
<p>The Plone instance used a standard small setup: a single ZEO client talking to a ZEO server, fronted by Apache with the magic RewriteRule. The Zope configuration had been tweaked slightly to be usable on a small (by Plone's standard!) 512MB RAM host, but I hadn't had time to do any other optimisations (for example, serving static files from Apache) that I would normally do.</p>
<p>Django forces you to do at least some of these. Static files are always served by an external web server (unless you are really determined to sail through all the warnings in the documentation). I also finally got around to turning on gzip compression.</p>
<p>One remaining task that I planned to do in the Apache configuration was to provide redirects for my RSS feeds. This wasn't as easy as I might have liked, since the old feed URLs had query string elements; and indeed, it was some of those values that I needed to formulate a correct redirect. I ended up with the following:</p>
<pre class="code">RewriteEngine On<br />RewriteCond %{query_string} ^(.*)?EntryCategory=(.*)?&amp;(.*)<br />RewriteRule ^/search_rss /feeds/categories/%2/ [R=permanent,L,NC]<br /></pre>
<p>This simply declares three match groups in the RewriteCond regex, the second of which (hence %2) is the category slug. The RewriteRule then issues a permanent redirect to the new URL. We have to use RewriteCond, because RewriteRule regexes won't match a query string, only the URL path.</p>
<p>Next up is the mod_wsgi configuration. I use a fairly standard configuration, which is generally as follows:</p>
<pre class="code">WSGIScriptAlias / /var/websites/www.stereoplex.com/bin/django.wsgi<br />WSGIDaemonProcess stereoplex user=stereoplex group=stereoplex processes=3 threads=25 maximum-requests=1000 stack-size=524288<br />WSGIProcessGroup stereoplex<br /></pre>
<p>This runs the Stereoplex web application in its own process group, and with its own user and group membership. This is a safety net: if the site is compromised or (to be honest, more likely) I screw up and the app tries to write to the filesystem, its rights are limited by the host's file access control. The WSGI script file is generated by buildout. Probably the most interesting parameter here is the stack size. This is set lower than Linux's default value; for this sort of app, it doesn't need to be as large as the default. Setting this value to lower than the default led to a massive memory saving (remember, this is only a 512MB host; and this site is one of around half a dozen running on the same machine).</p>
<h3>Memcached</h3>
<p>Mingus makes extensive use of Django's caching support. Other Django sites on the same host use a Memcached instance, so I just pointed Stereoplex at that one. Memcached is essentially in a default configuration, except only bound to the localhost IP address, rather than the public IP address. It's configured to use a maximum of 64MB of RAM. This doesn't sound a great deal, but even with two or three websites using it, I haven't seen it go over 40MB.</p>
<h2>So... All Done?</h2>
<p>Nearly.</p>
<p>If I'm honest, the content editing experience isn't as nice as Django as it was in Plone. This is expected. Plone is a CMS, and has an administrative interface that's focussed on the business of managing content. Django isn't a CMS, it's a more general web framework. The experience is more like editing content in the ZMI.</p>
<p>That said, there are advantages. I've found third-party Django software much easier to integrate and customise than third-party Plone products ever were. I'm not fighting reams of configuration all the time. There's a strong mindset of developing apps to be reusable in the Django world; Mingus itself is little more than some UI glue (as, really, is Stereoplex). If I were tasked with delivering a large CMS with flexible authentication and authorisation, workflow, and so forth: I'd go for Plone in an instant. But for this job, many of Plone's strengths simply don't apply, and somthing more simple and lightweight was more appropriate.</p>
<p>Anyway - I'm quite pleased with the results. There are still a few kinks to be worked out (pygementize only seems to be being applied on the home page, not individual post pages, for example) but I'll get there over the next couple of weeks.</p>
<p>If there's anything you'd like to have more information on, then leave a comment (click on the article heading - yes, a proper link to comments is on the list!) or of course, just go and grab the code at <a href="http://github.com/danfairs/stereoplex">Github</a>.</p>
<p>Enjoy!</p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>Dan Fairs</dc:creator>
    <dc:rights></dc:rights>
    
      <dc:subject>plone</dc:subject>
    
    
      <dc:subject>django</dc:subject>
    
    <dc:date>2010-01-14T23:38:34Z</dc:date>
    <dc:type>Page</dc:type>
  </item>


  <item rdf:about="http://www.stereoplex.com/blog/python-unicode-and-unicodedecodeerror">
    <title>Python, Unicode and UnicodeDecodeError</title>
    <link>http://www.stereoplex.com/blog/python-unicode-and-unicodedecodeerror</link>
    <description>In the years I've been developing in Python, Unicode seems to be the topic which causes the greatest amount of confusion amongst developers. Hopefully much of this confusion should go away in Python 3, for reasons I'll come to at the end; but until then, the UnicodeDecodeError is the bane of many developers' lives.
</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<h3>Unicode and Encodings</h3>
<p>OK, let's take a step away from text for a moment. I want you to think of a number between one and ten. Got one? Great - now, grab a pen and paper, and write it down.<br /><br />What number did you think of? Well, I thought of the number six. And when I wrote it down, it looks like this:<br /><br /><img alt="6" class="image-inline" src="numeric___png_580x500_crop_q85.jpg" /></p>
<p>Of course, if I were an ancient Roman (or possibly a clockmaker), I could have written this:<br /> <br /></p>
<p><img alt="Six in Roman Numerals" class="image-inline" src="bars___png_580x500_crop_q85.jpg" /><br /><br /> <br />They all mean the same thing - the number six. But we've written them in different ways. In other words, we've 'encoded' our idea of the number six in our head in three different ways - three different encodings.<br /><br />The separation of the idea of 'the number six' from its actual representation is basically all Unicode is. The Unicode Character set (UCS) defines a set of things (loosely, a set of letters) that we can represent. How we represent each of those letters is called an encoding. There's only one Unicode, but there are many encodings. In Unicode parlance, each of those 'things' (letters) are known as 'code points'. Unicode separates the characters' meaning from their representation.<br /><br />For historical reasons, the most common encoding (in Western Europe and the US, anyway) is ASCII. This is also Python's default encoding. <br /><br />Let's think about ASCII for a moment. It's an encoding that uses 7 bits, which limits it to 128 possible values. That's enough to represent all the characters that Western Europe and the US use (letters in both cases, the numbers, punctuation, a few characters with diacritics). Therefore, Unicode strings that only include code points that are in these 128 ASCII characters can be encoded as ASCII. Conversely, any ASCII encoded string can be decoded to Unicode.<br /><br />It's worth reiterating that terminology, as you come across it a lot: the transformation from Unicode to an encoding like ASCII is called 'encoding'. The transformation from ASCII back to Unicode is called 'decoding'.<br /><br /></p>
<pre>    Unicode  ---- encode ----&gt; ASCII<br />    ASCII    ---- decode ----&gt; Unicode</pre>
<h3>Non-ASCII encodings</h3>
<p>Most people don't live in the US or Western Europe, and therefore have a requirement to store more characters than can be represented with ASCII. What those folk need to represent *is* part of the Unicode set (Unicode is massive!) - so a different encoding is required. Common encodings have familiar names: UTF-8 and UTF-16. UTF-8, for example, uses a single byte for encoding all the ASCII values, then variable numbers of bytes to encode further characters. (The ins and outs of these encodings are beyond the scope of this article - check out their respective Wikipedia entries for the gory details.)<br /><br />The fact that the first byte of UTF-8 isthe same as ASCII is important, since it means that the encoding is backwards-compatible with ASCII. However, it can mask problems in software. We'll come to this shortly.</p>
<h3>Some terminology</h3>
<p>Unicode-related terminology can get confusing. Here's a quick glossary:<br /><br /></p>
<ul>
<li>To encode</li>
<ul>
<li>Encoding (the verb) means to take a a Unicode string and produce a byte string</li>
</ul>
<li>To decode</li>
<ul>
<li>Decoding (the verb) means to take a byte string and produce a Unicode string</li>
</ul>
<li>An encoding</li>
<ul>
<li>An encoding (the noun) is a mapping that describes how to represent a Unicode character as a byte or series of bytes. Encodings are named (like 'ascii', or 'utf-8') and are used both when encoding (verb!) Unicode strings and decoding byte strings.</li>
</ul>
</ul>
<p><br />In other words, when you encode or decode, you need to specify the encoding that you're using. This will become clearer shortly.</p>
<h3>Python, bytes and strings</h3>
<p>You've probably noticed that there seems to be a couple of ways of writing down strings in Python. One looks like this:</p>
<pre>  'this is a string'<br /></pre>
<p>Another looks like this:</p>
<pre>  u'this is a string'<br /></pre>
<p>There's a good chance that you also know that the second one of those is a Unicode string. But what's the first one? And what does it actually mean to 'be a Unicode string'?<br /><br />The first one is simply a sequence of bytes. This byte sequence is, by convention, an ASCII representation (ie. encoding) of a string. The whole Python standard library, and most third-party modules, happily deal with strings natively in this encoding. As long as you live in US or Western Europe, then that's probably fine for you.<br /><br />The second one is a representation of a Unicode string. This can therefore contain any of the Unicode code points. It's possible that whatever you're using to edit the Python code (or just view it) might not be able to display the entire Unicode character set - for instance, a terminal usually has an encoding that it assumes data it's trying to display is in. There's a special notation, therefore, for representing arbitrary Unicode code points within a Python Unicode string: the \u and \U escapes. These will be followed by four or eight hex digits; there's some subtlety here (see the Python string reference for further information) but you can simply think of the number after the \u (or \U) representing the Unicode code point of the character. So, for example, the following Python string:</p>
<pre>  u'\u0062'<br /></pre>
<p>represents LATIN SMALL LETTER B, or more simply:</p>
<pre>  u'b'<br /></pre>
<p>To summarise then: the Unicode character set encompasses all characters that we may wish to represent. Individual encodings (ASCII, UTF-8, UTF-16, etc.) are representations of all or some of that full Unicode character set.</p>
<h3>Encoding and Decoding</h3>
<p>Byte strings and Unicode strings provide methods to perform the encoding and decoding for you. Remembering that you *encode* from Unicode to an encoding, you might try the following:<br /><br /></p>
<pre>&gt;&gt;&gt; u'\u0064'.encode('ascii')<br />'d'</pre>
<p>As you'd expect, the Unicode string has an 'encode' method. You tell Python which encoding you want ('ascii' in this case, there are lots more supported by Python - check the docs) using the first parameter to the encode() call.<br /><br />Conversely, byte strings have a decode() method:<br /><br /></p>
<pre>&gt;&gt;&gt; 'b'.decode('ascii')<br />u'b'</pre>
<p><br />Here, we're telling Python to take the byte string 'b', decode it based on the ASCII decoder and return a Unicode string. <br /><br />Note that in both these previous cases, we didn't really need to specify 'ascii' manually, since Python uses that as a default.</p>
<h3>UnicodeEncodeError</h3>
<p>So, we've established that there are encodings which can represent Unicode, or more usually, a certain subset of the Unicode character set. We've already talked about how ASCII can only represent 128 characters. So, what happens if you have a Unicode string that contains code points that are outside that 128 characters? Let's try something all too familiar to UK users: the £ sign. The Unicode code point for this character is 0x00A3:<br /><br /></p>
<pre>&gt;&gt;&gt; u'\u00A3'.encode('ascii')<br />Traceback (most recent call last):<br />  File "&lt;stdin&gt;", line 1, in &lt;module&gt;<br />UnicodeEncodeError: 'ascii' codec can't encode character u'\xa3' <br />in position 0: ordinal not in range(128)</pre>
<p>Boom. This is Python telling you that it encountered a character in the Unicode string which it can't represent in the requested encoding. There's a fair amount of information in the error: it's giving you the character that it's having problems with, what position it was at in the string, and (in the case of ASCII) it's telling you that the number it was expecting was in the range 0 - 127.<br /><br />How do you fix a UnicodeEncodeError? Well, you've got a couple of options:</p>
<ul>
<li>Pick an encoding that does have a representation for the problematic character</li>
<li>Use one of the error handling arguments to encode()</li>
</ul>
<p>The first option is obviously ideal, although its practicality depends on what you're doing with the encoded data. If you're passing it to another system that (for example) requires its text files in ASCII format, you're stuck. In that case, you're left with one of the other two options. You can pass 'ignore', 'replace', 'xmlcharrefreplace' or 'backslashreplace' to the encode call:<br /><br /></p>
<pre>&gt;&gt;&gt; u'\u0083'.encode('ascii', 'ignore')<br />''<br />&gt;&gt;&gt; u'\u0083'.encode('ascii', 'replace')<br />'?'<br />&gt;&gt;&gt; u'\u0083'.encode('ascii','xmlcharrefreplace')<br />'&amp;#131;'<br />&gt;&gt;&gt; u'\u0083'.encode('ascii','backslashreplace')<br />'\\x83'</pre>
<p><br />If you choose one of those options, you'll have to let the eventual consumer of your encoded text know how to handle these.</p>
<h3>UnicodeDecodeError</h3>
<p>This one is probably more familiar to most developers. A UnicodeDecodeError occurs when you ask Python to decode a byte string using a specified encoding, but Python encounters a byte sequence in that string that isn't in the encoding that you specified (phew!). This one probably benefits from an example.<br /><br />Consider once more the ASCII encoding. Being a 7-bit representation, ASCII only has 127 characters, represented by the numbers 0 - 127. So let's imagine the ASCII-encoded string below:</p>
<pre>'Hi!'</pre>
<p><br />In terms of ASCII numbers, that is:<br /><br /> 72 105 33<br /><br />Or in actual Python:<br /><br /></p>
<pre>&gt;&gt;&gt; s = chr(72) + chr(105) + chr(33)<br />&gt;&gt;&gt; s<br />'Hi!'<br />&gt;&gt;&gt; s.decode('ascii')<br />u'Hi!'</pre>
<p>That's all great. But what happens if we add a byte that's not in the ASCII range?<br /><br /></p>
<pre>&gt;&gt;&gt; s = s + chr(128)<br />&gt;&gt;&gt; s.decode('ascii') <br />Traceback (most recent call last):<br />  File "&lt;stdin&gt;", line 1, in &lt;module&gt;<br />UnicodeDecodeError: 'ascii' codec can't decode byte 0x80 <br />in position 3: ordinal not in range(128)</pre>
<p>Boom. Python is saying that it encountered a character 0x80 (which is 128 in hex, the one we added) which was at position 3 (counting from zero) in the source byte string which was not in the range 0 - 127.<br /><br />This is normally caused by using the incorrect encoding to try to decode a byte string to Unicode. So, for example, if you were given a UTF-8 byte string, and tried to decode it as ASCII, then you might well see a UnicodeDecodeError.<br /><br />But why only might?<br /><br />Well, remember what I mentioned before - UTF-8 shares the first 127 characters with ASCII. That means that you can take a UTF-8 byte sequence, and decode it with the ASCII decoder, and *as long as there are no characters outside the ASCII range* it will work. *Only* when that byte string starts featuring characters which don't exist within the ASCII encoding do errors start being thrown.</p>
<h3>ASCII - the default codec</h3>
<p>Lots of Python programmers (well, US and Western European ones) can get quite a way into their Python careers converting byte strings to unicode like this:</p>
<pre>&gt;&gt;&gt; print unicode('hi!')<br />u'hi!'<br /></pre>
<p>What's going on here? Well, Python uses the ascii codec by default. So, the above is equivalent to:</p>
<pre>&gt;&gt;&gt; 'hi!'.decode('ascii')<br />u'hi!'<br /></pre>
<p>And, because most US/European test data is composed of this byte string:</p>
<pre>  'test'<br /></pre>
<p>... nobody notices the problem until the Japanese office complains the intranet is broken.</p>
<h3>Unicode Coercion</h3>
<p>If you try to interpolate a byte string with a Unicode string, or vice-versa, Python will try and convert the byte string to Unicode using the default (ie. ascii) codec. So:</p>
<pre>&gt;&gt;&gt; u'Hi' + ' there'<br />u'Hi there'<br />&gt;&gt;&gt; u'Hi %s' % 'there'<br />u'Hi there'<br />&gt;&gt;&gt; 'Hi %s' % u'there'<br />u'Hi there'</pre>
<p>These all work fine, because all the strings that we're working with can be represented with ASCII. Look what happens when we try a character which can't be represented with ASCII though:</p>
<pre>&gt;&gt;&gt; u'Hi ' + chr(128)</pre>
<pre>Traceback (most recent call last):<br />  File "&lt;stdin&gt;", line 1, in &lt;module&gt;<br />UnicodeDecodeError: 'ascii' codec can't decode byte 0x80 <br />in position 0: ordinal not in range(128)</pre>
<p><br />Python sees we're trying to combine a Unicode string with a byte string, so tries to decode the byte string to Unicode using the ASCII codec. Since character 128 (the Euro symbol, as it happens) can't be represented in ASCII, Python throws a UnicodeDecodeError.<br /><br />In my experience, Unicode coercion is often where UnicodeDecodeErrors manifest themselves. The programmer has a Unicode string (probably a template) into which they're trying to put some data from a database. Relational databases tend to supply byte strings. Usually the encoding is a property on the database connection. Often, however, developers simply assume it's ASCII (or don't do anything special at all, which in Python amounts to the same thing). They try to stick the data from the database (perhaps in UTF-8 or ISO-8859-1) into a Unicode string using the %s format specifier, Python tries to decode the byte string using the ascii codec, and the whole thing falls flat on its face.</p>
<h3>Why do Python byte strings have an encode() method?</h3>
<p>The sharp-eyed amongst you will have noticed that byte strings have an encode() method as well as a decode() method. What does this do? Quite simply, it does a decode-then-encode. The byte string is decoded to Unicode using the default (ascii) encoding, and is then encoded to the target encoding specified in the call to encode() using the appropriate encoding. As you'd expect, fun and games ensue if the original byte string isn't actually encoded in ASCII at all.</p>
<h3>Avoiding Unicode Errors</h3>
<p>So - this is really what you care about, right? How do you avoid these Unicode problems? Well, there are three simple rules:</p>
<ul>
<li>Within your application, always use Unicode</li>
<li>When you're reading text in to your application, decode it as soon as possible with the correct encoding</li>
<li>When you're outputting text from your application, encode at that point and do it explicitly</li>
</ul>
<p>What does this mean in practice? Well, it means:</p>
<ul>
<li>Whenever you're writing string literals in code, always use u''.</li>
<li>Whenever you read any text in, call .decode('encoding') on the byte string to obtain Unicode</li>
<li>Whenever you're writing text out, pick an appropriate encoding to handle whatever Unicode you're outputting - remember that ASCII can only represent a very limited subset</li>
</ul>
<p><br />There are more places than you probably realise that text can get into your application. Here's some:</p>
<ul>
<li>An incoming request from a web browser</li>
<li>Some text read in from a data file on disk</li>
<li>A template file read in from disk</li>
<li>Some user's input from a form</li>
<li>Some data from a database</li>
<li>Data returned from a web services call</li>
</ul>
<p><br />Frameworks help a lot here. Many frameworks handle the common encoding and decoding cases (usually the template encoding, and data encoding from a database) for you, and just pass you back Unicode strings. Watch out for web request variables - many of those may be plain byte strings. Also watch out for web service responses; you might need to inspect the response headers to find out the encoding. And even then be careful; I've come across situations with in-house apps where declared encoding were simply wrong, leading to unexpected UnicodeDecodeErrors.</p>
<h3>Figuring out which encoding to use</h3>
<p>When you're faced with a byte string, how do you know which decoding to use? The answer is, unfortunately, simple: you don't. Some environments (such as the Web) may help you - HTTP requests and responses contain headers which specify the encoding used within them. You can inspect those, and if they're wrong - well, at least you've got someone else to blame.<br /><br />If you're lucky, you know the byte string is encoding some XML. XML is gets a lot of flack, but one of the things it does right is to specify explicitly a default encoding that's actually useful (UTF-8) and provide a mechanism to declare a different encoding. So with XML, you can scan the first few bytes of the file, decode using UTF-8, and look for the magic encoding declaration. If there isn't one, then you can safely decode the rest of the file using UTF-8. If there is one, then switch encoding. Of course, your XML library of choice will do all this for you, and should give you Unicode text back once you've read your XML in.<br /><br />If you're unlucky, then you've got two more options. First off, you can talk to the people who run your source (or destination) system - find out what encodings they're using, or accept, and use those.</p>
<p>The final, last resort option is to simply have a range of common encodings to try. A list I often use is ASCII, ISO-8859-1, UTF-8, UTF-16. Keep trying to decode with each of those in turn until one works. Which encodings you pick of course depends on what kind of files you're expecting to see. You may also run into problems of course if you have a byte string in encoding X which also happens to be valid when decoded using encoding Y - in this case, you'll just get garbage data. This is the cause of many of the 'funny character' bugs you see in web applications: byte strings being decoded using an encoding which happened to work, but was in fact not the original encoding used to create the byte string.</p>
<h3>Python 3</h3>
<p>I'm not going to talk too much about Python 3, since I haven't actually used it yet. <br /><br />But - you rarely hear .NET or Java programmers complaining about Unicode errors. This is simply because both .NET and Java define a string to *be* Unicode in the first place. Anything involving the String class (in either runtime) is Unicode anyway; the developer sees encoding problems much less frequently as it's much less common for unexpected byte data to creep into applications. This doesn't mean the problems don't exist, of course: at the end of the day, text is still being encoded to and from byte strings; it's just done explicitly. (The fact that the default encoding on MS Windows, the OS on which many of these systems run, is UTF-16 helps here too - many more characters can be encoded in UTF-16 than ASCII).<br /><br />My understanding is that Python 3 takes this general approach. Python 2's 'str' type is gone. In its place is the 'unicode' type (equivalent to Java and .NET's String class), and the 'bytes' type. String operations are done on 'unicode' instances.</p>
<h3>Coding in a Unicode world</h3>
<p>Unicode is here to stay. The days of writing software that would only need to work in American universities, where the only language and script used was US English in Latin text are long gone. There's no magic to Unicode and the various encodings, and once you understand what's going on, there's no reason to have that sick feeling in the pit of your stomach the next time you see a UnicodeDecodeErrror. Just remember these rules:</p>
<ul>
<li>Decode on the way in</li>
<li>Unicode everywhere in your application</li>
<li>Encode on the way out</li>
</ul>
<p> </p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>Dan Fairs</dc:creator>
    <dc:rights></dc:rights>
    
      <dc:subject>python</dc:subject>
    
    
      <dc:subject>zope</dc:subject>
    
    
      <dc:subject>plone</dc:subject>
    
    
      <dc:subject>django</dc:subject>
    
    <dc:date>2010-09-30T15:21:39Z</dc:date>
    <dc:type>Page</dc:type>
  </item>


  <item rdf:about="http://www.stereoplex.com/blog/creating-a-python-2-4-plone-and-zope-development-e">
    <title>Creating a Python 2.4, Plone and Zope Development Environment on Mac OS X Leopard</title>
    <link>http://www.stereoplex.com/blog/creating-a-python-2-4-plone-and-zope-development-e</link>
    <description>Compiling Python, Zope and Plone on Leopard isn't as easy as it is on Linux. Here's a walkthrough of the process, from a bare Leopard install right through to having a working Plone 3 development environment, using paster and buildout.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p><b>UPDATED 27/01/2008:</b> Added instructions on building PIL with buildout<br /></p>
<p>If you're new to Zope and Plone on the Mac, and just want to get up and running, stop reading right now! Instead, go and download the unified <a href="http://plone.org/products/plone">Plone installer for Mac OS X, Linux and Solaris on Plone.org</a>. This article is for people who need more control over the installation, and need to build the core pieces from source.</p>
<p>Still reading? Good. <br /></p>
<p>Note that the information here has been drawn together from a number of places on the web. However, it's not been presented as an end-to-end process. That's what I aim to do here.<br /></p>
<h3>Install development tools</h3>
<p>Development tools (compilers and headers, basically) aren't installed by default on Leopard. Dig out your Mac OS X DVD, and install Xcode. This will give you everything you need. You should be able to do something like this:</p>
<pre>$ gcc<br />i686-apple-darwin9-gcc-4.0.1: no input files</pre>
<p>You can see that the gcc compiler is installed. If instead you see 'command not found', then you don't have gcc installed.<br /></p>
<br />
<h3>Compiling Python</h3>
<p>Python is the first hurdle. Leopard ships with Python 2.5, but Zope 2 still requires 2.4. Unfortunately, Python 2.4 doesn't compile out of the box with Leopard. You'll see something like this:</p>
<pre>hornet:Python-2.4.5 dan$ ./configure --prefix=/Users/dan/tmp/opt<br />[snip lots of output]<br /><br />hornet:Python-2.4.5 dan$ make<br />[compile, compile, explode]<br />gcc -fno-strict-aliasing -Wno-long-double -no-cpp-precomp -mno-fused-madd -DNDEBUG -g -O3 <br />  -Wall -Wstrict-prototypes -I. -I./Include  -DPy_BUILD_CORE  -c ./Modules/signalmodule.c <br />  -o Modules/signalmodule.o<br />gcc -fno-strict-aliasing -Wno-long-double -no-cpp-precomp -mno-fused-madd -DNDEBUG -g -O3 <br />  -Wall -Wstrict-prototypes -I. -I./Include  -DPy_BUILD_CORE  -c ./Modules/posixmodule.c <br />  -o Modules/posixmodule.o<br />./Modules/posixmodule.c: In function 'posix_setpgrp':<br />./Modules/posixmodule.c:3145: error: too few arguments to function 'setpgrp'<br />make: *** [Modules/posixmodule.o] Error 1</pre>
<p>Ouch. <br /></p>
<p>Fortunately the solution for this is actually on the Python.org site. (If you scroll down a bit, which I didn't initially). Simply modify your configure line to include an extra define:</p>
<pre class="literal-block">./configure MACOSX_DEPLOYMENT_TARGET=10.5</pre>
<p>You should fine Python now compiles. You can then make install.</p>
<p>Oh - but the fun isn't over yet! Have a play with your new Python. At best, you'll find your arrow keys don't work. At worst, you'll experience odd crashes with the helpful generic Bus Error message.<br /></p>
<p>Fortunately, <a href="http://www.zopyx.com/blog/compiling-readline-support-for-python-on-macosx">Andreas Jung</a> has the answer here: a broken or outdated version of the readline library. You can compile your own, or do what Andreas recommends and install one from <a href="http://darwinports.com/">DarwinPorts</a>. Once that's installed, you simply need to do:</p>
<pre>$ sudo port -d selfupdate<br />$ sudo port install readline</pre>
<p>You should now have a working readline. Next up, you need to configure your Python build environment to pick this up (otherwise it'll just pick up the broken OS X readline again.) I actually did this the way Andreas describes, a manual hack in the Makefile:</p>
<pre>CC = gcc -I/opt/local/include -L/opt/local/lib</pre>
<p>I suspect that you'd be able to pass --includedir=/opt/local/include and --libdir=/opt/local/lib to the configure script, and that would be a cleaner way of doing it. Once you've down that, run make and make install, and you should have a working Python, with working readline support, that doesn't crash.</p>
<p>Remember to set your PATH up to use your new Python binary in preference to the system one - something like:</p>
<pre>export PATH=/Users/dan/opt/bin:$PATH</pre>
<p>What's next?</p>
<h3>setuptools</h3>
<p>A lot of stuff these days is installed using setuptools. Download <a href="http://peak.telecommunity.com/dist/ez_setup.py">ez_setup.py</a> to somewhere convenient, and run it using your new python:</p>
<pre>python ez_setup.py</pre>
<p>This will download and install setuptools into your Python 2.4's site-packages directory, and configure it so that it's usable.</p>
<p>To check it's installed correctly, type:</p>
<pre>which easy_install</pre>
<p>The path should be within your Python 2.4 install. If it's not then you've managed to install it into your system Python. First - why are you running as root; and second, check your PATH.<br /></p>
<p>Note that from now on we're going to be using a package called virtualenv to isolate any further package installs. You've gone to a lot of effort to get a working 2.4 install - let's keep it tidy!<br /></p>
<h3>virtualenv</h3>
<p>virtualenv is a fantastic tool which creates a Python 'sandbox' to work in. This means that you can install packages in a virtualenv to your heart's content, and it won't pollute the main Python installation. It creates a new python binary, and lib/python24/site-packages structure to facilitate this.</p>
<p>Installing it is dead easy:</p>
<pre>easy_install virtualenv</pre>
<p>virtualenv will be downloaded and installed. Let's immediately create a virtualenv to work in:</p>
<pre>$ mkdir -p ~/virtual/2.4<br />$ cd ~/virtual/2.4<br />$ virtualenv plone3<br />New python executable in plone3/bin/python<br />Installing setuptools............done.<br />$ cd plone3<br />$ source bin/activate<br />(plone3)$</pre>
<p>Note how your prompt has changed - this is a visual clue that you're now working in a virtual environment. Look at the output for:</p>
<pre>which python</pre>
<p>You should see that the Python from the virtualenv is used. This of course means that any further easy_installs you do will be into thsis virtualenv.</p>
<p>As above, I find it useful to put my virtualenvs for Python 2.4 and Python 2.5 in separate directories. But that's just me - running source bin/activate in each virtualenv will do the right thing.</p>
<h3>ZopeSkel</h3>
<p>ZopeSkel is a package which will install paster, buildout, and some commands to help create Plone development instances (amongst other things, including Silva instances, Plone themes, etc.) Installing it is easy:</p>
<pre> (plone3)$ easy_install ZopeSkel</pre>
<p>This will whir for a while. Once it's installed, you should find that you have a paster installed:</p>
<pre>which paster</pre>
<p>... will show you that it has, as with everything else, been installed into your virtualenv.</p>
<h3>Creating a Plone 3 development environment</h3>
<p>We're on the final leg of the journey now! <br /></p>
<p>paster and buildout do most of the heavy lifting for us here. First of all, we use paster to create a buildout for us:</p>
<pre>paster create -t plone3_buildout p3</pre>
<p>This, again, will whir for a while. plone3_buildout refers to the template to be used to create p3. If you're interested, you can see what other templates were installed:</p>
<pre>paster create --list-templates</pre>
<p>Of course, paster create --help will give you more information on available options.</p>
<h3>PIL</h3>
<p>Before we run our buildout, we need to install the Python Imaging Library, also known as PIL. Previous versions of this article described how to download and install this from source. However, there is now an easier way.</p>
<p>Edit the p3/buildout.cfg file and edit it as follows:</p>
<pre>[buildout]<br /><br />parts =<br />  PIL<br />  ....<br /><br />eggs =<br />  ...<br />  PIL<br /><br />[PIL]<br />recipe = zc.recipe.egg<br />egg = PIL==1.1.6<br />find-links = http://dist.repoze.org/ <br /></pre>
<h3><br /></h3>
<h3>Building Zope and Plone</h3>
<p>Now we have our buildout, we're ready to go. Let's finally build Zope out, configured for Plone. During this process, you'll be asked for an initial administrative username and password for Zope:<br /></p>
<pre>(plone3)$ cd p3<br />(plone3)$ python bootstrap.py<br />(plone3)$ bin/buildout</pre>
<p>Now go and make a cup of tea - that last step can take a while. <br /></p>
<p>What's happening is that buildout is using the buildout.cfg to locate and download all the eggs and parts which make up a Zope 2 and Plone 3 installation. It will then compile and install those for you. If you run buildout with multiple -v options (eg. -vvvv) then it'll give you a lot more information about what it's doing.</p>
<p>When this finishes, you should be able to run up your Zope instance:</p>
<pre>(plone3)$ bin/instance fg</pre>
<p>Browse to http://localhost:8080 (assuming you kept the port the same) and you should see the familiar Zope Quick Start page.</p>
<h3>Creating a development egg</h3>
<p>It's likely that all new development that you'll be doing in Plone 3 will be egg-based. However, it's not immediately obvious how you get started.</p>
<p>Development eggs live in the src/ directory of your buildout. As with the main Plone 3 buildout, we use paster to create the egg for us:</p>
<pre>(plone3)$ cd src/<br />(plone3)$ paster create -t plone egg.name</pre>
<br />
<p>You'll get asked some questions. The namespace and package you should set appropriate for your project (I'll assume you answered 'egg' and 'name' respectively); and say 'no' to the Zope 2 product and zip-safe questions. Paster will then go ahead and create the file structure for your egg in src.</p>
<p>Finally (and I mean it this time) we have to tell buildout about the development egg. Go back up one level of directory, above src, and you should see a file called buildout.cfg. Edit this file, and add the following:</p>
<pre>[buildout]<br />eggs =<br />    egg.name<br />...<br />develop =<br />    src/egg.name<br />...<br />[instance]<br />zcml = <br />    egg.name</pre>
<p>Once this is saved, then run buildout again in offline mode:</p>
<pre>bin/buildout -o</pre>
<p>This will look at your buildout.cfg and modify the buildout to include your development egg.</p>
<h3>And after that?</h3>
<p>If you've got to here, well done! You've now got a development environment. The final stage, once you've developed your egg, is to package it. This is pure setuptools. From within your egg:</p>
<pre>python setup.py sdist bdist_egg</pre>
<p>This will create a finished egg, placed in the dist/ directory, ready to use. It will also create a source distribution (ending in .tar.gz).<br /></p>
<p>There's a lot more to learn around this process - but I hope that's clarified the basic procedure, and workarounds for some of the issues specific to Mac OS X Leopard.</p>
<p></p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>Dan Fairs</dc:creator>
    <dc:rights></dc:rights>
    
      <dc:subject>macintosh</dc:subject>
    
    
      <dc:subject>python</dc:subject>
    
    
      <dc:subject>zope</dc:subject>
    
    
      <dc:subject>plone</dc:subject>
    
    <dc:date>2008-05-07T15:49:05Z</dc:date>
    <dc:type>Page</dc:type>
  </item>




</rdf:RDF>

