<?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-4489744187593527994</id><updated>2011-11-03T12:09:12.067-07:00</updated><category term='exercise'/><category term='e27sg4 singapore entrepreneur web2.0 web2 web'/><category term='apt-get ubuntu debian linux tips'/><category term='erlang'/><category term='concurrency'/><category term='book'/><category term='gpacs'/><category term='emacs font'/><category term='python design patterns'/><title type='text'>nihilanth</title><subtitle type='html'>The nihilanth.net blog of cabbages and kings</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>44</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-310653543892294479</id><published>2008-07-27T19:28:00.000-07:00</published><updated>2008-08-01T23:19:55.348-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='apt-get ubuntu debian linux tips'/><title type='text'>Ubuntu: "bad signature" problems prevent apt-get update from working</title><content type='html'>When running a routine package update, I sometimes get errors like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$ sudo apt-get update&lt;br /&gt;...&lt;br /&gt;W: A error occurred during the signature verification. The repository is not updated and the previous index files will be used.GPG error: http://security.ubuntu.com hardy-security Release: The following signatures were invalid: BADSIG 40976EAF437D05B5 Ubuntu Archive Automatic Signing Key &lt;ftpmaster@ubuntu.com&gt;&lt;br /&gt;W: Failed to fetch http://security.ubuntu.com/ubuntu/dists/hardy-security/Release&lt;br /&gt;W: Some index files failed to download, they have been ignored, or old ones used instead.&lt;br /&gt;W: You may want to run apt-get update to correct these problems&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This &lt;a href="https://bugs.launchpad.net/ubuntu/+source/apt/+bug/24234"&gt;ubuntu bugs page&lt;/a&gt; suggests this:&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$ sudo apt-get update -o Acquire::http::No-Cache=true&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;but the problem persists.&lt;br /&gt;&lt;strong&gt;Solved it!&lt;/strong&gt; By deleting the incorrectly signed Release and Release.gpg files that were downloaded in the last try:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$ sudo rm -f /var/lib/apt/lists/partial/security.ubuntu.com_ubuntu_dists_hardy-security_Release*&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now apt-get is successful.&lt;br /&gt;I got the idea above from the &lt;a href="https://bugs.launchpad.net/ubuntu/+source/apt/+bug/24234/comments/20"&gt;instructions in comment 20 to the bug&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-310653543892294479?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/310653543892294479/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=310653543892294479' title='43 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/310653543892294479'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/310653543892294479'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2008/07/ubuntu-bad-signature-problems-prevent.html' title='Ubuntu: &quot;bad signature&quot; problems prevent apt-get update from working'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>43</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-3623476173974276014</id><published>2008-06-29T08:30:00.000-07:00</published><updated>2008-06-29T08:41:58.689-07:00</updated><title type='text'>McCain vs Obama</title><content type='html'>&lt;p&gt;From &lt;a href="http://www.cnn.com/2007/POLITICS/10/01/mccain.christian.nation/index.html"&gt;CNN&lt;/a&gt;:&lt;br /&gt;&lt;blockquote&gt;Muslim and Jewish groups on Monday sharply criticized Sen. John McCain's comments that he would prefer a Christian president to lead the United States.&lt;/blockquote&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;The Arizona Republican's remarks came in an interview with Beliefnet, a Web site that covers religious issues and affairs.&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;"I just have to say in all candor that since this nation was founded primarily on Christian principles, personally, I prefer someone who has a grounding in my faith," the GOP presidential hopeful told the Web site in an interview published Saturday.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/blockquote&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Its hilarious that other religious groups are attacking him for emphasizing &lt;em&gt;his&lt;/em&gt; religion and excluding theirs, and not because &lt;a href="http://www.stephenjaygould.org/ctrl/treaty_tripoli.html"&gt;this is blatantly false&lt;/a&gt;.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;On the other hand, Barack Obama seems to take a more reasonable stand:&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;Whatever we once were, we're no longer just a Christian nation; we are also a Jewish nation, a Muslim nation, a Buddhist nation, a Hindu nation, and a nation of non-believers. (http://www.cbn.com/CBNnews/204017.aspx)&lt;/blockquote&gt;&lt;p&gt;&lt;br /&gt;&lt;p&gt;Here is another quote:&lt;br /&gt;&lt;blockquote&gt;Democracy demands that the religiously motivated translate their concerns into universal, rather than religion-specific, values. It requires that their proposals be subject to argument, and amenable to reason. (from http://obama.senate.gov/speech/060628-call_to_renewal/)&lt;/blockquote&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-3623476173974276014?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/3623476173974276014/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=3623476173974276014' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/3623476173974276014'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/3623476173974276014'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2008/06/mccain-vs-obama-from-cnn-muslim-and.html' title='McCain vs Obama'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-69289561079782614</id><published>2008-06-29T08:05:00.000-07:00</published><updated>2008-06-29T08:30:39.761-07:00</updated><title type='text'></title><content type='html'>US Presidential Candiates on Politics vs Religion&lt;br /&gt;&lt;p&gt;I was returning from a social trip to Jakarta. While at the airport, wondering how to kill time, I ran into a bookshop and spied Richard Dawkin's "The God Delusion". I bought it immediately. Honestly, I did it more to dig up interesting factoids to tell my religious wife than because I thought the book would have any new perspectives for the godless geek that I am.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;However, I had forgotten that Richard Dawkins writes very well, and it has been quite an entertaining read so far.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;I could feel indignation bringing blood rushing to my eyes when I read chapters on how atheism is discriminated against in American politics and how the religious are blatantly lying in the press regarding the United States being founded as a Christian nation.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;The chapter "Arguments for God' existence" does a very good job of summarizing the very few arguments from first principles put forward to justify the existence of God.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;And the book led me to discover two blogs:&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;ul&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://scienceblogs.com/pharyngula/"&gt;Pharyngula&lt;/a&gt;, a  blog about "Evolution, development, and random biological ejaculations from a godless liberal", belonging to PZ Myers, a bilogist and associate professor at the Univertisty of Minnesota.&lt;/li&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://godlessgeeks.com/"&gt;Atheists of Silicon Valley&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/ul&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-69289561079782614?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/69289561079782614/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=69289561079782614' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/69289561079782614'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/69289561079782614'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2008/06/us-presidential-candiates-on-politics.html' title=''/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-1977520425550949119</id><published>2008-05-27T18:21:00.000-07:00</published><updated>2008-05-27T18:29:15.631-07:00</updated><title type='text'>Building and installing a local debian package</title><content type='html'>Recently, I ran into trouble with the 'python-zeroc-ice' package on Ubuntu 8.04. It used to work fine on Ubuntu 7.10.&lt;br /&gt;&lt;br /&gt;Some digging revealed that the package python-zeroc-ice-3.2.1-2 claims it supports python2.5, but the C module for ICE is built against python2.4.&lt;br /&gt;&lt;br /&gt;So I compiled the package and installed it locally.  This is how:&lt;br /&gt;&lt;br /&gt;1. Downloaded the source package:&lt;br /&gt;$ apt-get source python-zeroc-ice&lt;br /&gt;&lt;br /&gt;2. Installed all other packages needed to *build* this pacakge:&lt;br /&gt;$ sudo apt-get build-dep python-zeroc-ice&lt;br /&gt;&lt;br /&gt;3. Fixed the problem:&lt;br /&gt;$ cd zeroc-ice-python-3.2.1 # yes, the source package/dir is called zeroc-python-ice&lt;br /&gt;$ vim config/Make.rules&lt;br /&gt;  -&gt; set "PYTHON_VERSION = python2.5" in the appropriate place.&lt;br /&gt;$ dch -i # bring up the changelog with new entry&lt;br /&gt;  Added a changelog messsage.  The package version, at the top of the changelog, is now python-zeroc-ice-3.2.1-2ubuntu1&lt;br /&gt;$ dpkg-buildpackage -uc -us&lt;br /&gt;&lt;br /&gt;4. Install the new package&lt;br /&gt;$ cd ..&lt;br /&gt;$ dpkg -i python-zeroc-ice_3.2.1-2ubuntu1_i386.deb&lt;br /&gt;&lt;br /&gt;And we are done.  However, the change above means Ice won't be available for python2.4 anymore.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-1977520425550949119?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/1977520425550949119/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=1977520425550949119' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/1977520425550949119'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/1977520425550949119'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2008/05/building-and-installing-local-debian.html' title='Building and installing a local debian package'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-5546132845747228967</id><published>2008-04-09T01:57:00.000-07:00</published><updated>2008-08-01T23:13:44.485-07:00</updated><title type='text'>Gotcha with git-svn dcommit?</title><content type='html'>&lt;p&gt;http://kerneltrap.org/mailarchive/git/2007/8/21/255431: this should be kept in mind/verified when using git-svn.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-5546132845747228967?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/5546132845747228967/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=5546132845747228967' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/5546132845747228967'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/5546132845747228967'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2008/04/gotcha-with-git-svn-dcommit.html' title='Gotcha with git-svn dcommit?'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-8745574103707997618</id><published>2008-04-09T01:48:00.000-07:00</published><updated>2008-08-01T23:31:48.847-07:00</updated><title type='text'>Tracking multiple branches in svn repo with git-svn</title><content type='html'>For some time now, have wanted to use git and git-svn to track the subversion repo at work. That repo has lots of branches, and individual changesets are merged very often between branches, making it a nightmare to find out what has already been merged, and what not.&lt;br /&gt;Used to keep track of all this in a text file. Its becoming tedious. Have been trying to use git-svn to ease the pain.&lt;br /&gt;On the first try, git-svn gave up halfway during the clone operation with an error message about protocol/network error. This git-svn was the one in ubuntu repositories.&lt;br /&gt;Since then, installed git from source.&lt;br /&gt;First I tried &lt;a href="http://www.dmo.ca/blog/20070608113513"&gt;this way&lt;/a&gt;. It works, but then gitk will only show me one branch: the one I have currently checked out.&lt;br /&gt;Now, I am trying &lt;a href="http://www.jukie.net/~bart/blog/20080303200359"&gt;this other way&lt;/a&gt;. Hopefully this will work better.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-8745574103707997618?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/8745574103707997618/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=8745574103707997618' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/8745574103707997618'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/8745574103707997618'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2008/04/tracking-multiple-branches-in-svn-repo.html' title='Tracking multiple branches in svn repo with git-svn'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-581466804021743567</id><published>2008-04-05T10:01:00.000-07:00</published><updated>2008-05-27T18:38:08.662-07:00</updated><title type='text'>Code merging</title><content type='html'>&lt;p&gt;Wanting to merge two branches of code, both of which have had concurrent commits going on them for some time. Code is kept in svn, so the branches actually live in separate directories.  Most changesets have been merged back and forth, yet residual changes remain.&lt;br /&gt;&lt;p&gt;I get diff to tell the files which are different between the branches.&lt;br/&gt;&lt;br /&gt;&lt;pre&gt;diff -q -r -x '\.svn' -x '*.pyx' -x '*~' branch1 branch2&lt;/pre&gt;&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;This produces lines like:&lt;/p&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Files zeroc-ice-python_3.2.1/config/Make.rules and zeroc-ice-python-3.2.1-new/config/Make.rules differ&lt;br /&gt;Only in zeroc-ice-python-3.2.1-new/config: Make.rules.GNU&lt;br /&gt;Only in zeroc-ice-python-3.2.1-new/config: Make.rules.GNU_kFreeBSD&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;I need to munge the output of diff. I want to have 4 columns: directory (sans the branchname), filename, link to the file in branch1 if it exists there, link to file in branch2 if it exists there. I want this ordered/sorted by directory, and I want the output in emacs org mode format.  I filter the output of the above diff command through this perl script:&lt;/p&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="keyword"&gt;while&lt;/span&gt; (&amp;lt;&amp;gt;) {&lt;br /&gt;    &lt;span class="keyword"&gt;if&lt;/span&gt; (&lt;span class="string"&gt;/^Only in/&lt;/span&gt;) {&lt;br /&gt;        m&lt;span class="string"&gt;/Only in (.*): (.*)$/&lt;/span&gt;; $&lt;span class="variable-name"&gt;dirname&lt;/span&gt;=$&lt;span class="variable-name"&gt;1&lt;/span&gt;; $&lt;span class="variable-name"&gt;fname&lt;/span&gt;=$&lt;span class="variable-name"&gt;2&lt;/span&gt;; &lt;span class="comment"&gt;# sep dirpath and filena&lt;/span&gt;&lt;span class="comment"&gt;&lt;span class="highlight-beyond-fill-column"&gt;me&lt;/span&gt;&lt;/span&gt;&lt;span class="comment"&gt;&lt;br /&gt;&lt;/span&gt;        ($&lt;span class="variable-name"&gt;branch&lt;/span&gt; = $&lt;span class="variable-name"&gt;dirname&lt;/span&gt;) =~ s&lt;span class="string"&gt;/([^\/]+).*/$1/&lt;/span&gt;; &lt;span class="comment"&gt;# the branch name is the first&lt;/span&gt;&lt;span class="comment"&gt;&lt;span class="highlight-beyond-fill-column"&gt; part of the dirpath (e.g. branch-xy/dir/path/file)&lt;/span&gt;&lt;/span&gt;&lt;span class="comment"&gt;&lt;br /&gt;&lt;/span&gt;        $&lt;span class="variable-name"&gt;dirname&lt;/span&gt; =~ s&lt;span class="string"&gt;/$branch\/?(.*)/$1/&lt;/span&gt;; &lt;span class="comment"&gt;# remove the 'branch-xy' from dirpath &lt;/span&gt;&lt;span class="comment"&gt;&lt;span class="highlight-beyond-fill-column"&gt;to get the remaining dirpath&lt;/span&gt;&lt;/span&gt;&lt;span class="comment"&gt;&lt;br /&gt;&lt;/span&gt;        $&lt;span class="variable-name"&gt;_&lt;/span&gt; = &lt;span class="string"&gt;"| $dirname/$fname | [[file:~/workspace/couffable/branches/$branch/&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="highlight-beyond-fill-column"&gt;$dirname/$fname][Only in: $branch]] |"&lt;/span&gt;&lt;/span&gt;&lt;span class="highlight-beyond-fill-column"&gt;;&lt;/span&gt;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;if&lt;/span&gt; (&lt;span class="string"&gt;/^Files /&lt;/span&gt;) {&lt;br /&gt;        m&lt;span class="string"&gt;/Files (.*) and (.*) differ/&lt;/span&gt;; $&lt;span class="variable-name"&gt;left&lt;/span&gt;=$&lt;span class="variable-name"&gt;1&lt;/span&gt;;&lt;br /&gt;        $&lt;span class="variable-name"&gt;left&lt;/span&gt; =~ m&lt;span class="string"&gt;/release-3-11\/(.*\/)?([^\/]+)/&lt;/span&gt;;&lt;br /&gt;        &lt;span class="keyword"&gt;if&lt;/span&gt; (defined($&lt;span class="variable-name"&gt;1&lt;/span&gt;)) { $&lt;span class="variable-name"&gt;dirname&lt;/span&gt;=$&lt;span class="variable-name"&gt;1&lt;/span&gt;; } &lt;span class="keyword"&gt;else&lt;/span&gt; { $&lt;span class="variable-name"&gt;dirname&lt;/span&gt; = &lt;span class="string"&gt;""&lt;/span&gt;; }&lt;br /&gt;        $&lt;span class="variable-name"&gt;left&lt;/span&gt;=$&lt;span class="variable-name"&gt;2&lt;/span&gt;; &lt;span class="comment"&gt;# remove branch name, sep dir and file&lt;br /&gt;&lt;/span&gt;        $&lt;span class="variable-name"&gt;_&lt;/span&gt; = &lt;span class="string"&gt;"| $dirname$left | [[file:~/workspace/couffable/branches/release-3-&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="highlight-beyond-fill-column"&gt;11/$dirname$left][r311]] | [[file:~/workspace/couffable/branches/release-3-11-airtel/$dirname$left][r311-airtel]] |"&lt;/span&gt;&lt;/span&gt;&lt;span class="highlight-beyond-fill-column"&gt;;&lt;/span&gt;&lt;br /&gt;    }&lt;br /&gt;    print $&lt;span class="variable-name"&gt;_&lt;/span&gt; . &lt;span class="string"&gt;"\n"&lt;/span&gt;;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-581466804021743567?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/581466804021743567/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=581466804021743567' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/581466804021743567'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/581466804021743567'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2008/04/code-merging-wanting-to-merge-two.html' title='Code merging'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-2435071137354018454</id><published>2008-04-04T02:37:00.000-07:00</published><updated>2008-04-05T23:20:52.753-07:00</updated><title type='text'>Tesla = Pylons + SQLAlchemy + Elixir</title><content type='html'>&lt;p&gt;&lt;a href="http://code.google.com/p/tesla-pylons-elixir/"&gt;Tesla&lt;/a&gt; is a &lt;a href="http://pythonpaste.org/"&gt;Paster&lt;/a&gt; &lt;a href="http://pythonpaste.org/script/developer.html#id5"&gt;template&lt;/a&gt; creating &lt;a href="http://pylonshq.com/"&gt;Pylons&lt;/a&gt; applications using &lt;a href="http://www.sqlalchemy.org/"&gt;SQLAlchemy&lt;/a&gt;/&lt;a href="http://elixir.ematia.de/trac/wiki"&gt;Elixir&lt;/a&gt; ORM. Adds some simple database paster commands. Includes the following features:&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;   &lt;li&gt;Create model classes&lt;/li&gt;&lt;br /&gt;   &lt;li&gt;Simple database commands (create/drop tables)&lt;/li&gt;&lt;br /&gt;   &lt;li&gt;Migrations (using SoC &lt;a href="http://code.google.com/p/sqlalchemy-migrate/"&gt;migrate&lt;/a&gt; library)&lt;/li&gt;&lt;br /&gt;   &lt;li&gt;Create and run batch scripts&lt;/li&gt;&lt;br /&gt;   &lt;li&gt;Handles SQLAlchemy setup and session refresh&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Now, will Tesla/Elixir allow me to have composite primary keys of my choosing? Can I have, for most models, an Elixir definition, and for some, a more flexible SQLAlchemy mapper? How?&lt;/p&gt;&lt;br /&gt;&lt;p&gt;The reason I'd like to do that is: &lt;a href="http://www.joehruska.com/?p=6"&gt;ActiveRecord does a poor job of structuring database tables for performance&lt;/a&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;What a bunch of libraries! Sometimes you wonder if plain PHP with embedded SQL isn't better after all. No, no, that was heresy...&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-2435071137354018454?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/2435071137354018454/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=2435071137354018454' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/2435071137354018454'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/2435071137354018454'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2008/04/tesla-pylons-sqlalchemy-elixir-tesla-is.html' title='Tesla = Pylons + SQLAlchemy + Elixir'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-1741926536904826646</id><published>2008-04-01T21:46:00.000-07:00</published><updated>2008-04-05T23:18:45.137-07:00</updated><title type='text'>Blogging from GNOME Blog</title><content type='html'>&lt;p&gt;After several futile attempts at blogging from within Emacs to Blogger.com, I found the GNOME Blog applet and am using it now. It is not emacs, but at least it is less clunky than the blogspot web interface.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-1741926536904826646?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/1741926536904826646/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=1741926536904826646' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/1741926536904826646'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/1741926536904826646'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2008/04/blogging-from-gnome-blog-after-several.html' title='Blogging from GNOME Blog'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-7737042119575351432</id><published>2008-03-31T01:54:00.000-07:00</published><updated>2008-04-01T08:32:56.454-07:00</updated><title type='text'>Nose: db setup and teardown</title><content type='html'>&lt;p&gt;In the last post I noted some documentation related to nose and ORM. Well, they did not work for me because my setup was not exactly like others'.  Here is what worked for me.  In &lt;pre&gt;tests/__init__.py&lt;/pre&gt;:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;  &lt;li&gt;I imported &lt;pre&gt;from pylons import config&lt;/pre&gt; to get the SQLAlchemy engine embedded in pylons's config variable&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;I imported &lt;pre&gt;import quickwiki.model as model&lt;/pre&gt; so that I could get hold of my models and metadata&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;I created a class &lt;pre&gt;TestModel&lt;/pre&gt; inheriting from &lt;pre&gt;TestCase&lt;/pre&gt; to hold the setup and teardown code&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;In the &lt;pre&gt;tearDown&lt;/pre&gt; method, I do &lt;pre&gt;model.metadata.drop_all(bind=engine)&lt;/pre&gt; to destroy all tables&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;In the &lt;pre&gt;setUp&lt;/pre&gt; method, I call &lt;pre&gt;tearDown&lt;/pre&gt; to destroy tables if they have not already been cleaned up, and then call &lt;pre&gt;model.metadata.create_all(bind=engine)&lt;/pre&gt; to create the tables.&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;Here is the final code:&lt;br /&gt;    &lt;pre&gt;&lt;br /&gt;&lt;span class="string"&gt;&lt;span class="highlight-current-line"&gt;"""Pylons application test package&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;br /&gt;When the test runner finds and executes tests within this directory,&lt;br /&gt;this file will be loaded to setup the test environment.&lt;br /&gt;&lt;br /&gt;It registers the root directory of the project in sys.path and&lt;br /&gt;pkg_resources, in case the project hasn't been installed with&lt;br /&gt;setuptools. It also initializes the application via websetup (paster&lt;br /&gt;setup-app) with the project's test.ini configuration file.&lt;br /&gt;"""&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;import&lt;/span&gt; os&lt;br /&gt;&lt;span class="keyword"&gt;import&lt;/span&gt; sys&lt;br /&gt;&lt;span class="keyword"&gt;from&lt;/span&gt; unittest &lt;span class="keyword"&gt;import&lt;/span&gt; TestCase&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;import&lt;/span&gt; pkg_resources&lt;br /&gt;&lt;span class="keyword"&gt;import&lt;/span&gt; paste.fixture&lt;br /&gt;&lt;span class="keyword"&gt;import&lt;/span&gt; paste.script.appinstall&lt;br /&gt;&lt;span class="keyword"&gt;from&lt;/span&gt; paste.deploy &lt;span class="keyword"&gt;import&lt;/span&gt; loadapp&lt;br /&gt;&lt;span class="keyword"&gt;from&lt;/span&gt; routes &lt;span class="keyword"&gt;import&lt;/span&gt; url_for&lt;br /&gt;&lt;br /&gt;&lt;span class="variable-name"&gt;__all__&lt;/span&gt; = [&lt;span class="string"&gt;'url_for'&lt;/span&gt;, &lt;span class="string"&gt;'TestController'&lt;/span&gt;]&lt;br /&gt;&lt;br /&gt;&lt;span class="variable-name"&gt;here_dir&lt;/span&gt; = os.path.dirname(os.path.abspath(__file__))&lt;br /&gt;&lt;span class="variable-name"&gt;conf_dir&lt;/span&gt; = os.path.dirname(os.path.dirname(here_dir))&lt;br /&gt;&lt;br /&gt;sys.path.insert(0, conf_dir)&lt;br /&gt;pkg_resources.working_set.add_entry(conf_dir)&lt;br /&gt;pkg_resources.require(&lt;span class="string"&gt;'Paste'&lt;/span&gt;)&lt;br /&gt;pkg_resources.require(&lt;span class="string"&gt;'PasteScript'&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;&lt;span class="variable-name"&gt;test_file&lt;/span&gt; = os.path.join(conf_dir, &lt;span class="string"&gt;'test.ini'&lt;/span&gt;)&lt;br /&gt;&lt;span class="variable-name"&gt;cmd&lt;/span&gt; = paste.script.appinstall.SetupCommand(&lt;span class="string"&gt;'setup-app'&lt;/span&gt;)&lt;br /&gt;cmd.run([test_file])&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;from&lt;/span&gt; pylons &lt;span class="keyword"&gt;import&lt;/span&gt; config&lt;br /&gt;&lt;span class="keyword"&gt;import&lt;/span&gt; quickwiki.model &lt;span class="keyword"&gt;as&lt;/span&gt; model&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="type"&gt;TestModel&lt;/span&gt;(TestCase):&lt;br /&gt;    &lt;span class="string"&gt;"""&lt;br /&gt;    We want the database to be created from scratch before each test and dropped&lt;br /&gt;    after each test (thus making them unit tests).&lt;br /&gt;    """&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;setUp&lt;/span&gt;(&lt;span class="keyword"&gt;self&lt;/span&gt;):&lt;br /&gt;        &lt;span class="keyword"&gt;self&lt;/span&gt;.tearDown()&lt;br /&gt;        engine = config[&lt;span class="string"&gt;'pylons.g'&lt;/span&gt;].sa_engine&lt;br /&gt;        model.metadata.create_all(bind=engine)&lt;br /&gt;        &lt;br /&gt;        page = model.Page()&lt;br /&gt;        page.title = &lt;span class="string"&gt;'FrontPage'&lt;/span&gt;&lt;br /&gt;        page.content = &lt;span class="string"&gt;'Welcome to the QuickWiki front page'&lt;/span&gt;&lt;br /&gt;        model.Session.save(page)&lt;br /&gt;        model.Session.commit()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;tearDown&lt;/span&gt;(&lt;span class="keyword"&gt;self&lt;/span&gt;):&lt;br /&gt;&lt;br /&gt;        engine = config[&lt;span class="string"&gt;'pylons.g'&lt;/span&gt;].sa_engine&lt;br /&gt;        model.metadata.drop_all(bind=engine)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="type"&gt;TestController&lt;/span&gt;(TestModel):&lt;br /&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;__init__&lt;/span&gt;(&lt;span class="keyword"&gt;self&lt;/span&gt;, *args, **kwargs):&lt;br /&gt;        wsgiapp = loadapp(&lt;span class="string"&gt;'config:test.ini'&lt;/span&gt;, relative_to=conf_dir)&lt;br /&gt;        &lt;span class="keyword"&gt;self&lt;/span&gt;.app = paste.fixture.TestApp(wsgiapp)&lt;br /&gt;        TestCase.__init__(&lt;span class="keyword"&gt;self&lt;/span&gt;, *args, **kwargs)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;&lt;br /&gt;Well, that almost worked. I fell foul of the 'setup-app' command and 'setup-config' in websetup.py. As you can see, the 'tests/__init__.py' file loads and executes the paster 'setup-app' command.  Stands to reason: the app should be 'set up' before I run tests.&lt;br /&gt;&lt;br /&gt;My setup-app is responsible for creating the DB and populating it with some initial data. But now I can't repeat my tests, because the first time I run the tests, the db is created and initial data put in, and the next time I run the tests... poof:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;IntegrityError: (IntegrityError) column title is not unique&lt;br /&gt;u'INSERT INTO pages (title, content) VALUES (?, ?)' ['FrontPage',&lt;br /&gt;'Welcome to the QuickWiki front page']&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Well, I thought, I would just drop everything in the db in the tearDown and ensure that tearDown is called before setUp is run. No go.  For some reason, it seems, the tearDown() is not working.&lt;br /&gt;&lt;br /&gt;Well, it seems I must have at least one test, for the fixtures to be run.  So I created a dummy test, and all was well.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-7737042119575351432?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/7737042119575351432/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=7737042119575351432' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/7737042119575351432'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/7737042119575351432'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2008/03/nose-db-setup-and-teardown.html' title='Nose: db setup and teardown'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-7065659542965056318</id><published>2008-03-31T01:17:00.000-07:00</published><updated>2008-03-31T01:22:41.355-07:00</updated><title type='text'>Pylons, Paste, Nose and ORMs</title><content type='html'>&lt;p&gt;Trying to do some unit tests in Pylons.  Pylons uses &lt;a href="http://somethingaboutorange.com/mrl/projects/nose/"&gt;nose&lt;/a&gt;. However, the &lt;a href="http://wiki.pylonshq.com/display/pylonsdocs/Unit+Testing"&gt;Pylons Unit Testing&lt;/a&gt; guide is a little short on describing how to setup and teardown the database before each test.  Here is all the docco to hand:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;  &lt;li&gt;&lt;a href="http://wiki.pylonshq.com/display/pylonsdocs/Unit+Testing"&gt;Pylons Unit Testing Guide&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;&lt;a href="http://pythonpaste.org/testing-applications.html"&gt;Testing Applications with Paste&lt;/a&gt;: more focused on the web part of the web application testiing&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;&lt;a href="http://wiki.pylonshq.com/pages/viewpage.action?pageId=9011209"&gt;Unit Testing with Pylons + SQLAlchemy&lt;/a&gt;: explains the use of test setups and teardowns to bring the test DB up and down&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-7065659542965056318?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/7065659542965056318/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=7065659542965056318' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/7065659542965056318'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/7065659542965056318'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2008/03/pylons-paste-nose-and-orms.html' title='Pylons, Paste, Nose and ORMs'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-7287258759636390200</id><published>2008-03-29T13:20:00.000-07:00</published><updated>2008-03-29T13:22:36.433-07:00</updated><title type='text'></title><content type='html'>&lt;p&gt;The &lt;a href="http://www.openhandsetalliance.com/"&gt;Open Handset Alliance&lt;/a&gt;, led by Google, is developing and releasing the &lt;a href="http://code.google.com/andriod/"&gt;Android&lt;/a&gt; mobile OS.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;But Android is not the only open mobile OS in town.  Another noteworthy player is the &lt;a href="http://www.limofoundation.org/"&gt;LiMo foundation&lt;/a&gt;.  Its mission is to produce an open, Linux based software platform for mobile devices.  LiMo has been around longer and there are already LiMo devices in the market.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;There are other efforts towards putting Linux on mobile devices:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;  &lt;li&gt;Motorola has a Linux-Java based platform called MOTOMAGX.  It is not an open polatform, AFAIK.  Also, Motorola's mobile division's future is uncertain rigt now.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;&lt;a href="http://www.openmoko.com/"&gt;Openmoko&lt;/a&gt; is trying to create both a software platform and hardware; they release the design to the hardware as well.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;&lt;a href="https://wiki.ubuntu.com/MobileAndEmbedded"&gt;Ubuntu Mobile&lt;/a&gt;&lt;/li&gt;.&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Nokia recently joined LiMo.  Does this mean a Googls vs Nokia fight in the mobile OS arean? Not to mention, Symbian OS and Windows Mobile are the current dominant players and already have application developer mindshare.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-7287258759636390200?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/7287258759636390200/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=7287258759636390200' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/7287258759636390200'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/7287258759636390200'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2008/03/open-handset-alliance-led-by-google-is.html' title=''/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-3855851191210512829</id><published>2008-03-16T22:50:00.000-07:00</published><updated>2008-03-16T23:03:33.140-07:00</updated><title type='text'>Typing accented/european characters in Emacs</title><content type='html'>Normally I never have to type anything but ASCII in Emacs.  But I have been doing some internationalization testing/development, and now and then need to type some non-ASCII characters.  Right now, I am happy to be able to type accented characters, though the ultimate goal is to be able to type in a wide variety of languages.&lt;br /&gt;&lt;br /&gt;To get emacs to display most languages and scripts:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt; &lt;li&gt; I ensure than my LANG environment is UTF-8&lt;br /&gt; &lt;li&gt; I install xfonts-intl-* and emacs-intl-fonts packages&lt;br /&gt; &lt;li&gt; I do a `C-h h' to see the multilingual hello file.... the languages of my interest should be present there&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;To type `interesting' characters in Emacs, I have found that this works:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt; &lt;li&gt; Change input method to RFC1345 by:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;C-x RET C-\ RET (select default input method RFC1345)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt; &lt;li&gt; Type an accented chanracter using its RFC1345 mnemonic.  E.g., to type the inverted exclamation mark used in Spanish I would type "&amp;!I" (makes sense: '&amp;' introduces the special character, ! is the excalamation mark, and I inverts it).&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;To be able to quickly lookup the RFC, I installed the `doc-rfc' meta-package. Now, to read RFC3412, for example, I do:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt; C-x C-f /usr/share/doc/RFC/links/rfc3412.txt.gz&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;and since I have installed the `emacs-goodies-el' package, which contains `rfcview', the RFC shows up nicely formatted in Emacs.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-3855851191210512829?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/3855851191210512829/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=3855851191210512829' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/3855851191210512829'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/3855851191210512829'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2008/03/typing-accentedeuropean-characters-in.html' title='Typing accented/european characters in Emacs'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-2934830057264128504</id><published>2008-03-15T10:06:00.000-07:00</published><updated>2008-03-15T10:11:29.619-07:00</updated><title type='text'>Ticking noise from HDD</title><content type='html'>Often, my laptop's HDD makes a chirp/ticking sound.  Tracked it down to &lt;a href="https://bugs.launchpad.net/ubuntu/+source/acpi-support/+bug/59695"&gt;this issue&lt;/a&gt; and &lt;a href="https://bugs.launchpad.net/ubuntu/+source/acpi-support/+bug/59695/comments/14"&gt;this fix&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;In short, the issue is that the HDD's firmware is set to save power by parking its head; but the OS is accessing the HDD quite frequently.  As a result, the HDD aggressively parks its head, and then has to unpark it because the OS does a read/write.  Turns out this parking/unparking could wear out the driver faster than if there was no power management.&lt;br /&gt;&lt;br /&gt;Additionally, this happens even when the laptop is on AC... where there is no power saving advantage.&lt;br /&gt;&lt;br /&gt;The fix linked above simply turns off the HDD's power management.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-2934830057264128504?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/2934830057264128504/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=2934830057264128504' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/2934830057264128504'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/2934830057264128504'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2008/03/ticking-noise-from-hdd.html' title='Ticking noise from HDD'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-5151885165733985166</id><published>2008-03-01T20:59:00.000-08:00</published><updated>2008-03-01T21:57:19.712-08:00</updated><title type='text'>How to choose clothes to wear</title><content type='html'>&lt;ul&gt;&lt;br /&gt;  &lt;li&gt; Choose according to your station&lt;br /&gt;  &lt;ul&gt;&lt;br /&gt;      &lt;li&gt; Choose according to what looks good on you&lt;br /&gt;      &lt;ul&gt;&lt;br /&gt;          &lt;li&gt; Exclude what you wore yesterday&lt;br /&gt;          &lt;ul&gt;&lt;br /&gt;               &lt;li&gt; Out of the rest, choose what you like &lt;/li&gt;&lt;br /&gt;          &lt;/ul&gt;&lt;br /&gt;          &lt;/li&gt;&lt;br /&gt;      &lt;/ul&gt;&lt;br /&gt;      &lt;/li&gt;&lt;br /&gt;  &lt;/ul&gt;&lt;br /&gt;  &lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-5151885165733985166?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/5151885165733985166/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=5151885165733985166' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/5151885165733985166'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/5151885165733985166'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2008/03/how-go-choose-clothes.html' title='How to choose clothes to wear'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-2415503625175919833</id><published>2008-01-25T09:52:00.000-08:00</published><updated>2008-01-26T23:46:07.122-08:00</updated><title type='text'>My First Emacs Lisp Function</title><content type='html'>&lt;pre&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;how-many-chars&lt;/span&gt; ()&lt;br /&gt;  (interactive)&lt;br /&gt;  (message &lt;span class="string"&gt;"We are %d chracters into this buffer."&lt;/span&gt;&lt;br /&gt;           (- (point)&lt;br /&gt;              (&lt;span class="keyword"&gt;save-excursion&lt;/span&gt;&lt;br /&gt;                (goto-char (point-min))&lt;br /&gt;                (point)))))&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-2415503625175919833?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/2415503625175919833/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=2415503625175919833' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/2415503625175919833'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/2415503625175919833'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2008/01/my-first-emacs-lisp-function.html' title='My First Emacs Lisp Function'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-3474555750340843272</id><published>2008-01-09T00:13:00.000-08:00</published><updated>2008-01-26T23:46:51.553-08:00</updated><title type='text'>Transporting and putting ssh public keys on remote servers the easy way</title><content type='html'>I login to a lot of Linux boxes, and I use ssh public keys to login.  When I create a new account/server, I have to:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;copy over my ssh public key to the remote host&lt;/li&gt;&lt;li&gt;login to the remote host&lt;/li&gt;&lt;li&gt;put the public key in the authorized_keys file, set the permissions etc.&lt;/li&gt;&lt;li&gt;delete the copy of the public key&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;I figured there ought to be an easier way to do this, and here it is:&lt;br /&gt;&lt;pre&gt;cat .ssh/id_rsa.pub | ssh parijat@192.168.1.97 "mkdir -p ~/.ssh ; cat - &gt;&gt; ~/.ssh/authorized_keys ; chmod -R go-rwx ~/.ssh"&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-3474555750340843272?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/3474555750340843272/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=3474555750340843272' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/3474555750340843272'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/3474555750340843272'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2008/01/transporting-and-putting-ssh-public.html' title='Transporting and putting ssh public keys on remote servers the easy way'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-1789962300113438113</id><published>2008-01-08T03:39:00.000-08:00</published><updated>2008-01-08T05:32:06.413-08:00</updated><title type='text'>Reactor vs Proactor</title><content type='html'>I found a comparison of the Reactor and the Proactor pattern &lt;a href="http://www.artima.com/articles/io_design_patterns2.html"&gt;here&lt;/a&gt;. Both patterns talk about isses that crop up when building a concurrent network server. Both are related alternatives to thread based concurrency (or could work as a complement to thread based concurrency).&lt;br /&gt;Both revolve around the concept of an IO De-multiplexer, event sources and event handlers. The driver program registers some event sources (e.g., sockets) with an IO de-multiplexer (e.g., select() or poll()). When an event occurs on a socket, a corresponding event handler is called. Of course, there must be some map between events from an event source to event handlers.&lt;br /&gt;I found that these patterns are more or less embodied in the Python asyncore and asynchat modules, and want to discuss how the modules implement these patterns.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;The Basics&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;We'll first compare the terminology of the patterns with that of the Python modules.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;blocking IO: this would translate to a &lt;span style="font-family:courier new;"&gt;read()/write()&lt;/span&gt; on a blocking socket. The call would block until there was some data available to read or the socket was closed. The thread making the call cannot do anything else.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;non-blocking, synchronous IO: this would translate to a &lt;span style="font-family:courier new;"&gt;read()/write()&lt;/span&gt; on a non-blocking socket. The call would return immediately, either with the data read/written, or with a signal that the IO operation could not complete (e.g., &lt;span style="font-family:courier new;"&gt;read()&lt;/span&gt; returns with &lt;span style="font-family:courier new;"&gt;-1&lt;/span&gt;, and &lt;span style="font-family:courier new;"&gt;errno&lt;/span&gt; set to &lt;span style="font-family:courier new;"&gt;EWOULBLOCK/EAGAIN&lt;/span&gt;. It is then the caller's responsibility to keep calling repeatedly until the operation succeeds.&lt;/li&gt;&lt;li&gt;non-blocking, asynchronous IO: this would translate to Unix &lt;span style="font-family: courier new;"&gt;SIGIO&lt;/span&gt; mechanisms (unfortunately, I am not familiar with this), or posix &lt;span style="font-family: courier new;"&gt;aio_*&lt;/span&gt; functions (not familiar with these either). Essentially, these IO calls return immediately, and the &lt;span style="font-style: italic;"&gt;OS&lt;/span&gt; starts doing the operation in a separate (kernel level) thread; when the operation is ready, the user code is given some notification.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;The Reactor Pattern: asyncore&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;According to the authors, here is how the Reactor pattern, which usually would use non-blocking synchronous IO, would work:&lt;br /&gt;&lt;blockquote&gt;Here's a read in Reactor:  &lt;br /&gt;&lt;ol&gt;&lt;li&gt;An event handler declares interest in I/O events that indicate readiness for read on a particular socket&lt;/li&gt;&lt;li&gt;The event de-multiplexer waits for events&lt;/li&gt;&lt;li&gt;An event comes in and wakes-up the demultiplexor, and the demultiplexor calls the  appropriate handler&lt;/li&gt;&lt;li&gt;The event handler performs the actual read operation, handles the data read, declares  renewed interest in I/O events, and returns control to the dispatcher&lt;/li&gt;&lt;/ol&gt;&lt;/blockquote&gt;How does this work in Python? Its done using the asyncore module.&lt;br /&gt;&lt;ol&gt;&lt;li&gt;The IO demux is the asyncore.loop() function; it listens for events on sockets using either the select() or poll() OS call. It uses a global or user supplied dictionary to map sockets to event handlers (see below). Event handlers are instances of asyncore.dispatcher (or its subclasses). A dispatcher contains a socket and registers itself in the global map, letting loop() know that its methods should be called in response to events on its sockets.  It also, through its readable() and writable() methods, lets loop() know what events it is interested in handling.&lt;/li&gt;&lt;li&gt;loop() uses select() or poll() to wait for events on the sockets it knows about.&lt;/li&gt;&lt;li&gt;select()/poll() returns; loop() goes through each socket that has an event, find the corresponding dispatcher object, determines the type of event, and calls a method corresponding to the event on the dispatcher object. In fact, loop() translates raw readable/writable events on sockets to slightly higher-level events using state information about the socket.&lt;/li&gt;&lt;li&gt;The dispatcher object's method is supposed to perform the actual IO: for example, in handle_read() we would read() the data off the socket and process it. Control then returns to loop(). Of course, one problem is that we should not do lengthy tasks in our handler, because then our server would not behave very concurrently and be unable to process other events in time.  But what if we did need to do time-taking tasks in response to the event? Thats a subject for another post. For now we assume that our handlers can return quickly enough that as a whole the server behaves pretty concurrently.&lt;/li&gt;&lt;/ol&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;&lt;br /&gt;The Proactor pattern: a psuedo-implementation in asynchat&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;According to the authors, here is how the Proactor pattern, which would usually use true asynchronous IO operations provided by the OS, would work:&lt;br /&gt;&lt;blockquote&gt;Here is a read operation in Proactor (true async):   &lt;ol&gt;&lt;li&gt;A handler initiates an asynchronous read operation (note: the OS must support asynchronous  I/O). In this case, the handler does not care about I/O readiness events, but instead registers  interest in receiving completion events.&lt;/li&gt;&lt;li&gt;The event demultiplexor waits until the operation is completed&lt;/li&gt;&lt;li&gt;While the event demultiplexor waits, the OS executes the read operation in a parallel kernel  thread, puts data into a user-defined buffer, and notifies the event demultiplexor that the read is  complete&lt;/li&gt;&lt;li&gt;The event demultiplexor calls the appropriate handler;&lt;/li&gt;&lt;li&gt;The event handler handles the data from user defined buffer, starts a new asynchronous  operation, and returns control to the event demultiplexor.&lt;/li&gt;&lt;/ol&gt;&lt;/blockquote&gt;How does this work in Python? Using the asynchat module.&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Event handlers are instances of asynchat.async_chat (or rather, its subclasses). Taking read as an example, the handler would register interest in reading data by providing a readable() method that returns True.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;loop() would then use it to wait on its socket until the socket was readable. When the socket become readable, instead of calling some OS function to read the data, async_chat.handle_read() is called.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;This method will slurp up all available data.&lt;/li&gt;&lt;li&gt;Then, handle_read() would call the collect_incoming_data() method of the subclass.  From the subclass's point of view, someone else has done the job of doing the actual IO, and it is being signaled that the IO operation is complete.&lt;/li&gt;&lt;li&gt;collect_incoming_data() processes the data, and by returning, implicitly starts a new async IO cycle.&lt;/li&gt;&lt;/ol&gt;The similarity between asynchat and Proactor is that from the application writer's point of view, he only has to write code to collect_incoming_data(). The difference is that, with asynchat, user level code is doing the IO, instead of true async facilities provided by the OS. The difference is greater when considering write operations. In a true Proactor, the event handler would initiate the write, and the event demultiplexer would wait for the completion event. However, in asynchat, the event handler (the subclass of async_chat) does not initiate the write per-se: it creates the data and pushes it onto a fifo, and loop(), indirectly through async_chat, writes it to the socket using synchronous non-blocking IO.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;A Unified API&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;Basically, Python's asynchat is providing an emulated Proactor interface to application writers. It would be good if asynchat could be redone so that it could use true async IO operations on OSes that support them, and fall back to synchronous IO when it is not available.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-1789962300113438113?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/1789962300113438113/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=1789962300113438113' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/1789962300113438113'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/1789962300113438113'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2008/01/reactor-vs-proactor.html' title='Reactor vs Proactor'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-1122659241732402591</id><published>2008-01-06T01:30:00.000-08:00</published><updated>2008-01-06T02:02:08.991-08:00</updated><title type='text'>Python's asynchat module</title><content type='html'>&lt;p&gt;&lt;b&gt;Introduction&lt;/b&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;As mentioned in the previous post, I was going to look at how to write a network server using Python's asynchat module.  To utilize the asynchat module's capabilities, I had to change the semantics of the echo server a little bit. The echo server using asyncore would echo back the data as soon as it got it.  The echo server using asynchat will echo data back line by line, where each line should be terminated by the string "\r\n".&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;b&gt;The async_chat interface for server writers&lt;/b&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;asynchat provides a higher level interface than asyncore.  In this interface, to write a server, you subclass asynchat.async_chat, and override two methods:&lt;br /&gt;&lt;dl&gt;&lt;br /&gt;    &lt;dt&gt;collect_incoming_data(data)&lt;/dt&gt;&lt;br /&gt;    &lt;dd&gt;Unlinks asyncore, you don't have to bother with handle_read() event.  The framework will read the data for you and call this method with the data.  You probably want to save this data somewhere, in preparation for processing it later&lt;/dd&gt;&lt;br /&gt;    &lt;dt&gt;found_terminator()&lt;/dt&gt;&lt;br /&gt;    &lt;dd&gt;The framework calls this method when it detects that a 'terminator' has been found in the incoming data stream. The framework decides this based on information you give to the framework using the set_terminator() method.&lt;/dd&gt;&lt;br /&gt;&lt;/dl&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;b&gt;Getting started&lt;/b&gt;&lt;/p&gt;&lt;br /&gt;So how do you use this module to write a server?  Just as with asyncore, you write a server class and instantiate and object; this object's socket is the server socket; you handle the event handle_accept() and create objects of class async_chat (or, rather, a subclass of async_chat that you created) to handle the client connection.  The only difference between asyncore and asynchat is, so far, the object that you instantiate to handle the client connection.&lt;br /&gt;&lt;br /&gt;Let's get started.  First we look at the driver code:&lt;br /&gt;&lt;br /&gt;&lt;tt&gt;server_main.py&lt;/tt&gt;:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    import asynchat_echo_server&lt;br /&gt;    ...&lt;br /&gt;    server = module.EchoServer((interface, port))&lt;br /&gt;    server.serve_forever()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;b&gt;The server class&lt;/b&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;Our 'EchoServer' class looks pretty much like before:&lt;br /&gt;&lt;br /&gt;&lt;tt&gt;asynchat_echo_server.py&lt;/tt&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class EchoServer(asyncore.dispatcher):&lt;br /&gt;&lt;br /&gt;    allow_reuse_address         = False&lt;br /&gt;    request_queue_size          = 5&lt;br /&gt;    address_family              = socket.AF_INET&lt;br /&gt;    socket_type                 = socket.SOCK_STREAM&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    def __init__(self, address, handlerClass=EchoHandler):&lt;br /&gt;        self.address            = address&lt;br /&gt;        self.handlerClass       = handlerClass&lt;br /&gt;&lt;br /&gt;        asyncore.dispatcher.__init__(self)&lt;br /&gt;        self.create_socket(self.address_family,&lt;br /&gt;                               self.socket_type)&lt;br /&gt;&lt;br /&gt;        if self.allow_reuse_address:&lt;br /&gt;            self.set_resue_addr()&lt;br /&gt;&lt;br /&gt;        self.server_bind()&lt;br /&gt;        self.server_activate()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    def server_bind(self):&lt;br /&gt;        self.bind(self.address)&lt;br /&gt;        log.debug("bind: address=%s:%s" % (self.address[0], self.address[1]))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    def server_activate(self):&lt;br /&gt;        self.listen(self.request_queue_size)&lt;br /&gt;        log.debug("listen: backlog=%d" % self.request_queue_size)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    def fileno(self):&lt;br /&gt;        return self.socket.fileno()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    def serve_forever(self):&lt;br /&gt;        asyncore.loop()&lt;br /&gt;    # TODO: try to implement handle_request()&lt;br /&gt;&lt;br /&gt;    # Internal use&lt;br /&gt;    def handle_accept(self):&lt;br /&gt;        (conn_sock, client_address) = self.accept()&lt;br /&gt;        if self.verify_request(conn_sock, client_address):&lt;br /&gt;            self.process_request(conn_sock, client_address)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    def verify_request(self, conn_sock, client_address):&lt;br /&gt;        return True&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    def process_request(self, conn_sock, client_address):&lt;br /&gt;        log.info("conn_made: client_address=%s:%s" % \&lt;br /&gt;                     (client_address[0],&lt;br /&gt;                      client_address[1]))&lt;br /&gt;        self.handlerClass(conn_sock, client_address, self)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    def handle_close(self):&lt;br /&gt;        self.close()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The difference is in the &lt;code&gt;handlerClass&lt;/code&gt;, which is defined to be EchoHandler as before, but is coded differently.   When we instantiate this object, it gets added to the global map of sockets that loop() is monitoring, and now loop() will monitor events on the client socket as well as the server socket.  There can be any number of sockets.  This behaviour is the same as that of asyncore.&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;b&gt;handling per-client connections&lt;/b&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;Here is how we start our new EchoHandler:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class EchoHandler(asynchat.async_chat):&lt;br /&gt;&lt;br /&gt;    LINE_TERMINATOR     = "\r\n"&lt;br /&gt;&lt;br /&gt;    def __init__(self, conn_sock, client_address, server):&lt;br /&gt;        asynchat.async_chat.__init__(self, conn_sock)&lt;br /&gt;        self.server             = server&lt;br /&gt;        self.client_address     = client_address&lt;br /&gt;        self.ibuffer            = []&lt;br /&gt;&lt;br /&gt;        self.set_terminator(self.LINE_TERMINATOR)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As can be seen, the init method calls async_chat.set_terminator() method with a string argument.  The string argument tells async_chat that a message or record is terminated when it encounters the string in the data.  Now, loop() will wait on this client socket and call async_chat's handle_read() method.  async_chat's handle_read() will read the data, look at it, and call the collect_incoming_data() method that you define:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    def collect_incoming_data(self, data):&lt;br /&gt;        log.debug("collect_incoming_data: [%s]" % data)&lt;br /&gt;        self.ibuffer.append(data)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As you can see, we just buffer the data here for later processing.&lt;br /&gt;&lt;br /&gt;Now, in the handle_read() method, async_chat will look for the string set by set_terminator().  If it finds it, then it will call the found_terminator() method that we define:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    def found_terminator(self):&lt;br /&gt;        log.debug("found_terminator")&lt;br /&gt;        self.send_data()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;When we find that we have a complete line (because it was terminated by "\r\n") we just send the data back.  After all, we are writing an echo server.&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;b&gt;Sending data&lt;/b&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;Sending data back to peers is a common task.  Using asyncore, we would create the data to be sent back and put it in a buffer.  Then we'd wait for handle_write() events, writing as much data from the buffer to the socket as possible in each event.&lt;br /&gt;&lt;br /&gt;asynchat makes this easier.  We create the data, put it in a so called 'producer' object, and push the producer object to a FIFO.  async_chat will then call each producer in turn, get data from it, send it out over the socket, piece by piece, until the producer is exhausted; it will then move on to the next producer.&lt;br /&gt;&lt;br /&gt;If it encounters a None object in place of a producer, async_chat will close the connection.&lt;br /&gt;&lt;br /&gt;All this can be accomplished with:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    def send_data(self):&lt;br /&gt;        data = "".join(self.ibuffer)&lt;br /&gt;        log.debug("sending: [%s]" % data)&lt;br /&gt;        self.push(data+self.LINE_TERMINATOR)&lt;br /&gt;        self.ibuffer = []&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As you can see, putting the data in a producer object and pushing it on to the FIFO takes just one line of code: &lt;code&gt;self.push(...)&lt;/code&gt;.  We dont have to define a producer class in the normal case because async_chat provides a &lt;code&gt;simple_producer&lt;/code&gt; class for us, and the &lt;code&gt;push()&lt;/code&gt; method creates an object of that class, populates it with whatever we supply, and then pushes it on to the FIFO.  This behaviour can be over-ridden, using the async_chat module API, but we will look at that in another installment.&lt;br /&gt;&lt;br /&gt;We have not bothered to push a None onto the FIFO, because we depend on the client closing the connection.  We might have put a timer and when the timer expired, close the connection ourselves, to handle clients that go away without properly closing the connection.&lt;br /&gt;&lt;br /&gt;Here is the full code:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;import logging&lt;br /&gt;import asyncore&lt;br /&gt;import asynchat&lt;br /&gt;import socket&lt;br /&gt;&lt;br /&gt;logging.basicConfig(level=logging.DEBUG, format="%(created)-15s %(levelname)8s %(thread)d %(name)s %(message)s")&lt;br /&gt;log                     = logging.getLogger(__name__)&lt;br /&gt;&lt;br /&gt;BACKLOG                 = 5&lt;br /&gt;SIZE                    = 1024&lt;br /&gt;&lt;br /&gt;class EchoHandler(asynchat.async_chat):&lt;br /&gt;&lt;br /&gt;    LINE_TERMINATOR     = "\r\n"&lt;br /&gt;&lt;br /&gt;    def __init__(self, conn_sock, client_address, server):&lt;br /&gt;        asynchat.async_chat.__init__(self, conn_sock)&lt;br /&gt;        self.server             = server&lt;br /&gt;        self.client_address     = client_address&lt;br /&gt;        self.ibuffer            = []&lt;br /&gt;&lt;br /&gt;        self.set_terminator(self.LINE_TERMINATOR)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    def collect_incoming_data(self, data):&lt;br /&gt;        log.debug("collect_incoming_data: [%s]" % data)&lt;br /&gt;        self.ibuffer.append(data)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    def found_terminator(self):&lt;br /&gt;        log.debug("found_terminator")&lt;br /&gt;        self.send_data()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    def send_data(self):&lt;br /&gt;        data = "".join(self.ibuffer)&lt;br /&gt;        log.debug("sending: [%s]" % data)&lt;br /&gt;        self.push(data+self.LINE_TERMINATOR)&lt;br /&gt;        self.ibuffer = []&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    def handle_close(self):&lt;br /&gt;        log.info("conn_closed: client_address=%s:%s" % \&lt;br /&gt;                     (self.client_address[0],&lt;br /&gt;                      self.client_address[1]))&lt;br /&gt;&lt;br /&gt;        asynchat.async_chat.handle_close(self)&lt;br /&gt;&lt;br /&gt;class EchoServer(asyncore.dispatcher):&lt;br /&gt;&lt;br /&gt;    allow_reuse_address         = False&lt;br /&gt;    request_queue_size          = 5&lt;br /&gt;    address_family              = socket.AF_INET&lt;br /&gt;    socket_type                 = socket.SOCK_STREAM&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    def __init__(self, address, handlerClass=EchoHandler):&lt;br /&gt;        self.address            = address&lt;br /&gt;        self.handlerClass       = handlerClass&lt;br /&gt;&lt;br /&gt;        asyncore.dispatcher.__init__(self)&lt;br /&gt;        self.create_socket(self.address_family,&lt;br /&gt;                               self.socket_type)&lt;br /&gt;&lt;br /&gt;        if self.allow_reuse_address:&lt;br /&gt;            self.set_resue_addr()&lt;br /&gt;&lt;br /&gt;        self.server_bind()&lt;br /&gt;        self.server_activate()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    def server_bind(self):&lt;br /&gt;        self.bind(self.address)&lt;br /&gt;        log.debug("bind: address=%s:%s" % (self.address[0], self.address[1]))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    def server_activate(self):&lt;br /&gt;        self.listen(self.request_queue_size)&lt;br /&gt;        log.debug("listen: backlog=%d" % self.request_queue_size)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    def fileno(self):&lt;br /&gt;        return self.socket.fileno()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    def serve_forever(self):&lt;br /&gt;        asyncore.loop()&lt;br /&gt;    # TODO: try to implement handle_request()&lt;br /&gt;&lt;br /&gt;    # Internal use&lt;br /&gt;    def handle_accept(self):&lt;br /&gt;        (conn_sock, client_address) = self.accept()&lt;br /&gt;        if self.verify_request(conn_sock, client_address):&lt;br /&gt;            self.process_request(conn_sock, client_address)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    def verify_request(self, conn_sock, client_address):&lt;br /&gt;        return True&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    def process_request(self, conn_sock, client_address):&lt;br /&gt;        log.info("conn_made: client_address=%s:%s" % \&lt;br /&gt;                     (client_address[0],&lt;br /&gt;                      client_address[1]))&lt;br /&gt;        self.handlerClass(conn_sock, client_address, self)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    def handle_close(self):&lt;br /&gt;        self.close()&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-1122659241732402591?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/1122659241732402591/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=1122659241732402591' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/1122659241732402591'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/1122659241732402591'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2008/01/pythons-asynchat-module.html' title='Python&apos;s asynchat module'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-2069608399241014063</id><published>2008-01-03T19:34:00.001-08:00</published><updated>2008-01-03T19:47:15.449-08:00</updated><title type='text'>Writing a server with Python's asyncore module</title><content type='html'>&lt;span style="font-weight: bold;"&gt;The Python asyncore and aynchat modules&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The Python standard library provides two modules---asyncore and&lt;br /&gt;asynchat---to help in writing concurrent network servers using&lt;br /&gt;event-based designs.  The documentation does not give good examples,&lt;br /&gt;so I am making some notes.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Overview&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The basic idea behind the asyncore module is that:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;there is a function, asyncore.loop() that does select() on a bunch of 'channels'.  Channels are thin wrappers around sockets.&lt;/li&gt;&lt;li&gt;when select reports an event on any socket, loop() examines the event and the socket's state to create a higher level event;&lt;/li&gt;&lt;li&gt;it then calls a method on the channel corresponding to the higher level event.&lt;/li&gt;&lt;/ul&gt;asyncore provides a low-level, but flexible API to build network&lt;br /&gt;servers.  asynchat builds upon asyncore and provides an API that is&lt;br /&gt;more suitable for request/response type of protocols.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;aysncore&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The asyncore module's API consists of:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;the loop method, to be called by a driver program;&lt;/li&gt;&lt;li&gt;the dispatcher class, to be subclassed to do useful stuff.  The dispatcher class is what is called 'channel' elsewhere.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;+-------------+           +--------+&lt;br /&gt;| driver code |---------&gt; | loop() |&lt;br /&gt;+-------------+           +--------+&lt;br /&gt;      |                      |&lt;br /&gt;      |                      | loop-dispatcher API (a)&lt;br /&gt;      |                      |&lt;br /&gt;      |                  +--------------+&lt;br /&gt;      |                  | dispatcher   |&lt;br /&gt;      +-----------------&gt;| subclass     |&lt;br /&gt;                         +--------------+&lt;br /&gt;                             |&lt;br /&gt;                             | dispatcher-logic API (b)&lt;br /&gt;                             |&lt;br /&gt;                         +--------------+&lt;br /&gt;                         | server logic |&lt;br /&gt;                         +--------------+&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This is all packaged nicely in an object oriented way.  So, we have&lt;br /&gt;the dispatcher class, that extends/wraps around the socket class (from&lt;br /&gt;the socket module in the Python standard library).  It provides all&lt;br /&gt;the socket class' methods, as well as methods to handle the higher&lt;br /&gt;level events.  You are supposed to subclass dispatcher and implement&lt;br /&gt;the event handling methods to do something useful.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;The loop-dispatcher API&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The loop function looks like this:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt; loop( [timeout[, use_poll[, map[,count]]]])&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;What is the map?  It is a dictionary whose keys are the&lt;br /&gt;file-descriptors, or fds, of the socket (i.e., socket.fileno()), and&lt;br /&gt;whose values are the dispatcher objects.&lt;br /&gt;&lt;br /&gt;When we create a dispatcher object, it automatically gets added to a&lt;br /&gt;global list of sockets.  The loop() function does a select() on this&lt;br /&gt;list unless we provide an explicit map.  (Hmm... we might always want&lt;br /&gt;to use explicit maps; then our loop calls will be thread safe and we&lt;br /&gt;will be able to launch multiple threads, each calling loop on&lt;br /&gt;different maps.)&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Methods a dispatcher subclass should implement&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;loop() needs some methods from the dispatcher object:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt; readable(): should return True, if you want the fd to be observed for read events;&lt;/li&gt;&lt;li&gt;writable(): should return True, if you want the fd to be observed for write events;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;If either read or write is true, the corresponding fd will be examined&lt;br /&gt;for errors also. Obviously, it makes no sense to have a dispatcher&lt;br /&gt;which returns False for both readable() and writable().&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;handle_read: socket is readable; dispatcher.recv() can be used to actually get the data&lt;/li&gt;&lt;li&gt;handle_write: socket is writable; dispatcher.send(data) can be used to actually send the data&lt;/li&gt;&lt;li&gt;handle_error: socket encountered an error&lt;/li&gt;&lt;li&gt;handle_expt: socket received OOB data (not really used in practice)&lt;/li&gt;&lt;li&gt;handle_close: socket was closed remotely or locally&lt;/li&gt;&lt;/ul&gt;Server sockets get one more event.&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt; handle_accept: a new incoming connection can be accept()ed.  Call the accept() method really accept the connection.  To create a server socket, call the bind() and listen() methods on it first.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Client sockets get this event:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;handle_connect: connection to remote endpoint has been made. To initiate the connection, first call the connect() method on it.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Other socket methods are available in dispatch:  create_socket(),&lt;br /&gt;close(), set_resue_addr().&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;How to write a server using asyncore&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;The standard library documentation gives a client example, but not a&lt;br /&gt;server example.  Here are some notes on the latter.&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Subclass dispatched to create a listening socket&lt;/li&gt;&lt;li&gt;In its handle_accept method, create new dispatchers.  They'll get added to the global socket map.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Note: the handlers must not block or take too much time... or the&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;server won't be concurrent.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;These socket-like functions that dispatcher extends should not be bypassed.  They do funky things to detect higher level events.  For e.g., how does asyncore figure out that the socket is closed?  If I remember correctly, there are two ways to detect whether a non-blocking socket is closed:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt; select() returns a read event, but when you call recv()/read() you get zero bytes;&lt;/li&gt;&lt;li&gt;you call send()/write() and it fails with an error (sending zero bytes is not an error).&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;(I wish I had a copy of Unix Network Programming by Stevens handy&lt;br /&gt;right now.)&lt;br /&gt;&lt;br /&gt;Will look at asynchat in another post.&lt;br /&gt;&lt;br /&gt;The code for the server is below:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;asyncore_echo_server.py&lt;/span&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;import logging&lt;br /&gt;import asyncore&lt;br /&gt;import socket&lt;br /&gt;&lt;br /&gt;logging.basicConfig(level=logging.DEBUG, format="%(created)-15s %(msecs)d %(levelname)8s %(thread)d %(name)s %(message)s")&lt;br /&gt;log                     = logging.getLogger(__name__)&lt;br /&gt;&lt;br /&gt;BACKLOG                 = 5&lt;br /&gt;SIZE                    = 1024&lt;br /&gt;&lt;br /&gt;class EchoHandler(asyncore.dispatcher):&lt;br /&gt;&lt;br /&gt;    &lt;br /&gt;    def __init__(self, conn_sock, client_address, server):&lt;br /&gt;        self.server             = server&lt;br /&gt;        self.client_address     = client_address&lt;br /&gt;        self.buffer             = ""&lt;br /&gt;&lt;br /&gt;        # We dont have anything to write, to start with&lt;br /&gt;        self.is_writable        = False&lt;br /&gt;&lt;br /&gt;        # Create ourselves, but with an already provided socket&lt;br /&gt;        asyncore.dispatcher.__init__(self, conn_sock)&lt;br /&gt;        log.debug("created handler; waiting for loop")&lt;br /&gt;&lt;br /&gt;    def readable(self):&lt;br /&gt;        return True     # We are always happy to read&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    def writable(self):&lt;br /&gt;        return self.is_writable # But we might not have&lt;br /&gt;                                # anything to send all the time&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    def handle_read(self):&lt;br /&gt;        log.debug("handle_read")&lt;br /&gt;        data = self.recv(SIZE)&lt;br /&gt;        log.debug("after recv")&lt;br /&gt;        if data:&lt;br /&gt;            log.debug("got data")&lt;br /&gt;            self.buffer += data&lt;br /&gt;            self.is_writable = True  # sth to send back now&lt;br /&gt;        else:&lt;br /&gt;            log.debug("got null data")&lt;br /&gt;&lt;br /&gt;    def handle_write(self):&lt;br /&gt;        log.debug("handle_write")&lt;br /&gt;        if self.buffer:&lt;br /&gt;            sent = self.send(self.buffer)&lt;br /&gt;            log.debug("sent data")&lt;br /&gt;            self.buffer = self.buffer[sent:]&lt;br /&gt;        else:&lt;br /&gt;            log.debug("nothing to send")&lt;br /&gt;        if len(self.buffer) == 0:&lt;br /&gt;            self.is_writable = False&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    # Will this ever get called?  Does loop() call&lt;br /&gt;    # handle_close() if we called close, to start with?&lt;br /&gt;    def handle_close(self):&lt;br /&gt;        log.debug("handle_close")&lt;br /&gt;        log.info("conn_closed: client_address=%s:%s" % \&lt;br /&gt;                     (self.client_address[0],&lt;br /&gt;                      self.client_address[1]))&lt;br /&gt;        self.close()&lt;br /&gt;        #pass&lt;br /&gt;&lt;br /&gt;            &lt;br /&gt;class EchoServer(asyncore.dispatcher):&lt;br /&gt;&lt;br /&gt;    allow_reuse_address         = False&lt;br /&gt;    request_queue_size          = 5&lt;br /&gt;    address_family              = socket.AF_INET&lt;br /&gt;    socket_type                 = socket.SOCK_STREAM&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    def __init__(self, address, handlerClass=EchoHandler):&lt;br /&gt;        self.address            = address&lt;br /&gt;        self.handlerClass       = handlerClass&lt;br /&gt;&lt;br /&gt;        asyncore.dispatcher.__init__(self)&lt;br /&gt;        self.create_socket(self.address_family,&lt;br /&gt;                               self.socket_type)&lt;br /&gt;&lt;br /&gt;        if self.allow_reuse_address:&lt;br /&gt;            self.set_resue_addr()&lt;br /&gt;&lt;br /&gt;        self.server_bind()&lt;br /&gt;        self.server_activate()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    def server_bind(self):&lt;br /&gt;        self.bind(self.address)&lt;br /&gt;        log.debug("bind: address=%s:%s" % (self.address[0], self.address[1]))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    def server_activate(self):&lt;br /&gt;        self.listen(self.request_queue_size)&lt;br /&gt;        log.debug("listen: backlog=%d" % self.request_queue_size)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    def fileno(self):&lt;br /&gt;        return self.socket.fileno()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    def serve_forever(self):&lt;br /&gt;        asyncore.loop()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    # TODO: try to implement handle_request()&lt;br /&gt;&lt;br /&gt;    # Internal use&lt;br /&gt;    def handle_accept(self):&lt;br /&gt;        (conn_sock, client_address) = self.accept()&lt;br /&gt;        if self.verify_request(conn_sock, client_address):&lt;br /&gt;            self.process_request(conn_sock, client_address)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    def verify_request(self, conn_sock, client_address):&lt;br /&gt;        return True&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    def process_request(self, conn_sock, client_address):&lt;br /&gt;        log.info("conn_made: client_address=%s:%s" % \&lt;br /&gt;                     (client_address[0],&lt;br /&gt;                      client_address[1]))&lt;br /&gt;        self.handlerClass(conn_sock, client_address, self)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    def handle_close(self):&lt;br /&gt;        self.close()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;and to use it:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    server = asyncore_echo_server.EchoServer((interface, port))&lt;br /&gt;    server.serve_forever()&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-2069608399241014063?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/2069608399241014063/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=2069608399241014063' title='63 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/2069608399241014063'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/2069608399241014063'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2008/01/writing-server-with-pythons-asyncore.html' title='Writing a server with Python&apos;s asyncore module'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>63</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-2002273618708093785</id><published>2007-12-09T07:49:00.000-08:00</published><updated>2007-12-09T08:01:15.715-08:00</updated><title type='text'>Convoluted C++</title><content type='html'>When reading Bruce Eckel's &lt;a href="http://www.mindview.net/Books/TICPP/ThinkingInCPP2e.html"&gt;TICCP&lt;/a&gt; volume 1 and doing an exercise on pointers-to-member syntax, I was reminded of one thing I like about Lisp and even more about Scheme: simple syntax.  In comparison, C++ is so awful.&lt;br /&gt;&lt;br /&gt;For e.g., Ex 25 in Ch 11 of Volume 1 asks us:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;Create a class containing an array of int. Can you index through&lt;br /&gt;this array using a pointer to member?&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Well, here is the class:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;class Foo {&lt;br /&gt;public:&lt;br /&gt;     int a[10];&lt;br /&gt;     Foo() {&lt;br /&gt;   for (int i = 0; i &lt; 10; i++)&lt;br /&gt;        a[i] = i;&lt;br /&gt;     }&lt;br /&gt;};&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Now, how do I declare the pointer-to-member that will refer to &lt;code&gt;a&lt;/code&gt;?&lt;br /&gt;&lt;br /&gt;After several tries, I got it:&lt;br /&gt;&lt;code&gt;int (Foo::*mem)[10];&lt;/code&gt;, which I use like this:&lt;br /&gt;&lt;code&gt;mem = &amp;Foo::a;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;I'd tried:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;int Foo::*mem;&lt;br /&gt;...&lt;br /&gt;mem = &amp;Foo::a;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;and&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;int Foo::*mem;&lt;br /&gt;...&lt;br /&gt;mem = &amp;Foo::a[0];&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;and&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;int Foo::*mem[];&lt;br /&gt;...&lt;br /&gt;mem = &amp;Foo::a[0];&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;(this crashed the compiler, GCC 3.4.4 on Cygwin)&lt;br /&gt;&lt;br /&gt;and&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;int Foo::*mem[10];&lt;br /&gt;...&lt;br /&gt;mem = &amp;Foo::a;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;The point is that special syntax to do everything is so icky once you have tasted Scheme. I'd say half or more of Bruce's book is about the syntax to get to the various features of the language.  You are focusing so much on learning syntax that you don't have any time to figure out how you'd use those features for real work.  And with the convoluted syntax in your way, you are not likely to be using the appropriate feature where it would be really handy.&lt;br /&gt;&lt;br /&gt;Nevertheless, TICPP does and excellent job of just that: describing the language's features and trying to give a scenario where that feature would be useful.  Too bad, knowing the features requires memorizing so much syntax.  In C, to get to a "feature", I'd just look at the man page for the relevant function.  There are no man pages for syntax, unfortunately.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-2002273618708093785?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/2002273618708093785/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=2002273618708093785' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/2002273618708093785'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/2002273618708093785'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2007/12/convoluted-c_09.html' title='Convoluted C++'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-8875358613783121065</id><published>2007-12-09T07:42:00.000-08:00</published><updated>2007-12-09T07:49:15.114-08:00</updated><title type='text'>Convoluted C++</title><content type='html'>Been going through Bruce Eckel's "Thinking in C++" books.  I've not programmed in C++ for a while, as I mostly program in Python (and am learning Ruby, Lisp, Erlang, and stuff). However, I felt it would be good to sharpen up my C++ and Java chops.&lt;br /&gt;&lt;br /&gt;So, I'd learnt C++ from Bjarne Stroustroup's book a long time ago. I thought I'd covered pretty much the whole book, except multiple inheritance.  Alas, I'd learnt little.  Well, enough to write a Python extension or two.&lt;br /&gt;&lt;br /&gt;Bruce does an excellent job of explaining the language if you are a programmer that already knows C.  Otherwise you should go and get familiar with C first.  Recommened reading.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-8875358613783121065?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/8875358613783121065/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=8875358613783121065' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/8875358613783121065'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/8875358613783121065'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2007/12/convoluted-c.html' title='Convoluted C++'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-2244568717916388388</id><published>2007-09-15T08:31:00.000-07:00</published><updated>2007-09-15T11:34:21.262-07:00</updated><title type='text'>Design Patterns in Python: II</title><content type='html'>As mentioned before, I am going through &lt;a href="http://video.google.com/videoplay?docid=-288473283307306160"&gt;Part II&lt;/a&gt; of a talk on DPs in Python. The first part of the talk focuses on the &lt;a href="http://home.earthlink.net/%7Ehuston2/dp/template_method.html"&gt;Template Method&lt;/a&gt; design pattern.  I have seen this pattern in the form of "algorithm" pattern in C++ template library and elsewhere (SAX parser library, various formatter libraries), but the video talk explains this very well. Alex Martelli offers "Self-Delgation" as an alternative name for this pattern. I suppose Algorithm could be another name, at least for some situations.&lt;br /&gt;&lt;br /&gt;Well, we all know that the TM pattern concerns itself with separating the generic part of an algorithm or process into an "organizing method", which calls "hooks" at appropriate points to execute certain steps of the algorithm/process. So far, so good. What is revealing is that this separation can be done in three ways:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Inheritance TM&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;The organizing method is defined in an ABC. Sub-classes provide concrete implementations of hooks. Obvious variations: the parent class provides no-op, or reasonable default implementations of hooks. E.g., Queue.Queue follows the TM pattern and provides concrete hook implementations, which provide a full-implementation of a FIFO queue. However, because of the TM pattern, it can be sub-classed and the hooks overridden to make a LIFO queue, for e.g.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Factored TM&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In this technique, the organizing method is in one class, and the hooks are implemented in another. The hook class can be passed to the  class implementing the organizing method as an argument at instantiation time.&lt;br /&gt;&lt;br /&gt;Further, hooks can be grouped. There can be a class per group.&lt;br /&gt;&lt;br /&gt;Even further, each hook can be a class by itself.  Then we get the Strategy pattern.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Mixin TM&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This is applicable for languages allowing multiple inheritance. The idea is:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;You multiply-inherit from a Mixin class that provides the "organizing methods"&lt;/li&gt;&lt;li&gt;You implement hooks to obtain the desired functionality.&lt;/li&gt;&lt;/ul&gt;An e.g.: you could derive your class from DictMixin, which causes your object to have the dictionary interface. All you have to do is implement a few hook methods, and automatically the rest of the rich dictionary interface (which is based on top of these hooks) is part of your class!&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Combined Inheritance, Factored, and Mixin TM&lt;/span&gt;&lt;br /&gt;One interesting use of these three techniques is in the SocketServer module:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;TCPServer is a concrete implementations of network servers. They use &lt;span style="font-weight: bold;"&gt;factored TM&lt;/span&gt;: the TCP/UDPServer implements the organizing method, and a class derived from BaseRequestHandler provides the concrete hooks. Well, BaseRequestHandler provides some no-op hook implementations, that serve as documentation.&lt;/li&gt;&lt;li&gt;You can inherit from BaseRequestHandler and override some methods to specialize its behaviour. This is &lt;span style="font-weight: bold;"&gt;inheritance TM&lt;/span&gt;.&lt;/li&gt;&lt;li&gt;You can inherit from TCPServer and ThreadinMixin. ThreadingMixin overrides the TM of TCPServer and provides the multi-threaded TM. You just provide the hook implementation (the request handler) to have a multi-threaded tcp server.&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-weight: bold;"&gt;The Strategy and State Patterns&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The main idea in the Strategy Pattern seems to be: say you have a complicated process (the organizing method or OM) with many &lt;span style="font-style: italic;"&gt;decision points&lt;/span&gt;.  A decision point is impemented by an object which is invoked for action.  That is, in TM we would have self.do_somthing(), but in Strategy we would have self.strat.do_something().  The strat is a reference to an object of some SC of a Strategy class that defines do_something().  To change the behaviour, we change strat from one SC instance to another.  That is, we would do something like: self.strat = Strat1(), or self.strat = Strat2() depending on the situation. Obviously, Strat1 and Strat2 don't have to know much about each other for this to be scalable/appropriate.&lt;br /&gt;&lt;br /&gt;In the State pattern, it is the state that knows how to switch behaviour. So we would have a decision point impemented as self.state.do_something(), and we would change behaviour by self.state.set_state(foo) or self.state.set_state(bar).&lt;br /&gt;&lt;br /&gt;In Python, we can blur the lines:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;1. Traditional&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;class Foo:&lt;br /&gt;    def bark():&lt;br /&gt;        print "bow"&lt;br /&gt;class Bar:&lt;br /&gt;    def bark():&lt;br /&gt;        print "wow"&lt;br /&gt;class OM:&lt;br /&gt;   def __init__():&lt;br /&gt;       self.strat  = Foo()&lt;br /&gt;   def work():&lt;br /&gt;        print "working"&lt;br /&gt;        self.strat.bark()&lt;br /&gt;        print "work done"&lt;br /&gt;    def change_voice():&lt;br /&gt;        self.strat = Bar()&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;2. Change method&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;class OM:&lt;br /&gt;    def __init__():&lt;br /&gt;    def bark():&lt;br /&gt;        print "bow"&lt;br /&gt;    def bark2():&lt;br /&gt;        print "wow"&lt;br /&gt;    def work():&lt;br /&gt;        print "working"&lt;br /&gt;       self.bark()&lt;br /&gt;        print "work done"&lt;br /&gt;    def change_voice():&lt;br /&gt;        &lt;span style="font-weight: bold;"&gt;self.bark = self.bark2&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;3. Change __class__&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;class OM:&lt;br /&gt;    def work():&lt;br /&gt;        print "working"&lt;br /&gt;        self.bark()&lt;br /&gt;        print "work done"&lt;br /&gt;    def bark():&lt;br /&gt;        print "bow"&lt;br /&gt;    def change_voice():&lt;br /&gt;        self.__class__ = Cat&lt;br /&gt;&lt;br /&gt;class Cat(OM):&lt;br /&gt;    def bark():&lt;br /&gt;        print "meow"&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-2244568717916388388?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/2244568717916388388/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=2244568717916388388' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/2244568717916388388'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/2244568717916388388'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2007/09/design-patterns-in-python-ii.html' title='Design Patterns in Python: II'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-1793708934763373559</id><published>2007-09-15T05:50:00.000-07:00</published><updated>2007-09-15T08:31:13.843-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python design patterns'/><title type='text'>Design Patterns in Python: I</title><content type='html'>&lt;span style="font-family: times new roman;"&gt;I found some time to catch up on general tech reading. Or rather, viewing. Because I have been viewing some excellent Google EngEDU videos.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: times new roman;"&gt;I just watched &lt;/span&gt;&lt;a style="font-family: times new roman;" href="http://video.google.com/videoplay?docid=-3035093035748181693"&gt;"Advanced Topics in Programming Languages Series: Python Design Patterns (Part 1)"&lt;/a&gt;&lt;span style="font-family: times new roman;"&gt;. The presenter, Alex Martelli, emphasized that Design Patterns can be useful, useless or dangerous/overkill depending on the particular language of choice. In other words, as the A good example would be Singleton, which is probably conceptually broken. Python provides modules instead that work handily. I found myself remembering a particular use of Singleton in my own Python code that is, well, icky and reeks of uselessness.  Anyway, Alex covered Creational Patterns in brief, and then went on to talk about Structural Patterns. They were more interesting and Alex gave some examples from the python library modules.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; font-family: times new roman;"&gt;Inheritance vs Composition&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family: times new roman;"&gt;Alex mentioned that object composition is favored over inheritance for most cases. By composing or "wrapping" we can do things that inheritance cannot, like selectively modifying the interface to an object, restricting the interface provided. (Inheritance cannot restrict the interface to the object).&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; font-family: times new roman;"&gt;Hold vs Wrap&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family: times new roman;"&gt;So we go with composition.  Composing objects leads to the choice of the "Hold vs Wrap" choice . I have faced this choice myself and I have seen the irritating consequences of the wrong choice.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: times new roman;"&gt;Say I have to compose objects O with subobject S. &lt;/span&gt;&lt;span style="font-weight: bold; font-family: times new roman;"&gt;Hold:&lt;/span&gt;&lt;span style="font-family: times new roman;"&gt; object O has a reference to S, nothing more. Then I would access S as &lt;/span&gt;&lt;span style="font-family: times new roman;font-family:courier new;" &gt;O.S.some_method(). &lt;span style="font-weight: bold;"&gt;Wrap:&lt;/span&gt; hold (maybe via a private name), and provide delegation, so that you can say O.some_method().&lt;br /&gt;&lt;br /&gt;The latter choice brings us into the realm of the Structural Design Patterns. At this point I would say that it becomes quite irritating to see code like this: foo.bar.some_method(). Basically O is exposing its internal structure to its clients and violating the "Law of Demeter":http://en.wikipedia.org/wiki/Law_of_Demeter.&lt;br /&gt;  &lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight: bold; font-family: times new roman;"&gt;Creational Patterns&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: times new roman;"&gt;Alex discussed the Singleton pattern and how it has fundamentally a problem with inheritance. He clarified that instead of a Singleton, what is usually required is a shared-state. He introduced the Borg/Monostate pattern that solves the same problem with more control possible.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: times new roman;"&gt;The next few patterns were the Factory series of patterns. Luckily, in Python, any callable can be passed into client code and can behave as an object factory or factory method. In fact, each class is a kind of factory, which can be customized to your heart's content by implementing/overriding the &lt;/span&gt;&lt;span style="font-family: times new roman;font-family:courier new;" &gt;__new__() method.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="font-size:100%;"&gt;Structural Patterns&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;The next few patterns to be discussed were:&lt;br /&gt;&lt;/span&gt;&lt;ul style="font-family: times new roman;"&gt;&lt;li&gt;Adapter: you have code expecting interface C, supplier code providing interface S (a superset of C), you write an adapter from C to S;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Facade: you have one or more objects; you create a single interface concentrating and simplifying the needed functionality for clients; a discussion of Facade was mentioned: &lt;a href="http://www.tonymarston.net/php-mysql/design-patterns.html"&gt;http://www.tonymarston.net/php-mysql/design-patterns.html&lt;/a&gt; (I have not looked at it yet)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Bridge: you have several implementations of abstraction A, all expecting functionality F, and you have (or want to have) several implementations providing F, and you dont want the client code to know of/depend on the F implementation. To do this, make sure that each implementation of A holds a reference R to F, and accesses the functionality F &lt;span style="font-style: italic;"&gt;only&lt;/span&gt; by delegating to R. A good example is the &lt;span style="font-weight: bold;"&gt;SocketServer&lt;/span&gt; module, where &lt;span style="font-weight: bold;"&gt;BaseServer&lt;/span&gt; is the abstraction A, &lt;span style="font-weight: bold;"&gt;BaseRequestHandler&lt;/span&gt; is the functionality F. The appropriate implementation of BaseRequestHandler is passed at creation time to the appropriate sub-class of BaseServer, which then keeps a reference to it (the R). When a new request handler is required, R() is called to create a new object.  Alex mentions that this is rather like a factory (but then, callables are factories), and that it is typical of Bridge DPs in Python to keep reference to a class and instantiate from it later. I wonder what a bridge to do a ORM would look like. I am now a little bit more confident of digging into the guts of &lt;a href="http://www.sqlalchemy.org/"&gt;SQLAlchemy&lt;/a&gt;.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Decorator: reuse/tweak without inheritance. Difference from Adapter/Facade would be that the interface does not change, but new functionality is somehow added.  A buffering adapter to a file object would be an example: the starting and resulting interface is the same, and the difference is only whether data is written to disk with or without buffering. The gzip module is an example.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Proxy: a proxy looks like a decorator but deals with lifetime, access, location issues rather than adding functionality. Hmm.. well, CORBA comes to mind.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-family: times new roman;"&gt;I am now going to watch &lt;/span&gt;&lt;a style="font-family: times new roman;" href="http://video.google.com/videoplay?docid=-288473283307306160"&gt;Part II&lt;/a&gt;&lt;span style="font-family: times new roman;"&gt; of the same series, which should deal with Behavioral Patterns.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: times new roman;"&gt;The slides used for the talk are available at &lt;/span&gt;&lt;a style="font-family: times new roman;" href="http://www.aleax.it/goo_pydp.pdf"&gt;http://www.aleax.it/goo_pydp.pdf&lt;/a&gt;&lt;span style="font-family: times new roman;"&gt; (8 MB PDF file).&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-1793708934763373559?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/1793708934763373559/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=1793708934763373559' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/1793708934763373559'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/1793708934763373559'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2007/09/i-found-some-time-to-catch-up-on.html' title='Design Patterns in Python: I'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-832681276612254077</id><published>2007-08-21T09:21:00.001-07:00</published><updated>2007-08-21T09:58:14.255-07:00</updated><title type='text'>What does Orkut have to do with crimes?</title><content type='html'>When I came home from work, I found Star News Asia reporting on how Orkut is a channel for depravity and crime.  There were blurbs like &lt;span style="font-style: italic;"&gt;"Bach ke rehna"&lt;/span&gt; (Hindi: save yourself, or stay away)&lt;span style="font-style: italic;"&gt;&lt;/span&gt;, &lt;span style="font-style: italic;"&gt;"Maut ki website"&lt;/span&gt; (Website of death), &lt;span style="font-style: italic;"&gt;"Kishoron ko kiya ja raha hai gumraha"&lt;/span&gt; (Youngsters are being misled),  &lt;span style="font-style: italic;"&gt;"Orkut par ankush jaroori?" &lt;/span&gt;(Should Orkut be censored?).&lt;br /&gt;&lt;br /&gt;It started with a report of a young girl murdered by a "friend" she met on Orkut. Google news shows similar cases &lt;a href="http://www.merinews.com/catFull.jsp?articleID=126031"&gt;being covered&lt;/a&gt; in &lt;a href="http://www.timesnow.tv/Newsdtls.aspx?NewsID=2169"&gt;various media&lt;/a&gt;. Too many to list. But what was strange about this news story was the exclusive focus on Orkut and blaming it for:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;A means of disseminating information to its users about rave parties (where all-night dancing and drugs are mixed together);&lt;/li&gt;&lt;li&gt;Misleading youngsters: while Orkut started as a means of connecting people, it has (according to the news story) become a hive of people talking about sex, rave parties, drugs, etc.&lt;/li&gt;&lt;/ul&gt;I find it strange to see Orkut being targeted so exclusively and being portrayed as promoting these activities. Several objections come to mind:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;So this girl met this guy online, befriended him, and met him in a hotel room alone; the guy turned out to be not that friendly and caring and murdered the girl. Agreed, one knows less about people you meet online. That requires some care and judgment on behalf of the user, no?&lt;/li&gt;&lt;li&gt;This meeting could have happened off line. I know of people being harmed by friendly looking acquaintances. What about that?&lt;/li&gt;&lt;li&gt;Is Orkut a motivator for rave parties, or are people wishing to go to rave parties using Orkut as a medium?&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Why Orkut, and not other social networking sites? Because it is more popular in India? If Orkut were not popular, would people have not used any other social networking platform? If none were available, would they not use email lists, IRC channels, BBSes? Shall we ban them?&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Agreed, all kinds of users interact online and some are not so moral. Is it Orkut's responsibility for what people post and tell each other? Is it a parent/guardian's responsibility or Orkut's that young children are not exposed to adult content?&lt;/li&gt;&lt;li&gt;The internet is a dangerous place. Many sites and forums contain adult content. Places other than Orkut are used to organize parties, some of them "not so nice". Shall we block the internet?&lt;/li&gt;&lt;/ul&gt;The news story seems to me to be more an attempt to sensationalize and demonize and less to point to a problem, inform people, or offer a balanced view.&lt;br /&gt;&lt;br /&gt;Here is where we could go from here:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Let's ban Orkut. Users will go to MySpace. Lets ban that, next. Then Facebook. Then ...&lt;/li&gt;&lt;li&gt;Finally we will have stomped out the evil social nets. Next target: BBSes and forums. They make it too easy for people to reach each other.&lt;/li&gt;&lt;li&gt;Ergh... we forgot about personal web-pages. No more personal websites. All media to be controlled and all publications to be approved by Almighty Censorship Alliance of Sensational News Channels.&lt;/li&gt;&lt;li&gt;Oops, we forgot about email. Bye bye, you nasty little disseminator of information about parties, drugs and personal details.&lt;/li&gt;&lt;li&gt;Now we plug the network cable out of the PC.&lt;/li&gt;&lt;li&gt;Ban books: too many porno flimsies around.&lt;/li&gt;&lt;li&gt;No more phone: you better come home and get interviewed by the family before you meet my daughter.&lt;/li&gt;&lt;li&gt;No talking to strangers on the roadside.&lt;/li&gt;&lt;li&gt;No roads.&lt;/li&gt;&lt;/ul&gt;Funny, I don't see anything about responsible and judicious use of communication anywhere in the list. I guess only news channels know how to do that. Ornery folks like us should just ban everything.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-832681276612254077?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/832681276612254077/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=832681276612254077' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/832681276612254077'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/832681276612254077'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2007/08/what-does-orkut-have-to-do-with-crimes.html' title='What does Orkut have to do with crimes?'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-3556728314995615865</id><published>2007-08-19T05:16:00.000-07:00</published><updated>2007-08-19T11:41:26.413-07:00</updated><title type='text'>Erlang bit syntax wrinkles</title><content type='html'>The Erlang bit syntax is wonderful, if complicated.&lt;br /&gt;&lt;br /&gt;Say you have a Binary like:&lt;br /&gt;&lt;blockquote style="font-family: courier new;"&gt;84&gt; Bin1 = &lt;&lt;1,0,2&gt;&gt;.&lt;br /&gt;&lt;&lt;1,0,2&gt;&gt;&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;Then, this will match:&lt;br /&gt;&lt;blockquote style="font-family: courier new;"&gt;85&amp;gt; &amp;lt;&amp;lt;A:1/binary, B:8, C:1/binary&amp;gt;&amp;gt; = Bin1.&lt;br /&gt;&amp;lt;&amp;lt;1,0,2&amp;gt;&amp;gt; %% A =&gt; 1, C =&gt; 2&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;But this will not match:&lt;br /&gt;&lt;blockquote style="font-family: courier new;"&gt;86&amp;gt; &amp;lt;&amp;lt;A:1/binary, B:1/binary, C:1/binary&amp;gt;&amp;gt; = Bin1.&lt;br /&gt;&lt;br /&gt;=ERROR REPORT==== 19-Aug-2007::17:46:17 ===&lt;br /&gt;Error in process &amp;lt;0.139.0&amp;gt; with exit value: {{badmatch,&amp;lt;&amp;lt;3&amp;gt;&amp;gt;},[{erl_eval,expr,3}]}&lt;br /&gt;&lt;br /&gt;** exited: {{badmatch,&amp;lt;&amp;lt;1,0,2&amp;gt;&amp;gt;,[{erl_eval,expr,3}]} **&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;It seems that I can specify a literal '0' as an int type (the default type) but not a binary type (see http://erlang.org/doc/reference_manual/expressions.html#bit_syntax for the gory details).  Wonder why?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-3556728314995615865?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/3556728314995615865/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=3556728314995615865' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/3556728314995615865'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/3556728314995615865'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2007/08/erlang-bit-syntax-wrinkles.html' title='Erlang bit syntax wrinkles'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-8507848496667035474</id><published>2007-08-17T10:59:00.000-07:00</published><updated>2007-08-17T13:34:42.159-07:00</updated><title type='text'>GNU Emacs, SLIME, Allegro</title><content type='html'>For some reason, I decided that I just had to switch to GNU Emacs from XEmacs on my WinXP laptop. So I went and downloaded Emacs 22.1 for Windows and installed it.&lt;br /&gt;&lt;br /&gt;Setting it up for Erlang was easy: I added this to C:\Documents and Settings\Parijat\.emacs:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;;; Erlang:&lt;br /&gt;;; Add the location of the elisp files to the load-path&lt;br /&gt;(setq load-path (cons  "C:/erl5.5.4/lib/tools-2.5.4/emacs" load-path))&lt;br /&gt;;; set the location of the man page hierarchy&lt;br /&gt;(setq erlang-root-dir "c:/erl5.5.4")&lt;br /&gt;;; add the home of the erlang binaries to the exec-path&lt;br /&gt;(setq exec-path (cons "c:/erl5.5.4/bin" exec-path))&lt;br /&gt;;; load and eval the erlang-start package to set up everything else &lt;br /&gt;(require 'erlang-start)&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Now, to setup Common Lisp. I use Allegro CL 8.0 Free Express Edition.  (Yes, one day I'll buy the full version and repay Franz, but only after getting myself Lisp Certified ;-))  I was following the instructions at &lt;a href="http://www.franz.com/emacs/slime.lhtml"&gt;http://www.franz.com/emacs/slime.lhtml&lt;/a&gt; and  &lt;a href="http://common-lisp.net/project/asdf-install/tutorial/setup.html"&gt;http://common-lisp.net/project/asdf-install/tutorial/setup.html&lt;/a&gt; .&lt;br /&gt;&lt;br /&gt;This is what I did:&lt;br /&gt;&lt;br /&gt;1. Opened up ACL and loaded asdf into it: &lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;International Allegro CL Free Express Edition&lt;br /&gt;8.0 [Windows] (Aug 17, 2007 22:59)&lt;br /&gt;Copyright (C) 1985-2006, Franz Inc., Oakland, CA, USA.  All Rights Reserved.&lt;br /&gt;&lt;br /&gt;This development copy of Allegro CL is licensed to:&lt;br /&gt;   Trial User&lt;br /&gt;&lt;br /&gt;CG version 1.81.2.23 / IDE version 1.80.2.21&lt;br /&gt;Loaded options from C:\Documents and Settings\Parijat\My Documents\allegro-prefs.cl.&lt;br /&gt;&lt;br /&gt;;; Optimization settings: safety 1, space 1, speed 1, debug 2.&lt;br /&gt;;; For a complete description of all compiler switches given the current optimization settings&lt;br /&gt;;; evaluate (EXPLAIN-COMPILER-SETTINGS).&lt;br /&gt;&lt;br /&gt;[changing package from "COMMON-LISP-USER" to "COMMON-GRAPHICS-USER"]&lt;br /&gt;CG-USER(1): (require :asdf)&lt;br /&gt;; Fast loading C:\Program Files\acl80-express\code\ASDF.001&lt;br /&gt;T&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;2. Then, downloaded asdf-install and unpacked it into "C:\Documents and Settings\Parijat\asdf-install" (the source tarball has several nested directories called asdf-install; the innnermost contains the .asd file, and that's the one I am referring to here).&lt;br /&gt;&lt;br /&gt;3. Told asdf about this directory:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;CG-USER(4): (pushnew "c:/Documents and Settings/Parijat/asdf-install/" asdf:*central-registry* :test #'equal)&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;and compiled and loaded asdf-install:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;CG-USER(5): (asdf:operate 'asdf:compile-op 'asdf-install)&lt;br /&gt;CG-USER(6): (asdf:operator 'asdf:load-op 'asdf-install)&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;4. Well, all this stuff was in the end to get SLIME.  I went to http://common-lisp.net/project/slime/#downloading and found the link to the latest SLIME. Now, on Windows, it seems asdf-install can't download and install tarballs off net (at least on Allegro CL).  Then I downloaded and put the slime tarball in C:\Temp.  Then:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;(setq asdf-install::*verify-gpg-signatures* nil)&lt;br /&gt;(setq *cygwin-bash-program* "c:/cygwin/bin/bash.exe")&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;and then:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;CG-USER(7): (asdf-install:install "C:/Temp/slime-2.0.tgz")&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;This did not work, complaining:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;Warning: Cannot find tar command "C:\\PROGRA~1\\Cygwin\\bin\\bash.exe".&lt;br /&gt;Error: Unable to extract tarball C:/Temp/slime-2.0.tgz.&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;It seems my mod to *cygwin-bash-program* did not take effect inside asdf.  Wonder why.  I even quit Allegro, restarted it, defined *cygwin-bash-program* before compiling and loading asdf-install. No luck. (One thing I did not try was to define asdf-install:*cygwin-bash-program* instead of vanilla *cygwin-bash-program*).  So I just went and hardcoded &lt;code&gt;tar-command()&lt;/code&gt; in &lt;code&gt;installer.lisp&lt;/code&gt; to return "C:\\cygwin\\bin\\bash.exe".&lt;br /&gt;&lt;br /&gt;Now the above command works.&lt;br /&gt;&lt;br /&gt;5. The Franz documentation instructs me to start "mlisp.exe" or "alise.exe" LISP image (see next step).  But my ACL 8.0 express did not come with these images; it only has "allegro-ansi.exe".  So, following the README in the acl80-express directory, I create the images, by starting allegro-ansi.exe, and in the "Debug window", typing the following:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;CG-USER(1): (build-lisp-image "sys:mlisp.dxl" :case-mode :case-sensitive-lower&lt;br /&gt;                      :include-ide nil :restart-app-function nil&lt;br /&gt;        :restart-init-function nil)&lt;br /&gt;CG-USER(2): (sys:copy-file "sys:allegro-ansi.exe" "sys:mlisp.exe")&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Now, I have a mlisp.exe and mlisp.dxl.  Sure enough, clicking on mlisp.exe launches a console LISP without the GUI.&lt;br /&gt;&lt;br /&gt;6. Now I have to load slime into Emacs.  According to the instructions on the Franz webpage, I created a file "C:\Documents and Settings\Parijat\.slime.lisp", with the contents:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;(load "C:/Documents and Settings/Parijat/.asdf-install-dir/site/slime-2.0/swank-loader.lisp")&lt;br /&gt;&lt;br /&gt;(sys:with-command-line-arguments (("p" :short port :required)&lt;br /&gt;      ("ef" :long ef :required))&lt;br /&gt;  (restvar)&lt;br /&gt;  (swank::create-server :port (parse-integer port :junk-allowed nil)&lt;br /&gt;          :style :spawn&lt;br /&gt;                 :dont-close t&lt;br /&gt;   :coding-system (or ef "latin-1")))&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;and added this to my .emacs:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;;; Allegro LISP + SLIME&lt;br /&gt;(push "c:/Documents and Settings/Parijat/.asdf-install-dir/site/slimw-2.0/" load-path)&lt;br /&gt;(require 'slime)&lt;br /&gt;(slime-setup)&lt;br /&gt;&lt;br /&gt;(setq slime-multiprocessing t)&lt;br /&gt;(setq *slime-lisp* "C:/Program Files/acl80-express/mlisp.exe")&lt;br /&gt;(setq *slime-port* 4006)&lt;br /&gt;&lt;br /&gt;(defun slime ()&lt;br /&gt;  (print "this is my slime")&lt;br /&gt;  (interactive)&lt;br /&gt;  (shell-command &lt;br /&gt;   (format "%s +B +cm -L \"c:/Documents and Settings/Parijat/.slime.lisp\" -- -p %s --ef %s &amp;"&lt;br /&gt;    *slime-lisp* *slime-port*&lt;br /&gt;    slime-net-coding-system))&lt;br /&gt;  (delete-other-windows)&lt;br /&gt;  (while (not (ignore-errors (slime-connect "localhost" *slime-port*)))&lt;br /&gt;    (sleep-for 0.2)))&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;I restart emacs. I say "M-x slime". And wait.  The mlisp.exe window shows up, but no connection.&lt;br /&gt;&lt;br /&gt;I figure that the problem is in (swank:create-server :port ...) which is supposed to start the swank server, does not work.  If I run it in the mlisp.exe window, the command does not return (okay, maybe it is not supposed to).  I can't telnet to port 4006 on the localhost, though.&lt;br /&gt;&lt;br /&gt;Puff!!!  It used to be simpler.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-8507848496667035474?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/8507848496667035474/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=8507848496667035474' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/8507848496667035474'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/8507848496667035474'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2007/08/gnu-emacs-slime-allegro.html' title='GNU Emacs, SLIME, Allegro'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-4918261312716863285</id><published>2007-08-07T10:24:00.000-07:00</published><updated>2007-08-07T12:26:41.573-07:00</updated><title type='text'>Concurrency: Erlang vs Java</title><content type='html'>Its fashionable to extol the high-performance of Erlang concurrency these days.  Programming Erlang, Section 8.11, has this problem:&lt;br /&gt;&lt;blockquote style="font-style: italic;"&gt;Write a ring benchmark. Create N processes in a ring. Send a message&lt;br /&gt;round the ring M times so that a total of N * M messages get&lt;br /&gt;sent. Time how long this takes for different values of N and M.&lt;/blockquote&gt;Well, I copied off the Java program from here: http://www.sics.se/~joe/ericsson/du98024.html&lt;br /&gt;&lt;br /&gt;And then I wrote this Erlang program:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;-module(threadperftest).&lt;br /&gt;-compile(export_all).&lt;br /&gt;&lt;br /&gt;timeit() -&gt;&lt;br /&gt; register(mainproc, self()),&lt;br /&gt; io:format(" Procs,  Mesgs, SpawnTotal,  SpawnProc,   RunTotal,    RunMesg~n", []),&lt;br /&gt; timeit_aux(1000, 1000, 10000),&lt;br /&gt; init:stop().&lt;br /&gt;&lt;br /&gt;timeit_aux(NProcs, NMsgs, NProcsMax) -&gt;&lt;br /&gt; if NProcs =&lt;&gt;&lt;br /&gt;     main([NProcs, NMsgs]),&lt;br /&gt;     timeit_aux(NProcs+1000, NMsgs, NProcsMax);&lt;br /&gt;    true -&gt; void&lt;br /&gt; end.&lt;br /&gt;    &lt;br /&gt;main([N, M]) -&gt;&lt;br /&gt; {_, _W0} = statistics(wall_clock),&lt;br /&gt; FirstPid = spawn(fun loop0/0),&lt;br /&gt; % io:format("First: ~p~n", [FirstPid]),&lt;br /&gt; LastPid = setup(N-1, FirstPid),&lt;br /&gt; FirstPid ! LastPid,&lt;br /&gt; {_, W1} = statistics(wall_clock),&lt;br /&gt; LastPid ! M,&lt;br /&gt; receive&lt;br /&gt; stop -&gt;&lt;br /&gt;     void&lt;br /&gt; end,&lt;br /&gt; {_, W2} = statistics(wall_clock),&lt;br /&gt; io:format("~6B, ~6B, ~10g, ~10g, ~10g, ~10g~n", [N, M, W1*1.0, 1.0*W1/N, W2*1.0, W2*1000.0/(M*N+N)]).&lt;br /&gt;&lt;br /&gt;setup(0, PrevPid) -&gt;&lt;br /&gt; PrevPid;&lt;br /&gt;setup(HowMany, PrevPid) -&gt;&lt;br /&gt; Pid = spawn(fun() -&gt; loop(PrevPid) end),&lt;br /&gt; % io:format("~p: Linking to: ~p~n", [Pid, PrevPid]),&lt;br /&gt; setup(HowMany - 1, Pid).&lt;br /&gt;&lt;br /&gt;loop0() -&gt;&lt;br /&gt; receive&lt;br /&gt; LinkedPid when is_pid(LinkedPid) -&gt;&lt;br /&gt;     % io:format("First: ~p: Linking to ~p~n", [self(), LinkedPid]),&lt;br /&gt;     loop1(LinkedPid);&lt;br /&gt; X -&gt;&lt;br /&gt;     erlang:error({unknownMessageError, X, self()})&lt;br /&gt; end.&lt;br /&gt;&lt;br /&gt;loop1(LinkedPid) -&gt;&lt;br /&gt; receive&lt;br /&gt; M when is_integer(M), M &gt; 0 -&gt;&lt;br /&gt;     % io:format("~p: Received: ~p~n", [self(), M]),&lt;br /&gt;     LinkedPid ! (M-1),&lt;br /&gt;     loop1(LinkedPid);&lt;br /&gt; M when is_integer(M), M =:= 0 -&gt;&lt;br /&gt;     % io:format("~p: Received: ~p, Terminating~n", [self(), M]),&lt;br /&gt;     mainproc ! stop,&lt;br /&gt;     done;&lt;br /&gt; X -&gt;&lt;br /&gt;     erlang:error({unknownMessageError, X, self()})&lt;br /&gt; end.&lt;br /&gt;  &lt;br /&gt;loop(LinkedPid) -&gt;&lt;br /&gt; receive&lt;br /&gt; M when is_integer(M), M &gt; 0 -&gt;&lt;br /&gt;     % io:format("~p: Received: ~p~n", [self(), M]),&lt;br /&gt;     LinkedPid ! M,&lt;br /&gt;     loop(LinkedPid);&lt;br /&gt; M when is_integer(M), M =:= 0 -&gt;&lt;br /&gt;     % io:format("~p: Received: ~p, Terminating~n", [self(), M]),&lt;br /&gt;     LinkedPid ! M,&lt;br /&gt;     done;&lt;br /&gt; X -&gt;&lt;br /&gt;     erlang:error({unknownMessageError, X, self()})&lt;br /&gt; end.&lt;br /&gt;&lt;/pre&gt;&lt;/blockquote&gt;The performance is something like this (all times are in milliseconds, except the last one, which is in microseconds):&lt;br /&gt;&lt;pre&gt; Procs,  Mesgs, SpawnTotal,  SpawnProc,   RunTotal,    RunMesg&lt;br /&gt; 1000,   1000, 0.00000e+0, 0.00000e+0,    266.000,   0.265734&lt;br /&gt; 2000,   1000,    16.0000, 8.00000e-3,    562.000,   0.280719&lt;br /&gt; 3000,   1000, 0.00000e+0, 0.00000e+0,    969.000,   0.322677&lt;br /&gt; 4000,   1000, 0.00000e+0, 0.00000e+0,    1672.00,   0.417582&lt;br /&gt; 5000,   1000,    15.0000, 3.00000e-3,    2469.00,   0.493307&lt;br /&gt; 6000,   1000,    16.0000, 2.66667e-3,    3234.00,   0.538462&lt;br /&gt; 7000,   1000,    16.0000, 2.28571e-3,    4015.00,   0.572998&lt;br /&gt; 8000,   1000,    16.0000, 2.00000e-3,    4750.00,   0.593157&lt;br /&gt; 9000,   1000,    31.0000, 3.44444e-3,    5469.00,   0.607060&lt;br /&gt;10000,   1000,    31.0000, 3.10000e-3,    6375.00,   0.636863&lt;br /&gt;&lt;/pre&gt;The numbers in the RunMesg column are the time it takes to send one message from one process to another.  Why is it increasing?  Does the runtime have to do more work to find the target process?&lt;br /&gt;&lt;br /&gt;Here is the Java output (program below):&lt;br /&gt;&lt;pre&gt; Procs,  Mesgs, SpawnTotal, SpawnProcs,   RunTotal,   RunMesg&lt;br /&gt;  100,   1000,     16.000,      0.160,  15531.000,     15.531&lt;br /&gt;  200,   1000,     62.000,      0.310,  27047.000,     27.047&lt;br /&gt;  300,   1000,     94.000,      0.313,  37828.000,     37.828&lt;br /&gt;  400,   1000,    125.000,      0.313,  46859.000,     46.859&lt;br /&gt;  500,   1000,    141.000,      0.282,  54578.000,     54.578&lt;br /&gt;  600,   1000,    203.000,      0.338,  60594.000,     60.594&lt;br /&gt;  700,   1000,    234.000,      0.334,  65282.000,     65.282&lt;br /&gt;  800,   1000,    406.000,      0.508,  68156.000,     68.156&lt;br /&gt;  900,   1000,    484.000,      0.538,  69782.000,     69.782&lt;br /&gt; 1000,   1000,    735.000,      0.735,  70015.000,     70.015&lt;br /&gt;&lt;/pre&gt;The last column is &lt;strong&gt;in milliseconds&lt;/strong&gt; whereas for Erlang it was in microseconds, AND the number of procs is 100-1000, whereas for Erlang they were 1000-10,000.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-4918261312716863285?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/4918261312716863285/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=4918261312716863285' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/4918261312716863285'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/4918261312716863285'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2007/08/concurrency-erlang-vs-java.html' title='Concurrency: Erlang vs Java'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-4762663428320403514</id><published>2007-08-06T05:47:00.000-07:00</published><updated>2007-08-06T12:07:40.518-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><category scheme='http://www.blogger.com/atom/ns#' term='exercise'/><category scheme='http://www.blogger.com/atom/ns#' term='concurrency'/><category scheme='http://www.blogger.com/atom/ns#' term='book'/><title type='text'>Programming Erlang exercise problem: trouble in the concurrent universe?</title><content type='html'>Going through Programming Erlang, encountered this exercise problem in Section 8.11:&lt;br /&gt;&lt;blockquote style="font-style: italic;"&gt;Write a function start(AnAtom, Fun) to register AnAtom as spawn(Fun).  Make sure your program works correctly in the case when two parallel processes simultaneously evaluate start/2. In this case, you must guarantee that one of these processes succeeds and the other fails.&lt;/blockquote&gt;Here is my first solution, and it seems to compile and run.&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;-module(register_function).&lt;br /&gt;-export([start/2]).&lt;br /&gt;&lt;br /&gt;start(AnAtom, Fun) -&gt;&lt;br /&gt;  case whereis(AnAtom) of&lt;br /&gt;       undefined -&gt; register(AnAtom, spawn(Fun));&lt;br /&gt;       _ -&gt; error&lt;br /&gt;  end.&lt;br /&gt;&lt;/pre&gt;&lt;/blockquote&gt;Now, is there  race condition in the above? I mean, consider Processes A and B both executing start/2:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Process A calls whereis(AnAtom) which evaluates to 'undefined';&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Process B calls whereis(AnAtom) which evaluates to 'undefined';&lt;/li&gt;&lt;li&gt;Process A calls register(AnAtom, spawn(Fun)), which succeeds and returns true;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Process B calls register(AnAtom, spawn(Fun)), which fails as AnAtom has been registered&lt;/li&gt;&lt;/ol&gt;Superficially, we have satisfied the requirements of the problem: Process A succeeded and B failed.  But the sinister problem is that spawn(...) has been called by both processes anyway. Thus we have two instances of Fun being executed, with one of them in an anonymous process that I, at least for now, don't know how to access.&lt;br /&gt;&lt;br /&gt;Ironically, the problem seems to be because presumably register puts AnAtom in some "global" table. Exactly the kind of concurrency problem that Erlang tries to avoid.&lt;br /&gt;&lt;br /&gt;How would this be solved? Here is an idea:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Create a "register_function" server;&lt;/li&gt;&lt;li&gt;start/2 sends a message to this server with AnAtom and Fun as contents&lt;/li&gt;&lt;li&gt;presumably the server processes one message at a time and the code between matching a message and executing some expressions is atomic with respect to other messages to the same server&lt;/li&gt;&lt;/ol&gt;Puff. You'd think Erlang would come with built in functionality for this. Is this important?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-4762663428320403514?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/4762663428320403514/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=4762663428320403514' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/4762663428320403514'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/4762663428320403514'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2007/08/programming-erlang-exercise-problem.html' title='Programming Erlang exercise problem: trouble in the concurrent universe?'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-850852652856767872</id><published>2007-04-12T10:56:00.001-07:00</published><updated>2007-04-12T10:56:00.733-07:00</updated><title type='text'>That free lunch that you were talking about....</title><content type='html'>&lt;div xmlns='http://www.w3.org/1999/xhtml'&gt;&lt;a href='http://news.bbc.co.uk/2/hi/science/nature/6533169.stm'&gt;Reading on BBC&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;From his office in Nevada, entrepreneur Dennis Hope has spawned a multi-million-dollar property business selling plots of lunar real estate at $20 (£10) an acre.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;This guy has made $9m till now.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Buyers include Hollywood stars, large corporations - including the Hilton and Marriot hotel chains - and even former US presidents Ronald Reagan and Jimmy Carter. George W Bush is also said to be a stake holder.&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;What drives me nuts is:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;day. He allocates land by simply closing his eyes and pointing to a map of the Moon.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;So, uh, who said there was no free lunch? I mean, by the time this guy's claims are challenged in court, he would have enjoyed much of that $9m.&lt;br /&gt;&lt;br /&gt;Technorati Tags: &lt;a rel='tag' href='http://technorati.com/tag/greed stupid' class='performancingtags'&gt;greed stupid&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-850852652856767872?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/850852652856767872/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=850852652856767872' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/850852652856767872'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/850852652856767872'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2007/04/that-free-lunch-that-you-were-talking.html' title='That free lunch that you were talking about....'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-3244335758964643635</id><published>2007-04-10T05:25:00.000-07:00</published><updated>2007-04-10T05:37:14.849-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='emacs font'/><title type='text'>Emacs and bitstream fonts</title><content type='html'>I use emacs on Ubuntu heavily and naturally want a nice font to use. I chose to use the "bitstream vera sans mono" font. Well, easy does it: Options-&gt;Customize Emacs-&gt;Specific Face: "default", and setting the value of "Font Family" to "bitstream-bitstream vera sans mono" and value of  "Height" to "100" gave me the nice font that I wanted. "Custom" created the settings for me:&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;(custom-set-faces&lt;br /&gt;  ;; custom-set-faces was added by Custom -- don't edit or cut/paste it!&lt;br /&gt;  ;; Your init file should contain only one such instance.&lt;br /&gt; '(default ((t (:inherit nil :stipple nil :background "gray12" :foreground "green" :inverse-video nil :box nil :strike-through nil :overline nil :underline nil :slant normal :weight normal :height 117 :width normal :family "bitstream vera sans mono")))))&lt;/blockquote&gt;&lt;br /&gt;I should mention that I am using the "color-scheme" package and using a nice looking color scheme.  The color information probably comes from there.&lt;br /&gt;&lt;br /&gt;But then I tried to create a new frame (C-x 5 2) and lo! the new frame had my old font. Reading the emacs faq gave the answer: I'd have to set the font again in the variable that determines the options for all new frames.  So I set in my "~/.emacs":&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;(setq default-frame-alist&lt;br /&gt;      '((font . "-bitstream-bitstream vera sans mono-medium-r-normal--0-0-0-0-m-0-iso8859-1")))&lt;/blockquote&gt;and new frames would now have the specified font, which happens to be the same as the original. I wonder if it is a coincidence that the size of the font in new frames and initial matches, because I have not specified the height of the font in this variable.&lt;br /&gt;&lt;br /&gt;This seems like a ugly way: why specify the font properties in two different places? So I commented out the face created by custom, and oops! new frames would have a background of white. So I had to undo the change and let the fonts be specified in two places.  But at least the initial and new frames have the same good looking font.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-3244335758964643635?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/3244335758964643635/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=3244335758964643635' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/3244335758964643635'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/3244335758964643635'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2007/04/emacs-and-bitstream-fonts.html' title='Emacs and bitstream fonts'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-6956501924870659236</id><published>2007-04-10T05:17:00.000-07:00</published><updated>2007-04-10T05:25:03.883-07:00</updated><title type='text'>Ubuntu Edgy and X11 font bogosity</title><content type='html'>Using Ubuntu Edgy recently.  I'd installed the great bitstream vera fonts and could use them in gnome, but could not see them in X applications, in particular emacs.&lt;br /&gt;&lt;br /&gt;Turned out that /etc/X11/xorg.conf had a wrong configuration for fonts.  It originally said:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new;"&gt;Section "Files"&lt;br /&gt;        FontPath        "/usr/share/X11/fonts/misc"&lt;br /&gt;        FontPath        "/usr/share/X11/fonts/cyrillic"&lt;br /&gt;        FontPath        "/usr/share/X11/fonts/100dpi/:unscaled"&lt;br /&gt;        FontPath        "/usr/share/X11/fonts/75dpi/:unscaled"&lt;br /&gt;        FontPath        "/usr/share/X11/fonts.Type1"&lt;br /&gt;        FontPath        "/usr/share/X11/fonts/100dpi"&lt;br /&gt;        FontPath        "/usr/share/X11/fonts/75dpi"&lt;br /&gt;        FontPath        "/usr/share/X11/fonts/misc"&lt;br /&gt;        # path to defoma fonts&lt;br /&gt;        FontPath        "/var/lib/defoma/x-ttcidfont-conf.d/dirs/TrueType"&lt;br /&gt;EndSection&lt;br /&gt;&lt;/blockquote&gt;Turns out that the directory "/usr/share/X11/fonts" is wrong and should be "/usr/share/fonts/X11",  I changed the section to:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new;"&gt;Section "Files"&lt;br /&gt;        FontPath        "/usr/share/fonts/X11/misc"&lt;br /&gt;        FontPath        "/usr/share/fonts/X11/cyrillic"&lt;br /&gt;        FontPath        "/usr/share/fonts/X11/100dpi/:unscaled"&lt;br /&gt;        FontPath        "/usr/share/fonts/X11/75dpi/:unscaled"&lt;br /&gt;        FontPath        "/usr/share/fonts/X11/Type1"&lt;br /&gt;        FontPath        "/usr/share/fonts/X11/100dpi"&lt;br /&gt;        FontPath        "/usr/share/fonts/X11/75dpi"&lt;br /&gt;        FontPath        "/usr/share/fonts/X11/misc"&lt;br /&gt;        # path to defoma fonts&lt;br /&gt;        FontPath        "/var/lib/defoma/x-ttcidfont-conf.d/dirs/TrueType"&lt;br /&gt;        FontPath        "/var/lib/defoma/x-ttcidfont-conf.d/dirs/CID"&lt;br /&gt;EndSection&lt;/blockquote&gt;Then I restarted X. But no go. xlsfonts would still not list the bitstream fonts.&lt;br /&gt;The bitstream fonts were linked to from "/var/lib/defoma/x-ttcidfont-conf.d/dirs/TrueType". I went into that directory, and ran&lt;br /&gt;&lt;blockquote&gt;&lt;blockquote&gt;&lt;/blockquote&gt;$  sudo ttmkfdir -o fonts.dir -d .&lt;/blockquote&gt;Then I restarted X again. And yes, this time xlsfonts could see the bitstream fonts. So the problems were the wrong font paths and the fact that the fonts.dir file had not been created in the defoma directory. It seems that this is a new bug in Edgy.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-6956501924870659236?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/6956501924870659236/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=6956501924870659236' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/6956501924870659236'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/6956501924870659236'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2007/04/ubuntu-edgy-and-x11-font-bogosity.html' title='Ubuntu Edgy and X11 font bogosity'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-2364815416050153160</id><published>2007-03-29T09:44:00.001-07:00</published><updated>2007-03-29T09:56:12.489-07:00</updated><title type='text'>Software is knowledge</title><content type='html'>&lt;div xmlns="http://www.w3.org/1999/xhtml"&gt;People screaming about software engineering were pulling the wool over our eyes and trying to make a quick buck, all along, as we all suspected when we attended those boring software engineering classes in CS courses.&lt;br /&gt;&lt;br /&gt;Robert Glass got it right.  Software is NOT an artifact of engineering.  Making software is not like making a bridge.&lt;br /&gt;&lt;br /&gt;Software is an embodiment of knowledge. &lt;span style="font-weight: bold;"&gt;Executable knowledge.&lt;/span&gt; Knowledge that when pasted over some silicon and stimulated with electricity can cause things to happen.  &lt;span style="font-size:78%;"&gt;By itself very little beyond flipping voltage levels in areas of said silicon. But give it some tools (like a serial port, cable and lcd screen) and it can communicate. Give it more tools (serial port, cable, robot arm) and it can create physical changes in its environment.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Yeah, you got it. Software is less like a scalpel, more like a doctor.&lt;br /&gt;&lt;br /&gt;Why the sudden light?&lt;br /&gt;&lt;br /&gt;Well, I was trying to document high-availability solutions with MySQL. When going through the nitty-gritty of &lt;a href="http://dev.mysql.com/doc/refman/5.0/en/mysql-cluster-limitations.html#mysql-cluster-limitations-exclusive-to-cluster"&gt;MySQL Cluster's limitations&lt;/a&gt; (no offense intended: it is probably a good product; I am not dissing the software) it occured to me that they warranted noting down, so that my team would not have to read so much should they deal with MySQL Cluster. I found myself writing a short note with a link to the original page for details, because I felt that I could not tell the whole truth in a shorter way, and copying and pasting would be a waste. So, my documentation is a little more than some summary notes peppered with links to the full documentation.&lt;br /&gt;&lt;br /&gt;Anyone reading any code that tried to deal with the limitations above---&lt;a href="http://python.org/"&gt;written in an arguably already readable language&lt;/a&gt;---would still scratch their heads at why some weird stuff was being done. There would &lt;i style="font-weight: bold;"&gt;have&lt;/i&gt; to be comments explaining why this was done. The comment could be just a link to the MySQL documentation page. Or, it could have the full explanation, with the MySQL version in which said limitation lurked. Or both. The person reading the code would &lt;span style="font-style: italic;"&gt;&lt;span style="font-weight: bold;"&gt;have&lt;/span&gt;&lt;/span&gt; to understand the documentation before they could say they understood the code.&lt;br /&gt;&lt;br /&gt;The point is that: if there is good documentation about some topic, I can do little than point others to it and say "this is important". The documentation &lt;span style="font-style: italic;"&gt;is &lt;/span&gt;the communication.&lt;br /&gt;&lt;br /&gt;And the more important point is: you cannot claim to have understood a software unless you understand its intention and motivation. And in the above case, the motivation means reading the documentation. Most times, reading documentation means you come back with knowledge. In other words, to understand the software, you must, well, understand. Err.. I mean, you must acquire the knowledge the original developer had when creating the software.&lt;br /&gt;&lt;br /&gt;Which brings me to a wicked question: the reason I was writing the original notes about clustering was so that not everyone in my team has to know the nitty-gritty and just know enough to design and write code to be handle clusters if we decided to use clusters. But it looks like no one can actually decide if the code will be right without having read the full documentation anyway.&lt;br /&gt;&lt;br /&gt;What about code? That's worse. You now must understand the intention of the code. Then you must understand the motivation behind the code. To do that you must read the documentation.&lt;br /&gt;&lt;br /&gt;Software is knowledge.&lt;br /&gt;&lt;br /&gt;&lt;p class="poweredbyperformancing"&gt;And my documentation is little more than a reminder note or a collection of related links.&lt;br /&gt;&lt;/p&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-2364815416050153160?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/2364815416050153160/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=2364815416050153160' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/2364815416050153160'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/2364815416050153160'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2007/03/software-is-knowledge.html' title='Software is knowledge'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-6357380178279869793</id><published>2007-02-15T09:19:00.000-08:00</published><updated>2007-02-15T11:26:56.356-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='e27sg4 singapore entrepreneur web2.0 web2 web'/><title type='text'>Singapore Entreupreneur Web 2.0 Unconference</title><content type='html'>&lt;a href="http://blog.yanime.org/"&gt;Choon Keat&lt;/a&gt; came around at work to remind me of this &lt;a href="http://www.entrepreneur27.org/sg/"&gt;unconference&lt;/a&gt;. I'd wanted to attend and therefore we both left together. CK was a bit surprised at the number of people there---ought to be close to 200---because the last time he had attended it, it was a small group in a "pathetic room with pizza and beer on the side".&lt;br /&gt;&lt;br /&gt;What intrigued me about this event was the format. The self-styled unconference claims to have an informal setting where many parallel discussions are going on side by side and where you are either presenting, debating or absorbing. Should you be not doing any of these, you are supposed to use the Law of Two Feet to take yourself elsewhere where you can be useful.&lt;br /&gt;&lt;br /&gt;It wasn't quite like that.  This is the fourth instalment of the event, and &lt;a href="http://www.entrepreneur27.org/sg/2007/02/15/you-all-are-awesome/"&gt;this is the first time the organizers were trying to have three "tracks"&lt;/a&gt;. IMHO the tracks made it all too formal, because I was scientifically trying to decide how to split my time instead of just popping in at different talks and seeing whether something interesting was going on. Still, not having attended the previous events, I can't compare and this may very well be a good format.&lt;br /&gt;&lt;br /&gt;There was lots to hear, see and talk about. The debate was sometimes spirited and people were not afraid to speak. The questions (to presenters from startups) came fast and the presenters were able to roll with the punches.&lt;br /&gt;&lt;br /&gt;The first presentation I attended was from a startup called &lt;a href="http://www.activedeals.com/about.htm"&gt;ActiveDeals&lt;/a&gt; which puts movies in your MSN with some advertising along with it. It claims its product is interactive TV, about which I could only make out that people could chat about the video they were seeing with their friends and (presumably) on their website. If there is more interactivity the presented did not say. Haven't fully checked it out yet, but this is the kind of startup that depends a lot on execution and the main barrier to entry is the work to get there. Their main points were:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The videos would be short, on-demand, and focus on local stuff; something people would be comfortable watching as a short break at work;&lt;/li&gt;&lt;li&gt;The interactive options would lead to user-generated content: links, reviews, etc., which could then be linked against the videos and become content.&lt;/li&gt;&lt;li&gt;activedeals would be doing the video production, so that's some leg work.&lt;/li&gt;&lt;li&gt;Sponsorship of the video content would pay the bills.&lt;/li&gt;&lt;li&gt;The sponsors would have to be educated to the use the interactivity of the medium as opposed to just sell, sell, sell.&lt;/li&gt;&lt;/ul&gt;I don't know how far they have come along---there were some mockups in the presentation, and the presenter Todd Murray referred to them as such---but the presenter mentioned they would launch in March.&lt;br /&gt;&lt;br /&gt;I attended only 5 minutes of the next presentation, which about a &lt;a href="http://www.wisheus.com/Wisheus/www/"&gt;website called Wisheus&lt;/a&gt;. Here you can "publish your wishes and your wares". It might attract some teens. Could not see whats new about it. But I've not tried to sign-in, and I did not sit through the whole presentation, so if it has some twist, I don't know about it.&lt;br /&gt;&lt;br /&gt;I quit this talk to go to another about complex-systems and networks. The presentation started out with fractals, then on to complex systems with emergent behaviour (you know, the ones where autonomous agents which have simple behavioural rules but can interact with each other, produce complex, unpredictable behaviour), and then on to networks. Finished off with the observation that by observing a network, one could find out those nodes which are highly influential. For e.g., in the blogospere, there could be a blogger who is connected to just the right people and has interest in the idea you wish to popularize.  This blogger does not have to be an A-list blogger with lots of readership, just rightly connected. Okay, but the relationship with fractals or complex systems is tenuous. The talk could have been better.&lt;br /&gt;&lt;br /&gt;The next talk was titled "The Two Towers (of Web 2.0)". I walked in a little bit late and heard people going on about Ajax. Asked someone what the other pillar was, and heard that one pillar was not ajax but technology and the other was marketing. Marketing? Well... not surprising considering that the talk was anchored by a group called &lt;a href="http://sgentrepreneurs.com/"&gt;Singapore Entrepreneurs&lt;/a&gt;, a group that runs a blog and also &lt;a href="http://sgentrepreneurs.com/singapore-venture-capital-seed-funding/"&gt;provides vc funcding&lt;/a&gt; in some way. I chipped in with an observation that while Ajax is a great technology enabler, another interesting aspect is the opening of the web as services with APIs through which people create and transform existing content into new forms. Guess people did not like it.&lt;br /&gt;&lt;br /&gt;The only interesting points in this talk were that &lt;a href="http://james.seng.sg/archives/2007/02/09/why_singapore_is_not_ready_for_web_20.html"&gt;singapore is not ready for web 2.0&lt;/a&gt; and that the talk should have been about doing stuff, not about whether we can or should do it. The rest of the time was spent in regurgigating stuff from techcrunch.com (did not know it was so popular), and blaming timid singaporean VCs.&lt;br /&gt;&lt;br /&gt;The next talk I was interested in attending was by Yahoo about &lt;a href="http://widgets.yahoo.com/"&gt;widgets &lt;/a&gt;&lt;a href="http://forevergeek.com/geek_resources/yahoo_widgets_vs_google_gadgets.php"&gt;and gadgets.&lt;/a&gt; But at this point I had to leave.&lt;br /&gt;&lt;br /&gt;Interesting event and I look forward to attending more.  If I could stand up to the barrage of questions that other starter-uppers got then I would be proud of my pitching skills (don't have any, I think). I do hope the level of the discussion goes up a little bit. It is most interesting when people talk about local circumstances and local problems and not debate about whether Singapore is too small. It is. Apparently some people want to start-up anyway. What could they do better?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-6357380178279869793?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/6357380178279869793/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=6357380178279869793' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/6357380178279869793'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/6357380178279869793'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2007/02/singapore-entreupreneur-web-20.html' title='Singapore Entreupreneur Web 2.0 Unconference'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-8134223514553992002</id><published>2007-02-12T02:20:00.000-08:00</published><updated>2007-02-10T22:07:23.703-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gpacs'/><title type='text'>GPACS: when to refactor</title><content type='html'>Here is a situation:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;You are working on specs for the next sprint and they are not complete;&lt;/li&gt;&lt;li&gt;You are considering a new technology to replace one you are using;&lt;/li&gt;&lt;li&gt;Experimentation has shown that it is good.&lt;/li&gt;&lt;/ul&gt;I say, take the time to refactor!  Use whatever knowledge you have now. Don't overdo it, because the new specs may call for some changes.&lt;br /&gt;&lt;br /&gt;People struggle to allocate time to refactor. One should do it whenever one can. Especially when:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;You have time on  your hands;&lt;/li&gt;&lt;li&gt;You know going forward you will be using the new tech&lt;/li&gt;&lt;li&gt;You have the luxury of replacing some tech with another without simultaneously having to deal with changing requirements (at least while you are doing the replacement).&lt;/li&gt;&lt;/ul&gt;Does it also help not to have to think about the future design, while you are re-factoring something in the current design? Sometimes I think one needs to consciously choose to juggle only one variable at a time.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-8134223514553992002?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/8134223514553992002/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=8134223514553992002' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/8134223514553992002'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/8134223514553992002'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2007/02/gpacs-when-to-refactor.html' title='GPACS: when to refactor'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-3667122627866254792</id><published>2007-02-05T20:03:00.000-08:00</published><updated>2007-02-05T20:10:24.756-08:00</updated><title type='text'>Keeping the fires fed</title><content type='html'>I did a mid-sprint review yesterday. Work seemed to be going on at a rapid clip, even when some developers were involved in support activities or other projects. What I realized is that a steady stream of well-defined work activities keeps people occupied, purposeful, and productive.&lt;br /&gt;&lt;br /&gt;Where does re-design fit into this? When people start thinking about design or design changes, I've noticed that sometimes very blue-sky ideas come in. The good part is that the design might be very good (although most often it is hard to tell whether a design idea is significantly better that what you already have).&lt;br /&gt;&lt;br /&gt;The way to control design efforts to be useful, incremental and low-risk is to---probably---focus on implementing some part of a use case. Design can then evolve.&lt;br /&gt;&lt;br /&gt;Of course, radically new design may need &lt;a href="http://parijatmishra.blogspot.com/2007/02/gpacs-early-days.html"&gt;re-writes&lt;/a&gt;&lt;br /&gt;and revolution, not evolution, may be the way to go. But that way lies risk without the certainty that the new design would lead to a significantly improved situation. Especially in the face of evolving requirements.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-3667122627866254792?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/3667122627866254792/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=3667122627866254792' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/3667122627866254792'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/3667122627866254792'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2007/02/keeping-fires-fed.html' title='Keeping the fires fed'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-4184138385643616723</id><published>2007-02-03T02:10:00.001-08:00</published><updated>2007-02-03T02:10:38.914-08:00</updated><title type='text'>GPACS: the early days</title><content type='html'>&lt;div xmlns='http://www.w3.org/1999/xhtml'&gt;GPACS is &lt;a href='http://affle.co.uk/'&gt;the company&lt;/a&gt;'s accepted software development process. It is a quite like Scrum. More about it at some other time. The point is that I am managing an enhancements to an existing software while also considering re-doing or modifying it for some new requirements. One of the first things I need to plan is to how to get various teams working together. The other thing I need to figure out is to how can the same team work simultaneously on "enhancements" to existing code-base while trying to implement something that has a slightly different set of requirements.&lt;br /&gt;.&lt;br /&gt;I came across a bunch of articles by Chad Fowler: &lt;a href='http://chadfowler.com/index.cgi/Computing/Programming/TheBigRewrite/TheBigRewrite.rdoc,v'&gt;"The big rewrite".&lt;/a&gt;&lt;br /&gt;And then &lt;a href='http://codecraft.info/index.php/archives/69/'&gt;another article&lt;/a&gt;&lt;br /&gt;by someone called Kevin Barnes. &lt;br /&gt;&lt;br /&gt;Oh, I was already thinking that a complete rewrite was out of the question because of the risks involved. But I could not have listed down the issues/risks as well as these two articles have.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p class='poweredbyperformancing'&gt;powered by &lt;a href='http://performancing.com/firefox'&gt;performancing firefox&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-4184138385643616723?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/4184138385643616723/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=4184138385643616723' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/4184138385643616723'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/4184138385643616723'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2007/02/gpacs-early-days.html' title='GPACS: the early days'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-584401739089754051</id><published>2007-01-01T02:14:00.001-08:00</published><updated>2007-01-01T02:14:16.865-08:00</updated><title type='text'>Audacity</title><content type='html'>&lt;div xmlns='http://www.w3.org/1999/xhtml'&gt;From http://www.iftf.org/features/map_of_the_decade.html:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Want a quick tour of the future? Each year, the Institute for the Future plots the big trends that will define the coming decade on an AT-A-GLANCE graphic map. Scanning the Map is a little like swallowing a time capsule from the future--and getting very smart very fast!&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;More cool words:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;The Group Economy:&lt;/strong&gt; Organizing for sociability&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Lightweight Infrastructures:&lt;/strong&gt; Rethinking the movement of goods and services&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Sensemaking:&lt;/strong&gt; The new science and technology of meaning&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Farming the Planet:&lt;/strong&gt; Ground zero for global turbulence&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Transformational Geography:&lt;/strong&gt; The new role of the global south&lt;/li&gt;&lt;li&gt;&lt;strong&gt;X-People:&lt;/strong&gt; The intentional evolution of human being&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Wow, this site really reminds me of http://huhcorp.com.&lt;br /&gt;&lt;br /&gt;Who funds these guys? Looks like it is a membership driven privately funded organization.&lt;br /&gt;&lt;br /&gt;Who are these guys? The roots were engineers from RAND corporation. Now? A group of researchers with technical degrees, and some technical experience.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p class='poweredbyperformancing'&gt;powered by &lt;a href='http://performancing.com/firefox'&gt;performancing firefox&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-584401739089754051?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/584401739089754051/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=584401739089754051' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/584401739089754051'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/584401739089754051'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2007/01/audacity.html' title='Audacity'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-6753849730903343310</id><published>2007-01-01T01:51:00.001-08:00</published><updated>2007-01-01T01:51:40.145-08:00</updated><title type='text'>What's the difference?</title><content type='html'>&lt;div xmlns='http://www.w3.org/1999/xhtml'&gt;From http://www.iftf.org/:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;We provide the foresight to create insights that lead to action.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;From http://huhcorp.com/:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;We provide distinct clients with groundbreaking business strategies and cutting-edge designs to aggressively and creatively compete in a changing economy.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p class='poweredbyperformancing'&gt;powered by &lt;a href='http://performancing.com/firefox'&gt;performancing firefox&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-6753849730903343310?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/6753849730903343310/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=6753849730903343310' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/6753849730903343310'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/6753849730903343310'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2007/01/what-difference.html' title='What&amp;#39;s the difference?'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-553549620652560081</id><published>2007-01-01T01:05:00.001-08:00</published><updated>2007-01-01T01:05:58.675-08:00</updated><title type='text'>Vote for me, get a TV</title><content type='html'>&lt;div xmlns='http://www.w3.org/1999/xhtml'&gt;&lt;blockquote&gt;&lt;br /&gt;December 29, 2006: Declaring color television a basic necessity, an Indian political party promised free sets to the poor and swept to power in May in the southern state of Tamil Nadu. So far, the Dravida Munnetra Kazhagam party, or DMK, has handed out 60,000 sets and plans to give away 30,000 more in coming months....As far as political platforms go, this one clearly has been a winner. "I will always vote for DMK," said Parimala, a 51-year-old widow who like many in these parts uses only one name....An electoral stunt for sure, but one that illustrates how populism has emerged as the default position of parties across India's political spectrum as they contend with the fading of once-dominant ideologies....But no one has gone as far as Tamil Nadu's DMK. Part of the reason is money: Tamil Nadu is one of India's wealthiest states, giving its government full tax coffers.&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;From free rice to free televisions. Obviously Tamil Nadu has been progressing a lot.&lt;br /&gt;&lt;br /&gt;&lt;p class='poweredbyperformancing'&gt;powered by &lt;a href='http://performancing.com/firefox'&gt;performancing firefox&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-553549620652560081?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/553549620652560081/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=553549620652560081' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/553549620652560081'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/553549620652560081'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2007/01/vote-for-me-get-tv.html' title='Vote for me, get a TV'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-8005947861617810683</id><published>2006-12-30T10:18:00.001-08:00</published><updated>2006-12-30T10:18:10.981-08:00</updated><title type='text'>Tenth deadly sin of project planning</title><content type='html'>&lt;div xmlns='http://www.w3.org/1999/xhtml'&gt;In an IEEE Software 'From the Editor' column, Steve McConnell writes (at http://www.stevemcconnell.com/ieeesoftware/eic19.htm), that the nine deadly sins of project planning are:&lt;br&gt;&lt;/br&gt;&lt;br&gt;&lt;/br&gt;(1) Not planning at all&lt;br&gt;&lt;/br&gt;(2) Failing to account for all project activities&lt;br&gt;&lt;/br&gt;(3) Failure to plan for risk&lt;br&gt;&lt;/br&gt;(4) Using the same plan for every project&lt;br&gt;&lt;/br&gt;(5) Applying pre-packaged plans indiscriminately&lt;br&gt;&lt;/br&gt;(6) Allowing a plan to diverge from project reality&lt;br&gt;&lt;/br&gt;(7) Planning in too much detail too soon&lt;br&gt;&lt;/br&gt;(8) Planning to catch up later&lt;br&gt;&lt;/br&gt;(9) Not learning from past planning sins&lt;br&gt;&lt;/br&gt;&lt;br&gt;&lt;/br&gt;I am managing a project right now, and I just can't figure out which sin I am committing: (2) or (7)? I suppose the answer is that is depends on the project and its circumstances. But of course. It always does.&lt;br&gt;&lt;/br&gt;&lt;br&gt;&lt;/br&gt;And uh, so at least now I know that these are all the deadly sins of project planning and other sins are presumably not deadly. They are merely extremely toxic, presumably. My project won't exactly die if I forget about some other practical issues, it will just go into convulsions or stop breathing for a while, or sway gently from side to side when it should be rocking.&lt;br&gt;&lt;/br&gt;&lt;br&gt;&lt;/br&gt;In other words, I still have to worry about the non-fatal issues in some particular order which is highly project dependent, and these so called fatal sins have to watched out for but exactly when a sin is being committed is also dependent on the project.&lt;br&gt;&lt;/br&gt;&lt;br&gt;&lt;/br&gt;Duh!&lt;br&gt;&lt;/br&gt;&lt;br&gt;&lt;/br&gt;Why do these wise men write these articles with zero information? I just wasted a perfectly good 5 mins which I could have spent more usefully reading about a API, framework, or some such, that would actually have solved a few real problems.&lt;br&gt;&lt;/br&gt;&lt;br&gt;&lt;/br&gt;The tenth deadly sin of project management: trying to get wisdom from anything other than hard facts when you should be doing project management: learning, building consensus, disseminating information, talking to your team, and writing code.&lt;br&gt;&lt;/br&gt;&lt;br&gt;&lt;/br&gt;&lt;br&gt;&lt;/br&gt;&lt;br&gt;&lt;/br&gt;&lt;br&gt;&lt;/br&gt;&lt;br&gt;&lt;/br&gt;&lt;p class='poweredbyperformancing'&gt;powered by &lt;a href='http://performancing.com/firefox'&gt;performancing firefox&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-8005947861617810683?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/8005947861617810683/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=8005947861617810683' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/8005947861617810683'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/8005947861617810683'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2006/12/tenth-deadly-sin-of-project-planning.html' title='Tenth deadly sin of project planning'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-4556395975182949593</id><published>2006-12-16T02:43:00.000-08:00</published><updated>2006-12-16T03:59:55.900-08:00</updated><title type='text'>When unix processes are too much</title><content type='html'>I want to ssh into a server, run vmstat for a while, then get back the output of vmstat. All automatically, via a Python program.  There are many problems.  Here is a simulation of the problems through the command line.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Take 1&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;The first thought was to ssh in,  run vmstat and redirect the output of vmstat to a file on the remove server, like so. Then later, I would kill ssh, and use scp to retrieve the logfile from the remote server. Here is the command line equivalent:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="font-family:courier new;"&gt;$ ssh 'couffable@asgard' 'vmstat 1 2&gt;&amp;1 &gt;/home/couffable/remote-vmstat.log' &amp;amp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;[2] 10405&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;On remote machine:&lt;br /&gt;[couffable@asgard ~]$ ps ax | grep vmstat&lt;br /&gt;20921 ?        Ss     0:00 bash -c vmstat 1 2&gt;&amp;1 &gt;/home/couffable/remote-vmstat.log&lt;br /&gt;20938 ?        S      0:00 vmstat 1&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;This starts things nicely and gives me the PID of the ssh process.  The trouble is when I want to stop the process:&lt;br /&gt;&lt;br /&gt;On local machine&lt;br /&gt;$ kill 10405&lt;br /&gt;$ fg # to get bash to retrive ssh's status and remove it from zombie list&lt;br /&gt;&lt;br /&gt;But on the remote machine, vmstat is still running!&lt;br /&gt;[couffable@asgard ~]$ ps ax | grep vmstat&lt;br /&gt;20921 ?        Ss     0:00 bash -c vmstat 1 2&gt;&amp;amp;1 &gt;/home/couffable/remote-vmstat.log&lt;br /&gt;20938 ?        S      0:00 vmstat 1&lt;br /&gt;&lt;br /&gt;This won't do at all.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Take 2&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;I noticed that if I run vmstat though ssh without redirecting vmstat's output on the remote machine, ssh shows vmstat's output on my terminal. So I said, why not redirect &lt;span style="font-weight: bold;"&gt;ssh's&lt;/span&gt; output?.   Here is what I tried:&lt;br /&gt;&lt;br /&gt;On local machine:&lt;br /&gt;$ ssh 'couffable@asgard' 'vmstat 1' 2&gt;&amp;1 &gt;/home/couffable/local-vmstat.log &amp;amp;&lt;br /&gt;[2] 10569&lt;br /&gt;&lt;br /&gt;On the remote machine:&lt;br /&gt;[couffable@asgard ~]$ ps ax | grep vmstat&lt;br /&gt;21239 ?        Ss     0:00 vmstat 1&lt;br /&gt;&lt;br /&gt;Bingo! Now vmstat's output is being stored in a local file, not on the remote machine.  Or is it? I notice that on the local machine:&lt;br /&gt;$ # press enter&lt;br /&gt;[2]+  Stopped                 ssh 'couffable@asgard' 'vmstat 1' 2&gt;&amp;1 &gt;/home/couffable/local-vmstat.log&lt;br /&gt;&lt;br /&gt;That is, vmstat/ssh's output is not getting logged because ssh stops when backgrounded.  Puff!&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Take 3&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I decided that I won't background ssh.&lt;br /&gt;&lt;br /&gt;On local machine:&lt;br /&gt;$ ssh 'couffable@asgard' 'vmstat 1' 2&gt;&amp;1 &gt;/home/couffable/local-vmstat.log&lt;br /&gt;&lt;br /&gt;On remote machine:&lt;br /&gt;[couffable@asgard ~]$ ps ax | grep vmstat&lt;br /&gt;21624 ?        Ss     0:00 vmstat 1&lt;br /&gt;&lt;br /&gt;After some time, again on local machine (in another terminal):&lt;br /&gt;$ ps ax | grep vmstat&lt;br /&gt;10880 pts/1    S+     0:00 ssh couffable@asgard vmstat 1&lt;br /&gt;$ kill 10880&lt;br /&gt;&lt;br /&gt;And yes, the vmstat on remote machine dies too.  Now, will this work in Python?&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Take 4&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The trouble with running the results of Take 3 in Python is that I can't directly invoke ssh: the redirection won't happen. For redirection, I need a shell.  So I could do something like:&lt;br /&gt;&lt;br /&gt;&gt;&gt;&gt; pid = os.spawnlp(os.P_NOWAIT, "sh", "sh", "-c", "ssh couffable@asgard vmstat 1", "2&gt;&amp;1", "&gt;vmstat.log")&lt;br /&gt;&gt;&gt;&gt; procs -----------memory---------- ---swap-- -----io---- --system-- ----cpu----&lt;br /&gt;r  b   swpd   free   buff  cache   si   so    bi    bo   in    cs us sy id wa&lt;br /&gt;0  0    144  82808 231972 1293708    0    0     2     2    0     2  1  0 99  0&lt;br /&gt;&lt;br /&gt;What? No redirection?  Not good.  After killing ssh processes, I try again:&lt;br /&gt;&gt;&gt;&gt; pid = os.spawnlp(os.P_NOWAIT, "sh", "sh", "-c", "ssh couffable@asgard vmstat 1 2&gt;&amp;amp;1 &gt;vmstat.log")&lt;br /&gt;&gt;&gt;&gt; pid&lt;br /&gt;&lt;br /&gt;Redirection is working now. I kill the process:&lt;br /&gt;&gt;&gt;&gt; os.kill(11935,9)&lt;br /&gt;&gt;&gt;&gt; pid, status = os.waitpid(11935, 0)&lt;br /&gt;&gt;&gt;&gt; status&lt;br /&gt;9&lt;br /&gt;&lt;br /&gt;Ok, so far so good.  But the ssh and vmstat processes are still running!  (Killing ssh does stop both ssh and vmstat, like Take 2 and unlike Take 1.) What gives? Killing sh does not stop ssh since sh &lt;span style="font-style: italic;"&gt;spawned&lt;/span&gt; ssh. Hmm.... I don't really want sh to &lt;span style="font-style: italic;"&gt;spawn&lt;/span&gt; ssh. I want sh to redirect standard output and error and then &lt;span style="font-style: italic;"&gt;exec&lt;/span&gt; ssh.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Take 5&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&gt;&gt;&gt; pid = os.spawnlp(os.P_NOWAIT, "sh", "sh", "-c", "exec ssh couffable@asgard vmstat 1 2&gt;&amp;1 &gt;vmstat.log")&lt;br /&gt;&gt;&gt;&gt; pid&lt;br /&gt;12080&lt;br /&gt;&lt;br /&gt;On local macine:&lt;br /&gt;$ ps ax | grep vmstat&lt;br /&gt;12080 pts/4    S+     0:00 ssh couffable@asgard vmstat 1&lt;br /&gt;&lt;br /&gt;On remote machine:&lt;br /&gt;[couffable@asgard ~]$ ps ax | grep vmstat&lt;br /&gt;24081 ?        Ss     0:00 vmstat 1&lt;br /&gt;&lt;br /&gt;Well and good.  Now let's kill the ssh process.&lt;br /&gt;On local machine:&lt;br /&gt;&gt;&gt;&gt; os.kill(12080, 9)&lt;br /&gt;&gt;&gt;&gt; pid, status = os.waitpid(12080, 0)&lt;br /&gt;&gt;&gt;&gt; status&lt;br /&gt;9&lt;br /&gt;&lt;br /&gt;On local machine:&lt;br /&gt;$ ps ax | grep vmstat&lt;br /&gt;{nothing}&lt;br /&gt;&lt;br /&gt;On remote machine:&lt;br /&gt;[couffable@asgard ~]$ ps ax | grep vmstat&lt;br /&gt;{nothing}&lt;br /&gt;&lt;br /&gt;Yay!&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;The question is: all this mucking around with Unix plumbing is wonderful, but could I have have done it faster if I'd written my own fork(), redirect, and exec mini-program instead of trying to invoke os.spawnlp(...) using sh magic?&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-4556395975182949593?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/4556395975182949593/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=4556395975182949593' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/4556395975182949593'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/4556395975182949593'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2006/12/i-want-to-ssh-into-server-run-vmstat.html' title='When unix processes are too much'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-709573578536483149</id><published>2006-12-09T08:35:00.001-08:00</published><updated>2006-12-09T08:35:09.921-08:00</updated><title type='text'>Jeff Darcy's notes on really high performance servers</title><content type='html'>&lt;div xmlns='http://www.w3.org/1999/xhtml'&gt;In his article "High-Performance Server Architecture" (&lt;a href='http://pl.atyp.us/content/tech/servers.html'&gt;http://pl.atyp.us/content/tech/servers.html)&lt;/a&gt;&lt;br&gt;&lt;/br&gt;Jeff Darcy talks about what kills performance. He mentions the following:&lt;br&gt;&lt;/br&gt;&lt;br&gt;&lt;/br&gt;&lt;ul&gt;&lt;li&gt;data copies&lt;/li&gt;&lt;li&gt;context switches&lt;/li&gt;&lt;li&gt;memory allocation&lt;/li&gt;&lt;li&gt;lock contention&lt;/li&gt;&lt;/ul&gt;as the biggest four reasons for poor performance, especially at high concurrency. I did not quite understand his suggestions on reducing lock contention. But perhaps it will become clearer by putting his idea to work on a real problem.&lt;br&gt;&lt;/br&gt;&lt;br&gt;&lt;/br&gt;He also mentions:&lt;br&gt;&lt;/br&gt;&lt;ul&gt;&lt;li&gt;How does your storage subsystem perform with larger vs. smaller requests? With sequential vs. random? How well do read-ahead and write-behind work?&lt;/li&gt;&lt;li&gt;How efficient is the network protocol you're using? Are there parameters or flags you can set to make it perform better? Are there facilities like TCP_CORK, MSG_PUSH, or the Nagle-toggling trick that you can use to avoid tiny messages?&lt;/li&gt;&lt;li&gt;Does your system support scatter/gather I/O (e.g. readv/writev)? Using these can improve performance and also take much of the pain out of using buffer chains.&lt;/li&gt;&lt;li&gt;What's your page size? What's your cache-line size? Is it worth it to align stuff on these boundaries? How expensive are system calls or context switches, relative to other things?&lt;/li&gt;&lt;li&gt;Are your reader/writer lock primitives subject to starvation? Of whom? Do your events have "thundering herd" problems? Does your sleep/wakeup have the nasty (but very common) behavior that when X wakes Y a context switch to Y happens immediately even if X still has things to do?&lt;/li&gt;&lt;/ul&gt;&lt;br&gt;&lt;/br&gt;I am now itching to try a few things. However, how does one begin in a dynamic language like Python? I can't do much about memory allocation. Umm... also, Python does its own reference counting, so data copies should not be a huge problem: I just have to ensure that my own code does not make unnecessary copies. Context switches and lock contention look like the primary and secondary targets to focus on for performance and design.&lt;br&gt;&lt;/br&gt;&lt;br&gt;&lt;/br&gt;This statement is rather interesting:&lt;br&gt;&lt;/br&gt;&lt;br&gt;&lt;/br&gt;&lt;blockquote&gt;It's very important to use a "symmetric" approach in which a given thread can go from being a listener to a worker to a listener again without ever changing context. Whether this involves partitioning connections between threads or having all threads take turns being listener for the entire set of connections seems to matter a lot less.&lt;/blockquote&gt;Now, how does one go about doing that and still have clean, understandable, and maintainable code?&lt;br&gt;&lt;/br&gt;&lt;br&gt;&lt;/br&gt;&lt;br&gt;&lt;/br&gt;&lt;br&gt;&lt;/br&gt;&lt;p class='poweredbyperformancing'&gt;powered by &lt;a href='http://performancing.com/firefox'&gt;performancing firefox&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-709573578536483149?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/709573578536483149/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=709573578536483149' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/709573578536483149'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/709573578536483149'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2006/12/jeff-darcy-notes-on-really-high.html' title='Jeff Darcy&amp;#39;s notes on really high performance servers'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4489744187593527994.post-9115215220203218240</id><published>2006-12-06T10:39:00.001-08:00</published><updated>2006-12-06T10:39:07.562-08:00</updated><title type='text'>Will you look into the crystal ball for me, please?</title><content type='html'>&lt;div xmlns='http://www.w3.org/1999/xhtml'&gt;I wonder what &lt;a href='http://www.mobilestrategiesblog.futuretext.com/'&gt;this guy has to say about mobile advertising.&lt;/a&gt;&lt;br&gt;&lt;/br&gt;&lt;br&gt;&lt;/br&gt;&lt;br&gt;&lt;/br&gt;&lt;p class='poweredbyperformancing'&gt;powered by &lt;a href='http://performancing.com/firefox'&gt;performancing firefox&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4489744187593527994-9115215220203218240?l=parijatmishra.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://parijatmishra.blogspot.com/feeds/9115215220203218240/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4489744187593527994&amp;postID=9115215220203218240' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/9115215220203218240'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4489744187593527994/posts/default/9115215220203218240'/><link rel='alternate' type='text/html' href='http://parijatmishra.blogspot.com/2006/12/will-you-look-into-crystal-ball-for-me.html' title='Will you look into the crystal ball for me, please?'/><author><name>Parijat Mishra</name><uri>http://www.blogger.com/profile/17919810837936878861</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry></feed>
