<?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: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-11788780</id><updated>2012-01-28T17:58:59.232-08:00</updated><category term='flash'/><category term='clustering'/><category term='ZeroMQ'/><category term='Jasmine'/><category term='ironpython'/><category term='ironport'/><category term='twisted'/><category term='books'/><category term='gentoo'/><category term='compilers'/><category term='mozart'/><category term='hosting'/><category term='adobe'/><category term='parsing'/><category term='linkedin'/><category term='pyweek'/><category term='jw player'/><category term='fms'/><category term='sprint'/><category term='BeOS'/><category term='dzone'/><category term='freebase'/><category term='scams'/><category term='CrunchBang'/><category term='git'/><category term='AI'/><category term='gevent'/><category term='Smalltalk'/><category term='nginx'/><category term='rails'/><category term='turbogears'/><category term='celery'/><category term='oreilly'/><category term='Unity'/><category term='alice'/><category term='video'/><category term='email'/><category term='unicode'/><category term='real-time systems'/><category term='bdd'/><category term='gegypt'/><category term='peer programming'/><category term='Openmoko'/><category term='googleio'/><category term='pivotal labs'/><category term='facebook'/><category term='IPv6'/><category term='scheme'/><category term='lettuce'/><category term='i18n'/><category term='distributed'/><category term='olpc'/><category term='java'/><category term='webrat'/><category term='engineering'/><category term='aquarium'/><category term='wxpython'/><category term='graphics'/><category term='cucumber'/><category term='lambda'/><category term='django'/><category term='concurrency'/><category term='ideas'/><category term='lift'/><category term='rest'/><category term='io2011'/><category term='OpenSolaris'/><category term='flickr'/><category term='mac'/><category term='innovation'/><category term='shrapnel'/><category term='optimization'/><category term='palm'/><category term='OOP'/><category term='dsl'/><category term='silverstripe'/><category term='akka'/><category term='genshi'/><category term='ubuntu'/><category term='svn'/><category term='subversion'/><category term='google'/><category term='oz'/><category term='ruby'/><category term='virtualization'/><category term='darwin'/><category term='hpc'/><category term='education'/><category term='jazz'/><category term='support'/><category term='perl'/><category term='ActionScript'/><category term='continuations'/><category term='Socket.IO'/><category term='lua'/><category term='BSD'/><category term='minix3'/><category term='flask'/><category term='OS X'/><category term='grammar'/><category term='PC-BSD'/><category term='proofs'/><category term='animation'/><category term='DEC'/><category term='lubuntu'/><category term='productivity'/><category term='hardware'/><category term='Go'/><category term='pycharm'/><category term='math'/><category term='user experience'/><category term='louie'/><category term='awesome'/><category term='music'/><category term='ssh'/><category term='vnc'/><category term='xmonad'/><category term='corba'/><category term='gae'/><category term='netbeans'/><category term='stackless'/><category term='jquery'/><category term='wikipedia'/><category term='wireless'/><category term='telephony'/><category term='Linux'/><category term='unix'/><category term='xubuntu'/><category term='twitter'/><category term='zsh'/><category term='gdd07'/><category term='pygame'/><category term='foss'/><category term='qt'/><category term='pypy'/><category term='management'/><category term='pycon2008'/><category term='Mepis'/><category term='window managers'/><category term='astronomy'/><category term='fish'/><category term='funny'/><category term='html5'/><category term='erlang'/><category term='web'/><category term='C'/><category term='open source'/><category term='netgear'/><category term='OpenStack'/><category term='hadoop'/><category term='prolog'/><category term='Qimo'/><category term='dell'/><category term='firefox'/><category term='bless'/><category term='second life'/><category term='css'/><category term='pycon2011'/><category term='pycon2007'/><category term='arch linux'/><category term='software engineering'/><category term='haskell'/><category term='vim'/><category term='eclipse'/><category term='playframework'/><category term='foxmarks'/><category term='aws'/><category term='xp'/><category term='treo'/><category term='startups'/><category term='humor'/><category term='xml'/><category term='business'/><category term='ctm'/><category term='scala'/><category term='threads'/><category term='MySQL'/><category term='scalability'/><category term='video games'/><category term='logic'/><category term='ddj'/><category term='x11'/><category term='security'/><category term='vmware'/><category term='intellij'/><category term='cloud'/><category term='bash'/><category term='flex'/><category term='gui'/><category term='tornado web'/><category term='rspec'/><category term='android'/><category term='rubymine'/><category term='html'/><category term='ssl'/><category term='regular expressions'/><category term='neuroscience'/><category term='testing'/><category term='JavaScript'/><category term='w3c'/><category term='ObjC'/><category term='ide'/><category term='computer history'/><category term='asynchronous'/><category term='auto'/><category term='burnout'/><category term='apple'/><category term='OpenSocial'/><category term='GNOME'/><category term='C++'/><category term='dart'/><category term='rdf'/><category term='agile'/><category term='python'/><category term='twilio'/><category term='mako'/><category term='aws2011'/><category term='sicp'/><category term='windows'/><category term='netbsd'/><category term='programming languages'/><category term='hookbox'/><category term='science'/><category term='database'/><category term='code review'/><category term='computer science'/><category term='eopl'/><category term='dependency injection'/><category term='wowza'/><category term='playn'/><category term='emacs'/><category term='operating systems'/><category term='personal'/><category term='php'/><category term='python3'/><category term='programming'/><category term='streaming'/><category term='stm'/><category term='lisp'/><category term='NodeJS'/><category term='YouTube'/><category term='openlaszlo'/><category term='YUI'/><category term='pylons'/><category term='pascal'/><category term='C#'/><category term='comet'/><category term='free software'/><category term='primes'/><category term='plone'/><category term='blogger'/><category term='MySQLdb'/><category term='quotes'/><category term='iPad'/><category term='authlogic'/><title type='text'>JJinuxLand</title><subtitle type='html'>This is a purely technical blog concerning topics such as Python, Ruby, Linux, open source software, the Web, and lesser-known programming languages.&lt;br&gt;&lt;i&gt;Ad maiorem Dei gloriam inque hominum salutem.&lt;/i&gt;</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default?start-index=101&amp;max-results=100'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>624</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-11788780.post-1892092373271577492</id><published>2012-01-27T15:03:00.001-08:00</published><updated>2012-01-27T15:09:52.920-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='YouTube'/><title type='text'>YouTube: Vibop by NewBlue</title><content type='html'>I'm proud to announce that we launched a new &lt;a href="http://apiblog.youtube.com/2011/07/introducing-youtubecomcreate.html"&gt;YouTube.com/create partner&lt;/a&gt;, &lt;a href="http://www.youtube.com/create_detail/Vibop"&gt;Vibop&lt;/a&gt;:&lt;blockquote&gt;Vibop makes your videos shine with just a few clicks. Add an animated intro, a vintage filter, a cartoon look, a silent movie theme, and dozens of other effects. Brighten dark images and fix a shaky camera. Fun, fast, and easy, Vibop will take your memories to the next level!&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;&lt;iframe width="560" height="315" src="http://www.youtube.com/embed/tYvmX_bFKqk" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-1892092373271577492?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/1892092373271577492/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=1892092373271577492' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/1892092373271577492'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/1892092373271577492'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2012/01/youtube-vibop-by-newblue.html' title='YouTube: Vibop by NewBlue'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://img.youtube.com/vi/tYvmX_bFKqk/default.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-3673736296899624410</id><published>2012-01-27T14:43:00.000-08:00</published><updated>2012-01-27T14:45:51.841-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='dart'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Dart at BayPiggies</title><content type='html'>&lt;a href="http://3.bp.blogspot.com/-Z6U7A2wWHXk/TyMpA75K4rI/AAAAAAAAC1M/GYlkPdvHxfg/s1600/Screen%2Bshot%2B2012-01-27%2Bat%2B2.38.05%2BPM.png"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 320px; height: 237px;" src="http://3.bp.blogspot.com/-Z6U7A2wWHXk/TyMpA75K4rI/AAAAAAAAC1M/GYlkPdvHxfg/s320/Screen%2Bshot%2B2012-01-27%2Bat%2B2.38.05%2BPM.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5702446649235399346" /&gt;&lt;/a&gt;&lt;br /&gt;I gave a talk on &lt;a href="http://www.dartlang.org/"&gt;Dart&lt;/a&gt; last night at &lt;a href="http://baypiggies.net"&gt;BayPiggies&lt;/a&gt;. We had a great turn out, and the crowd was super interactive! Never underestimate a Python programmer's ability to question every aspect of a new programming language, especially one that uses curly braces and semicolons ;)&lt;br /&gt;&lt;br /&gt;If you're interested, here are the &lt;a href="https://docs.google.com/open?id=0B_Z56A18VRlxMTI3YWU2ZTYtNzY3ZC00MDA1LTgzYmYtYTNhMDY3ZjUwNzc0"&gt;slides&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-3673736296899624410?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/3673736296899624410/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=3673736296899624410' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/3673736296899624410'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/3673736296899624410'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2012/01/dart-at-baypiggies.html' title='Dart at BayPiggies'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-Z6U7A2wWHXk/TyMpA75K4rI/AAAAAAAAC1M/GYlkPdvHxfg/s72-c/Screen%2Bshot%2B2012-01-27%2Bat%2B2.38.05%2BPM.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-29828993711253443</id><published>2012-01-24T14:21:00.000-08:00</published><updated>2012-01-24T14:27:19.361-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='agile'/><category scheme='http://www.blogger.com/atom/ns#' term='software engineering'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><title type='text'>Walking Skeletons and TODO Outlines</title><content type='html'>These days, applications are so complicated and contain so many layers that it's difficult to know where to start. Should you work bottom up or top down? How much should you work on one layer before starting to work on the next layer? How can you ensure that the layers work properly together? Building a walking skeleton and managing a TODO document in outline format are two techniques that work well together to conquer complex problems, even ones involving multiple layers. Best of all, you don’t have to worry about trying to get things right the first time or getting lost along the way.&lt;blockquote&gt;A Walking Skeleton is a tiny implementation of the system that performs a small end-to-end function. It need not use the final architecture, but it should link together the main architectural components. The architecture and the functionality can then evolve in parallel. -- &lt;a href="http://alistair.cockburn.us/index.php/Walking_skeleton"&gt;Alistair Cockburn&lt;/a&gt;&lt;/blockquote&gt;Building a walking skeleton is a great way to handle the complexity of dealing with multiple layers. Start with the simplest possible feature, and implement it top down. Ideally, the feature should force you to work your way all the way down the stack. The goal is to make sure the layers work together.&lt;br /&gt;&lt;br /&gt;As you’re building the walking skeleton, you may think of many things that you need to add, test, or in general worry about. It's helpful to maintain a TODO document in outline format so that you can organize and plan your attack, especially when you’re working with multiple layers at the same time. Eventually, each TODO item can be transferred into a test, a piece of code, an issue in the bug tracking system, or perhaps just an email to someone else.&lt;br /&gt;&lt;br /&gt;Once you’ve built a walking skeleton, should you go back to developing one layer at a time? For most applications where the cost of change is low, probably not. Actively building one layer at a time is frequently very inefficient. A more efficient approach is to focus on one feature at a time. Sketch out the feature using a set of TODOs and build it top-down, managing the TODOs as you go. If you focus on one feature at a time instead of one layer at a time, you won’t end up building a lot of code in different layers that never actually gets used. The time saved by only building what you need and only building it when you have all the information you need more than compensates for the refactoring time.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Thanks go to Chris Lopez for his help with this post.&lt;/i&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-29828993711253443?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/29828993711253443/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=29828993711253443' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/29828993711253443'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/29828993711253443'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2012/01/walking-skeletons-and-todo-outlines.html' title='Walking Skeletons and TODO Outlines'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-1409932919440105228</id><published>2012-01-16T18:10:00.000-08:00</published><updated>2012-01-16T18:48:41.289-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='computer science'/><category scheme='http://www.blogger.com/atom/ns#' term='computer history'/><title type='text'>Books: Out of their Minds: The Lives and Discoveries of 15 Great Computer Scientists</title><content type='html'>I just finished reading &lt;a href="http://www.amazon.com/Out-their-Minds-Discoveries-Scientists/dp/0387982698/ref=sr_1_1?ie=UTF8&amp;qid=1326766240&amp;sr=8-1"&gt;Out of their Minds: The Lives and Discoveries of 15 Great Computer Scientists&lt;/a&gt;.  In short, I liked it.  I wouldn't say I liked it as much as, say, &lt;a href="http://www.amazon.com/Coders-Work-Reflections-Craft-Programming/dp/1430219483"&gt;Coders at Work&lt;/a&gt;, however, I'm glad I read it.  What I liked most about it was that it contains biographies from really early computer science pioneers such as  Ada Lovelace, John von Neumann, John Backus, and John McCarthy.  I know computer history after 1970 really well, but this book contains a lot of stuff from before 1970.&lt;br /&gt;&lt;br /&gt;I jotted down a few interesting tidbits while I was reading the book.  However, since I read the Kindle version of the book, I only have percentages, not page numbers.  Anyway, I hope you're as entertained by some of these as I was.&lt;br /&gt;&lt;br /&gt;John Backus, who lead the team that created Fortran at IBM, flunked out of college [2%].  He had a metal plate installed in his head [3%].  He disliked calculus but liked algebra [3%] (just like me!).  These days, Backus is a proponent of functional programming [7%].&lt;br /&gt;&lt;br /&gt;John von Neumann, who helped establish the fundamentals of computer architecture, thought that creating a programming language (i.e. Fortran) was a waste of time since programming wasn't a big problem [4%].&lt;br /&gt;&lt;br /&gt;Ada Lovelace, who was the first programmer (not to mention, a girl!), was a gambler, an alcoholic, and a cocaine addict.  She died of cancer at the age of 36.  She is credited with inventing loops and subroutines [5%].&lt;br /&gt;&lt;br /&gt;Fortran only had globals.  Algol, which is considered an ancestor of C, added locals, thus permitting recursion [6%].&lt;br /&gt;&lt;br /&gt;McCarthy, who designed Lisp, was born in 1927 to Communist party activists.  He had an Irish, Catholic father and a Lithuanian, Jewish mother [8%].  McCarthy is the reason Algol had recursion [11%].  (I didn't know that C got recursion because of Lisp.)&lt;br /&gt;&lt;br /&gt;Alan Kay, who did pioneering work on object-oriented programming and helped create Smalltalk, got thrown out of school for protesting the Jewish quota [15%].&lt;br /&gt;&lt;br /&gt;Edsger W. Dijkstra, who did influential work on a lot of early computer science problems such as concurrency, did very well in school and wanted to turn programming into a respectable discipline [21%].&lt;br /&gt;&lt;br /&gt;Fred Brooks, who wrote "The Mythical Man-Month", wrote this about iterative development:&lt;blockquote&gt;In "The Mythical Man-Month" I said build one and throw it away.  But that isn't what I say anymore.  Now I say, build a minimal thing--get it out in the field and start getting feedback, and then add function to it incrementally.  The waterfall model of specify, build, test is just plain wrong for software.  The interaction with the user is crucial to developing the specification.  You have to develop the specification as you build and test.&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-1409932919440105228?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/1409932919440105228/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=1409932919440105228' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/1409932919440105228'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/1409932919440105228'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2012/01/books-out-of-their-minds-lives-and.html' title='Books: Out of their Minds: The Lives and Discoveries of 15 Great Computer Scientists'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-6527623936954218079</id><published>2011-12-30T10:35:00.001-08:00</published><updated>2011-12-30T10:43:16.431-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='shrapnel'/><category scheme='http://www.blogger.com/atom/ns#' term='stackless'/><category scheme='http://www.blogger.com/atom/ns#' term='gegypt'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><category scheme='http://www.blogger.com/atom/ns#' term='concurrency'/><title type='text'>Python: Shrapnel</title><content type='html'>After many years of hopeful expectation, IronPort (now part of Cisco) finally &lt;a href="https://github.com/ironport/"&gt;open sourced&lt;/a&gt; a bunch of stuff, most importantly &lt;a href="https://github.com/ironport/shrapnel"&gt;Shrapnel&lt;/a&gt;, our proprietary version of stackless Python. Back when I worked at IronPort (2004-2006), I was dying for them to open source this stuff. The existing open source version of stackless Python at the time never had quite the same flavor or emphasis as Shrapnel, but these days &lt;a href="http://www.gevent.org/"&gt;gevent&lt;/a&gt; is very similar.&lt;br /&gt;&lt;br /&gt;Anyway, congratulations to Sam Rushing, Mark Peek, etc., and thank you Cisco!  By the way, there's a &lt;a href="https://github.com/ironport/shrapnel/blob/master/coro/_coro.pyx#L56"&gt;reference to me&lt;/a&gt; in the code ;)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-6527623936954218079?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/6527623936954218079/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=6527623936954218079' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/6527623936954218079'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/6527623936954218079'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/12/python-shrapnel.html' title='Python: Shrapnel'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-1492282346057793001</id><published>2011-12-16T13:33:00.000-08:00</published><updated>2011-12-16T14:48:26.237-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='dart'/><category scheme='http://www.blogger.com/atom/ns#' term='gegypt'/><category scheme='http://www.blogger.com/atom/ns#' term='google'/><title type='text'>Dart at g|egypt 2.0</title><content type='html'>&lt;a href="http://3.bp.blogspot.com/-nfbYBhnD4p0/Tuu9v11GROI/AAAAAAAACqE/tIPixqVsW1Q/s1600/lars_bak.jpg"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 320px; height: 180px;" src="http://3.bp.blogspot.com/-nfbYBhnD4p0/Tuu9v11GROI/AAAAAAAACqE/tIPixqVsW1Q/s320/lars_bak.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5686847584086672610" /&gt;&lt;/a&gt;&lt;a href="http://2.bp.blogspot.com/-QY-8FtjuOS0/Tuu9sCYsVqI/AAAAAAAACp4/FjfYxS2pQAo/s1600/dart_talk_1.jpg"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://2.bp.blogspot.com/-QY-8FtjuOS0/Tuu9sCYsVqI/AAAAAAAACp4/FjfYxS2pQAo/s320/dart_talk_1.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5686847518737716898" /&gt;&lt;/a&gt;&lt;a href="http://1.bp.blogspot.com/-M9wEBQoh3-c/Tuu9kH1_koI/AAAAAAAACps/93fIjojsItg/s1600/dart_talk_2.jpg"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://1.bp.blogspot.com/-M9wEBQoh3-c/Tuu9kH1_koI/AAAAAAAACps/93fIjojsItg/s320/dart_talk_2.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5686847382763836034" /&gt;&lt;/a&gt;I gave a talk on Dart at g|egypt 2.0.   All I can say is, wow!&lt;br /&gt;&lt;br /&gt;First of all, I wasn't even on the agenda.  In fact, the room I was using wasn't really even labelled on the map--it took me several minutes to find it.  It was a small room.  2 minutes before my talk was supposed to start, I only had 3 people in the audience.  However, 10 minutes later, every seat was taken, and people were standing at the back and along the sides.  The room was packed!&lt;br /&gt;&lt;br /&gt;Since Dart is so new, we haven't spoken about it at very many places.  I told the Egyptians that I was giving them a chance to become the best Dart programmers in the world because they were seeing the talk before most of the rest of the world had seen it.  I almost got a standing ovation ;)&lt;br /&gt;&lt;br /&gt;People were very excited about Dart and asked a ton of excellent questions.  Even after my talk, I had to stand around for 2.5 hours answering more questions.&lt;br /&gt;&lt;br /&gt;The comments on Google+ were very supportive:&lt;br /&gt;&lt;br /&gt;&lt;a href="https://plus.google.com/103481739165478403084/posts"&gt;Hady Allam&lt;/a&gt; said, "your DART session is one of the best sessions i have ever attended. Thank You +Shannon Behrens and hope to see you again in Egypt."&lt;br /&gt;&lt;br /&gt;&lt;a href="https://plus.google.com/103684318952941443174"&gt;Abdurrahman Alraies&lt;/a&gt; said, "The dart session was the best surprise of the day.  Thank you very much +Shannon Behrens and +Sebastian Trzcinski-Clément It says much about how important is Egypt to Google."&lt;br /&gt;&lt;br /&gt;&lt;a href="https://plus.google.com/117478487445951455805"&gt;Saied Attala&lt;/a&gt; said, "really i have opened the comment box for 30 minutes and can't write every thing for you.  i loved your Dart sessions and your sense of humor.  i enjoyed talking with you and having pics with you."&lt;br /&gt;&lt;br /&gt;Those comments were very touching to me.  To all the Egyptians out there who went to my Dart talk, thank you for making me feel so special!  I hope I get to come back to Egypt again!&lt;br /&gt;&lt;br /&gt;After the trip, I accidentally ran into &lt;a href="https://plus.google.com/117369940038227331789/posts"&gt;Lars Bak&lt;/a&gt;, the creator of Dart, in Zurich.  That was very exciting for me!&lt;br /&gt;&lt;br /&gt;If you're interested, here are the &lt;a href="https://docs.google.com/viewer?a=v&amp;pid=explorer&amp;chrome=true&amp;srcid=0B_Z56A18VRlxYjU4NzQ4YmEtMDdmNi00YzU1LTlhYjEtYWE1ZWZjZWYyMmYz&amp;hl=en_US"&gt;slides&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Thanks also to &lt;a href="https://plus.google.com/117478487445951455805/posts"&gt;Saied Attala&lt;/a&gt; and &lt;a href="https://plus.google.com/100965599294063024961/posts"&gt;Ayman Reda Bedair&lt;/a&gt; for the pictures and &lt;a href="https://plus.google.com/118397406534237711570/posts"&gt;Seth Ladd&lt;/a&gt; for most of the slides.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-1492282346057793001?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/1492282346057793001/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=1492282346057793001' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/1492282346057793001'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/1492282346057793001'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/12/dart-at-gegypt-20.html' title='Dart at g|egypt 2.0'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-nfbYBhnD4p0/Tuu9v11GROI/AAAAAAAACqE/tIPixqVsW1Q/s72-c/lars_bak.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-4858210263708893498</id><published>2011-12-12T06:20:00.000-08:00</published><updated>2011-12-16T13:33:35.115-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='YouTube'/><category scheme='http://www.blogger.com/atom/ns#' term='gegypt'/><category scheme='http://www.blogger.com/atom/ns#' term='google'/><title type='text'>YouTube for Your Business at g|egypt 2.0</title><content type='html'>&lt;a href="http://3.bp.blogspot.com/-rbCeIZgq7sM/TuYPdrGItxI/AAAAAAAACik/MAB8Z21VONI/s1600/gegypt_youtube_api.jpg" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://3.bp.blogspot.com/-rbCeIZgq7sM/TuYPdrGItxI/AAAAAAAACik/MAB8Z21VONI/s320/gegypt_youtube_api.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5685248582060914450" /&gt;&lt;/a&gt;&lt;br /&gt;I gave a talk called &lt;a href="https://docs.google.com/open?id=0B_Z56A18VRlxMzQwYWFkNzAtOTY3MS00NDg5LWEzOTItNTYzOTIxYjE3ODc2"&gt;YouTube for Your Business&lt;/a&gt; at g|egypt 2.0.&lt;br /&gt;&lt;br /&gt;The talk went very well.  There were about 200 people in the audience (and almost 900 in total at the event), which is a great turnout for a YouTube API talk.  People asked me questions for about an hour afterwards.  I even created a video for the talk:&lt;br /&gt;&lt;br /&gt;&lt;iframe width="560" height="315" src="http://www.youtube.com/embed/CqN7VibZZq8" frameborder="0" allowfullscreen=""&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;I was really worried that I would lose my voice during the talk.  It's very crackly right now. &lt;br /&gt;&lt;br /&gt;Since this is the first time I've visited the middle east, I'm suffering a little from culture shock.  I forgot to remove a beer joke from one of my slides.  It took me a few seconds to realize why people weren't laughing.  What's really funny is that I don't even drink!&lt;br /&gt;&lt;br /&gt;I was very amazed by a few things.  There were a lot of women at the event.  There are far more female programmers in Egypt then there are in the US, as far as I can tell.  The next thing that surprised me was that almost everyone here uses Windows--even on the server side.  That's certainly not the case in the San Francisco Bay Area.  At all the companies I've worked at recently, people tended to use Apple laptops and Linux on the servers.  The last thing that surprised me was how many people wanted to have their picture taken with me.  For about two hours, I felt like a total rock star ;)&lt;br /&gt;&lt;br /&gt;I have a talk on Dart tomorrow and a talk on Python the next day.  Let's hope my voice holds up ;)&lt;br /&gt;&lt;br /&gt;Thanks go to &lt;a href="https://plus.google.com/112226555598377388198/posts"&gt;Mohamed Naguib&lt;/a&gt; for taking the picture.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-4858210263708893498?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/4858210263708893498/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=4858210263708893498' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/4858210263708893498'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/4858210263708893498'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/12/youtube-for-your-business-at-gegypt-20.html' title='YouTube for Your Business at g|egypt 2.0'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-rbCeIZgq7sM/TuYPdrGItxI/AAAAAAAACik/MAB8Z21VONI/s72-c/gegypt_youtube_api.jpg' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-8180391145203816160</id><published>2011-11-29T15:16:00.000-08:00</published><updated>2011-11-29T16:03:59.367-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='computer science'/><title type='text'>Computer Science: The Travelling Salesman Problem</title><content type='html'>I was thinking about the &lt;a href="http://en.wikipedia.org/wiki/Travelling_salesman_problem"&gt;Travelling Salesman&lt;/a&gt; problem this morning.  I came up with an algorithm that permits a few nice optimizations.  My guess is that Knuth probably already came up with this algorithm, formally analyzed it, and then came up with 15 others that were much better.  Nonetheless, I figured I'd jot it down.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Warning&lt;/b&gt;: This is a rough draft of an algorithm.  You shouldn't bother reading this if you want real rigor.  I'm just writing it down to get it off my chest.&lt;br /&gt;&lt;br /&gt;Rather than picking the first segment of the journey and working my way to the end, I want to pick a middle segment of the journey and work my way outwards recursively.&lt;br /&gt;&lt;br /&gt;Given a list of distances (or times, or costs, or whatever) between cities, sort the list from shortest distance to longest distance regardless of which cities are involved.  Now, loop over this list and use each element of the list as a middle segment of your journey.  This gives us a list of "first steps" (or rather, first middle steps).  Looping over the list from shortest distance to longest distance is an important optimization because it increases the likelihood that we'll find an optimal path early, allowing us to skip over a lot of potential paths later.&lt;br /&gt;&lt;br /&gt;Also sort the list of distances so that for each city, we have a list of other cities in ascending distance order.  By sorting all of the city pairs in order of (first city, distance), you can use one sort for all of the pairs.&lt;br /&gt;&lt;br /&gt;Now, here comes the recursion.  We have a bunch of middle parts of the journey.  Use recursion to extend the journey either by adding to the beginning of the journey or by adding to the end of the journey.  Keep recursing until you have a complete path or a partial path that is already longer than the best path seen so far.  Now, we can either extend the journey at the beginning or at the end.  Recursively try extending the journey by either adding to the beginning or the end.  However, do it in order so that you try adding the shorter distances first.  There's an implicit merge sort going on in this algorithm.  This, again, is an optimization to allow you to skip work later.&lt;br /&gt;&lt;br /&gt;While we were recursing, we had a function that took two things, a middle chunk of the journey and a set of cities not yet visited.  Apply memoization so that anytime we get the same parameters, we return the same answer (by using a cache, of course).  This is an important optimization.&lt;br /&gt;&lt;br /&gt;Last of all, using the above algorithm, we'll quickly come up with the first complete path that has a decently minimal distance.  Keep track of this as the best complete path seen so far.  Anytime we are recursing and we come up with a partial path that is longer than the best complete path seen so far, we can stop recursing, give up, and try something else.  This is an important optimization to save work "at the leaves of the tree".&lt;br /&gt;&lt;br /&gt;I can't even wrap my head around the big O of this algorithm.  I suspect it's one of those cases where you use words like "amortized cost".&lt;br /&gt;&lt;br /&gt;This algorithm does have a weakness.  If every distance between cities is exactly the same, it'll try every possibility.  Similarly, if every distance between cities is exactly the same except one pair of cities which has a longer distance, it'll still try every possibility.  I'm not sure of the degree to which the memoization fixes this problem.  It'd be good to extend the algorithm somehow to recognize this situation and short circuit, i.e. automatically throw out paths of duplicate length.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-8180391145203816160?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/8180391145203816160/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=8180391145203816160' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/8180391145203816160'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/8180391145203816160'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/11/computer-science-travelling-salesman.html' title='Computer Science: The Travelling Salesman Problem'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-849613594872239099</id><published>2011-11-29T14:46:00.001-08:00</published><updated>2011-12-01T14:57:21.571-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='primes'/><category scheme='http://www.blogger.com/atom/ns#' term='math'/><title type='text'>Math: Factoring Numbers</title><content type='html'>I was thinking this morning about factoring numbers.  I wonder if it might sometimes be helpful to use real numbers to gain an interesting perspective in order to solve certain problems involving integer numbers (i.e. number theory problems).  For instance, I was thinking about factoring large numbers.&lt;br /&gt;&lt;br /&gt;For every natural number, C, (that isn't equal to 0 or 1), there are an infinite number of pairs of positive, real numbers A and B for which A * B = C.  For instance, 6 = 1.0 * 6.0 = 2.4 * 2.5 = 2.0 * 3.0 = ...  I wonder if playing around with pairs of real numbers like (2.4, 2.5) can lead you to pairs of integer numbers like (2, 3).&lt;br /&gt;&lt;br /&gt;Imagine all the pairs (A, B) for which A * B = C.  Let's create a way to graph all such pairs in a funny sort of way.  Let's pick a bunch of A's going from 0 to C.  For each different A, we can calculate B via C / A.  Let's consider the parts of A and B that are to the right of the decimal point to see if one pair, (A, B) can lead us to another pair (A', B') which are integers (i.e. have only zeros to the right of the decimal point).  In fact, let's see if we can come up with a numerical analysis approach where we use estimations to hunt down (A', B').&lt;br /&gt;&lt;br /&gt;To do this, let's create a funny sort of three dimensional graph.  Here's the pseudo code (assuming we're trying to factor some number, c):&lt;pre&gt;for step in range (1, LARGE_NUM_OF_STEPS + 1):&lt;br /&gt;    a = (step / LARGE_NUM_OF_STEPS) * c&lt;br /&gt;    b = c / a&lt;br /&gt;    x = a - int(a)  # x and y will always be in the range [0, 1).&lt;br /&gt;    y = b - int(b)&lt;br /&gt;    z = floor(a)&lt;br /&gt;    draw_point(x, y, z)&lt;/pre&gt;If you use a very large number for LARGE_NUM_OF_STEPS, you'll create a funny looking 3D graph.  Any place where x = 0 and y = 0, you'll have a pair of integers (a, b) that multiply to equal c.&lt;br /&gt;&lt;br /&gt;Naturally, this is an extremely expensive way to factor numbers.  However, I'll bet you'd learn a lot about factoring numbers by looking at this graph.  In fact, I'm guessing that looking at this graph will lead you to a numerical-analysis-style approximation algorithm for honing in on valid integer pairs (a, b) where a * b = c.&lt;br /&gt;&lt;br /&gt;Humans are fairly good at visualizing things in 3D, but it'd be really cool to extend this graph to four dimensions (perhaps using time as the fourth dimension).  The fourth dimension would be used for various values of C, from 0 to infinity.  I really think that looking at such a graph would help with fast number factoring.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Updated:&lt;/b&gt; Fixed a couple of errors pointed out by BMeph.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-849613594872239099?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/849613594872239099/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=849613594872239099' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/849613594872239099'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/849613594872239099'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/11/math-factoring-numbers.html' title='Math: Factoring Numbers'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-825381545563913592</id><published>2011-11-25T12:56:00.000-08:00</published><updated>2011-11-25T13:20:04.403-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='computer science'/><title type='text'>Computer Science: NP-complete Problems are Really NP-complete</title><content type='html'>First of all, let me apologize for my sloppy terminology.  I've always been a better software engineer than a computer scientist.&lt;br /&gt;&lt;br /&gt;This is how &lt;a href="http://en.wikipedia.org/wiki/NP-complete"&gt;Wikipedia&lt;/a&gt; describes NP-complete problems:&lt;blockquote&gt;In computational complexity theory, the complexity class NP-complete (abbreviated NP-C or NPC) is a class of decision problems. A decision problem L is NP-complete if it is in the set of NP problems so that any given solution to the decision problem can be verified in polynomial-time, and also in the set of NP-hard problems so that any NP problem can be converted into L by a transformation of the inputs in polynomial-time.&lt;br /&gt;Although any given solution to such a problem can be verified quickly, there is no known efficient way to locate a solution in the first place; indeed, the most notable characteristic of NP-complete problems is that no fast solution to them is known. That is, the time required to solve the problem using any currently known algorithm increases very quickly as the size of the problem grows. As a result, the time required to solve even moderately large versions of many of these problems easily reaches into the billions or trillions of years, using any amount of computing power available today. As a consequence, determining whether or not it is possible to solve these problems quickly, called the P versus NP problem, is one of the principal unsolved problems in computer science today.&lt;br /&gt;&lt;br /&gt;While a method for computing the solutions to NP-complete problems using a reasonable amount of time remains undiscovered, computer scientists and programmers still frequently encounter NP-complete problems. NP-complete problems are often addressed by using approximation algorithms.&lt;/blockquote&gt;Note that it says, "As a consequence, determining whether or not it is possible to solve these problems quickly, called the P versus NP problem, is one of the principal unsolved problems in computer science today."  I haven't seen the proof for it, but I've also heard that if you could prove that one NP-complete problem does not have a polynomial-time solution to it, then that would prove that none of them have a polynomial-time solution to them.  The theory is a little over my head, but I'm going to take a shot.&lt;br /&gt;&lt;br /&gt;I'd like to propose a problem that is NP-complete and show that it does not have a polynomial-time solution to it.  Imagine I pick an n-tuple of random 32-bit, unsigned integers.  Your job is guess what the n-tuple is.  Hence, where n is 2, I might pick (376, 792), and your job is to guess that tuple.  If you guess a certain tuple, I can tell you whether you're right or not.  Hence, I can verify a solution in polynomial-time (it takes me O(n) number of int comparisons to verify a solution).  However, to try to solve such a problem, you either have to get really lucky or you have to brute force it.  If you find a method other than brute force trying every possible solution, then obviously there's something wrong with my random number generator.  To use brute force to guess the solution requires O(A^n) for some constant A.  Since it's "to the n", it's not polynomial-time (3nA^3 is polynomial-time since the exponent is fixed; A^n is not polynomial-time since the exponent varies with the size of n).&lt;br /&gt;&lt;br /&gt;Now I know that some of you reading this understand the computer science a lot better than I do.  Have I actually shown (at least informally) that NP-complete problems are really NP-complete?  If not, can you, in lay man's terms explain what I'm misunderstanding?  Thanks!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-825381545563913592?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/825381545563913592/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=825381545563913592' title='15 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/825381545563913592'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/825381545563913592'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/11/computer-science-np-complete-problems.html' title='Computer Science: NP-complete Problems are Really NP-complete'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>15</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-3957985941767981396</id><published>2011-11-18T18:02:00.000-08:00</published><updated>2011-11-18T18:32:44.388-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='dart'/><category scheme='http://www.blogger.com/atom/ns#' term='video games'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Tetris in Dart</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/-9x2fBsR7sGw/TscP3Ck8raI/AAAAAAAACak/34u7Ih4Ivrk/s1600/Screen%2Bshot%2B2011-11-18%2Bat%2B6.09.03%2BPM.png"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 300px; height: 320px;" src="http://2.bp.blogspot.com/-9x2fBsR7sGw/TscP3Ck8raI/AAAAAAAACak/34u7Ih4Ivrk/s320/Screen%2Bshot%2B2011-11-18%2Bat%2B6.09.03%2BPM.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5676523293582863778" /&gt;&lt;/a&gt;&lt;br /&gt;For my first Dart program, I decided to implement Tetris.  I was inspired by Alexei Kourbatov at &lt;a href="http://www.javascripter.net"&gt;javascripter.net&lt;/a&gt; which contains an implementation of Tetris he wrote way back in 1999!&lt;br /&gt;&lt;br /&gt;You can play my version &lt;a href="http://rube-goldbergs-book-search-treasure-hunt.googlecode.com/git/dart/src/index.html"&gt;here&lt;/a&gt;.  The source code is &lt;a href="http://rube-goldbergs-book-search-treasure-hunt.googlecode.com/git/dart/src/"&gt;here&lt;/a&gt;.  It's part of a treasure hunt I'm building, but I'll say more about that later.&lt;br /&gt;&lt;br /&gt;Overall, I like Dart.  The Dart version of my code is about the same length as the JavaScript version of my code (the Dart version is slightly longer because I switched from using innerHTML to using the DOM).  The combination of the optional static typing and Dart Editor helped me avoid many, many bugs.  I'm still a Python guy at heart, but my experience with Dart was a very pleasant one.&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;&lt;br /&gt;I jotted down some lessons I learned.  &lt;i&gt;Warning&lt;/i&gt;: This is my first Dart program, and I haven't even read the tutorial yet, so I could be missing the boat on some of these points!&lt;br /&gt;&lt;br /&gt;Don't try to play with DOM elements before the DOM is loaded.  The easiest way to achieve this is to import your Dart-compiled JavaScript at the bottom of the page.&lt;br /&gt;&lt;br /&gt;Dart has generics.  This is a little unfamiliar for a scripting guy like me.&lt;br /&gt;&lt;br /&gt;I had a hard time finding methods like JavaScript's Math.abs and Math.floor.  They're now members of num.&lt;br /&gt;&lt;br /&gt;This is how you add to the DOM:&lt;pre&gt;HTMLDocument doc = window.document;&lt;br /&gt;HTMLParagraphElement p = doc.createElement('p');&lt;br /&gt;p.textContent = message;&lt;br /&gt;doc.body.appendChild(p);&lt;/pre&gt;Using innerHTML works too, but I suspect it's frowned upon.&lt;br /&gt;&lt;br /&gt;DOM manipulation isn't as easy as using innerHTML.  It's not quite as horrible as I feared, but it's still a pain in the butt.&lt;br /&gt;&lt;br /&gt;Sometimes you need to use a temporary variable to help out the type system.  If you write:&lt;pre&gt;doc.getElementById("s-" + i + "-" + j).src = 'images/s0.png';&lt;/pre&gt;it'll say "'src' is not a member of Element."  However, you can easily fix this by writing:&lt;pre&gt;HTMLImageElement img = doc.getElementById("s-" + i + "-" + j);&lt;br /&gt;img.src = 'images/s0.png';&lt;/pre&gt;Translating from JavaScript to well-typed Dart is a linearly time-consuming, but relatively straightforward progress.&lt;br /&gt;&lt;br /&gt;The intelligent code completion in Dart Editor was really helpful since I don't really know the language or its APIs.  I can edit text a lot faster in Vim, but code completion helps me know what to write.&lt;br /&gt;&lt;br /&gt;Trying to figure out the proper way to handle keyboard events in Dart is hard because of the DOM.  I'm looking at &lt;a href="https://developer.mozilla.org/en/DOM/KeyboardEvent"&gt;developer.mozilla.org/en/DOM/KeyboardEvent&lt;/a&gt;, and it looks like there are two ways to do things, the deprecated way, and the way that isn't implemented yet.&lt;br /&gt;&lt;br /&gt;Command-g is the usual Apple shortcut to find the next occurrence of something, but it doesn't work in Dart Editor.&lt;br /&gt;&lt;br /&gt;They're working on a &lt;a href="http://www.dartlang.org/articles/improving-the-dom/"&gt;new DOM API&lt;/a&gt;.  I've decided not to use it until they say it's stable.&lt;br /&gt;&lt;br /&gt;The way main() works in the Sunflower Example doesn't match the way main() works in sample applications generated by Dart Editor.  I'm not sure why.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-3957985941767981396?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/3957985941767981396/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=3957985941767981396' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/3957985941767981396'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/3957985941767981396'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/11/tetris-in-dart.html' title='Tetris in Dart'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-9x2fBsR7sGw/TscP3Ck8raI/AAAAAAAACak/34u7Ih4Ivrk/s72-c/Screen%2Bshot%2B2011-11-18%2Bat%2B6.09.03%2BPM.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-6088981341510146548</id><published>2011-11-02T18:37:00.000-07:00</published><updated>2011-11-02T18:55:59.202-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='rubymine'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='intellij'/><category scheme='http://www.blogger.com/atom/ns#' term='playn'/><category scheme='http://www.blogger.com/atom/ns#' term='eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pycharm'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Eclipse vs. Intellij for Android, PlayN, Google App Engine, and Python Development</title><content type='html'>I can't figure out whether I should use Eclipse or IntelliJ.&lt;br /&gt;&lt;br /&gt;Eclipse is good in that it's open source and free.  Furthermore, Google has released several plugins for it such as the ADT (Android Developer Tools) plugin and the Google Plugin for Eclipse.  However, Eclipse generally leaves me feeling confused, overwhelmed, and out of control.  I spent two days reading a bunch of tutorials, but I still feel like I can't do what I want.  I installed Aptana Studio 3 (which includes PyDev) in order to play around with my Python Google App Engine project.  However, I couldn't figure out how to do two things:  1) update the PYTHONPATH for my project to include a few project-specific directories 2) use two space indents in Python code just for this project (which is the standard at Google).&lt;br /&gt;&lt;br /&gt;On the other hand, there's IntelliJ.  I've never used IntelliJ for Java development, but I've used PyCharm and RubyMine for Python and Ruby development.  The downside is that it's fairly expensive.  The upside is that it's really good.  It doesn't leave me feeling confused, overwhelmed, and out of control.  In general, I'm able to get it to do what I want.  Furthermore, the IDEAVim keybindings are pretty good (not as good as the JVi plugin for NetBeans, but still pretty good).&lt;br /&gt;&lt;br /&gt;I'm hoping to start with a toy project that uses the PlayN framework (e.g. Java), Android, etc.  It'd be nice if I could use the same IDE for Java, Python, web stuff, etc.  I don't really know all the various Google APIs such as Google App Engine for Java and Android, so an IDE that can guide me along would be helpful.  It seems like the only solution is to use Eclipse for the Google stuff and IntelliJ for Python, Ruby, and web stuff.  I haven't purchased a license yet, but I did win a license for RubyMine which might be applicable.&lt;br /&gt;&lt;br /&gt;I just read &lt;a href="http://stuffandtech.blogspot.com/2010/11/android-development-eclipse-vs-intellij.html"&gt;Android Development: Eclipse vs. IntelliJ IDEA&lt;/a&gt; which said a) IntelliJ is way better b) all of the Android tools are still accessible outside of the IDE anyway.  I also noticed that IntelliJ has come out with its own Android plugin, and it's even open source.  I'm leaning toward IntelliJ, but I hate being the only one around using a certain tool.  Any advice you guys have would be welcome.&lt;br /&gt;&lt;br /&gt;(By the way, I'm sure someone is going to come along and plug Vim, Emacs, or TextMate.  I'm personally a Vim diehard.  However, I've come to appreciate the benefit of using Vim keybindings within a larger IDE.  YMMV.)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-6088981341510146548?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/6088981341510146548/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=6088981341510146548' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/6088981341510146548'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/6088981341510146548'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/11/eclipse-vs-intellij-for-android-playn.html' title='Eclipse vs. Intellij for Android, PlayN, Google App Engine, and Python Development'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-7154794353276289727</id><published>2011-09-22T15:09:00.001-07:00</published><updated>2011-09-22T15:19:23.198-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='YouTube'/><category scheme='http://www.blogger.com/atom/ns#' term='personal'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>YouTube: Magisto</title><content type='html'>It's tough work being a developer advocate at YouTube.  I often have to do things like blog, talk tech with other programmers, and invite people to lunch.  It even requires a higher standard of personal hygiene.  For instance, the other day, I had to buy a new Google T-shirt because I had a large toothpaste stain on the shirt I was wearing, and I had a business lunch with a potential partner.&lt;br /&gt;&lt;br /&gt;Well after months of effort, my work has finally come to fruition.  TechCrunch just announced that &lt;a href="http://techcrunch.com/2011/09/21/youtube-magisto/"&gt;Magisto is now part of YouTube.com/create&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;iframe width="420" height="315" src="http://www.youtube.com/embed/brWNpv3Zvdw" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;Hmm, probably a good time to go to the gym.  All those business lunches are starting to show ;)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-7154794353276289727?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/7154794353276289727/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=7154794353276289727' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/7154794353276289727'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/7154794353276289727'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/09/youtube-magisto.html' title='YouTube: Magisto'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://img.youtube.com/vi/brWNpv3Zvdw/default.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-705575826041557715</id><published>2011-08-30T09:21:00.001-07:00</published><updated>2011-08-30T09:25:19.365-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='humor'/><category scheme='http://www.blogger.com/atom/ns#' term='computer history'/><title type='text'>Humor: Millions of Microprocessors</title><content type='html'>Today I ran "fortune", and I got:&lt;blockquote&gt;I went to my first computer conference at the New York Hilton about 20 years ago.  When somebody there predicted the market for microprocessors would eventually be in the millions, someone else said, "Where are they all going to go? It's not like you need a computer in every doorknob!"&lt;br /&gt;&lt;br /&gt;Years later, I went back to the same hotel.  I noticed the room keys had been replaced by electronic cards you slide into slots in the doors.&lt;br /&gt;&lt;br /&gt;There was a computer in every doorknob.&lt;br /&gt;		-- Danny Hillis&lt;/blockquote&gt;Aside from the fact that that's an awesome quote, I'm pretty excited about this because Dannis Hillis was one of the founders at a company I worked for (Metaweb).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-705575826041557715?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/705575826041557715/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=705575826041557715' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/705575826041557715'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/705575826041557715'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/08/humor-millions-of-microprocessors.html' title='Humor: Millions of Microprocessors'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-6140256931934455539</id><published>2011-08-23T11:53:00.000-07:00</published><updated>2011-08-23T12:55:05.312-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='vim'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><title type='text'>Linux: Vim Freezing</title><content type='html'>I had a problem with Vim freezing, and I finally figured out why.  If you log into a Linux machine with X11 forwarding turned on, but without an X server, vim hangs when you try to start it.  So do various other commands.&lt;br /&gt;&lt;br /&gt;I recently switched from an Ubuntu laptop to a MacBook Pro running OS X.  In my .ssh/config, I had:&lt;pre&gt;Host my-server-name&lt;br /&gt; ForwardX11 yes&lt;/pre&gt;When I sshed in from my Linux box, things were fine.  However, when I sshed in from my Mac, things were broken.  There are three workarounds:&lt;ol&gt;&lt;li&gt;Don't enabled X11 forwarding.  X11 forwarding is enabled for ssh by either the -Y or -X flags or by the ForwardX11 configuration variable.&lt;/li&gt;&lt;li&gt;If you do enable X11 forwarding, make sure you start an X11 server first and then log in with a fresh xterm or terminal window so that the DISPLAY environmental variable is properly set.&lt;/li&gt;&lt;li&gt;Pass -X to vim so that it doesn't try to connect to the X server.  Vim will not be able to alter the window title or the clipboard.  It was surprising to me that vim will try to connect to the X server even if you're not using gvim.  This approach won't fix other applications that may rely on X in unknown ways.&lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-6140256931934455539?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/6140256931934455539/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=6140256931934455539' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/6140256931934455539'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/6140256931934455539'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/08/linux-vim-freezing.html' title='Linux: Vim Freezing'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-597444716337613352</id><published>2011-08-11T18:35:00.000-07:00</published><updated>2011-08-11T18:46:22.900-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='html5'/><category scheme='http://www.blogger.com/atom/ns#' term='css'/><title type='text'>CSS: Fading Clipped Text</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/-N0-RS1nZacg/TkSEDtvHimI/AAAAAAAAAMM/E1oEUgBAL70/s1600/screenshot.png"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 319px; height: 320px;" src="http://4.bp.blogspot.com/-N0-RS1nZacg/TkSEDtvHimI/AAAAAAAAAMM/E1oEUgBAL70/s320/screenshot.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5639777832726399586" /&gt;&lt;/a&gt;CSS has a feature, "text-overflow: ellipsis;", that works really well when you want to clip a piece of text and replace it with "...".  However, it only works for a single line of text.  It doesn't work when you have a paragraph of text, and you want to clip all the text below a certain point.  &lt;br /&gt;&lt;br /&gt;This is a problem I've struggled with a couple times.  I've always wanted to figure out how to have the text fade out at the bottom of the box.  Before now, I've aways just clipped the text at the bottom of the box; that looks ugly since most of the time, the text is vertically clipped in the middle of a sentence.&lt;br /&gt;&lt;br /&gt;During a recent HTLM5 Hackathon at Google, I learned about gradients and masks.  After about 45 minutes worth of futzing around, I finally achieved the effect I was looking for.  Here's the code.  It basically puts a mask over the text with a gradient that makes the text fade out.&lt;pre&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;html&amp;gt;&lt;br /&gt;  &amp;lt;head&amp;gt;&lt;br /&gt;    &amp;lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8"&amp;gt;&lt;br /&gt;    &amp;lt;title&amp;gt;Fading Text&amp;lt;/title&amp;gt;&lt;br /&gt;    &amp;lt;style type="text/css"&amp;gt;&lt;br /&gt;      .box {&lt;br /&gt;        list-style: none;&lt;br /&gt;        padding: 5px;&lt;br /&gt;        background-color: #F5F5F5;&lt;br /&gt;        border: 1px solid #DCDCDC;&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      .video-list {&lt;br /&gt;        padding-left: 0;&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      .video-list li.box {&lt;br /&gt;        width: 400px;&lt;br /&gt;        height: 125px;&lt;br /&gt;        margin-bottom: 5px;&lt;br /&gt;        overflow: hidden;&lt;br /&gt;        position: relative;&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      .video-title {&lt;br /&gt;        font-weight: bold;&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      .video-duration {&lt;br /&gt;        padding-bottom: 1em;&lt;br /&gt;        font-style: italic;&lt;br /&gt;        font-size: 75%;&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      .video-description {&lt;br /&gt;        font-size: 75%;&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      .video-description-fade {&lt;br /&gt;        position: absolute;&lt;br /&gt;        bottom: 0;&lt;br /&gt;        left: 5px;&lt;br /&gt;        height: 2em;&lt;br /&gt;        background-color: #F5F5F5;&lt;br /&gt;        -webkit-mask-image: -webkit-gradient(&lt;br /&gt;        linear, left top, left bottom,&lt;br /&gt;        from(rgba(0,0,0,0)), to(rgba(0,0,0,1)));&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      .video-description,&lt;br /&gt;      .video-description-fade {&lt;br /&gt;        width: 272px;&lt;br /&gt;      }&lt;br /&gt;    &amp;lt;/style&amp;gt;&lt;br /&gt;  &amp;lt;/head&amp;gt;&lt;br /&gt;  &amp;lt;body&amp;gt;&lt;br /&gt;    &amp;lt;ul class="video-list"&amp;gt;&lt;br /&gt;      &amp;lt;li class="box playlist-item"&amp;gt;&lt;br /&gt;        &amp;lt;div class="video-title"&amp;gt;Google I/O 2011: HTML5 Showcase for Web Developers: The Wow and the How&amp;lt;/div&amp;gt;&lt;br /&gt;        &amp;lt;div class="video-duration"&amp;gt;60:23&amp;lt;/div&amp;gt;&lt;br /&gt;        &amp;lt;div class="video-description"&amp;gt;&lt;br /&gt;          Eric Bidelman, Arne Roomann-Kurrik&lt;br /&gt;&lt;br /&gt;          We'll share the strengths and extents of HTML5, showing magnificent&lt;br /&gt;          demos of bleeding-edge features in Google Chrome. Digging into&lt;br /&gt;          high-fidelity graphics, performance, and system integration, we'll&lt;br /&gt;          break each demo down on the big screen to show how it was&lt;br /&gt;          constructed. Then we'll show you how to use Chrome to its full&lt;br /&gt;          potential in your own projects.&lt;br /&gt;        &amp;lt;/div&amp;gt;&lt;br /&gt;        &amp;lt;div class="video-description-fade"&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;      &amp;lt;/li&amp;gt;&lt;br /&gt;&lt;br /&gt;      &amp;lt;li class="box playlist-item"&amp;gt;&lt;br /&gt;        &amp;lt;div class="video-title"&amp;gt;Chicken Monkey Duck&amp;lt;/div&amp;gt;&lt;br /&gt;        &amp;lt;div class="video-duration"&amp;gt;1:20&amp;lt;/div&amp;gt;&lt;br /&gt;        &amp;lt;div class="video-description"&amp;gt;&lt;br /&gt;          Get it on iTunes:&lt;br /&gt;          http://itunes.apple.com/us/album/the-very-last-songs-i-will/id379861243&lt;br /&gt;&lt;br /&gt;          LYRICS&lt;br /&gt;&lt;br /&gt;          Monkey chicken chicken,&lt;br /&gt;          Monkey Chicken, duck duck,&lt;br /&gt;          Chicken monkey monkey, Chicken Monkey,&lt;br /&gt;          chicken chicken monkey duck.&lt;br /&gt;          Monkey duck,&lt;br /&gt;          Chicken duck,&lt;br /&gt;          Monkey monkey duck duck,&lt;br /&gt;          Chicken Monkey, chicken chicken monkey,&lt;br /&gt;          "Chicken Monkey Duck."&lt;br /&gt;&lt;br /&gt;          Chicken chicken monkey duck,&lt;br /&gt;          Chicken Monkey, duck duck,&lt;br /&gt;          Chicken chicken monkey,&lt;br /&gt;          Chicken monkey,&lt;br /&gt;          Chicken duck.&lt;br /&gt;          Chicken duck duck,&lt;br /&gt;          Chicken monkey monkey duck,&lt;br /&gt;          "Chicken Monkey Duck?"&lt;br /&gt;          Chicken duck.&lt;br /&gt;          Monkey duck duck:&lt;br /&gt;          Chicken chicken, monkey,&lt;br /&gt;          Chicken, monkey monkey, Chicken Monkey.&lt;br /&gt;          Chicken chicken monkey?&lt;br /&gt;          Chicken, monkey monkey,&lt;br /&gt;          "Chicken Monkey Duck."&lt;br /&gt;&lt;br /&gt;          Chicken chicken, monkey,&lt;br /&gt;          "Chicken Monkey Duck."&lt;br /&gt;          Chicken chicken, duck.&lt;br /&gt;          Chicken Monkey, monkey,&lt;br /&gt;          Chicken Monkey—chicken duck.&lt;br /&gt;&lt;br /&gt;          Duck, Chicken Monkey&lt;br /&gt;          Chicken chicken monkey, chicken.&lt;br /&gt;          Duck duck,&lt;br /&gt;          Chicken chicken, duck.&lt;br /&gt;          Chicken, monkey monkey...&lt;br /&gt;          Chicken!&lt;br /&gt;&lt;br /&gt;          Duck duck, Chicken Monkey,&lt;br /&gt;          Chicken chicken, monkey.&lt;br /&gt;          Chicken Chicken (monkey monkey),&lt;br /&gt;          Chicken monkey,&lt;br /&gt;          "Chicken Monkey Duck."&lt;br /&gt;&lt;br /&gt;          Chicken chicken chicken,&lt;br /&gt;          Monkey monkey,&lt;br /&gt;          Chicken Monkey:&lt;br /&gt;          Duck duck duck duck duck duck duck duck,&lt;br /&gt;          "Goose."&lt;br /&gt;&lt;br /&gt;          The Album:&lt;br /&gt;          iTunes — http://itunes.apple.com/us/album/the-very-last-songs-i-will/id379861243&lt;br /&gt;&lt;br /&gt;          Bandcamp — http://music.mikephirman.com&lt;br /&gt;        &amp;lt;/div&amp;gt;&lt;br /&gt;        &amp;lt;div class="video-description-fade"&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;      &amp;lt;/li&amp;gt;&lt;br /&gt;    &amp;lt;/ul&amp;gt;&lt;br /&gt;  &amp;lt;/body&amp;gt;&lt;br /&gt;&amp;lt;/html&amp;gt;&lt;/pre&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-597444716337613352?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/597444716337613352/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=597444716337613352' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/597444716337613352'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/597444716337613352'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/08/css-fading-text.html' title='CSS: Fading Clipped Text'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-N0-RS1nZacg/TkSEDtvHimI/AAAAAAAAAMM/E1oEUgBAL70/s72-c/screenshot.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-3790964313748091988</id><published>2011-08-11T16:41:00.001-07:00</published><updated>2011-08-11T19:00:16.884-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Python: Newbie Nugget: Lambda Expressions</title><content type='html'>I gave a talk for the &lt;a href="http://www.meetup.com/sfpython/"&gt;SF Python Meetup&lt;/a&gt; on lambda expressions:&lt;br /&gt;&lt;br /&gt;&lt;iframe src="https://docs.google.com/present/embed?id=dfbr4rw2_5w2zpfxgh" frameborder="0" width="410" height="342"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;(If you can't see the slides above, you can &lt;a href="https://docs.google.com/present/view?id=dfbr4rw2_5w2zpfxgh"&gt;see them here&lt;/a&gt;.)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-3790964313748091988?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/3790964313748091988/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=3790964313748091988' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/3790964313748091988'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/3790964313748091988'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/08/python-newbie-nugget-lambda-expressions.html' title='Python: Newbie Nugget: Lambda Expressions'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-4827511225742808149</id><published>2011-08-03T11:30:00.001-07:00</published><updated>2011-08-03T11:36:10.071-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OS X'/><category scheme='http://www.blogger.com/atom/ns#' term='vnc'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>Linux: VNC Stopped Working</title><content type='html'>I have been using VNC to connect from my Ubuntu box to an OS X box.  On the Ubuntu side, I was using vinagre, which is the standard "Remote Desktop Viewer" application.  On the Mac side, I was using standard desktop sharing.&lt;br /&gt;&lt;br /&gt;Things were working for months, but they stopped working for me the other day.  I could connect just fine, but I could only see a black screen.  Apparently, this is a &lt;a href="https://bugs.launchpad.net/ubuntu/+source/vinagre/+bug/776028"&gt;known issue&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Switching to Remmina (sudo apt-get install remmina) fixed the problem.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-4827511225742808149?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/4827511225742808149/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=4827511225742808149' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/4827511225742808149'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/4827511225742808149'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/08/linux-vnc-stopped-working.html' title='Linux: VNC Stopped Working'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-5287386066853293117</id><published>2011-07-23T15:08:00.000-07:00</published><updated>2011-07-23T15:23:53.438-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C'/><category scheme='http://www.blogger.com/atom/ns#' term='ObjC'/><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='Go'/><title type='text'>Go: A Second Impression</title><content type='html'>My initial impressions of Go have always been somewhat negative because I was comparing it to things like Scala, Haskell, and Python.  However, having read a good part of the tutorial, I've changed my opinion.  I really like Go!&lt;br /&gt;&lt;br /&gt;Go doesn't try to be an extension of C to make it more like Smalltalk--that's Objective C.  Go doesn't try to be the end-all-be-all of object-oriented languages derived from C--that's Scala.  &lt;br /&gt;&lt;br /&gt;Since it neither maintains backwards compatibility with C nor adds a ton of features, I originally had a hard time getting excited about Go.  Now I see that Go is a modern language that tries to follow what I think of as C's &lt;i&gt;philosophy&lt;/i&gt;.  It's simple, elegant, small, and native.&lt;br /&gt;&lt;br /&gt;There's an old saying:&lt;blockquote&gt;&lt;i&gt;Do not seek to follow in the footsteps of the wise men of old. Seek what they sought.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt; - Matsuo Munefusa (”Basho”)&lt;/blockquote&gt;I think that perfectly describes Go.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-5287386066853293117?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/5287386066853293117/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=5287386066853293117' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/5287386066853293117'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/5287386066853293117'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/07/go-second-impression.html' title='Go: A Second Impression'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-4910882631565386262</id><published>2011-07-16T08:08:00.000-07:00</published><updated>2011-07-16T08:28:41.896-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='proofs'/><category scheme='http://www.blogger.com/atom/ns#' term='computer science'/><title type='text'>Humor: Proving Programs</title><content type='html'>I've always been weary of programming proofs.&lt;br /&gt;&lt;br /&gt;For instance, I can mathematically prove that 4195835 / 3145727 &gt; 1.3338.  However, I know of certain &lt;a href="http://en.wikipedia.org/wiki/Pentium_FDIV_bug"&gt;Pentium processors&lt;/a&gt; that would disagree with me.  If I try to prove that the following bit of C code prints out "Hello World":&lt;pre&gt;if (4195835.0 / 3145727.0 &amp;gt; 1.3338)&lt;br /&gt;    printf("Hello World\n");&lt;br /&gt;else&lt;br /&gt;    system("rm -rf /");&lt;/pre&gt;I might be a bit surprised when it deletes my hard drive instead ;)&lt;br /&gt;&lt;br /&gt;1 bunny + 1 bunny = 2 bunnies, right?  Well it depends on their sexes.  It's possible that in a given time period, 1 bunny + 1 bunny might equal 5 bunnies.  As I joked in a previous blog post, "All models are wrong.  Some models are useful."&lt;br /&gt;&lt;br /&gt;I really think this same thing applies to proving programs.  Donald Knuth &lt;a href="http://en.wikipedia.org/wiki/Donald_Knuth"&gt;famously said&lt;/a&gt;, "Beware of bugs in the above code; I have only proved it correct, not tried it."&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-4910882631565386262?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/4910882631565386262/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=4910882631565386262' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/4910882631565386262'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/4910882631565386262'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/07/humor-proving-programs.html' title='Humor: Proving Programs'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-3275846558930072870</id><published>2011-07-16T06:52:00.001-07:00</published><updated>2011-07-16T07:36:31.700-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='software engineering'/><category scheme='http://www.blogger.com/atom/ns#' term='programming languages'/><title type='text'>Books: Masterminds of Programming</title><content type='html'>I just finished reading &lt;a href="http://www.amazon.com/Masterminds-Programming-Conversations-Creators-Languages/dp/0596515170/ref=sr_1_1?ie=UTF8&amp;qid=1310824099&amp;sr=8-1"&gt;Masterminds of Programming: Conversations with the Creators of Major Programming Languages&lt;/a&gt;&lt;blockquote&gt;Masterminds of Programming features exclusive interviews with the creators of several historic and highly influential programming languages. In this unique collection, you'll learn about the processes that led to specific design decisions, including the goals they had in mind, the trade-offs they had to make, and how their experiences have left an impact on programming today.&lt;/blockquote&gt;In short, I really enjoyed it.  Here's an extremely abbreviated and opinionated summary:&lt;br /&gt;&lt;br /&gt;Adin D. Falkoff (APL) made programming as mathematical as possible.&lt;br /&gt;&lt;br /&gt;Thomas E. Kurtz (BASIC) was generally a nice guy who wanted to bring programming to the masses.&lt;br /&gt;&lt;br /&gt;Charles H. Moore (FORTH) frustrated the heck out of me.  He stated that operating systems are the software industry's biggest con job.  I disagree.  Operating systems protect me to some degree from bad and malicious code.  They also let me run multiple programs at the same time and allow me to keep running even when one of the programs crashes.  He also said that a piece of code written in any other programming language will be 10 times as large (in number of lines of code) as the same code written in Forth.  I'd like to see him try that trick with Python!&lt;br /&gt;&lt;br /&gt;Robin Milner (ML) was completely fascinated with programming models and proving the correctness of code.  That reminds me of the quote, "All models are wrong.  Some models are useful."&lt;br /&gt;&lt;br /&gt;Donald D. Chamberlin (SQL) showed me some of the history of SQL.  I didn't know IBM research was such an interesting place.&lt;br /&gt;&lt;br /&gt;Alfred Aho, Peter Weinberger, and Brian Kernighan (AWK) were as good as I expected.&lt;br /&gt;&lt;br /&gt;Charles Geschke and John Warnock (PostScript) talked about Adobe and the history of PostScript.  I just don't like that Charles guy, and I don't like Adobe.  However, they're smart guys.&lt;br /&gt;&lt;br /&gt;Bjarne Stroustrup (C++) was as frustrating as I expected.&lt;br /&gt;&lt;br /&gt;Bertrand Meyer (Eiffel) was really interesting.  He wrote a book in French that has had a profound impact on French programmers.  If he had translated that book into English, it's likely he'd be as famous as, say, Richard Stevens (the author of "UNIX Network Programming").&lt;br /&gt;&lt;br /&gt;Brad Cox and Tom Love (Objective-C) showed me that Objective-C's goal was to enhance C in the smallest way possible to make it a bit more like Smalltalk.&lt;br /&gt;&lt;br /&gt;Larry Wall (Perl) was awesome, as usual.&lt;br /&gt;&lt;br /&gt;Simon Peyton Jones, Paul Hudak, Philip Wadler, and John Hughes (Haskell) were fascinating, as usual.&lt;br /&gt;&lt;br /&gt;Guido van Rossum (Python) was practical and interesting, as usual.&lt;br /&gt;&lt;br /&gt;Luiz Henrique de Figueiredo and Roberto Ierusalimschy (Lua) were okay.  (I'm a Python guy, so it's a bit hard for me to get excited about Lua.)&lt;br /&gt;&lt;br /&gt;James Gosling (Java) appears to suffer from premature optimization.&lt;br /&gt;&lt;br /&gt;Grady Booch, Ivar Jacobson, and James Rumbaugh (UML) left me even less interested in learning UML.&lt;br /&gt;&lt;br /&gt;Anders Hejlsberg (Turbo Pascal, Delphi, C#) was awesome.  I knew I liked this guy from previous books, but this interview made me like him even more.&lt;br /&gt;&lt;br /&gt;Overall, I think this book was a bit drier than, say, &lt;a href="http://www.codersatwork.com/"&gt;Coders at Work: Reflections on the Craft of Programming&lt;/a&gt;, so you should read that one first.  However, if you're a guy like me who loves programming languages, this book is a must read.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-3275846558930072870?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/3275846558930072870/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=3275846558930072870' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/3275846558930072870'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/3275846558930072870'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/07/books-masterminds-of-programming.html' title='Books: Masterminds of Programming'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-1510255150546322227</id><published>2011-07-15T23:52:00.000-07:00</published><updated>2011-07-16T06:28:08.821-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python3'/><category scheme='http://www.blogger.com/atom/ns#' term='jquery'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Books: Python 3 Web Development Beginner’s Guide</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/-vzZvZggEdfE/TiE2LU2hBvI/AAAAAAAAAKs/sGAVaFeJjJE/s1600/Python%2B3%2BWeb%2BDevelopment%2BBeginner%25E2%2580%2599s%2BGuide.png"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 125px; height: 152px;" src="http://2.bp.blogspot.com/-vzZvZggEdfE/TiE2LU2hBvI/AAAAAAAAAKs/sGAVaFeJjJE/s320/Python%2B3%2BWeb%2BDevelopment%2BBeginner%25E2%2580%2599s%2BGuide.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5629840577393657586" /&gt;&lt;/a&gt;&lt;br /&gt;Packt Publishing asked me to review &lt;a href="http://www.packtpub.com/python-3-web-development-beginners-guide/book"&gt;Python 3 Web Development Beginner's Guide&lt;/a&gt;.  I'll have to admit, it's a bit of an odd duck.  A better (albeit overly verbose) title might have been "An Introduction to Rich Internet Application Development Using jQuery UI, a Very Modern Version of Python, a Relatively Old Python Web Application Framework Named CherryPy, and an Ancient Version of HTML Written by a Guy Who Uses Windows".&lt;br /&gt;&lt;br /&gt;The first tipoff that this book was a bit strange was that the author uses Windows and some combination of Firefox and IE.  It seems like most web developers use OS X (or occasionally Linux), and they prefer Chrome over IE.&lt;br /&gt;&lt;br /&gt;The next tipoff was the use of jQuery UI.  jQuery UI is a very modern technology which is often used to build rich internet applications.  RIAs really aren't the sort of thing that I would expect to see in a book for beginners.  What happened to the old days when beginning web applications focused on the server dynamically generating HTML?  If I took the time to count the number of lines of code, I wouldn't be surprised if this book had more JavaScript than Python.&lt;br /&gt;&lt;br /&gt;The title of this book mentions Python 3, but if you search for "Python 3" in the book, there are extremely few mentions of it.  This book really isn't about Python 3 per se (as compared to Python 2); it has a lot more to do with jQuery UI.&lt;br /&gt;&lt;br /&gt;Whereas Python 3 and jQuery UI are very modern technologies, standing in contrast is the book's use of HTML 4 and CherryPy.  HTML 4 is an *ancient* version of HTML.  I would expect anyone using jQuery UI to use either XHTML or HTML5.  At the very least, I would have expected one of the transitional DTDs.  Similarly, he uses CherryPy.  Although I agree that CherryPy is solid code, it's also fairly old.  It predates any of the modern Python frameworks.&lt;br /&gt;&lt;br /&gt;This book claims to teach web development "without having to learn another web framework" [p. 1].  That's simply not true.  It makes heavy use of CherryPy.  The &lt;a href="http://www.cherrypy.org/"&gt;home page for CherryPy&lt;/a&gt; calls it an "HTTP framework" and says that it has "everything you would expect from a decent web framework."  It's not as full-featured as, say, Django, but parts in the example code such as "@cherrypy.expose" [p. 36] are certainly framework features.  In fact, "@cherrypy.expose" is part of CherryPy's object publishing system, which it uses as a replacement for regex-based URL routing.&lt;br /&gt;&lt;br /&gt;Another thing that's a bit strange about this book is that the author doesn't use a client-side or a server-side templating language.  In JavaScript, he tends to use string concatenation, which is weird because there is a templating plugin for jQuery.  On the server, he embeds HTML directly in the Python code, which is pretty ugly (as he mentions on p. 229).&lt;br /&gt;&lt;br /&gt;Furthermore, the code is extremely sloppy.  The code does not follow Python's style guide concerning whitespace (PEP-8) (see, for example, p. 145) even though PEP-8 is extremely standard in the Python community.  I don't know of anyone who puts a space before the colon in expressions such as "if not isinstance(name,str) :" [p. 146].  Nor is it even self consistent.  The indentation in the JavaScript is not only non-standard and inconsistent, it's occasionally completely wrong [p. 118] (i.e. the indentation disagrees with the braces).&lt;br /&gt;&lt;br /&gt;Aside from bad style, I'm a little concerned about various coding practices.  For instance, the JavaScript at the bottom of p. 40 has variables that don't use var.  This means they're effectively global.  This is extremely bad practice.  Fortunately, he does use var in other places in the book.&lt;br /&gt;&lt;br /&gt;On the subject of security, there are several standard security vulnerabilities that web applications must protect against:  XSS (cross-site scripting vulnerabilities), SQL injection attacks, XSRF (cross-site request forgeries), and session fixation (or session hijacking) attacks.  Every book on web development should cover these.&lt;br /&gt;&lt;br /&gt;The book mentions XSS, but I fear it's approach may not be thorough enough.  It does not mention the term "SQL injection" attack, but the ORM shown in the book does look to be somewhat safe.  It mentions XSRF, but says that it's out of scope.  It doesn't mention "session fixation" or "session hijacking" at all.  In general, I don't think the book is good enough about "escaping things" properly.  For instance, on p. 293 the author creates a URL in JavaScript using values from a form, but he doesn't take care to URL encode the parameters.&lt;br /&gt;&lt;br /&gt;Despite all of the above, I can say this about the book.  The author does a good job explaining the web to beginners.  Modern web applications are fairly complicated beasts.  There's the client, the web server, and the database server, and they each require their own syntaxes.  The author does a decent job explaining what runs where.  It can be difficult for an expert web developer, such as myself, to remember that newbies might not know all these things.&lt;br /&gt;&lt;br /&gt;In summary, will this book help you become a competent, professional web developer?  Absolutely not.  Is it as well written as, say, &lt;a href="http://pragprog.com/book/rails4/agile-web-development-with-rails"&gt;Agile Web Development with Rails&lt;/a&gt;.  No.  However, might it be a good way for a beginner to dip his toes in web development with Python and jQuery UI?  Maybe.&lt;br /&gt;&lt;br /&gt;(Disclaimers: Packt gave me a free electronic copy of this book in trade for my review.  I have not read the whole thing.  I did read the first 50 pages and skimmed various key sections.)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-1510255150546322227?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/1510255150546322227/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=1510255150546322227' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/1510255150546322227'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/1510255150546322227'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/07/books-python-3-web-development.html' title='Books: Python 3 Web Development Beginner’s Guide'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-vzZvZggEdfE/TiE2LU2hBvI/AAAAAAAAAKs/sGAVaFeJjJE/s72-c/Python%2B3%2BWeb%2BDevelopment%2BBeginner%25E2%2580%2599s%2BGuide.png' height='72' width='72'/><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-2639962398891823162</id><published>2011-07-07T07:36:00.000-07:00</published><updated>2011-07-07T07:36:00.071-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='googleio'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='Go'/><category scheme='http://www.blogger.com/atom/ns#' term='io2011'/><category scheme='http://www.blogger.com/atom/ns#' term='google'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>Google I/O 2011</title><content type='html'>I went to Google I/O May 10-12, 2011.  Note that the first day of Google I/O was my second day as a Google employee.  These are my personal notes.  If you're interested in any of these talks, you can find them on YouTube.&lt;h4&gt;Keynote&lt;/h4&gt;One of the slides during the keynote showed a picture of an android eating an apple.&lt;br /&gt;&lt;br /&gt;There were 5000 people in the room for the keynote.&lt;br /&gt;&lt;br /&gt;There were 110 cities watching at viewer parties.&lt;br /&gt;&lt;br /&gt;Android has come a long way:  100 million activations, 450,000 developers, 215 carriers, 310 devices, 112 countries, 400,000 activations per day, 200,000 apps in the Android market,  4.5 billion apps installed.&lt;br /&gt;&lt;br /&gt;Android 3.1 is Honeycomb.&lt;br /&gt;&lt;br /&gt;Android can now act as a USB host.  Hence, you can now get a keyboard and a mouse for your tablet.&lt;br /&gt;&lt;br /&gt;You can use an X-Box controller with it.&lt;br /&gt;&lt;br /&gt;Android 3.1 will be used for Google TV.&lt;br /&gt;&lt;br /&gt;Android market will be available on Google TV.&lt;br /&gt;&lt;br /&gt;Ice Cream Sandwich will be the new Android release.&lt;br /&gt;&lt;br /&gt;The hope is for one Android OS everywhere.&lt;br /&gt;&lt;br /&gt;It will be open source.&lt;br /&gt;&lt;br /&gt;They have Android software that recognizes where you're looking and can recognize your head movement too.&lt;br /&gt;&lt;br /&gt;There are books on Android market.&lt;br /&gt;&lt;br /&gt;There are movies on Android market.  They're not tied to any specific device.  They're just tied to your Google account.&lt;br /&gt;&lt;br /&gt;You can "pin" a movie in order to download it to your device.&lt;br /&gt;&lt;br /&gt;Verizon seems to have the coolest new Android stuff.&lt;br /&gt;&lt;br /&gt;Google has a new music service in beta.&lt;br /&gt;&lt;br /&gt;If you add music to your account, you can listen to that music from any of device.&lt;br /&gt;&lt;br /&gt;There are Windows and Mac clients.&lt;br /&gt;&lt;br /&gt;There's a web-based music manager.&lt;br /&gt;&lt;br /&gt;It's completely synced on all devices.&lt;br /&gt;&lt;br /&gt;There's an "instant mix" feature based on machine learning.&lt;br /&gt;&lt;br /&gt;The music is kept in the cloud.  There's no need for syncing.&lt;br /&gt;&lt;br /&gt;You can cache music recently played.&lt;br /&gt;&lt;br /&gt;You can make stuff available instantly.&lt;br /&gt;&lt;br /&gt;The Music application is initially by invitation only.&lt;br /&gt;&lt;br /&gt;It's free while in beta.&lt;br /&gt;&lt;br /&gt;You can upload 20,000 songs.&lt;br /&gt;&lt;br /&gt;You need Android 2.2 or higher for the Android app.&lt;br /&gt;&lt;br /&gt;There's going to be an alliance to determine how quickly Android devices should be updated and how long they should be maintained.&lt;br /&gt;&lt;br /&gt;Sprint is in the Alliance.  So is Verizon.  I'm a little peeved at Sprint because they won't upgrade my Samsung Moment beyond Android 2.1, and I need at least 2.2 for some of the apps I'm interested in.&lt;br /&gt;&lt;br /&gt;Android open-accessory is a standard accessory platform.  It has hardware and software components.&lt;br /&gt;&lt;br /&gt;There was a demo of an exercise bike plugging into a phone.&lt;br /&gt;&lt;br /&gt;The accessory development kit (ADK) is based on Arduino.&lt;br /&gt;&lt;br /&gt;There was a demo of a full-sized (i.e. man-sized) labyrinth board controlled by tilting a tablet.&lt;br /&gt;&lt;br /&gt;The ADK doesn't have any NDAs or fees.  It's completely open.&lt;br /&gt;&lt;br /&gt;Android @ Home is an OS for your home.&lt;br /&gt;&lt;br /&gt;There's an Android @ Home framework.  It has its own wireless protocol.  It has very low cost connectivity.&lt;br /&gt;&lt;br /&gt;He said to envision a real world Farmville ;)&lt;br /&gt;&lt;br /&gt;He showed a demo of the lights being connected to a game of Quake.&lt;br /&gt;&lt;br /&gt;LightingScience has prototype lightbulbs.&lt;br /&gt;&lt;br /&gt;Project Tungsten is a hub for your home.  It runs Android @ Home.&lt;br /&gt;&lt;br /&gt;They showed a prototype where you can just touch a CD to the hub and it starts streaming music from the cloud.&lt;br /&gt;&lt;br /&gt;Android@Home.&lt;br /&gt;&lt;br /&gt;They gave away free tablets!!!&lt;br /&gt;&lt;br /&gt;There wasn't any mention of GAE.  It was mostly about Android.&lt;h4&gt;Life of a Google API Developer&lt;/h4&gt;They were showing off Google API explorer, which looks pretty neat.&lt;br /&gt;&lt;br /&gt;There's a list of APIs.  Try them.&lt;br /&gt;&lt;br /&gt;There are lots of APIs.&lt;br /&gt;&lt;br /&gt;See &lt;a href="http://code.google.com/apis/explorer"&gt;code.google.com/apis/explorer&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The API explorer is much nicer than using curl.&lt;br /&gt;&lt;br /&gt;They use OAuth2 with delegated auth.&lt;br /&gt;&lt;br /&gt;The Google API Discovery Service is an API to serve docs about APIs.&lt;br /&gt;&lt;br /&gt;There are generic client libraries for the Google stuff for Java, Python, PHP, .NET, Ruby, etc.&lt;br /&gt;&lt;br /&gt;OAuth2 is complicated, but they have nice helper libraries.&lt;br /&gt;&lt;br /&gt;They're using Mercurial on code.google.com.&lt;br /&gt;&lt;br /&gt;Daryl Spitzer said Go is amazingly succinct.&lt;br /&gt;&lt;br /&gt;Passwords are like underwear.  Keep them secret.  Change them often.&lt;br /&gt;&lt;br /&gt;The APIs have quotas.  However, they're reasonable for moderate apps.&lt;br /&gt;&lt;br /&gt;Google is introducting some APIs that you have to pay for.&lt;br /&gt;&lt;br /&gt;5000 tablets were given to users for free a month before the actual release of the tablets.&lt;br /&gt;&lt;br /&gt;The talks for Google I/O are on YouTube.&lt;h4&gt;Go for Web Apps&lt;/h4&gt;Rob Pike (of UNIX fame) gave part of the talk!&lt;br /&gt;&lt;br /&gt;Go is fun, efficient, and open source.&lt;br /&gt;&lt;br /&gt;Go has a web server.&lt;br /&gt;&lt;br /&gt;It's natively compiled.&lt;br /&gt;&lt;br /&gt;It has gdb support.&lt;br /&gt;&lt;br /&gt;It simplifies some stuff.&lt;br /&gt;&lt;br /&gt;It was originally intended for systems code like building web servers, but it's now more general.&lt;br /&gt;&lt;br /&gt;It has 1st class functions.&lt;br /&gt;&lt;br /&gt;It has low level types, like float32.&lt;br /&gt;&lt;br /&gt;I don't understand its replacement for exceptions.  [Several weeks later, I read an article on &lt;a href="http://blog.golang.org/2010/08/defer-panic-and-recover.html"&gt;Defer, Panic, and Recover&lt;/a&gt;.]&lt;br /&gt;&lt;br /&gt;goinstall is its package manager.&lt;br /&gt;&lt;br /&gt;It has closures.&lt;br /&gt;&lt;br /&gt;It is statically typed.&lt;br /&gt;&lt;br /&gt;Google API clients are coming for Go.&lt;br /&gt;&lt;br /&gt;Go now has support for GAE!  The apps are compiled in the cloud.&lt;br /&gt;&lt;br /&gt;Go is the first native language on GAE.&lt;br /&gt;&lt;br /&gt;Go is not theoretically interesting.  It's just really useful.&lt;br /&gt;&lt;br /&gt;Go's library support is improving quickly.&lt;br /&gt;&lt;br /&gt;It has garbage collection and full control over how memory is laid out.&lt;h4&gt;Programming Well With Others&lt;/h4&gt;The format for of this talk was really entertaining.  In fact, it was my favorite talk.  They pretended to do a radio show called "The Ben and Fitz Show".&lt;br /&gt;&lt;br /&gt;There are people who are friendly but steal too much time and attention.&lt;br /&gt;&lt;br /&gt;Perfectionists suck energy out of a project.&lt;br /&gt;&lt;br /&gt;"You are not your code."&lt;h4&gt;Python at Google&lt;/h4&gt;This talk was by Wesley Chun.&lt;br /&gt;&lt;br /&gt;io2011 is the tag to use for Google I/O this year.&lt;br /&gt;&lt;br /&gt;Python just turned 20.&lt;br /&gt;&lt;br /&gt;C++ is Google's primary language.&lt;br /&gt;&lt;br /&gt;Python was used at Google before Google really existed.  It was used in the original crawler.&lt;br /&gt;&lt;br /&gt;Java came later.&lt;br /&gt;&lt;br /&gt;Google has a Python training class.&lt;br /&gt;&lt;br /&gt;YouTube gets 2 billion views/day.&lt;br /&gt;&lt;br /&gt;There are 35 hours of video uploaded to YouTube every minute.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://cp4k.blogspot.com/"&gt;Welcome to Computer Programming For Kids (CP4K)&lt;/a&gt; looks like a fun book.&lt;h4&gt;HTML5 Showcase&lt;/h4&gt;This talk was amazing.  &lt;br /&gt;&lt;br /&gt;Here's &lt;a href="http://bit.ly/html5io2010"&gt;the video&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Here's &lt;a href="http://code.google.com/p/html5/"&gt;some code&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;HTML5 can handle binary data.&lt;br /&gt;&lt;br /&gt;There's a file uploader that can upload multiple files or even a whole directory.  You can drag and drop images to upload them.&lt;br /&gt;&lt;br /&gt;The talk was overwhelmingly awesome!&lt;br /&gt;&lt;br /&gt;There's 3D support in the browser.&lt;br /&gt;&lt;br /&gt;There's WebGL 3D.  It runs on the GPU.&lt;br /&gt;&lt;br /&gt;They showed a 3D file browser tied to an in-browser command line.&lt;br /&gt;&lt;br /&gt;They showed real-time audio processing in JavaScript.&lt;br /&gt;&lt;br /&gt;They showed CSS that produces 3D effects declaratively.&lt;br /&gt;&lt;br /&gt;The three parts of this talk were about files, graphing, and audio.&lt;h4&gt;YouTube APIs iframe Player&lt;/h4&gt;Using the iframe player simplifies things.&lt;br /&gt;&lt;br /&gt;HTML5 doesn't have fine tuned video support yet:  there's a content protection problem; there's no full-sceen HD (except Webkit); there's no microphone and camera access; there's a format problem.&lt;br /&gt;&lt;br /&gt;However:  HTML5 has better accessability; there's mobile support; it has faster start times; it's more reliable.&lt;br /&gt;&lt;br /&gt;HTML5 doesn't support all the features used by YouTube.&lt;br /&gt;&lt;br /&gt;Custom AS3 players won't work on iOS, of course.&lt;br /&gt;&lt;br /&gt;There's a YT object you can play with on the Chrome console.&lt;br /&gt;&lt;br /&gt;A guy named Greg wrote the HTML5 player.&lt;br /&gt;&lt;br /&gt;WebM is a better encoding.&lt;br /&gt;&lt;br /&gt;The performance of HTML5 video on older hardware is not necessarily better than Flash.  (That's disappoints me because I know Flash can be a hog.  I know Quicktime ran pretty well on older Mac hardware that can't keep up with watching modern movies via Flash.)&lt;h4&gt;Fireside for Developer Relations&lt;/h4&gt;Mike Wenton leads developer relations.&lt;br /&gt;&lt;br /&gt;There are lots of GTUGs (Google technology users groups).&lt;br /&gt;&lt;br /&gt;The public forums need more Google attention, especially for GAE.&lt;br /&gt;&lt;br /&gt;Google needs to reach out to unconverted fans at business conventions.&lt;br /&gt;&lt;br /&gt;Android's scale of growth has been mind blowing.&lt;br /&gt;&lt;br /&gt;There are a bunch of programmers who are nuts about Google.&lt;br /&gt;&lt;br /&gt;There were lots of developer advocates in the crowd.&lt;h4&gt;Closure Talk&lt;/h4&gt;The speaker was the one who did did the closure book.  He worked on Google calendar.  He's no longer at Google.&lt;br /&gt;&lt;br /&gt;He defined a large project to be 30k+ lines of code, 4+ people, and 6+ months.&lt;br /&gt;&lt;br /&gt;How should you code a RIA like Gmail and Calendar?  Use GWT?  Write a ton of JS?  Calendar, Gmail, Maps, Blogger, Docs, and Reader were all pure JavaScript.&lt;br /&gt;&lt;br /&gt;Closure has many parts.  There is the the Closure library, Closure templates, and the Closure compiler (which also does static checking).  They're all independent.  It even makes sense to use the Closure compiler with jQuery.&lt;br /&gt;&lt;br /&gt;Code written using the Prototype library is not easy for people to read unless they already know Prototype.&lt;br /&gt;&lt;br /&gt;This code always returns undefined because of ";" insertion:&lt;pre&gt;function f() {&lt;br /&gt; return&lt;br /&gt;  "foo";&lt;br /&gt;}&lt;/pre&gt;Too many script includes in the head leads to slowness.&lt;br /&gt;&lt;br /&gt;Create a special version for mobile.  However, this can lead to ugly forking.&lt;br /&gt;&lt;br /&gt;The Closure compiler can compile templates + your JavaScript + the Closure library iteself into optimized JavaScript.&lt;br /&gt;&lt;br /&gt;The compiler is a whole program optimizing compiler.&lt;br /&gt;&lt;br /&gt;The templating language escapes HTML by default.&lt;br /&gt;&lt;br /&gt;The closure library can compress an amazing amount.  In advanced mode, it's incredible.&lt;br /&gt;&lt;br /&gt;YUI compressor can compress JavaScript to 27% of original size.  However, the Closure compiler running in advanced mode can compile JavaScript to 0.5% of its original size.&lt;br /&gt;&lt;br /&gt;It does things like strip functions that are never called, which is useful if you're using a library like jQuery.&lt;br /&gt;&lt;br /&gt;Don't create a mobile version of your JavaScript library.  Use 1 version, and let the Closure compiler strip stuff not needed by defining which user agent you can assume.  Hence, you can compile multiple different versions of the same library.&lt;br /&gt;&lt;br /&gt;The compiler can statically enforce type hints that you give in docstring comments.&lt;br /&gt;&lt;br /&gt;Closure tools help manage and optimize large JavaScript apps.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-2639962398891823162?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/2639962398891823162/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=2639962398891823162' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/2639962398891823162'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/2639962398891823162'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/07/google-io-2011.html' title='Google I/O 2011'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-2798891346377792131</id><published>2011-07-06T12:53:00.000-07:00</published><updated>2011-07-06T13:09:03.844-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='pivotal labs'/><category scheme='http://www.blogger.com/atom/ns#' term='peer programming'/><category scheme='http://www.blogger.com/atom/ns#' term='software engineering'/><category scheme='http://www.blogger.com/atom/ns#' term='google'/><category scheme='http://www.blogger.com/atom/ns#' term='code review'/><title type='text'>Software Engineering: Code Reviews vs. Peer Programming</title><content type='html'>I've been thinking lately about the benefits of code reviews vs. peer programming.  In general, I think most companies that really care about code quality use either code reviews or peer programming.  Various companies are famous for using either one approach or the other, which leads me to wonder which approach is better under which circumstances?&lt;br /&gt;&lt;br /&gt;Pivotal Labs is famous for doing full-time peer programming.  It's well known that they do a good job writing software.  However, I wonder if full-time peer programming might be too expensive for mundane code.  I also wonder if it makes sense to work as a pair when someone needs to spend a few days reading, learning, and researching.&lt;br /&gt;&lt;br /&gt;In contrast, Google is famous for code reviewing all commits before checkin.  Certainly this frees up engineers to get more work done since they can spend a high percentage of their time working in parallel on separate tasks.  However, I wonder if a code reviewer really has the ability to make the same level of architectural improvements as a peer programmer.  Certainly a code reviewer can catch style mistakes, but it's much harder to tell someone their entire approach is wrong (for instance, threaded code vs. asynchronous code).&lt;br /&gt;&lt;br /&gt;Furthermore, I wonder if code reviewers in general can catch all the little assumptions that get built into code.  It reminds me of a story my boss once told me.  A few decades ago, he was working on satellite control software.  There was a piece of code that made it through three levels of code review even thought it contained a bug.  It was missing a minus sign in some equations having to do with navigation.  When the satellite was launched, it started spinning because it kept "thinking" it was upside down.  It wasted half of its fuel before they could get the problem under control.  My boss said that for satellites, the lifespan of a project is directly connected to the amount of fuel onboard.  Hence, this came to be known as the three million dollar minus sign.&lt;br /&gt;&lt;br /&gt;If this code had been peer programmed rather than code reviewed (or in addition to code review), would the peer have spotted the problem as the equation was being worked out?  Certainly this taught me a valuable lesson about code review.  It's far too easy to get hung up on less critical issues that are easy to spot, like style, instead of focusing on more important issues that require more brain power to understand.&lt;br /&gt;&lt;br /&gt;In the book &lt;a href="http://www.amazon.com/Professional-Software-Development-Schedules-Successful/dp/0321193679"&gt;Professional Software Development: Shorter Schedules, Higher Quality Products, More Successful Projects, Enhanced Careers&lt;/a&gt; (see &lt;a href="http://jjinux.blogspot.com/2006/04/software-engineering-professional.html"&gt;my blog post&lt;/a&gt;), Steve McConnell said that NASA found that the single most effective way to cut down on defects was to always have a second pair of eyes present.  They were talking about building the shuttle, but I think the same thing applies to software.&lt;br /&gt;&lt;br /&gt;Despite their pervasive use of code reviews or peer programming, Google and Pivotal Labs have both had their fair share of bugs.  Even NASA makes mistakes.  Hence, it's easy to see that neither code reviews nor peer programming can banish all defects.  Given how fallible human beings are, it seems to me that the best way to keep people from dying because of mistakes is to avoid situations where mistakes are fatal.  Driving is a fairly dangerous activity, and tens of thousands of people die each year in the United States because of driving errors.  However, even more than that make mistakes and yet survive because of various safety precautions.&lt;br /&gt;&lt;br /&gt;So even though I think code reviews and/or peer programming are important factors in writing high quality software, I think it's even better to avoid situations where software defects can cause serious damage.  I certainly think that the more mission critical a piece of software is, the smaller and stupider it should be.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-2798891346377792131?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/2798891346377792131/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=2798891346377792131' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/2798891346377792131'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/2798891346377792131'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/07/software-engineering-code-reviews-vs.html' title='Software Engineering: Code Reviews vs. Peer Programming'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-435069292708929192</id><published>2011-07-05T11:35:00.000-07:00</published><updated>2011-07-06T06:52:59.801-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='aws2011'/><category scheme='http://www.blogger.com/atom/ns#' term='aws'/><title type='text'>Amazon Web Services Summit 2011: Navigate the Cloud</title><content type='html'>On June 21, 2011 I went to an Amazon Web Services conference in San Francisco.  These are my notes.&lt;br /&gt;&lt;br /&gt;In general, the conference was informative, but a bit subdued.  It was about a quarter of the size of Google IO, and they weren't giving away anything huge for free.  Furthermore, it was only one day, and the size of the crowd was much smaller.  However, that makes sense since they have multiple of these conferences per year.  Nonetheless, it was interesting, and I learned some stuff.&lt;h4&gt;Keynote&lt;/h4&gt;The part of the keynote was given by Dr. Werner Vogels, the CTO.&lt;br /&gt;&lt;br /&gt;AWS doesn't lock you into an OS or language.&lt;br /&gt;&lt;br /&gt;There are 339 billion objects in S3, and S3 handles 200,000+ storage transactions per second.&lt;br /&gt;&lt;br /&gt;On a daily basis, they add as much capacity as they had in total in&lt;br /&gt;2000.&lt;br /&gt;&lt;br /&gt;Spot pricing allows you to spin up an instance when the price is right.&lt;br /&gt;&lt;br /&gt;AWS CloudFormation deploys a stack of software.&lt;br /&gt;&lt;br /&gt;AWS Elastic Beanstalk is easy to begin and impossible to outgrow.&lt;br /&gt;&lt;br /&gt;CloudFormation and Elastic Beanstalk don't cost anything extra.&lt;br /&gt;&lt;br /&gt;They now have Simple Email Service.&lt;br /&gt;&lt;br /&gt;They're building features by working from the customer backwards.  They start by writing the press release, then the FAQ, next the documentation, and then finally the code.&lt;br /&gt;&lt;br /&gt;They mentioned that Amazon RDS (Relational Database Service) is not as scalable as S3.&lt;br /&gt;&lt;br /&gt;AWS is now a PCI level 1 service provider.&lt;br /&gt;&lt;br /&gt;amazon.com (i.e. the retail business) is moving to AWS.&lt;br /&gt;&lt;br /&gt;They mentioned Amazon VPC (Virtual Private Cloud).&lt;h5&gt;Autodesk&lt;/h5&gt;There have been more than 2 million downloads of Sketchbook Mobile.&lt;br /&gt;&lt;br /&gt;Homestyler is the fast, easy, free way to design your dream home.&lt;br /&gt;&lt;br /&gt;Autodesk uses EC2, S3, EBS, and CloudFront.&lt;br /&gt;&lt;br /&gt;Homestyler can now do photo realistic renderings of your home.&lt;br /&gt;&lt;br /&gt;They mentioned project Nitrous.  It provides a rich, in-browser experience.  It's like Google Docs but for your 3D modeling documents.&lt;br /&gt;&lt;br /&gt;They're doing stuff we're they're rendering in the cloud and then streaming to an iPad.&lt;h5&gt;TellApart&lt;/h5&gt;They handle customer data in real-time (i.e. with 100ms latencies) on AWS.&lt;br /&gt;&lt;br /&gt;They have a primarily ex-Google team.&lt;br /&gt;&lt;br /&gt;They provide customized ads based on user behavior.&lt;br /&gt;&lt;br /&gt;They have some serious latency requirements.  They had to hit 120ms end-to-end.  They worked closely with AWS to meet their needs.&lt;br /&gt;&lt;br /&gt;Their ad performance is incredible.  They have a 7.5% click through rate.&lt;h5&gt;Adobe&lt;/h5&gt;This part of the keynote was given by Mitch Nelson, Director, Managed Systems.&lt;br /&gt;&lt;br /&gt;LiveCycle is a managed service.&lt;br /&gt;&lt;br /&gt;They talked about the Flash Media Server.&lt;br /&gt;&lt;br /&gt;Adobe Connect is a managed service.  It's an online meeting product.  It's based on Flash.&lt;h5&gt;Cloud Computing at NASA&lt;/h5&gt;This part of the keynote was about the JPL (Jet Propulsion Laboratory).&lt;br /&gt;&lt;br /&gt;They wanted to augment availability in multiple geographic regions.&lt;br /&gt;&lt;br /&gt;Some things can be &lt;i&gt;safer&lt;/i&gt; in the cloud.&lt;br /&gt;&lt;br /&gt;They're using the VPC (Virtual Private Cloud).  They use IPSec.&lt;br /&gt;&lt;br /&gt;They've saved a lot of money by adopting cloud computing.&lt;br /&gt;&lt;br /&gt;Buying infrastructure way ahead of time is expensive for NASA.  Cloud computing saves NASA a lot of money by allowing them to scale to meet their needs.&lt;br /&gt;&lt;br /&gt;He talked a lot about the Mars Rover.&lt;br /&gt;&lt;br /&gt;They spin up a bunch of instances to handle bursty traffic, processing data from the Mars Rover, which sends data once a day (I think).&lt;br /&gt;&lt;br /&gt;The speaker said that the "missions" are his "customers".&lt;br /&gt;&lt;br /&gt;These projects are using cloud computing:  Mars Rovers, Deep Space Network, Lunar mapping&lt;br /&gt;product, and airborn missions.  None of them look like they involve astronauts.&lt;h5&gt;SmugMug&lt;/h5&gt;SmugMug is profitable.  They have no debt.  They're a top 250 website.  It's a private company.  They had $10M+ in yearly revenues in '07.&lt;br /&gt;&lt;br /&gt;They provide unlimited storage for storing photos and unlimited bandwidth for uploading and download photos.  They're motto is "More, better, faster" (in comparison to other photo sites).&lt;br /&gt;&lt;br /&gt;They can handle photos up to 48 megapixels in size.&lt;br /&gt;&lt;br /&gt;They can handle 1920x1080p video.&lt;br /&gt;&lt;br /&gt;They use a hybrid of AWS and their own datacenter.  They have 5 datacenters.  They're moving completely to AWS.  They have many petabytes of data stored in S3.&lt;br /&gt;&lt;br /&gt;Amazon really listens to their customers, trying to solve their needs.&lt;br /&gt;&lt;br /&gt;One time, SmugMug's RubberBand project tried to spin up 1920 cores in a single API call.  Amazon spun them all up as requested.  SmugMug renamed that project SkyNet.&lt;br /&gt;&lt;br /&gt;SmugMug lets customers tie their Amazon and SmugMug accounts together so that SmugMug can pass on the cost of storing raw photos and videos onto their customers (since they're so expensive to store).&lt;br /&gt;&lt;br /&gt;SmugMug is designed for breakage, so there was minimal impact during the "Amazonpocalypse".  They made use of multiple availability zones.&lt;br /&gt;&lt;br /&gt;EBS is just like a hard disk.  If you want local redundancy, use RAID.  Want GEO redundancy?  Replicate.  EBS can/does fail just like HDDs.  EBS does solve lots of problems, but use it in the appropriate way.&lt;h5&gt;Customer Panel&lt;/h5&gt;Use Akami for dynamic generation of content, and use CloudFront for more static content.&lt;br /&gt;&lt;br /&gt;Dealing with Akami is a pain in the butt.&lt;br /&gt;&lt;br /&gt;The customers on the panel made it through the Amazonpocolypse fairly well.  However, they had to architect for it.&lt;br /&gt;&lt;br /&gt;The elasticity of Amazon's services is what saves the most money.  Customers only pay for resources when they use them.  The customers on the panel were pretty good about spinning up and spinning down instances.  Also, the customers said that they can't build their own data centers fast enough to meet their own needs.&lt;br /&gt;&lt;br /&gt;TellApart uses Spot instances to run Hadoop jobs.  That means they only spin up instances when the price is low enough.&lt;br /&gt;&lt;br /&gt;Without AWS, a lot of the customers would never have been able to attempt certain projects.&lt;br /&gt;&lt;br /&gt;Akami hasn't broken for SmugMug.  HAProxy also hasn't broken for them.&lt;br /&gt;&lt;br /&gt;AutoDesk is using Scala.&lt;br /&gt;&lt;br /&gt;TellApart uses GAE (Google App Engine) for its own dashboards.&lt;br /&gt;&lt;br /&gt;The NASA guy uses CloudFormation to setup templates for their clusters.&lt;br /&gt;&lt;br /&gt;Amazon often builds infrastructure that their customers have already built so that all the other new customers don't have to rebuild that same infrastructure.&lt;br /&gt;&lt;br /&gt;RDS has very unpredictable latency.  None of the panel customers were using it.&lt;h4&gt;Security and Compliance Overview&lt;/h4&gt;This talk was given by Matt Tavis, Principal Solutions Architect.&lt;br /&gt;&lt;br /&gt;Here's a link to the &lt;a href="http://aws.amazon.com/security/"&gt;AWS Security Center&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Security on AWS is based on a shared responsibility model.&lt;br /&gt;&lt;br /&gt;AWS is responsible for facilities, physical security, network infrastructure, etc.&lt;br /&gt;&lt;br /&gt;The customer is responsible for the OS, application, security groups, firewalls, network configuration, and accounts.&lt;br /&gt;&lt;br /&gt;AWS has complex features for who has access to interact with which AWS APIs if you have multiple people at the same company.&lt;br /&gt;&lt;br /&gt;AWS favors replication over backup.&lt;br /&gt;&lt;br /&gt;I wonder if you can mess around with EC2 or EBS and try to read the raw disk to see if there is stuff left over from a previous user.&lt;br /&gt;&lt;br /&gt;By default, there's a mandatory inbound firewall, which defaults to deny.&lt;br /&gt;&lt;br /&gt;VPC is a networking feature.&lt;h4&gt;High Availability in the Cloud: Architectural Best Practices&lt;/h4&gt;This talk was by Josh Fraser, the VP of business development at RightScale.&lt;br /&gt;&lt;br /&gt;RightScale is the world's #1 cloud management system.&lt;br /&gt;&lt;br /&gt;RightScale serves 40,000 users and 2.5 million servers.&lt;br /&gt;&lt;br /&gt;RightScale is a SaaS.&lt;br /&gt;&lt;br /&gt;RightScale is the layer between the app and the cloud provider.&lt;br /&gt;&lt;br /&gt;You need to design for failure.&lt;br /&gt;&lt;br /&gt;Backup and replication need to be taken seriously.&lt;br /&gt;&lt;br /&gt;A cloud is a physical datacenter entity behind an API endpoint.&lt;br /&gt;&lt;br /&gt;He says that Amazon has 5 clouds.  AWS is a cloud provider.&lt;br /&gt;&lt;br /&gt;RightScale has ServerTemplates (which act like recipes).  They don't like cloud images (because they're too fixed).&lt;br /&gt;&lt;br /&gt;They have an integrated approach that puts together all the parts needed to build a single server or set of servers.&lt;br /&gt;&lt;br /&gt;ServerTemplates are a server's DNA.&lt;br /&gt;&lt;br /&gt;DR is disaster recovery.  HA is high availability.&lt;br /&gt;&lt;br /&gt;Implementing HA best practices is always about balancing cost, complexity, and risk.&lt;br /&gt;&lt;br /&gt;You need to automate your infrastructure.&lt;br /&gt;&lt;br /&gt;Always place at least one of each component (load balancers, app servers, databases) in at least two AZs (autonomous zones?).&lt;br /&gt;&lt;br /&gt;You need alerting and monitoring.&lt;br /&gt;&lt;br /&gt;Use stateless apps.&lt;br /&gt;&lt;br /&gt;It's critical to be able to programmatically switch DNS.&lt;br /&gt;&lt;br /&gt;Use HAProxy, Zeus, etc. for load balancing.&lt;br /&gt;&lt;br /&gt;Scale up and down conservatively.  Don't bring up 1000 instances because of 1 minute's demand.&lt;br /&gt;&lt;br /&gt;Snapshot your EBS volumes.&lt;br /&gt;&lt;br /&gt;Consider a NoSQL solution.&lt;br /&gt;&lt;br /&gt;They use Cassandra in multiple regions, and it works well for them.&lt;br /&gt;&lt;br /&gt;EBS snapshots can't cross regions.&lt;br /&gt;&lt;br /&gt;There is no one size fits all solution.&lt;br /&gt;&lt;br /&gt;The most common approaches they see involve multi-AZ configurations with a solid DR plan.&lt;h4&gt;Amazon.com's Journey to the Cloud&lt;/h4&gt;Retail is everything at Amazon that isn't AWS.  Retail is a customer of AWS.&lt;br /&gt;&lt;br /&gt;This talk summarizes the history of Amazon retail from 1995 to 2011.  I think the switch to AWS was in 2011.&lt;br /&gt;&lt;br /&gt;They initially ran on a single box.  They housed the server in their main corporate offices.  There was a "water event" that caused them to move to a datacenter.&lt;br /&gt;&lt;br /&gt;In 2001, they started switching from Digital Tru64 to Linux.&lt;br /&gt;&lt;br /&gt;In 2004, they had 50 million lines of C++ code.&lt;br /&gt;&lt;br /&gt;AWS started in 2006.  S3 came first, and then came EC2.  AWS actually wasn't built to solve problems for the retail side.  It was always built to be general purpose, and the retail side was responsible for figuring out how to use it.&lt;br /&gt;&lt;br /&gt;IMDb is an Amazon owned subsidiary.  It's very separate of the rest of Amazon.&lt;br /&gt;&lt;br /&gt;Amazon has strict runtime latency and scale requirements.  If your widget can't meet them, your widget won't get shown.&lt;br /&gt;&lt;br /&gt;Retail uses VPC.  VPC makes AWS look just like your own datacenter.&lt;br /&gt;&lt;br /&gt;November 10, 2010 is when they turned off the last frontend server not using AWS.  All traffic for amazon.com is now served from AWS.&lt;br /&gt;&lt;br /&gt;They've had billions of orders over the lifetime of amazon.com.&lt;br /&gt;&lt;br /&gt;Amazon has taken really old orders out of the database and moved them into S3.&lt;br /&gt;&lt;br /&gt;They use Oracle.&lt;br /&gt;&lt;br /&gt;Be sure to consider compliance issues.&lt;br /&gt;&lt;br /&gt;When moving to the cloud, start with simple applications (or simple parts of larger applications).&lt;br /&gt;&lt;br /&gt;Iterate toward your desired end-state.&lt;br /&gt;&lt;br /&gt;The cloud can't cover up sloppy engineering.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-435069292708929192?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/435069292708929192/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=435069292708929192' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/435069292708929192'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/435069292708929192'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/07/amazon-web-services-summit-2011.html' title='Amazon Web Services Summit 2011: Navigate the Cloud'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-6034276246968402569</id><published>2011-07-05T11:22:00.000-07:00</published><updated>2011-07-05T11:27:16.813-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='video'/><category scheme='http://www.blogger.com/atom/ns#' term='YouTube'/><category scheme='http://www.blogger.com/atom/ns#' term='animation'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Introducing YouTube.com/create</title><content type='html'>I just wrote my first blog post for the YouTube API blog:  &lt;a href="http://apiblog.youtube.com/2011/07/introducing-youtubecomcreate.html"&gt;Introducing YouTube.com/create&lt;/a&gt;:&lt;blockquote&gt;YouTube.com/create is a platform for third-party applications that enable users to create videos. The idea is simple. The third-party application runs in an HTML iframe on YouTube. The user creates a video with the application, and then the application uploads the video to YouTube for the user to watch and share.&lt;/blockquote&gt;&lt;iframe width="560" height="349" src="http://www.youtube.com/embed/m-t4pcO99gI" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;To read more, check out the story on the &lt;a href="http://apiblog.youtube.com/2011/07/introducing-youtubecomcreate.html"&gt;YouTube API blog&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-6034276246968402569?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/6034276246968402569/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=6034276246968402569' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/6034276246968402569'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/6034276246968402569'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/07/introducing-youtubecomcreate.html' title='Introducing YouTube.com/create'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://img.youtube.com/vi/m-t4pcO99gI/default.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-4524717610198460887</id><published>2011-06-29T11:37:00.000-07:00</published><updated>2011-06-29T11:43:45.640-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Books: Python 3 Web Development Beginner’s Guide</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/-sxVXGLOmesQ/TgtxPLecPLI/AAAAAAAAAJQ/KmsBLmuXzSQ/s1600/Python%2B3%2BWeb%2BDevelopment%2BBeginner%25E2%2580%2599s%2BGuide.jpg"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 125px; height: 152px;" src="http://2.bp.blogspot.com/-sxVXGLOmesQ/TgtxPLecPLI/AAAAAAAAAJQ/KmsBLmuXzSQ/s320/Python%2B3%2BWeb%2BDevelopment%2BBeginner%25E2%2580%2599s%2BGuide.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5623713065294642354" /&gt;&lt;/a&gt;&lt;br /&gt;I've been asked by Packt to review a new book called &lt;a href="http://www.packtpub.com/python-3-web-development-beginners-guide/book"&gt;Python 3 Web Development Beginner’s Guide&lt;/a&gt;.  Here's the overview:&lt;ul&gt;&lt;li&gt;Build your own Python web applications from scratch&lt;/li&gt;&lt;li&gt;Follow the examples to create a number of different Python-based web applications, including a task list, book database, and wiki application&lt;/li&gt;&lt;li&gt;Have the freedom to make your site your own without having to learn another framework&lt;/li&gt;&lt;/ul&gt;I looked through the table of contents, and I scanned the first 30 page.  It looks like a fun book!  In fact, it's exactly the sort of book I was hoping someone would write.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-4524717610198460887?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/4524717610198460887/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=4524717610198460887' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/4524717610198460887'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/4524717610198460887'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/06/books-python-3-web-development.html' title='Books: Python 3 Web Development Beginner’s Guide'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-sxVXGLOmesQ/TgtxPLecPLI/AAAAAAAAAJQ/KmsBLmuXzSQ/s72-c/Python%2B3%2BWeb%2BDevelopment%2BBeginner%25E2%2580%2599s%2BGuide.jpg' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-8584547890413748802</id><published>2011-06-22T09:57:00.000-07:00</published><updated>2011-06-22T10:04:31.313-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><category scheme='http://www.blogger.com/atom/ns#' term='gae'/><title type='text'>Python: Increasing the Timeouts for urlfetch in Google App Engine</title><content type='html'>Google App Engine provides a function, google.appengine.api.urlfetch.fetch, for fetching URLs.  I do believe all the other HTTP client libraries are monkey patched to make use of that function, which is written to take advantage of various Google infrastructure.  The fetch function has a default timeout of 5 seconds.  You can set a higher timeout by passing a deadline parameter, but the maximum is 10 seconds.  Unfortunately, passing a deadline keyword parameter is often difficult if it's a third-party library that is making the call to fetch, for instance if you're using the GData client library.&lt;br /&gt;&lt;br /&gt;I looked for a way to set the deadline parameter in a more global way, but I couldn't fine one by mere inspection of the code.  I came up with the following HACK in order to work around this problem:&lt;pre&gt;# HACK: Monkeypatch google.appengine.api.urlfetch.fetch to increase the&lt;br /&gt;# deadline.  This is used by the various client libraries.&lt;br /&gt;def _fetch(*args, **kargs):&lt;br /&gt;  from google.appengine.api.urlfetch import _orig_fetch  # Import late.&lt;br /&gt;  kargs["deadline"] = 10&lt;br /&gt;  return _orig_fetch(*args, **kargs)&lt;br /&gt;_fetch.this_is_the_wrapper = True&lt;br /&gt;&lt;br /&gt;# HACK: Because of the way the dev app server works when reloading code,&lt;br /&gt;# things are a little tricky here.&lt;br /&gt;from google.appengine.api import urlfetch&lt;br /&gt;if not hasattr(urlfetch.fetch, "this_is_the_wrapper"):&lt;br /&gt;  urlfetch._orig_fetch = urlfetch.fetch&lt;br /&gt;  urlfetch.fetch = _fetch&lt;br /&gt;else:&lt;br /&gt;  assert hasattr(urlfetch, "_orig_fetch")&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-8584547890413748802?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/8584547890413748802/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=8584547890413748802' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/8584547890413748802'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/8584547890413748802'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/06/python-increasing-timeouts-for-urlfetch.html' title='Python: Increasing the Timeouts for urlfetch in Google App Engine'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-2779207571754776047</id><published>2011-06-09T12:45:00.000-07:00</published><updated>2011-06-09T12:48:40.359-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='html5'/><category scheme='http://www.blogger.com/atom/ns#' term='io2011'/><category scheme='http://www.blogger.com/atom/ns#' term='google'/><title type='text'>Google I/O 2011</title><content type='html'>I went to Google I/O a few weeks ago.  Although I took notes, rather than blogging all my notes, I thought I'd just link to my favorite two talks:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.youtube.com/watch?v=q-7l8cnpI4k"&gt;Programming Well with Others: Social Skills for Geeks&lt;/a&gt;&lt;blockquote&gt;Are languages, compilers, debuggers, and algorithms all you need to be a successful software engineer? In a perfect world, those who produce the best code should be the most successful. Unfortunately, we live in a world of imperfect people, and collaborating with others is at least as important as having great technical skills.&lt;/blockquote&gt;&lt;a href="http://www.youtube.com/watch?v=WlwY6_W4VG8"&gt;HTML5 Showcase for Web Developers: The Wow and the How&lt;/a&gt;&lt;blockquote&gt;We'll share the strengths and extents of HTML5, showing magnificent demos of bleeding-edge features in Google Chrome. Digging into high-fidelity graphics, performance, and system integration, we'll break each demo down on the big screen to show how it was constructed. Then we'll show you how to use Chrome to its full potential in your own projects.&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-2779207571754776047?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/2779207571754776047/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=2779207571754776047' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/2779207571754776047'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/2779207571754776047'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/06/google-io-2011.html' title='Google I/O 2011'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-5618887376328651559</id><published>2011-06-03T10:06:00.001-07:00</published><updated>2011-06-03T15:20:04.263-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Python: Getting a Fair Flip out of an Unfair Coin</title><content type='html'>If you have an unfair coin (i.e. one that favors heads or tails), how do generate a fair flip (i.e. one that doesn't favor heads or tails)?  My buddy Hy Carrinski and I came up with the following algorithm:&lt;pre&gt;"""Get a fair flip out of an unfair coin."""&lt;br /&gt;&lt;br /&gt;from collections import defaultdict&lt;br /&gt;from random import random&lt;br /&gt;&lt;br /&gt;FLIP_RATIO = 0.7&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;def flip_unfair_coin():&lt;br /&gt;    """Flip an unfair coin.  Return a boolean."""&lt;br /&gt;    return random() &amp;gt; FLIP_RATIO&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;def create_fair_flip():&lt;br /&gt;    """Generate a fair flip.  Return a boolean."""&lt;br /&gt;    while True:&lt;br /&gt;        flip = flip_unfair_coin()&lt;br /&gt;        if flip != flip_unfair_coin():&lt;br /&gt;            return flip&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# Demonstrate that it works.&lt;br /&gt;&lt;br /&gt;if __name__ == '__main__':&lt;br /&gt;    results = defaultdict(int)&lt;br /&gt;    for i in xrange(1000000):&lt;br /&gt;        results[create_fair_flip()] += 1&lt;br /&gt;    percentage = (results[False] / float(results[False] + results[True])) * 100&lt;br /&gt;    print "Percentage heads:", percentage&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-5618887376328651559?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/5618887376328651559/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=5618887376328651559' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/5618887376328651559'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/5618887376328651559'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/06/python-getting-fair-flip-out-of-unfair.html' title='Python: Getting a Fair Flip out of an Unfair Coin'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-558022533187676706</id><published>2011-05-28T20:55:00.001-07:00</published><updated>2011-05-28T21:00:57.806-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='personal'/><title type='text'>Personal: I Work at YouTube!</title><content type='html'>I got a job at Google working as a developer advocate for YouTube APIs.  It's nice because it's a bit more of a social position than I usually have, but it still involves some work on open source code.  Unfortunately, my desk is in Mountain View, so my commute is three hours a day; I probably won't be able to keep that up indefinitely.  Anyway, I'm still very excited :)&lt;br /&gt;&lt;br /&gt;I started three weeks ago, and I must admit that Google is about twice as awesome as I was even hoping for :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-558022533187676706?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/558022533187676706/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=558022533187676706' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/558022533187676706'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/558022533187676706'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/05/personal-i-work-at-youtube.html' title='Personal: I Work at YouTube!'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-7665531472805844245</id><published>2011-05-17T17:32:00.000-07:00</published><updated>2011-05-17T17:39:27.102-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='personal'/><category scheme='http://www.blogger.com/atom/ns#' term='computer science'/><category scheme='http://www.blogger.com/atom/ns#' term='computer history'/><title type='text'>Personal: Name Dropping</title><content type='html'>Ken Thompson (initial author of Unix) was awarded the Japan Prize today at Google.  I just got to meet and talk to him, Vint Cerf (one of the "fathers of the Internet"), and Rob Pike (legendary Unix hacker, famous author, and co-author of Go).  Since I shook each of their hands, I've decided to never wash my hand again ;)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-7665531472805844245?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/7665531472805844245/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=7665531472805844245' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/7665531472805844245'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/7665531472805844245'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/05/personal-name-dropping.html' title='Personal: Name Dropping'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-8200910441782984173</id><published>2011-04-19T14:59:00.000-07:00</published><updated>2011-04-20T19:35:40.438-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='C'/><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pascal'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='lua'/><category scheme='http://www.blogger.com/atom/ns#' term='scala'/><title type='text'>Call Me Crazy: Calling Conventions</title><content type='html'>It's been said that the most significant contribution to computer science was the invention of the subroutine.  Every major programming language has its own variation on how subroutines work.  For instance, there are procedures, functions, methods, coroutines, etc.  When implementing a compiled language or a virtual machine, you must decide how to implement subroutines at the assembly language level.  For instance, you must decide how to pass arguments, what goes on the stack, what goes on the heap, what goes in registers, what goes in statically allocated memory, etc.  These conventions are called calling conventions.  You might not think of calling conventions very often, but they're an interesting topic.&lt;br /&gt;&lt;br /&gt;First of all, what does a subroutine provide over a simple goto?  A subroutine provides a way to pass arguments, execute a chunk of code, and return to where you were before you called the subroutine.  You can implement each of these yourself in assembly, but standardizing calling conventions in a language makes the process more stable, repeatable, and less prone to error.&lt;br /&gt;&lt;br /&gt;The way you implement calling conventions in a language like C is at least conceptually something like the following.  You push all your arguments onto the stack; you push your current location in the code (i.e. the IP register) onto the stack; and then you jump to the subroutine.  The subroutine executes its code, puts its return value in a register, and then jumps to the return-to address you put on the stack.  The caller is responsible for popping the arguments off the stack (this is necessary to support varargs).  Often, the compiler may optimize the code by putting values into registers rather than on the stack.&lt;br /&gt;&lt;br /&gt;What's the difference between subroutines, procedures, functions, and methods?  I think of subroutines as a low-level, catch-all term.  In Pascal as well as some other languages, a procedure is expected to have side effects but not return a value, whereas a function is not supposed to have side effects but is supposed to return a value.  Of course, not having side effects is a convention, not something enforced by the compiler.  A method is a function call on an object.  Typically, the object is implicitly passed as the first argument, although that may not be explicit in the syntax for the language.&lt;br /&gt;&lt;br /&gt;Coroutines are a twist on subroutines.  When you call a subroutine, it'll eventually return to the caller.  If A calls B, then eventually B will return to A.  Coroutines don't have to behave in this manner--i.e. they don't have to return to the caller.  A may call B which may call C which may call A in a circular manner, without maintaining a stack of return-to addresses.&lt;br /&gt;&lt;br /&gt;Recursion is a technique in which a function can call itself.  Some languages like Lisp provide recursion as a low-level operation, and all looping constructs are built on top of recursion.  There are some algorithms that are more elegantly implemented using recursion than looping such as the &lt;a href="http://en.wikipedia.org/wiki/Ackermann_function"&gt;Ackermann function&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Recursion implies that you have some sort of stack (whether or not you use a C-level stack).  That's because if a function takes one argument and recurses five times, you need a places for five arguments.  Languages like COBOL put arguments in statically allocated memory instead of on a stack.  Hence, COBOL lacks recursion.&lt;br /&gt;&lt;br /&gt;Tail call optimization is an interesting technique that reduces the use of the stack.  If function A calls function B, and then B's last step is to call function C, rather than having a return-to address for A and a return-to address for B, you can just have C return directly to A since there's nothing else in function B that needs to be done.  &lt;br /&gt;&lt;br /&gt;This works really well when a function calls itself as its last step.  If F calls itself as its last step, rather than having a large stack of return-to addresses for F, you can optimize it away.  In this way, what would normally be a recursive algorithm can be translated into simple interation.  This is a critical technique in languages like Lisp that rely on recursion for looping; however, it's not present in languages like Python.  (Guido van Rossum has argued that it's harder to debug bugs if parts of the stack have been optimized away.  It's possible to end up in a function with no way of knowing how you got there.)&lt;br /&gt;&lt;br /&gt;Stacklessness is a technique whereby the language's stack does not rely on an assembly or C-level stack, but rather exists on the heap.  Because of the way C stacks require contiguous space, it's difficult to allocate space for 1000s of them at the same time.  In a stackless interpreter, because stack frames are allocated dynamically from the heap, you can allocate a greater number of them stacks.  This technique is used by stackless interpreters such as Lua as well as the greenlets library in Python in order to implement something like lightweight multithreading.&lt;br /&gt;&lt;br /&gt;Some languages  provide more flexibility than others in the way arguments are passed.  C has the ability to pass a fixed number of arguments, but there's also a facility to pass an arbitrary number of arguments call varargs.  Some languages like Perl and Ruby allow you to pass additional keyword arguments in the form of a hash to the function (e.g. "f(1, 2, :arg =&amp;gt; 1)").  Some languages like Common Lisp and Python have advanced support for keyword arguments allowing you to call a method that looks like "def f(a, b, c=1, d=2, e=3)" like "f('a', 'b', e=3, d=3)".  Notice that you can leave out or rearrange the order of keyword arguments.  Python allows you to pass a variable number of positional arguments as well as a variable number of keyword arguments and it will even raise an exception if you pass a keyword argument that isn't expected.  Common Lisp was one of the first languages to have extremely sophisticated parameter passing, including sophisticated support for keyword arguments.&lt;br /&gt;&lt;br /&gt;Currying is a technique whereby if a function accepts two parameters, A and B, then what you really have is a function that takes an argument A and then returns another function that takes an argument B.  In this way, all functions can be reduced to a list of functions that accept 0 or 1 arguments.  When a language supports currying, you can pass a value for A and then later pass a value for B.  Haskell supports currying.&lt;br /&gt;&lt;br /&gt;Partial application is a related technique that lets you pass an arbitrary number of arguments, perhaps in a random order if keyword arguments are supported, and then actually call the function later.  If you have a function that takes one parameter, then you can partially apply it to one argument, and you are left with a function that takes no parameters that you can call at a later time.  Partial application is more general than currying.  It's supported in Python.  However, in languages like Python, Haskell, Lisp, etc., it's always possible to create a new function that takes no parameters that simply executes another function passing a particular value to that function.  Hence, it's trivial to implement partial application manually.&lt;br /&gt;&lt;br /&gt;Lazy evaluation is a technique whereby the arguments to a function are not evaluated until they are used within the function.  If you call f(1/0) in Python, you will always get a DivisionByZero exception.  However, in languages such as Haskell, if you call f (1/0), as long as f never evaluates its argument, you'll won't get an error.  Languages that do not have lazy evaluation are said to be "strict".  Hence, Haskell is not strict.&lt;br /&gt;&lt;br /&gt;It is an error prone situation to have a language that is not strict (i.e. it supports lazy evaluation) but also supports side effects.  For instance, consider writing the expression f(print(1), print(2) in a language that wasn't strict but permitted side effects.  Depending on whether or not f evaluated its parameters, 1 and 2 may or may not be printed, and you couldn't be sure which order they would be printed in.  Hence, functions in Haskell are both lazy and side-effect free.&lt;br /&gt;&lt;br /&gt;Inlining is an optimization technique that helps avoid the overhead of a function call.  If a function A calls a very small function B, an optimizing compiler might take a copy of the compiled code for B and put it "inline" in the compiled code for A.  This reduces the function call overhead but results in larger binary sizes.&lt;br /&gt;&lt;br /&gt;In some situations, some compilers can aggressively inline all functions.  Alternatively, the compiler may be able to put all the code for all the functions side by side and simply jump between the different parts of the code without using the stack.  Some programmers use this technique manually in order to write small, but highly efficient programs.  In general, the way you think calling conventions work in a language may be a simplified, idealistic way of how they actually work.  In the case of inlining, the function call completely disappears.&lt;br /&gt;&lt;br /&gt;Continuation passing style is a technique whereby all function calls can be transformed in a way that makes tail call optimization possible.  Consider the following function:&lt;pre&gt;def sum(n):&lt;br /&gt;    if n == 0:&lt;br /&gt;        return 0&lt;br /&gt;    else:&lt;br /&gt;        return n + sum(n - 1)&lt;/pre&gt;This function cannot natively take advantage of tail call optimization because after it recurses, the return value must be added to n.  It's easy to change this to a function that takes advantage of tail call optimization manually:&lt;pre&gt;def sum(n, _accum=0):&lt;br /&gt;    if n == 0:&lt;br /&gt;        return _accum&lt;br /&gt;    else:&lt;br /&gt;        return sum(n - 1, _accum + n)&lt;/pre&gt;In this case, an accumulator is passed explicitly.  (Remember, however, that even though I'm using Python syntax, Python does not support tail call optimization.)&lt;br /&gt;&lt;br /&gt;Rather than passing an accumulator that contains a value, you can pass an accumulator that contains what other code needs to execute after the recursive call.  Here is an example of that:&lt;pre&gt;def sum(n, _callback=lambda x: x):&lt;br /&gt;    if n == 0:&lt;br /&gt;        return _callback(0)&lt;br /&gt;    else:&lt;br /&gt;        return sum(n - 1, lambda x: _callback(x + n))&lt;/pre&gt;By the way, if you find that code difficult to understand, don't feel bad!  It took me more than half an hour to write it, despite the fact that I had seen the technique before!  That goes to show you that switching to continuation passing style isn't easy.  However, it can be accomplished via a mechanical (i.e. "just follow the steps") process, and there are languages that can do it for you automatically.  Scala has a compiler flag that turns on continuation passing style so that the JVM behaves differently (conceptually it uses a lot of anonymous functions that exist on the heap rather than return-to addresses that exist on the stack).&lt;br /&gt;&lt;br /&gt;Returning to C, you might think that the calling conventions of C were decided upon years ago, and the only variations would have to do with compiler optimizations.  Surprisingly, that is not the case.  For instance, AMD helped define the new calling conventions for System V on AMD64.  In fact, the calling convention are neither C nor AMD64 dependent. It's entirely defined by the operating systems application binary interface standard.  Hence, the calling conventions for AMD64 systems are slightly different under Windows (specifically in relation to what registers are used to pass variables).  See &lt;a href="http://www.x86-64.org/documentation"&gt;here&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/X86_calling_conventions#x86-64_Calling_Conventions"&gt;here&lt;/a&gt; for more information.&lt;br /&gt;&lt;br /&gt;So if you're thinking about implementing a new language, and you wonder what's in a subroutine, you make the call!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-8200910441782984173?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/8200910441782984173/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=8200910441782984173' title='19 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/8200910441782984173'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/8200910441782984173'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/call-me-crazy-calling-conventions.html' title='Call Me Crazy: Calling Conventions'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>19</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-1401679628860942297</id><published>2011-04-19T14:41:00.000-07:00</published><updated>2011-04-19T14:50:47.742-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='palm'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><title type='text'>Perl: Text-based Address Books</title><content type='html'>Over the years, I've stored my addresses in a variety of formats.  I used to have a Palm Pilot.  These days, I have an Android phone, and Google keeps track of my addresses.  However, I've always had a second copy of my addresses in a text file.  The format looks something like this:&lt;pre&gt;A &amp; R Stock to Performance:&lt;br /&gt;  Address: 2849 Willow Pass Rd. #A, Concord, CA  94519&lt;br /&gt;  Work Phone: (925) 689-1846&lt;/pre&gt;There are many advantages to using a text-based format.  For instance, since I'm an expert Vim users, I can search and edit the file extremely quickly.  Best of all, the format works on every operating system and never goes obsolete.  The only downside is that I have to input the address twice:  once for Google and once for my own notes.&lt;br /&gt;&lt;br /&gt;Long ago, I wrote a Perl script to convert my notes file into a Palm Pilot database.  Even though I don't have a Palm Pilot anymore, I keep the script around.  I can easily alter it to output the addresses in different formats.&lt;br /&gt;&lt;br /&gt;If you like the format I used above, here's the source code so that you can hack it to do whatever you want:&lt;pre&gt;#!/usr/bin/perl -w&lt;br /&gt;&lt;br /&gt;#&lt;br /&gt;# Author: Shannon -jj Behrens&lt;br /&gt;# Email: jjinux@gmail.com&lt;br /&gt;#&lt;br /&gt;# This program converts the addresses stored in my notes file into a Palm&lt;br /&gt;# database file (a pdb), which I can then import into my Handspring Visor.  &lt;br /&gt;# See usage for more details.&lt;br /&gt;#&lt;br /&gt;# Global variables: &lt;br /&gt;# $_0 - This is $0, but without the path.&lt;br /&gt;# $pdb:: - This is a handle to the Palm address database being edited.&lt;br /&gt;#&lt;br /&gt;&lt;br /&gt;# %phone_mappings - Mappings from things such as "Cell" to integers.  These &lt;br /&gt;#   values were deduced from the @Palm::Address::phoneLabels array as well&lt;br /&gt;#   as usage in my notes.txt file.&lt;br /&gt;%phone_mappings:: = (&lt;br /&gt;    Work =&amp;gt; 0,&lt;br /&gt;    Home =&amp;gt; 1,&lt;br /&gt;    Fax =&amp;gt; 2,&lt;br /&gt;    Other =&amp;gt; 3,&lt;br /&gt;    Email =&amp;gt; 4,&lt;br /&gt;    Cell =&amp;gt; 7&lt;br /&gt;);&lt;br /&gt;&lt;br /&gt;# $MAX_PHONES - The maximum amount of "Phone" type fields.&lt;br /&gt;$MAX_PHONES:: = 8;&lt;br /&gt;&lt;br /&gt;use strict;&lt;br /&gt;use Palm::PDB;&lt;br /&gt;use Palm::StdAppInfo;&lt;br /&gt;use Palm::Address;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# Output usage information to the user and exit with value 64 (see man &lt;br /&gt;# sysexits on a FreeBSD machine).&lt;br /&gt;#&lt;br /&gt;# By the way, here's how I use this program:&lt;br /&gt;#&lt;br /&gt;# mkdir palm&lt;br /&gt;# pilot-xfer -b palm&lt;br /&gt;# ./notes_to_palm ~/notes.txt palm/AddressDB.pdb&lt;br /&gt;# pilot-xfer -r palm&lt;br /&gt;# rm -r palm&lt;br /&gt;sub usage {&lt;br /&gt;    print "usage: $_0:: notes.txt AddressDB.pdb\n";&lt;br /&gt;    exit 64;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# There's an ugly "bug" that causes Perl 5.6 to complain about unamed&lt;br /&gt;# categories.  Hence, I have to do a little bit of initializing or else I get a&lt;br /&gt;# whole bunch of uninitialized warnings.  &lt;br /&gt;sub init_category_names {&lt;br /&gt;    for (my $i=0; $i &amp;lt; Palm::StdAppInfo::numCategories; $i++) {&lt;br /&gt;        if (!defined($pdb::-&amp;gt;{appinfo}{categories}[$i]{name})) {&lt;br /&gt;            $pdb::-&amp;gt;{appinfo}{categories}[$i]{name} = "";&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    $pdb::-&amp;gt;{appinfo}{dirtyFields} = 1;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# Return a new record.  I have to do a little bit of initializing or else I get&lt;br /&gt;# a whole bunch of uninitialized warnings.  I wonder why the libraries don't do&lt;br /&gt;# this automatically since they initialize everything to undef anyway.  Also,&lt;br /&gt;# I'll insert a phoneIndex variable into the $record so that I can use the&lt;br /&gt;# phone slots on a first come first serve basis.&lt;br /&gt;sub new_record {&lt;br /&gt;    my $record = $pdb::-&amp;gt;new_Record;&lt;br /&gt;    foreach my $field (qw( name firstName company phone1 phone2 phone3&lt;br /&gt;                           phone4 phone5 phone6 phone7 phone8 address&lt;br /&gt;                           city state zipCode country title custom1&lt;br /&gt;                           custom2 custom3 custom4 note )) {&lt;br /&gt;        $record-&amp;gt;{fields}{$field} = "";&lt;br /&gt;    }&lt;br /&gt;    $record-&amp;gt;{phoneLabel}{reserved} = 0;&lt;br /&gt;    $record-&amp;gt;{fields}{phoneIndex} = 1;&lt;br /&gt;    return $record;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# Process a name line, such as "Shannon -jj Behrens (author)".&lt;br /&gt;sub handle_name {&lt;br /&gt;    my ($fullname) = @_;&lt;br /&gt;&lt;br /&gt;    # Check for note.&lt;br /&gt;    if ($fullname =~ /^(.*)\((.*)\)/) {&lt;br /&gt;        $fullname = $1;&lt;br /&gt;        $record::-&amp;gt;{fields}{note} = $2;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    # Assume last word is last name, everything else is first name.&lt;br /&gt;    my @name = split / /, $fullname;&lt;br /&gt;    $record::-&amp;gt;{fields}{name} = pop @name;&lt;br /&gt;    $record::-&amp;gt;{fields}{firstName} = join " ", @name;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# Process a phone type (e.g. Cell) and a value (e.g. "(925) 209-6439").  Please&lt;br /&gt;# read p5-palm's Address.pm module to see the interesting way Palm phone&lt;br /&gt;# numbers work.  When possible, use the email address as the primary "Phone"&lt;br /&gt;# field.&lt;br /&gt;sub handle_phonelike_field {&lt;br /&gt;    my ($type, $value) = @_;&lt;br /&gt;&lt;br /&gt;    my $phone_index = $record::-&amp;gt;{fields}{phoneIndex};&lt;br /&gt;    return if ($phone_index &amp;gt; $MAX_PHONES::);&lt;br /&gt;    my $phone_field = "phone$phone_index";&lt;br /&gt;    $record::-&amp;gt;{fields}{$phone_field} = $value;&lt;br /&gt;    $record::-&amp;gt;{phoneLabel}{$phone_field} = $phone_mappings::{$type};&lt;br /&gt;    $record::-&amp;gt;{phoneLabel}{display} = $phone_index - 1&lt;br /&gt;        if ($type eq "Email");&lt;br /&gt;    $record::-&amp;gt;{fields}{phoneIndex}++;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# Process an address line, such as "125 Gilger Ave., Martinez, CA  94553".&lt;br /&gt;sub handle_address {&lt;br /&gt;    my ($fulladdress) = @_;&lt;br /&gt;&lt;br /&gt;    my @address = split /, /, $fulladdress;&lt;br /&gt;    $record::-&amp;gt;{fields}{address} = $address[0];&lt;br /&gt;    $record::-&amp;gt;{fields}{city} = $address[1];&lt;br /&gt;    my @state_zip = split /  /, $address[2];&lt;br /&gt;    $record::-&amp;gt;{fields}{state} = $state_zip[0];&lt;br /&gt;    $record::-&amp;gt;{fields}{zipCode} = $state_zip[1];&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# Handle one line, $_, from the notes.txt file.  Remember, there must be at &lt;br /&gt;# least one blank line after every address record in the notes.txt file in &lt;br /&gt;# order to flush it to disk.&lt;br /&gt;#&lt;br /&gt;# Creates globals:  $section::, $record::.&lt;br /&gt;sub handle_line {&lt;br /&gt;    # Ignore anything not in the Contacts section.&lt;br /&gt;    if ($_ =~ /-{5,} ([^\-]+) -{5,}/) {&lt;br /&gt;        $section:: = $1;&lt;br /&gt;        return;&lt;br /&gt;    }&lt;br /&gt;    return if (!defined($section::) or&lt;br /&gt;               $section:: ne "Contacts");&lt;br /&gt;&lt;br /&gt;    # A blank line is a signal to flush the current record, if any.  Otherwise,&lt;br /&gt;    # it can just be ignored.&lt;br /&gt;    if ($_ =~ /^[ \t]*$/) {&lt;br /&gt;        if (defined($record::)) {&lt;br /&gt;            $pdb::-&amp;gt;append_Record($record::);&lt;br /&gt;            $record:: = undef;&lt;br /&gt;        }&lt;br /&gt;        return ;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    # If the line doesn't start with a space, this is a new record's name.&lt;br /&gt;    # Start a new record, and then handle the name.&lt;br /&gt;    if ($_ =~ /^([^ ].*):/) {&lt;br /&gt;        my $fullname = $1;&lt;br /&gt;        $record:: = new_record;&lt;br /&gt;        handle_name $fullname;&lt;br /&gt;        return;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    # Check for phone numbers and email addresses.  &lt;br /&gt;    foreach my $type (keys %phone_mappings::) {&lt;br /&gt;        if ((($type eq "Email") and ($_ =~ /  Email: (.*)/)) or &lt;br /&gt;            (($type ne "Email") and ($_ =~ /  $type Phone: (.*)/))) {&lt;br /&gt;            handle_phonelike_field $type, $1;&lt;br /&gt;            return;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    # Check for address.&lt;br /&gt;    if ($_ =~ /  Address: (.*)/) {&lt;br /&gt;        handle_address $1;&lt;br /&gt;        return;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    # Check for company.&lt;br /&gt;    if ($_ =~ /  Company: (.*)/) {&lt;br /&gt;        $record::-&amp;gt;{fields}{company} = $1;&lt;br /&gt;        return;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    # Anything else can be appended to the additional notes section.&lt;br /&gt;    s/^\s+//;&lt;br /&gt;    $record::-&amp;gt;{fields}{note} .= $_;&lt;br /&gt;    print STDERR "Additional notes: $_";&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;my @pieces = split /\//, $0;&lt;br /&gt;$_0:: = $pieces[$#pieces];&lt;br /&gt;usage if (scalar(@ARGV) != 2);&lt;br /&gt;my $notes = shift;&lt;br /&gt;my $addresses = shift;&lt;br /&gt;open(NOTES, $notes) || die "$_0::: $notes: $!\n";&lt;br /&gt;$pdb:: = new Palm::Address;&lt;br /&gt;init_category_names;&lt;br /&gt;handle_line while (&amp;lt;NOTES&amp;gt;);&lt;br /&gt;$pdb::-&amp;gt;Write($addresses);&lt;br /&gt;close NOTES;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-1401679628860942297?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/1401679628860942297/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=1401679628860942297' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/1401679628860942297'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/1401679628860942297'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/perl-text-based-address-books.html' title='Perl: Text-based Address Books'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-8208512793762715664</id><published>2011-04-19T12:07:00.000-07:00</published><updated>2011-04-19T14:15:30.788-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='neuroscience'/><category scheme='http://www.blogger.com/atom/ns#' term='music'/><category scheme='http://www.blogger.com/atom/ns#' term='jazz'/><category scheme='http://www.blogger.com/atom/ns#' term='math'/><title type='text'>Of Neuroscience and Jazz</title><content type='html'>I am neither a neuroscientist nor a very accomplished musician, but I'd like to talk about the intersection of neuroscience and music.  I have a theory that there is a neuroscience basis for why it takes a mature musical palate to enjoy jazz.&lt;br /&gt;&lt;br /&gt;First, let me say a little something about neuroscience (based on the limited understanding I've gained by watching a bunch of talks).  One of the things your brain is particularly good at is recognizing patterns and predicting patterns.  At the lowest level, if two nerves are close to each other, and they both fire, it's counted as a pattern--i.e. those two things are connected.  Similarly, if a nerve fires and then a short while later it fires again, that's a pattern as well.  Hence, if both of my fingers feel something, there's a pattern, or if I feel a tapping on a single finger, that's a pattern as well.&lt;br /&gt;&lt;br /&gt;However, the brain is not limited to low level patterns.  Rather, it can respond to a hierarchy of patterns.  Paraphrasing &lt;a href="http://en.wikipedia.org/wiki/Ivan_Pavlov"&gt;Pavlov&lt;/a&gt;, if a dog hears a can opener and then smells food and then gets fed, we all know that the dog will soon recognize the pattern and start salivating as soon as he merely hears the can opener.  My  point is that a sophisticated pattern is composed of lower level patterns recursively, all the way down to the level of individual neurons firing.&lt;br /&gt;&lt;br /&gt;Next, let me talk about sine waves and chords.  Let's start with a single note.  A single note might look something like y = sin(x):&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/-pTDakbYYZbk/Ta3gliAtxiI/AAAAAAAAAIU/YFR80PTPpZ8/s1600/y_sin_x.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 313px; height: 135px;" src="http://3.bp.blogspot.com/-pTDakbYYZbk/Ta3gliAtxiI/AAAAAAAAAIU/YFR80PTPpZ8/s320/y_sin_x.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5597376847280981538" /&gt;&lt;/a&gt;This wave is very simple, and somewhat boring.&lt;br /&gt;&lt;br /&gt;(By the way, I'm using &lt;a href="http://www.wolframalpha.com/"&gt;wolframalpha.com&lt;/a&gt; to create these graphs.  WolframAlpha is really interesting.  It's a computational knowledge engine, and graphing things is just one of a ton of things it can do.)&lt;br /&gt;&lt;br /&gt;Now, let's look at a chord consisting of a single note as well as the note that is an octave above it.  Here's y = sin(x) + sin(2 * x):&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/-pirXJDpComg/Ta3hM5VFG_I/AAAAAAAAAIc/fb0wxkMEgXg/s1600/y_equals_sin_x_plus_sin_2x.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 313px; height: 126px;" src="http://4.bp.blogspot.com/-pirXJDpComg/Ta3hM5VFG_I/AAAAAAAAAIc/fb0wxkMEgXg/s320/y_equals_sin_x_plus_sin_2x.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5597377523555310578" /&gt;&lt;/a&gt;This curve is somewhat more sophisticated.  However, you can still recognize the pattern by the time x = 10.&lt;br /&gt;&lt;br /&gt;Now, let's look at what I think is a fifth, which is still a nice sounding chord.  Here's y = sin(x) + sin(1.5 * x):&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/-xSq1VmUjTOY/Ta3h08_8PsI/AAAAAAAAAIk/wdkzawhkHbw/s1600/fifth.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 313px; height: 126px;" src="http://3.bp.blogspot.com/-xSq1VmUjTOY/Ta3h08_8PsI/AAAAAAAAAIk/wdkzawhkHbw/s320/fifth.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5597378211735158466" /&gt;&lt;/a&gt;This pattern is slightly more sophisticated, but it's still pleasingly recognizable.&lt;br /&gt;&lt;br /&gt;Now, let me show you something that is not pleasing to the ear.  This curve represents what might happen if you hit two keys that are right next to each other on the keyboard.  Here's y = sin(x) + sin(1.1 * x):&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/-ZfUn4E66umw/Ta3iVjHJ4NI/AAAAAAAAAIs/QZNl6AFcPg4/s1600/ugly.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 132px;" src="http://3.bp.blogspot.com/-ZfUn4E66umw/Ta3iVjHJ4NI/AAAAAAAAAIs/QZNl6AFcPg4/s320/ugly.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5597378771721773266" /&gt;&lt;/a&gt;This curve pulses in an ugly way.  You have to get all the way out to x = 120 or so to even see the pattern.  In a manner of speaking, the pattern is only there by "brute force".&lt;br /&gt;&lt;br /&gt;So what's my point?  Simple patterns are easy to recognize, and they can be recognized in less time (i.e. for smaller variations of x, which I probably should have called t).&lt;br /&gt;&lt;br /&gt;Here's something that I think is more of a jazz chord.  Here's y = sin(x) + sin(1.25 * x) + sin(1.5 * x) + sin(2 * x):&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/-Oe4DYaE8QjQ/Ta3z64SxEZI/AAAAAAAAAJE/KkdME16h71Q/s1600/jazz2.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 120px;" src="http://4.bp.blogspot.com/-Oe4DYaE8QjQ/Ta3z64SxEZI/AAAAAAAAAJE/KkdME16h71Q/s320/jazz2.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5597398104760455570" /&gt;&lt;/a&gt;This curve is really interesting.  You can recognize what's going on by the time you get to x = 50, but the "texture" of the curve is a lot more interesting.  Whenever I play this sort of chord, it sounds deep, rich, and interesting.  If the first chord reminds me of "Mary had a Little Lamb", this last chord reminds me of the forbidden love between Lancelot and Guinevere and the pain it caused King Aurthur.&lt;br /&gt;&lt;br /&gt;So what's my point?  You have to go higher up the pattern recognition pyramid in order to recognize more sophisticated patterns.  A more sophisticated musical palate is able to recognize patterns that a simpler musical palate may not recognize.  That's why jazz requires a sophisticated palate--it uses chords that require more effort to recognize.&lt;br /&gt;&lt;br /&gt;Of course, there are many dimensions to music, and individual chords are just one dimension.  There are also chord progressions and beats.  The same sort of thing applies to these other dimensions.&lt;br /&gt;&lt;br /&gt;Here's the circle of fifths (thanks Wikipedia!):&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/-_Jdz-SgqbVA/Ta3m8w6wo8I/AAAAAAAAAI8/ojezujENs7E/s1600/circle_of_fifths.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 320px;" src="http://3.bp.blogspot.com/-_Jdz-SgqbVA/Ta3m8w6wo8I/AAAAAAAAAI8/ojezujENs7E/s320/circle_of_fifths.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5597383843489293250" /&gt;&lt;/a&gt;Pick any three chords in a row on the circle of fiths, such as C, G, and D, and you have the basis for a simple, feel-good song.  If you just bounce around between the different chords on a guitar using different strumming patterns, you'll immediately recognize a song you already know or create a new song that sounds pleasing.  Throw in a few 7th chords and a few minor chords, and the song starts taking on additional richness because the pattern becomes less simple.&lt;br /&gt;&lt;br /&gt;If you try to use more than three chords from the circle of fifths, your brain might be left thinking, "Wait a minute.  What key am I supposed to be in?"  It may be more difficult to recognize the pattern.  However, this is exactly the sort of thing that happens in jazz.&lt;br /&gt;&lt;br /&gt;The same thing applies to beats.  Here's a simple walz, "Um pa pa.  Um pa pa."  Here's a typical rap beat, "Bum chbum bum chbumbum ch bum bum ch."   More sophisticated beats require more sophisticated pattern recognition.&lt;br /&gt;&lt;br /&gt;Every song is composed of some pattern repetition and some pattern violation.  Simple, pop songs that are enjoyable by youthful palates involve a lot of pattern repetition and less pattern violation.  Sophisticated music palates enjoy songs with less pattern repetition and more pattern variation.  Furthermore sophisticated music palates get bored of songs more quickly.  They recognize the patterns quickly, and they're ready to move on.&lt;br /&gt;&lt;br /&gt;Furthermore, popular songs are played more often on the radio so that your brain has a better chance to become more familiar with the patterns.  A listener will usually enjoy the song more after listening to it 10 times than if he's only listened to it once.  If a song has so much pattern repetition that you enjoy it the first time, you'll often grow bored of it very quickly.  Conversely, it's very difficult to enjoy sophisticated classical pieces the first time you hear them.  All the popular classical pieces have been driven into our heads since we were kids.&lt;br /&gt;&lt;br /&gt;Why is this?  I think this can be explained by neuroscience as well.  The brain works hard to find patterns, and when it does, the simple recognition of a pattern is somewhat calming.  It's says something like, "Hmm, I've seen that pattern before.  Coooooool."  However, if a pattern is repeated too often, the brain starts to filter it out; it becomes boring.  It says, "Nothing new here.  Pay attention to something else."  Pattern violations catch your attention.  You brain says, "Hey, wait a second.  I didn't expect that!  You should pay attention because something new is happening!"  Hence, composers must always straddle the line between calming pattern repetitions and exciting pattern violations.  How far you go in either direction dictates how sophisticated a musical palate will be required to enjoy the song, and thus, who will enjoy it.&lt;br /&gt;&lt;br /&gt;Ok, so that's my theory of neuroscience and music.  I don't have any MRIs to back it up, nor do I have the educational background to claim real expertise on the subject.  Nonetheless, I think there's some truth to it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-8208512793762715664?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/8208512793762715664/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=8208512793762715664' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/8208512793762715664'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/8208512793762715664'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/of-neuroscience-and-jazz.html' title='Of Neuroscience and Jazz'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-pTDakbYYZbk/Ta3gliAtxiI/AAAAAAAAAIU/YFR80PTPpZ8/s72-c/y_sin_x.gif' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-1955730524426941580</id><published>2011-04-19T11:34:00.000-07:00</published><updated>2011-04-19T12:01:07.599-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='video games'/><title type='text'>Gaming: How Do Characters Know What to Say?</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/-gSjS8hSTIvs/Ta3V5PpM23I/AAAAAAAAAIM/eap4VrkciZk/s1600/papermario_screen012.jpg"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 320px; height: 212px;" src="http://4.bp.blogspot.com/-gSjS8hSTIvs/Ta3V5PpM23I/AAAAAAAAAIM/eap4VrkciZk/s320/papermario_screen012.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5597365091319995250" /&gt;&lt;/a&gt;My wife and I like to play games like Paper Mario together.  Paper Mario is a long game with a lot of dialog.  At any point in the game, you can talk to any character, and that character will say something "sensible".  For instance, they'll ask you to help them out, or they'll thank you if you've already helped them out.  I've always wondered how that's coded.  Similarly, I've always wondered how many ways I could think up to code it.&lt;br /&gt;&lt;br /&gt;The simplest approach is to use a complicated set of possibly nested if/else statements.  For instance, if Mario has this item, then say this.  Otherwise, if he has beaten this level, say that.  Certainly that's a valid approach, and it doesn't even matter if it's slow.  Since people read so slowly, trying to optimize how quickly you can come up with what text to show next is absolutely the last thing you would ever need to optimize in a video game.&lt;br /&gt;&lt;br /&gt;At the opposite extreme, this problem could be solved with a rules engines.  There are libraries that let you wrangle control of complex if/else hierarchies.  (The first time I heard about rules engines was when I was interviewing at a consulting company where they were building a welfare system for various counties in California.  Apparently, the rules surrounding welfare systems are so complex that a rules engine is a necessity.)&lt;br /&gt;&lt;br /&gt;You can also use a mix of if/else statements and switch statements.  For instance, the main game is a mostly linear progression from one state to the next, which is easily solved by a switch statement.  If you are in this state, say this.  If you're in that state, say that.  However, sometimes Mario goes on side quests.  In cases like that, a particular character might need to have a separate switch statement to know what to say for that side quest.&lt;br /&gt;&lt;br /&gt;Similar to using a switch statement, you can use a non-sparse array per character where each state is an index into that array, and each spot in that array has a pointer to a message.  Since the array contains every state, many of the states will point to the same message.&lt;br /&gt;&lt;br /&gt;You can create a mapping from state to message using a sparse tree.  To find out what to say, traverse the tree to find the state that is closest to, but still less than the current state.  A sparse tree has a benefit over a non-sparse array in that the tree is only as large as the number of messages the character might say.&lt;br /&gt;&lt;br /&gt;Rather than having an array of states for each character, you could also have a single array of states for the entire game.  When you save the player's position in the game, you simply save his index into the array.  The first spot in the array has a mapping of what every character would say at the beginning of the game.  Each new spot in the array has a list of updates for all the characters who would say something different for that new state.  This works like a database transaction log.  Since there are only a few hundred or a few thousand characters, you can easily store in memory what each character would say at the current state in the game.&lt;br /&gt;&lt;br /&gt;I'm going to take a guess and say that they probably just used a simple set of nested if/else statements, but it's kind of fun to think of other approaches.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-1955730524426941580?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/1955730524426941580/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=1955730524426941580' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/1955730524426941580'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/1955730524426941580'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/gaming-how-do-characters-know-what-to.html' title='Gaming: How Do Characters Know What to Say?'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-gSjS8hSTIvs/Ta3V5PpM23I/AAAAAAAAAIM/eap4VrkciZk/s72-c/papermario_screen012.jpg' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-8807576159129634078</id><published>2011-04-18T19:34:00.000-07:00</published><updated>2011-04-18T20:20:22.760-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><title type='text'>Ruby: My Take on Pivotal Labs, Part II</title><content type='html'>As I mentioned in my &lt;a href="http://jjinux.blogspot.com/2011/04/ruby-my-take-on-pivotal-labs-part-i.html"&gt;previous post&lt;/a&gt;, I have tremendous respect for how Pivotal Labs builds software.  In this blog post, I want to cover why practices used at Pivotal Labs may not always be appropriate at other companies.  The core of my argument is that Pivotal Labs is a consultancy; hence, their priorities are not always the same as the priorities for a startup building its own software.&lt;br /&gt;&lt;br /&gt;First of all, let me talk about full-time pair programming.  In the book &lt;a href="http://www.stevemcconnell.com/psd.htm"&gt;Professional Software Development&lt;/a&gt;, Steve McConnell states that NASA discovered that the single most effective way to reduce defects (in manufacturing, etc.) is to always have a second pair of eyes present (i.e. to work in pairs).  However, NASA must go to extreme lengths to avoid defects because lives are on the line.  That's rarely the case with most startups.  Most defects are merely embarrassing.  In many cases, code review may be more efficient than full-time pair programming.  In some cases involving purely aesthetic matters, it may sometimes be acceptable to even skip the full code reviews and settle for aesthetic reviews instead.&lt;br /&gt;&lt;br /&gt;Full-time pair programming is also a great way to train newbies.  It's definitely in Pivotal Lab's interest to have all of its employees extremely well trained and interchangeable.  However, it's really the customer that's paying for this training, and it's certainly expensive.  In a startup building its own code, this expense may not be warranted.  Certainly, there's huge value in having overlap between employees so that no one employee who gets hit by a bus can take out the entire company.  However, some specialization may be appropriate.  When I worked at IronPort, we had a low-level, FreeBSD kernel guy.  He spent a lot of time tweaking the kernel in a way that only a FreeBSD kernel guy can.  We also had people who specialized in JavaScript.  Expecting every person to be capable of pair programming on FreeBSD kernel development as well as ajax hacking is simply asking for too much, and it's too expensive in terms of programmer time.  Sometimes a little specialization can be helpful.&lt;br /&gt;&lt;br /&gt;Full-time pair programming may also help a particular task get done faster.  However, if you have a limited number of engineers at a small startup, certainly the total time to completion for all programming tasks will go down if all programming must be done in pairs.  Pivotal Labs has the advantage that they can scale up the number of people on a project if a job calls for it, and they make more money when they do so.  It's the opposite at a small startup.  The number of programmers at a small startup is often somewhat limited and fixed, and there are economic advantages to getting more done with fewer people.  If you only have two programmers, and one of them is a Ruby expert and the other an ActionScript expert, it might be best to just let them do their thing individually so that at least two things are getting done at the same time.&lt;br /&gt;&lt;br /&gt;Don't get me wrong.  I'm not saying that pair programming isn't extremely valuable.  I'm just saying that &lt;i&gt;full-time&lt;/i&gt; pair programming is extremely expensive, and sometimes the cost isn't justifiable for smaller startups that aren't consultancies.&lt;br /&gt;&lt;br /&gt;The next thing I'd like to cover is tests.  Pivotal Labs writes a lot of tests.  It makes sense for them because they get paid to write them, and they never want to get caught with their pants down.  If Pivotal Labs has a choice of implementing two features with a 30% chance of having a defect or of implementing one feature with a 2% chance of having a defect, they're only going to implement one feature.  Since they don't get paid by the feature, they get paid the same either way.&lt;br /&gt;&lt;br /&gt;The economics just aren't the same at a startup building its own software.  Don't get me wrong--I really appreciate the value of testing, but I also try to keep in mind the cost.  My approach to building software at a small startup is to consider the return on investment when writing tests.  Some tests provide a very high return on investment.  They catch a lot of potential bugs and require very little work.  I'm thinking of high-level Cucumber and Webrat tests.  Some tests require a lot of work, but don't catch very many actual bugs.  I'm thinking of view tests.  A view test neither tests that a view looks right, nor does it test that its interface with the controller is correct.  My point is that it may be in Pivotal Lab's best interest to implement view tests, but if I'm working at a small startup that isn't a consultancy, it probably isn't in my interest to implement view tests.&lt;br /&gt;&lt;br /&gt;So what's my point in bringing all of this up?  As much as I appreciate how Pivotal Labs does things, I understand that those practices may not always be appropriate for a small startup building its own software.  It reminds me of that old essay, &lt;a href="http://www.jwz.org/doc/worse-is-better.html"&gt;The Rise of "Worse is Better"&lt;/a&gt;.  As much as I hate to admit it, sometimes worse &lt;i&gt;is&lt;/i&gt; better.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-8807576159129634078?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/8807576159129634078/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=8807576159129634078' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/8807576159129634078'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/8807576159129634078'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/ruby-my-take-on-pivotal-labs-part-ii.html' title='Ruby: My Take on Pivotal Labs, Part II'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-5598081459319022220</id><published>2011-04-18T16:22:00.001-07:00</published><updated>2011-04-18T19:38:51.129-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='rspec'/><category scheme='http://www.blogger.com/atom/ns#' term='webrat'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='cucumber'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><title type='text'>Ruby: My Take on Pivotal Labs, Part I</title><content type='html'>&lt;a href="http://pivotallabs.com/"&gt;Pivotal Labs&lt;/a&gt; is one of my favorite companies.  I have a tremendous amount of respect for how they develop software.  They put the "extreme" in extreme programming, and more importantly, they get stuff done.  However, there are some things that Pivotal Labs tend to do that I disagree with.&lt;br /&gt;&lt;br /&gt;I have such high regard for Pivotal Labs that I specifically try to find startups that started at Pivotal Labs when I'm looking for a job.  Since these companies often make you do a three hour pair programming session on their code, I've seen the code of multiple companies that started at Pivotal Labs.  Hence, although I don't know if Pivotal Labs has an official opinion on these topics, I've seen these things at enough companies that I feel it's worth commenting on.&lt;br /&gt;&lt;br /&gt;First of all, many of the companies that I interviewed at didn't use database-level foreign key constraints and database constraints in general.  I know that it's trendy in the Rails world to try to enforce your constraints at the application level.  (I'll admit that in some cases involving sharding, you can't use foreign key constraints.  However, very few Rails applications are built with sharding.)  Unfortunately, the application is simply incapable of truly enforcing certain constraints such as uniqueness constraints and foreign key constraints.  Only the database can apply these constraints because they interact with ACID and transactions.&lt;br /&gt;&lt;br /&gt;Secondly, as far as I can tell, several of the companies I interviewed at are susceptible to &lt;a href="http://jjinux.blogspot.com/2009/07/rails-configuring-admins-checkboxes-and.html"&gt;mass assignment&lt;/a&gt; vulnerabilities because they don't properly lock things down with attr_accessible such that things are not accessible by default.  Perhaps this problem is going away in Rails 3, I don't know.  However, many of the companies I talked to didn't want me to explain to them why their code was vulnerable.&lt;br /&gt;&lt;br /&gt;Thirdly, a lot of projects at Pivotal Labs tend to use view tests.  I think that unit testing views is a total waste of time.  Apparently, so does &lt;a href="http://www.sarahmei.com/blog/2010/05/29/outside-in-bdd/"&gt;Sarah Mei&lt;/a&gt; at Pivotal Labs.&lt;br /&gt;&lt;br /&gt;Rather than unit testing the model, view, and controller separately, I prefer to rely on Cucumber and Webrat more heavily.  I still write some model tests using RSpec, but I never duplicate things that are already tested by Cucumber and Webrat.  I explained my approach and my reasoning &lt;a href="http://jjinux.blogspot.com/2010/12/ruby-why-i-prefer-cucumber.html"&gt;in another blog post&lt;/a&gt;.  Unfortunately, not everyone agrees with me, and this is a &lt;a href="http://www.rubyinside.com/dhh-offended-by-rspec-debate-4610.html"&gt;hot topic&lt;/a&gt; right now.&lt;br /&gt;&lt;br /&gt;Last of all, I've seen several Pivotal Labs code bases that use Rails 2.3.X, but haven't switched to using the rails_xss plugin.  The rails_xss plugin can really help avoid XSS vulnerabilities.  It allows you to remove most of the h() calls in your templates since it escapes things by default.  I think this behavior is the norm in Rails 3, so this complaint will soon go away for new code bases.&lt;br /&gt;&lt;br /&gt;There's an old saying that if two people agree on everything, than only one of them is doing all the thinking.  Hence, it should come as no surprise that I disagree with a few things that I've seen in various Pivotal Labs projects.  Hopefully no one at Pivotal Labs will think worse of me for pointing those things out.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-5598081459319022220?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/5598081459319022220/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=5598081459319022220' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/5598081459319022220'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/5598081459319022220'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/ruby-my-take-on-pivotal-labs-part-i.html' title='Ruby: My Take on Pivotal Labs, Part I'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-7741626961229188643</id><published>2011-04-18T15:58:00.000-07:00</published><updated>2011-04-18T16:08:04.214-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='oz'/><category scheme='http://www.blogger.com/atom/ns#' term='math'/><title type='text'>Math: Sierpinski's Triangle is a Variation of Pascal's Triangle</title><content type='html'>Here's a picture of Pascal's triangle from Wikipedia.  It's animated just in case you don't remember how Pascal's triangle is created:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/PascalTriangleAnimated2.gif/220px-PascalTriangleAnimated2.gif"&gt;&lt;br /&gt;&lt;br /&gt;Here's a picture of Sierpinski triangle, also from Wikipedia:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://upload.wikimedia.org/wikipedia/en/thumb/8/88/Sierpinski_Triangle.svg/220px-Sierpinski_Triangle.svg.png"&gt;&lt;br /&gt;&lt;br /&gt;You can see in the top image that to calculate a spot in Pascal's triangle, you just add the two above spots.  To get Sierpinski's triangle instead of Pascal's triangle, you just xor the above two spots.  Cute trick, eh?&lt;br /&gt;&lt;br /&gt;I learned this trick while reading &lt;a href="http://www.info.ucl.ac.be/~pvr/book.html"&gt;Concepts, Techniques, and Models of Computer Programming&lt;/a&gt;, which is a fantastic book, by the way.  Here's my Oz code for printing out Pascal's triangle:&lt;pre&gt;% This is a generic version of Pascal's triangle that let's you specify the&lt;br /&gt;% operation instead of just using "+".&lt;br /&gt;&lt;br /&gt;declare GenericPascal OpList ShiftLeft ShiftRight&lt;br /&gt;fun {GenericPascal Op N}&lt;br /&gt;   if N==1 then [1]&lt;br /&gt;   else L in&lt;br /&gt;      L={GenericPascal Op N-1}&lt;br /&gt;      {OpList Op {ShiftLeft L} {ShiftRight L}}&lt;br /&gt;   end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;fun {OpList Op L1 L2}&lt;br /&gt;   case L1 of H1|T1 then&lt;br /&gt;      case L2 of H2|T2 then&lt;br /&gt;  {Op H1 H2}|{OpList Op T1 T2}&lt;br /&gt;      end&lt;br /&gt;   else nil end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;fun {ShiftLeft L}&lt;br /&gt;   case L of H|T then&lt;br /&gt;      H|{ShiftLeft T}&lt;br /&gt;   else [0] end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;fun {ShiftRight L} 0|L end&lt;br /&gt;&lt;br /&gt;declare&lt;br /&gt;fun {FastPascal N} {GenericPascal Number.'+' N} end&lt;br /&gt;&lt;br /&gt;for I in 1..10 do {Browse {GenericPascal Number.'+' I}} end&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-7741626961229188643?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/7741626961229188643/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=7741626961229188643' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/7741626961229188643'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/7741626961229188643'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/math-sierpinskis-triangle-is-variation.html' title='Math: Sierpinski&apos;s Triangle is a Variation of Pascal&apos;s Triangle'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-374916760125166563</id><published>2011-04-18T15:39:00.000-07:00</published><updated>2011-04-18T15:48:48.667-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><category scheme='http://www.blogger.com/atom/ns#' term='Jasmine'/><title type='text'>JavaScript: Jasmine</title><content type='html'>I went to a talk the other day on &lt;a href="http://pivotal.github.com/jasmine/"&gt;Jasmine&lt;/a&gt;:&lt;blockquote&gt;Jasmine is a behavior-driven development framework for testing your JavaScript code. It does not depend on any other JavaScript frameworks. It does not require a DOM. And it has a clean, obvious syntax so that you can easily write tests.&lt;/blockquote&gt;&lt;pre&gt;describe("Jasmine", function() {&lt;br /&gt;  it("makes testing JavaScript awesome!", function() {&lt;br /&gt;    expect(yourCode).toBeLotsBetter();&lt;br /&gt;  });&lt;br /&gt;});&lt;/pre&gt;Jasmine started life at Pivotal Labs and is like RSpec for JavaScript.  Thanks to the flexibility of JavaScript, it has a powerful mocking / stubbing framework.  Jasmine is not a replacement for Selenium.  It's good for testing JavaScript functions that calculate things, but it doesn't try to make testing the DOM any easier.  Just as unit tests written using RSpec are not a replacement for integration tests written using Cucumber and Webrat, similarly unit tests written using Jasmine are not a replacement for integration tests written using Selenium.  Nonetheless, it looks useful.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-374916760125166563?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/374916760125166563/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=374916760125166563' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/374916760125166563'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/374916760125166563'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/javascript-jasmine.html' title='JavaScript: Jasmine'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-8112946283044490906</id><published>2011-04-18T13:22:00.000-07:00</published><updated>2011-04-18T13:43:45.924-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='scala'/><title type='text'>Ruby: nil is a Billion Dollar Mistake</title><content type='html'>The fact is, I really like Ruby.  However, there are some ways in which it uses nil that I really disagree with.  For instance, in Ruby, if you try to look up something in a hash that doesn't exist, you get a nil.  Similarly, if you try to reference an @attribute that hasn't been set yet, you'll get a nil.&lt;br /&gt;&lt;br /&gt;That reminds me of this article, &lt;a href="http://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare"&gt;Null References: The Billion Dollar Mistake&lt;/a&gt;:&lt;blockquote&gt;Tony Hoare introduced Null references in ALGOL W back in 1965 “simply because it was so easy to implement”, says Mr. Hoare. He talks about that decision considering it “my billion-dollar mistake”.&lt;/blockquote&gt;Compounding this problem is Ruby's current lack of real keyword arguments (although, I know they're coming).  Hence, if you pass a keyword argument like f(:foo =&gt; 1), and then try to use the keyword argument in the function like options[:foooo], the misspelling will result in a nil as if the argument hadn't been passed.  This masks a real problem.  All of these have resulted in real bugs in my code and lots of frustration.&lt;br /&gt;&lt;br /&gt;It doesn't have to be this way.  In Python, if you try to look up something in a dict that doesn't exist, you get a KeyError exception.  If you try to use an attribute that doesn't exist, you get an AttributeError exception.  If you misspell a keyword argument, you get a TypeError exception.  Exceptions are nice because they catch the problem right away instead of allowing it to fester.  In Ruby, if I get a nil that I'm not expecting, I might not find out that there's a problem until much, much later in a different part of the code when I try to call a method on the nil thinking it's a real object.&lt;br /&gt;&lt;br /&gt;Since I'm on the subject of hashes, I think I should mention that Haskell and Scala take a different approach.  If you look up something in Scala that may not exist, it returns an Option instance.  An Option instance may or may not contain a real value.  (Similarly, Haskell uses the Maybe monad.)  You have to do work to get the value out of the Option instance, and the type system will catch you if you just blindly assume that there's always something in the Option instance.  This is one case where the ML type system can help you avoid a whole class of bugs.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-8112946283044490906?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/8112946283044490906/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=8112946283044490906' title='37 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/8112946283044490906'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/8112946283044490906'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/ruby-nil-is-billion-dollar-mistake.html' title='Ruby: nil is a Billion Dollar Mistake'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>37</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-7714465422537849878</id><published>2011-04-18T13:08:00.000-07:00</published><updated>2011-04-18T13:16:16.015-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Python: Adding New Methods to an Instance</title><content type='html'>It's easy to dynamically add new methods to a class in Python, but all my attempts to add new methods directly to an instance had always involved hacks.  When I discovered that Ruby had syntax to add methods directly to an instance, my curiosity in the subject was rekindled.  Fortunately, someone pointed me to &lt;a href="http://countergram.com/adding-bound-methods"&gt;this blog post&lt;/a&gt; that shows how.&lt;br /&gt;&lt;br /&gt;Why should you care?  Most of the time, you shouldn't.  You might want to use this trick if you were trying to do something like JavaScript's prototypal-based inheritance system.  In my case, I think I was trying to do some funky monkey patching where I only wanted to monkey patch a particular instance instead of the class as a whole.  Anyway, even if I can't come up with a good use case, I'm still glad I know how to do it ;)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-7714465422537849878?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/7714465422537849878/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=7714465422537849878' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/7714465422537849878'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/7714465422537849878'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/python-adding-new-methods-to-instance.html' title='Python: Adding New Methods to an Instance'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-1808771084189683949</id><published>2011-04-18T12:56:00.000-07:00</published><updated>2011-04-18T13:05:06.648-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='fish'/><title type='text'>Linux: Fish Hanging</title><content type='html'>&lt;a href="http://fishshell.com/"&gt;fish&lt;/a&gt; is a "user friendly command line shell for UNIX-like operating systems such as Linux."  I've been using fish for about a year, and I really like it.  Unfortunately, until recently, I had a problem that I couldn't log into virtual consoles in Linux.  If I hit Cntl-Alt-F1 and tried to log in, fish would just hang.  This was mentioned &lt;a href="http://www.mail-archive.com/fish-users@lists.sourceforge.net/msg01989.html"&gt;on the mailing list&lt;/a&gt; a long time ago.  I'm pleased to say that I finally solved the problem.&lt;br /&gt;&lt;br /&gt;I had the following code in my ~/.config/fish/config.fish:&lt;pre&gt;type fortune &gt; /dev/null&lt;br /&gt;and begin&lt;br /&gt; fortune&lt;br /&gt; echo&lt;br /&gt;end&lt;/pre&gt;Basically, this code says, "If fortune exists, run it.  Otherwise, don't complain."  I figured out through a process of elimination that was the culprit.  I replaced it with:&lt;pre&gt;if status --is-interactive&lt;br /&gt; fortune&lt;br /&gt; echo&lt;br /&gt;end&lt;/pre&gt;This code will result in an error if fortune doesn't exist, but it won't die.  It turns out to be fine for me since I only run fish on my laptop, and I usually install fortune at the same time I install fish.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-1808771084189683949?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/1808771084189683949/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=1808771084189683949' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/1808771084189683949'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/1808771084189683949'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/linux-fish-hanging.html' title='Linux: Fish Hanging'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-4449910195692337546</id><published>2011-04-18T12:22:00.000-07:00</published><updated>2011-04-18T12:46:24.435-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><title type='text'>JavaScript: Perfectly Encapsulated May Mean Perfectly Untestable</title><content type='html'>It's no secret that JavaScript is like Lisp in that you can accomplish amazing things using a huge number of small, nested functions.  In fact, you can write things like:&lt;pre&gt;(function () {&lt;br /&gt;    function pickNose() {&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    function fart() {&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    pickNose();&lt;br /&gt;    fart();&lt;br /&gt;})();&lt;/pre&gt;In this code, an anonymous function is defined and immediately called.  pickNose() and fart() are two internal functions that are used by the outer function, but they are not available to the outside world.&lt;br /&gt;&lt;br /&gt;It's amazing what you can get done using nested closures like this, but there's a cost.  How do you write tests for pickNose() and fart()?  Certainly, you can write a test for the outer function as a whole, but there's no way to test those inner functions in a standalone way without doing some refactoring.  In a certain sense, the code is like a script in that you can test the thing as a whole, but you can't test the parts in a standalone way.&lt;br /&gt;&lt;br /&gt;What's the solution?  I'm sure there are many.  You could have the outer function take a parameter such that when the correct value is passed, the code could flip over to testing mode and test itself.  Another approach is to use Douglas Crockford's module pattern.  The outer function could return an object that has references to the inner functions.  That way you can call them externally and test them.  However, that may not be an option if you are really paranoid about the outside world getting references to those functions.&lt;br /&gt;&lt;br /&gt;In my opinion, getting overly paranoid about people calling your inner functions isn't very fruitful.  JavaScript doesn't have much to help you keep modules away from each other.  As soon as you have hostile JavaScript on the page, it's sort of game over.  All of the JavaScript operates with the same permissions--it's not like you can sandbox a particular module.&lt;br /&gt;&lt;br /&gt;Furthermore, it's difficult if not impossible to prevent people from just grabbing your JavaScript source code and hacking it to do whatever they want (especially if they can rely on the help of an external server).  To state the obvious, JavaScript doesn't have very good internal security boundaries, so your server must always be distrustful of the JavaScript that it's talking to.  Of course, that's the way the web has worked for as long as I've been coding.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-4449910195692337546?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/4449910195692337546/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=4449910195692337546' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/4449910195692337546'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/4449910195692337546'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/javascript-perfectly-encapsulated-may.html' title='JavaScript: Perfectly Encapsulated May Mean Perfectly Untestable'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-6325371481803348816</id><published>2011-04-18T11:53:00.000-07:00</published><updated>2011-04-18T12:17:18.494-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='twisted'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='louie'/><title type='text'>Python: Using Louie with Twisted</title><content type='html'>&lt;a href="http://11craft.github.com/louie/"&gt;Louie&lt;/a&gt; "provides Python programmers with a straightforward way to dispatch signals between objects in a wide variety of contexts. It is based on PyDispatcher, which in turn was based on a highly-rated recipe in the Python Cookbook."  Louie is like an event system used in a GUI toolkit.  Similarly, you can think of Louie as an internal pubsub system.  &lt;a href="http://twistedmatrix.com/trac/"&gt;Twisted&lt;/a&gt; is a framework for building asynchronous network servers.  Using Louie in your Twisted applications can make your applications a little less "twisted".&lt;br /&gt;&lt;br /&gt;When you code a Twisted application, you often put a lot of your logic in custom Factory and Protocol classes.  However, if you have one application that has to talk to, say, three different types of servers, and you have a custom Protocol and Factory class for each server (perhaps each server speaks a different network protocol), it can be confusing to have your application logic broken up all over the place.  Louie can help with that.&lt;br /&gt;&lt;br /&gt;When you are implementing a Protocol class, when you receive a new Twisted event, you can generate a Louie event.  In that way, you can have a single application-specific class that responds to events from a wide variety of Twisted Protocol classes.  When I used this approach on my Twisted application, the code went from being very "scatter brained" to something more linear.  I had a bunch of small functions that each responded to Louie events, and all the functions were in order in the same class.  It certainly helped me wrangle control of the complexity of the system.&lt;br /&gt;&lt;br /&gt;It may seem strange to translate Twisted events into Louie events, but considering the fact that Louie has support specifically for Twisted and considering the fact that I learned this trick from Drew Pertulla, a Twisted user, I know I'm not the only one using this trick.&lt;br /&gt;&lt;br /&gt;I have a couple more tips.  First of all, Louie tries really hard to pass only the arguments that your function is looking for.  Hence, if your function doesn't accept a sender argument, it won't pass a sender argument.  This is really helpful, but it can bite you.  The magic tends to break down if your function is wrapped by a decorator or if your function is a closure (i.e. a nested function).  In these cases, Louie can't figure out exactly which arguments to pass, and stuff can break.  However, it's usually easy to work around situations like this once you figure out what's going on.&lt;br /&gt;&lt;br /&gt;I have one other mildly off-topic trick for working with Twisted.  Queues and Twisted work really well together.  If you have one piece of Twisted code that simply reads from a queue and then does something, then other parts of the Twisted code can put things in the queue in a synchronous manner.  It sounds simple, but this one trick really helped me out a few different times.&lt;br /&gt;&lt;br /&gt;Last of all, I wanted to mention gevent.  Twisted is certainly a mature library with support for a range of protocols, but if you're working with green field code, you might want to take a peek at &lt;a href="http://www.gevent.org/"&gt;gevent&lt;/a&gt; instead.  Aside from having &lt;a href="http://nichol.as/asynchronous-servers-in-python"&gt;excellent performance&lt;/a&gt;, it also lets you write code in a synchronous manner, which is helpful for mere mortals like myself.  If you're thinking about using Twisted vs. gevent, check out &lt;a href="http://jjinux.blogspot.com/2011/04/pycon-using-coroutines-to-create.html"&gt;this talk&lt;/a&gt; that the Meebo guys gave at PyCon.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-6325371481803348816?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/6325371481803348816/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=6325371481803348816' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/6325371481803348816'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/6325371481803348816'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/python-using-louie-with-twisted.html' title='Python: Using Louie with Twisted'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-1041843594299144057</id><published>2011-04-18T11:18:00.000-07:00</published><updated>2011-04-18T11:33:18.821-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='hookbox'/><category scheme='http://www.blogger.com/atom/ns#' term='comet'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>JavaScript: JS.IO is Solid, Hookbox Might be the Bee's Knees</title><content type='html'>Recently, I needed to add realtime (i.e. comet, websockets, flash sockets, etc.) support to an application.  JS.IO is a JavaScript library that provides a long polling system using the Comet Session Protocol (CPS).  It doesn't try to do what Socket.IO does, i.e. abstract all the various transport mechanisms such as websockets and flash sockets.  Rather, it provides long polling and leaves the switch to websockets or flash sockets to the user.  There are server-side libraries to integrate with JS.IO using Twisted, Eventlet, Erlang, etc.&lt;br /&gt;&lt;br /&gt;My experiences with JS.IO were very positive.  Although the documentation was sorely lacking, Michael Carter (the author) was extremely helpful when I was getting started.  Furthermore, my testing with crossbrowsertesting.com proved that JS.IO was very reliable across an incredible range of browsers.  It was the most reliable comet library I tried.  By the way, I was doing this cross-domain.&lt;br /&gt;&lt;br /&gt;These days, Michael Carter has started a new project called Hookbox:&lt;blockquote&gt;Hookbox is a Comet server and message queue that tightly integrates with your existing web application via web hooks and a REST interface.&lt;/blockquote&gt;I blogged about &lt;a href="http://jjinux.blogspot.com/2011/04/pycon-hookbox-all-python-web-frameworks.html"&gt;Michael's talk at PyCon&lt;/a&gt;.  Although Hookbox doesn't perfectly solve all my problems (it doesn't yet having clustering support), I'm extremely hopeful that this will turn out to be a great project!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-1041843594299144057?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/1041843594299144057/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=1041843594299144057' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/1041843594299144057'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/1041843594299144057'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/javascript-jsio-is-solid-hookbox-might.html' title='JavaScript: JS.IO is Solid, Hookbox Might be the Bee&apos;s Knees'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-672928583267103110</id><published>2011-04-18T10:56:00.000-07:00</published><updated>2011-04-18T11:30:05.667-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='comet'/><category scheme='http://www.blogger.com/atom/ns#' term='Socket.IO'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>JavaScript: Socket.IO Didn't Meet Needs</title><content type='html'>Recently, I needed to add realtime (i.e. comet, websockets, flash sockets, etc.) support to an application.  Socket.IO is a library built on top of Node.JS that "aims to make realtime apps possible in every browser and mobile device, blurring the differences between the different transport mechanisms."  Since Socket.IO did exactly what I needed, I was hoping it would solve my problems easily and that I wouldn't have to implement what Socket.IO did myself.&lt;br /&gt;&lt;br /&gt;Unfortunately, things didn't work out so well.  I had to do things in a cross-domain manner.  Although the browser support list for Socket.IO is very good, that didn't match up with my actual experience.  I built a simple application that tried to send and receive a message using Socket.IO, and then it reported on which transport was used.  Unfortunately, many of the browsers that I wanted to support such as IE 6 and 7 and Opera just didn't work, even though they were supposed to.  Here are some of &lt;a href="http://groups.google.com/group/socket_io/browse_thread/thread/b1a32a19d14dd000?pli=1"&gt;my results&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Furthermore, if you watch the mailing list, a lot of questions just get dropped on the floor.  I'm sure this is because the authors of Socket.IO are completely overwhelmed.  Hopefully as Socket.IO matures and the list of expert users grows, this will improve.&lt;br /&gt;&lt;br /&gt;By the way, you might wonder how I tested so many browsers.  I paid for an account on &lt;a href="http://crossbrowsertesting.com"&gt;crossbrowsertesting.com&lt;/a&gt;.  It was worth every penny!  First, I would use it to try to take a snapshot of my test page on all the different browsers.  Then, I would use the web-based VNC system to log into the systems and view the page manually in order to see what was going wrong.  This was very helpful to determine exactly which browsers did and didn't work.&lt;br /&gt;&lt;br /&gt;Anyway, as I said, I hope Socket.IO gets better because it solves a real need.  Perhaps it already has, since I was doing this testing several months ago.  However, if you need to use Socket.IO, I heartily recommend you make use of crossbrowsertesting.com to make sure that the browsers you need to support actually work.&lt;br /&gt;&lt;br /&gt;In my next post, I'll cover JS.IO.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-672928583267103110?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/672928583267103110/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=672928583267103110' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/672928583267103110'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/672928583267103110'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/javascript-socketio-didnt-meet-needs.html' title='JavaScript: Socket.IO Didn&apos;t Meet Needs'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-372965210530980045</id><published>2011-04-18T10:41:00.000-07:00</published><updated>2011-04-18T10:55:52.162-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='twisted'/><category scheme='http://www.blogger.com/atom/ns#' term='ZeroMQ'/><title type='text'>ZeroMQ is Amazing!</title><content type='html'>Recently, I had to improve the performance of mesh networking in a mesh of, say, 10 nodes.  The original code used a simple RPC system built using JSON on top of netstring.  Every message to every node involved a new connection.  On a cluster of 10 nodes, I was getting 30 messages per second.&lt;br /&gt;&lt;br /&gt;I enhanced the code by using persistent connections.  I also switched from RPC (i.e. using a whole roundtrip that blocks the whole connection) to message passing (i.e. passing a message doesn't necessarily result in a response and doesn't tie up the socket).  This improved the performance to 300 messages per second.&lt;br /&gt;&lt;br /&gt;Next, my buddy encouraged me to try out ZeroMQ.  Man was I amazed!  I hit something like 1800 messages per second on a cluster of 10 nodes!  I can only imagine what ZeroMQ was doing in order to hit this number.  Perhaps it was batching messages more intelligently (from my experience, that's an amazingly effective technique).&lt;br /&gt;&lt;br /&gt;I ran the same test on a range of cluster sizes, from 2 to 10.  The performance graph had a sawtooth shape.  The shape was consistent between runs.  I'm inferring that ZeroMQ doesn't try to have one node send the message to all the other nodes.  Rather, the message filters through the cluster in a tree-like manner.&lt;br /&gt;&lt;br /&gt;Anyway, I'm sorry I can't share the graphs or the code, but let me just say that I was very impressed with ZeroMQ!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-372965210530980045?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/372965210530980045/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=372965210530980045' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/372965210530980045'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/372965210530980045'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/zeromq-is-amazing.html' title='ZeroMQ is Amazing!'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-4848407301837717778</id><published>2011-04-18T10:36:00.001-07:00</published><updated>2011-04-18T10:39:58.978-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='fish'/><title type='text'>Linux: ^\</title><content type='html'>Am I the only one who didn't know that you could use ^\ (i.e. control backslash) to kill a process when ^c doesn't work?  Usually, I have to use ^z to background the process, and then type kill -9 %1.  I think ^\ makes the process dump core, but since dumping core seems to be turned off by default, it works out well.  Here's an example of my killing a process under fish (my shell):&lt;pre&gt;fish: Job 1, “nosetests” terminated by signal SIGQUIT (Quit request from job control with core dump (^\))&lt;/pre&gt;Thanks to Jeff Lindsay for the tip.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-4848407301837717778?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/4848407301837717778/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=4848407301837717778' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/4848407301837717778'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/4848407301837717778'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/linux.html' title='Linux: ^\'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-3441727363534084152</id><published>2011-04-16T15:34:00.000-07:00</published><updated>2011-04-16T15:36:22.672-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='computer history'/><title type='text'>Videos: IBM Centennial Film: 100 X 100 - A century of achievements that have changed the world</title><content type='html'>&lt;a href="http://www.youtube.com/watch?v=39jtNUGgmd4&amp;feature=channel"&gt;IBM Centennial Film: 100 X 100 - A century of achievements that have changed the world&lt;/a&gt;&lt;blockquote&gt;The film features one hundred people, who each present the IBM achievement recorded in the year they were born. The film chronology flows from the oldest person to the youngest, offering a whirlwind history of the company and culminating with its prospects for the future.&lt;/blockquote&gt;I found the film to be very moving.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-3441727363534084152?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/3441727363534084152/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=3441727363534084152' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/3441727363534084152'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/3441727363534084152'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/videos-ibm-centennial-film-100-x-100.html' title='Videos: IBM Centennial Film: 100 X 100 - A century of achievements that have changed the world'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-6560821047891554195</id><published>2011-04-13T15:34:00.000-07:00</published><updated>2011-04-15T15:58:51.198-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='html5'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Python: I'm Looking for a Python Instructor</title><content type='html'>I'm looking for a talented, friendly Python programmer to do corporate training.  I'm helping out a company called Marakana.  They do corporate training and have multiple gigs lined up for Python.  Unfortunately, their normal Python instructor is getting a little bit busy right now, and so am I.  If you're interested in giving a four day training session on Python, please send email to me at jjinux at gmail dot com.  My buddy Robert Zuber has already prepared the course materials, and the pay is pretty decent.  They're also looking for people to teach Ruby on Rails, HTML5, JavaScript, and Android, but I figured most of my readers are Python programmers.&lt;br /&gt;&lt;br /&gt;I'm sure this is obvious, but you must be comfortable with public speaking.  You get bonus points if you've given a talk at PyCon or a local users group.  You get double bonus points if you're well known in the Python community, especially if I know you ;)&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update:&lt;/b&gt; The response was pretty overwhelming, so I think I can stop looking.  Thanks, guys.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-6560821047891554195?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/6560821047891554195/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=6560821047891554195' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/6560821047891554195'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/6560821047891554195'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/python-im-looking-for-python-instructor.html' title='Python: I&apos;m Looking for a Python Instructor'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-5751078311445884639</id><published>2011-04-06T13:11:00.000-07:00</published><updated>2011-04-06T14:17:01.174-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pycon2011'/><title type='text'>PyCon: Closing Lightning Talks</title><content type='html'>PyCon will be held in Montreal in 2014 and 2015.&lt;br /&gt;&lt;br /&gt;Twiggy is a new Pythonic logger.  It has a totally new design (i.e. it's not like log4j).  It's the first really new logging design in 15 years.  It uses lots of chaining method calls like jQuery.  It makes parsing logs easier.  It has a modern config system.  It has better traceback printing.  It has an asynchronous logger.&lt;br /&gt;&lt;br /&gt;Askbot is a Stack Overflow clone in Python.&lt;br /&gt;&lt;br /&gt;The Python Miro Community has Python videos.  They're rolling out universal subtitle support.&lt;br /&gt;&lt;br /&gt;Minuteman is a tool to replace your build.sh.  It acts as a workspace and project manager.  It is like zc.buildout.  It's also like Maven and Gentoo.  It's a "metabuild system."  It has no docs, no tests, and no users.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://howoldismykid.com/"&gt;Hold Old is My Kid?&lt;/a&gt; is a website that helps you figure out your kid's age in days, months, and years.&lt;br /&gt;&lt;br /&gt;flufl.enum is an enum library written by Barry Warsaw.&lt;br /&gt;&lt;br /&gt;MOE_write is a library for dealing with Python2 vs. Python3.  It's from Google.  It allows you to maintain 2 or 3 branches (?).  It runs 2to3 and 3to2 to patch back and forth between branches.  It's a scary hack.&lt;br /&gt;&lt;br /&gt;Microsoft stopped funding IronPython.  It's still going forward in the open source community.  There's a project called IronPython Tools for Visual Studio.  It has intellisense that can do crazy, static type analysis.  It can figure out the type of a variable even through function calls.&lt;br /&gt;&lt;br /&gt;A guy wrote Adventure in Python3.  He ported it from the original Fortran code.  He even did a weird thing where you can play it within the Python shell.  What's weirder is that he wanted people to be able to do things like write "north" in the Python shell, without needing to add the parenthesis to make it a function call.  Hence, he overwrote the __repr__ method so that calling "north" would automatically call the function.  Cute hack.&lt;br /&gt;&lt;br /&gt;Python has a ton of mock testing libraries.  Almost everyone should make use of mocks, at least to mock out services only available over the Internet or to test your exceptions handling.  There's a new library called "Fudge" that was inspired by "Mocha".&lt;br /&gt;&lt;br /&gt;Side note: my buddy Kyle wrote a mocking library called &lt;a href="http://code.google.com/p/ditto/"&gt;ditto&lt;/a&gt; at IronPort/Cisco which has various useful features.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-5751078311445884639?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/5751078311445884639/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=5751078311445884639' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/5751078311445884639'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/5751078311445884639'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/pycon-closing-lightning-talks.html' title='PyCon: Closing Lightning Talks'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-2086125143474054907</id><published>2011-04-06T12:59:00.000-07:00</published><updated>2011-04-06T13:10:05.670-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pycon2011'/><title type='text'>PyCon: Hidden Treasures in the Standard Library</title><content type='html'>&lt;a href="http://us.pycon.org/2011/schedule/presentations/251/"&gt;Hidden Treasures in the Standard Library&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The talk was by Doug Hellmann.  He writes &lt;a href="http://www.doughellmann.com/PyMOTW/"&gt;Python Module of the Week&lt;/a&gt;.  By the way, that is the most popular Python blog according to Google Reader, at least the last time I checked.  Doug is publishing the series of blog posts as a book.  Oh, and he's  a nice guy :)&lt;br /&gt;&lt;br /&gt;Side note: I met a guy who worked at a company that provided mapping software.  The software was used by Google maps.  He worked on the routing algorithms.  He said the whole system consisted of 200 million lines of code.  (I'm not sure how that's possible.)  However, he also said that they distribute an SDK that contains .NET, the JRE, and Python within it.&lt;br /&gt;&lt;br /&gt;Use the csv module with your own "dialects" to handle data that has fields with characters between each field.&lt;br /&gt;&lt;br /&gt;SQLite3 has been added to the standard library.  You can create custom column types in Python.&lt;br /&gt;&lt;br /&gt;You can create signed, serialized data using the hmac library.&lt;br /&gt;&lt;br /&gt;You can serialize data using the built in JSON support.  Look at json.dumps(default=...).&lt;br /&gt;&lt;br /&gt;You can override sys.excepthook with your own function.&lt;br /&gt;&lt;br /&gt;You can create complex logging handlers.  For instance, you can send non-verbose messages to the console, or you can send verbose messages to a log file.  You can even combine these two things.  For instance, you can configure it so that it logs tracebacks, only to file, and only on error.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-2086125143474054907?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/2086125143474054907/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=2086125143474054907' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/2086125143474054907'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/2086125143474054907'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/pycon-hidden-treasures-in-standard.html' title='PyCon: Hidden Treasures in the Standard Library'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-9208320128909857018</id><published>2011-04-05T17:13:00.000-07:00</published><updated>2011-04-06T12:32:12.728-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pycon2011'/><category scheme='http://www.blogger.com/atom/ns#' term='astronomy'/><title type='text'>PyCon: Greasing the Wheels of Exploration with Python</title><content type='html'>&lt;a href="http://us.pycon.org/2011/schedule/presentations/255/"&gt;Greasing the Wheels of Exploration with Python&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Here's the talk summary:&lt;blockquote&gt;The control of the Mars Exploration Rovers (MER) requires a complex set of coordinated activites by a team. Early in the MER mission the author automated in Python much of the task of one of the operation positions, the Payload Uplink Lead, for 7 of the 9 cameras on each rover. This talk describes the MER rovers, the operation tasks and that implemented system.&lt;/blockquote&gt;They used gigapan images.&lt;br /&gt;&lt;br /&gt;They use virtual reality to visualize what's going on.&lt;br /&gt;&lt;br /&gt;Dust was a serious problem for the rovers.&lt;br /&gt;&lt;br /&gt;There's lots of Python &lt;strike&gt;on the rovers&lt;/strike&gt; used to control the rovers. &lt;br /&gt;&lt;br /&gt;The speaker's background is in machine learning and robotics.&lt;br /&gt;&lt;br /&gt;The rovers have been running for 6-7 years.  They find 1-2 bugs a year.  Bugs are usually fixed in a matter of hours.&lt;br /&gt;&lt;br /&gt;They uses Ames Vision Workbench, Nebula, and OpenStack.  All three of these are open source.&lt;br /&gt;&lt;br /&gt;The speaker was from Ames Research Center, NASA.&lt;br /&gt;&lt;br /&gt;Side note: unfortunately, I missed the first five minutes.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-9208320128909857018?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/9208320128909857018/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=9208320128909857018' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/9208320128909857018'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/9208320128909857018'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/pycon-greasing-wheels-of-exploration.html' title='PyCon: Greasing the Wheels of Exploration with Python'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-8303672140251497946</id><published>2011-04-05T17:03:00.000-07:00</published><updated>2011-04-05T17:08:46.945-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pycon2011'/><title type='text'>PyCon: Fun with Python's Newer Tools</title><content type='html'>&lt;a href="http://us.pycon.org/2011/schedule/presentations/261/"&gt;Fun with Python's Newer Tools&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;collections.namedtuple works just like normal tuples, but it lets you assign a name to each field.  It's fast, and there is no additional per-tuple space cost.&lt;br /&gt;&lt;br /&gt;There's a lot of cool stuff in the collections package.&lt;br /&gt;&lt;br /&gt;Named tuples can be used for "instance prototypes" by using "other_tuple._replace(field=5)".  Don't be discouraged by the "_"--they had to do that so as to avoid a namespace conflict.&lt;br /&gt;&lt;br /&gt;You can subclass a named tuple.  It's based on __slots__.&lt;br /&gt;&lt;br /&gt;In Python 3.2, there is a functools.lru_cache.  It's a decorator.  It accepts a maxsize argument.&lt;br /&gt;&lt;br /&gt;Side note: I missed the first five minutes of the talk.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-8303672140251497946?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/8303672140251497946/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=8303672140251497946' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/8303672140251497946'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/8303672140251497946'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/pycon-fun-with-pythons-newer-tools.html' title='PyCon: Fun with Python&apos;s Newer Tools'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-2139160083631244688</id><published>2011-04-05T16:59:00.001-07:00</published><updated>2011-04-05T17:02:45.781-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OpenStack'/><category scheme='http://www.blogger.com/atom/ns#' term='cloud'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pycon2011'/><title type='text'>PyCon: An Open success for the cloud: OpenStack</title><content type='html'>Side note: I missed part of the talk because I had to leave early to go to mass.&lt;br /&gt;&lt;br /&gt;OpenStack turns a pile of hardware into a cloud.&lt;br /&gt;&lt;br /&gt;Swift is their object storage system.&lt;br /&gt;&lt;br /&gt;Nova is their system for provisioning virtual machines.&lt;br /&gt;&lt;br /&gt;Glance is their system for image storage.&lt;br /&gt;&lt;br /&gt;Burrow is their distributed message system.&lt;br /&gt;&lt;br /&gt;Dash is a Django user interface for managing virtual machines.&lt;br /&gt;&lt;br /&gt;NASA uses OpenStack.&lt;br /&gt;&lt;br /&gt;Rackspace is going to use it.  They were acquired by Rackspace.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-2139160083631244688?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/2139160083631244688/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=2139160083631244688' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/2139160083631244688'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/2139160083631244688'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/pycon-open-success-for-cloud-openstack.html' title='PyCon: An Open success for the cloud: OpenStack'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-8134572478120219734</id><published>2011-04-05T16:38:00.000-07:00</published><updated>2011-04-05T16:59:03.925-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pycon2011'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>PyCon: Disqus: Serving 400 million people with Python</title><content type='html'>Disqus is a commenting system for blogs.&lt;br /&gt;&lt;br /&gt;It's the largest Django application.&lt;br /&gt;&lt;br /&gt;They get 500 million visitors a month.&lt;br /&gt;&lt;br /&gt;It's used by CNN, IGN, MTV, etc.&lt;br /&gt;&lt;br /&gt;Every engineer at the company is also a product manager.&lt;br /&gt;&lt;br /&gt;They have a flat company structure.&lt;br /&gt;&lt;br /&gt;They're experiencing exponential traffic growth.&lt;br /&gt;&lt;br /&gt;They have 100 servers.&lt;br /&gt;&lt;br /&gt;They rent hardware.&lt;br /&gt;&lt;br /&gt;Their Python code is CPU bound.&lt;br /&gt;&lt;br /&gt;They're using Apache + mod_wsgi.&lt;br /&gt;&lt;br /&gt;Their background tasks are IO bound.  They use Celery + gevent for background tasks.  This saves memory over using separate processes for each background job.&lt;br /&gt;&lt;br /&gt;They use Graphite for monitoring.  They use Etsy's statsd proxy for Graphite.&lt;br /&gt;&lt;br /&gt;"Measure anything and everything."&lt;br /&gt;&lt;br /&gt;They had to fork and monkey patch Django in order to scale.&lt;br /&gt;&lt;br /&gt;They deploy to production 3-7 times a day.&lt;br /&gt;&lt;br /&gt;They use Hudson for continuous integration.  They have a large test suite that takes 30 minutes to run.  They love Hudson, but they said it's too Java oriented.&lt;br /&gt;&lt;br /&gt;They do not suffer from not invented here syndrome (NIH).&lt;br /&gt;&lt;br /&gt;They have rolling deploy with fast rollbacks.&lt;br /&gt;&lt;br /&gt;They use the unittest module and nose.&lt;br /&gt;&lt;br /&gt;They use coverage.py and Pyflakes.&lt;br /&gt;&lt;br /&gt;They love pep8.py and Pyflakes.&lt;br /&gt;&lt;br /&gt;They learned Django and then learned Python.&lt;br /&gt;&lt;br /&gt;Python package management is a mess.&lt;br /&gt;&lt;br /&gt;They're a good Python shop, and they have a good engineering culture.&lt;br /&gt;&lt;br /&gt;They have open sourced a bunch of stuff.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-8134572478120219734?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/8134572478120219734/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=8134572478120219734' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/8134572478120219734'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/8134572478120219734'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/pycon-disqus-serving-400-million-people.html' title='PyCon: Disqus: Serving 400 million people with Python'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-42855807435644807</id><published>2011-04-05T16:26:00.000-07:00</published><updated>2011-04-05T16:37:46.440-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pycon2011'/><title type='text'>PyCon: Going Full Python - Threadless</title><content type='html'>The keynote was by a guy from Threadless.&lt;br /&gt;&lt;br /&gt;Threadless sells clever, artistic T-shirts.&lt;br /&gt;&lt;br /&gt;The company has lots of culture.  It's very hip.&lt;br /&gt;&lt;br /&gt;The designs are community driven.  The community votes on them.&lt;br /&gt;&lt;br /&gt;They don't actually print the shirts themselves.&lt;br /&gt;&lt;br /&gt;Threadless is in Chicago.  Rahm Emanuel visited.  After he visited, &lt;a href="http://twitter.com/MayorEmanuel/status/3123505528840192"&gt;he Twittered&lt;/a&gt;, "Seriously, this city used to build things. Now we're just assholes with novelty t-shirts. I'm with motherfucking stupid."&lt;br /&gt;&lt;br /&gt;They switched from PHP to Django.  They're still fighting a lot of technical debt.&lt;br /&gt;&lt;br /&gt;Threadless is an "art community".&lt;br /&gt;&lt;br /&gt;The speaker was hilarious.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-42855807435644807?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/42855807435644807/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=42855807435644807' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/42855807435644807'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/42855807435644807'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/pycon-going-full-python-threadless.html' title='PyCon: Going Full Python - Threadless'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-3021181446382380466</id><published>2011-04-05T16:08:00.000-07:00</published><updated>2011-04-05T16:26:32.865-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pycon2011'/><title type='text'>PyCon: Lightning Talks</title><content type='html'>&lt;a href="http://readthedocs.org/"&gt;Read the Docs&lt;/a&gt; is a site that hosts documentation.  The speaker said "shit" a lot.&lt;br /&gt;&lt;br /&gt;Chef or Puppet--choose one.&lt;br /&gt;&lt;br /&gt;Haystack is a project for doing full-text search.&lt;br /&gt;&lt;br /&gt;"Emacs pinky" is a real problem.&lt;br /&gt;&lt;br /&gt;DjangoZoom enables turnkey deployment for Django.&lt;br /&gt;&lt;br /&gt;PyCon did a donation drive for Japan.  People could donate by texting to 90999.  It wasn't actually very successful, but the Python Software Foundation pitched in to help out.&lt;br /&gt;&lt;br /&gt;JavaScript is like English--it's a real mess, but it works.&lt;br /&gt;&lt;br /&gt;In ECMAScript 5 (ES5), you can start your script with '"use strict";'.&lt;br /&gt;&lt;br /&gt;Firefox (because of the ES Harmony project) has added all sorts of weird additions to JavaScript.  Many of them were taken from Python.&lt;br /&gt;&lt;br /&gt;flufl.i18n is a high level API for i18n.  It's higher level than gettext.  It makes it easy to handle multiple languages at the same time.  It was written by Barry Warsaw at Canonical.&lt;br /&gt;&lt;br /&gt;PyWO is the "Python Window Organizer".  It works on top of your existing window manager.  It allows you to move and resize windows in useful ways.  It support tiling.  However, it's less controlling / automatic than a normal tiling window manager.&lt;br /&gt;&lt;br /&gt;Grace Law (a Python recruiter) said that you should be learning and hacking in your spare time.  It helps with interviews to talk about something you're excited about in your coding life.  Speed is also important.  You should be quick.&lt;br /&gt;&lt;br /&gt;Side note: I saw lots of Ubuntu users and lots of Macs.  Windows users were more unusual this year.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-3021181446382380466?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/3021181446382380466/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=3021181446382380466' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/3021181446382380466'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/3021181446382380466'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/pycon-lightning-talks_05.html' title='PyCon: Lightning Talks'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-7455372006897111841</id><published>2011-04-05T16:00:00.000-07:00</published><updated>2011-04-05T16:08:07.763-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='hpc'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pycon2011'/><title type='text'>PyCon: Supercomputer and Cluster Application Performance Analysis using Python</title><content type='html'>&lt;a href="http://us.pycon.org/2011/schedule/presentations/42/"&gt;Supercomputer and Cluster Application Performance Analysis using Python&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The speaker was from Sandia labs.&lt;br /&gt;&lt;br /&gt;The goal of his project was measure and track the performance of super computers.&lt;br /&gt;&lt;br /&gt;His software used Python, matplotlib, MySQL, etc.  It was a Tkinter application.&lt;br /&gt;&lt;br /&gt;The interface was definitely created by an engineer ;)  It was somewhat overwhelming.&lt;br /&gt;&lt;br /&gt;There was some UI in the application to manage database schemas.&lt;br /&gt;&lt;br /&gt;It's weird to see Tk applications when you're so accustomed to "dot-comish" web apps.&lt;br /&gt;&lt;br /&gt;It seems painful to me to have to recreate an admin to manage database schemas when good ones already exist.&lt;br /&gt;&lt;br /&gt;I think the speaker was running Windows 2000.&lt;br /&gt;&lt;br /&gt;There was some UI in the application for creating queries to query the database.&lt;br /&gt;&lt;br /&gt;The application could show graphs plotted by matplotlib.&lt;br /&gt;&lt;br /&gt;Kiviat charts are hard to code, but they're very useful for plotting multivariable data.&lt;br /&gt;&lt;br /&gt;His projects were called Pylot, Co-Pylot, and eCo-Pylot.  He plans on open sourcing them, but that type of thing takes a while at a big lab.&lt;br /&gt;&lt;br /&gt;His project, Mantevo, is already open source.&lt;br /&gt;&lt;br /&gt;Since the goal of the project is to measure and track the performance of super computers, I asked if they had come up with any interesting results yet.  They haven't yet because they're just getting started.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-7455372006897111841?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/7455372006897111841/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=7455372006897111841' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/7455372006897111841'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/7455372006897111841'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/pycon-supercomputer-and-cluster.html' title='PyCon: Supercomputer and Cluster Application Performance Analysis using Python'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-943442004334825075</id><published>2011-04-05T15:50:00.001-07:00</published><updated>2011-04-05T15:59:37.692-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pycon2011'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>PyCon: HTTP in Python: which library for what task?</title><content type='html'>&lt;a href="http://us.pycon.org/2011/schedule/presentations/140/"&gt;HTTP in Python: which library for what task?&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The talk was by a Mercurial guy.  He works on Google Code.&lt;br /&gt;&lt;br /&gt;When you think of HTTP, what you're probably thinking of is RFC 2616, i.e. HTTP/1.0.  HTTP/1.1 has features that you're probably not remembering to handle correctly.  This resulted in a bug that took him a very long time to diagnose.&lt;br /&gt;&lt;br /&gt;HTTP/1.1 allows pipelining, which means you send all the requests right away and then wait for the responses to come in serialized over the same socket.&lt;br /&gt;&lt;br /&gt;Using chunked encoding lets you keep the connection open between requests even if you don't know how much data will be streamed.  Did you know that you can specify additional headers between chunks?&lt;br /&gt;&lt;br /&gt;Did you know the "100 Continue" response gets sent before you finish sending the body?&lt;br /&gt;&lt;br /&gt;httplib (used by urllib2) is very minimal.  It doesn't even do SSL certificate validation!  It doesn't support keepalive.  It doesn't have unit tests.&lt;br /&gt;&lt;br /&gt;httplib2 is just a wrapper around httplib that adds some stuff.&lt;br /&gt;&lt;br /&gt;There is PycURL.  It's based on libcurl, which is the gold standard for HTTP libraries.  However, it's not very Pythonic and it has a steep learning curve.&lt;br /&gt;&lt;br /&gt;twisted.web.http only supports HTTP/1.0 [or perhaps it doesn't support very much of HTTP/1.1].&lt;br /&gt;&lt;br /&gt;The author is working on a new library.  It uses select and is thus non-blocking.  It has "100 Continue" support.  It has lots of unit tests.  However, it doesn't support pipelining.&lt;br /&gt;&lt;br /&gt;Using httplib (via urllib2) is okay if your needs are simple.&lt;br /&gt;&lt;br /&gt;PycURL is awesome if you can tolerate the steep learning curve.&lt;br /&gt;&lt;br /&gt;You can get the author's new library from &lt;a href="http://code.google.com/p/py-nonblocking-http/"&gt;http://code.google.com/p/py-nonblocking-http/&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-943442004334825075?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/943442004334825075/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=943442004334825075' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/943442004334825075'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/943442004334825075'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/pycon-http-in-python-which-library-for.html' title='PyCon: HTTP in Python: which library for what task?'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-8244112978186686432</id><published>2011-04-05T12:57:00.000-07:00</published><updated>2011-04-05T13:05:34.406-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pycon2011'/><category scheme='http://www.blogger.com/atom/ns#' term='ZeroMQ'/><title type='text'>PyCon: Advanced Network Architectures With ZeroMQ</title><content type='html'>&lt;a href="http://us.pycon.org/2011/schedule/presentations/98/"&gt;Advanced Network Architectures With ZeroMQ&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This was a talk on ZeroMQ by Zed Shaw.  He and his talk were a lot more sedate than I expected.  He's also older than I thought he was.&lt;br /&gt;&lt;br /&gt;He uses Emacs, screen, and Ubuntu.&lt;br /&gt;&lt;br /&gt;ZeroMQ servers should not be exposed to the naked internet.  If you send it invalid protocol data, assertions will kill the process.&lt;br /&gt;&lt;br /&gt;The talk was very fast, but most of it was like the ZeroMQ guide.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-8244112978186686432?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/8244112978186686432/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=8244112978186686432' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/8244112978186686432'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/8244112978186686432'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/pycon-advanced-network-architectures.html' title='PyCon: Advanced Network Architectures With ZeroMQ'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-2539549513898752272</id><published>2011-04-05T12:52:00.000-07:00</published><updated>2011-04-05T12:56:51.576-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pycon2011'/><title type='text'>PyCon: Exhibition of Atrocity</title><content type='html'>&lt;a href="http://us.pycon.org/2011/schedule/presentations/138/"&gt;Exhibition of Atrocity&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Here's the summary:&lt;blockquote&gt;Believe it or not, but you can write pretty horrendously awful code even in a language as elegant as Python. Over the years, I've committed my share of sins; now it's time to come clean. Step right up for a tour of twisted, evil, and downright wrong code, and learn some strategies to avoid writing criminally bad code--if you dare!&lt;/blockquote&gt;This was a fun talk full of fun examples.&lt;br /&gt;&lt;br /&gt;The speaker showed code he had worked on over the course of 11 years.&lt;br /&gt;&lt;br /&gt;They used Hungarian notation at one point!&lt;br /&gt;&lt;br /&gt;snake-guice is a simple, lightweight Python dependency injection framework based on google-guice.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-2539549513898752272?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/2539549513898752272/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=2539549513898752272' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/2539549513898752272'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/2539549513898752272'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/pycon-exhibition-of-atrocity.html' title='PyCon: Exhibition of Atrocity'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-9067376348179720602</id><published>2011-04-05T12:43:00.000-07:00</published><updated>2011-04-05T12:49:06.662-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='twisted'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pycon2011'/><category scheme='http://www.blogger.com/atom/ns#' term='gevent'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>PyCon: Using Coroutines to Create Efficient, High-Concurrency Web Applications</title><content type='html'>&lt;a href="http://us.pycon.org/2011/schedule/presentations/79/"&gt;Using Coroutines to Create Efficient, High-Concurrency Web Applications&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Here's the summary:&lt;blockquote&gt;Creating high-concurrency python web applications is inherently difficult for a variety of reasons. In this talk, I'll discuss the various iterations of application server paradigms we've used at meebo, the advantages/disadvantages of each approach, and why we've settled on a coroutine-based WSGI setup to handle our high-concurrency web applications going forward.&lt;/blockquote&gt;They started with CGI.  Then they switched to mod_wsgi.  Then they switched to Twisted.  Finally, they switched to gevent and Gunicorn.  They said that this was the best of both worlds.&lt;br /&gt;&lt;br /&gt;Guido said, "I hate callback based programming."&lt;br /&gt;&lt;br /&gt;The speaker showed a great chart which showed the strengths and weaknesses of the various approaches.&lt;br /&gt;&lt;br /&gt;Gunicorn is a lightweight WSGI server.  It has worker processes.  It supports gevent.&lt;br /&gt;&lt;br /&gt;mod_wsgi is fast.  However, if you use Gunicorn with multiple processes, it'll beat mod_wsgi.&lt;br /&gt;&lt;br /&gt;Use Greins to manage Gunicorn.&lt;br /&gt;&lt;br /&gt;gevent-profiler is useful.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-9067376348179720602?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/9067376348179720602/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=9067376348179720602' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/9067376348179720602'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/9067376348179720602'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/pycon-using-coroutines-to-create.html' title='PyCon: Using Coroutines to Create Efficient, High-Concurrency Web Applications'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-7983608978205310278</id><published>2011-04-05T11:56:00.000-07:00</published><updated>2011-04-05T12:21:25.640-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ide'/><category scheme='http://www.blogger.com/atom/ns#' term='vim'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pycon2011'/><title type='text'>I Code Sooooo Slowly!</title><content type='html'>One thing I've learned over and over is that a programmer's skill with his preferred editor is no indication of his skill as a programmer.  One of the best programmers I know stuck with Pico for years!  Certainly many of the programmers mentioned in "Coders at Work" use Emacs without even trying to learn it well.&lt;br /&gt;&lt;br /&gt;If you've been reading my blog for a while, you know that I'm obsessed with productivity, especially when it comes to editors.  There's a reason why.  I'm slow...really slow!&lt;br /&gt;&lt;br /&gt;When I was at PyCon, I participated in a coding challenge.  The grand prize was a MacBook Air, and there were only 8 participants.  I figured I stood a good chance at winning the MacBook Air which I needed since I was leaving Twilio.&lt;br /&gt;&lt;br /&gt;The challenge used &lt;a href="http://www.singpath.com/eli/index.html"&gt;SingPath&lt;/a&gt;:&lt;blockquote&gt;SingPath is the most FUN way to practice software languages!  SingPath provides a platform to those that want to test their programming skills in a competitive and fun environment.&lt;/blockquote&gt;(By the way, did you notice that it's spelled Singpath in the title and SingPath in the introductory paragraph?)&lt;br /&gt;&lt;br /&gt;Anyway, the challenge consisted of 10 questions that you have to write Python code to solve.  I finished 6th out of 8!  I was the slowest programmer among the professional programmers.  It gets worse ;)  The guy who won the competition was a Russian guy (I think).  He finished all 10 problems in 10 minutes.  I only finished 8 problems after 30 minutes.  I would have finished the others, but I ran out of time.&lt;br /&gt;&lt;br /&gt;It was definitely humbling to realize just how slow I am.  What's ironic, though, is that I'm really fast at manipulating text in an editor.  (Sometimes I use Vim and sometimes I use NetBeans with the jVi plugin so that it feels like Vim.)  I can make text fly, but that doesn't translate into coding faster.&lt;br /&gt;&lt;br /&gt;Fortunately, I have other assets as a programmer.  For instance, my code is well known for being extremely clean, and I rarely have very many bugs.  Nonetheless, it's frustrating that I'm so slow, and I really wish there was something I could do to improve my speed.&lt;br /&gt;&lt;br /&gt;Having spent so much time futzing with my editor, I think it's clear that that isn't the problem.  I think the problem is that my mind is constantly in "proof mode".  I double check everything, code review my own code multiple times, think about all the edge cases, etc.  Trying to code fast just doesn't work for me.  When I did the programming competition, I was coding as fast as I possibly could.  If I had taken my time like normal, I probably would have taken 2-3 times as long (but the code would have had really nice documentation and tests too).&lt;br /&gt;&lt;br /&gt;Does anyone have any realistic advice for how I can increase my speed without sacrificing quality?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-7983608978205310278?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/7983608978205310278/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=7983608978205310278' title='15 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/7983608978205310278'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/7983608978205310278'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/i-code-sooooo-slowly.html' title='I Code Sooooo Slowly!'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>15</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-3992106249357448986</id><published>2011-04-05T11:47:00.000-07:00</published><updated>2011-04-05T12:40:44.488-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ide'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pycon2011'/><category scheme='http://www.blogger.com/atom/ns#' term='emacs'/><title type='text'>Python: Python IDEs Panel</title><content type='html'>&lt;a href="http://us.pycon.org/2011/schedule/presentations/270/"&gt;Python IDEs Panel&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Side note: There were surprisingly few people at this talk.  It seems like most Python programmers get started with either Vim or Emacs and then don't change.  It's ironic that I'm so obsessed with programmer productivity given that I'm such a slow coder ;)&lt;br /&gt;&lt;br /&gt;The panel consisted of representatives who worked on Python Tools for Visual Studio, PyCharm, Komodo IDE, Wing IDE, and a Python mode for Emacs (pythonmode.el).  There was no one present to champion PyDev or NetBeans.&lt;br /&gt;&lt;br /&gt;Michael Foord prefers Wing IDE.&lt;br /&gt;&lt;br /&gt;Python Tools for Visual Studio has debugging support for high performance computing (HPC).  It supports MPI.  It can debug a program that uses multiple processes.  It supports both IronPython and cPython.  You can use iPython within Visual Studio to control a cluster of machines.  You can write Python code to analyze the variables in the individual frames of a stack.&lt;br /&gt;&lt;br /&gt;PyCharm makes test driven development (TDD) fast!  The speaker was using PyCharm to test drive the development of a class which he was creating quickly in the test file.  PyCharm can automatically create the scaffolding for a class as you use the class in your test.  It can create the scaffolding for methods, add imports, add constructors, etc. all automatically as you try to use those things in your tests.  It has helpful coding suggestions and refactoring support.  It has code snippets.  It's crazy how much it can guess what to create automatically.  It has great Django support.  It has Django-specific code completion.  The demo for PyCharm was flat out amazing!&lt;br /&gt;&lt;br /&gt;Komodo Edit is free and open source.  However, Komodo IDE is commercial.  It adds support for debugging, etc.  It opens very quickly.  It's good at working with multiple languages at the same time.  It has an HTML preview feature that can show you an HTML page within Komodo (presumably because it's written in XUL).  It has a regular expression debugger.  There are about 80 extensions.  In 2011, InfoWorld rated Komodo IDE as the best Python IDE.&lt;br /&gt;&lt;br /&gt;Wing IDE is commercial.  It supports multiple keyboard personalities.  It has a debug shell.  The debugger and intellisense work well together.&lt;br /&gt;&lt;br /&gt;The champion for Emacs was Barry Warsaw.  He showed how to integrate Pyflakes.  The demo wasn't particularly inspiring.&lt;br /&gt;&lt;br /&gt;Most of the IDEs have been around for about 10 years.&lt;br /&gt;&lt;br /&gt;When I brought up the fact that so many successful programmers use Emacs, the general consensus was that the people who succeed using Emacs have been doing the same sort of thing for a really long time.  They have it entirely in their head.  They don't really need an IDE, and an IDE isn't really helpful for their situation.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-3992106249357448986?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/3992106249357448986/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=3992106249357448986' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/3992106249357448986'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/3992106249357448986'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/python-python-ides-panel.html' title='Python: Python IDEs Panel'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-6929052625430969194</id><published>2011-04-05T11:33:00.000-07:00</published><updated>2011-04-06T12:50:33.396-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pycon2011'/><title type='text'>PyCon: The Data Structures of Python</title><content type='html'>&lt;a href="http://us.pycon.org/2011/schedule/presentations/178/"&gt;The Data Structures of Python&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Use types idiomatically.&lt;br /&gt;&lt;br /&gt;Sometimes you don't have a choice.&lt;br /&gt;&lt;br /&gt;Be efficient when it doesn't cost you anything.&lt;br /&gt;&lt;br /&gt;Think about set vs. frozenset, mutable vs. immutable.&lt;br /&gt;&lt;br /&gt;There is now a collections.OrderedDict class in Python 2.7 and 3.1.&lt;br /&gt;&lt;br /&gt;Sometimes you need your data structure to address more than one concern.  Use combinations of things from the standard library.&lt;br /&gt;&lt;br /&gt;collections.deque is a linked list.  It's pop(0) and insert(0, item) operations are O(1), whereas those operations are slower with normal lists.  In Python 2.7, there's a maxlen parameter for the deque class (which I assume turns it into a circular queue).&lt;br /&gt;&lt;br /&gt;You can use the array type to efficiently represent an array of ints, etc.&lt;br /&gt;&lt;br /&gt;heapq is also interesting.&lt;br /&gt;&lt;br /&gt;collections.abc contains abstract base classes.&lt;br /&gt;&lt;br /&gt;"Don't subclass dict ever!"  He said this is true of other containers as well.  He said there are too many edge cases.  You should instead subclass things like collections.Mapping instead.&lt;br /&gt;&lt;br /&gt;There's an ordered set class on the Python Cookbook site.  (Presumably, it combines a set with a list.)&lt;br /&gt;&lt;br /&gt;Don't do more than necessary.  ABCs (abstract base classes) can help.&lt;br /&gt;&lt;br /&gt;You can use a frozenset as a dict key, and you can use frozensets as members in other sets.&lt;br /&gt;&lt;br /&gt;Tuples are more efficient than lists.&lt;br /&gt;&lt;br /&gt;Side note: &lt;a href="http://htraf.htsql.org/"&gt;htraf.htsql.org&lt;/a&gt; is an insanely good WSGI / ReST interface for databases.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-6929052625430969194?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/6929052625430969194/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=6929052625430969194' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/6929052625430969194'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/6929052625430969194'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/pycon-data-structures-of-python.html' title='PyCon: The Data Structures of Python'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-3174790927296707994</id><published>2011-04-05T11:20:00.000-07:00</published><updated>2011-04-05T11:32:37.200-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='twisted'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pycon2011'/><category scheme='http://www.blogger.com/atom/ns#' term='asynchronous'/><category scheme='http://www.blogger.com/atom/ns#' term='concurrency'/><title type='text'>PyCon: Ten Years of Twisted</title><content type='html'>&lt;a href="http://us.pycon.org/2011/schedule/presentations/208/"&gt;Ten Years of Twisted&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The talk was by Glyph Lefkowitz, the original author of Twisted.&lt;br /&gt;&lt;br /&gt;He mentioned Medusa and Sam Rushing.&lt;br /&gt;&lt;br /&gt;Twisted started because Glyph wanted to add email and HTTP handling to his video game, Twisted Reality.  He started by using the "select" function call.&lt;br /&gt;&lt;br /&gt;Twisted is good because it unifies the protocols.&lt;br /&gt;&lt;br /&gt;Glyph is only the 3rd most prolific Twisted committer.&lt;br /&gt;&lt;br /&gt;When you write tests for Twisted code, you can have both the client and server in the same process.&lt;br /&gt;&lt;br /&gt;Twisted is event-driven and asynchronous.&lt;br /&gt;&lt;br /&gt;Before switching to Python, Glyph wrote Twisted Reality in Java using threads.&lt;br /&gt;&lt;br /&gt;Twisted is powerful and flexible.&lt;br /&gt;&lt;br /&gt;Twisted is switching from the term "framework" to "engine".&lt;br /&gt;&lt;br /&gt;"Reactor included."&lt;br /&gt;&lt;br /&gt;Use "twistd --help" to see which servers are available for free with Twisted.&lt;br /&gt;&lt;br /&gt;Glyph said, "I hope we have another 10 years of Twisted."&lt;br /&gt;&lt;br /&gt;Side note: Gevent was really popular this year.  During his keynote (which I missed because I overslept, unfortunately), Guido said that he rejected the "everything is a callback approach".  However, he also said he didn't like approaches using Greenlets because you couldn't be exactly sure where the context switches were happening.  I know he also doesn't like threads.  He seems to prefer explicit yields for asynchronous IO.  As far as I can tell, many Python programmers aren't following his suggestions because Gevent seemed really, really popular this year.  It's certainly my favorite approach for asynchronous network servers.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-3174790927296707994?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/3174790927296707994/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=3174790927296707994' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/3174790927296707994'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/3174790927296707994'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/pycon-ten-years-of-twisted.html' title='PyCon: Ten Years of Twisted'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-739401674870631031</id><published>2011-04-04T18:00:00.000-07:00</published><updated>2011-04-04T18:04:52.055-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pycon2011'/><title type='text'>PyCon: Lightning Talks</title><content type='html'>&lt;a href="http://qtile.org/"&gt;Qtile&lt;/a&gt; is a tiling window manager written in Python.  It's an alternative to Awesome.&lt;br /&gt;&lt;br /&gt;In Tunesia, they were using flame throwers against protesters.&lt;br /&gt;&lt;br /&gt;In a "do-ocracy", you don't govern, you just do.&lt;br /&gt;&lt;br /&gt;PyParsing and SPARK are good parsing libraries.&lt;br /&gt;&lt;br /&gt;Resolver is a Python-powered spreadsheet that works on Windows.&lt;br /&gt;&lt;br /&gt;Dirigible is the same thing as Resolver, but it runs as a web application.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-739401674870631031?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/739401674870631031/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=739401674870631031' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/739401674870631031'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/739401674870631031'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/pycon-lightning-talks.html' title='PyCon: Lightning Talks'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-4914141974949081452</id><published>2011-04-04T17:52:00.000-07:00</published><updated>2011-04-04T18:00:48.665-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='comet'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pycon2011'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>PyCon: Hookbox: All Python web-frameworks, now real-time. Batteries Included</title><content type='html'>&lt;a href="http://us.pycon.org/2011/schedule/presentations/249/"&gt;Hookbox: All Python web-frameworks, now real-time. Batteries Included&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Hookbox is a new server written by Michael Carter (of JS.IO and Orbited fame) for doing comet.  It is a simple comet server that uses web hooks to talk to your normal web server.  Your normal web server can take care of all the business logic, and Hookbox can take care of the comet.&lt;br /&gt;&lt;br /&gt;Side note: SF.net uses MongoDB and has ugly denormalization problems.&lt;br /&gt;&lt;br /&gt;Hookbox replaces what you would have had to do with RabbitMQ + Orbited.&lt;br /&gt;&lt;br /&gt;Michael called Hookbox a "web-enabled message queue".&lt;br /&gt;&lt;br /&gt;It's based on Eventlet.&lt;br /&gt;&lt;br /&gt;It uses the comet session protocol (CSP).&lt;br /&gt;&lt;br /&gt;It takes care of pub/sub, history, presence, moderation, etc.&lt;br /&gt;&lt;br /&gt;The documentation is not very complete.&lt;br /&gt;&lt;br /&gt;It doesn't support clustering.&lt;br /&gt;&lt;br /&gt;Hookbox is really good at delegating business logic to your application.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-4914141974949081452?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/4914141974949081452/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=4914141974949081452' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/4914141974949081452'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/4914141974949081452'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/pycon-hookbox-all-python-web-frameworks.html' title='PyCon: Hookbox: All Python web-frameworks, now real-time. Batteries Included'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-3487783175618544828</id><published>2011-04-04T17:39:00.000-07:00</published><updated>2011-04-04T17:52:35.970-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python3'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pycon2011'/><title type='text'>PyCon: Porting to Python 3</title><content type='html'>&lt;a href="http://us.pycon.org/2011/schedule/presentations/21/"&gt;Porting to Python 3&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://py3ksupport.appspot.com/"&gt;py3ksupport.appspot.com&lt;/a&gt; has a list of the top 50 Python projects and which of them support Python3.  As of the talk, 34% of the top 50 Python projects supported Python3.  I just checked, and it's up to 54%.&lt;br /&gt;&lt;br /&gt;There are multiple strategies to porting to Python3:&lt;ul&gt;&lt;li&gt;Only support Python3.&lt;/li&gt;&lt;li&gt;Use separate trees for Python2 and Python3.&lt;/li&gt;&lt;li&gt;Include both versions in a single download and set package_dir in setup.py&lt;/li&gt;&lt;li&gt;Implement "continuous conversion" using 2to3.  This approach is recommended for libraries.  Distribute can help.&lt;/li&gt;&lt;li&gt;Use a single codebase with no conversion.  This requires loads of compatibility hacks.  It's fun, but it's ugly.  Check out the "six" project if that's what you want to do.&lt;/li&gt;&lt;/ul&gt;Try 2to3 first.  If in doubt, use distribute.&lt;br /&gt;&lt;br /&gt;Libraries should port as soon as possible.&lt;br /&gt;&lt;br /&gt;In order to prepare, use Python 2.7 with the -3 flag.  Fix all the deprecation warnings.&lt;br /&gt;&lt;br /&gt;Use separate variables for string vs. binary data.&lt;br /&gt;&lt;br /&gt;Add "b" and "t" to file flags.&lt;br /&gt;&lt;br /&gt;For sort, switch from "cmp" to "key".&lt;br /&gt;&lt;br /&gt;Reminder: __foo__ is often pronounced "dunder foo" (aka "double under foo").&lt;br /&gt;&lt;br /&gt;Increase your test coverage.  This significantly helps!&lt;br /&gt;&lt;br /&gt;Use "2to3 -w ." to port an entire directory.&lt;br /&gt;&lt;br /&gt;In setup.py for distribute, use use_2to3=True.&lt;br /&gt;&lt;br /&gt;urllib2.urlparse is not right.  Use the urlparse module directly.&lt;br /&gt;&lt;br /&gt;Bytes vs. unicode is the hard part.&lt;br /&gt;&lt;br /&gt;Using == to compare bytes and str doesn't work.  b"a"[0] does not equal "a"[0].  Hence, the comparison will return False in a way that is unexpected and silent.&lt;br /&gt;&lt;br /&gt;Trying to support Python 2.5 and Python3 at the same time is REALLY hard.  It's much easier to support Python 2.7 and Python 3.0 at the same time.&lt;br /&gt;&lt;br /&gt;Seeking in a UTF-8 text file is slow because it involves decoding, not simple indexing.&lt;br /&gt;&lt;br /&gt;See &lt;a href="http://docs.python.org/py3k/howto/pyporting.html"&gt;http://docs.python.org/py3k/howto/pyporting.html&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;There's a book called "Porting to Python 3".&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-3487783175618544828?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/3487783175618544828/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=3487783175618544828' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/3487783175618544828'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/3487783175618544828'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/pycon-porting-to-python-3.html' title='PyCon: Porting to Python 3'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-7186150164728047715</id><published>2011-04-02T14:36:00.000-07:00</published><updated>2011-04-02T14:45:08.109-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python3'/><category scheme='http://www.blogger.com/atom/ns#' term='unicode'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pycon2011'/><title type='text'>PyCon: Status of Unicode in Python 3</title><content type='html'>&lt;a href="http://us.pycon.org/2011/schedule/presentations/266/"&gt;Status of Unicode in Python 3&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The talk was by Victor Stinner.  I went to dinner with him and a few other people.  He was a nice, French guy.&lt;br /&gt;&lt;br /&gt;The encoding for source code defaults to UTF-8 in Python 3.&lt;br /&gt;&lt;br /&gt;Surrogate escapes are a new feature in Python 3.2.  They let you deal with stuff that can't be decoded as UTF-8.  For instance, you can decode a filename string to a unicode object without losing data even if the decoding isn't clean.&lt;br /&gt;&lt;br /&gt;There are still issues to work on.&lt;br /&gt;&lt;br /&gt;Victor had bootstrap issues implementing all this stuff.&lt;br /&gt;&lt;br /&gt;It took a lot of hard work to improve all this stuff.&lt;br /&gt;&lt;br /&gt;Check out &lt;a href="https://github.com/haypo/unicode_book"&gt;Programming with Unicode&lt;/a&gt;, which is a book that Victor wrote.&lt;br /&gt;&lt;br /&gt;Victor has event more Unicode fixes in store for Python 3.3.&lt;br /&gt;&lt;br /&gt;Side note: I had an idea.  It'd be cool to create a tool that shows you a call tree for your application.  In the call tree, it can show you where all the encodes and decodes are done.  This would help you know where to do encodes and decodes.  This would really help when porting from Python2 to Python3.  Figuring out where to do encodes and decodes is a lot more subtle than you might think.  It doesn't always make sense to do it at the very edges of your application.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-7186150164728047715?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/7186150164728047715/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=7186150164728047715' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/7186150164728047715'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/7186150164728047715'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/pycon-status-of-unicode-in-python-3.html' title='PyCon: Status of Unicode in Python 3'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-3798277236886410010</id><published>2011-04-02T14:23:00.000-07:00</published><updated>2011-04-02T14:36:14.934-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='flask'/><category scheme='http://www.blogger.com/atom/ns#' term='pycon2011'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>PyCon: Opening the Flask</title><content type='html'>&lt;a href="http://us.pycon.org/2011/schedule/presentations/121/"&gt;Opening the Flask&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The talk was by Armin Ronacher, one of the authors of Flask.&lt;br /&gt;&lt;br /&gt;Flask started as an elaborate April Fool's joke.&lt;br /&gt;&lt;br /&gt;The guys who did Flask did a lot of cool stuff before doing Flask, such as Jinja2.&lt;br /&gt;&lt;br /&gt;Flask was originally Jinja2, Werkzeug, and some glue code cleverly embedded in a single file download.&lt;br /&gt;&lt;br /&gt;Marketing beats quality.&lt;br /&gt;&lt;br /&gt;Originally, they didn't do any testing or any code review.  That's changed.&lt;br /&gt;&lt;br /&gt;They wanted to restart with good docs and good tests.  They documentation and tests are pretty good these days.&lt;br /&gt;&lt;br /&gt;The name "Flask" is a play on another framework, "Bottle".&lt;br /&gt;&lt;br /&gt;Flask is still based on Werkzeug and Jinja2.&lt;br /&gt;&lt;br /&gt;Flask can use Blinker as a signalling system (?).&lt;br /&gt;&lt;br /&gt;Flask is about 800 lines of code, 1500 lines of tests, and 200 A4-sized pages of documentation.&lt;br /&gt;&lt;br /&gt;There are lots of extensions.&lt;br /&gt;&lt;br /&gt;Flask uses decorator-based routes, but there are other options.&lt;br /&gt;&lt;br /&gt;Flask supports URL routing as well as URL generation.&lt;br /&gt;&lt;br /&gt;Jinja2 uses template inheritance.&lt;br /&gt;&lt;br /&gt;They use context locals to make writing apps simper.  Armin suggests that you might as well embrace context locals--you'll end up needing them anyway.&lt;br /&gt;&lt;br /&gt;Armin is very against import-time side effects.&lt;br /&gt;&lt;br /&gt;Flask is in favor of explicit application setup.  For Flask, this has occasionally led to circular imports that they had to find workarounds for.&lt;br /&gt;&lt;br /&gt;WSGI middleware works with Flask.&lt;br /&gt;&lt;br /&gt;Flask has several advantages over Bottle.&lt;br /&gt;&lt;br /&gt;You can override various aspects of how Flask works.  For instance, you can use the "@app.before_request" and "@app.after_request" decorators.&lt;br /&gt;&lt;br /&gt;They make use of lots of extensions.&lt;br /&gt;&lt;br /&gt;Flask has a goal of keeping the core very small.&lt;br /&gt;&lt;br /&gt;You can use other templating engines if you want, but Jinja2 will always be a dependency just in case any extensions need it.&lt;br /&gt;&lt;br /&gt;Documentation matters.&lt;br /&gt;&lt;br /&gt;Communication matters.  It's very easy to submit issues to Flask.  You don't even need to create an account.&lt;br /&gt;&lt;br /&gt;It's important to keep committing to projects otherwise the project looks dead.&lt;br /&gt;&lt;br /&gt;Consistency matters.&lt;br /&gt;&lt;br /&gt;It's important that even the documentation be beautiful.&lt;br /&gt;&lt;br /&gt;Flask uses Sphinx for documentation.&lt;br /&gt;&lt;br /&gt;Armin didn't have a strong rule of thumb for when you should use Flask vs. Pyramid.  Neither the Flask guys nor the Pyramid guys are overly religious about their frameworks.&lt;br /&gt;&lt;br /&gt;The audience turnout was about the same for both the Flask and Pyramid talks.&lt;br /&gt;&lt;br /&gt;The Flask guys are thinking about redoing the code for reverse routing.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-3798277236886410010?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/3798277236886410010/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=3798277236886410010' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/3798277236886410010'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/3798277236886410010'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/pycon-opening-flask.html' title='PyCon: Opening the Flask'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-2847581232527255947</id><published>2011-04-02T13:36:00.000-07:00</published><updated>2011-04-02T14:23:20.627-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pylons'/><category scheme='http://www.blogger.com/atom/ns#' term='pycon2011'/><category scheme='http://www.blogger.com/atom/ns#' term='turbogears'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>PyCon: State of Pylons/TurboGears 2/repoze.bfg</title><content type='html'>&lt;a href="http://us.pycon.org/2011/schedule/presentations/60/"&gt;State of Pylons/TurboGears 2/repoze.bfg&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;There are about 2000 people on the Pylons mailing list.&lt;br /&gt;&lt;br /&gt;Ben Bangert said that Pylons relies too heavily on subclassing.  When people subclass and override stuff in a framework's parent class, it makes it difficult to alter the parent class without breaking people's code.&lt;br /&gt;&lt;br /&gt;Side note: The new Pyramid T-shirt is beautifully done, but evil aliens (or anything else for that matter) are a big turn off for me.&lt;br /&gt;&lt;br /&gt;Pylons is big, but Ben said that too much of Pylons is dependent on him.&lt;br /&gt;&lt;br /&gt;Pylons is merging with repoze.bfg.  It's going to be called "The Pylons Project", but it's based on the code in repoze.bfg.&lt;br /&gt;&lt;br /&gt;Chris McDonough wrote repoze.bfg.  It's a great, but relatively unknown framework.  Pylons has better name recognition.&lt;br /&gt;&lt;br /&gt;TurboGears 2 is built on Pylons.&lt;br /&gt;&lt;br /&gt;The new framework is called Pyramid and is part of "The Pylons Project".&lt;br /&gt;&lt;br /&gt;TurboGears 2 and Pylons are going to be maintained together so as not to strand a bunch of legacy projects.&lt;br /&gt;&lt;br /&gt;Side note: There were a lot of big beards at this PyCon.  Big beards have always been popular among hackers, but it's even more popular right now.  I wonder if this has anything to do with the Giants pitching staff and with the Giants winning the World Series.&lt;br /&gt;&lt;br /&gt;TurboGears will experiment and innovate on top of Pyramid in the same way it used to experiment and innovate on top of Pylons.&lt;br /&gt;&lt;br /&gt;repoze.bfg has a catchphrase:  "Plumbing Zope 2 into the WSGI pipeline."  It actually doesn't take any code from Zope, but it does borrow some ideas.&lt;br /&gt;&lt;br /&gt;There are about 200 people on the repoze.bfg mailing list.  It has 100% statement coverage.  100% of the code is documented--if a feature isn't documented, then it doesn't exist.  There are 80 committers to the repoze.bfg project!&lt;br /&gt;&lt;br /&gt;Pyramid is just repoze.bfg renamed.&lt;br /&gt;&lt;br /&gt;There is a Paster template for Pyramid--multiple actually.&lt;br /&gt;&lt;br /&gt;These are the features in repoze.bfg:  it maps URLs to code; it has an authentication framework; it supports I18N; it supports single file applications as well as larger projects; it makes use of PasteDeploy; it has unit, functional, and integration testing; it uses WSGI; it has great docs; it supports multiple templating engines; it supports Google App Engine; it has plugins; it can serve static files; it has sessions; it has cross site request forgery (CSRF) protection; it supports events; it has good exceptions handling; it supports WSGI middleware; it's extremely fast; it has 100% statement coverage.&lt;br /&gt;&lt;br /&gt;One reason that it is so fast is that a lot of work is done at startup so that less work needs to be done at runtime.&lt;br /&gt;&lt;br /&gt;It is not a full-stack framework.  It has no persistence layer.&lt;br /&gt;&lt;br /&gt;It currently has 16 dependencies, but single file applications are still possible.&lt;br /&gt;&lt;br /&gt;It is "unopinionated".&lt;br /&gt;&lt;br /&gt;Chris (the repoze.bfg guy) said, "I'm a Django fan.  I love Django."  Obviously, he doesn't think Django is appropriate for all situations.&lt;br /&gt;&lt;br /&gt;Pyramid != Zope.  It's not Pylons.  It's not MVC.  Chris hates that term.&lt;br /&gt;&lt;br /&gt;Flask may be more appropriate for small projects.  Pyramid may be a better fit for larger applications.&lt;br /&gt;&lt;br /&gt;Pyramid exposes plug points all over the place.&lt;br /&gt;&lt;br /&gt;Pyramid supports row-level security.&lt;br /&gt;&lt;br /&gt;There's a Pyramid OpenID library.&lt;br /&gt;&lt;br /&gt;They use Lettuce together with a web driver.  It can work without Selenium.&lt;br /&gt;&lt;br /&gt;WebTest from Ian Bicking looks interesting.&lt;br /&gt;&lt;br /&gt;The next version of Pyramid will target Python3.&lt;br /&gt;&lt;br /&gt;Paste is in a state of flux, but it's too important to be abandoned.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-2847581232527255947?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/2847581232527255947/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=2847581232527255947' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/2847581232527255947'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/2847581232527255947'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/pycon-state-of-pylonsturbogears.html' title='PyCon: State of Pylons/TurboGears 2/repoze.bfg'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-2535556159953015473</id><published>2011-04-01T11:35:00.000-07:00</published><updated>2011-04-01T11:44:43.485-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OpenStack'/><category scheme='http://www.blogger.com/atom/ns#' term='cloud'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pycon2011'/><title type='text'>PyCon: Python - The Secret Sauce in the Open Cloud</title><content type='html'>&lt;a href="http://us.pycon.org/2011/schedule/presentations/248/"&gt;Python - The Secret Sauce in the Open Cloud&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This talk was given by Jason Huggins, the creator of Selenium.&lt;br /&gt;&lt;br /&gt;The talk was about OpenStack which is open source, cloud computing software.&lt;br /&gt;&lt;br /&gt;It used to be that OpenStack was very difficult to get started with.  Things are getting much simpler.&lt;br /&gt;&lt;br /&gt;Cloud computing is still on top of a huge hype wave.&lt;br /&gt;&lt;br /&gt;OpenStack allows you to run your own cloud.&lt;br /&gt;&lt;br /&gt;Side note: there were a lot more women this year.&lt;br /&gt;&lt;br /&gt;You can use whichever hypervisor you want:  KVM, QEMU, Xen, VirtualBox, VMware, etc.&lt;br /&gt;&lt;br /&gt;OpenStack glues together a bunch of third-party parts.  It has an API.  It uses a database.  It has a messaging protocol.  It has a lot of glue code.&lt;br /&gt;&lt;br /&gt;He said that his most important slide was a link to &lt;a href="http://ansolabs.com/deploy"&gt;http://ansolabs.com/deploy&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Install VirtualBox.&lt;br /&gt;&lt;br /&gt;Install Vagrant.  It's an API on top of VirtualBox.&lt;br /&gt;&lt;br /&gt;Get chef recipes from the OpenStack cookbooks.&lt;br /&gt;&lt;br /&gt;Using OpenStack has become substantially easier during the last four months.&lt;br /&gt;&lt;br /&gt;What out for disk I/O.  Use copy-on-write disk images.&lt;br /&gt;&lt;br /&gt;Make the VM as small as possible.&lt;br /&gt;&lt;br /&gt;When asked about Apple VMs, he said, "Apple is like Oracle:  Oracle doesn't scale financially."&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-2535556159953015473?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/2535556159953015473/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=2535556159953015473' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/2535556159953015473'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/2535556159953015473'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/pycon-python-secret-sauce-in-open-cloud.html' title='PyCon: Python - The Secret Sauce in the Open Cloud'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-8962253377591161417</id><published>2011-04-01T11:23:00.000-07:00</published><updated>2011-04-01T11:34:56.718-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pycon2011'/><category scheme='http://www.blogger.com/atom/ns#' term='celery'/><title type='text'>PyCon: Distributed Tasks with Celery</title><content type='html'>&lt;a href="http://us.pycon.org/2011/schedule/presentations/1/"&gt;Distributed Tasks with Celery&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Celery is an asynchronous job queue based on distributed message passing.&lt;br /&gt;&lt;br /&gt;ShootQ looks like a useful site for professional photographers.&lt;br /&gt;&lt;br /&gt;Celery takes care of scheduling.  It also handles errors with retries.&lt;br /&gt;&lt;br /&gt;It works with Web hooks too, although normally you can stick to just plain Python.&lt;br /&gt;&lt;br /&gt;It can control nodes via remote control by broadcasting messages.&lt;br /&gt;&lt;br /&gt;It can run on top of Reddis, etc. as well.&lt;br /&gt;&lt;br /&gt;Use CELERY_ALWAYS_EAGER when unit testing.&lt;br /&gt;&lt;br /&gt;RabbitMQ does NOT like running out of memory!  Really bad things happen.&lt;br /&gt;&lt;br /&gt;RabbitMQ doesn't really have replication.&lt;br /&gt;&lt;br /&gt;Side note: Convore is really popular.  I had never even heard of it, and it seemed like about half the people present were using it.  It's a web-based chat system.  It's a mix of Twitter and IRC.  It's very threaded.&lt;br /&gt;&lt;br /&gt;Celery doesn't yet have priority queues.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-8962253377591161417?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/8962253377591161417/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=8962253377591161417' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/8962253377591161417'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/8962253377591161417'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/pycon-distributed-tasks-with-celery.html' title='PyCon: Distributed Tasks with Celery'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-4028642045911769469</id><published>2011-04-01T11:11:00.000-07:00</published><updated>2011-04-01T11:21:10.167-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pycon2011'/><title type='text'>PyCon: Keynote</title><content type='html'>&lt;h5&gt;Steve Holden: Python Software Foundation Chairman&lt;/h5&gt;The attendance in 2009 was really down; the PSF lost a lot of money.  2010 was better.  2011 has the biggest attendance ever.  There were 1400+ registrations.&lt;h5&gt;Dr. Hilary Mason from Bit.ly&lt;/h5&gt;List comprehensions are the most popular feature in Python according to her data.&lt;br /&gt;&lt;br /&gt;The Kinect For Xbox 360 from Microsoft is awesome.&lt;br /&gt;&lt;br /&gt;She recommended the book "Collective Intelligence".&lt;br /&gt;&lt;br /&gt;Side note: Tavis Rudd has a totally awesome speech recognition setup.  He has created his own spoken language / grammar with words customized to doing everything, especially controlling Emacs.  Listening to him speak, it sounds like a foreign language, but he's able to control his machine very quickly.  He had to develop this because he was suffering from RSI very badly.  He has a Mac with two virtual machines.  One runs Windows with Dragon Speak.  The other runs Linux.  He logs into Linux from Windows.  He has high-level words that allow him to operate very quickly.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-4028642045911769469?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/4028642045911769469/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=4028642045911769469' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/4028642045911769469'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/4028642045911769469'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/pycon-keynote.html' title='PyCon: Keynote'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-7594295292771395760</id><published>2011-04-01T10:45:00.000-07:00</published><updated>2011-04-01T11:11:38.073-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python3'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pycon2011'/><title type='text'>PyCon: Cooking with Python 3</title><content type='html'>&lt;a href="http://us.pycon.org/2011/schedule/presentations/274/"&gt;Cooking with Python 3&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Side note: I met a guy from Lockheed Martin who was working on the Joint Strike Fighter using C++.  He said that "malloc" and "new" were not allowed; everything had to be statically allocated.&lt;br /&gt;&lt;br /&gt;Python3 now uses "except ValueError as e:" instead of "except ValueError, e:".&lt;br /&gt;&lt;br /&gt;There are a bunch of really subtle changes in Python3, such as the fact that some_exception[0] no longer works.&lt;br /&gt;&lt;br /&gt;Python3 chains exception messages if an exception occurs while another exception is being handled.&lt;br /&gt;&lt;br /&gt;If you write "[x * x for x in nums]; print x", x won't be in scope.  This is a bit surprising.&lt;br /&gt;&lt;br /&gt;The exception object does &lt;b&gt;not&lt;/b&gt; escape the except block.  It won't be defined outside the block.  This is really surprising in situations like:&lt;pre&gt;e = True&lt;br /&gt;try:&lt;br /&gt;    ...&lt;br /&gt;except Exception as e:&lt;br /&gt;    ....&lt;br /&gt;print e&lt;/pre&gt;It'll say that e isn't defined when you try to print it.  It was flagged as a bug in the bug tracker, but then closed as invalid.&lt;br /&gt;&lt;br /&gt;Exec has been locked down a lot in Python3.  For instance, this no longer works:&lt;pre&gt;def foo():&lt;br /&gt;    exec("a = 23")&lt;br /&gt;    print(a)&lt;br /&gt;foo()&lt;/pre&gt;execfile is gone.&lt;br /&gt;&lt;br /&gt;reload has moved to the imp module.&lt;br /&gt;&lt;br /&gt;There is now a new set literal notation, "fruit = {'apple', 'banana'}", although you can't use it to create an empty set for obvious reasons.&lt;br /&gt;&lt;br /&gt;There are now set comprehensions: "upper_fruits = {f.upper() for f in fruits}".&lt;br /&gt;&lt;br /&gt;There are now dict comprehensions: "d = {name.upper(): value for name, value in stock.items()}".&lt;br /&gt;&lt;br /&gt;"some_dict.keys()" gives you a "view" on the dict, not a list.&lt;br /&gt;&lt;br /&gt;"zip" now produces an iterator, not a list.  This sort of thing is very common in Python3.&lt;br /&gt;&lt;br /&gt;You can now write: "a, *middle_items, b = [1, 2, 3, 4]".&lt;br /&gt;&lt;br /&gt;The "*" in the following forces all the subsequent arguments to be keyword arguments (meaning you must specify the keyword when passing them): "def foo(x, *, y)".&lt;br /&gt;&lt;br /&gt;Function annotations are new.  They look like "def add(x: int, y: int) -&gt; int:...".  By default, they don't actually do anything.  However, libraries can create whatever meaning they want for them.  The thing after the colon can be any Python expression.  This adds an __annotations__ member to the function object.&lt;br /&gt;&lt;br /&gt;You could easily build a function decorator that uses the annotations to do optional static type checking.&lt;br /&gt;&lt;br /&gt;If multiple libraries use annotations for different things, you could end up with conflicting uses of function annotations.&lt;br /&gt;&lt;br /&gt;Use StringIO for strings and BytesIO for bytes.&lt;br /&gt;&lt;br /&gt;This is a byte object: b"byte".&lt;br /&gt;&lt;br /&gt;argparse is a new argument parser descended from optparse.&lt;br /&gt;&lt;br /&gt;The division operator no longer truncates.  It gives you a float.  If you want integer division, use "1 // 3".&lt;br /&gt;&lt;br /&gt;Use "2to3 -w ." to port an entire directory to Python3.&lt;br /&gt;&lt;br /&gt;"unicode" and b"bytes" are completely separate.&lt;br /&gt;&lt;br /&gt;2to3 is not a magic bullet.  It's actually pretty stupid.&lt;br /&gt;&lt;br /&gt;The bytes vs. unicode thing is by far the hardest part of porting.&lt;br /&gt;&lt;br /&gt;We tried porting redis-py to Python3.  I now think that the unicode vs. bytes problem is going to making porting extremely difficult, and it's going to lead to a lot of bugs.&lt;br /&gt;&lt;br /&gt;The following is False 'b"H"[0] == "H"'.  That's because it's actually equal to 72.  This is going to lead to a lot of subtle bugs during porting.&lt;br /&gt;&lt;br /&gt;There is a unicodedata module to look up unicode character metadata.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-7594295292771395760?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/7594295292771395760/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=7594295292771395760' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/7594295292771395760'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/7594295292771395760'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/04/pycon-cooking-with-python-3.html' title='PyCon: Cooking with Python 3'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-2198031981490011439</id><published>2011-03-31T16:43:00.001-07:00</published><updated>2011-03-31T16:49:00.453-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='books'/><title type='text'>Books: Coders at Work</title><content type='html'>I just finished reading &lt;a href="http://codersatwork.com/"&gt;Coders at Work:&lt;/a&gt;&lt;blockquote&gt;Based on nearly eighty hours of conversations with fifteen all-time great programmers and computer scientists, the Q&amp;A interviews in Coders at Work provide a multifaceted view into how great programmers learn to program, how they practice their craft, and what they think about the future of programming.&lt;/blockquote&gt;In short, I really enjoyed this book.  It was relaxing, stimulating, humbling, and enabling all at the same time.&lt;br /&gt;&lt;br /&gt;One thing that really stood out was that programmers used to be able to understand systems extremely deeply because they could fit the whole thing in their heads.  In contrast, it simply boggles my mind to think of how many lines of code are involved in showing Google Maps to me (think of my laptop, all the routers, Google's servers, the JavaScript, the server-side code, etc.).  If you're up for some light technical reading, this book is highly recommended.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-2198031981490011439?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/2198031981490011439/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=2198031981490011439' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/2198031981490011439'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/2198031981490011439'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/03/books-coders-at-work.html' title='Books: Coders at Work'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-5254137883403990573</id><published>2011-03-25T11:44:00.000-07:00</published><updated>2011-03-25T11:46:14.539-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><title type='text'>The Zen of Testing</title><content type='html'>Tests that use a style you disagree with are better than no tests at all.&lt;br /&gt;&lt;br /&gt;Tests that catch more errors are better than tests that catch fewer errors.&lt;br /&gt;&lt;br /&gt;Getting more done with less effort is better than getting less done with more effort.&lt;br /&gt;&lt;br /&gt;Tests that are readable are better than tests that are unreadable.  &lt;br /&gt;&lt;br /&gt;Tests that are easy to update are better than tests that are hard to update.&lt;br /&gt;&lt;br /&gt;Tests that fail when appropriate are better than tests that fail when inappropriate.&lt;br /&gt;&lt;br /&gt;Tests that test the stuff you care about are better than tests that test stuff you don't care about.&lt;br /&gt;&lt;br /&gt;Tests that take little effort and catch a lot are better than tests that take a lot of effort and catch little.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-5254137883403990573?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/5254137883403990573/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=5254137883403990573' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/5254137883403990573'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/5254137883403990573'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/03/zen-of-testing.html' title='The Zen of Testing'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-4605432708203348250</id><published>2011-03-23T23:07:00.000-07:00</published><updated>2011-03-24T05:28:20.292-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='flask'/><category scheme='http://www.blogger.com/atom/ns#' term='twilio'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><category scheme='http://www.blogger.com/atom/ns#' term='lettuce'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Python: Behavioral Driven Development for Telephone Applications Using Lettuce, Flask, and Twilio</title><content type='html'>I'm going to be giving a talk tomorrow night at &lt;a href="http://baypiggies.net/"&gt;BayPiggies&lt;/a&gt; called "Behavioral Driven Development for Telephone Applications Using Lettuce, Flask, and Twilio".  Here's the description:&lt;blockquote&gt;Behavioral driven development is a style of programming popular in the Ruby world using tools such as Cucumber and Webrat.  In this talk, I'll show how the same tricks can be used in Python too, using a library called Lettuce.  I'll also show off Flask which is a new micro web framework.  Last of all, I'll cover Twilio which is an API that makes it easy to build telephone-based applications using web technologies.&lt;/blockquote&gt;I'm going to be showing code samples from my demo application &lt;a href="https://github.com/jjinux/pyteladventure"&gt;PyTeladventure&lt;/a&gt;.  If you can't make it to the talk, you might way to have a peek at the code.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-4605432708203348250?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/4605432708203348250/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=4605432708203348250' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/4605432708203348250'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/4605432708203348250'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/03/python-behavioral-driven-development.html' title='Python: Behavioral Driven Development for Telephone Applications Using Lettuce, Flask, and Twilio'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-1791009600386190610</id><published>2011-03-21T11:46:00.000-07:00</published><updated>2011-03-22T15:21:51.351-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='dell'/><category scheme='http://www.blogger.com/atom/ns#' term='hardware'/><title type='text'>Hardware: Fixed a Dell Inspiron 1545</title><content type='html'>&lt;div style="float: right"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/-T_YiGXmeha0/TYkgr3AqM7I/AAAAAAAAAIE/-BEoQbdgfrY/s1600/2011-03-21%2B12.39.50.jpg"&gt;&lt;img style="margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 240px; height: 320px;" src="http://1.bp.blogspot.com/-T_YiGXmeha0/TYkgr3AqM7I/AAAAAAAAAIE/-BEoQbdgfrY/s320/2011-03-21%2B12.39.50.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5587032750602990514" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/-djK7nVBGrvY/TYkggcyv3ZI/AAAAAAAAAH8/wp1kZ1jjEO8/s1600/2011-03-21%2B12.41.01.jpg"&gt;&lt;img style="margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 240px; height: 320px;" src="http://4.bp.blogspot.com/-djK7nVBGrvY/TYkggcyv3ZI/AAAAAAAAAH8/wp1kZ1jjEO8/s320/2011-03-21%2B12.41.01.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5587032554586758546" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;My buddy Adam Ulvi loaned me a laptop since I didn't have one.  (My last day at Twilio was last Thursday.)  He had a Dell Inspiron 1545 lying around, and he was nice enough to mail it to me.  The only catch was that there was some breakage around the display near the hinge.  Apparently, this is extremely common, so I decided to try to fix it.&lt;br /&gt;&lt;br /&gt;I bought a replacement part from &lt;a href="http://Parts-People.com"&gt;Parts-People.com&lt;/a&gt;.  The part I bought was the display cover, and it came with replacement hinges.  It cost about $50.  I went to Dell's site to find the service manual.  It was actually a fairly involved repair, but I managed to get it done.  My only regret was that I didn't buy a replacement part for the piece of plastic that frames the front side of the screen.  I didn't notice that it was broken too, so I had to make due with some carefully applied electrical tape.  Fortunate, it doesn't look as ghetto as it sounds.&lt;br /&gt;&lt;br /&gt;Anyway, the repair went really well.  The laptop looks pretty much brand new, with the exception of one small piece of electrical tape that blends in pretty well anyway.  The new lid made a big difference.  I've learned two things from this experience:  1) Dell's build quality sucks (which is something that my buddies have been telling me for a while now).  2) Dell's repair manuals are incredibly good.  Following the instructions was a piece of cake, even it did take me two hours.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-1791009600386190610?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/1791009600386190610/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=1791009600386190610' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/1791009600386190610'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/1791009600386190610'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/03/hardware-fixed-dell-inspiron-1545.html' title='Hardware: Fixed a Dell Inspiron 1545'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-T_YiGXmeha0/TYkgr3AqM7I/AAAAAAAAAIE/-BEoQbdgfrY/s72-c/2011-03-21%2B12.39.50.jpg' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-8976882602332042696</id><published>2011-03-15T15:34:00.000-07:00</published><updated>2011-03-15T15:37:52.003-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='lubuntu'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>Linux: lubuntu</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/-0SZc-mLy8S4/TX_qFrTJryI/AAAAAAAAAHs/DlswrRh42kw/s1600/lubuntu.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 320px;" src="http://1.bp.blogspot.com/-0SZc-mLy8S4/TX_qFrTJryI/AAAAAAAAAHs/DlswrRh42kw/s400/lubuntu.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5584439446206000930" /&gt;&lt;/a&gt;&lt;br /&gt;I decided to give &lt;a href="http://lubuntu.net/"&gt;lubuntu&lt;/a&gt; a try:&lt;blockquote&gt;lubuntu is a faster, more lightweight and energy saving variant of Ubuntu using LXDE, the Lightweight X11 Desktop Environment.&lt;/blockquote&gt;If you already have Ubuntu installed, trying lubuntu is really easy; just run "sudo apt-get install lubuntu-desktop".&lt;br /&gt;&lt;br /&gt;In summary, it's very pretty, super fast, and crazy small.  In fact, its memory usage was almost laughable considering I was running it on a 4GB MacBook Pro.  I think my total memory usage was something in the 200-300MB range.&lt;br /&gt;&lt;br /&gt;The downside is that there are a lot of things that I've grown accustomed to in Ubuntu that I can't live without.  Ubuntu has a GUI to swap the capslock key with the control key, and it has a GUI to tweak my touchpad and power management settings.  There are probably ways to configure these things by hand under lubuntu, but I've grown mildly impatient in my old age ;) &lt;br /&gt;&lt;br /&gt;The biggest challenge for me was that lubuntu doesn't know about encrypted home directories.  I have an encrypted home directory, and Ubuntu knows that it has to run ecryptfs-mount-private when I log in; in fact, it doesn't even need to ask me for my password again since it does it as part of the login process.  When I ran lubuntu, I had to log in, run ecryptfs-mount-private (typing in my password again), log out, and then log back in.&lt;br /&gt;&lt;br /&gt;I have a couple more tips.  To access the OpenBox menus, middle click on the desktop.  If you decide to install lubuntu, don't tell it to switch to lxdm.  Otherwise, if you remove lubuntu, you'll end up with a borked gdm setup.  To fix it, you'll need to remove and reinstall gdm.&lt;br /&gt;&lt;br /&gt;In summary, I really like lubuntu.  It makes my machine feel lightning fast, faster than any other computer I've ever owned--at least until I fire up NetBeans ;)  I'm not sure if they'll fix the things I mentioned, but if those things don't affect you, I think lubuntu is worth a try.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-8976882602332042696?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/8976882602332042696/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=8976882602332042696' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/8976882602332042696'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/8976882602332042696'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/03/linux-lubuntu.html' title='Linux: lubuntu'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-0SZc-mLy8S4/TX_qFrTJryI/AAAAAAAAAHs/DlswrRh42kw/s72-c/lubuntu.png' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-531185077189893267</id><published>2011-03-13T20:18:00.000-07:00</published><updated>2011-03-13T20:47:53.683-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GNOME'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='awesome'/><category scheme='http://www.blogger.com/atom/ns#' term='xmonad'/><title type='text'>Linux: Awesome is Awesome!</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/-n5QR-m5zgKU/TX2JXR7y9XI/AAAAAAAAAHk/EIAghNEGyRU/s1600/awesome_screenshot.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 250px;" src="http://3.bp.blogspot.com/-n5QR-m5zgKU/TX2JXR7y9XI/AAAAAAAAAHk/EIAghNEGyRU/s400/awesome_screenshot.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5583770146053485938" /&gt;&lt;/a&gt;I decided to give &lt;a href="http://awesome.naquadah.org/"&gt;Awesome&lt;/a&gt; a try.  Awesome is a tiling window manager like xmonad.  I've been using it for about a month, and I like it a lot.  &lt;br /&gt;&lt;br /&gt;It integrates with GNOME much better than dwm.  Most users of dwm don't use GNOME, but I do.  Its default settings are a lot nicer than xmonad's.  When I use xmonad, I spend all my time futzing with my .xmonad.hs, but I haven't had to tweak Awesome at all the whole time I've used it.  For instance, Awesome comes with a ton of layouts built in.  &lt;br /&gt;&lt;br /&gt;Getting gvim to work perfectly under xmonad required some tweaking, and I couldn't figure out how to get it to work perfectly under dwm.  I know that this is gvim's fault because it doesn't follow the exact size prescribed by the window manager, so the bottom part of the screen gets messed up.  I'm sure most dwm users don't use gvim--they probably run vim in a terminal.  It worked perfectly under Awesome.&lt;br /&gt;&lt;br /&gt;Getting NetBeans to work well took even more work, and even now it sometimes freezes in predictable (and avoidable) ways.  I had to "sudo apt-get install suckless-tools", and then I added "xterm -e wmname LG3D" to my startup applications (i.e. my GNOME session).  Finally, I switched to JDK 1.7.0 and edited /usr/local/netbeans-6.9.1/etc/netbeans.conf so that "netbeans_jdkhome="/usr/local/jdk-1.7.0".&lt;br /&gt;&lt;br /&gt;Since Awesome and GNOME each have their own panels, I did some tweaking to make them get along better.  I have Awesome's panel on the top, and one GNOME panel on the bottom.  I got rid of things from the GNOME panel that were already provided by the Awesome panel.  I'm pretty pleased with the result, and I didn't even have to futz with any configuration files to do it.&lt;br /&gt;&lt;br /&gt;I really like the keybindings in Awesome.  Since I had already played with xmonad and dwm, I didn't really have to re-learn the keybindings for Awesome since they're so similar.  I just had to realize that all the keys were based on the command key (i.e. Mod4) instead of the control key.&lt;br /&gt;&lt;br /&gt;I especially like having a different set of tags (i.e. virtual desktops) per monitor.  That's exactly the paradigm I like the most.  I use a big monitor for most things, and I use a lot of different tags.  I use my laptop screen just for instant messaging applications, and I only use a single tag.&lt;br /&gt;&lt;br /&gt;Plugging in or unplugging my monitor is a pain, especially since I have to tweak the Nvidia settings every time.  I know it's a bit "ghetto", but my solution was to restart X every time I want to plug in or unplug my monitor.  I think this actually takes less time than shuffling my windows around and futzing with my Nvidia settings.&lt;br /&gt;&lt;br /&gt;I have found one Awesome bug.  When I first login, my cursor is a clock, and it doesn't go away.  If I play with the menus, it does.  It's not a big deal, so I don't care.&lt;br /&gt;&lt;br /&gt;The default layout is floating, and each tag has its own layout.  This default works well for me.  I only have to press Command-space to switch to tiling.&lt;br /&gt;&lt;br /&gt;One of these days, I might give Qtile a try since it's written in Python and my buddy really likes it.  However, in the meantime, I'm pretty happy with Awesome.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-531185077189893267?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/531185077189893267/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=531185077189893267' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/531185077189893267'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/531185077189893267'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/03/linux-awesome-is-awesome.html' title='Linux: Awesome is Awesome!'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-n5QR-m5zgKU/TX2JXR7y9XI/AAAAAAAAAHk/EIAghNEGyRU/s72-c/awesome_screenshot.png' height='72' width='72'/><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-6935716182607386004</id><published>2011-03-13T20:13:00.001-07:00</published><updated>2011-03-13T20:18:26.074-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='personal'/><category scheme='http://www.blogger.com/atom/ns#' term='linkedin'/><title type='text'>Personal: Should I Leave LinkedIn?</title><content type='html'>I got email from LinkedIn:&lt;blockquote&gt;Give your productivity a boost - for free!&lt;br /&gt;Exclusive offer: Try LinkedIn Premium free for 1 month*&lt;br /&gt;&lt;br /&gt;Dear Shannon -jj,&lt;br /&gt;&lt;br /&gt;Find and manage high quality contacts with LinkedIn Premium. Get started today and get 1 month free!.&lt;br /&gt;&lt;br /&gt;LinkedIn Premium helps you be great at what you do, with tools such as:&lt;br /&gt;&lt;br /&gt;• InMail: Contact anyone on LinkedIn without an introduction - response guaranteed!**&lt;br /&gt;• Expanded Profile Views: See expanded profiles of everyone on LinkedIn, even people outside of your network&lt;br /&gt;• Who's Viewed My Profile: Get the inside scoop on how you're viewed professionally by accessing the complete list&lt;br /&gt;&lt;br /&gt;You'll also get a lot more including up to 700 profiles per search.&lt;br /&gt;&lt;br /&gt;Your exclusive offer expires soon so get started today!&lt;/blockquote&gt;This means that LinkedIn is going to be making money off of spammy recruiters at my expense.  That is really a violation of my trust.  Am I wrong?  I'm seriously thinking about deleting my account.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-6935716182607386004?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/6935716182607386004/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=6935716182607386004' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/6935716182607386004'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/6935716182607386004'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/03/personal-should-i-leave-linkedin.html' title='Personal: Should I Leave LinkedIn?'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-6420798166265528869</id><published>2011-03-03T08:09:00.000-08:00</published><updated>2011-03-03T09:03:55.938-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='humor'/><title type='text'>Humor: Donald Knuth Jokes</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/-shpY8VHtm2w/TW_GDkiCAGI/AAAAAAAAAHc/4N7nOukwar8/s1600/knuth-is-my-homeboy.jpg"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 190px; height: 320px;" src="http://2.bp.blogspot.com/-shpY8VHtm2w/TW_GDkiCAGI/AAAAAAAAAHc/4N7nOukwar8/s320/knuth-is-my-homeboy.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5579896227983655010" /&gt;&lt;/a&gt;&lt;br /&gt;At the age of two, Donald Knuth implemented Conway's Game of Life on Babbage's difference engine which was pretty impressive since Conway hadn't been born yet...neither had Babbage.&lt;br /&gt;&lt;br /&gt;The next version of TeX will be self hosting...and will run on bare metal.&lt;br /&gt;&lt;br /&gt;Skynet is actually an out of control TeX macro.&lt;br /&gt;&lt;br /&gt;Knuth can store all integers between 0 and 127 in 4 bits.&lt;br /&gt;&lt;br /&gt;Knuth invented binary.&lt;br /&gt;&lt;br /&gt;Chuck Norris is actually an android that Donald Knuth created.  He was funded by DARPA.&lt;br /&gt;&lt;br /&gt;Knuth once wrote, "Email is a wonderful thing for people whose role in life is to be on top of things. But not for me; my role is to be on the bottom of things."  He succeeded.&lt;br /&gt;&lt;br /&gt;Knuth started to dabble in botany in his backyard.  He gave it up, though, when all his trees ended up with squareroots.&lt;br /&gt;&lt;br /&gt;Knuth &lt;i&gt;is&lt;/i&gt; the last highlander.&lt;br /&gt;&lt;br /&gt;Knuth created the Antikythera device when he was a young lad vacationing in Greece.&lt;br /&gt;&lt;br /&gt;When Knuth wants to speed up an algorithm, he doesn't code in assembly--he codes in binary.&lt;br /&gt;&lt;br /&gt;Donald Knuth enjoys coding in Python, but he prefers to write the .pyc files directly.&lt;br /&gt;&lt;br /&gt;One time Knuth decided to crosscompile his algorithms from binary to quaternary.  In the process, he invented DNA.&lt;br /&gt;&lt;br /&gt;Knuth's brain is the most powerful source of energy known to man.  Unfortunately, harnessing it would destroy mankind.&lt;br /&gt;&lt;br /&gt;Knuth recently created a new sorting algorithm.  Its performance is O(1).&lt;br /&gt;&lt;br /&gt;Knuth invented literate programming so that TeX could be compiled by less capable compilers.  Knuth's own compiler compiles the English.&lt;br /&gt;&lt;br /&gt;If you can think of more, please leave them in the comments below :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-6420798166265528869?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/6420798166265528869/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=6420798166265528869' title='25 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/6420798166265528869'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/6420798166265528869'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/03/humor-donald-knuth-jokes.html' title='Humor: Donald Knuth Jokes'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-shpY8VHtm2w/TW_GDkiCAGI/AAAAAAAAAHc/4N7nOukwar8/s72-c/knuth-is-my-homeboy.jpg' height='72' width='72'/><thr:total>25</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-5150621375331389105</id><published>2011-02-28T21:30:00.000-08:00</published><updated>2011-02-28T21:40:55.302-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='scala'/><title type='text'>Personal: Looking for a New Job</title><content type='html'>I'm looking for a new job.  I really like my current company, but it's not a very good fit for me for various reasons.  Anyway, here's &lt;a href="http://jjinux.github.com/resume/"&gt;my resume&lt;/a&gt;.  Here's what I'm looking for:&lt;ul&gt;&lt;li&gt;I'd like to find something in San Francisco or the East Bay&lt;/li&gt;&lt;li&gt;I'd like to go somewhere where the code is clean and high quality&lt;/li&gt;&lt;li&gt;As much as I enjoy startups, I'd really like to find something calm and relaxing&lt;/li&gt;&lt;li&gt;I prefer to code in Python or Ruby, but I also really enjoy weird programming languages like Scala, Haskell, and Erlang&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-5150621375331389105?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/5150621375331389105/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=5150621375331389105' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/5150621375331389105'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/5150621375331389105'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/02/personal-looking-for-new-job.html' title='Personal: Looking for a New Job'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-608578162267839704</id><published>2011-02-25T22:51:00.000-08:00</published><updated>2011-02-28T10:31:46.754-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='ActionScript'/><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='bless'/><title type='text'>Bless: A New Twist on Concurrent Programming</title><content type='html'>&lt;i&gt;(Disclaimer: I'm just a guy who likes learning new programming languages.  I'm not an academic language expert.)&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;I've been reading &lt;a href="http://codersatwork.com/"&gt;Coders at Work&lt;/a&gt; lately and really enjoying it.  Fran Allen is a woman who won the Turing Award for her work on optimizing compilers.  She said something interesting.  She said that we've been in a rut since C came along because the pointers in C prevent the compiler from doing a lot of interesting optimizations.  She said that programming languages before C were actually more optimizable.  She also said something about using offsets rather than pointers so that the compiler could easily move memory around.&lt;br /&gt;&lt;br /&gt;It reminded me of &lt;a href="http://www.dreamingincode.com/"&gt;Dreaming in Code&lt;/a&gt;.  Alan Kay said that when it comes to software, we're still building pyramids because we haven't yet discovered the programming version of the arch.  Pyramids are heavy, whereas arches can support an amazing amount of weight while not having as much weight themselves.&lt;br /&gt;&lt;br /&gt;I remember years ago that Alan Kay's statements made me think of actors in Erlang.  Fran Allen's statements also made me think of actors in that an actor could be pointed to by a pointer, whereas all the state in that actor could be represented by offsets of that pointer.  This is how you can isolate actors from each other within the same UNIX process.  (Of course, you have to make sure the language doesn't provide the equivalent of pointer arithmetic with indexes.  That's easy.  References are indexes, and you can't set them arbitrarily.)&lt;br /&gt;&lt;br /&gt;One thing is clear in "Coders at Work".  Everyone is trying to figure out better ways to do concurrency.  Currently, the three competing models are threads, actors, and STM (software transactional memory).  One lesson that I learned when I read &lt;a href="http://www.locusmag.com/2009/Westfahl_Predictions.html"&gt;Why Science Fiction So Often Fails to Predict the Future&lt;/a&gt; is that life isn't homogeneous.  That post pointed out that every form of human transportation is still in use somewhere in the world.  Did I walk to work, drive, or take the train?  The answer is a little bit of all three.  Similarly, I've read that the CISC vs. RISC debate has fizzled because these days chips are CISC on the outside and RISC on the inside.  Hence, I've been thinking of ways to use threads, actors, and STM together.&lt;br /&gt;&lt;br /&gt;Bless is a new "twist" on concurrent programming.  I haven't written it yet--I'm just thinking out loud to get feedback from fellow language enthusiasts.  Bless is a little bit like Erlang at a high-level and a little like Python at the mid-level.  I haven't decided on the low-level parts yet.&lt;br /&gt;&lt;br /&gt;First, let's start with Erlang-style processes.  I like to think of them as "green processes" because they form their own "address space", but they're implemented by the VM.  However, to avoid confusion, I'm going to call them actors.  An actor in Bless is an entity that is implemented by the VM.  It has its own VM-level stack and its own VM-level heap. The VM-level stack must not use the C stack. At the C level, an actor can be referenced by a pointer (i.e. a location in memory), but all data within the actor is referenced by offsets to that pointer.  Hence, the actor can move around in memory without disturbing the contents.&lt;br /&gt;&lt;br /&gt;Each actor has its own green process.  Actors can be scheduled onto any number of operating system threads. Erlang is like this.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Each actor can receive asynchronous messages using a queue.  I think there are queue implementations that don't require locks; however, I was also thinking of implementing the queue using STM.&lt;br /&gt;&lt;br /&gt;All data is local to an actor.  The code for an actor looks sort of like a Python module.  It has lexically scoped functions that have local variables.  Variables global to the actor are kind of like instance variables in object oriented languages.  If a module contains the code for an actor, you can run that same code on multiple green processes. For instance, you might use a new actor for every web server connection. Perhaps it makes sense to use the term "module" for the source code for an actor and "actor" for when that module is run on a green process (remember, I say "process" not in the UNIX sense, but rather because each actor has its own data).&lt;br /&gt;&lt;br /&gt;Data is not immutable like In Erlang.  However, messages between actors are passed by deeping copying the data.  I'm thinking of the messages as being JSON-like messages.  This is something my buddy Warren DeLano was a real fan of.  Immutable data made a lot of sense when Erlang was created because you can implement message passing via passing a pointer to immutable data.  However, mutable data is more familiar to most programmers, and if you have 100+ CPUs, you can afford to copy some data around.  In a perfect implementation, messages would be contiguous blocks of data, and they could be memcpy'd between green processes.  One thing I learned from the Sam Rushing is that you'd be amazed at how fast memcpy is, especially if you have 100+ CPUs to play with.&lt;br /&gt;&lt;br /&gt;I know that a lot of functional people are going to be upset with my use of mutable data.  That's okay.  Most of these ideas still apply if you want to go purely functional.  That's cool too.&lt;br /&gt;&lt;br /&gt;Actors can block on IO, like in Erlang. (I don't buy Node.JS's argument that everything should be a callback.  A little brilliance at the VM level can make everyone else's life easier.  Think of how Java frees programmers from the pains of manual memory management.)  However, like Erlang, you can use asynchronous IO under the covers.&lt;br /&gt;&lt;br /&gt;Bless will need a scheduler in order to schedule actors onto the real kernel threads underneath.  This sort of thing is done in Erlang and Scala, so there's nothing particularly mysterious about this.  Note that if you have 100+ CPUs, the time slices can be longer which cuts down on the amount of work you need to do for scheduling.&lt;br /&gt;&lt;br /&gt;I want Pythonic syntax.  I want indentation-based syntax.  I know some Python haters will reject Bless because of this, but they'd probably find other reasons to reject Bless anyway.  Besides, Python isn't the only language to use indentation.  Another one of my favorite languages, Haskell, does it too.&lt;br /&gt;&lt;br /&gt;I want blocks.  Blocks in Ruby are wonderful.  I think the best way to represent a block in Python is "function_call(args) do: ...".  The "do:" behaves the same way "if ...:" does.  Blocks should follow the same scoping rules that if statements do.  I'm not opposed to anonymous functions such as "def (args):".&lt;br /&gt;&lt;br /&gt;I want keyword arguments.  Python, Common Lisp, and Smalltalk all got this right.&lt;br /&gt;&lt;br /&gt;I want variable declarations.  I would prefer the keyword "var", but it should behave sort of like "my" in Perl.  Variable declarations can help the system catch you if you misspell things, and they also help when you want to set a variable in an outer scope.&lt;br /&gt;&lt;br /&gt;There is no "self" in method calls.  It's just like functions in a module.  However, you can of course "instantiate" the module (i.e. run the actor) multiple times in separate green processes.  There's no need for "@" like in Ruby or "self." in Python because it's all lexically scoped.&lt;br /&gt;&lt;br /&gt;If I decide to do static typing, I want it to look like ActionScript3, "var a: type".  If I decide to do an ML-style type system, the ":type" can be optional.&lt;br /&gt;&lt;br /&gt;Although I really like Python syntax, I think I'd be okay with a slightly-lower level language like Java.  I.e., I'm not against using a compiler, and I am interested in resolving symbols at compile time.  If you think about levels of dynamism, Python, Java, and Haskell all have valid, interesting approaches. I really like the middle ground that ActionScript3 provides. At this point, I care more about the approach to concurrency in Bless than which level of dynamism to use.  Certainly, having both a compiler and an interpreter (like Common Lisp, Haskell, and Ocaml) is very appealing.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Compared to how crazy you can get in Python, I'd be okay with sacrificing a little bit of that dynamism for a little more safety, although I'm not a fan of babysitting the compiler like in Java, and I am still a fan of either duck typing or ML-style typing.&lt;br /&gt;&lt;br /&gt;By the way, there are other forms of concurrency such as data parallelism, data-flow programming (aka promises and futures), etc.  To some degree, I think they are orthogonal--i.e. you can do them in Bless as well, but that's not my focus.  It's best to avoid scope creep and second system syndrome.&lt;br /&gt;&lt;br /&gt;I think there are a few things I haven't straightened out in my head.  I haven't thought about how to reuse functions between actors.  Perhaps mixins make sense.  I also haven't thought about inheritance.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Notice that if you do it right, you can safely run multiple programs in the same UNIX process using separate actors.  However, to do this, you'll need to control how actors are able to obtain references to each other.  This would be a nice feature, but it can easily be thrown out if it makes the implementation too complex--there's a reason we have UNIX processes. It may make sense to think of the actors within a UNIX process as all being "friendly", whereas separate UNIX processes are allowed to be somewhat less "friendly" with each other.&lt;br /&gt;&lt;br /&gt;That's it.  It's basically Erlang-style concurrency with Python syntax and actors with mutable "process-local" state (note, that I know that Erlang has mutable process-local state too).  Easy peasy.  Anyone know how to implement this thing? ;)&lt;br /&gt;&lt;br /&gt;By the way, if I ever did manage to implement it, my next step would be to implement an operating system as I &lt;a href="http://jjinux.blogspot.com/2007/03/ernos-erlang-networked-operating-system.html"&gt;described here&lt;/a&gt;.  Basically, start with a microkernel and then code all the things like the filesystem in userland using Bless.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-608578162267839704?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/608578162267839704/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=608578162267839704' title='21 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/608578162267839704'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/608578162267839704'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/02/bless-new-twist-on-concurrent.html' title='Bless: A New Twist on Concurrent Programming'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>21</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-671567776576607454</id><published>2011-01-25T18:07:00.001-08:00</published><updated>2011-01-25T18:16:59.583-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ssl'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Python: SSL Hell</title><content type='html'>I was having a hard time getting SSL to work with gevent on Python 2.6.  It turns out I had two problems.&lt;br /&gt;&lt;br /&gt;The first resulted in this error message:&lt;pre&gt;SSLError: [Errno 336265218] _ssl.c:337: error:140B0002:SSL routines:SSL_CTX_use_PrivateKey_file:system lib&lt;/pre&gt;It turned out to be a permissions issue.  I ran "cat" on the file, and it turned out that I didn't have access to it:&lt;pre&gt;cat: /etc/mycompany/certs/httpd/mycompany-wildcard.key: Permission denied&lt;/pre&gt;I ran the command with sudo, and the problem went away.&lt;br /&gt;&lt;br /&gt;The second error was related to using urllib2 under gevent:&lt;pre&gt;URLError: &amp;lt;urlopen error [Errno 2] _ssl.c:490: The operation did not complete (read)&amp;gt;&lt;br /&gt;&amp;lt;Greenlet at 0x2add8d0: start_publisher&amp;gt; failed with URLError&lt;br /&gt;...&lt;br /&gt;SSLError: [Errno 8] _ssl.c:490: EOF occurred in violation of protocol&lt;br /&gt;&amp;lt;Greenlet at 0x2add958: &amp;lt;bound method WSGIServer.wrap_socket_and_handle of &amp;lt;WSGIServer at 0x2b48750 fileno=3 address=127.0.0.1:34848&amp;gt;&amp;gt;(&amp;lt;socket at 0x2b48a10 fileno=5 sock=127.0.0.1:34848, ('127.0.0.1', 37858))&amp;gt; failed with SSLError&lt;/pre&gt;This problem was because I was using gevent to monkeypatch the socket module, but I wasn't using it to monkeypatch the ssl module.  Once I monkeypatched the ssl module, everything worked.&lt;br /&gt;&lt;br /&gt;I had a heck of a time writing nosetests that would fire up a server using gevent and connect to it over SSL using urllib2.  However, those nosetests proved very valuable in helping me figure out when and where SSL was breaking for me.&lt;br /&gt;&lt;br /&gt;Here's what one of those nose tests looked like:&lt;pre&gt;# Unfortunately, this monkey patching is not isolated to just this module.&lt;br /&gt;&lt;br /&gt;from gevent import monkey&lt;br /&gt;monkey.patch_all(thread=False)  # Nose uses threads.&lt;br /&gt;&lt;br /&gt;import urllib2&lt;br /&gt;&lt;br /&gt;import gevent&lt;br /&gt;&lt;br /&gt;from myproj import server&lt;br /&gt;&lt;br /&gt;TEST_INTERFACE = "127.0.0.1"&lt;br /&gt;TEST_PORT = 34848&lt;br /&gt;URL = "https://%s:%s" % (TEST_INTERFACE, TEST_PORT)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;def test_server():&lt;br /&gt;&lt;br /&gt;    test_successful_box = [False]&lt;br /&gt;&lt;br /&gt;    def start_server():&lt;br /&gt;        server.main(interface=TEST_INTERFACE, port=TEST_PORT)&lt;br /&gt;&lt;br /&gt;    def start_publisher():&lt;br /&gt;        response = urllib2.urlopen(URL)&lt;br /&gt;        assert response.msg == "OK"&lt;br /&gt;        test_successful_box[0] = True&lt;br /&gt;        gevent.killall(greenlets)&lt;br /&gt;&lt;br /&gt;    greenlets = [gevent.spawn(start_server), gevent.spawn(start_publisher)]&lt;br /&gt;    gevent.joinall(greenlets)&lt;br /&gt;    assert test_successful_box[0]&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-671567776576607454?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/671567776576607454/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=671567776576607454' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/671567776576607454'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/671567776576607454'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/01/python-ssl-hell.html' title='Python: SSL Hell'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-6388287870509506552</id><published>2011-01-07T15:09:00.001-08:00</published><updated>2011-01-07T16:40:48.725-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='twisted'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Python: use_twisted Decorator</title><content type='html'>This is a decorator that you can put on a Python function that will temporarily fire up Twisted's reactor and run a function under Twisted.  This is useful if most of your program doesn't use Twisted, but you have a function that must use Twisted.&lt;br /&gt;&lt;br /&gt;Here's an example of using it:&lt;pre&gt;@use_twisted&lt;br /&gt;@defer.inlineCallbacks&lt;br /&gt;def do_something_twisted():&lt;br /&gt;    value = yield do_something_else_twisted()&lt;br /&gt;    other_value = yield do_more_stuff()&lt;br /&gt;    defer.returnValue(value + other_value)&lt;/pre&gt;Here's the decorator:&lt;pre&gt;def use_twisted(twisted_function):&lt;br /&gt;&lt;br /&gt;    """This is a decorator to run a function under Twisted.&lt;br /&gt;&lt;br /&gt;    Temporarily fire up the reactor and run the function under Twisted.&lt;br /&gt;    It should return a deferred, of course.&lt;br /&gt;&lt;br /&gt;    Unfortunately, there's a bug in Twisted that only allows you to&lt;br /&gt;    start and stop the reactor once.  See&lt;br /&gt;    http://jjinux.blogspot.com/2011/01/python-usetwisted-decorator.html.&lt;br /&gt;    Hence, this decorator will prevent you from calling it twice.  So&lt;br /&gt;    sorry :(&lt;br /&gt;&lt;br /&gt;    """&lt;br /&gt;&lt;br /&gt;    from twisted.python.failure import Failure&lt;br /&gt;&lt;br /&gt;    captured_value = []  # Think of this as a box.&lt;br /&gt;&lt;br /&gt;    def wrapper(*args, **kargs):&lt;br /&gt;        reactor.callLater(0, call_twisted_function, twisted_function, args,&lt;br /&gt;                          kargs)&lt;br /&gt;        reactor.run()&lt;br /&gt;        assert captured_value&lt;br /&gt;        value = captured_value[0]&lt;br /&gt;        if isinstance(value, (Exception, Failure)):&lt;br /&gt;            raise value&lt;br /&gt;        else:&lt;br /&gt;            return value&lt;br /&gt;&lt;br /&gt;    def call_twisted_function(twisted_function, args, kargs):&lt;br /&gt;        deferred = twisted_function(*args, **kargs)&lt;br /&gt;        deferred.addBoth(capture_and_stop)&lt;br /&gt;&lt;br /&gt;    def capture_and_stop(value):&lt;br /&gt;        captured_value.append(value)&lt;br /&gt;        reactor.callLater(0, reactor.stop)&lt;br /&gt;        return value&lt;br /&gt;&lt;br /&gt;    if globals().get("_reactor_run", False):&lt;br /&gt;        raise AssertionError("The use_twisted decorator can only be used once")&lt;br /&gt;    else:&lt;br /&gt;        globals()["_reactor_run"] = True&lt;br /&gt;&lt;br /&gt;    return wrapper&lt;/pre&gt;&lt;b&gt;Updated:&lt;/b&gt; Added code to prevent the decorator from being called twice.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-6388287870509506552?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/6388287870509506552/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=6388287870509506552' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/6388287870509506552'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/6388287870509506552'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/01/python-usetwisted-decorator.html' title='Python: use_twisted Decorator'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-2030467882415210676</id><published>2011-01-06T17:54:00.000-08:00</published><updated>2011-01-06T18:17:50.109-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='ssh'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Linux: pssh</title><content type='html'>Have you ever needed to run a bunch of shell commands over ssh on a bunch of servers?  I know there are probably a ton of tools out there to do this, but when I asked my operations buddy Geoff which he preferred, he told me to try out &lt;a href="http://code.google.com/p/parallel-ssh/"&gt;pssh&lt;/a&gt; (aka parallel-ssh).  I tried it, and I was pleased to discover it was easy to setup and easy to use.&lt;br /&gt;&lt;br /&gt;It's a Python package.  Make sure you have setuptools installed (on Ubuntu, use "sudo apt-get install python-setuptools").  Then run "sudo easy_install pssh".  It creates the following binaries in /usr/local/bin:  prsync, pssh, pnuke, pslurp, pscp, and pssh-askpass.&lt;br /&gt;&lt;br /&gt;It's best if you install your ssh key on each system.  I have a shell script called ssh-installkey to do that:&lt;pre&gt;#!/bin/sh&lt;br /&gt;#&lt;br /&gt;# Install my ssh key on a remote system.&lt;br /&gt;&lt;br /&gt;[ -n "$1" ] || {&lt;br /&gt; echo "usage: ssh-installkey username@host" &amp;gt;&amp;2&lt;br /&gt; return 1&lt;br /&gt;}&lt;br /&gt;ssh $1 "mkdir -p -m 700 .ssh"&lt;br /&gt;ssh $1 "cat &amp;gt;&amp;gt; ~/.ssh/authorized_keys2" &amp;lt; ~/.ssh/id_dsa.pub&lt;br /&gt;ssh $1 "chmod 600 ~/.ssh/authorized_keys2"&lt;/pre&gt;Unfortunately, you'll have to run this script manually for each of the servers, which involves typing in your password a bunch of times.  However, once you have your ssh key installed, your life will be much more pleasant.  &lt;br /&gt;&lt;br /&gt;To use pssh, you should create a hosts file with the hosts that you want to control.  It's a simple file with one host per line.  If you need to specify a username, you can use the format username@host, just like ssh.&lt;br /&gt;&lt;br /&gt;Now, you can try out pssh, "pssh -h hosts.txt -i ls".  The "-i" tells pssh to output the results from each server "inline" (which looks nice).  If you don't care about the output of the command (for instance, if you're compiling something), you can just leave out the -i.&lt;br /&gt;&lt;br /&gt;There are a couple of gotchas to be aware of.  First of all, each command starts with a fresh login.  That means using "cd" in one command doesn't help at all for the next command.  I tend to use commands like "cd dir &amp;&amp; do_something" when running pssh.  Secondly, if your command takes a long time to run, pass "-t -1" to turn off timeouts.&lt;br /&gt;&lt;br /&gt;Lastly, you'll need to do some more work if you want to use sudo.  By default, sudo won't run if you don't have a tty, which you won't if you're using pssh.  To fix this, you'll have to manually log into each server and edit /etc/sudoers.  Comment out the line that says "Defaults requiretty".  Once you do that, you'll be able to use sudo with pssh.&lt;br /&gt;&lt;br /&gt;I was able to use pssh to control a cluster of 10 EC2 instances in order to install ZeroMQ.  (In real life, I'd add ZeroMQ to the AMI so that it was already installed on each server, but using pssh helped me get something up quickly so that I could experiment.)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-2030467882415210676?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/2030467882415210676/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=2030467882415210676' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/2030467882415210676'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/2030467882415210676'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/01/linux-pssh.html' title='Linux: pssh'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-7251837693605482762</id><published>2011-01-06T17:48:00.000-08:00</published><updated>2011-01-06T17:54:01.446-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Unity'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>Linux: Unity</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rrgRZwGYA4M/TSZxS-Xt8OI/AAAAAAAAAHQ/rm7r1PjJevo/s1600/screenshot.png"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 320px; height: 201px;" src="http://1.bp.blogspot.com/_rrgRZwGYA4M/TSZxS-Xt8OI/AAAAAAAAAHQ/rm7r1PjJevo/s320/screenshot.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5559255360829518050" /&gt;&lt;/a&gt;&lt;br /&gt;I tried out &lt;a href="http://unity.ubuntu.com/"&gt;Unity&lt;/a&gt; which is going to be the user interface for Ubuntu 11.04.  It's built on top of GNOME, and it's currently used in Ubuntu's Netbook edition.  Overall, I liked it.  It's definitely one step closer to OS X.  For instance, menus are shown at the top of the screen rather than at the top of each window.  Unfortunately, I encountered several bugs when trying to use an external monitor, so clearly Canonical's programmers have their work cut out for them to hit Ubuntu's 11.04 release (due in April, of course).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-7251837693605482762?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/7251837693605482762/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=7251837693605482762' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/7251837693605482762'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/7251837693605482762'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/01/linux-unity.html' title='Linux: Unity'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_rrgRZwGYA4M/TSZxS-Xt8OI/AAAAAAAAAHQ/rm7r1PjJevo/s72-c/screenshot.png' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-6450046327495804918</id><published>2011-01-03T16:34:00.001-08:00</published><updated>2011-01-03T16:44:33.180-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OS X'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='CrunchBang'/><category scheme='http://www.blogger.com/atom/ns#' term='Mepis'/><category scheme='http://www.blogger.com/atom/ns#' term='apple'/><title type='text'>Linux: CrunchBang Linux 10 on a MacBook Pro</title><content type='html'>I tried CrunchBang Linux 10 on a MacBook Pro.  Previously, I had a lot of trouble dual booting with OS X, so I did the same thing I did for Ubuntu--I told it to use the entire disk.  This turned out to be a big mistake.&lt;br /&gt;&lt;br /&gt;I put GRUB on the MBR since I wasn't dual booting.  I also set up an encrypted LVM.  The system wouldn't boot.  I just got a flashing folder with a "?" icon.  I think this is a known problem with Debian right now.&lt;br /&gt;&lt;br /&gt;I also wanted to try MEPIS.  It's based on Debian as well.  It even has a utility that you run from within OS X that sets stuff up for dual booting.  Unfortunately, since I wiped OS X, that wasn't available.  &lt;br /&gt;&lt;br /&gt;I was doing all this on my company's spare Macbook Pro (while my Macbook Pro was in the shop).  Unfortunately, the DVD for my Macbook Pro wouldn't boot on the other Macbook Pro (which was just a few months newer).  Hence, I couldn't reinstall OS X.&lt;br /&gt;&lt;br /&gt;My solution was to install Ubuntu.  I gave it the whole disk, and everything worked out okay.  I was a little bummed since I wanted to try a new distro (either CrunchBang or MEPIS), but it's hard to argue with a working system ;)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-6450046327495804918?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/6450046327495804918/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=6450046327495804918' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/6450046327495804918'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/6450046327495804918'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2011/01/linux-crunchbang-linux-10-on-macbook.html' title='Linux: CrunchBang Linux 10 on a MacBook Pro'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-6086677146672350811</id><published>2010-12-21T16:05:00.001-08:00</published><updated>2010-12-21T16:09:04.686-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='personal'/><category scheme='http://www.blogger.com/atom/ns#' term='apple'/><title type='text'>Apple: Ouch!</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_rrgRZwGYA4M/TRFA9orkc2I/AAAAAAAAAHE/87a8bk92p6I/s1600/2010-12-21%2B12.54.14.jpg"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://2.bp.blogspot.com/_rrgRZwGYA4M/TRFA9orkc2I/AAAAAAAAAHE/87a8bk92p6I/s320/2010-12-21%2B12.54.14.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5553291243160892258" /&gt;&lt;/a&gt;&lt;br /&gt;I had my laptop wrapped in a blanket in my backpack when I dropped it on concrete at BART yesterday.  It landed on the corner with quite a lot of force and bent a few metal pieces.  Everything seems to work, with the exception of the ethernet port.  Thankfully, I rarely use that.  I'm hoping this is covered by the warranty, although it seems unlikely.  The bent aluminum reminds me of body damage on a car.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-6086677146672350811?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/6086677146672350811/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=6086677146672350811' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/6086677146672350811'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/6086677146672350811'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2010/12/apple-ouch.html' title='Apple: Ouch!'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_rrgRZwGYA4M/TRFA9orkc2I/AAAAAAAAAHE/87a8bk92p6I/s72-c/2010-12-21%2B12.54.14.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-860638288372278644</id><published>2010-12-15T22:31:00.000-08:00</published><updated>2010-12-15T22:38:48.151-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='vim'/><title type='text'>Vim: PeepCode Videos</title><content type='html'>There are commercial videos on &lt;a href="http://peepcode.com/"&gt;PeepCode&lt;/a&gt; that show how to use Vim.  They're fantastic!  The production quality is very high, so they're worth paying for.  I bought both of them, and I learned quite a lot.  Even the first one covered a ton of advanced stuff, so I highly recommend it.&lt;br /&gt;&lt;br /&gt;If you're just getting started with Vim, I suggest you:&lt;ul&gt;&lt;li&gt;Read &lt;a href="http://yehudakatz.com/2010/07/29/everyone-who-tried-to-convince-me-to-use-vim-was-wrong/"&gt;this blog post&lt;/a&gt; from Yehuda Katz.  Basically, he suggests you use the GUI to get started and iteratively gain more experience with Vim.  I think that's good advice.&lt;/li&gt;&lt;li&gt;Next, I suggest you fire up Vim and take the internal tutorial.&lt;/li&gt;&lt;li&gt;Last of all, watch &lt;a href="http://peepcode.com/products/smash-into-vim-i"&gt;Smash into Vim part I&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;If you're an advanced Vim user, watch both videos and tell me what your favorite part was ;)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-860638288372278644?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/860638288372278644/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=860638288372278644' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/860638288372278644'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/860638288372278644'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2010/12/vim-peepcode-videos.html' title='Vim: PeepCode Videos'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-927090542740980888</id><published>2010-12-10T15:01:00.001-08:00</published><updated>2010-12-10T15:06:30.385-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='comet'/><category scheme='http://www.blogger.com/atom/ns#' term='NodeJS'/><category scheme='http://www.blogger.com/atom/ns#' term='Socket.IO'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>JavaScript: IE and DTDs</title><content type='html'>I was having a hard time getting Internet Explorer 8 (IE8) working with Socket.IO's "htmlfile" transport (or any other transport for that matter).  I was getting weird permissions issues.  I finally fixed the problem by adding a DTD to the top of my HTML file, "&amp;lt;!doctype html&amp;gt;".  I was just being lazy not having a DTD, but I was really surprised when that fixed my problem.  Of course, I'm still having a hard time getting IE7, IE6, and Opera working cross-domain (technically, I'm just using a different port, but eventually it'll truly be cross-domain).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-927090542740980888?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/927090542740980888/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=927090542740980888' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/927090542740980888'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/927090542740980888'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2010/12/javascript-ie-and-dtds.html' title='JavaScript: IE and DTDs'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-8817983760530541085</id><published>2010-12-08T11:50:00.000-08:00</published><updated>2010-12-08T12:16:34.967-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='rspec'/><category scheme='http://www.blogger.com/atom/ns#' term='webrat'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='cucumber'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Ruby: Why I Prefer Cucumber</title><content type='html'>I've been struggling to articulate why, in most cases, I prefer high-level integration tests with Cucumber and Webrat (or Capybara) over low-level model, view, and controller tests with RSpec.  I think I finally have an example that conveys what I'm trying to say.  Consider the following Cucumber test:&lt;pre&gt;Scenario: create a new film unsuccessfully&lt;br /&gt;  Given I am logged in as "admin"&lt;br /&gt;  And I am on the admin films page&lt;br /&gt;&lt;br /&gt;  When I follow "New film"&lt;br /&gt;  And I press "Create"&lt;br /&gt;  Then I should see "There were problems with the following fields:"&lt;br /&gt;  And I should see "Name can't be blank"&lt;br /&gt;  And I should see "Url name can't be blank"&lt;br /&gt;  And I should see "Sort name can't be blank"&lt;br /&gt;  And I should see "URL doesn't look like a valid RTMPE URL"&lt;br /&gt;  But I should not see "Trailer URL doesn't look like a valid RTMPE URL"&lt;br /&gt;  And I should not see "Scene URL doesn't look like a valid RTMPE URL"&lt;/pre&gt;This test is short, readable, and easy to write.  It doesn't test every possible validation failure, and it's not the only test I have.  (In fact, I have some RSpec model tests that test the more esoteric URL validation rules.)  However, it does test the model, view, and controller's handling of validation failures, and it even tests that they integrate with each other.&lt;br /&gt;&lt;br /&gt;Can you imagine trying to write the same tests by separately testing the model, view, and controller using RSpec?  Now, imagine trying to use a separate test for each assertion.  That's a lot of code for something so trivial--this ain't rocket science, guys!  Finally, remember that when you test the things separately, there's nothing preventing the code from crashing when you put all the pieces together.  (For instance, what happens if the controller and view each pass their RSpec tests, but they disagree on the spelling of one of the assigns?)&lt;br /&gt;&lt;br /&gt;Is there benefit to testing things separately--absolutely.  Is it worth it in this case--absolutely not.  I think it's important to remember that at a certain level, our job is to implement features that work.  Tests are a means to an end--they help us keep the code working.  They don't really have any intrinsic value for the stakeholder.  They only have the secondary value of keeping the code working when it is extended.&lt;br /&gt;&lt;br /&gt;Just as there is engineering value in implementing features using less code (as long as it remains readable), there is also engineering value in implementing features using less testing code (as long as the tests continue to serve their purpose of preventing regressions). &lt;br /&gt;&lt;br /&gt;My point is that &lt;i&gt;Cucumber lets you test more using less effort.&lt;/i&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-8817983760530541085?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/8817983760530541085/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=8817983760530541085' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/8817983760530541085'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/8817983760530541085'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2010/12/ruby-why-i-prefer-cucumber.html' title='Ruby: Why I Prefer Cucumber'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-7386297357188302884</id><published>2010-11-26T10:00:00.000-08:00</published><updated>2010-11-26T10:02:01.338-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='personal'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='twilio'/><category scheme='http://www.blogger.com/atom/ns#' term='bdd'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Personal: Wife in Labor</title><content type='html'>Wife in labor. Built &lt;a href="https://github.com/jjinux/Teletimer"&gt;Twilio app&lt;/a&gt; to time contractions. Call it to try it out: (866) 948-3615.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-7386297357188302884?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/7386297357188302884/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=7386297357188302884' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/7386297357188302884'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/7386297357188302884'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2010/11/personal-wife-in-labor.html' title='Personal: Wife in Labor'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-2993244407009505034</id><published>2010-11-19T11:02:00.000-08:00</published><updated>2010-11-19T11:04:22.270-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='software engineering'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Software Engineering: Coping When You Must Repeat Yourself</title><content type='html'>These days, most software engineers are familiar with the acronym "DRY" which stands for "don't repeat yourself".  In general, it's good advice.  In a perfect world, no code would ever be duplicated and every piece of "truth" would only exist in one place.  However, the real world isn't quite so perfect, and things are far less DRY than you might imagine.  The question is, how do you cope?&lt;br /&gt;&lt;br /&gt;First let me show you some reasons why you can't always keep it DRY:&lt;br /&gt;&lt;br /&gt;Often, the same truth is duplicated in the code, the tests, and the docs.  Duplicating the same piece of truth in the code and in the tests helps each verify the other.  Generally, the code is more general than the tests (the tests verify examples of running the more general code), but the duplication is there.  When you update one (for instance to change the API), you'll need to update the other.  This is a case where not keeping it DRY pays off--if you have to update the tests, that's a reminder that you'll also have to update all the other consumers of your API.&lt;br /&gt;&lt;br /&gt;Similarly, the API docs often duplicate the truth that is built in to the code.  That's because it's helpful to explain the truth to the computer in one way (using very precise code) and explain the truth to the reader in another way (using friendly, high-level English).  Every truthful comment duplicates what the code already says, but not every piece of code is easily and quickly readable by human readers--this is especially true in, say, assembly.&lt;br /&gt;&lt;br /&gt;Another area where truth is duplicated is in APIs.  The function defines a name and an API.  The caller uses it.  They must agree on these things or the code won't work.  If the caller decides to use a different name or a different API, the code will break.  Essentially, programmers have decided that it's better to duplicate the name and the API rather than duplicate the contents of the function.  This points to a useful trick--sometimes a small amount of duplication saves a large amount of duplication.  You'll also see this sometimes in comments when they say "see also..."&lt;br /&gt;&lt;br /&gt;Another source of duplication concerns public vs. private.  For instance, in C, the same API is duplicated in the .h file and the .c file.  Sometimes, the same piece of code must be duplicated in different projects.  For instance, one operating system might need to define the same C types as another operating system because there's no easy way for them to share the same header files.&lt;br /&gt;&lt;br /&gt;At a higher level, one time I had to add the same function I wrote to two projects.  One project was proprietary company code.  The other was open source (I had permission, of course).  For technical reasons, it was impractical for the company code to import or subclass the open source code, so I was stuck just duplicating it.  &lt;br /&gt;&lt;br /&gt;Often, you'll need to duplicate the same piece of truth in multiple languages.  For instance, think of how many HTTP client libraries there are in all the different programming languages.  It doesn't matter how good an HTTP client library is if it's not easily accessible from the programming language I'm currently coding in.  Sometimes there will be multiple HTTP client libraries for the same language because they're implemented differently (for instance, syncronously vs. asyncronously).&lt;br /&gt;&lt;br /&gt;I mentioned tests before.  Often tests duplicate some setup or teardown or perhaps the same pattern of interacting with a function.  Refactoring is sometimes appropriate, but not always.  It is commonly held that this is one area where keeping it DRY is less important than keeping it simple and isolated.  A perfectly DRY collection of unittests that is difficult to comprehend and difficult to debug when something fails is less helpful than a set of simple, isolated unittests that contain a small amount of duplication.  If the duplication causes multiple tests to fail, you'll know to keep fixing the tests until they all pass.&lt;br /&gt;&lt;br /&gt;The question remains, how do you cope when you can't keep it DRY?&lt;br /&gt;&lt;br /&gt;Greppability is very important.  (By grep, I mean any tool that can search for a string or regular expression.  I don't necessarily mean the UNIX tool "grep".)  In highly dynamic languages like Ruby (that have great facilities for metaprogramming, but no static typing or interfaces) and highly factored frameworks like Rails (that use lots of files and levels of indirection), even a brilliant IDE can fail in comparison to a simple "grep tour".  If you refactor a class in Ruby, how will you remember to refactor all the mocks of that class?  You might have a user of your class that has a mock of your class that still makes use of your old API.  The tests might be passing even though the code will assuredly crash.  If you use grep, you can update all the callers of your class as well as all the mocks of the class.  Grep can also help you find instances of a string in non-code documentation, and it even occasionally works with binary files.  My point is, don't underestimate the utility of grep.  Rather, you should aim for greppability.  A function named "f" is not greppable, but a function named "calculate_apr" is.  (By the way, naming all your loop variables "iterator" does not improve greppability, it just wastes time.)&lt;br /&gt;&lt;br /&gt;Another way of coping when things aren't DRY is to have cross referencing comments.  If you know that you must duplicate the same piece of truth in 5 places, add a comment next to each of those 5 places that refers to the other 5 places.  Don't be afraid to duplicate the comment exactly.  Your comment can say something like, "If you change this, don't forget to update..."&lt;br /&gt;&lt;br /&gt;Another thing that helps mitigate duplication is proximity.  Docstrings belong in the code because if a programmer updates one, he'll be more likely to update the other (although even proximity can't always help lazy programmers).  If all the API documentation is in a separate file, that file will go stale very quickly.&lt;br /&gt;&lt;br /&gt;Parallelization also helps.  For instance, this code has a small amount of duplication:&lt;pre&gt; some_a = 1&lt;br /&gt; some_a.invoke_method()&lt;br /&gt; register(some_a)&lt;br /&gt; call_something_unrelated()&lt;br /&gt; some_b = 2&lt;br /&gt; some_b.invoke_method()&lt;br /&gt; register(some_b)&lt;/pre&gt;Sometimes you can factor out this duplication.  However, in less dynamic languages like C, it may not always be easy to do so.  However, parallelization can really help:&lt;pre&gt; some_a = 1&lt;br /&gt; some_b = 2&lt;br /&gt; some_a.invoke_method()&lt;br /&gt; some_b.invoke_method()&lt;br /&gt; register(some_a)&lt;br /&gt; register(some_b)&lt;br /&gt; call_something_unrelated()&lt;/pre&gt;Another old trick for coping with duplication is to have one source generate the other.  Generating API documentation using javadoc is a good example of this.  Sometimes you can use a program to generate code for multiple programming languages.  There's another example of "generation" that I sometimes use in Python.  I use string interpolation when creating docstrings.  For instance, if there's a piece of documentation that should be duplicated in multiple places, string interpolation makes it possible so that I only have to write that piece of documentation once.&lt;br /&gt;&lt;br /&gt;Another source of duplication has to deal with the plethora of tools programmers must use. There is the source code itself, a revision control system, a bug tracker, and a wiki.  Often times, the same piece of truth needs to be duplicated in all of these places.  This is one place where Trac really shines.  Once you properly configure Trac, you can reference the bug number in each of your commits.  Trac's commit hook will take that commit and add it as a comment in the original bug with a reference to the source code in Trac's source code viewer.  Hence, Trac (which is a bug tracking system, a wiki, and a source code viewer) and the revision control system work together to reduce duplication.&lt;br /&gt;&lt;br /&gt;It's unfortunate that life isn't always as DRY as you'd like it to be.  However, keeping a few tricks in mind can really help mitigate the problems caused by having to duplicate a piece of truth in more than once place.  If you have other tricks, feel free to leave them in a comment below.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-2993244407009505034?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/2993244407009505034/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=2993244407009505034' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/2993244407009505034'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/2993244407009505034'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2010/11/software-engineering-coping-when-you.html' title='Software Engineering: Coping When You Must Repeat Yourself'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11788780.post-2699736004685353144</id><published>2010-11-18T11:01:00.000-08:00</published><updated>2010-11-18T11:16:51.803-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='comet'/><category scheme='http://www.blogger.com/atom/ns#' term='NodeJS'/><category scheme='http://www.blogger.com/atom/ns#' term='Socket.IO'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>JavaScript: Naughty Socket.IO Example</title><content type='html'>File this under the "things you probably shouldn't do, but are fun anyways" category.  &lt;a href="http://socket.io/"&gt;Socket.IO&lt;/a&gt; is a library for &lt;a href="http://nodejs.org/"&gt;Node.JS&lt;/a&gt; that provides Comet using a plethora of different approaches (WebSocket, Flash socket, AJAX long polling, etc.).  I hacked the Socket.IO chat demo so that it reads HTML from my terminal and just dumps it to the browser.  Hence, I can control people's browsers from my terminal.  Insecure?  Yeah.  Fun?  Oh yeah!&lt;br /&gt;&lt;br /&gt;Anyway, here's how I hacked the server.js file in Socket.IO's chat demo:&lt;pre&gt;io.on('connection', function (client) {&lt;br /&gt;&lt;br /&gt; // Read from /dev/tty and send it to the browser.&lt;br /&gt; var stream = fs.createReadStream('/dev/tty', {encoding: 'ascii'});&lt;br /&gt;&lt;br /&gt; stream.on('error', function (exception) {&lt;br /&gt;   client.send({announcement: 'Exception: ' + exception});&lt;br /&gt; });&lt;br /&gt;&lt;br /&gt; stream.on('data', function (data) {&lt;br /&gt;   client.send({html: data});&lt;br /&gt; });&lt;br /&gt; ...&lt;/pre&gt;And here's how I hacked chat.html:&lt;pre&gt;function message(obj) {&lt;br /&gt; var el = document.createElement('p');&lt;br /&gt; if ('html' in obj) el.innerHTML = obj.html;&lt;br /&gt; ...&lt;/pre&gt;Here's what it looks like in my terminal:&lt;pre&gt;sudo ./server.js&lt;br /&gt;18 Nov 10:14:04 - socket.io ready - accepting connections&lt;br /&gt;18 Nov 10:14:06 - Initializing client with transport "websocket"&lt;br /&gt;18 Nov 10:14:06 - Client 5832344139926136 connected&lt;br /&gt;&lt;br /&gt;&amp;lt;i&amp;gt;I'm typing this in to control the page.&amp;lt;/i&amp;gt;&lt;br /&gt;&amp;lt;script&amp;gt;alert('Oh baby!');&amp;lt;/script&amp;gt; This doesn't work with innerHTML, thankfully.&lt;br /&gt;&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;Node.JS&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Socket.IO&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11788780-2699736004685353144?l=jjinux.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jjinux.blogspot.com/feeds/2699736004685353144/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=11788780&amp;postID=2699736004685353144' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/2699736004685353144'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11788780/posts/default/2699736004685353144'/><link rel='alternate' type='text/html' href='http://jjinux.blogspot.com/2010/11/javascript-naughty-socketio-example.html' title='JavaScript: Naughty Socket.IO Example'/><author><name>Shannon -jj Behrens</name><uri>http://www.blogger.com/profile/03270879497119114175</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://bp3.blogger.com/_rrgRZwGYA4M/R3cMoOSu8sI/AAAAAAAAACo/XR7WzQXh1Bs/S220/jj_metaweb_cropped.jpg'/></author><thr:total>0</thr:total></entry></feed>
