<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-8427412129779288317</id><updated>2026-03-30T06:33:06.562-04:00</updated><category term="Python"/><category term="shell"/><category term="Zope"/><category term="javascript"/><category term="OmniFocus"/><category term="networking"/><category term="organization"/><category term="ExtJS"/><category term="Mac OS X"/><category term="vim"/><category term="AppleScript"/><category term="Evernote"/><category term="RAID"/><category term="Twisted"/><category term="Ubuntu"/><category term="YUI"/><category term="Zenoss"/><category term="css"/><title type='text'>Concision and Concinnity</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://concisionandconcinnity.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://concisionandconcinnity.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default?start-index=26&amp;max-results=25&amp;redirect=false'/><author><name>Ian McCracken</name><uri>http://www.blogger.com/profile/11153934961249932934</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>56</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-8427412129779288317.post-6601419199206483198</id><published>2013-11-09T11:01:00.003-05:00</published><updated>2013-11-09T11:01:39.472-05:00</updated><title type='text'>ouimeaux 0.5.1 released: Fuzzy device matching, bug fixes</title><content type='html'>Several new features making it easier to use, as well as a lot of bug fixes. Full list &lt;a href=&quot;https://github.com/iancmcc/ouimeaux#changelog&quot;&gt;here&lt;/a&gt;. Included:&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;span style=&quot;font-family: monospace;&quot;&gt;wemo status/wemo switch status&lt;/span&gt;
&lt;/strong&gt;: Prints the current on/off status of all switches or specific switches in your environment.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fuzzy device name matching:&lt;/strong&gt; You can now use fuzzy matching on the command line so you don&#39;t have to preconfigure aliases. For example, &lt;span style=&quot;font-family: monospace;&quot;&gt;wemo switch tvrm on&lt;/span&gt;
will work as well as &lt;span style=&quot;font-family: monospace;&quot;&gt;wemo switch &quot;TV Room&quot; on&lt;/span&gt;
.&lt;/li&gt;
&lt;/ul&gt;
The command-line help is also much more complete. Check it out!

&lt;br /&gt;
&lt;a href=&quot;https://github.com/iancmcc/ouimeaux&quot;&gt;ouimeaux on GitHub&lt;/a&gt;
&lt;br /&gt;
&lt;a href=&quot;https://pypi.python.org/pypi/ouimeaux/0.5.1&quot;&gt;ouimeaux on PyPI&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://concisionandconcinnity.blogspot.com/feeds/6601419199206483198/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8427412129779288317/6601419199206483198' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/6601419199206483198'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/6601419199206483198'/><link rel='alternate' type='text/html' href='http://concisionandconcinnity.blogspot.com/2013/11/ouimeaux-051-released-fuzzy-device.html' title='ouimeaux 0.5.1 released: Fuzzy device matching, bug fixes'/><author><name>Ian McCracken</name><uri>http://www.blogger.com/profile/11153934961249932934</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8427412129779288317.post-1237450869139702653</id><published>2013-05-25T15:51:00.003-04:00</published><updated>2013-05-25T15:56:04.140-04:00</updated><title type='text'>ouimeaux 0.3 released: Device Caching, Aliases, Configuration Options and More</title><content type='html'>&lt;p&gt;In response to a couple of tickets, I&#39;ve released &lt;a href=&quot;https://pypi.python.org/pypi/ouimeaux/0.3&quot;&gt;ouimeaux 0.3&lt;/a&gt;. It includes some bug fixes and a few nice features:

&lt;h4&gt;Device Cache&lt;/h4&gt;
&lt;p&gt;Discovery results are now cached on the filesystem, so command-line operations will be much faster to initialize. The cache can be disabled with the &lt;code&gt;--no-cache&lt;/code&gt; option.

&lt;h4&gt;Aliases&lt;/h4&gt;
Some device names are long or contain spaces, which makes using them on the command line unwieldy. You can now define aliases that make that easier to deal with. For example, you can alias &quot;TV Room&quot; to &quot;tv&quot; allowing you to run &lt;code&gt;wemo switch tv on&lt;/code&gt;.

&lt;h4&gt;Discovery Response Server Binding&lt;/h4&gt;
&lt;p&gt;The UDP server that listens for responses to the UPnP broadcast can now be configured to bind to a specific IP and port.

&lt;h4&gt;Config File&lt;/h4&gt;
&lt;p&gt;You can configure all of the above by modifying &lt;code&gt;~/.wemo/config.yml&lt;/code&gt;. It&#39;s fairly self-explanatory.

&lt;h4&gt;Toggle command&lt;/h4&gt;
&lt;p&gt;Tiny feature: You can now pass &quot;toggle&quot; as well as &quot;on&quot; or &quot;off&quot; as a &lt;code&gt;wemo switch&lt;/code&gt; command.

&lt;p&gt;pip install or fork at will, and let me know how it goes.

&lt;p&gt;
&lt;a href=&quot;https://pypi.python.org/pypi/ouimeaux/0.3&quot;&gt;PyPI&lt;/a&gt;
&lt;p&gt;
&lt;a href=&quot;https://github.com/iancmcc/ouimeaux&quot;&gt;GitHub&lt;/a&gt;</content><link rel='replies' type='application/atom+xml' href='http://concisionandconcinnity.blogspot.com/feeds/1237450869139702653/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8427412129779288317/1237450869139702653' title='18 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/1237450869139702653'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/1237450869139702653'/><link rel='alternate' type='text/html' href='http://concisionandconcinnity.blogspot.com/2013/05/ouimeaux-03-released-configuration.html' title='ouimeaux 0.3 released: Device Caching, Aliases, Configuration Options and More'/><author><name>Ian McCracken</name><uri>http://www.blogger.com/profile/11153934961249932934</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>18</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8427412129779288317.post-1287753850311276072</id><published>2013-04-22T10:15:00.002-04:00</published><updated>2013-04-22T10:15:33.163-04:00</updated><title type='text'>ouimeaux 0.2 released: Now with Motion events!</title><content type='html'>Finally had a chance to fire up Wireshark and figure out how WeMo Motions and Switches talk to each other. I&#39;ve worked the results of my discovery into &lt;a href=&quot;https://github.com/iancmcc/ouimeaux&quot;&gt;ouimeaux 0.2&lt;/a&gt;. Now you can register callbacks to be notified whenever a Motion or Switch changes state (i.e., when motion is detected or a switch is switched). I also cleaned up a few bugs with the UDP server, so you should be able to discover periodically now without it throwing an error.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://pypi.python.org/pypi/ouimeaux/0.2&quot;&gt;https://pypi.python.org/pypi/ouimeaux/0.2&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://github.com/iancmcc/ouimeaux&quot;&gt;https://github.com/iancmcc/ouimeaux&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://concisionandconcinnity.blogspot.com/feeds/1287753850311276072/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8427412129779288317/1287753850311276072' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/1287753850311276072'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/1287753850311276072'/><link rel='alternate' type='text/html' href='http://concisionandconcinnity.blogspot.com/2013/04/ouimeaux-02-released-now-with-motion.html' title='ouimeaux 0.2 released: Now with Motion events!'/><author><name>Ian McCracken</name><uri>http://www.blogger.com/profile/11153934961249932934</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8427412129779288317.post-2837479716438237629</id><published>2013-02-07T01:05:00.001-05:00</published><updated>2013-02-07T01:07:21.674-05:00</updated><title type='text'>Controlling Belkin WeMo switch with a Raspberry Pi</title><content type='html'>&lt;p&gt;
&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;http://www.youtube.com/embed/THcxY-3hm4U&quot; frameborder=&quot;0&quot; allowfullscreen&gt;&lt;/iframe&gt;
&lt;/p&gt;
&lt;p&gt;
Here&#39;s a simple example of a way to use the &lt;a href=&quot;https://github.com/iancmcc/ouimeaux&quot;&gt;ouimeaux&lt;/a&gt; library. The RPi&#39;s running a server that discovers a WeMo switch by name (specified on the command line), then essentially forwards switch input from the GPIO pin to the WeMo. With a power supply circuit to let it run off 110 AC and a nicer switch, you could mount this in the wall to control your WeMo a little quicker than pulling out your phone, for those cases when you&#39;re already in the room. Said my sister: &quot;Congratulations, you&#39;ve reinvented the light switch.&quot;
&lt;/p&gt;
&lt;p&gt;
The switch circuit is the simplest possible; example circuitry and code can be found &lt;a href=&quot;http://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/robot/buttons_and_switches/&quot;&gt;here&lt;/a&gt;.
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://concisionandconcinnity.blogspot.com/feeds/2837479716438237629/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8427412129779288317/2837479716438237629' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/2837479716438237629'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/2837479716438237629'/><link rel='alternate' type='text/html' href='http://concisionandconcinnity.blogspot.com/2013/02/controlling-belkin-wemo-switch-with.html' title='Controlling Belkin WeMo switch with a Raspberry Pi'/><author><name>Ian McCracken</name><uri>http://www.blogger.com/profile/11153934961249932934</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img.youtube.com/vi/THcxY-3hm4U/default.jpg" height="72" width="72"/><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8427412129779288317.post-6028687958917753939</id><published>2013-02-07T01:03:00.002-05:00</published><updated>2013-02-07T01:03:46.674-05:00</updated><title type='text'>ouimeaux: Command line and Python API for WeMo hacking</title><content type='html'>&lt;p&gt;I bought a bunch of &lt;a href=&quot;http://www.belkin.com/us/wemo&quot;&gt;Belkin WeMo Switches&lt;/a&gt; over Christmas, and while I appreciate that my lights now have IP addresses, controlling them can really only be done through the WeMo iPhone app, which (besides having a few understandable bugs) is slow to start up. More to the point, I had no way to hook the switches up to any other events beyond using &lt;a href=&quot;http://ifttt.com/&quot;&gt;IFTTT&lt;/a&gt;, which seems to have a one-switch limit. 

&lt;p&gt;
Luckily, WeMo devices appear to provide a fairly complete SOAP API. &lt;a href=&quot;http://www.issackelly.com/blog/2012/08/04/wemo-api-hacking/&quot;&gt;Isaac Kelly posted a proof of concept&lt;/a&gt; discovering and controlling switches, so obviously more direct control was possible. Hacking together a decent library only took a couple of nights.

&lt;p&gt;
Thus: &lt;a href=&quot;https://github.com/iancmcc/ouimeaux&quot;&gt;ouimeaux&lt;/a&gt;. It uses &lt;a href=&quot;http://www.gevent.org/&quot;&gt;gevent&lt;/a&gt; for async I/O and the minimal UPnP functionality required (broadcasting M-SEARCH requests, listening for responses), and it uses &lt;a href=&quot;http://docs.python-requests.org/en/latest/&quot;&gt;requests&lt;/a&gt; to communicate directly with the devices. Service stubs are built on the fly and provide easy attribute access to actions (for example, &lt;code&gt;switch.basicevent.SetBinaryState(BinaryState=1)&lt;/code&gt;). There&#39;s also a handy &lt;code&gt;explain()&lt;/code&gt; method to print out all services, actions and arguments. The only useful things I had time to figure out how to do—turn switches on and off, get current switch state—I pulled up into methods on the switch object itself: &lt;code&gt;switch.on(), switch.off(), switch.get_state()&lt;/code&gt;. The documentation gives more details.

&lt;p&gt;Once that was written, a simple command line script was straightforward. Installing the package will get you the &lt;code&gt;wemo&lt;/code&gt; script in your PATH. &lt;code&gt;wemo list&lt;/code&gt; to discover names of devices, &lt;code&gt;wemo switch &quot;Switch Name&quot; on&lt;/code&gt; to turn it on. Nothing else so far, but it&#39;s highly hackable. 

