<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>jjmojojjmojo: In Effect</title>
	<atom:link href="http://lionfacelemonface.wordpress.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://lionfacelemonface.wordpress.com</link>
	<description>Programming... for life</description>
	<lastBuildDate>Sun, 20 Nov 2011 17:42:51 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
<cloud domain='lionfacelemonface.wordpress.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://s2.wp.com/i/buttonw-com.png</url>
		<title>jjmojojjmojo: In Effect</title>
		<link>http://lionfacelemonface.wordpress.com</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="http://lionfacelemonface.wordpress.com/osd.xml" title="jjmojojjmojo: In Effect" />
	<atom:link rel='hub' href='http://lionfacelemonface.wordpress.com/?pushpress=hub'/>
		<item>
		<title>RFC: Crushinator Probe/Interrogation Implementation Details (branching questions, skipping, validation, etc)</title>
		<link>http://lionfacelemonface.wordpress.com/2011/10/08/rfc-crushinator-probeinterrogation-implementation-details-branching-questions-skipping-validation-etc/</link>
		<comments>http://lionfacelemonface.wordpress.com/2011/10/08/rfc-crushinator-probeinterrogation-implementation-details-branching-questions-skipping-validation-etc/#comments</comments>
		<pubDate>Sat, 08 Oct 2011 11:00:50 +0000</pubDate>
		<dc:creator>jjmojojjmojo</dc:creator>
				<category><![CDATA[crushinator]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[rfc]]></category>

		<guid isPermaLink="false">http://lionfacelemonface.wordpress.com/?p=558</guid>
		<description><![CDATA[Here I discuss the approach that's forming to handle the 'branching questions' requirements (and some other things where PasteScript is lacking) in Crushinator. Please take a look, and let me know what you think.<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lionfacelemonface.wordpress.com&amp;blog=2073436&amp;post=558&amp;subd=lionfacelemonface&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<h1>Introduction</h1>
<p>The Crushinator project aims to replace PasteScript as a <em>de facto</em> standard for developing boilerplate code generation systems. My interest in starting the project came from use of and work on ZopeSkel/templer.core. There are several issues with the way that PasteScript solicits values from the user that are restrictive to the developer and often frustrating for the user.</p>
<p>If you&#8217;re not familiar with how PasteScript works, you typically invoke it via the <code>paster create</code> sub-command. You then get prompted for a series of values that correspond to template values. It works in a linear fashion. The values may be manipulated prior, but are essentially passed verbatim to the underlying template</p>
<p>For more background, see the <a href="https://github.com/jjmojojjmojo/Crushinator/blob/master/docs/design.rst" target="_blank">design document</a>.</p>
<h2>Terminology Clarification</h2>
<h3>&#8216;user&#8217; vs &#8216;developer&#8217;</h3>
<p>When I talk about <em>users</em> here, I mean someone who invokes the <code>paster</code> (or, <code>zopeskel/templer</code>) command to run a code generation &#8216;template&#8217;. When I talk about <em>developers</em> I&#8217;m talking about folks that write the &#8216;template&#8217; code.</p>
<h3>&#8216;template&#8217; vs Template vs template</h3>
<p>There&#8217;s a confusing bit of nomenclature in PasteScript regarding the word &#8216;template&#8217;. Typically when people talk about templates, they mean a predefined structure; most people think of Word templates or cut pieces of plastic or paper used to guide a pen or saw. In both cases, there&#8217;s an implication of structure and repeatability.</p>
<p>When <em>programmers</em> talk about templates, they typically mean text files with special syntax; these text files provide structure and repeatability as in the common sense, but templates for programmers are typically filled with place holders, and often simplified logic. Examples are vast, but include Cheetah, Jinja2, Smarty, Mason, etc.</p>
<p>In PasteScript, Template is a base class that handles the interaction between the user and (typically) a template (in the programmer sense). It also does some things with treating code layouts like templates, doing additive injections of code, etc.</p>
<p>Separating these concerns and quelling this confusion is a core goal of the Crushinator project. That said, the only real frame of reference anyone reading this will likely have is PasteScript, so when I use the term &#8216;template&#8217;, I&#8217;ll put it in quotes, and I&#8217;ll be referring to templates in the <em>PasteScript</em> sesne. If I don&#8217;t quote it, assuming it wasn&#8217;t just a typo, I mean it in the <em>programmer</em> sense.</p>
<h2>Specific Issues This Document Addresses</h2>
<p>This section covers the specific problems with PasteScript that are being addressed by the Probe/Interrogation implementation, and the new concepts and features that will also be implemented.</p>
<h3>Skipping</h3>
<p>We&#8217;ve often found it useful to skip some prompts. Lets say the user enters a response to a prompt, and that implies some sane defaults. It&#8217;s difficult for the developer to handle this situation with the core PasteScript library (ZopeSkel and templer.core have overcome this on their own).</p>
<p>A concrete example: there are about a half-dozen template variables in the python egg-related templates (<code>package</code>, <code>namespace</code>, <code>nested-namespace</code>) that deal with package metadata (license, author, keywords, etc). These values are strictly optional in the sense of building the code skeleton successfully and creating a working package. Aside from the ZopeSkel hacking around the issue, there&#8217;s no way to just ask the user &#8220;do you want to set metadata now?&#8221;, and if they say &#8220;no&#8221;, skip all of those questions, substituting sane defaults. (See ZopeSkel&#8217;s &#8216;easy mode&#8217; to see this in practice)</p>
<p>This happens because the prompting happens independently of the processing and template transformation. You have to wait until the user is finished answering all of the questions before you can act.</p>
<h3>Validation</h3>
<p>In PasteScript, there is no &#8216;baked-in&#8217; facility for checking a value from the user. If you do detect an error, you have to rerun the prompt loop, or intervene on your own.</p>
<p>This has caused some pain; aside from the obvious (e.g. the developer assumes a raw integer and the user enters &#8216;one&#8217;), often the values entered by the user will correspond directly to a python identifier, or a package name. A good developer will typically massage the value to make it conform, but it doesn&#8217;t always happen.</p>
<p>What&#8217;s worse, it can confuse the user. Say they entered &#8220;My Class&#8221;, understanding that the mangling will happen. They expected &#8220;MyClass&#8221;, but instead got &#8220;My_Class&#8221; due to an assumption on the developers part. I&#8217;d rather see an error here, or feedback about what happened (&#8216;My Class&#8217; is not a valid class name, is My_Class ok?&#8217;, or &#8216;You set the content type to &#8220;My Class&#8221;, so we&#8217;re going to call the class My_Class&#8217;, etc).</p>
<p>The way I look at it, the code generated by a tool like Crushinator should be &#8216;sound&#8217; out of the box. The user should be able to understand what happened by looking at it, it shouldn&#8217;t do anything &#8216;magical&#8217; for its own sake, and as much as possible, it should <em>run without error</em>.</p>
<p>Validation and allowing some sort of feedback to the user will alleviate these issues.</p>
<h3>Branching</h3>
<p>Building on the &#8216;skipping&#8217; concept, this involves presenting the user with a whole new set of prompts given their specific response to a certain prompt. As far as I know, there is no current implementation of this concept in any PasteScript-based boilerplate generators.</p>
<p>An example: The user is building a new python package. It asks them, &#8220;would you like to set up Sphinx documentation&#8221;. If the user says &#8220;yes&#8221;, they are asked specific questions that are necessary for Sphinx to function. If they say &#8220;no&#8221;, they go on without it. A similar approach could be used for adding a zc.buildout structure.</p>
<h3>Grouping</h3>
<p>There&#8217;s utility in being able to arbitrarily group prompts, primarily in &#8216;alternative&#8217; front ends, such as the web or desktop. This could be implemented as a tabbed interface, or a &#8216;wizard&#8217;, or just allow the developer to provide more guidance to the user by giving a description and/or help-related text that covers a subset of a larger suite of prompts.</p>
<p>To my knowledge, this is not implemented in PasteScript.</p>
<h3>More information</h3>
<p>In PasteScript, developers are limited to a single, simple string that gets passed to the user when solicited for a value. Often developers want to provide a more detailed explanation for what the value means, what its implications are, etc.</p>
<p>Crusinator will provide several mechanisms for the developer to communicate with the user (but most of these will be optional)</p>
<h3>Markup in Prompts</h3>
<p>Currently there&#8217;s no way to effectively provide emphasis, urls, or otherwise enrich the prompts as they&#8217;re given to the user. This limits both the amount of help the developer can give the user, and makes more robust user interfaces (web, desktop) more difficult to implement.</p>
<p>There will be a single markup standard used throughout Crushinator. This is independent from any template processors that the developer uses.</p>
<p>Markup would apply to any text that is presented to the user.</p>
<h3>Internationalization</h3>
<p>i18n is not currently a common component of PasteScript (I don&#8217;t think it&#8217;s impossible to implement it with gettext, but AFAIK nobody&#8217;s doing it). As PasteScript-based generators have often become the go-to &#8216;quick-start&#8217; for new users of a framework, the lack of this feature is a hindrance to acceptance.</p>
<p>It will be optional but recommended (using gettext makes it pretty easy to open the door to other folks translating it for you). However it is implemented, it will need to be able to handle the markup mentioned above. All text provided to the user will be expected to be passed through that translation mechanism.</p>
<h3>User Feedback</h3>
<p>When a value fails validation, it&#8217;s useful to tell the user why. Crushinator will expand on this concept, allowing user feedback when validation <em>passes</em>.</p>
<p>An example: The user is building a python egg, and they opt to not fill out the metadata. The developer can point the user to the generated setup.py file, and a link to the setuptools page explaining what the values mean, so they&#8217;ll have a pointer to what they&#8217;re supposed to do.</p>
<h1>Implementation</h1>
<h2>Terms</h2>
<p>In the <a href="https://github.com/jjmojojjmojo/Crushinator/blob/master/docs/design.rst" target="_blank">design document</a>, I&#8217;ve settled on a separation of many of the concerns and picked nomenclature to identify the parts. In this document, I&#8217;m focusing on the Probe, Interrogation, and (to a lesser extent) the Runner classes. There will be mention of the Collector and UserInterface classes, and fleeting mention of Skeleton and Injector, but the specifics of UserInterface, Skeleton and Injector are still in flux. Although Collector has a base implementation, it&#8217;s only utilized by the UserInterface, so there&#8217;s not much need for detail here.</p>
<p>A basic overview of the terms/base classes:</p>
<table rules="none">
<tbody valign="top">
<tr>
<td><strong>Probe:</strong></td>
<td>A singe piece of information required to complete the code generation task.</td>
</tr>
<tr>
<td style="text-align:left;"><strong>Interrogation:</strong></td>
<td>A collection of <em>Probes</em>.</td>
</tr>
<tr class="field">
<td class="field-name" style="text-align:left;"><strong>UserInterface:</strong></td>
<td class="field-body">Literally, a user interface. Provides user interaction.</td>
</tr>
<tr class="field">
<td class="field-name" style="text-align:left;"><strong>Runner:</strong></td>
<td class="field-body">A collection of <em>Interrogations</em> and <em>Skeletons</em>. Acts as intermediary; provides Probes to the User Interface, and template values to the Skeletons and Injectors.</td>
</tr>
<tr class="field">
<td class="field-name" style="text-align:left;"><strong>Collector:</strong></td>
<td class="field-body">Seeker of default values. Current base implementation pulls from a handful of setup files, and the command line</td>
</tr>
</tbody>
</table>
<p>All of these classes reside in <code>crushinator.framework</code>. As typical implementations are built, they will be part of the <code>crushinator.toolkit</code> package.</p>
<h2>Basic Communications</h2>
<p>Crushinator itself provides a setuptools entry-point facility to register (plug-in) User Interfaces, Collectors, and Runners. The User Interface provides a plug-in point for Runners. In this way the Runner expresses compatibility with a given set of User Interfaces. The User Interface will also register any setuptools &#8216;console scripts&#8217; that the user will use to invoke it.</p>
<h2>Help Documentation</h2>
<p>A user will invoke the User Interface via the console script. The User Interface must provide a help() method (and corresponding UI element or command line switch), which is expected to return a list of all Runners that have expressed compatibility with that User Interface. The help() method will also take a Runner class or string containing a dotted-package notation (e.g. &#8216;my.package.MyRunnerClass&#8217;).</p>
<p>The Runner class must also provide a help() method. When a Runner specifier is passed to the help() method for a User Interface, after look-up, this method is to be invoked by the User Interface. The help() method for a Runner will also take a specifier. In this case the value can be: a Probe class/package-notation, an Interrogation class/package-notation, a two-tuple  (of Interrogation class/package-notation, Probe name) or just a Probe name.</p>
<p>Probes and Interrogations will also (optionally) have a help() method, returning general text about what they do. This might not typically differ from the description property also expected, but is provided in the event that more context can be given outside of a short clarification as to what a Probe/Interrogation&#8217;s purpose or expected value is.</p>
<p>In all cases, (as mentioned earlier) it will be possible that the text will contain a lightweight markup.</p>
<p>The goal here is to encourage developers to include useful help at each level of the system, thus providing a better end-user experience. The user should be able to pull up help on a specific Runner, or a specific Probe and/or Interrogation with relative ease, and the User Interface should be able to provide such help text in an intuitive way.</p>
<h2>User Interaction</h2>
<p>The User Interface will provide some mechanism for a user to select a Runner they wish to work with. Default values will be pulled via invoking one or more appropriate Collectors. Collectors return a dictionary, where the keys are Runner class specifications (dotted package notation), and the values are dictionaries of individual Probe names and their default values. The defaults for a specific Runner are passed to the Runner&#8217;s constructor.</p>
<p>The Runner will internally maintain a collection of Interrogations, which, in turn, maintain a collection of Probes. The User Interface invokes the Runner&#8217;s next() and set() methods as it interacts with the user.</p>
<p>The next() method returns a Probe object. The set() method takes a value and a Probe specifier. This &#8216;commits&#8217; the value back to the Runner. The set() method returns any success output from the Probe, or raises an exception upon error. This gives us a few useful API interactions:</p>
<ul>
<li>Branching becomes possible. The Runner consults the Probe&#8217;s next() method to retrieve the next Probe (or Interrogation) that should be presented to the user. What the Probe returns is dependent on its current value.</li>
<li>Validation happens along the way. Each Probe implements a validate() method, which is invoked by the Runner&#8217;s set() method. The validate() method can also be used on its own to check a value before saving it.</li>
<li>The Interrogation also implements a validate() method, allowing for invariant checks and other sorts of group validation.</li>
<li>The User Interface can build dynamic interfaces by pre-polling the Probes without calling set(). This is necessary when building UIs for the web or desktop.</li>
<li>The user can get feedback about successes as well as errors.</li>
</ul>
<p>Another feature in this communication is a high-level of autonomy. A Runner must provide a list of Probes to a User Interface outside of the next()/set() interaction, as well as a separate list of Interrogations. Each Probe or Interrogation can be invoked, validated, and then set() independently. This provides the sort of flexibility necessary for asynchronous UIs. Those lists will react to existing values just like the next() method does (the API here is still a bit cloudy, they may actually take values as a method argument). In this way the User Interface can present the user with a &#8216;wizard&#8217; view or tabbed interface.</p>
<p>After all of the Probe values are collected from the user and set back on the Runner, the User Interface will then call the invoke() Runner method. This is where the actual code generation will happen. The invoke() method will raise an exception which contains error text from any failed Skeletons or Injectors. If it runs successfully, the return value will be a success string, much like the validate() method of a Probe.</p>
<p><em>The exact details here may change quite a bit over time as the Skeleton and Injector classes are fleshed out. There&#8217;s a good chance that the Runner will be passed a list of files that were changed, or other useful info beyond &#8220;code generated successfully&#8221;. However, the intent is the same: let the user know what&#8217;s going on.<br />
</em></p>
<h2>More Details</h2>
<p>I&#8217;m planning to use reStructuredText to handle markup in labels, help text, and other text passed to the user. It will probably happen automatically, but I may need to provide a way to prevent processing if it gets in the way.</p>
<p>Gettext will be used to set up translations of all core implementations. It may be rolled into the reStructuredText processor if that ends up being automatic. The idea is to make help text and translations a seamless part of the development process.</p>
<p>Interrogations and Runners will implement some sort of dictionary-like API to retrieve specific Probes. I see the use case here in a fancy web UI that validates via AJAX as the user enters values.</p>
<p>As I mentioned earlier, heavy use of setuptools entry points will be employed to register User Interfaces as console apps, and to provide a way for Runners to integrate with specific UIs. That said, there will be a set of stock implementations for user interfaces that should be compatible with any Runner.</p>
<h2>Additional Products</h2>
<p>In the crushinator.toolkit package, the aim is to provide useful implementations and helper libraries to make building code generators as easy as possible. To facilitate the implementations outlined above, a few specific utilities start to emerge:</p>
<ul>
<li>Factory methods for Probes and Interrogations. It should be easy to make a Probe or Interrogation. The factory pattern makes sense here.<br />
<em>NOTE: This might actually be best suited for crushinator.framework</em><br />
Some convenience features would include:</li>
<ul>
<li>passing a callable to dynamically set the validate() method</li>
<li>generating a template variable name (&#8216;short name&#8217;) for the  probe automatically based on the label<em></em></li>
</ul>
<li>Implementation of validating Probes for many useful data types:</li>
<ul>
<li>Integer</li>
<li>Float</li>
<li>E-mail address</li>
<li>Python identifier</li>
<li>URI</li>
</ul>
<li>User Interface registration helper for Runners. There should be <em>automatic</em> registration for all of the stock UI implementations in crushinator.toolkit.</li>
<li>Gettext/reStructuredText parsing for labels, help, etc. Preferably one function that handles both tasks at once. <em>This may be rolled into the base classes and become transparent to the developer.</em></li>
<li>Logging &#8211; I&#8217;ve already begun to implement logging in both the crushinator.framework and crushinator.toolkit packages. I want to make that as streamlined as possible for the developer. The idea is to swallow as many exceptions as possible, returning &#8216;friendly&#8217; text to the user, but always log that something happened with as much detail as possible. I&#8217;m impressed with the way Python handles logging, and it&#8217;s ideal for systems like this.</li>
</ul>
<h1>Wrap Up</h1>
<p>I&#8217;m preparing to implement things as I&#8217;ve outlined in this document. I&#8217;m interested in hearing what others think about this plan; is there anything I&#8217;ve omitted? Are there any assumptions I&#8217;m making that aren&#8217;t accurate? Have I overlooked any potential problems with user interface implementations you&#8217;re interested in seeing developed?</p>
<p>Comments, corrections, discussion, questions&#8230; really any feedback would be greatly appreciated.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/lionfacelemonface.wordpress.com/558/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/lionfacelemonface.wordpress.com/558/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/lionfacelemonface.wordpress.com/558/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/lionfacelemonface.wordpress.com/558/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/lionfacelemonface.wordpress.com/558/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/lionfacelemonface.wordpress.com/558/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/lionfacelemonface.wordpress.com/558/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/lionfacelemonface.wordpress.com/558/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/lionfacelemonface.wordpress.com/558/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/lionfacelemonface.wordpress.com/558/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/lionfacelemonface.wordpress.com/558/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/lionfacelemonface.wordpress.com/558/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/lionfacelemonface.wordpress.com/558/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/lionfacelemonface.wordpress.com/558/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lionfacelemonface.wordpress.com&amp;blog=2073436&amp;post=558&amp;subd=lionfacelemonface&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://lionfacelemonface.wordpress.com/2011/10/08/rfc-crushinator-probeinterrogation-implementation-details-branching-questions-skipping-validation-etc/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/5683b763dd1f27ec0c045b66cba43ab0?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">jjmojojjmojo</media:title>
		</media:content>
	</item>
		<item>
		<title>I&#8217;m on that tweeting thing now! &#8211; and it&#8217;s already paying off (Diazo/Theme editor control panel in plone)</title>
		<link>http://lionfacelemonface.wordpress.com/2011/09/29/im-on-that-tweeting-thing-now-and-its-already-paying-off-diazotheme-editor-control-panel-in-plone/</link>
		<comments>http://lionfacelemonface.wordpress.com/2011/09/29/im-on-that-tweeting-thing-now-and-its-already-paying-off-diazotheme-editor-control-panel-in-plone/#comments</comments>
		<pubDate>Thu, 29 Sep 2011 12:25:53 +0000</pubDate>
		<dc:creator>jjmojojjmojo</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[plone diazo theme twitter]]></category>

		<guid isPermaLink="false">http://lionfacelemonface.wordpress.com/?p=544</guid>
		<description><![CDATA[I&#8217;m now officially on twitter, @jjmojojjmojo . Adding folks from the developer communities I&#8217;m involved with, and noticed a tweet from @optilude, mentioning this: http://www.screenr.com/b1Rs. I want to take a closer look at the UI later today, but it looks pretty great so far. This tweeing thing might be more fruitful than I ever thought.<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lionfacelemonface.wordpress.com&amp;blog=2073436&amp;post=544&amp;subd=lionfacelemonface&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m now officially on twitter, @jjmojojjmojo .</p>
<p>Adding folks from the developer communities I&#8217;m involved with, and noticed a tweet from @optilude, mentioning this: http://www.screenr.com/b1Rs. </p>
<p>I want to take a closer look at the UI later today, but it looks pretty great so far. This tweeing thing might be more fruitful than I ever thought.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/lionfacelemonface.wordpress.com/544/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/lionfacelemonface.wordpress.com/544/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/lionfacelemonface.wordpress.com/544/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/lionfacelemonface.wordpress.com/544/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/lionfacelemonface.wordpress.com/544/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/lionfacelemonface.wordpress.com/544/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/lionfacelemonface.wordpress.com/544/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/lionfacelemonface.wordpress.com/544/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/lionfacelemonface.wordpress.com/544/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/lionfacelemonface.wordpress.com/544/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/lionfacelemonface.wordpress.com/544/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/lionfacelemonface.wordpress.com/544/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/lionfacelemonface.wordpress.com/544/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/lionfacelemonface.wordpress.com/544/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lionfacelemonface.wordpress.com&amp;blog=2073436&amp;post=544&amp;subd=lionfacelemonface&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://lionfacelemonface.wordpress.com/2011/09/29/im-on-that-tweeting-thing-now-and-its-already-paying-off-diazotheme-editor-control-panel-in-plone/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/5683b763dd1f27ec0c045b66cba43ab0?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">jjmojojjmojo</media:title>
		</media:content>
	</item>
		<item>
		<title>Why Shooting for 100% Test Coverage is Important</title>
		<link>http://lionfacelemonface.wordpress.com/2011/09/24/why-shooting-for-100-test-coverage-is-important/</link>
		<comments>http://lionfacelemonface.wordpress.com/2011/09/24/why-shooting-for-100-test-coverage-is-important/#comments</comments>
		<pubDate>Sat, 24 Sep 2011 12:10:03 +0000</pubDate>
		<dc:creator>jjmojojjmojo</dc:creator>
				<category><![CDATA[python]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[testing python pyramid wsgi nose coverage]]></category>

		<guid isPermaLink="false">http://lionfacelemonface.wordpress.com/?p=531</guid>
		<description><![CDATA[100% test coverage may be impossible, but this week I&#8217;ve found that striving for it has many benefits. I&#8217;ve been working on a new project that&#8217;s based on the Pyramid framework. The framework creators claim 99% test coverage. The other developer and I thought that was pretty great, and decided that setting a similar goal [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lionfacelemonface.wordpress.com&amp;blog=2073436&amp;post=531&amp;subd=lionfacelemonface&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>100% test coverage may be impossible, but this week I&#8217;ve found that striving for it has many benefits.</p>
<p>I&#8217;ve been working on a new project that&#8217;s based on the <a href="https://docs.pylonsproject.org/projects/pyramid/1.2/index.html">Pyramid</a> framework. The framework creators claim 99% test coverage. The other developer and I thought that was pretty great, and decided that setting a similar goal for our project would be worth the trouble. </p>
<p>I don&#8217;t think I need to go into intimate details about why tests of the various forms (unit, functional, integration, systems, load, stress) are important (I will if somebody asks :)). I think it can be summed it up in a simple truth: </p>
<blockquote><p>
In the long run, writing tests saves you time, and makes your code stronger.
</p></blockquote>
<p>I&#8217;ve spent most of this week refactoring tests in an effort to get to that mythical 100% goal, and I&#8217;ve found the experience to be extremely fruitful.</p>
<p><strong>Note: we&#8217;re using <a href="http://readthedocs.org/docs/nose/en/latest/">Nose</a> to run the tests and generate the coverage report.</strong></p>
<h3>I&#8217;ve learned a lot.</h3>
<p>I know much more about how Pyramid works (and it&#8217;s been refreshingly straight-forward). I&#8217;ve become a fan of <a href="http://webtest.pythonpaste.org/en/latest/index.html">WebTest</a>, and intimately familiar with <a href="http://docs.webob.org/en/latest/reference.html">WebOb</a>. All of this was a side-effect of the need to break down the tests into units that could be adequately run at the various levels, to touch all of the code. I had to dig into the APIs for each package to figure out how to test certain things, and again, at each level. Luckily, these particular packages are very well documented, and their code is relatively clean and easy to understand, so the process was much less arduous than it might have been given a different toolset.</p>
<p>I had to dig into protocol-level aspects of the application. Some of more opaque parts of Pyramid, WebOb, WSGI and HTTP required closer inspection. After this week, I know so much more about multi-part form encoding (MIME encoding technically, <a href="http://www.ietf.org/rfc/rfc2046.txt">RFC 2046</a>), file uploads (<a href="http://www.ietf.org/rfc/rfc1867">RFC 1867</a>), and perhaps more importantly, how Pyramid and WebOb implement these concepts. </p>
<p>The separation between unit and functional testing became more obvious, and more than ever, I appreciate the need for them to be separate. </p>
<h3>The code&#8217;s gotten better.</h3>
<p> When faced with a line of code that wasn&#8217;t covered, I had to figure out why. The answer to &#8216;why&#8217; forced me to re-evaluate the way the tests were written and think critically about how the application is structured. </p>
<p>This is, of course, in addition to the expected benefit of catching untested code. That untested code manifested in several ways:</p>
<ul>
<li>
I found a handful of use cases that just weren&#8217;t being tested. </li>
<li>I found some <em>edge</em> cases that were not being tested (accounted for in the code, but not being executed by the tests)</li>
<li>I found API interfaces that were implemented but never executed.</li>
<li>I found tests that were passing, but not testing what they were supposed to test.</li>
<li>I found one specific edge case that was accounted for in the code, but it turned out to be effectively <em>impossible</em>.</li>
</ul>
<p>What this amounts to is exposure of: false assumptions, bad tests, and some potential design flaws. In fixing and/or addressing them, the code has attained a new level of solidity. And even the tests themselves are a lot better than they were last week. </p>
<h3>In summation&#8230;</h3>
<p>I&#8217;d have to say working toward 100% coverage, even if you can&#8217;t get there, is more than worth the effort. Just exposing one or two bad tests or use cases that were neglected is worth it, and I gained a lot more than that from this endeavor.</p>
<h3>Epilogue</h3>
<p><em>I started writing this yesterday morning, when I had one or two stubborn lines of code that kept eluding coverage. As of about 6 o&#8217;clock last night, I&#8217;ve actually achieved my goal of <strong>100% test coverage</strong>. I need to do a bit more digging to make sure this isn&#8217;t a misleading number, but given what&#8217;s gone into getting here, I feel pretty confident that even if it&#8217;s not as impressive an achievement as it sounds, that it is a reflection of a very good test suite, and a well put together application.</em></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/lionfacelemonface.wordpress.com/531/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/lionfacelemonface.wordpress.com/531/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/lionfacelemonface.wordpress.com/531/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/lionfacelemonface.wordpress.com/531/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/lionfacelemonface.wordpress.com/531/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/lionfacelemonface.wordpress.com/531/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/lionfacelemonface.wordpress.com/531/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/lionfacelemonface.wordpress.com/531/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/lionfacelemonface.wordpress.com/531/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/lionfacelemonface.wordpress.com/531/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/lionfacelemonface.wordpress.com/531/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/lionfacelemonface.wordpress.com/531/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/lionfacelemonface.wordpress.com/531/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/lionfacelemonface.wordpress.com/531/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lionfacelemonface.wordpress.com&amp;blog=2073436&amp;post=531&amp;subd=lionfacelemonface&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://lionfacelemonface.wordpress.com/2011/09/24/why-shooting-for-100-test-coverage-is-important/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/5683b763dd1f27ec0c045b66cba43ab0?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">jjmojojjmojo</media:title>
		</media:content>
	</item>
		<item>
		<title>Crushinator: The PasteScript Replacement &#8211; Progress!</title>
		<link>http://lionfacelemonface.wordpress.com/2011/08/02/crushinator-the-pastescript-replacement-progress/</link>
		<comments>http://lionfacelemonface.wordpress.com/2011/08/02/crushinator-the-pastescript-replacement-progress/#comments</comments>
		<pubDate>Tue, 02 Aug 2011 12:52:25 +0000</pubDate>
		<dc:creator>jjmojojjmojo</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://lionfacelemonface.wordpress.com/?p=529</guid>
		<description><![CDATA[I&#8217;ve got some good docs written up and posted over at http://code.google.com/p/lionfacelemonface/wiki/CrushinatorDesign. I&#8217;m interested in hearing others thoughts on the requirements, approach, design, etc. It&#8217;s a work in progress; I feel pretty good about most of the decisions made but everything is up for discussion. I&#8217;m currently working on the implementation details. I also presented [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lionfacelemonface.wordpress.com&amp;blog=2073436&amp;post=529&amp;subd=lionfacelemonface&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve got some good docs written up and posted over at <a href="http://code.google.com/p/lionfacelemonface/wiki/CrushinatorDesign">http://code.google.com/p/lionfacelemonface/wiki/CrushinatorDesign</a>. </p>
<p>I&#8217;m interested in hearing others thoughts on the requirements, approach, design, etc. </p>
<p>It&#8217;s a work in progress; I feel pretty good about most of the decisions made but everything is up for discussion. I&#8217;m currently working on the implementation details.</p>
<p>I also presented on this project at the last TriZPUG meeting. For a more general, brief overview (&#8216;elevator pitch&#8217; of sorts), see <a href="http://trizpug.org/Members/jj/crushinator-presentation-07282011/crushinator-presentation.pdf" title="my slides (PDF)">my slides (PDF)</a>.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/lionfacelemonface.wordpress.com/529/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/lionfacelemonface.wordpress.com/529/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/lionfacelemonface.wordpress.com/529/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/lionfacelemonface.wordpress.com/529/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/lionfacelemonface.wordpress.com/529/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/lionfacelemonface.wordpress.com/529/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/lionfacelemonface.wordpress.com/529/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/lionfacelemonface.wordpress.com/529/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/lionfacelemonface.wordpress.com/529/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/lionfacelemonface.wordpress.com/529/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/lionfacelemonface.wordpress.com/529/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/lionfacelemonface.wordpress.com/529/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/lionfacelemonface.wordpress.com/529/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/lionfacelemonface.wordpress.com/529/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lionfacelemonface.wordpress.com&amp;blog=2073436&amp;post=529&amp;subd=lionfacelemonface&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://lionfacelemonface.wordpress.com/2011/08/02/crushinator-the-pastescript-replacement-progress/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/5683b763dd1f27ec0c045b66cba43ab0?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">jjmojojjmojo</media:title>
		</media:content>
	</item>
		<item>
		<title>Dump single config file from your uber buildout</title>
		<link>http://lionfacelemonface.wordpress.com/2011/07/13/dump-single-config-file-from-your-uber-buildout/</link>
		<comments>http://lionfacelemonface.wordpress.com/2011/07/13/dump-single-config-file-from-your-uber-buildout/#comments</comments>
		<pubDate>Thu, 14 Jul 2011 04:39:11 +0000</pubDate>
		<dc:creator>jjmojojjmojo</dc:creator>
				<category><![CDATA[buildout]]></category>
		<category><![CDATA[plone]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://lionfacelemonface.wordpress.com/?p=520</guid>
		<description><![CDATA[We have the annotate command that buildout provides. It&#8217;s great, but what I wish it did was resolve all of the values and give me a usable buildout.cfg that I could potentially reuse, diff, etc. As your buildouts get more modular and flexible, a tool like that becomes more and more important. Using buildout.extensionscripts, I [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lionfacelemonface.wordpress.com&amp;blog=2073436&amp;post=520&amp;subd=lionfacelemonface&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>We have the <code>annotate</code> command that buildout provides. It&#8217;s great, but what I wish it did was resolve all of the values and give me a usable <code>buildout.cfg</code> that I could potentially reuse, diff, etc. As your buildouts get more modular and flexible, a tool like that becomes more and more important.</p>
<p>Using <a href="http://pypi.python.org/pypi/buildout.extensionscripts/1.0">buildout.extensionscripts</a>, I whipped up a quick proof of concept, that I think might be helpful. I should create a full-blown buildout extension for this someday (prodding is welcome).</p>
<p>The file and an example buildout using it are up on my <a href="http://code.google.com/p/lionfacelemonface/source/browse/trunk/cfg-dump">google code SVN repo</a>. </p>
<p>Here&#8217;s how you can try it out (make sure you have the usual pre-requisites for zc.buildout: python, gcc, etc):<br />
<code>
<pre>
$ svn checkout http://lionfacelemonface.googlecode.com/svn/trunk/cfg-dump
$ cd cfg-dump
$ python bootstrap.py
$ bin/buildout -c dump.cfg
</pre>
<p></code></p>
<p>Don&#8217;t worry, the buildout will exit before anything is actually installed. It will download <code>templer.core</code>, mr.developer, and the one or two recipes that are used in the buildout files. I wanted the dumping process to be as quick and painless as possible, but from what I can tell it&#8217;s unavoidable. Buildout will install requisite recies. <code>mr.developer</code> will download <code>templer.core</code>. I wanted to test how my code got along with another buildout extension, and I used mr.developer, and used <code>templer.core</code> since I had it in mind. It seems to play nice but because extensions are evaluated after the config is parsed, but before the installation, I can&#8217;t really stop other extensions from doing things if buildout invokes them first.</p>
<p>If you peek into the buildout itself, you&#8217;ll notice its kind of contrived and somewhat odd. It&#8217;s a mishmash of buildouts I&#8217;m currently putting together. It&#8217;s not intended to be run directly, but it will probably not fail if you did.</p>
<p>Be careful using the extension on a buildout that you care about; it&#8217;s not destructive, but it could use more checks for things like existing dump files and such. It&#8217;s also not extensively tested.</p>
<p>Once the buildout is complete (shouldn&#8217;t take more than a 20 seconds or so), you will see a file called <code>buildout-dump.cfg</code>. Ta-da!</p>
<p>The formatting of the dumped config leaves a little bit to be desired (buildout section is not at the top, no comments injected or preserved), but it should  be runnable. It makes an assumption or two (sections/options prefixed with &#8216;_&#8217; are skipped, computed values are skipped if they can be detected, etc), so YYMV (please let me know of specific recipes you have trouble with). I consider this a bonus feature at this point, but reliable round-tripping would be pretty cool.</p>
<p>Here are the options available (no configuration is necessary out of the box):</p>
<ul>
<li><strong>dump-skip-empty</strong> &#8211; boolean. If true, and a value is empty (or false I think, need to verify that &#8211; it may not be what we want), omit it from the output instead of outputting <code>param = </code>. Defaults to false.</li>
<li><strong>dump-file</strong> &#8211; the path to a file to put the dump into (the contents will be overwritten!). If this option is set but empty, no file will be written. Defaults to <code>${buildout:directory}/buildout-dump.cfg</code></li>
<li><strong>dump-eject</strong> &#8211; boolean, if true, the buildout will terminate after dumping the cfg file. If false, it will proceed to complete the buildout.</li>
</ul>
<p>The extension also looks at the verbosity level switch (<code>-v</code>). If present, the config file will be dumped to stdout as well as being written.</p>
<p>So please, try it out, let me know how it could be improved (I&#8217;ve only scratched the surface of the zc.buildout internals), if you find bugs, etc. </p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/lionfacelemonface.wordpress.com/520/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/lionfacelemonface.wordpress.com/520/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/lionfacelemonface.wordpress.com/520/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/lionfacelemonface.wordpress.com/520/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/lionfacelemonface.wordpress.com/520/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/lionfacelemonface.wordpress.com/520/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/lionfacelemonface.wordpress.com/520/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/lionfacelemonface.wordpress.com/520/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/lionfacelemonface.wordpress.com/520/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/lionfacelemonface.wordpress.com/520/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/lionfacelemonface.wordpress.com/520/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/lionfacelemonface.wordpress.com/520/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/lionfacelemonface.wordpress.com/520/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/lionfacelemonface.wordpress.com/520/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lionfacelemonface.wordpress.com&amp;blog=2073436&amp;post=520&amp;subd=lionfacelemonface&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://lionfacelemonface.wordpress.com/2011/07/13/dump-single-config-file-from-your-uber-buildout/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/5683b763dd1f27ec0c045b66cba43ab0?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">jjmojojjmojo</media:title>
		</media:content>
	</item>
		<item>
		<title>WSGI and Paste Deploy: The Bare Necessities</title>
		<link>http://lionfacelemonface.wordpress.com/2011/03/30/wsgi-and-paste-deploy-the-bare-necessities/</link>
		<comments>http://lionfacelemonface.wordpress.com/2011/03/30/wsgi-and-paste-deploy-the-bare-necessities/#comments</comments>
		<pubDate>Wed, 30 Mar 2011 22:03:37 +0000</pubDate>
		<dc:creator>jjmojojjmojo</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://lionfacelemonface.wordpress.com/?p=499</guid>
		<description><![CDATA[Using paster to create layouts for various types of projects is great, especially when you need to get up and running quickly. The downside, is that that sometimes the hand-holding, even along with some amazing documentation, doesn&#8217;t give you the whole story. I&#8217;m in the planning stages for a new project, and although I&#8217;m still [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lionfacelemonface.wordpress.com&amp;blog=2073436&amp;post=499&amp;subd=lionfacelemonface&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Using paster to create layouts for various types of projects is great, especially when you need to get up and running quickly. The downside, is that that sometimes the hand-holding, even along with some <a href="http://docs.pylonsproject.org/docs/pyramid.html">amazing documentation</a>, doesn&#8217;t give you the whole story.</p>
<p>I&#8217;m in the planning stages for a new project, and although I&#8217;m still on the fence as to what we&#8217;ll use to develop it, I&#8217;m sweet on <a href="http://pylonsproject.org/">Pyramid</a>. However, I&#8217;m also a WSGI n00b, and haven&#8217;t used the paste stack for anything beyond building plone buildouts and archetypes boilerplate. That means that <a href="http://pythonpaste.org/deploy/">PasteDeploy</a> is not in my playbook. There are some great <a href="http://pythonpaste.org/script/">PasteScript</a> templates provided by the Pyramid project to get started quickly, but I didn&#8217;t see any good explanation of <em>why</em> the files were created, and how they actually worked, even in the super-awesome Pyramid docs.</p>
<p><em>Disclaimer: I didn&#8217;t look that hard. :P</em></p>
<p>So, to ease my pain, I got back to basics. I started with a fresh egg, the PasteDeploy documentation and <a href="http://www.python.org/dev/peps/pep-0333/">PEP 333</a>. I came up with the absolute, bare-minimum code, setup, and config necessary to produce a functioning (albeit boring) WSGI application, and the necessary glue to run it via PasteDeploy.</p>
<p>If you&#8217;re the type that doesn&#8217;t need much explaination and just want to look at the code, go <a href="http://code.google.com/p/lionfacelemonface/source/browse/trunk/pastedeploy-minimal/trunk">right to the source</a> (<a href="http://code.google.com/p/lionfacelemonface/source/checkout">checkout instructions</a>)</p>
<h2>Prerequisites</h2>
<p>You&#8217;ll need, at minimum, a modern version of python (I used the 2.6 version that ships with Debian Squeeze), <a href="http://pypi.python.org/pypi/virtualenv">virtualenv</a>, and some way to <a href="http://pypi.python.org/pypi/pip">install virtualenv</a>. </p>
<p>I&#8217;ll leave getting virtualenv installed as an exercise for the reader :)</p>
<p>I&#8217;m going to assume you&#8217;re in a Linux environment in the few times we need to interact with the operating system. As far as I know, this should work on any operating system Python runs on, but as always, your mileage may vary.</p>
<h2>Environment</h2>
<p><code>
<pre>
$ virtualenv fun-minimal-wsgi
$ cd fun-minimal-wsgi
$ source bin/activate
(fun-minimal-wsgi)$ easy_install PasteScript PasteDeploy
</pre>
<p></code><br />
If for some reason you&#8217;re not familiar with virtualenv (and if you&#8217;re a python developer, you really should be, it&#8217;s great), these commands:</p>
<ol>
<li>Sets up a sandbox python environment (but will use system packages if they exist and haven&#8217;t been overridden locally)</li>
<li>Puts you into that environment</li>
<li>Installs PasteDeploy</li>
</ol>
<p>To get out of the environment, you can type <code>deactivate</code>:</p>
<p><code>
<pre>
(fun-minimal-wsgi)$ deactivate
</pre>
<p></code></p>
<h2>Bare-Minimum Egg</h2>
<p>We&#8217;ll store our WSGI code in a simple python egg, that is setuptools-enabled (pretty much the standard). This requires a couple of directories, and some special files (I based this off of output of the PasteScript &#8216;basic_namespace&#8217; template, for details about what this means, see <a href="http://peak.telecommunity.com/DevCenter/setuptools">the setuptools docs</a>).</p>
<p>Here&#8217;s what the layout looks like:<br />
<code>
<pre>
fun-minimal-wsgi\
--&gt; minimal\
-----&gt; minimal\
---------&gt; __init__.py
-----&gt; setup.py
</pre>
<p></code></p>
<p>Here&#8217;s a quick list of commands to create an empty version of that layout (assume that we&#8217;re still in the <code>fun-minimal-wsgi</code> directory):</p>
<p><code>
<pre>
(fun-minimal-wsgi)$ mkdir minimal
(fun-minimal-wsgi)$ touch minimal/setup.py
(fun-minimal-wsgi)$ mkdir minimal/minimal
(fun-minimal-wsgi)$ touch minimal/minimal/__init__.py
</pre>
<p></code></p>
<p>The basic idea here is we have an egg source directory <code>minimal</code>, containing a <code>setup.py</code> file, used to install and configure it, and a singular package, also called <code>minimal</code> (coincidentally&#8230; what it&#8217;s named is not a requirement of setuptools).</p>
<p>Now for the contents of <code>setup.py</code>:</p>
<p><code>
<pre>
from setuptools import setup, find_packages
import os

version = '1.0'

setup(name='minimal',
      version=version,
      description="",
      long_description="""
         Very, very minimal example of a WSGI application and middleware.
      """,
      # Get more strings from
      # http://pypi.python.org/pypi?:action=list_classifiers
      classifiers=[
        "Programming Language :: Python",
        ],
      keywords='',
      author='Josh Johnson',
      author_email='none of your beeswax@somehost.com',
      url='http://lionfacelemonface.wordpress.com',
      license='BSD',
      include_package_data=True,
      packages=['minimal',],
      zip_safe=False,
      install_requires=[
          'setuptools',
      ],
      entry_points="""
      """,
      )
</code></pre>
<p>This is, at its bare minimum, all you need to say you've got an egg. :)</p>
<p>You could run <code>python setup.py develop</code> and start using it in your virtual environment (but don't yet), not that it does anything yet :)</p>
<h2>Bare-Minimum WSGI App</h2>
<p>This code goes into <code>minimal/minimal/__init__.py</code>. This is right out of PEP 333, with a couple of changes to take out any logic, and show all of the possible ways to create a WSGI app, and WSGI middleware.</p>
<p><code>
<pre>
# simple_app and AppClass are right out of PEP 333
def simple_app(environ, start_response):
    """Simplest possible application object"""
    status = '200 OK'
    response_headers = [('Content-type', 'text/plain')]
    start_response(status, response_headers)
    return ['Hello world!\n']

class AppClass:
    """Produce the same output, but using a class

    (Note: 'AppClass' is the "application" here, so calling it
    returns an instance of 'AppClass', which is then the iterable
    return value of the "application callable" as required by
    the spec.

    If we wanted to use *instances* of 'AppClass' as application
    objects instead, we would have to implement a '__call__'
    method, which would be invoked to execute the application,
    and we would need to create an instance for use by the
    server or gateway.
    """

    def __init__(self, environ, start_response):
        self.environ = environ
        self.start_response = start_response

    def __iter__(self):
        status = '200 OK'
        response_headers = [('Content-type', 'text/plain')]
        self.start_response(status, response_headers)
        yield "Hello world!\n"

# not in PEP 333, but mentioned in the comments to AppClass
class AlternateAppClass:
    def __call__(self, environ, start_response):
        status = '200 OK'
        response_headers = [('Content-type', 'text/plain')]
        start_response(status, response_headers)
        return ['Hello world!\n']

class MinimalMiddleware:
    """
    Bare-minimum, doesn't do anything at all, middleware.
    """
    def __init__(self, application):
        self.application = application

    def __call__(self, environ, start_response):
        return self.application(environ, start_response)

class SimpleMiddleware:
    """
    Takes a prefix, and appends it to each line in the response.
    """
    def __init__(self, application, prefix):
        self.application = application
        self.prefix = prefix

    def __call__(self, environ, start_response):
        response = self.application(environ, start_response)
        return ['%s %s' % (self.prefix, s) for s in response]
</pre>
<p></code><br />
Read PEP 333 for full explainations, but here's the gist:</p>
<p><strong>WSGI apps</strong> are super simple. At minimum, they're a simple function (<code>simple_app</code>), and at their most complex, a simple that implements the iterator protocol (<code>AppClass</code>). The function, or the <code>__call__</code> method (in the case of <code>AppClass</code>, <code>__init__</code> is standing in for <code>__call__</code>) take a '<code>start_response</code>' callable and a dictionary with environment information (think CGI variables). They then use the callable to set the necessary response code and headers (in our case '200 OK' and 'Content-Type: text/plain'), then return an iterable, where each member is a line in the output to send to the browser.</p>
<p>The <code>AppClass</code> example is probably a good way to implement a WSGI app class, but it's not the simplest. What's required is a callable, so any object that implements a <code>__call__</code> method and returns an iterable. <code>AppClass</code> implements the iterator protocol and short-circuits the need for instantiation. For the sake of clarity and throughness, I added the <code>AlternateAppClass</code> class to illustrate this. </p>
<p>We'll see the functional difference later when it's being wired up for use by PasteDeploy.</p>
<p><strong>WSGI Middleware</strong> works like a bucket-brigade. An application object is passed from middleware class to middleware class until all are called. The middleware is instantiated with an application object, and when called is expected to return a response. I've included a simplified minimal implementation (<code>MinimalMiddleware</code>), and an implementation that manipulates the request before returning it (SimpleMiddleware).</p>
<p><strong>Note:</strong> In the event that you're trying to jump ahead, this code might not run out of the box. I'm allowing a configuration option to be passed to <code>SimpleMiddleware</code>, and since it's required, it would fail.</p>
<h2>PasteDeploy Configuration - Application</h2>
<p>Now for the server part. You'll need to call this file something useful and put it somewhere you can get to (I've put it in the egg, <code>minimal/minimal.ini</code>).<br />
<code>
<pre>
[app:main]
use = egg:minimal

[server:main]
use = egg:Paste#http
host = 0.0.0.0
port = 6543
</pre>
<p></code></p>
<p>This does two things. It tells PasteDeploy to look in the egg called 'minimal' for an 'app-factory' <em>entry point</em> called <em>main</em> (more on this in a bit). It then configures the server.</p>
<h2>Glue  - Application</h2>
<p>Before we can run the server we have to do three things: create an application factory, add the entry point specified in <code>minimal.ini</code>, and then install the egg.</p>
<p>The factory is simply a callable that returns a WSGI application. We'll add this to <code>minimal/minimal/__init__.py</code>.</p>
<p><code>
<pre>
...
def main(global_config, **settings):
    # settings comes from paste deploy, whatever values were in the section of the
    # deployment config file
    if settings.get('use_class', False):
        return AppClass
    elif settings.get('use_alt_class', False):
        return AlternateAppClass()
    else:
        return simple_app
...
</pre>
<p></code></p>
<p>An application factory, for PasteDeploy, is a callable that takes a config object (the contents of the whole config file), and any number of keyword arguments, which correspond to the other options mentioned in the config file (in our case, had we put <code>toggle = True</code> in <code>[app:main]</code>, that argument would be passed along to our factory)</p>
<p>I've added a few config options that the factory can respond to, to make it easy to try out the different ways of implementing the same WSGI application.</p>
<p>Now for the entry point, the real glue that ties PasteDeploy with our code.</p>
<p>We'll add the following to <code>setup.py</code> in the call to <code>setup()</code>:<br />
<code>
<pre>
...
      entry_points="""
      [paste.app_factory]
      main = minimal:main
      """,
...
</pre>
<p></code></p>
<p>Note that <code>minimal:main</code> maps directly to the <code>main()</code> function we created as our application factory. Had we named it something else, or if we wanted to provide multiple app factories for whatever reason, we could specify them one after the other, the name of the entry point (what we'd reference in <code>minimal.ini</code>), and a path to the function. The name <code>main</code> is just the default, and used as a convention/convienence. Here are some other examples:</p>
<ul>
<li>Put <code>use = egg:minimal#otherapp</code> in <code>minimal.ini</code>, and <code>otherapp = minimal:some_other_function_in_init</code> in <code>setup.py</code></li>
<li>Put <code>use = egg:minimal#main</code> in <code>minimal.ini</code>, and <code>main = minimal.some_package:other_function</code> in <code>setup.py</code></li>
<li>Put <code>use = egg:minimal#other</code> in <code>minimal.ini</code>, and <code>main = minimal.some_package.package_deeper:other_function</code> in <code>setup.py</code></li>
</ul>
<p>You get the idea.</p>
<h2>Install the Egg</h2>
<p>We'll want to install this in <em>develop</em> mode, so changes to the code will be reflected as soon as they are made. </p>
<p><strong>Note:</strong> entry points are only updated when the egg is installed, so you'll have to repeat this procedure every time you make a change to them in your egg.</p>
<p><em>Make sure you've activated your virtual environment before you do this!</em></p>
<p><code>
<pre>
(fun-minimal-wsgi)$ cd minimal
(fun-minimal-wsgi)$ python setup.py develop
(fun-minimal-wsgi)$ cd ..
</pre>
<p></code></p>
<h2>Starting The Server</h2>
<p>Now we should be able to start the server using the <code>paster serve</code> command.</p>
<p><code>
<pre>
(fun-minimal-wsgi)$ paster serve minimal/minimal.ini --reload
</pre>
<p></code></p>
<p>You can now navigate to http://127.0.0.1:6543 and you should see 'hello world'</p>
<p>Since we specified <code>--reload</code>, we should be able to make changes to the application code and <code>minimal.ini</code> and the server will reload for us (remember the note about entry points though).</p>
<h2>PasteDeploy Configuration - Middleware</h2>
<p>Things will change a little bit now:</p>
<p><code>
<pre>
[app:minimal_app]
use = egg:minimal

[filter:middleware]
use = egg:minimal
prefix = yikes:

[pipeline:main]
pipeline =
    middleware
    minimal_app
...
</pre>
<p></code></p>
<p>Here we changed the <code>[app:main]</code> heading to an easy-to-refer-to name. We then defined the middleware part (middleware is called 'filters' in PasteDeploy configs), and a <code>pipeline</code>, which links up the middleware and the application.</p>
<p>The middleware specified here is also called 'main'. </p>
<p>Note we've also specified a parameter to the middleware. If we didn't need to, we could have just used <code>egg:minimal</code> in the <code>[pipeline:main]</code> section, in replace of <code>[middleware]</code></p>
<p><strong>Note:</strong> This will break your running server instance, since we are referring to a filter factory that doesn't exist.</p>
<h2>Glue - Middleware</h2>
<p>Now to make the middleware work. We'll need to a new entry point to <code>minimal/setup.py</code>, so it now looks like this:</p>
<p><code>
<pre>
...
      entry_points="""
      [paste.app_factory]
      main = minimal:main

      [paste.filter_factory]
      main = minimal:middleware
      """,
...
</pre>
<p></code></p>
<p>Again, the name of the entry point is not significant, and <code>main</code> is used as the default so we don't have to specify it in the ini file.</p>
<p>Now for the filter factory in <code>minimal/minimal/__init__.py</code>:</p>
<p><code>
<pre>
...
# middleware factory for paste deploy
def middleware(global_config, **settings):
    prefix = settings.get('prefix', 'Booyeah:')

    def factory(app):
        return SimpleMiddleware(app, prefix)

    return factory
</pre>
<p></code></p>
<p>Here, we're using the <code>SimpleMiddleware</code> class, and finally utilizing/providing the <code>prefix</code> parameter. </p>
<p>Now we can re-install the egg (to get the new entry point) and restart the server.</p>
<p><code>
<pre>
(fun-minimal-wsgi)$ cd minimal
(fun-minimal-wsgi)$ python setup.py develop
(fun-minimal-wsgi)$ cd ..
(fun-minimal-wsgi)$ paster serve minimal/minimal.ini --reload
</pre>
<p></code></p>
<p>Now, visiting http://127.0.0.1:6543 will look a little different.</p>
<p>The same middleware can be added to the pipeline multiple times as well, try adding another copy of <code>middleware</code> to <code>[pipeline:main]</code> and see what happens. You could also add another section referencing the same filter factory, but call it something else, and use a different setting for the prefix. </p>
<h2>Conclusion</h2>
<p>So, this helped me understand how WSGI works, and how PasteDeploy is used to serve it. This is the foundation of what Pylons, TurboGears and Pyramid use, and so it's important to understand to help understand the mentality behind everything else these frameworks do. </p>
<p>Please feel free to comment! Any feedback is appreciated, this was primarily a learning exercise for me :)</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/lionfacelemonface.wordpress.com/499/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/lionfacelemonface.wordpress.com/499/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/lionfacelemonface.wordpress.com/499/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/lionfacelemonface.wordpress.com/499/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/lionfacelemonface.wordpress.com/499/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/lionfacelemonface.wordpress.com/499/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/lionfacelemonface.wordpress.com/499/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/lionfacelemonface.wordpress.com/499/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/lionfacelemonface.wordpress.com/499/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/lionfacelemonface.wordpress.com/499/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/lionfacelemonface.wordpress.com/499/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/lionfacelemonface.wordpress.com/499/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/lionfacelemonface.wordpress.com/499/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/lionfacelemonface.wordpress.com/499/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lionfacelemonface.wordpress.com&amp;blog=2073436&amp;post=499&amp;subd=lionfacelemonface&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://lionfacelemonface.wordpress.com/2011/03/30/wsgi-and-paste-deploy-the-bare-necessities/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/5683b763dd1f27ec0c045b66cba43ab0?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">jjmojojjmojo</media:title>
		</media:content>
	</item>
		<item>
		<title>Using the plone spinner animation in your ajax calls</title>
		<link>http://lionfacelemonface.wordpress.com/2011/02/24/using-the-plone-spinner-animation-in-your-ajax-calls/</link>
		<comments>http://lionfacelemonface.wordpress.com/2011/02/24/using-the-plone-spinner-animation-in-your-ajax-calls/#comments</comments>
		<pubDate>Thu, 24 Feb 2011 21:36:52 +0000</pubDate>
		<dc:creator>jjmojojjmojo</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://lionfacelemonface.wordpress.com/?p=493</guid>
		<description><![CDATA[Here&#8217;s a quick jquery snippet that you can use with modern versions of plone to do the little spinner status thingy during an ajax load: /*** * Overload default ajax methods and show a spinner graphic under the mouse pointer when things are loading ***/ $(function(){ var spinner = $(''); spinner.css({ display: 'none', position: 'absolute', [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lionfacelemonface.wordpress.com&amp;blog=2073436&amp;post=493&amp;subd=lionfacelemonface&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Here&#8217;s a quick jquery snippet that you can use with modern versions of plone to do the little spinner status thingy during an ajax load:</p>
<p><code></p>
<pre>
/***
 * Overload default ajax methods and show a spinner graphic under the mouse
   pointer when things are loading
 ***/

$(function(){
    var spinner = $('<img src="spinner.gif" />');
    spinner.css({
        display: 'none',
        position: 'absolute',
    });
    $('body').append(spinner);
});

jQuery.ajaxSetup({
  beforeSend: show_spinner,
  complete: hide_spinner,
});

function show_spinner(){
    $('img#ajax_waiting_spinner').css('display', 'block');

    $('body').mousemove(spinner_follow_mouse);

    /// position it ititially
    $('body').mousemove();
}

function hide_spinner(){
    $('img#ajax_waiting_spinner').css('display', 'none');

    // this prevents us from killing other
    // mousemove handlers that might be registered.
    $('body').unbind('mousemove', spinner_follow_mouse);
}

function spinner_follow_mouse(event){
     $('img#ajax_waiting_spinner').css('top', event.clientY+10).css('left', event.clientX+10);
}
</pre>
<p></code></p>
<p>Notes:</p>
<p>You&#8217;ll want to register this in your default profile if you&#8217;re installing this as part of a product. Add<br />
<code></p>
<pre>
	    &lt;javascript cacheable=&quot;True&quot; compression=&quot;none&quot; cookable=&quot;False&quot;
	              enabled=&quot;True&quot; id=&quot;ajax_spinner.js&quot;
	              expression=&quot;&quot;
	              inline=&quot;True&quot;/&gt;
</pre>
<p></code></p>
<p>to <code>jsregistry.xml</code>. I&#8217;ll leave adding it via the ZMI an an exercise for the reader :)</p>
<p>This works *almost* perfectly. If you don&#8217;t move the mouse after initialting an ajax call via a click, the mousemove event never fires and you don&#8217;t see the waiting graphic.</p>
<p>YYMV, could be optimized (especially the way I&#8217;m registering the js), I probably shouldn&#8217;t be using <code>$()</code> (no trouble yet *shrug*), etc, etc. Just a quick note so I don&#8217;t forget and so I can get some feedback on the approach.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/lionfacelemonface.wordpress.com/493/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/lionfacelemonface.wordpress.com/493/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/lionfacelemonface.wordpress.com/493/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/lionfacelemonface.wordpress.com/493/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/lionfacelemonface.wordpress.com/493/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/lionfacelemonface.wordpress.com/493/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/lionfacelemonface.wordpress.com/493/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/lionfacelemonface.wordpress.com/493/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/lionfacelemonface.wordpress.com/493/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/lionfacelemonface.wordpress.com/493/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/lionfacelemonface.wordpress.com/493/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/lionfacelemonface.wordpress.com/493/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/lionfacelemonface.wordpress.com/493/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/lionfacelemonface.wordpress.com/493/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lionfacelemonface.wordpress.com&amp;blog=2073436&amp;post=493&amp;subd=lionfacelemonface&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://lionfacelemonface.wordpress.com/2011/02/24/using-the-plone-spinner-animation-in-your-ajax-calls/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/5683b763dd1f27ec0c045b66cba43ab0?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">jjmojojjmojo</media:title>
		</media:content>
	</item>
		<item>
		<title>Custom Add&#8230; Menu Action</title>
		<link>http://lionfacelemonface.wordpress.com/2011/02/23/custom-add-menu-action/</link>
		<comments>http://lionfacelemonface.wordpress.com/2011/02/23/custom-add-menu-action/#comments</comments>
		<pubDate>Wed, 23 Feb 2011 16:27:48 +0000</pubDate>
		<dc:creator>jjmojojjmojo</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://lionfacelemonface.wordpress.com/?p=489</guid>
		<description><![CDATA[I should expand on this more, but here&#8217;s a quick note about something I just did in a project I&#8217;m working on that may help some other people: You know that Add&#8230; menu in the green bar when you have edit privileges on a page (I think the actual permission is &#8216;Modify portal content&#8217;)? Have [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lionfacelemonface.wordpress.com&amp;blog=2073436&amp;post=489&amp;subd=lionfacelemonface&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>I should expand on this more, but here&#8217;s a quick note about something I just did in a project I&#8217;m working on that may help some other people:</p>
<p>You know that Add&#8230; menu in the green bar when you have edit privileges on a page (I think the actual permission is &#8216;Modify portal content&#8217;)?</p>
<p>Have you ever wanted to replace the default call to portal_factory, via the createObject script with something else? Maybe a custom browser view that will create an object or give the user something else to do? (in my case it was &#8216;create a contact&#8217; or &#8216;associate some existing contacts&#8217;).</p>
<p>Trying to use the five  tag in zcml doesn&#8217;t work (it does work, however, to add new items to the display menu).</p>
<p>This does (at least in plone 4.0+, should also apply to 3.5). Given a standard plone egg-based product called my.product:</p>
<p><strong>In <code>my/product</code></strong>, add the following:</p>
<ul>
<li><code>overrides.zcml</code></li>
<li>a menu module, I used a package, you can just use <code>menu.py</code></li>
</ul>
<p><strong>In <code>overrides.zcml</code></strong>:</p>
<p><code></p>
<pre>
&lt;configure
    xmlns=&quot;http://namespaces.zope.org/zope&quot;
    xmlns:five=&quot;http://namespaces.zope.org/five&quot;
    xmlns:i18n=&quot;http://namespaces.zope.org/i18n&quot;
    xmlns:gs=&quot;http://namespaces.zope.org/genericsetup&quot;
    xmlns:browser=&quot;http://namespaces.zope.org/browser&quot;
    i18n_domain=&quot;blitzen.services&quot;&gt;

   &lt;browser:menu
    id=&quot;plone_contentmenu_factory&quot;
    title=&quot;The 'add' menu - allows the user to add new content items in the context&quot;
    class=&quot;.menu.MyFactoriesMenu&quot;
    /&gt;

&lt;/configure&gt;
</pre>
<p></code></p>
<p><strong>In <code>menu.py</code>:</strong></p>
<p><code></p>
<pre>

from plone.app.contentmenu.menu import FactoriesMenu, FactoriesSubMenuItem

class MyFactoriesMenu(FactoriesMenu):
    &quot;&quot;&quot;
    Overload factories menu to do fancy stuff with adding new contacts
    &quot;&quot;&quot;

    def getMenuItems(self, context, request):
        # menuitems is a list of tal-friendly dictionaries
        menuitems = super(MyFactoriesMenu, self).getMenuItems(context, request)

        # we only want to pull these contact switcheroo shenanagians
        # if we're on a workspace object
        if context.portal_type != 'Workspace':
            return menuitems

        new_items = []

        for item in menuitems:
            if item.get('id', False) == 'Contact':
                new_item = item.copy()
                new_item['action'] = &quot;%s/@@new_contact&quot; % (context.absolute_url())
                new_items.append(new_item)
            else:
                new_items.append(item)

        return new_items
</pre>
<p></code></p>
<p>There you go. Restart Zope and it should be using your menu items class instead of Plone&#8217;s. You can do whatever you want in there, change the order of the items, remove some, inject new ones, etc. See <code>plone.app.contentmenu.menu</code>&#8216;s <code>configure.zcml</code> file for more explaination, and the <code>FactoriesMenu</code> class for more information about what the list of dictionaries returned by <code>getMenuItems</code> is expecting. </p>
<p>One note: menus are aggressively cached in plone, so you might have to restart the app server where you&#8217;d normally just use <code>plone.reload</code>. Enjoy!</p>
<p>One other note: there are adapters you can override to do full-blown customizations. This approach seems a bit weird to me but I think it&#8217;s sound for the use case (I just want to override the createObject link in the menu).</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/lionfacelemonface.wordpress.com/489/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/lionfacelemonface.wordpress.com/489/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/lionfacelemonface.wordpress.com/489/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/lionfacelemonface.wordpress.com/489/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/lionfacelemonface.wordpress.com/489/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/lionfacelemonface.wordpress.com/489/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/lionfacelemonface.wordpress.com/489/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/lionfacelemonface.wordpress.com/489/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/lionfacelemonface.wordpress.com/489/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/lionfacelemonface.wordpress.com/489/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/lionfacelemonface.wordpress.com/489/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/lionfacelemonface.wordpress.com/489/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/lionfacelemonface.wordpress.com/489/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/lionfacelemonface.wordpress.com/489/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lionfacelemonface.wordpress.com&amp;blog=2073436&amp;post=489&amp;subd=lionfacelemonface&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://lionfacelemonface.wordpress.com/2011/02/23/custom-add-menu-action/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/5683b763dd1f27ec0c045b66cba43ab0?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">jjmojojjmojo</media:title>
		</media:content>
	</item>
		<item>
		<title>Profiling Plone 4 (well, profiling Zope while &gt;running&lt; Plone 4)</title>
		<link>http://lionfacelemonface.wordpress.com/2011/02/08/profiling-plone-4-well-profiling-zope-while-running-plone-4/</link>
		<comments>http://lionfacelemonface.wordpress.com/2011/02/08/profiling-plone-4-well-profiling-zope-while-running-plone-4/#comments</comments>
		<pubDate>Wed, 09 Feb 2011 02:46:57 +0000</pubDate>
		<dc:creator>jjmojojjmojo</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://lionfacelemonface.wordpress.com/?p=463</guid>
		<description><![CDATA[I mentioned in my last post that RelStorage is the new cool, and I lamented briefly about how my application shouldn&#8217;t suck, but it does because of something I can&#8217;t really figure out, because none of the profiling tools of yore work with the newest zope and, by the transitive property of major suckage, they [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lionfacelemonface.wordpress.com&amp;blog=2073436&amp;post=463&amp;subd=lionfacelemonface&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>I mentioned in my last post that RelStorage is the new cool, and I lamented briefly about how my application shouldn&#8217;t suck, but it does because of something I can&#8217;t really figure out, because none of the profiling tools of yore work with the newest zope and, by the transitive property of major suckage, they don&#8217;t work with newer Plone (specifically, plone 4.0) either.</p>
<p>So I consolidated my code, optimized the crap out of it,  checked out the trunk of ZCatalog (hastily following <a href="http://permalink.gmane.org/gmane.comp.web.zope.plone.devel/25711">some new developments</a>) and sat down to wrap python&#8217;s cProfiler around my browser view, in a vain attempt at trying to make some sense out of my issue (I&#8217;ll probably post about it in detail soon, I&#8217;m still tinkering)</p>
<p>Anyway, after some trial and error, I came across a feature of Zope that I didn&#8217;t know existed: it has a built-in profiler. :P</p>
<p>Here&#8217;s how to turn it on in a typical buildout (this is untested, so please, if you try it out let me know if you run into any weirdness):<br />
<code></p>
<pre>
[buildout]
parts =
    instance

extends = 

http://dist.plone.org/release/4.1-latest/versions.cfg

find-links =

http://dist.plone.org/release/4.1-latest

http://pypi.blitzen.unc.edu

http://dist.plone.org/thirdparty

[instance]
recipe = plone.recipe.zope2instance
user = admin:secret
http-address = 1040
debug-mode = on
verbose-security = on
blob-storage = var/blobstorage

eggs =
    Zope2
    Plone
    ${buildout:eggs}

environment-vars =
    PROFILE_PUBLISHER 1

zope-conf-additional =
    publisher-profile-file ${buildout:directory}/profile.dat
</pre>
<p></code></p>
<p>The important part would be the <code>environment-vars</code> and <code>zope-conf-additional</code> bits. I&#8217;m not sure if the environment var is necessary or not. </p>
<p>This gives you a new tab under Debug Settings in the ZMI Control Panel. </p>
<p>So you&#8217;d navigate to http://127.0.0.1:1040/Control_Panel/manage_main, and you&#8217;d see something like this:</p>
<p><a href="http://lionfacelemonface.files.wordpress.com/2011/02/screen-shot-1.png"><img src="http://lionfacelemonface.files.wordpress.com/2011/02/screen-shot-1.png?w=912&#038;h=432" alt="" title="screen shot 1" width="912" height="432" class="aligncenter size-full wp-image-479" /></a></p>
<p>Clicking on the  &#8216;Debug Information&#8217; link brings you to a page with a couple of tabs at the top. Clicking on the &#8220;Profiling&#8221; tab, brings up this interface: (assuming you&#8217;ve clicked around your plone site a bit)</p>
<p><a href="http://lionfacelemonface.files.wordpress.com/2011/02/screen-shot-2.png"><img src="http://lionfacelemonface.files.wordpress.com/2011/02/screen-shot-2.png?w=957&#038;h=620" alt="" title="screen shot 2" width="957" height="620" class="aligncenter size-full wp-image-482" /></a></p>
<p>So this is really great. I now have the full power of python&#8217;s built-in profiling tools right there in Zope. I can use this interface to try to identify issues, or use the <a href="http://docs.python.org/library/profile.html">pstats</a> module to analyze the profiler.dat file. </p>
<p>The downside is that nothing jumped out at me when I looked at the results after running my slow, memory-eating code. But hey, what I said before about Plone not having profiling is now totally False! I think I just need to get more familair with the results and processing them. Maybe something will shake out eventually.</p>
<p>One caveat: I&#8217;m not 100% sure just yet, but I think it&#8217;s best to do profiling when you&#8217;re <em>not</em> running in foreground mode (<code>bin/instance fg</code>). It seems a lot of time and function calls are spent checking/loading file system resources, which is done more frequently in that mode. I&#8217;m thinking that it&#8217;s best to try to emulate your production environment as much as possible (get rid of things like plone.reload, PDBDebugMode, DocFinderTab, etc).</p>
<p>w00t!</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/lionfacelemonface.wordpress.com/463/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/lionfacelemonface.wordpress.com/463/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/lionfacelemonface.wordpress.com/463/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/lionfacelemonface.wordpress.com/463/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/lionfacelemonface.wordpress.com/463/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/lionfacelemonface.wordpress.com/463/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/lionfacelemonface.wordpress.com/463/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/lionfacelemonface.wordpress.com/463/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/lionfacelemonface.wordpress.com/463/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/lionfacelemonface.wordpress.com/463/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/lionfacelemonface.wordpress.com/463/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/lionfacelemonface.wordpress.com/463/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/lionfacelemonface.wordpress.com/463/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/lionfacelemonface.wordpress.com/463/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lionfacelemonface.wordpress.com&amp;blog=2073436&amp;post=463&amp;subd=lionfacelemonface&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://lionfacelemonface.wordpress.com/2011/02/08/profiling-plone-4-well-profiling-zope-while-running-plone-4/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/5683b763dd1f27ec0c045b66cba43ab0?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">jjmojojjmojo</media:title>
		</media:content>

		<media:content url="http://lionfacelemonface.files.wordpress.com/2011/02/screen-shot-1.png" medium="image">
			<media:title type="html">screen shot 1</media:title>
		</media:content>

		<media:content url="http://lionfacelemonface.files.wordpress.com/2011/02/screen-shot-2.png" medium="image">
			<media:title type="html">screen shot 2</media:title>
		</media:content>
	</item>
		<item>
		<title>What&#8217;s the problem with RelStorage?</title>
		<link>http://lionfacelemonface.wordpress.com/2011/01/28/whats-the-problem-with-relstorage/</link>
		<comments>http://lionfacelemonface.wordpress.com/2011/01/28/whats-the-problem-with-relstorage/#comments</comments>
		<pubDate>Fri, 28 Jan 2011 20:37:59 +0000</pubDate>
		<dc:creator>jjmojojjmojo</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://lionfacelemonface.wordpress.com/?p=461</guid>
		<description><![CDATA[Last night in a desperate attempt to squeeze some performance out of a plone site that was being loaded with data marshalled from a MySQL database (a new plone-based app is replacing the old generic php-introspects-the-db frontend generator-based one). Only about 3000 objects, bad performance, lots of CPU and RAM used, etc. And it&#8217;s Plone [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lionfacelemonface.wordpress.com&amp;blog=2073436&amp;post=461&amp;subd=lionfacelemonface&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Last night in a desperate attempt to squeeze some performance out of a plone site that was being loaded with data marshalled from a MySQL database (a new plone-based app is replacing the old generic php-introspects-the-db frontend generator-based one).</p>
<p>Only about 3000 objects, bad performance, lots of CPU and RAM used, etc. And it&#8217;s Plone 4. It was also eating up disk space, I&#8217;d have to pack it multiple times during the course of the import or the Data.fs would climb to 60+ GB.</p>
<p>I have a sinking suspicion that the bulk of the bad performance is due to some very fancy and very hastily put together roll up screens, our virtualization environment (KVM on RHEL 5), or some other factor, but I&#8217;m not sure. Profiler tools don&#8217;t seem to work well with the zope that comes with Plone 4, and what did work (<a href="http://plone.org/products/callprofiler">CallProfiler</a>) didn&#8217;t work with five browser views, so when optimizing we were basically flying blind.</p>
<p>We&#8217;ve gotten rid of any brain.getObject() calls, prudent use of memoize, plone.app.caching&#8230; we&#8217;ve done whatever we can to make it faster, and it was better, but still no bueno.</p>
<p>Anyway on a whim I switched my development copy of the app to use <a href="http://pypi.python.org/pypi/RelStorage">RelStorage</a> with MySQL on the back-end (only using mysql because it&#8217;s what we&#8217;ve already got deployed). I also added memcached into the mix. The non-scientific, contrived tests I did saw a massive increase in performance (only a slight difference between using memcached and not). Almost 4000 requests per second for the default home page, vs about 900 using a stock ZEO setup on basically the same hardware (I was inadvertently testing RelStorage on 2GB of RAM vs 4 in the initial tests). <strong>Logged in</strong>. Our expensive roll up screens showed an increase from 150 requests per second to 223, with twice as much data in play (there&#8217;s an especially slow report that pulls about 1600 objects from the catalog and provides pagination.. when I did my relstorage tests I used a script to generate 900 and then copy-and-pasted them until I got over 3k). </p>
<p>RAM no longer climbed to the point where the VM would swap itself to death, the site remained responsive under heavy load (importing from the MySQL db via <a href="http://pypi.python.org/pypi/wsapi4plone.core">wsapi4plone</a>) with only one client running, the database files have barely climbed over 17GB  and I forgot to tweak some innodb optimization factors before I created the RelStorage tables (granted the import isn&#8217;t done yet, about 50% of the data is left to import..).</p>
<p>So, I&#8217;m way happy about all this. But my question now is, why doesn&#8217;t it seem like more people are using this? It&#8217;s documented very well, it&#8217;s a little weird to set up if you&#8217;re not a RDBMS person, but it was very easy. It&#8217;s fast, it&#8217;s efficient, and doesn&#8217;t require anything from the developer. You can even build it without undo if you want to make it presumably faster (I opted to not do that, but it&#8217;s a nice option). You can scale out the DB server and memcached easily (probably not easier than ZEO + FileStorage, but still very easy). You can move up to postgres or oracle if you need more heft. You can theoretically go back to ZEO if you want (I had trouble with the conversion scripts and cron4plone, but they&#8217;re fast and would probably do the trick).</p>
<p><strong>RelStorage is, like, my favourite thing ever. So what&#8217;s the downside?</strong></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/lionfacelemonface.wordpress.com/461/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/lionfacelemonface.wordpress.com/461/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/lionfacelemonface.wordpress.com/461/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/lionfacelemonface.wordpress.com/461/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/lionfacelemonface.wordpress.com/461/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/lionfacelemonface.wordpress.com/461/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/lionfacelemonface.wordpress.com/461/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/lionfacelemonface.wordpress.com/461/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/lionfacelemonface.wordpress.com/461/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/lionfacelemonface.wordpress.com/461/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/lionfacelemonface.wordpress.com/461/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/lionfacelemonface.wordpress.com/461/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/lionfacelemonface.wordpress.com/461/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/lionfacelemonface.wordpress.com/461/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lionfacelemonface.wordpress.com&amp;blog=2073436&amp;post=461&amp;subd=lionfacelemonface&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://lionfacelemonface.wordpress.com/2011/01/28/whats-the-problem-with-relstorage/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/5683b763dd1f27ec0c045b66cba43ab0?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">jjmojojjmojo</media:title>
		</media:content>
	</item>
	</channel>
</rss>