&lt;p&gt;I hope to explore the Motion API next, and provide the ability to listen to events from Motion devices. Firmware uploading is exposed too; might be possible to intercept and modify to get an SSH server running.
</content><link rel='replies' type='application/atom+xml' href='http://concisionandconcinnity.blogspot.com/feeds/6028687958917753939/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8427412129779288317/6028687958917753939' title='25 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/6028687958917753939'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/6028687958917753939'/><link rel='alternate' type='text/html' href='http://concisionandconcinnity.blogspot.com/2013/02/ouimeaux-command-line-and-python-api.html' title='ouimeaux: Command line and Python API for WeMo hacking'/><author><name>Ian McCracken</name><uri>http://www.blogger.com/profile/11153934961249932934</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>25</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8427412129779288317.post-6190278327039151586</id><published>2011-01-31T09:59:00.000-05:00</published><updated>2011-01-31T09:59:46.775-05:00</updated><title type='text'>pyxdeco: Python eXtraordinary Decorators</title><content type='html'>Decorators in Python are limited to action when the function they wrap is actually called. Which is fine and all, but they can be more useful. Or at least could be, if they allowed for hooks at definition or instance creation. I&#39;ve packaged up a technique I&#39;ve been using to do just that and released it as &lt;a href=&quot;http://pypi.python.org/pypi/pyxdeco&quot;&gt;pyxdeco&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
I wrote the code when trying to register instance methods as event listeners automatically when the instance was created. Decorator syntax made the most sense by far:&lt;br /&gt;
&lt;pre name=&quot;code&quot; class=&quot;python&quot;&gt;...
@on(&#39;click&#39;)
def click_listener(self):
    ...
&lt;/pre&gt;...but Python decorators can&#39;t be used that way, since the decorator wouldn&#39;t be invoked until click_listener was actually called.&lt;br /&gt;
&lt;br /&gt;
This package adds the ability to define decorators to be called at any of the three points of the function&#39;s existence: when the function is defined, when an instance of the class containing the function is created, and when the function is itself called (the last is of course not new, just a regular decorator, but is provided for consistency and completeness).&lt;br /&gt;
&lt;br /&gt;
Code is on &lt;a href=&quot;http://github.com/iancmcc/pyxdeco&quot;&gt;github&lt;/a&gt;, downloads available from &lt;a href=&quot;http://pypi.python.org/pypi/pyxdeco&quot;&gt;pypi&lt;/a&gt;. Suggestions for examples or future development welcome.</content><link rel='replies' type='application/atom+xml' href='http://concisionandconcinnity.blogspot.com/feeds/6190278327039151586/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8427412129779288317/6190278327039151586' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/6190278327039151586'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/6190278327039151586'/><link rel='alternate' type='text/html' href='http://concisionandconcinnity.blogspot.com/2011/01/pyxdeco-python-extraordinary-decorators.html' title='pyxdeco: Python eXtraordinary Decorators'/><author><name>Ian McCracken</name><uri>http://www.blogger.com/profile/11153934961249932934</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8427412129779288317.post-6250793389906350957</id><published>2010-09-08T09:30:00.000-04:00</published><updated>2010-09-08T09:30:00.655-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Python"/><title type='text'>Decorator Factories (and decorator factory factories)</title><content type='html'>Decorators are a simple, handy way of implementing certain features in any language, but many people find them perplexing. When you start talking about functions that &lt;em&gt;generate&lt;/em&gt; decorators, it gets a little twisty; when you then make helpful functions to create functions that create decorators, you might have to stop for a minute to unclench your brain. Really, though, the concept is quite simple if you take it step by step, so here it is (after the jump), hopefully demystified.&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;h2&gt;Decorators&lt;/h2&gt;&lt;br /&gt;
For the uninitiated, a decorator is a function that replaces another function, adding extra functionality along the way. Here is a trivial example in Python:&lt;br /&gt;
&lt;pre name=&quot;code&quot; class=&quot;python&quot;&gt;def squared(f):
    def inner(*args):
        result = f(*args)
        return result**2
    return inner

def timestwo(n):
    return n*2

timestwo = squared(timestwo)
&lt;/pre&gt;&lt;code&gt;squared()&lt;/code&gt; accepts a function and returns a function, which is the fundamental characteristic of a decorator. We then decorate &lt;code&gt;timestwo()&lt;/code&gt; by passing it through the decorator and replacing the reference to the original function with the result. After all this has executed, &lt;code&gt;timestwo&lt;/code&gt; is now actually the closure &lt;code&gt;inner&lt;/code&gt;, which keeps a reference to the original &lt;code&gt;timestwo&lt;/code&gt; in the decorator&#39;s scope so it can call it and mess with the result.&lt;br /&gt;
&lt;br /&gt;
In Python you can use nice decorator syntax:&lt;br /&gt;
&lt;br /&gt;
&lt;pre name=&quot;code&quot; class=&quot;python&quot;&gt;@squared
def timestwo(n):
    ...
&lt;/pre&gt;Which is synonymous with the manual call to the decorator and reassignment.&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;Decorator Factories&lt;/h2&gt;&lt;br /&gt;
So let&#39;s say you want a more flexible decorator that can raise to any power, not just 2. We could individually define &lt;code&gt;squared()&lt;/code&gt;, &lt;code&gt;cubed()&lt;/code&gt;, &lt;code&gt;timed()&lt;/code&gt; and so on, but it&#39;s much easier just to create a function that returns the decorator we need. We&#39;ll make a decorator factory, &lt;code&gt;raisetopower&lt;/code&gt;, that returns a decorator, &lt;code&gt;decorator&lt;/code&gt;, that returns a function, &lt;code&gt;inner&lt;/code&gt;, which will replace the decorated function. Again, we&#39;ll use closures all the way down, so we keep all the information we need in scope:&lt;br /&gt;
&lt;pre name=&quot;code&quot; class=&quot;python&quot;&gt;def raisetopower(exponent):
    def decorator(f):
        def inner(*args):
            result = f(*args)
            return result**exponent
        return inner
    return decorator
&lt;/pre&gt;Since &lt;code&gt;raisetopower(2)&lt;/code&gt; will return what is essentially &lt;code&gt;squared()&lt;/code&gt; from above, we could do:&lt;br /&gt;
&lt;pre name=&quot;code&quot; class=&quot;python&quot;&gt;squared = raisetopower(2)
cubed = raisetopower(3)
&lt;/pre&gt;But it is much easier just to do:&lt;br /&gt;
&lt;pre name=&quot;code&quot; class=&quot;python&quot;&gt;@raisetopower(2)
def timestwo(n):
    return n*2
&lt;/pre&gt;This may look strange to some, but bear in mind that it is functionally identical to:&lt;br /&gt;
&lt;pre name=&quot;code&quot; class=&quot;python&quot;&gt;timestwo = raisetopower(2)(timestwo)
&lt;/pre&gt;And since &lt;code&gt;raisetopower(2)==squared&lt;/code&gt;, we&#39;ve recreated the same situation we had above, only we can now pass in any exponent we want.&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;Decorator Factory Factories&lt;/h2&gt;&lt;br /&gt;
Naturally the same technique, creating and returning a closure wrapping the original function, can be nested indefinitely; at some point it becomes unreadable. Personally, I would never have multiple calls on the same line, so I draw the line at actually calling decorator factories. I have, however, found uses for decorator factory factories, which you would need when you wanted a reusable decorator factory with some information frozen inside. For example, in &lt;a href=&quot;http://concisionandconcinnity.blogspot.com/2010/09/released-txrestapi-01.html&quot;&gt;txrestapi&lt;/a&gt;, I needed decorators that tied a function to both a request method and a regular expression, like:&lt;br /&gt;
&lt;pre name=&quot;code&quot; class=&quot;python&quot;&gt;@GET(&#39;^/.*$&#39;)
def onGetAnything(self):
    ...
&lt;/pre&gt;&lt;code&gt;GET&lt;/code&gt; itself is just a decorator factory, but I defined &lt;code&gt;GET&lt;/code&gt; (and &lt;code&gt;POST&lt;/code&gt;, and &lt;code&gt;PUT&lt;/code&gt;, and &lt;code&gt;DELETE&lt;/code&gt;) using a decorator factory factory:&lt;br /&gt;
&lt;pre name=&quot;code&quot; class=&quot;python&quot;&gt;def decorator_factory_factory(method):
    def decorator_factory(regex):
        def decorator(f):
            def inner(*args, **kwargs):
                # do stuff with f(*args, **kwargs)
                # and method and regex
            return inner
        return decorator
    return decorator_factory

GET = decorator_factory_factory(&#39;GET&#39;)
PUT = decorator_factory_factory(&#39;PUT&#39;)
&lt;/pre&gt;Now, I could have used the function to do:&lt;br /&gt;
&lt;pre name=&quot;code&quot; class=&quot;python&quot;&gt;@decorator_factory_factory(&#39;GET&#39;)(&#39;^/.*$&#39;)
def onGetAnything(self):
    ...
&lt;/pre&gt;But as I said above, you reach a point where it&#39;s totally unreadable, so draw the line appropriately.&lt;br /&gt;
&lt;br /&gt;
So that&#39;s that: decorators, and functions that create decorators, and functions that create functions that create decorators, all unpacked. Really it&#39;s just the same technique, replacing a function with a closure wrapping that function, over and over.</content><link rel='replies' type='application/atom+xml' href='http://concisionandconcinnity.blogspot.com/feeds/6250793389906350957/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8427412129779288317/6250793389906350957' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/6250793389906350957'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/6250793389906350957'/><link rel='alternate' type='text/html' href='http://concisionandconcinnity.blogspot.com/2010/09/decorator-factories-and-decorator.html' title='Decorator Factories (and decorator factory factories)'/><author><name>Ian McCracken</name><uri>http://www.blogger.com/profile/11153934961249932934</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8427412129779288317.post-522911417121779898</id><published>2010-09-06T15:02:00.001-04:00</published><updated>2010-09-08T08:31:34.015-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Python"/><category scheme="http://www.blogger.com/atom/ns#" term="Twisted"/><title type='text'>Released: txrestapi 0.1</title><content type='html'>Sorry updates have been horribly infrequent; I both had a kid and moved to Austin (in that order) in the last few months. With that out of the way, to the code...&lt;br /&gt;
&lt;br /&gt;
I got frustrated chaining &lt;code&gt;twisted.web.resource.Resource&lt;/code&gt; classes to provide a complex REST API service, so I wrote a new package that lets you do it with regular expressions and decorators, like:&lt;br /&gt;
&lt;blockquote&gt;&lt;pre name=&quot;code&quot; class=&quot;python&quot;&gt;@GET(&#39;^/path/to/api/method$&#39;)
    def on_api_method(self, request):
       ...
&lt;/pre&gt;&lt;/blockquote&gt;I&#39;m pretty fond of it already. Code is on &lt;a href=&quot;http://github.com/iancmcc/txrestapi&quot;&gt;GitHub&lt;/a&gt;, package is available from &lt;a href=&quot;http://pypi.python.org/pypi/txrestapi&quot;&gt;pypi&lt;/a&gt; &amp;mdash; and, of course, via &lt;code&gt;easy_install&lt;/code&gt;. Full docs are at both sites, quick example after the jump.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Some quick tips:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;If you give your regular expressions named groups, those will be passed into the callback as keyword arguments (like Django).&lt;/li&gt;
&lt;li&gt;You can use the ALL decorator to register a callback for all methods, instead of just GET or POST or PUT or DELETE.&lt;/li&gt;
&lt;li&gt;You can use multiple decorators on the same callback to register the same callback for exactly those methods.&lt;/li&gt;
&lt;li&gt;Callbacks are evaluated in order, top to bottom, so put the most specific at the top.&lt;/li&gt;
&lt;li&gt;You can return &lt;code&gt;Resource&lt;/code&gt; objects from a callback as well as strings, at which point normal twisted.web traversal takes over. You can also, of course, return other &lt;code&gt;APIResource&lt;/code&gt; objects.&lt;br /&gt;
&lt;/ul&gt;&lt;blockquote&gt;&lt;pre name=&quot;code&quot; class=&quot;python&quot;&gt;from txrestapi.resource import APIResource
from txrestapi.methods import GET, POST, PUT, DELETE, ALL

class MyAPI(APIResource):

    @GET(&#39;^/(?P&amp;lt;id&amp;gt;[a-zA-Z0-9]+)&#39;)
    def get_info(self, request, id):
        return &quot;Requested information for id %s&quot; % id

    @POST(&#39;^/(?P&amp;lt;id&amp;gt;[a-zA-Z0-9]+)&#39;)
    def set_info(self, request, id):
        return &quot;Setting properties on id %s&quot; % id

    @PUT(&#39;^/(?P&amp;lt;id&amp;gt;[a-zA-Z0-9]+)&#39;)
    def create_ob(self, request, id):
        return &quot;Trying to create object with id %s&quot; % id

    @DELETE(&#39;^/(?P&amp;lt;id&amp;gt;[a-zA-Z0-9]+)&#39;)
    def del_ob(self, request, id):
        return &quot;Deleting object %s&quot; % id

    @ALL(&#39;^/&#39;)
    def default(self, request):
        return &quot;I match everything; clearly, you aren&#39;t asking for anything interesting.&quot;

from twisted.web.server import Site
from twisted.internet import reactor
site = Site(MyAPI(), timeout=None)
reactor.listenTCP(site, port=8090)
reactor.run()

&lt;/pre&gt;&lt;/blockquote&gt;Then make assorted requests to &lt;code&gt;http://localhost:8090/someid&lt;/code&gt; and watch them get routed to your callbacks.</content><link rel='replies' type='application/atom+xml' href='http://concisionandconcinnity.blogspot.com/feeds/522911417121779898/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8427412129779288317/522911417121779898' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/522911417121779898'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/522911417121779898'/><link rel='alternate' type='text/html' href='http://concisionandconcinnity.blogspot.com/2010/09/released-txrestapi-01.html' title='Released: txrestapi 0.1'/><author><name>Ian McCracken</name><uri>http://www.blogger.com/profile/11153934961249932934</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8427412129779288317.post-3569408104605829212</id><published>2010-03-02T22:57:00.001-05:00</published><updated>2010-09-08T08:31:43.010-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="javascript"/><title type='text'>Notes on Teaching JavaScript</title><content type='html'>For the past few months, I&#39;ve been tutoring a friend in JavaScript, and I&#39;ve rediscovered some trite knowledge anew: there&#39;s no better way to improve in a subject than to teach it.&lt;br /&gt;
&lt;br /&gt;
My friend and I come at programming JS from different directions: he&#39;s primarily a Web designer who uses jQuery to enhance pages that can stand on their own, while I&#39;m a Python/Zope developer who&#39;s currently using ExtJS as the framework for a full-blown web application. I did a lot of thinking before our first session about how to approach the undertaking&amp;mdash;should I teach to a framework, since that would yield the most immediate benefits? If so, which? Would I be able to explain best practices without going laboriously over never-again-used fundamentals of programming? Would our sessions even be worthwhile, since hundreds of tutorials are freely available?&lt;br /&gt;
&lt;br /&gt;
Luckily my tutee was enthusiastic about learning from the bottom up, which was significantly easier for me, since that&#39;s how I learned myself. Despite the familiar trajectory, our sessions have been pretty educational for me, too, in part because they&#39;ve highlighted the difficulties (and rewards) of &lt;i&gt;teaching&lt;/i&gt; JavaScript, as distinct from those involved in learning it.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
The first thing I realized was almost a tautology: different people approach JavaScript in different ways. The gap between designer and developer is quite large, and yet their disparate approaches are at once equally valid (grant me the possibly false dichotomy for just a minute). Other things being equal, designers want to add features and effects to their pages, to enhance the user experience. They think in CSS, and are primarily concerned with the manipulation of DOM elements. Developers, on the other hand, are used to complex APIs and constant refactoring, and are concerned with abstraction and (hopefully) readability. &lt;br /&gt;
&lt;br /&gt;
A designer may be perfectly happy with a long jQuery chain that would cause a developer spiritual distress; a developer may confront a simple problem with an elegant but unnecessarily complex solution that only serves to frustrate the designer. Being primarily a developer myself, I had to realign a bit, to overcome my instinct to make things &lt;i&gt;as useful as possible&lt;/i&gt;, instead of merely as useful as &lt;i&gt;necessary&lt;/i&gt;. At the same time, I felt it was important to present the basic programming mores that guide the more technically minded scripter&amp;mdash;jQuery chains be damned.&lt;br /&gt;
&lt;br /&gt;
The second thing I realized: It&#39;s easy to learn JavaScript wrong. The overhead of fooling around with JavaScript is incredibly low. Everybody uses a browser every day, and as soon as you learn how to open the console, or install Firebug or Greasemonkey, you can have a visible effect on any of the hundreds of sites of whose shortcomings frequent use has made you achingly aware. At the same time, decent entry-level JavaScript information is oddly hard to come by. Most you find on the Web is outdated. Few tutorials adequately address cross-browser issues. And when you finally give up in frustration, there&#39;s always jQuery, which is incredibly easy both to use and to misuse. &lt;br /&gt;
&lt;br /&gt;
Had jQuery been around when I needed the simple solutions, I might easily have managed to avoid the dirty low-level stuff to this day. Nothing, however, teaches you JavaScript quite so well as rolling your own cross-browser Ajax utilities, and so I studied the source code of Prototype and script.aculo.us and MochiKit, and now I&#39;m a far better programmer than someone starting out today might be forced to become.&lt;br /&gt;
&lt;br /&gt;
Since education was, in the instant case, the desired goal, I shied away from, say, teaching to jQuery (or even ExtJS or YUI, which are much closer in coding style to JavaScript per se), and guided our sessions as much as made sense towards a home-grown solution. Since my tutee is partially responsible for a large public site that needs only moderate, self-contained effects and widgets, but also would like his code to be reusable and readable by his many colleagues, he needed a little of the approach of both the designer and the developer. And so our ongoing project became to write a reusable, widget-focused library.&lt;br /&gt;
&lt;br /&gt;
Creating a simple library turned out to be a fantastic learning exercise: it requires investigation of namespaces, presents plenty of scoping problems to overcome, and requires attention to consistency, organization and sane API design. It&#39;s immediately useful, so no worries about the student getting bored. A library is infinitely extensible, but can also contain the most trivial low-level utilities. Every library has its own style, and lets its coders be clever.&lt;br /&gt;
&lt;br /&gt;
Most importantly, the creation of a library invites, and even insists upon, reiteration of what I find to be the most helpful guideline to keep in one&#39;s head while writing code: &lt;b&gt;Assume a third party will one day attempt to extend everything you produce.&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Personally, I&#39;ve found the experience quite valuable, for one crucial reason: I&#39;m not a designer. The interfaces and APIs I&#39;d normally create would probably be most useful to somebody like me. Watching the thought processes of somebody in the thick of it by day as they design an API &lt;i&gt;they&#39;d&lt;/i&gt; like to use is enormously instructive.&lt;br /&gt;
&lt;br /&gt;
Now, we&#39;re using jQuery as the basis for this library, because even though cross-browser issues are still unfortunately relevant, let&#39;s face it: frameworks are here, they aren&#39;t going anywhere, and they solve the problem without much effort, so it&#39;s not a paramount concern. Nonetheless, whenever we run into a problem, we write a solution from scratch before we use a jQuery shortcut (for example, we wrote a scope-setting decorator function similar to jQuery.proxy rather than just using proxy). When we&#39;ve found a need to use something more complex, like class-like inheritance, we&#39;ve studied many variations and written our own, and finally settled on a style that &lt;i&gt;feels&lt;/i&gt; like jQuery but adds substantial value (no small feat, as the concept of class inheritance is nearly diametrically opposed to the jQuery ethos). &lt;br /&gt;
&lt;br /&gt;
Probably nobody but my tutee&#39;s band of designers will ever use this library. But it&#39;s a great propadaeutic compromise, for both of us. I have an opportunity to share the best practices I&#39;ve managed to distill from the pored-over source of other frameworks, and I get the chance to see things from the designer&#39;s point of view, rather than focusing on that of the implementer (me) or end user (actual end user). &lt;br /&gt;
&lt;br /&gt;
If you can find a willing subject, I&#39;d recommend the practice of teaching JavaScript as an excellent way to coalesce the lore into a science.</content><link rel='replies' type='application/atom+xml' href='http://concisionandconcinnity.blogspot.com/feeds/3569408104605829212/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8427412129779288317/3569408104605829212' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/3569408104605829212'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/3569408104605829212'/><link rel='alternate' type='text/html' href='http://concisionandconcinnity.blogspot.com/2010/03/notes-on-teaching-javascript.html' title='Notes on Teaching JavaScript'/><author><name>Ian McCracken</name><uri>http://www.blogger.com/profile/11153934961249932934</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8427412129779288317.post-2892402798271851611</id><published>2010-02-28T00:08:00.000-05:00</published><updated>2010-02-28T07:41:07.086-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="OmniFocus"/><category scheme="http://www.blogger.com/atom/ns#" term="organization"/><title type='text'>Personal Agile Task Management with OmniFocus</title><content type='html'>The Zenoss dev team uses Rally for project management. It&#39;s fine for planning the activities of the entire team, but as a personal task manager I find it pretty useless. Its interface is unwieldy, and it doesn&#39;t allow for more or less fine-grained tasks. Great to get a high-level view of an iteration; bad for any low-level organization.&lt;br /&gt;
&lt;br /&gt;
Since I like OmniFocus for my own task management, I&#39;ve developed a system that mirrors Rally at the highest level, but allows for much more versatility at a lower level (not, of course, restricted to Rally; should function perfectly well for any Agile project). OmniFocus and Rally sync up surprisingly well, as far as that goes, but OmniFocus lets me take those extra steps to get myself an actual task list for the day and switch priorities on an hourly basis.&lt;br /&gt;
&lt;br /&gt;
First, the basic translations:&lt;br /&gt;
&lt;table&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;th&gt;Agile Concept&lt;/th&gt;&lt;th&gt;OmniFocus Analogue&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Iteration&lt;/td&gt;&lt;td&gt;Project Folder&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;User Story&lt;/td&gt;&lt;td&gt;Project&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Task&lt;/td&gt;&lt;td&gt;Action&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Estimate&lt;/td&gt;&lt;td&gt;Estimate&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;
Now, the details. I&#39;ve organized my project folders at the top level according to my various roles; under &quot;Zenoss Developer&quot; I have two folders, &lt;b&gt;Current Iteration&lt;/b&gt; and &lt;b&gt;Backlog&lt;/b&gt;. Since I don&#39;t use OmniFocus for project planning, just for task management in the foreseeable future (which usually doesn&#39;t include &lt;i&gt;next&lt;/i&gt; iteration), there&#39;s no need to have folders for every iteration in the release. &lt;br /&gt;
&lt;br /&gt;
When I break a project down, I put the resulting projects and tasks under the Backlog folder, in subfolders according to feature (&quot;User Interface,&quot; etc.). I also have a single-action list called &lt;b&gt;Tickets&lt;/b&gt; into which go actions representing tickets I need to fix. I try to include time estimates where appropriate.&lt;br /&gt;
&lt;br /&gt;
After we finish our iteration planning, I modify the projects in the Backlog folder with any changes, then I drag those projects and tickets planned for the iteration into the Current Iteration folder. I then break down tasks into sub-tasks for the nitty-gritty stuff that&#39;s too small for Rally but for which I still want a reminder.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2bKbF_x00L5psbGUsR3QO5AVz6Jk0X3d_28CP2rxJffXhtUcPfCMwmqQth5i1bJbtTdYtY6WVQQBoJqaAoxSw8glPC9O4zhNUpfdLBbFBx6Upa-1kXuH46t-YL-rTWnXnqUwYsjRntB91/s1600-h/Screen+shot+2010-02-27+at+11.37.01+PM.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2bKbF_x00L5psbGUsR3QO5AVz6Jk0X3d_28CP2rxJffXhtUcPfCMwmqQth5i1bJbtTdYtY6WVQQBoJqaAoxSw8glPC9O4zhNUpfdLBbFBx6Upa-1kXuH46t-YL-rTWnXnqUwYsjRntB91/s320/Screen+shot+2010-02-27+at+11.37.01+PM.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
When I create a project based on a user story, I use a &lt;a href=&quot;http://concisionandconcinnity.blogspot.com/2009/03/how-i-use-omnifocus.html&quot;&gt;template prepopulated with the tasks&lt;/a&gt; associated with &lt;i&gt;every&lt;/i&gt; user story—code review, running unit tests, communication with QA, etc. To this I add the tasks unique to the particular project. This cuts down on the drudgery of data entry while ensuring I don&#39;t forget anything. Contexts are all preset as well.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjInBoISM64cc15BOOIXwjAfbaRbBxfWKviQQKpKycx34r9Bhtvu6YK1YeCorSm2AK1oKHopzN4sCu-yHvaFT7VziA0WJtcs5vXMtWKzH_dAjIIQMUB_7zcayfOCKtuB4LSENQ5DLfyX161/s1600-h/Screen+shot+2010-02-27+at+11.46.37+PM.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjInBoISM64cc15BOOIXwjAfbaRbBxfWKviQQKpKycx34r9Bhtvu6YK1YeCorSm2AK1oKHopzN4sCu-yHvaFT7VziA0WJtcs5vXMtWKzH_dAjIIQMUB_7zcayfOCKtuB4LSENQ5DLfyX161/s320/Screen+shot+2010-02-27+at+11.46.37+PM.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Now my iteration is easy to see (and manage) at a glance. I have a &quot;Current Iteration&quot; context focused on the project folder, so I&#39;m not distracted or overwhelmed by the entire project at once. When priorities shift, I can change the order of the projects within the Current Iteration folder, or move things in and out of the Backlog folder. &lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjliq9VsoIv51NshK-u53WI9ZPCmjzEvH0b52vyRE4Nt8z_CeuHnWfCHPL4uJudLUUgCjsMrKIrOPrCu7vhmMW9XpSkuyY7T4x8kNa5XyuZ-wmLLUVn3pjGN2LvSNMPzBccHfEKGktseCXl/s1600-h/Screen+shot+2010-02-27+at+11.37.11+PM.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjliq9VsoIv51NshK-u53WI9ZPCmjzEvH0b52vyRE4Nt8z_CeuHnWfCHPL4uJudLUUgCjsMrKIrOPrCu7vhmMW9XpSkuyY7T4x8kNa5XyuZ-wmLLUVn3pjGN2LvSNMPzBccHfEKGktseCXl/s320/Screen+shot+2010-02-27+at+11.37.11+PM.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
When the iteration nears its end, I&#39;ve already begun to do some of the planning needed for the next iteration simply by virtue of using OmniFocus in a way compatible with Rally. Anything left in Current Iteration gets split to the next iteration, and whatever&#39;s at the top of Backlog gets dragged into Current Iteration to fill in.&lt;br /&gt;
&lt;br /&gt;
I don&#39;t worry too much about time estimates in OmniFocus, although I do make some attempt to populate the field just for the sake of keeping the information at hand. But generally I leave the reconciliation of my velocity with tasks to iteration planning in Rally.&lt;br /&gt;
&lt;br /&gt;
Anyway, this little system works pretty well for me. While at a high level it mirrors Rally, I also get the benefits of sub-tasks and user story templates, as well as integration with non-Zenoss-related actions.&lt;br /&gt;
&lt;br /&gt;
How do you reconcile your Agile project with personal task management? I&#39;d love to hear your methods in the comments, OmniFocus or no.</content><link rel='replies' type='application/atom+xml' href='http://concisionandconcinnity.blogspot.com/feeds/2892402798271851611/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8427412129779288317/2892402798271851611' title='13 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/2892402798271851611'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/2892402798271851611'/><link rel='alternate' type='text/html' href='http://concisionandconcinnity.blogspot.com/2010/02/personal-agile-task-management-with.html' title='Personal Agile Task Management with OmniFocus'/><author><name>Ian McCracken</name><uri>http://www.blogger.com/profile/11153934961249932934</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2bKbF_x00L5psbGUsR3QO5AVz6Jk0X3d_28CP2rxJffXhtUcPfCMwmqQth5i1bJbtTdYtY6WVQQBoJqaAoxSw8glPC9O4zhNUpfdLBbFBx6Upa-1kXuH46t-YL-rTWnXnqUwYsjRntB91/s72-c/Screen+shot+2010-02-27+at+11.37.01+PM.png" height="72" width="72"/><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8427412129779288317.post-1998967524920365803</id><published>2010-02-17T12:00:00.003-05:00</published><updated>2010-02-17T12:04:22.319-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Evernote"/><category scheme="http://www.blogger.com/atom/ns#" term="Mac OS X"/><category scheme="http://www.blogger.com/atom/ns#" term="OmniFocus"/><title type='text'>OmniFocus + Evernote</title><content type='html'>A standing opinion about Evernote is that it&#39;s fantastic at accepting information but terrible at giving it up again. You can search from within the app, but any kind of integration with other apps is only barely possible—by, say, linking to the Evernote web client.&lt;br /&gt;
&lt;br /&gt;
OmniFocus, for its part, is exceptional at helping you organize your life, but really isn&#39;t designed to be a repository of detailed information, so integration with Evernote is a natural desire. For example, I have an OmniFocus single-action list which I use to store blog post ideas; it&#39;s a natural next step to have an Evernote note associated with each containing notes and drafts. &lt;br /&gt;
&lt;br /&gt;
So here&#39;s the method (admittedly, somewhat manual) that I&#39;ve been using to keep references to Evernote notes with associated OmniFocus actions. &lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;&lt;b&gt;Create the note.&lt;/b&gt; This is the part that really ought to be automated, but isn&#39;t (some AppleScript-fu, which I unfortunately do not possess, could probably eliminate or ease this step. Suggestions welcome). Go into Evernote and make a new note, presumably titled similarly to your OmniFocus action.&lt;/li&gt;

&lt;li&gt;&lt;b&gt;Pull up the note in Finder.&lt;/b&gt; Herein lies the only interesting part. Since Evernote notes show up in Spotlight searches, there is at least some avenue for getting a reference to the note outside of Evernote itself; we will exploit this.&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Open up Finder and start a new search (type a character in the search box, then delete it).&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPkG3FqbAJvswXg3225KoXIexg_F-N8VDeQPq9QJOH3jyYoz9G1d7gLbOjls288DNemJSilGcrp49mtjwYs3K9xocN49Cn69H8WgIi15mMGqmQFwAqpc9fE3yhpODBbU6F51PgBNziI6V5/s1600-h/Screen+shot+2010-02-17+at+11.16.03+AM.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPkG3FqbAJvswXg3225KoXIexg_F-N8VDeQPq9QJOH3jyYoz9G1d7gLbOjls288DNemJSilGcrp49mtjwYs3K9xocN49Cn69H8WgIi15mMGqmQFwAqpc9fE3yhpODBbU6F51PgBNziI6V5/s320/Screen+shot+2010-02-17+at+11.16.03+AM.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/li&gt;

&lt;li&gt;Click the + button to add a new criterion, where Kind is Other. Type &quot;Evernote Note&quot; into the box. You can restrict this search to your home folder if you have other Evernote users on the same computer. Sort descending by Last Opened.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLrkDIRB-Mpw9TpF549KIZMN-67VErdWlbX3MMl28LsqFo2p2PDhdMEAxIdYak3Zv5E50B6JeGmJl7M_HhGsdddXRKjqImXaexRkVfmCnHAOiKzz9C-S_XVsuMrzfhdUWKq8KY4bAfeOWW/s1600-h/Screen+shot+2010-02-17+at+10.39.59+AM.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLrkDIRB-Mpw9TpF549KIZMN-67VErdWlbX3MMl28LsqFo2p2PDhdMEAxIdYak3Zv5E50B6JeGmJl7M_HhGsdddXRKjqImXaexRkVfmCnHAOiKzz9C-S_XVsuMrzfhdUWKq8KY4bAfeOWW/s320/Screen+shot+2010-02-17+at+10.39.59+AM.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/li&gt;

&lt;li&gt;Click &quot;Save.&quot; Save it as &quot;Evernote&quot; and add it to your sidebar.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhej0hZHJbT_Advg1lh1ISIGLlX1SolidFgTANuQ0KYDdNlce0dOE3lHST8tAnvFgNJDslQ3xjRM1OfW1nqccgz6ZjQ-5A0NnypBySaZf7uA6yOfOx9JLC-MMz_l0DCYLXDdZLeCgRthgPz/s1600-h/Screen+shot+2010-02-17+at+10.54.17+AM.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhej0hZHJbT_Advg1lh1ISIGLlX1SolidFgTANuQ0KYDdNlce0dOE3lHST8tAnvFgNJDslQ3xjRM1OfW1nqccgz6ZjQ-5A0NnypBySaZf7uA6yOfOx9JLC-MMz_l0DCYLXDdZLeCgRthgPz/s320/Screen+shot+2010-02-17+at+10.54.17+AM.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
Now selecting this saved search in Finder will pull up all your notes in an order conducive to this process. If you just created the note, it&#39;ll be right on top; otherwise, you can progressively filter the search by some text. &lt;/li&gt;

&lt;li&gt;&lt;b&gt;Drag the note to the OmniFocus.&lt;/b&gt; Just drop it on the action to get a link to Evernote in the notes field.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5PtJ5nbWfPTnnU3mVyAgouMrn3eWhaSq2nOZFI3Dkyp-ngz5K5ubZy5LNWEfynCFSqa2HXGx1-EcaCe8O9ZODTvz4ONkrwE23xIkM4kx1owqAY5T2BlRHrQN0oxVbfktfktTcnlenX3Ai/s1600-h/Screen+shot+2010-02-17+at+10.55.38+AM.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5PtJ5nbWfPTnnU3mVyAgouMrn3eWhaSq2nOZFI3Dkyp-ngz5K5ubZy5LNWEfynCFSqa2HXGx1-EcaCe8O9ZODTvz4ONkrwE23xIkM4kx1owqAY5T2BlRHrQN0oxVbfktfktTcnlenX3Ai/s320/Screen+shot+2010-02-17+at+10.55.38+AM.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;br /&gt;
Nothing fancy, nothing new, and as I said, still more manual than one would hope. A button in OF that automatically creates a note in Evernote and plugs the reference into the action would be sweet. Still, until somebody writes that or I manage to find the time to learn some AppleScript, this method seems to be working pretty well for me.</content><link rel='replies' type='application/atom+xml' href='http://concisionandconcinnity.blogspot.com/feeds/1998967524920365803/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8427412129779288317/1998967524920365803' title='14 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/1998967524920365803'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/1998967524920365803'/><link rel='alternate' type='text/html' href='http://concisionandconcinnity.blogspot.com/2010/02/omnifocus-evernote.html' title='OmniFocus + Evernote'/><author><name>Ian McCracken</name><uri>http://www.blogger.com/profile/11153934961249932934</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPkG3FqbAJvswXg3225KoXIexg_F-N8VDeQPq9QJOH3jyYoz9G1d7gLbOjls288DNemJSilGcrp49mtjwYs3K9xocN49Cn69H8WgIi15mMGqmQFwAqpc9fE3yhpODBbU6F51PgBNziI6V5/s72-c/Screen+shot+2010-02-17+at+11.16.03+AM.png" height="72" width="72"/><thr:total>14</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8427412129779288317.post-7707793609555181247</id><published>2010-02-10T11:29:00.001-05:00</published><updated>2010-02-17T11:34:10.426-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="shell"/><category scheme="http://www.blogger.com/atom/ns#" term="Zenoss"/><category scheme="http://www.blogger.com/atom/ns#" term="Zope"/><title type='text'>Zenoss daemon status in bash prompt</title><content type='html'>Seeing as how I bring Zenoss processes up and down forty times a day, I can get confused about what&#39;s running, and &lt;code&gt;zenoss status&lt;/code&gt; gives me a bunch of stuff I don&#39;t care about. So I wrote this bash function to output a simple string representing my running daemons.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;pre name=&quot;code&quot;&gt;zends () {
    RESULT=&quot;&quot;
    OUTPUT=&quot;`ps aux | grep $ZENHOME`&quot;
    SEARCHFOR=&quot;zeoctl:z
               (zopectl|Startup/run.py):Z
               zenhub:h
               zenjobs:j&quot;
    for f in $SEARCHFOR; do
        if [ -n &quot;`echo \&quot;$OUTPUT\&quot; | egrep \&quot;${f%:*}\&quot;`&quot; ]; then
            RESULT=${RESULT}${f#*:}
        fi
    done
    echo $RESULT
}&lt;/pre&gt;&lt;br /&gt;
This iterates over &lt;code&gt;regex:letter&lt;/code&gt; pairs and checks the &lt;code&gt;ps&lt;/code&gt; output to see if a process matches. If so, it appends the letter to the string.&lt;br /&gt;
&lt;br /&gt;
Then I added it into my prompt in &lt;code&gt;~/.bashrc&lt;/code&gt;:&lt;br /&gt;
&lt;br /&gt;
&lt;pre name=&quot;code&quot;&gt;PS1=&quot;[...snip...]$(zends)[...snip...] $&quot;&lt;/pre&gt;&lt;br /&gt;
Now my prompt looks like this:&lt;br /&gt;
&lt;pre style=&quot;background-color:black;color:white&quot;&gt;&lt;span style=&quot;color:cyan&quot;&gt;ian&lt;/span&gt; | &lt;span style=&quot;color:magenta&quot;&gt;zhj&lt;/span&gt; | &lt;span style=&quot;color:red&quot;&gt;10.42.1.7&lt;/span&gt; | &lt;span style=&quot;color:lightgreen&quot;&gt;core/Products&lt;/span&gt; &lt;span style=&quot;color:yellow&quot;&gt;(devdetail)&lt;/span&gt; &lt;span style=&quot;color:white&quot;&gt;$&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;
And I can tell at a glance that ZEO, zenhub and zenjobs are running, but not Zope.</content><link rel='replies' type='application/atom+xml' href='http://concisionandconcinnity.blogspot.com/feeds/7707793609555181247/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8427412129779288317/7707793609555181247' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/7707793609555181247'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/7707793609555181247'/><link rel='alternate' type='text/html' href='http://concisionandconcinnity.blogspot.com/2010/02/zenoss-daemon-status-in-bash-prompt.html' title='Zenoss daemon status in bash prompt'/><author><name>Ian McCracken</name><uri>http://www.blogger.com/profile/11153934961249932934</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8427412129779288317.post-6232661722649772983</id><published>2010-02-06T20:38:00.000-05:00</published><updated>2010-02-17T11:34:10.428-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ExtJS"/><category scheme="http://www.blogger.com/atom/ns#" term="javascript"/><category scheme="http://www.blogger.com/atom/ns#" term="Python"/><category scheme="http://www.blogger.com/atom/ns#" term="Zope"/><title type='text'>extdirect 0.4 released: Batched requests, new licensing</title><content type='html'>Sorry it&#39;s taken so long, those of you who&#39;ve made requests and opened tickets, but finally, there&#39;s an update to &lt;a href=&quot;http://pypi.python.org/pypi/extdirect&quot;&gt;extdirect&lt;/a&gt;. This release adds support for batched requests as described in the &lt;a href=&quot;http://extjs.com/products/extjs/direct.php&quot;&gt;Ext.Direct spec&lt;/a&gt; (multiple calls within a certain amount of time are gathered into a single request).&lt;br /&gt;
&lt;br /&gt;
In addition, I&#39;ve removed the actual ExtJS code itself so this could be released under a less restrictive license. Ext 3.x is now merely a prerequisite.&lt;br /&gt;
&lt;br /&gt;
Thanks to Brian Edwards and Jon-Pierre Gentil for their work on the analogous code in Zenoss, a version of which made its way into this release.</content><link rel='replies' type='application/atom+xml' href='http://concisionandconcinnity.blogspot.com/feeds/6232661722649772983/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8427412129779288317/6232661722649772983' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/6232661722649772983'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/6232661722649772983'/><link rel='alternate' type='text/html' href='http://concisionandconcinnity.blogspot.com/2010/02/extdirect-04-released-batched-requests.html' title='extdirect 0.4 released: Batched requests, new licensing'/><author><name>Ian McCracken</name><uri>http://www.blogger.com/profile/11153934961249932934</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8427412129779288317.post-1873797702944997518</id><published>2010-02-03T10:05:00.002-05:00</published><updated>2010-02-17T11:32:19.201-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="javascript"/><category scheme="http://www.blogger.com/atom/ns#" term="Python"/><category scheme="http://www.blogger.com/atom/ns#" term="vim"/><title type='text'>Run pyflakes/jslint automatically in Vim</title><content type='html'>As maybe you can tell from the infrequency of updates to this blog, I (and the others with whom I&#39;m working) have been churning out a huge amount of code recently, split roughly evenly between Python and JavaScript. One nice thing I got used to during the month I tried to switch from Vim to &lt;a href=&quot;http://macromates.com/&quot; target=&quot;_blank&quot;&gt;TextMate&lt;/a&gt; was a plugin that ran &lt;a href=&quot;http://pypi.python.org/pypi/pyflakes&quot; target=&quot;_blank&quot;&gt;pyflakes&lt;/a&gt; every time I saved a Python file. After I gave up and went back to Vim, I missed that check. Luckily, other, more vimscript-savvy coders had the same idea. Here&#39;s how to set up both pyflakes and jslint to run on save.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;&lt;b&gt;pyflakes&lt;/b&gt;. I installed &lt;a href=&quot;http://github.com/kevinw/pyflakes-vim&quot; target=&quot;_blank&quot;&gt;this excellent plugin&lt;/a&gt;:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;$ git clone git://github.com/kevinw/pyflakes-vim.git
$ mkdir -p ~/.vim/ftplugin/python
$ cp pyflakes-vim/pyflakes.vim ~/.vim/ftplugin/python&lt;/pre&gt;I also followed his advice and installed his fork of pyflakes, which is incredibly faster than the original version:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;$ git clone git://github.com/kevinw/pyflakes.git
$ cd pyflakes
$ sudo python setup.py install&lt;/pre&gt;&lt;br /&gt;
This plugin actually highlights errors inline as you edit, and puts details in the status bar when you&#39;re over a line with an error:&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear:both;text-align:center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpdCObjKwG9m9TLp6lnuswVKdTVv9KgB5DHUCA1MLiQMRHn82d4IkrxIBw-QRv-HGEBz0EUc4RBUz90YEBEyIvvend0hLOEg06WUYPgSmBR6CfdkUhE1quZIeQOPOxGcCvMpmvNBtNMYUb/s1600-h/Screen+shot+2010-02-03+at+9.47.01+AM.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-bottom: 1em; margin-left: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpdCObjKwG9m9TLp6lnuswVKdTVv9KgB5DHUCA1MLiQMRHn82d4IkrxIBw-QRv-HGEBz0EUc4RBUz90YEBEyIvvend0hLOEg06WUYPgSmBR6CfdkUhE1quZIeQOPOxGcCvMpmvNBtNMYUb/s320/Screen+shot+2010-02-03+at+9.47.01+AM.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;jslint&lt;/b&gt;. &lt;a href=&quot;http://github.com/hallettj/jslint.vim&quot;&gt;This plugin&lt;/a&gt; is easier to install, though it doesn&#39;t have the same on-the-fly checking as the pyflakes plugin.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;$ git clone git://github.com/hallettj/jslint.vim.git
$ cd jslint.vim
$ rake install&lt;/pre&gt;&lt;br /&gt;
Then you can run jslint easily from within Vim:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;:JSLint&lt;/pre&gt;&lt;br /&gt;
That&#39;ll give you a nice quickfix window with all your extant problems.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAPwxH-HingCj1LT69on3Ydpu-l7V2_PBx8d7Tu6gMA87_LXTymlDBCWAcapBIC5Pr9HjZCpf_2-TvDhyHwkX8jeK13tNnONyjGZC2RN9ml0muAku_1ofyCjlZF4p3ItL8ae-CXxWCA3ku/s1600-h/Screen+shot+2010-02-03+at+9.59.53+AM.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAPwxH-HingCj1LT69on3Ydpu-l7V2_PBx8d7Tu6gMA87_LXTymlDBCWAcapBIC5Pr9HjZCpf_2-TvDhyHwkX8jeK13tNnONyjGZC2RN9ml0muAku_1ofyCjlZF4p3ItL8ae-CXxWCA3ku/s320/Screen+shot+2010-02-03+at+9.59.53+AM.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
To have it run on save, simply add to &lt;code&gt;~/.vimrc&lt;/code&gt;:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;autocmd BufWritePost *.js JSLint&lt;/pre&gt;&lt;br /&gt;
Finally, you can also pass options into jslint by putting them in &lt;code&gt;~/.jslintrc&lt;/code&gt;. For example, because I&#39;m working with a few global namespaces/functions defined in other files, I have:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;/* global Ext, Zenoss, _t */&lt;/pre&gt;&lt;br /&gt;
That way, you can eliminate any noise and get right to the trailing commas.&lt;br /&gt;
&lt;br /&gt;
I may try my hand at hacking the jslint plugin to do the same inline highlighting as the pyflakes plugin.&lt;br /&gt;
&lt;/li&gt;
&lt;/ol&gt;Anyway, there it is. Definitely helps keep the silly typos down.</content><link rel='replies' type='application/atom+xml' href='http://concisionandconcinnity.blogspot.com/feeds/1873797702944997518/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8427412129779288317/1873797702944997518' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/1873797702944997518'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/1873797702944997518'/><link rel='alternate' type='text/html' href='http://concisionandconcinnity.blogspot.com/2010/02/run-pyflakesjslint-automatically-in-vim.html' title='Run pyflakes/jslint automatically in Vim'/><author><name>Ian McCracken</name><uri>http://www.blogger.com/profile/11153934961249932934</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpdCObjKwG9m9TLp6lnuswVKdTVv9KgB5DHUCA1MLiQMRHn82d4IkrxIBw-QRv-HGEBz0EUc4RBUz90YEBEyIvvend0hLOEg06WUYPgSmBR6CfdkUhE1quZIeQOPOxGcCvMpmvNBtNMYUb/s72-c/Screen+shot+2010-02-03+at+9.47.01+AM.png" height="72" width="72"/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8427412129779288317.post-4163878870817078949</id><published>2009-09-20T22:53:00.000-04:00</published><updated>2010-02-17T11:32:19.202-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ExtJS"/><category scheme="http://www.blogger.com/atom/ns#" term="javascript"/><category scheme="http://www.blogger.com/atom/ns#" term="Python"/><title type='text'>Ext.Direct remoting in Django</title><content type='html'>Well, as I didn&#39;t have a lot of other things weighing on me today, I created a Django implementation of &lt;a href=&quot;http://pypi.python.org/pypi/extdirect/0.2&quot;&gt;the extdirect package I released earlier today&lt;/a&gt;. It&#39;s slightly more involved to set up, probably due to my not knowing Django nearly as well as Zope (suggestions welcome, Django enthusiasts), but after the initial setup writing the router classes is just as easy. Here&#39;s how to do it.&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;ol&gt;&lt;li&gt;Add &#39;extdirect.django&#39; to INSTALLED_APPS in settings.py. This of course assumes you have extdirect installed.&lt;br /&gt;
&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;In views.py, define your router class and register it:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;from extdirect.django import DirectRouter, register_router

class MyRouter(DirectRouter):
    def uppercase(self, word):
        return word.upper()
    def lowercase(self, word):
        return word.lower()

register_router(MyRouter, &#39;Remote&#39;)
&lt;/pre&gt;The arguments to register_router are the router class, the client-side&lt;br /&gt;
namespace, and an optional url under /extdirect at which the router should be&lt;br /&gt;
available (defaults to the name of the class).  &lt;br /&gt;
&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;In the __init__.py of your app, add:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;import views
&lt;/pre&gt;This is so that the router classes will be registered on startup; it should&lt;br /&gt;
have no other effect. &lt;br /&gt;
&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;In the root URLconf, map the extdirect urls by adding:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;(r&#39;^extdirect/&#39;, include(&#39;extdirect.django.urls&#39;))

&lt;/pre&gt;&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;In your template, load the provider definitions:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;djangotemplate&quot; name=&quot;code&quot;&gt;{% load direct_providers %}
    {% direct_providers %}
&lt;/pre&gt;If you don&#39;t have Ext on the page already, you can write a stripped-down&lt;br /&gt;
version directly to the page by adding +direct.js to the template tag:&lt;br /&gt;
&lt;br /&gt;
&lt;pre name=&quot;code&quot;&gt;{% direct_providers +direct.js %}


&lt;/pre&gt;&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;That&#39;s it. You should now have access on that template to the remote&lt;br /&gt;
methods:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;javascript&quot; name=&quot;code&quot;&gt;Remote.MyRouter.uppercase({word:&#39;a word&#39;}, callback);&amp;nbsp;&lt;/pre&gt;&lt;pre class=&quot;javascript&quot; name=&quot;code&quot;&gt;&amp;nbsp; 
&lt;/pre&gt;&lt;br /&gt;
&lt;/li&gt;
&lt;/ol&gt;After that, as long as you put your router classes in views.py and register them with &lt;code&gt;register_router&lt;/code&gt;, everything should be available in the client automatically. Neat, no?&lt;br /&gt;
&lt;br /&gt;
If any Djangoers out there know how to do this more elegantly (in particular, some way to obviate the need to import views in __init__.py would be nice) please let me know. For now, though, I think it&#39;s pretty useful.</content><link rel='replies' type='application/atom+xml' href='http://concisionandconcinnity.blogspot.com/feeds/4163878870817078949/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8427412129779288317/4163878870817078949' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/4163878870817078949'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/4163878870817078949'/><link rel='alternate' type='text/html' href='http://concisionandconcinnity.blogspot.com/2009/09/extdirect-remoting-in-django.html' title='Ext.Direct remoting in Django'/><author><name>Ian McCracken</name><uri>http://www.blogger.com/profile/11153934961249932934</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8427412129779288317.post-2460930756829649459</id><published>2009-09-20T12:47:00.003-04:00</published><updated>2010-02-17T11:34:10.429-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ExtJS"/><category scheme="http://www.blogger.com/atom/ns#" term="javascript"/><category scheme="http://www.blogger.com/atom/ns#" term="Python"/><category scheme="http://www.blogger.com/atom/ns#" term="Zope"/><title type='text'>Ext.Direct remoting in Zope</title><content type='html'>With ExtJS 3.0 came &lt;a href=&quot;http://www.extjs.com/products/extjs/direct.php&quot;&gt;Ext.Direct&lt;/a&gt;,  an excellent library for remoting server-side methods to the client side. We had already made the decision to switch to Ext with the revamped &lt;a href=&quot;http://www.zenoss.com/&quot;&gt;Zenoss&lt;/a&gt; UI, so I&#39;d been working with Ext quite a lot; in the upcoming 2.5 release, there&#39;s a &lt;a href=&quot;http://community.zenoss.org/people/simon/blog/2009/09/16/new-event-console-default-settings&quot;&gt;brand-new event console&lt;/a&gt; that makes heavy use of Ext.Direct. After learning its ins and outs with all that work, I decided to write a Python version of the server-side component to make it easier to use; I then went further and created a Zope 2 and 3 compatible component that makes it trivial. &lt;a href=&quot;http://pypi.python.org/pypi/extdirect/0.2&quot;&gt;extdirect&lt;/a&gt; is the result. Here&#39;s some brief instruction on how to use it.&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
In a nutshell, Ext.Direct allows one to create a client-side API that maps to a server-side API. One creates a &quot;router&quot; on the server, available at a given URL, that can accept requests specifying a method name, route them to the correct method, and return the result. As a brief, non-working example (ignoring framework), if the server-side router provided some methods:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;class Router(object):
    def uppercase(self, word):
        return word.upper()
    def lowercase(self, word):
        return word.lower()
&lt;/pre&gt;&lt;br /&gt;
And that class were available at some URL, Ext.Direct lets you define a client-side provider that allows you to call those methods, without mapping each method to a separate URL:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;javascript&quot; name=&quot;code&quot;&gt;function callback(result) {
    ... do something ...
}
Remote.Router.uppercase({word:&#39;ext.direct&#39;}, callback);
Remote.Router.lowercase({word:&#39;EXT.DIRECT&#39;}, callback);
&lt;/pre&gt;&lt;br /&gt;
Ideally, the server would be able to create the provider definition by inspecting the class, generating JavaScript that could be inserted into a template. extdirect provides a router base class and a function that can generate its provider definition.&lt;br /&gt;
&lt;br /&gt;
I&#39;ll focus on the Zope-specific implementation, because it makes it so easy, but the base class can be used in any framework. I&#39;m thinking I&#39;ll add a Django implementation next.&lt;br /&gt;
&lt;br /&gt;
So, here&#39;s how to hook up a router in Zope, assuming extdirect is installed and available:&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;&lt;b&gt;Define your class.&lt;/b&gt; &lt;code&gt;DirectRouter&lt;/code&gt; is a subclass of &lt;code&gt;BrowserView&lt;/code&gt;, so use &lt;code&gt;self.context&lt;/code&gt; and &lt;code&gt;self.request&lt;/code&gt; at will. Let&#39;s say this goes into &lt;code&gt;utils.py&lt;/code&gt;:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;from extdirect.zope import DirectRouter

class Utils(DirectRouter):
    
    def contextid(self):
        return self.context.id

    def uppercase(self, word):
        return word.upper()
&lt;/pre&gt;&lt;br /&gt;
Make sure to return something JSON-serializable.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Register the class as an Ext.Direct router in ZCML&lt;/b&gt;. This step both registers a view for the router and registers a viewlet containing the JavaScript required to create the client-side provider (against the included extdirect viewlet manager):&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;xml&quot; name=&quot;code&quot;&gt;&amp;lt;configure xmlns=&quot;http://namespaces.zope.org/browser&quot;&amp;gt;

     &amp;lt;include package=&quot;extdirect.zope&quot; file=&quot;meta.zcml&quot;/&amp;gt;

       &amp;lt;directRouter 
           class=&quot;.utils.Utils&quot; 
           name=&quot;remote_utils&quot; 
           namespace=&quot;Remote&quot;
           /&amp;gt;

&amp;lt;/configure&amp;gt;&lt;/pre&gt;The class, of course, refers to the &lt;code&gt;DirectRouter&lt;/code&gt; subclass you defined in step 1. The name is like a normal &lt;code&gt;BrowserView&lt;/code&gt; name, and will be the URL at which your router will be available. The namespace is the namespace in the client at which the methods will be accessible, and can be longer (if you already have a namespace set up, you could do &quot;Zenoss.server.remote&quot; or whatever you like). This provider will be available at [namespace].[class name], so in this case, &lt;code&gt;Remote.Utils&lt;/code&gt;.&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Include the viewlet manager in a page template.&lt;/b&gt; You have two options here. If you&#39;ve already got Ext loaded through other means (say it&#39;s the framework you&#39;re using on the site), you can use:&lt;br /&gt;
&lt;pre name=&quot;code&quot; class=&quot;xml&quot;&gt;&amp;lt;tal:block tal:content=&quot;structure provider:extdirect&quot;/&amp;gt;&lt;/pre&gt;If you&#39;re not using Ext already, and don&#39;t want to, you can use a stripped-down version including only those components necessary for Ext.Direct, which is included in extdirect and registered as a resource and a viewlet:&lt;br /&gt;
&lt;pre name=&quot;code&quot; class=&quot;xml&quot;&gt;&amp;lt;tal:block tal:content=&quot;structure provider:extdirect+direct.js&quot;/&amp;gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Call methods at will.&lt;/b&gt; That&#39;s all. You can now call methods on that namespace and class:&lt;br /&gt;
&lt;pre name=&quot;code&quot; class=&quot;javascript&quot;&gt;Remote.Utils.contextid({}, callback);
Remote.Utils.uppercase({word:&#39;A word&#39;}, callback);
&lt;/pre&gt;&lt;/ol&gt;And that&#39;s all there is to it. It couldn&#39;t be simpler. Your router is available at &lt;code&gt;./remote_utils&lt;/code&gt;, but Ext.Direct handles all the communication so you don&#39;t ever really need to use it yourself.  Further details of the base class are available at &lt;a href=&quot;http://pypi.python.org/pypi/extdirect/0.2&quot;&gt;the project page on PyPI&lt;/a&gt;. Code is hosted on &lt;a href=&quot;http://code.google.com/p/extdirect/source/browse/trunk&quot;&gt;Google Code&lt;/a&gt;.</content><link rel='replies' type='application/atom+xml' href='http://concisionandconcinnity.blogspot.com/feeds/2460930756829649459/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8427412129779288317/2460930756829649459' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/2460930756829649459'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/2460930756829649459'/><link rel='alternate' type='text/html' href='http://concisionandconcinnity.blogspot.com/2009/09/remoting-server-side-methods-to-client.html' title='Ext.Direct remoting in Zope'/><author><name>Ian McCracken</name><uri>http://www.blogger.com/profile/11153934961249932934</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8427412129779288317.post-7698914016962745832</id><published>2009-07-06T10:00:00.007-04:00</published><updated>2010-02-17T13:11:34.128-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="vim"/><title type='text'>Vim, Part II: Matching Pairs</title><content type='html'>One of the more standard vim tweaks is to have parens, braces, brackets, and quotes auto-close&amp;mdash;that is, you type &lt;code&gt;(&lt;/code&gt;, and the &lt;code&gt;)&lt;/code&gt; is inserted automatically. This can be a big help in keeping track of nested groups. This is easy to do poorly; to do it well takes a little more thought. I took a look at TextMate&#39;s rules and mimicked them (mostly). Put all this stuff in your .vimrc (_vimrc on Windows).&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;em&gt;Note: A couple of these tweaks are modified or copied outright from the &lt;a target=&quot;_blank&quot; href=&quot;http://www.vim.org/scripts/index.php&quot;&gt;Vim Scripts repository&lt;/a&gt;.&lt;/em&gt;&lt;br /&gt;
&lt;h3&gt;Auto-close pairs&lt;/h3&gt;&lt;br /&gt;
The initial auto-close is quite easy; leaving the group is slightly more involved. If you&#39;re inside parens, at the end, and you want to leave them, instinct is just to type the closing &lt;code&gt;)&lt;/code&gt;; without a little logic, however, that will in no way achieve the desired result. The rule is: if typing the closing character would end the block &lt;em&gt;without&lt;/em&gt; auto-close (that is, because it&#39;s the character to the immediate right of the cursor), it should end it with auto-close. Here&#39;s how you achieve that.&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Map the auto-close for non-quotes&lt;/strong&gt;. This is trivial:&lt;br /&gt;
&lt;pre name=&quot;code&quot;&gt;inoremap ( ()&amp;lt;Left&amp;gt;
inoremap [ []&amp;lt;Left&amp;gt;
inoremap { {}&amp;lt;Left&amp;gt;
autocmd Syntax html,vim inoremap &amp;lt; &amp;lt;lt&amp;gt;&amp;gt;&amp;lt;Left&amp;gt;&lt;/pre&gt;&lt;br /&gt;
No big deal; when the opening character is typed, insert the closing character and hit &amp;lt;Left&amp;gt; so the cursor is between them.&lt;/li&gt;

&lt;li&gt;&lt;strong&gt;Function to check next character&lt;/strong&gt;.&lt;br /&gt;
&lt;pre name=&quot;code&quot;&gt;function! ClosePair(char)
if getline(&#39;.&#39;)[col(&#39;.&#39;) - 1] == a:char
return &quot;\&amp;lt;Right&amp;gt;&quot;
else
return a:char
endif
endf&lt;/pre&gt;&lt;/li&gt;

&lt;li&gt;&lt;strong&gt;Map closing characters&lt;/strong&gt;. Now, all you need to do is link the closing characters to the function:&lt;br /&gt;
&lt;pre name=&quot;code&quot;&gt;inoremap ) &amp;lt;c-r&amp;gt;=ClosePair(&#39;)&#39;)&amp;lt;CR&amp;gt;
inoremap ] &amp;lt;c-r&amp;gt;=ClosePair(&#39;]&#39;)&amp;lt;CR&amp;gt;
inoremap } &amp;lt;c-r&amp;gt;=ClosePair(&#39;}&#39;)&amp;lt;CR&amp;gt;&lt;/pre&gt;&lt;/li&gt;

&lt;li&gt;&lt;strong&gt;Map the auto-close for quotes&lt;/strong&gt;. This is only slightly more difficult than &lt;code&gt;()[]{}&lt;/code&gt;, because of the case where you might add an escaped quotation mark to a string. It requires a separate function: since the opening and closing characters are the same, we can&#39;t map the closing to &lt;code&gt;ClosePair&lt;/code&gt;. So we put it all in one function:&lt;br /&gt;
&lt;pre name=&quot;code&quot;&gt;function! QuoteDelim(char)
let line = getline(&#39;.&#39;)
let col = col(&#39;.&#39;)
if line[col - 2] == &quot;\\&quot;
&quot;Inserting a quoted quotation mark into the string
return a:char
elseif line[col - 1] == a:char
&quot;Escaping out of the string
return &quot;\&amp;lt;Right&amp;gt;&quot;
else
&quot;Starting a string
return a:char.a:char.&quot;\&amp;lt;Left&amp;gt;&quot;
endif
endf 
&lt;/pre&gt;&lt;br /&gt;
Then just need to map that:&lt;br /&gt;
&lt;pre name=&quot;code&quot;&gt;inoremap &quot; &amp;lt;c-r&amp;gt;=QuoteDelim(&#39;&quot;&#39;)&amp;lt;CR&amp;gt;
inoremap &#39; &amp;lt;c-r&amp;gt;=QuoteDelim(&quot;&#39;&quot;)&amp;lt;CR&amp;gt;
&lt;/pre&gt;&lt;br /&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;br /&gt;
Dump all that in to &lt;code&gt;.vimrc&lt;/code&gt;, source it or restart vim, and try typing some stuff! You may notice a few places where it isn&#39;t quite what you&#39;d want, though, which brings us to our next two tweaks.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Wrap visually selected text&lt;/h3&gt;&lt;br /&gt;
One of the nicer minor features of TextMate is its treatment of highlighted text. If you have something highlighted and type &lt;code&gt;a&lt;/code&gt;, it replaces the text, like other editors. If you type &lt;code&gt;(&lt;/code&gt;, however, it wraps the selected text in parentheses. This is enormously useful. Luckily, it&#39;s very easy to recreate in Vim:&lt;br /&gt;
&lt;pre name=&quot;code&quot;&gt;vnoremap (  &amp;lt;ESC&amp;gt;`&amp;gt;a)&amp;lt;ESC&amp;gt;`&amp;lt;i(&amp;lt;ESC&amp;gt;
vnoremap )  &amp;lt;ESC&amp;gt;`&amp;gt;a)&amp;lt;ESC&amp;gt;`&amp;lt;i(&amp;lt;ESC&amp;gt;
vnoremap {  &amp;lt;ESC&amp;gt;`&amp;gt;a}&amp;lt;ESC&amp;gt;`&amp;lt;i{&amp;lt;ESC&amp;gt;
vnoremap }  &amp;lt;ESC&amp;gt;`&amp;gt;a}&amp;lt;ESC&amp;gt;`&amp;lt;i{&amp;lt;ESC&amp;gt;
vnoremap &quot;  &amp;lt;ESC&amp;gt;`&amp;gt;a&quot;&amp;lt;ESC&amp;gt;`&amp;lt;i&quot;&amp;lt;ESC&amp;gt;
vnoremap &#39;  &amp;lt;ESC&amp;gt;`&amp;gt;a&#39;&amp;lt;ESC&amp;gt;`&amp;lt;i&#39;&amp;lt;ESC&amp;gt;
vnoremap `  &amp;lt;ESC&amp;gt;`&amp;gt;a`&amp;lt;ESC&amp;gt;`&amp;lt;i`&amp;lt;ESC&amp;gt;
vnoremap [  &amp;lt;ESC&amp;gt;`&amp;gt;a]&amp;lt;ESC&amp;gt;`&amp;lt;i[&amp;lt;ESC&amp;gt;
vnoremap ]  &amp;lt;ESC&amp;gt;`&amp;gt;a]&amp;lt;ESC&amp;gt;`&amp;lt;i[&amp;lt;ESC&amp;gt;
&lt;/pre&gt;&lt;br /&gt;
&lt;h3&gt;Backspace in empty pair deletes both&lt;/h3&gt;&lt;br /&gt;
Now that you have auto-closing parens, let&#39;s say you type one by mistake. Unfortunately, deleting it now involves two extra keystrokes (&amp;lt;ESC&amp;gt;xx or &amp;lt;Right&amp;gt;&amp;lt;Bksp&amp;gt;&amp;lt;Bksp&amp;gt; instead of &amp;lt;Bksp&amp;gt;). TextMate gets around this by deleting both if you&#39;re in an empty block. I was tired of mapping the same thing for every pair, though, so I wrote a function that uses the &lt;code&gt;matchpairs&lt;/code&gt; option, which exists to tell Vim what delineates a discrete block, to figure it out dynamically (thanks to &lt;a href=&quot;http://plasmasturm.org/&quot; target=&quot;_blank&quot;&gt;Aristotle Pagaltzis&lt;/a&gt; for cleaning it up!). It checks whether the characters on either side of the cursor constitute a valid pair:&lt;br /&gt;
&lt;pre name=&quot;code&quot;&gt;function! InAnEmptyPair()
let cur = strpart(getline(&#39;.&#39;),getpos(&#39;.&#39;)[2]-2,2)
for pair in (split(&amp;matchpairs,&#39;,&#39;) + [&#39;&quot;:&quot;&#39;,&quot;&#39;:&#39;&quot;])
if cur == join(split(pair,&#39;:&#39;),&#39;&#39;)
return 1
endif
endfor
return 0
endfunc&lt;/pre&gt;&lt;br /&gt;
And then the actual deletion function:&lt;br /&gt;
&lt;pre name=&quot;code&quot;&gt;func! DeleteEmptyPairs()
if InAnEmptyPair()
return &quot;\&amp;lt;Left&amp;gt;\&amp;lt;Del&amp;gt;\&amp;lt;Del&amp;gt;&quot;
else
return &quot;\&amp;lt;BS&amp;gt;&quot;
endif
endfunc&lt;/pre&gt;&lt;br /&gt;
Then map it to Backspace, so it&#39;s called whenever Backspace is:&lt;br /&gt;
&lt;pre name=&quot;code&quot;&gt;inoremap &amp;lt;expr&amp;gt; &amp;lt;BS&amp;gt; DeleteEmptyPairs()&lt;/pre&gt;&lt;br /&gt;
So, in plain English: when Backspace is hit, we check to see if the characters on the immediate left and right of the cursor are part of a matching pair, looking at the &lt;code&gt;matchpairs&lt;/code&gt; option to see what counts of a valid pair. If we are in a pair, delete both characters; otherwise, send a normal Backspace.&lt;br /&gt;
&lt;br /&gt;
Next (and probably last) in the series: Whatever&#39;s left in my .vimrc, namely, assorted tweaks to make one&#39;s coding life easier.</content><link rel='replies' type='application/atom+xml' href='http://concisionandconcinnity.blogspot.com/feeds/7698914016962745832/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8427412129779288317/7698914016962745832' title='12 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/7698914016962745832'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/7698914016962745832'/><link rel='alternate' type='text/html' href='http://concisionandconcinnity.blogspot.com/2009/07/vim-part-ii-matching-pairs.html' title='Vim, Part II: Matching Pairs'/><author><name>Ian McCracken</name><uri>http://www.blogger.com/profile/11153934961249932934</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>12</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8427412129779288317.post-7754099451310302125</id><published>2009-07-03T08:02:00.011-04:00</published><updated>2010-02-17T11:29:53.334-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Python"/><category scheme="http://www.blogger.com/atom/ns#" term="vim"/><title type='text'>Vim, Part I: Improved Python syntax, Blackboard color scheme</title><content type='html'>Every few months, I notice that TextMate exists and try to use it; inevitably, the requirement that I use arrow keys defeats the endeavor. It has a few features, however, that I like, most of which I&#39;ve ported to vim, my primary text editor. These features are too numerous for a single post, so I&#39;ll highlight them in a series. First up: An improved Python syntax definition and my favorite color scheme.&lt;br /&gt;&lt;span class=”fullpost”&gt;&lt;br /&gt;&lt;h3&gt;Python Syntax&lt;/h3&gt;&lt;br /&gt;&lt;a href=&quot;http://www.vim.org/scripts/script.php?script_id=790&quot; target=&quot;_blank&quot;&gt;The most popular Python syntax highlighting script&lt;/a&gt; is pretty complete, but it&#39;s missing a few things (Update: several of the improvements I made a year ago have been added to Dmitry Vasiliev&#39;s script, such as decorators, doctests, imports, etc. I&#39;ve merged the two, so this is slightly less special than before). Specifically: docstrings, &lt;code&gt;self&lt;/code&gt;, arguments/base classes, assignment as distinct from other operators, and a few other little things. TextMate had these, so I ported them, and the results were pretty good.&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;http://ianbits.googlecode.com/svn/trunk/vim/python.vim&quot;&gt;Download the improved syntax file&lt;/a&gt; and drop it into &lt;code&gt;~/.vim/syntax&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Blackboard Color Scheme&lt;/h3&gt;&lt;br /&gt;TextMate had a color scheme that I loved: Blackboard. The colors were primaryish without being irritatingly bright or clashing; keywords, builtins, and class definitions were given appropriate prominence. I ported it to Vim. Here&#39;s what it looks like:&lt;br /&gt;&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhliPrSau-8Ero1WaialWfo5QdsArRd8EnURwAbCEL_Eqo3PSbltaPdREc7871c5jF4HTkBrry9DTJGdK9yDZg61nuOdxkSgEOOJWblwWCOb1Pnt-21_8g2GcQXS8MxPWgS-H-_zqLaAEjN/s1600-h/Picture+1.png&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 336px; height: 400px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhliPrSau-8Ero1WaialWfo5QdsArRd8EnURwAbCEL_Eqo3PSbltaPdREc7871c5jF4HTkBrry9DTJGdK9yDZg61nuOdxkSgEOOJWblwWCOb1Pnt-21_8g2GcQXS8MxPWgS-H-_zqLaAEjN/s400/Picture+1.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5354216212568127106&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;That example doesn&#39;t include a doctest, but they are highlighted separately in the same color as builtins.&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;http://ianbits.googlecode.com/svn/trunk/vim/blackboard.vim&quot;&gt;Download the color scheme&lt;/a&gt; and drop it into &lt;code&gt;~/.vim/colors&lt;/code&gt;, then do &lt;code&gt;:colorscheme blackboard&lt;/code&gt; to see the results. Pretty easy on the eyes, no?&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Next in my Vim series: automatic handling of pair completion (and deletion).</content><link rel='replies' type='application/atom+xml' href='http://concisionandconcinnity.blogspot.com/feeds/7754099451310302125/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8427412129779288317/7754099451310302125' title='24 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/7754099451310302125'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/7754099451310302125'/><link rel='alternate' type='text/html' href='http://concisionandconcinnity.blogspot.com/2009/07/vim-part-i-improved-python-syntax.html' title='Vim, Part I: Improved Python syntax, Blackboard color scheme'/><author><name>Ian McCracken</name><uri>http://www.blogger.com/profile/11153934961249932934</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhliPrSau-8Ero1WaialWfo5QdsArRd8EnURwAbCEL_Eqo3PSbltaPdREc7871c5jF4HTkBrry9DTJGdK9yDZg61nuOdxkSgEOOJWblwWCOb1Pnt-21_8g2GcQXS8MxPWgS-H-_zqLaAEjN/s72-c/Picture+1.png" height="72" width="72"/><thr:total>24</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8427412129779288317.post-5241020091520684505</id><published>2009-06-06T11:32:00.009-04:00</published><updated>2010-02-17T11:31:34.002-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="AppleScript"/><category scheme="http://www.blogger.com/atom/ns#" term="Mac OS X"/><category scheme="http://www.blogger.com/atom/ns#" term="OmniFocus"/><title type='text'>Plaintext task list from OmniFocus with AppleScript</title><content type='html'>I live in &lt;a href=&quot;http://www.omnigroup.com/applications/omnifocus/&quot; target=&quot;_blank&quot;&gt;OmniFocus&lt;/a&gt; most of the day. I have lots of perspectives set up to help me parse my various responsibilities (see my previous post, &lt;a href=&quot;http://concisionandconcinnity.blogspot.com/2009/03/how-i-use-omnifocus.html&quot;&gt;&quot;How I Use OmniFocus,&quot;&lt;/a&gt; for more details); in particular, I have one perspective called &quot;Global Tasks&quot; (View Mode: Context, Filter: Active, Grouping: Ungrouped, Sorting: Due, Action Filter: Next Action) that essentially gives me a list, ordered by dueness, of what I should be doing right now.&lt;br /&gt;&lt;br /&gt;It helps me focus to have that list of tasks constantly in front of my eyes. This is counterbalanced by my desire to save screen real estate for the assorted terminal windows and browsers I need up to do my job; OmniFocus takes up valuable real estate. So I set about writing a quick script to pull tasks from OmniFocus, format them, and return them as plain text, which I could embed on my desktop with GeekTool. &lt;br /&gt;&lt;span class=”fullpost”&gt;&lt;br /&gt;&lt;pre&gt;set taskList to &quot;&quot;&lt;br /&gt;tell application &quot;OmniFocus&quot;&lt;br /&gt;    tell the default document to tell the front document window&lt;br /&gt;        set perspective name to &quot;Global Tasks&quot;&lt;br /&gt;        set oTrees to trees of content&lt;br /&gt;        set n to count of oTrees&lt;br /&gt;        repeat with i from 1 to n&lt;br /&gt;            set oTask to value of (item i of oTrees)&lt;br /&gt;            set taskTitle to name of oTask&lt;br /&gt;            set projTitle to name of containing project of oTask&lt;br /&gt;            set taskList to taskList &amp; taskTitle &amp; &lt;br /&gt;                &quot;  (&quot; &amp; projTitle &amp; &quot;)\n&quot;&lt;br /&gt;        end repeat&lt;br /&gt;    end tell&lt;br /&gt;end tell&lt;br /&gt;return taskList&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I saved that as &lt;code&gt;~/src/omnifocus_tasks.scpt&lt;/code&gt; and added a Command item to GeekTool that ran &lt;code&gt;osascript /Users/ian/src/omnifocus_tasks.scpt&lt;/code&gt; every minute. The result was exactly what I wanted:&lt;br /&gt;&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_WfsMnkb-r_ZbozgqFUt275TegwB8B6RBu6B5VR0xZ2OplbN_sSCAFbvUE8sgPTTrfJO_oo_Lerwam875PH2OKwhyphenhyphenOGwwwEgonFYA6vfvjJ7ILl1wmWjeLCw3jFpmEyHvge32xxQxuKif/s1600-h/Picture+2.png&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 228px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_WfsMnkb-r_ZbozgqFUt275TegwB8B6RBu6B5VR0xZ2OplbN_sSCAFbvUE8sgPTTrfJO_oo_Lerwam875PH2OKwhyphenhyphenOGwwwEgonFYA6vfvjJ7ILl1wmWjeLCw3jFpmEyHvge32xxQxuKif/s320/Picture+2.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5344242449608126834&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Now, even if I hide OmniFocus to make room for something else, I can&#39;t forget to check for my next tasks and keep everything up to date.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;</content><link rel='replies' type='application/atom+xml' href='http://concisionandconcinnity.blogspot.com/feeds/5241020091520684505/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8427412129779288317/5241020091520684505' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/5241020091520684505'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/5241020091520684505'/><link rel='alternate' type='text/html' href='http://concisionandconcinnity.blogspot.com/2009/06/plaintext-task-list-from-omnifocus-with.html' title='Plaintext task list from OmniFocus with AppleScript'/><author><name>Ian McCracken</name><uri>http://www.blogger.com/profile/11153934961249932934</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_WfsMnkb-r_ZbozgqFUt275TegwB8B6RBu6B5VR0xZ2OplbN_sSCAFbvUE8sgPTTrfJO_oo_Lerwam875PH2OKwhyphenhyphenOGwwwEgonFYA6vfvjJ7ILl1wmWjeLCw3jFpmEyHvge32xxQxuKif/s72-c/Picture+2.png" height="72" width="72"/><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8427412129779288317.post-4026446971205464121</id><published>2009-06-01T09:00:00.002-04:00</published><updated>2010-02-17T11:34:10.429-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Python"/><category scheme="http://www.blogger.com/atom/ns#" term="Zope"/><title type='text'>Rapid development egg to RPM with buildout</title><content type='html'>I&#39;m developing a feature for Zenoss that lives in its own Zope product. For the time being, it&#39;s fairly independent of Zenoss and can be demoed in a separate Zope instance. So I have a buildout for the Zope environment, also setting up a copy of the new product as a development egg (see &lt;a href=&quot;http://plone.org/documentation/tutorial/buildout/creating-a-new-package&quot;&gt;this tutorial&lt;/a&gt; for an example of how to set that up). I have that development egg pulled into the Zope demo buildout via svn:externals.&lt;br /&gt;&lt;br /&gt;At certain points in the development cycle, however, I want to send QA an RPM of the demo instance, with my package installed, so they can test it. This is kind of a hassle if I have to build the egg of the product and get it in the buildout directory on the build box in order to generate an RPM. Nor can I simply leave it as a development egg, because the package wouldn&#39;t be in the same place on the test box, so the link would fail.&lt;br /&gt;&lt;br /&gt;So here&#39;s an easy way to turn a development egg into a production egg for the purposes of creating an RPM, using a separate buildout config file.&lt;br /&gt;&lt;span class=&quot;fullpost&quot;&gt;&lt;br /&gt;You&#39;ll want to read &lt;a href=&quot;http://concisionandconcinnity.blogspot.com/2009/05/zc.html&quot;&gt;this tutorial&lt;/a&gt; on basic RPM creation using zc.buildout. In addition, the buildout.cfg will include a develop option in the buildout section:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;config&quot;&gt;[buildout]&lt;br /&gt;...&lt;br /&gt;develop = src/Products.MyCustomProduct&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;(&lt;code&gt;src&lt;/code&gt; exists in my demo directory, and has the svn:externals definition pointing at the custom package.) The egg is also included in the instance section:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;config&quot;&gt;[instance]&lt;br /&gt;...&lt;br /&gt;eggs = &lt;br /&gt;    ${buildout:eggs}&lt;br /&gt;    Products.MyCustomProduct&lt;br /&gt;&lt;br /&gt;zcml = Products.MyCustomProduct&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This gets me a development buildout, in which I can work on the package in &lt;code&gt;src/Products.MyCustomProduct&lt;/code&gt; without building eggs or anything.&lt;br /&gt;&lt;br /&gt;Now when it comes time to build the RPM, we need to modify the rpm config file from the tutorial to build Products.MyCustomProduct as an egg, then install it. We&#39;ll use the &lt;a href=&quot;http://pypi.python.org/pypi/z3c.recipe.egg&quot;&gt;z3c.recipe.egg&lt;/a&gt;, which lets you run setup.py in a source package with arbitrary arguments. Here&#39;s the new rpm.cfg:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;config&quot;&gt;[buildout]&lt;br /&gt;extends = buildout.cfg&lt;br /&gt;develop = &lt;br /&gt;parts = &lt;br /&gt;    MyCustomProduct-build&lt;br /&gt;    zope2&lt;br /&gt;    instance&lt;br /&gt;    zopepy&lt;br /&gt;    myzope&lt;br /&gt;&lt;br /&gt;[MyCustomProduct-build]&lt;br /&gt;recipe = z3c.recipe.egg:setup&lt;br /&gt;setup = src/Products.MyCustomProduct&lt;br /&gt;args = bdist_egg&lt;br /&gt;&lt;br /&gt;[instance]&lt;br /&gt;find-links = &lt;br /&gt;    ${buildout:find-links}&lt;br /&gt;    ${MyCustomProduct-build:setup}/dist&lt;br /&gt;&lt;br /&gt;[myzope]&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Explanation. So, the egg is already referenced in &lt;code&gt;buildout.cfg&lt;/code&gt; as one of the instance eggs. All we do in this one is remove its declaration as a development egg (with &lt;code&gt;develop=&lt;/code&gt; in the buildout section), add a part to create the egg artifact (MyCustomProduct-build), and tell the instance section where to find it (expanded find-links).&lt;br /&gt;&lt;br /&gt;z3c.recipe.egg just needs the setup option defined, which points at a package directory containing setup.py. It&#39;ll install by default, whereas we just want it to build an egg, so we specify custom args with the args option.&lt;br /&gt;&lt;br /&gt;Notice that we have to build the egg first. This is obvious upon reflection, but the first time I just added MyCustomProduct-build to the parts instead of making sure it was first, which meant that buildout couldn&#39;t find it in the instance step, because it didn&#39;t exist yet.&lt;br /&gt;&lt;br /&gt;This will create a deployment instance just fine on its own, in the current directory. But in the RPM example, we build out somewhere else, so we&#39;ll have to update the buildout command in the spec file so that it can tell buildout where to find the source of the custom package. This is a modification to the &lt;code&gt;%build&lt;/code&gt; script:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;config&quot;&gt;%build&lt;br /&gt;...&lt;br /&gt;bin/buildout -c rpm.cfg buildout:directory=%{installdir} MyCustomProduct-build:setup=%{sourcedir}/src/Products/ZenUI3&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Then we build the RPM as in the tutorial. This time, it&#39;ll package up the egg in its current state and install that artifact into the Zope instance. This means that I can generate an RPM for QA on the fly with a single command, which is lots easier than assembling the parts manually.&lt;br /&gt;&lt;/span&gt;</content><link rel='replies' type='application/atom+xml' href='http://concisionandconcinnity.blogspot.com/feeds/4026446971205464121/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8427412129779288317/4026446971205464121' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/4026446971205464121'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/4026446971205464121'/><link rel='alternate' type='text/html' href='http://concisionandconcinnity.blogspot.com/2009/06/rapid-development-egg-to-rpm-with.html' title='Rapid development egg to RPM with buildout'/><author><name>Ian McCracken</name><uri>http://www.blogger.com/profile/11153934961249932934</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8427412129779288317.post-5136637737377462963</id><published>2009-05-31T16:00:00.005-04:00</published><updated>2010-02-17T11:34:10.430-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Python"/><category scheme="http://www.blogger.com/atom/ns#" term="Zope"/><title type='text'>Using zc.buildout to generate an RPM</title><content type='html'>&lt;a href=&quot;http://pypi.python.org/pypi/zc.buildout&quot;&gt;zc.buildout&lt;/a&gt; is a project that allows one to define the steps to create an application environment in config files. It is, roughly, a Makefile replacement, or at least can be used that way. It makes it very easy to reproduce deployments, both for development and for production, with nothing but a bootstrap and a config file.&lt;br /&gt;&lt;br /&gt;The natural next step, of course, would be to use buildout to generate installable packages like RPMs. This is less straightforward than one might expect. There are probably many ways to do it, as buildout is very versatile; I&#39;ll explain one method I&#39;ve come up with after the jump.&lt;br /&gt;&lt;span class=&quot;fullpost&quot;&gt;&lt;br /&gt;(My rpm knowledge is pretty minimal, so this may not be strictly according to doctrine, but it seems to work flawlessly in the scenarios I&#39;ve tested.)&lt;br /&gt;&lt;br /&gt;I&#39;ll use as an example the installation of a vanilla Zope 2 instance, with an rc script to control it. One may of course add one&#39;s own products to the buildout, thereby creating a custom application; that is beyond the scope of this example (see Martin Aspeli&#39;s &lt;a href=&quot;http://plone.org/documentation/tutorial/buildout&quot;&gt;excellent Plone-oriented but generally informative tutorial&lt;/a&gt;, along with the &lt;a href=&quot;http://pypi.python.org/pypi/zc.buildout&quot;&gt;buildout docs&lt;/a&gt;, etc.).&lt;br /&gt;&lt;br /&gt;Here&#39;s the config for a Zope 2.11.2 buildout:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;config&quot;&gt;[buildout]&lt;br /&gt;parts =&lt;br /&gt;    zope2&lt;br /&gt;    instance&lt;br /&gt;    zopepy&lt;br /&gt;&lt;br /&gt;find-links =&lt;br /&gt;    http://dist.plone.org&lt;br /&gt;    http://download.zope.org/distribution/&lt;br /&gt;    http://effbot.org/downloads&lt;br /&gt;&lt;br /&gt;# Add additional eggs here&lt;br /&gt;eggs =&lt;br /&gt;&lt;br /&gt;# Reference any eggs you are developing here, one per line&lt;br /&gt;# e.g.: develop = src/my.package&lt;br /&gt;develop =&lt;br /&gt;&lt;br /&gt;[zope2]&lt;br /&gt;recipe = plone.recipe.zope2install&lt;br /&gt;url = http://www.zope.org/Products/Zope/2.11.2/Zope-2.11.2-final.tgz&lt;br /&gt;&lt;br /&gt;[instance]&lt;br /&gt;recipe = plone.recipe.zope2instance&lt;br /&gt;zope2-location = ${zope2:location}&lt;br /&gt;user = admin:admin&lt;br /&gt;http-address = 8080&lt;br /&gt;debug-mode = on&lt;br /&gt;#verbose-security = on&lt;br /&gt;&lt;br /&gt;# If you want Zope to know about any additional eggs, list them here.&lt;br /&gt;# This should include any development eggs you listed in develop-eggs above,&lt;br /&gt;# e.g. eggs = ${buildout:eggs} ${plone:eggs} my.package&lt;br /&gt;eggs =&lt;br /&gt;    ${buildout:eggs}&lt;br /&gt;&lt;br /&gt;# If you want to register ZCML slugs for any packages, list them here.&lt;br /&gt;# e.g. zcml = my.package my.other.package&lt;br /&gt;zcml =&lt;br /&gt;&lt;br /&gt;products =&lt;br /&gt;    ${buildout:directory}/products&lt;br /&gt;&lt;br /&gt;[zopepy]&lt;br /&gt;recipe = zc.recipe.egg&lt;br /&gt;eggs = ${instance:eggs}&lt;br /&gt;interpreter = zopepy&lt;br /&gt;extra-paths = ${zope2:location}/lib/python&lt;br /&gt;scripts = zopepy&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Notice I&#39;m using Plone&#39;s recipes for setting up a Zope instance; they work as well as you might hope. If you bootstrap and buildout from this config file, you&#39;ll get yourself a Zope server listening at 8080, and a custom interpreter with the right paths and everything. &lt;br /&gt;&lt;br /&gt;Make a new directory containing the above config file, saved as &lt;code&gt;buildout.cfg&lt;/code&gt;, and a copy of &lt;a href=&quot;http://svn.zope.org/*checkout*/zc.buildout/trunk/bootstrap/bootstrap.py&quot;&gt;bootstrap.py&lt;/a&gt;. If you like, you can make sure it works with:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;bash&quot;&gt;&lt;br /&gt;$ python bootstrap.py&lt;br /&gt;$ bin/buildout&lt;br /&gt;$ bin/instance fg&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That&#39;ll start up Zope in the foreground.&lt;br /&gt;&lt;br /&gt;A very nice feature of zc.buildout is the ability of config files to extend others. We&#39;ll use this to create a separate buildout with the extra/different features we need for an RPM. In this minimal example, the differences are few, but you can see how it would work well for more complex buildouts. Save this as rpm.cfg in the same directory as buildout.cfg:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;config&quot;&gt;&lt;br /&gt;[buildout]&lt;br /&gt;extends = buildout.cfg&lt;br /&gt;parts += myzope&lt;br /&gt;&lt;br /&gt;[myzope]&lt;br /&gt;recipe = zc.recipe.rhrc&lt;br /&gt;parts = zoperc&lt;br /&gt;&lt;br /&gt;[zoperc]&lt;br /&gt;run-script = ${buildout:directory}/bin/instance&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Building out with this would do the same thing as our buildout.cfg, but would also install &lt;code&gt;/etc/init.d/myzope&lt;/code&gt; as a control script, thanks to &lt;a href=&quot;http://pypi.python.org/pypi/zc.recipe.rhrc/1.2&quot;&gt;zc.recipe.rhrc&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Now we get to the (slightly) tricky part. Normally, one would build an RPM by creating a source tarball, dropping it in /usr/src/redhat/SOURCES, and pointing a spec file at it. We could do that here if we wanted to. We could also use &lt;a href=&quot;http://pypi.python.org/pypi/zc.sourcerelease&quot;&gt;zc.sourcerelease&lt;/a&gt; to create a tarball, including egg dependencies and whatnot, and do the same thing (though that irritatingly requires one to build out twice). But it&#39;s an extra step. &lt;br /&gt;&lt;br /&gt;The &lt;code&gt;directory&lt;/code&gt; option to buildout lets you build out into a directory other than the current.  You can also override the config file from the command line. So the simplest method to create an RPM is just to pass the install path to buildout in the spec file. Here&#39;s a spec file that works (save it as myzope.spec in the same directory as rpm.cfg):&lt;br /&gt;&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;config&quot;&gt;%define name myzope&lt;br /&gt;%define sourcedir %(echo $PWD)&lt;br /&gt;%define installdir /opt/%{name}&lt;br /&gt;%define rcscript /etc/init.d/%{name}&lt;br /&gt;&lt;br /&gt;Name: %{name}&lt;br /&gt;Version: 1.0&lt;br /&gt;Release: 0&lt;br /&gt;Summary: My Zope Instance&lt;br /&gt;URL: http://www.zope.org&lt;br /&gt;License: GPL&lt;br /&gt;Vendor: Me&lt;br /&gt;Packager: Me &amp;lt;me@example.com&amp;gt;&lt;br /&gt;Group: Applications/Database&lt;br /&gt;Buildroot: /tmp/%{name}-buildroot&lt;br /&gt;&lt;br /&gt;%description&lt;br /&gt;%{summary}&lt;br /&gt;&lt;br /&gt;%prep&lt;br /&gt;rm -rf %{rcscript} $RPM_BUILD_ROOT %{installdir}&lt;br /&gt;mkdir -p $RPM_BUILD_ROOT %{installdir}&lt;br /&gt;&lt;br /&gt;%build&lt;br /&gt;cd %{sourcedir}&lt;br /&gt;python bootstrap.py&lt;br /&gt;bin/buildout -c rpm.cfg buildout:directory=%{installdir}&lt;br /&gt;&lt;br /&gt;%install&lt;br /&gt;echo &quot;effective-user nobody&quot; &amp;gt;&amp;gt; %{installdir}/parts/instance/etc/zope.conf&lt;br /&gt;mkdir %{installdir}/products&lt;br /&gt;mkdir -p $RPM_BUILD_ROOT/etc/init.d&lt;br /&gt;mkdir -p $RPM_BUILD_ROOT%{installdir}&lt;br /&gt;mv %{rcscript} $RPM_BUILD_ROOT%{rcscript}&lt;br /&gt;mv %{installdir}/* $RPM_BUILD_ROOT%{installdir}&lt;br /&gt;&lt;br /&gt;%files&lt;br /&gt;%defattr(-, root, root, 0755)&lt;br /&gt;%{rcscript}&lt;br /&gt;%defattr(-, nobody, nobody, 0755)&lt;br /&gt;%{installdir}&lt;br /&gt;&lt;br /&gt;%clean&lt;br /&gt;rm -rf %{rcscript} $RPM_BUILD_ROOT %{installdir}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now, step by step. First of all, the clever bit:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;config&quot;&gt;&lt;br /&gt;%define sourcedir %(echo $PWD)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This lets us find the directory with the buildout config&amp;mdash;provided, of course, that the spec file lives in the same directory as &lt;code&gt;rpm.cfg&lt;/code&gt;, and that you run &lt;code&gt;rpmbuild&lt;/code&gt; from there. Notice that we also define the directory in which this will be installed, namely /opt/myzope.&lt;br /&gt;&lt;br /&gt;The rest is basic spec file stuff, until you get to the build script:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;config&quot;&gt;%build&lt;br /&gt;cd %{sourcedir}&lt;br /&gt;python bootstrap.py&lt;br /&gt;bin/buildout -c rpm.cfg buildout:directory=%{installdir}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So we&#39;re using the rpm config file, but overriding the directory option in the buildout section to use /opt/myzope as the location in which to build out the environment. We have to do it this way&amp;mdash;instead of, say, building out in $RPM_BUILD_ROOT&amp;mdash;because the paths in the buildout would then refer to /tmp/myzope-buildroot/opt/myzope instead of /opt/myzope.&lt;br /&gt;&lt;br /&gt;Then it&#39;s as simple as doing a couple Zope-specific steps (setting the user who runs Zope), copying the resulting environment into the build root (not forgetting /etc/init.d/myzope) and letting it be packaged up. &lt;br /&gt;&lt;br /&gt;Build it with &lt;code&gt;rpmbuild -bb myzope.spec&lt;/code&gt;. Shizam, an RPM that you can install. Start it up with &lt;code&gt;/sbin/service myzope start&lt;/code&gt; and check it out in the browser.&lt;br /&gt;&lt;br /&gt;There are a few other tricks involved in separating a more complex development buildout from one that is ready for an RPM. I&#39;ll explain further in my next post.&lt;/span&gt;</content><link rel='replies' type='application/atom+xml' href='http://concisionandconcinnity.blogspot.com/feeds/5136637737377462963/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8427412129779288317/5136637737377462963' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/5136637737377462963'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/5136637737377462963'/><link rel='alternate' type='text/html' href='http://concisionandconcinnity.blogspot.com/2009/05/zc.html' title='Using zc.buildout to generate an RPM'/><author><name>Ian McCracken</name><uri>http://www.blogger.com/profile/11153934961249932934</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8427412129779288317.post-4620068792678974113</id><published>2009-04-23T11:53:00.001-04:00</published><updated>2010-02-17T11:29:53.337-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Python"/><title type='text'>cliutils has a user not named Ian!</title><content type='html'>&lt;a href=&quot;http://jasonfriedland.blogspot.com/&quot; target=&quot;_blank&quot;&gt;Jason Friedland&lt;/a&gt; has very kindly &lt;a href=&quot;http://jasonfriedland.blogspot.com/2009/03/packaging-python-cliutils-for-fedora.html&quot; target=&quot;_blank&quot;&gt;submitted my &lt;a href=&quot;http://code.google.com/p/cliutils/&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;cliutils&lt;/code&gt;&lt;/a&gt; package for inclusion in Fedora&lt;/a&gt;. Thanks! Pretty exciting to learn that somebody is using it. Now I&#39;m trying to come up with ways to improve it. Any ideas for features to add, tens of readers?</content><link rel='replies' type='application/atom+xml' href='http://concisionandconcinnity.blogspot.com/feeds/4620068792678974113/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8427412129779288317/4620068792678974113' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/4620068792678974113'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/4620068792678974113'/><link rel='alternate' type='text/html' href='http://concisionandconcinnity.blogspot.com/2009/04/jason-friedland-has-very-kindly.html' title='cliutils has a user not named Ian!'/><author><name>Ian McCracken</name><uri>http://www.blogger.com/profile/11153934961249932934</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8427412129779288317.post-908303850361023187</id><published>2009-03-10T11:00:00.002-04:00</published><updated>2009-06-06T12:19:15.728-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Python"/><title type='text'>Human-comparison sort in Python</title><content type='html'>I recently had a need to sort a large list of items (I admit freely that this list comprised episodes of Battlestar Galactica) by a subjective criterion (my liking). This turned into a minor interface problem; how best to sort these without a) a Netflix queue interface or b) dragging Excel rows around? Also, how could I implement an &lt;em&gt;efficient&lt;/em&gt; sorting algorithm, rather than waste time with the simplest &lt;a href=&quot;http://en.wikipedia.org/wiki/Bubble_sort&quot; target=&quot;_blank&quot;&gt;bubble sort&lt;/a&gt;?&lt;br /&gt;&lt;br /&gt;The solution I came up with was almost too simple to feel proud of. Python uses an &lt;a href=&quot;http://en.wikipedia.org/wiki/Adaptive_sort&quot; target=&quot;_blank&quot;&gt;adaptive mergesort&lt;/a&gt; by default, which was plenty good enough for my purposes. All I had to do, then, was have it ask a human to compare each pair of elements.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;QUESTION_TEMPLATE = &quot;&quot;&quot;&lt;br /&gt;1. %s or&lt;br /&gt;2. %s or&lt;br /&gt;3. neither? [neither] &quot;&quot;&quot;&lt;br /&gt;&lt;br /&gt;def human_cmp(a, b):&lt;br /&gt;    answer = raw_input(&lt;br /&gt;        QUESTION_TEMPLATE % (a, b))&lt;br /&gt;    if answer==&quot;1&quot;:   return -1&lt;br /&gt;    elif answer==&quot;2&quot;: return 1&lt;br /&gt;    else:             return 0&lt;br /&gt;&lt;br /&gt;EPISODES = open(&#39;bsg_episodes.txt&#39;).readlines()&lt;br /&gt;EPISODES.sort(human_cmp)&lt;br /&gt;for i, title in EPISODES:&lt;br /&gt;    print &#39;%s. %s&#39; % (i, title)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So it just uses the built-in algorithm and asks you for each comparison. Let me tell you, way better than creating a single-elimination tournament bracket in Excel.</content><link rel='replies' type='application/atom+xml' href='http://concisionandconcinnity.blogspot.com/feeds/908303850361023187/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8427412129779288317/908303850361023187' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/908303850361023187'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/908303850361023187'/><link rel='alternate' type='text/html' href='http://concisionandconcinnity.blogspot.com/2009/03/human-comparison-sort-in-python.html' title='Human-comparison sort in Python'/><author><name>Ian McCracken</name><uri>http://www.blogger.com/profile/11153934961249932934</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8427412129779288317.post-4250013147920141957</id><published>2009-03-09T07:00:00.000-04:00</published><updated>2010-02-17T11:32:57.714-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Mac OS X"/><category scheme="http://www.blogger.com/atom/ns#" term="shell"/><title type='text'>Upgrade bash to 4.0 in Mac OS X</title><content type='html'>&lt;a href=&quot;http://tiswww.case.edu/php/chet/bash/bashtop.html&quot; target=&quot;_blank&quot;&gt;bash 4.0&lt;/a&gt; was released last month, and I&#39;m only now getting around to messing with it. So far the things I use most are the &#39;**&#39; recursive globbing and the &#39;autocd&#39; option. Here&#39;s how I upgraded my system and a few ways I use the new features.&lt;br /&gt;&lt;span class=&quot;fullpost&quot;&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Download, build and install.&lt;/strong&gt; Get the source tarball:&lt;br /&gt;&lt;pre class=&quot;bash&quot; name=&quot;code&quot;&gt;$ wget ftp://ftp.cwru.edu/pub/bash/bash-4.0.tar.gz&lt;/pre&gt;&lt;br /&gt;Build and install:&lt;br /&gt;&lt;pre class=&quot;bash&quot; name=&quot;code&quot;&gt;$ tar xzf bash-4.0.tar.gz&lt;br /&gt;$ cd bash-4.0&lt;br /&gt;$ ./configure &amp;&amp; make &amp;&amp; sudo make install&lt;/pre&gt;&lt;br /&gt;On my system (Leopard + Xcode) I had no trouble compiling. The binary is installed at &lt;code&gt;/usr/local/bin/bash&lt;/code&gt;. You can also install via MacPorts or Fink, I imagine, but I try to do these things myself when possible, if only for the sake of transparency.&lt;/li&gt; &lt;br /&gt;&lt;li&gt;&lt;strong&gt;Configure to use the new shell.&lt;/strong&gt; First, register the new binary as a valid shell:&lt;br /&gt;&lt;pre class=&quot;bash&quot; name=&quot;code&quot;&gt;$ sudo bash -c &quot;echo /usr/local/bin/bash &amp;gt;&amp;gt; /private/etc/shells&quot;&lt;/pre&gt;&lt;br /&gt;Then change your user to use it as its shell with &lt;code&gt;chsh&lt;/code&gt; (this is blatantly obvious, but modify the &quot;Shell&quot; line to point to &lt;code&gt;/usr/local/bin/bash&lt;/code&gt;).&lt;br /&gt;Now open a new shell (restarting Terminal.app will do, or just Cmd-N for a new window) and make sure your changes took:&lt;br /&gt;&lt;pre class=&quot;bash&quot; name=&quot;code&quot;&gt;[ian@iansmbp] ~/&amp;gt; echo $SHELL&lt;br /&gt;/usr/local/bin/bash&lt;br /&gt;[ian@iansmbp] ~/&amp;gt; echo $BASH_VERSION&lt;br /&gt;4.0.0(1)-release&lt;/pre&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;Enable the new features.&lt;/strong&gt; There&#39;s a bunch of features in the 4.0 release that I haven&#39;t gone through yet, including &lt;code&gt;coproc&lt;/code&gt;, improved programmable completion, a new &lt;code&gt;&amp;&amp;gt;&amp;gt;&lt;/code&gt; redirect operator (synonym of the old &lt;code&gt;&amp;gt;&amp;gt;myfile 2&amp;gt;&amp;1&lt;/code&gt; pattern) and new case-insensitive expansion options. You can peruse &lt;a href=&quot;http://tiswww.case.edu/php/chet/bash/NEWS&quot; target=&quot;_blank&quot;&gt;a list of the new stuff&lt;/a&gt; at your leisure. For immediate gratification, however, just turn on recursive globbing and autocd:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;bash&quot;&gt;$ echo &quot;shopt -s globstar autocd&quot; &amp;gt;&amp;gt; ~/.profile&lt;br /&gt;$ source ~/.profile&lt;/pre&gt;&lt;br /&gt;&lt;code&gt;**&lt;/code&gt; matches contents recursively. For example, where you might previously have recursively removed all your byte-compiled Python modules with:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;bash&quot;&gt;$ find . -name \*.pyc | xargs rm -f&lt;/pre&gt;&lt;br /&gt;You can now simply:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;bash&quot;&gt;$ rm -f **/*.pyc&lt;/pre&gt;&lt;br /&gt;If you&#39;ve got a deep directory structure, and you want to get into a subdirectory whose name you know, you can replace:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;bash&quot;&gt;$ cd path/to/the/directory/i/want/named/mydir&lt;/pre&gt;&lt;br /&gt;with:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;bash&quot;&gt;$ cd **/mydir&lt;/pre&gt;&lt;br /&gt;Hey, and if those extra three characters at the beginning of that last command are too much for you, then you&#39;ll love the &lt;code&gt;autocd&lt;/code&gt; option, which, when enabled, permits you to cd to a directory merely by typing its name:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;bash&quot;&gt;[ian@iansmbp] ~/src/zenoss/core/Products/&amp;gt; **/yui&lt;br /&gt;cd ZenWidgets/skins/zenui/yui&lt;br /&gt;[ian@iansmbp] ~/src/zenoss/core/Products/ZenWidgets/skins/zenui/yui/&amp;gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;More on new features as warranted; these two, however, are those of &lt;code&gt;zsh&lt;/code&gt; that I missed most.&lt;br /&gt;&lt;/span&gt;</content><link rel='replies' type='application/atom+xml' href='http://concisionandconcinnity.blogspot.com/feeds/4250013147920141957/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8427412129779288317/4250013147920141957' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/4250013147920141957'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/4250013147920141957'/><link rel='alternate' type='text/html' href='http://concisionandconcinnity.blogspot.com/2009/03/upgrade-bash-to-40-in-mac-os-x.html' title='Upgrade bash to 4.0 in Mac OS X'/><author><name>Ian McCracken</name><uri>http://www.blogger.com/profile/11153934961249932934</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8427412129779288317.post-254988267668307993</id><published>2009-03-07T14:26:00.007-05:00</published><updated>2009-03-10T10:29:05.635-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Python"/><title type='text'>How Joseph Method Stole My Day</title><content type='html'>My friend &lt;a href=&quot;http://www.stupididea.com&quot; target=&quot;_blank&quot;&gt;Method&lt;/a&gt; shared &lt;a href=&quot;http://abstrusegoose.com/126&quot;&gt;this&lt;/a&gt; with me via Google Reader this morning, accompanied by the provocative message, &quot;Here you go, Ian.&quot; The problem, in short, was to discover the first ten-digit prime in consecutive digits of e. And a day of planned work-catching-up dissolved before it began.&lt;br /&gt;&lt;br /&gt;I had a solution in twenty minutes or so, using third-party libraries &lt;a href=&quot;http://www.dlitz.net/software/pycrypto/&quot; target=&quot;_blank&quot;&gt;pycrypto&lt;/a&gt; (for isPrime) and &lt;a href=&quot;http://code.google.com/p/mpmath/&quot; target=&quot;_blank&quot;&gt;mpmath&lt;/a&gt; (for e), but that made the whole enterprise trivial and didn&#39;t quite seem cricket. Also, it was slightly inefficient, because, being limited by the code of others, I had to calculate e to n digits first, and then check that for primes; I figured if I could calculate digits of e lazily I&#39;d get an extremely minor but nonetheless pride-inducing speed increase.&lt;br /&gt;&lt;br /&gt;To Wikipedia! where I learned scads about primality tests (luckily I didn&#39;t read the page on the constant &lt;em&gt;e&lt;/em&gt; until later, or I would have learned that this was a common puzzle that pretty much everybody tries to solve. Also I would have learned the answer). I remembered how to calculate e using the Taylor series 1 + 1/1! + 1/2! + 1/3!..., so that wasn&#39;t a problem&amp;mdash;until I ran into precision woes. &lt;br /&gt;&lt;br /&gt;That led me down a dark path whereon I attempted to program arbitrary-precision arithmetic from scratch, and learned all about mantissas and bitwise operators over the course of five hours. I got pretty far with that before I realized that I was spending my entire day trying to comprehend what I imagine amounts to at least a month of college-level CS. I went to school for other things, like French poetry.&lt;br /&gt;&lt;br /&gt;Also at this point I remembered the existence of Python&#39;s own decimal floating-point arithmetic package, &lt;code&gt;decimal&lt;/code&gt;, which had apparently dropped completely out of my brain exactly when I needed it. Thankfully, it resurfaced before I had to give up on this psychotic enterprise altogether.&lt;br /&gt;&lt;br /&gt;After that it was easy. I post the code below not because I believe it is in any way notable&amp;mdash;I&#39;m sure it&#39;s old hat to those who went to school for this&amp;mdash;but because I just want someone to know that I actually did something today. Thanks, Method.&lt;br /&gt;&lt;span class=&quot;fullpost&quot;&gt;&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;python&quot;&gt;&lt;br /&gt;from random import randint&lt;br /&gt;from operator import mul&lt;br /&gt;from collections import deque&lt;br /&gt;from decimal import Decimal, getcontext&lt;br /&gt;&lt;br /&gt;# Allow enough precision to get an answer &lt;br /&gt;getcontext().prec = 1000&lt;br /&gt;&lt;br /&gt;def factorial(x): &lt;br /&gt;    if x==1: return x&lt;br /&gt;    return reduce(mul, xrange(2, x+1))&lt;br /&gt;&lt;br /&gt;def taylor_series():&lt;br /&gt;    &quot;&quot;&quot;&lt;br /&gt;    e == 1/1! + 1/2! + 1/3! ...&lt;br /&gt;    &quot;&quot;&quot;&lt;br /&gt;    s = Decimal(1)&lt;br /&gt;    _e = Decimal(1)&lt;br /&gt;    while True:&lt;br /&gt;        _e += (Decimal(1)/factorial(s))&lt;br /&gt;        yield _e &lt;br /&gt;        s += 1&lt;br /&gt;&lt;br /&gt;def digits_of_e():&lt;br /&gt;    &quot;&quot;&quot;&lt;br /&gt;    Yield digits of e lazily by noticing when&lt;br /&gt;    they stop changing as the Taylor series &lt;br /&gt;    converges.&lt;br /&gt;    &quot;&quot;&quot;&lt;br /&gt;    idx = 2 # skip &#39;2.&#39;&lt;br /&gt;    last = None&lt;br /&gt;    for iteration in taylor_series():&lt;br /&gt;        i = str(iteration)&lt;br /&gt;        try:&lt;br /&gt;            cur = i[idx]&lt;br /&gt;        except IndexError:&lt;br /&gt;            continue&lt;br /&gt;        if cur==last:&lt;br /&gt;            idx += 1&lt;br /&gt;            yield cur&lt;br /&gt;        last = i[idx]&lt;br /&gt;&lt;br /&gt;def ten_digit_chunks():&lt;br /&gt;    &quot;&quot;&quot;&lt;br /&gt;    Collect digits of e as they are discovered&lt;br /&gt;    and yield them in ten-digit chunks.&lt;br /&gt;    &quot;&quot;&quot;&lt;br /&gt;    l = deque()&lt;br /&gt;    for digit in digits_of_e():&lt;br /&gt;        l.append(digit)&lt;br /&gt;        # Keep the stack at 10 by popping&lt;br /&gt;        # from the left&lt;br /&gt;        while len(l)&gt;10:&lt;br /&gt;            l.popleft()&lt;br /&gt;        if len(l)==10: &lt;br /&gt;            yield &#39;&#39;.join(l)&lt;br /&gt;&lt;br /&gt;def is_prime(n, k=20):&lt;br /&gt;    &quot;&quot;&quot;&lt;br /&gt;    Simple implementation of the Miller-Rabin &lt;br /&gt;    primality test. Thanks, Wikipedia!&lt;br /&gt;&lt;br /&gt;    n is the integer to be tested; k is the &lt;br /&gt;    precision (number of random possible &lt;br /&gt;    witnesses to test against)&lt;br /&gt;    &quot;&quot;&quot;&lt;br /&gt;    # Find s and d such that (2**s)*d==n&lt;br /&gt;    d, s = n-1, 0&lt;br /&gt;    while not d &amp; 1: # d &amp; 1 == d % 2, only faster&lt;br /&gt;        d &gt;&gt;= 1 # d &gt;&gt; 1 == d / 2, only faster&lt;br /&gt;        s += 1&lt;br /&gt;    # Test for k random numbers&lt;br /&gt;    for i in xrange(k):&lt;br /&gt;        _c = False&lt;br /&gt;        a = randint(2, n-2)&lt;br /&gt;        x = pow(a, d, n) # a**d % n&lt;br /&gt;        if x==1 or x==n-1:&lt;br /&gt;            continue&lt;br /&gt;        for r in xrange(1, s):&lt;br /&gt;            x = pow(x, 2, n) # x**2 % n&lt;br /&gt;            if x==1:&lt;br /&gt;                return False&lt;br /&gt;            elif x==n-1:&lt;br /&gt;                _c = True&lt;br /&gt;                break&lt;br /&gt;        if _c: continue&lt;br /&gt;        return False&lt;br /&gt;    return True&lt;br /&gt;&lt;br /&gt;def main():&lt;br /&gt;    for chunk in ten_digit_chunks():&lt;br /&gt;        print &quot;Trying %s...&quot; % chunk&lt;br /&gt;        if is_prime(long(chunk)):&lt;br /&gt;            print &quot;=&quot;*20&lt;br /&gt;            print&lt;br /&gt;            print &quot;Found! First ten-digit&quot;&lt;br /&gt;            print &quot;prime in consecutive digits&quot;&lt;br /&gt;            print &quot;of e is %s!&quot; % chunk&lt;br /&gt;            print&lt;br /&gt;            print &quot;=&quot;*20&lt;br /&gt;            break&lt;br /&gt;&lt;br /&gt;if __name__==&quot;__main__&quot;:&lt;br /&gt;    main()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;UPDATE:&lt;/strong&gt; In order to get through this ridiculous set of problems to the unsatisfying and insulting end, you can use this implementation of the Brent-Salamin algorithm to find primes in digits of pi, replacing &lt;code&gt;taylor_series&lt;/code&gt; in the &lt;code&gt;digits_of_e&lt;/code&gt; function in the code above:&lt;br /&gt;&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;&lt;br /&gt;def brent_salamin():&lt;br /&gt;    &quot;&quot;&quot;&lt;br /&gt;    A series whose sum converges on pi&lt;br /&gt;    incredibly quickly.&lt;br /&gt;    &quot;&quot;&quot;&lt;br /&gt;    a = Decimal(&quot;1.&quot;)&lt;br /&gt;    b = Decimal(&quot;1.&quot;)/(Decimal(&quot;2.&quot;).sqrt())&lt;br /&gt;    t = Decimal(&quot;1.&quot;)/Decimal(&quot;4.&quot;)&lt;br /&gt;    p = Decimal(&quot;1.&quot;)&lt;br /&gt;    while True:&lt;br /&gt;        a1 = Decimal(a + b)/Decimal(&quot;2.&quot;)&lt;br /&gt;        b1 = Decimal(a*b).sqrt()&lt;br /&gt;        t1 = t - p*((a - a1)**2)&lt;br /&gt;        p1 = 2*p&lt;br /&gt;        yield ((a1+b1)**2)/(t1*Decimal(&quot;4.&quot;))&lt;br /&gt;        a, b, t, p = a1, b1, t1, p1&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/span&gt;</content><link rel='replies' type='application/atom+xml' href='http://concisionandconcinnity.blogspot.com/feeds/254988267668307993/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8427412129779288317/254988267668307993' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/254988267668307993'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8427412129779288317/posts/default/254988267668307993'/><link rel='alternate' type='text/html' href='http://concisionandconcinnity.blogspot.com/2009/03/my-friend-joseph-method-shared-this.html' title='How Joseph Method Stole My Day'/><author><name>Ian McCracken</name><uri>http://www.blogger.com/profile/11153934961249932934</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>