<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en" xml:base="http://eagain.net/blog/"><updated>2008-07-20T14:02:00+00:00</updated><id>http://eagain.net/blog/</id><link rel="alternate" href="http://eagain.net/blog/" type="text/html"/><link rel="self" href="http://eagain.net/blog/atom.xml"/><title>Tv's cobweb</title><author><name>Tommi Virtanen</name><email>tv@eagain.net</email></author><entry><title>EuroPython 2008 wrap up</title><id>http://eagain.net/#2008-07-19_europython2008</id><link rel="alternate" href="http://eagain.net/blog/2008/07/19/europython2008.html" type="text/html"/><published>2008-07-19T23:17:00+00:00</published><updated>2008-07-20T14:02:00+00:00</updated><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><a class="reference image-reference" href="http://www.flickr.com/photos/tv42/2653823082/" shape="rect"><img align="left" alt="Lightning talks" class="align-left" src="http://farm4.static.flickr.com/3064/2653823082_514d91dd47_m.jpg"/></a>
<p><a class="reference" href="http://www.europython.org/" shape="rect">EuroPython 2008</a> was fun. I presented two talks (<a class="reference" href="http://eagain.net/talks/pythonic-fs/" shape="rect">My God, it's Full
of Files -- Pythonic filesystem abstractions</a> and <a class="reference" href="http://eagain.net/talks/version-control-for-du-developers/" shape="rect">Version Control
for Du^H^HDevelopers</a>) and one lightning talk (<cite>RST+S5 for your
slides</cite>), participated in a bunch of open space sessions, listened to
about 13 talks, took a bunch of <a class="reference" href="http://www.flickr.com/photos/tv42/tags/europython2008/" shape="rect">pictures</a>, but most importantly had
interesting hallway conversations with interesting people.</p>
<a class="reference image-reference" href="http://www.flickr.com/photos/tv42/2652979397/" shape="rect"><img align="right" alt="&quot;Buildout for Pure Python Project&quot;, Carsten Rebbien" class="align-right" src="http://farm4.static.flickr.com/3054/2652979397_f6e61ea539_m.jpg"/></a>
<p>As usual, <a class="reference" href="http://codespeak.net/pypy/" shape="rect">PyPy</a> was heavily represented, and seems to be making nice
progress toward being the nice and featureful default Python
implementation of the future. I especially liked the restricted
execution features and the <a class="reference" href="http://llvm.org/" shape="rect">LLVM</a> backend. The <a class="reference" href="http://pypi.python.org/pypi/zc.buildout" shape="rect">zc.buildout</a> <a class="reference" href="http://registration.europython.eu/talk_abstracts.html#32" shape="rect">talk</a> made me
decide I will try to replace (part of?) one custom deploy mechanism
with <tt class="docutils literal"><span class="pre">zc.buildout</span></tt> -- most likely I'll end up rewriting most of the
current things as <tt class="docutils literal"><span class="pre">zc.buildout</span></tt> recipes, but hopefully some of the
pre-existing recipes will be useful, and hopefully I can then later
reuse the recipies I create for this setup.</p>
<p>Personally, I think my talks went ok. I understand videos will be
available later, as soon as transcoding etc are finished. I'm anxious
to see them myself, as I'm still finetuning my public speaking
skills. I'm learning, though -- this year I had no trouble staying
within my time slot, even when I was adjusting verbosity on the fly.</p>
<p>For some reason, I felt underprepared for the <a class="reference" href="http://eagain.net/talks/pythonic-fs/" shape="rect">filesystem API talk</a>,
but ultimately people liked the idea of a consistent Pythonic
filesystem API enough that we had an open space session on it, and
people were enthusiastic about a sprint to prototype the API. Which is
what we ended up doing, too -- I'll blog separately about the results
of that.</p>
<p>My <a class="reference" href="http://eagain.net/talks/version-control-for-du-developers/" shape="rect">decentralized version control talk</a> seemed to me to go over more
smoothly; I guess that's just because I've been thinking about version
control and project management a lot lately, so it was easy to talk
about the topic in a relaxed way.  On the other hand, it wasn't as
much a <em>call to action</em>, and it really was overly generic, so I didn't
get as strong audience participation there. We did have an interesting
conversation about branch management strategies and such, though.  I
consciously tried to keep the talk on a generic level, as I felt a
pure <tt class="docutils literal"><span class="pre">git</span></tt> talk would have alienated some listeners, but I did end
up feeling restricted by that. There was some interest on a <cite>Teach me
git</cite> -style session, but what we ended up doing was just talking one
on one about getting started with <tt class="docutils literal"><span class="pre">git</span></tt>, during the sprints. Sorry
if I missed any one of you -- grab me on <tt class="docutils literal"><span class="pre">#git</span></tt> to continue, or find
me in future conferences ;)</p>
<a class="reference image-reference" href="http://www.flickr.com/photos/tv42/2647465192/" shape="rect"><img align="right" alt="Twisted Q&amp;A session" class="align-right" src="http://farm4.static.flickr.com/3013/2647465192_a02166e67a_m.jpg"/></a>
<p>I was requested to organize an open space session for Twisted Q&amp;A, and
that is exactly <a class="reference" href="http://www.flickr.com/photos/tv42/2647465192/" shape="rect">what we did</a>. We went through a bunch of things
related to asynchronous programming concepts, Deferreds, working with
blocking code and libraries, database interfaces, debugging and unit
testing.</p>
<p>I was also pulled in to another Twisted open space session, that was
mostly about what greenlets are and how to use them. I tried to
explain the differences between classical Deferreds,
<tt class="docutils literal"><span class="pre">deferredGenerator</span></tt>/<tt class="docutils literal"><span class="pre">inlineCallbacks</span></tt>, and greenlets, to the best
of my understanding. As a summary, with greenlets any function you
call can co-operatively yield execution (I mean yield in the
scheduling meaning, giving away your turn to run, not in the Python
generator meaning -- interestingly <tt class="docutils literal"><span class="pre">inlineCallbacks</span></tt> etc actually
make those be the same thing... my kernel instincts make me want to
say "sleep"). Yielding in any subroutine means anything you do may end
up mutating your objects -- which is the root evil behind threading we
wanted to get away from. All the other mechanisms keep the top-level
function in explicit control of yielding. Around that time, most
people left for lunch, but about three of us stayed and talked about
debugging Deferreds and network packet processing with
<tt class="docutils literal"><span class="pre">twisted.pair</span></tt> and friends.</p>
<a class="reference image-reference" href="http://www.flickr.com/photos/tv42/2646629299/" shape="rect"><img align="left" alt="Lobby area" class="align-left" src="http://farm4.static.flickr.com/3065/2646629299_093c1ec225_m.jpg"/></a>
<p>One of the interesting hallway conversations was about what happens
when upstream web hosting listed on <a class="reference" href="http://pypi.python.org/pypi" shape="rect">PyPI</a> is failing. It seems PyPI
already does some sort of mirroring, but even that might not be
enough. Many companies seem to be bundling eggs of their dependencies
in their installation package, which sounds like a good setup for
commercial click-to-install deployment. But it would still be good to
see a <a class="reference" href="http://cpan.org/" shape="rect">CPAN</a>-style mirror network for PyPI, and at least some people
seemed even motivated to donating servers and bandwidth. Personally,
I'm mostly spoiled by the combination of Debian/Ubuntu and
decentralized version control, and my level of paranoia is too high to
automatically install unverified software from the internet anyway.
My primary motivation in the conversation was to point out that PyPI
already has some sort of mirroring/upload setup, and that you'd really
want to specify exact versions and SHA-1 hashes of your dependencies.
Optionally, you could delegate the known good hash storage to PyPI
(assuming you trusted PyPI not to attack you), but that would require
a full Debian-style signature chain from a trusted key, or you'd be
owned by anyone capable of MITM attacks, DNS forgery, or cracking a
PyPI mirror.</p></div></content><category term="python" label="python"/><category term="europython" label="europython"/><category term="europython2008" label="europython2008"/></entry><entry><title>EuroPython 2008 talk #1: My God it's Full of Files</title><id>http://eagain.net/#2008-07-07_ep2008-pythonic-fs</id><link rel="alternate" href="http://eagain.net/blog/2008/07/07/ep2008-pythonic-fs.html" type="text/html"/><updated>2008-07-07T10:31:00+00:00</updated><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p>I published slides for my first <a class="reference" href="http://www.europython.org/" shape="rect">Europython</a> talk. Note the slides
might not easy to understand without the actual talk -- the video
streaming at <a class="reference" href="http://ustream.tv/europython" shape="rect">Ustream</a> will work if the network infrastructure can take
it, and a downloadable video should be available later.</p>
<p><strong>My God, it's Full of Files</strong></p>
<p><em>Pythonic filesystem abstractions: An overview of different
filesystem(-like) APIs in Python and attempts for unifying them.</em></p>
<p>There's a lot of different filesystem(-like) APIs in Python. I intend
to provide an overview of existing projects, their status and
capabilities, and hopefully inspire you to work on improving things.</p>
<p><a class="reference" href="../talks/pythonic-fs/" shape="rect">Read more...</a></p></div></content><category term="python" label="python"/><category term="programming" label="programming"/><category term="europython" label="europython"/><category term="europython2008" label="europython2008"/><category term="filesystem" label="filesystem"/><category term="talk" label="talk"/></entry><entry><title>Incremental mapreduce</title><id>http://eagain.net/#2007-11-24_incremental-mapreduce</id><link rel="alternate" href="http://eagain.net/blog/2007/11/24/incremental-mapreduce.html" type="text/html"/><updated>2007-11-24T03:48:00+00:00</updated><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p>So Google has their <a class="reference" href="http://labs.google.com/papers/mapreduce.html" shape="rect">MapReduce</a>, and the people behind <a class="reference" href="http://couchdb.com/" shape="rect">CouchDB</a> are
throwing around <a class="reference" href="http://groups.google.com/group/couchdb/browse_thread/thread/3ccb595c69ac606d?hl=en" shape="rect">their ideas</a>. I spent some time thinking about
incremental mapreduce around July, and it's time I type out that page
full of scribbles.</p>
<p>First of all: I think the ideas thrown out by Damien above aren't
really mapreduce, As Google Intended. The real power of mapreduce is
in its inherent combination of parallelism and chainability, output of
one mapreduce is input to another, each processing step can run
massively in parallel with each other, etc. The proposed design is
like a one-iteration retarded cousin of mapreduce.</p>
<p>With that bashing now done (sorry), here's what I was thinking:</p>
<p>The way I imagined building an incremental mapreduce mechanism,
without storing the intermediate data and just recomputing chunks that
are out-of-date (which would be lame), is to add one extra concept
into the system: call it "demap". It will basically create "negative
entries" for the old data. This is basically what Damien did by
providing both the old and new data map calls, all the time, just said
differently, and I think my way might make the average call a lot
simpler. And I don't see any reason why my version wouldn't be
parallelizable, chainable, and generally yummy.</p>
<p><a class="reference" href="http://eagain.net/articles/incremental-mapreduce/" shape="rect">Read the article</a></p></div></content><category term="search" label="search"/><category term="database" label="database"/><category term="couchdb" label="couchdb"/><category term="programming" label="programming"/><category term="mapreduce" label="mapreduce"/><category term="cluster" label="cluster"/><category term="parallel" label="parallel"/></entry><entry><title>KVM, the virtualization mechanism, rocks</title><id>http://eagain.net/#2007-10-12_kvm-1-xen-0</id><link rel="alternate" href="http://eagain.net/blog/2007/10/12/kvm-1-xen-0.html" type="text/html"/><updated>2007-10-12T22:59:00+00:00</updated><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p>For years now, my primary machine has been a laptop. I've been
avoiding running <a class="reference" href="http://www.xensource.com/" shape="rect">Xen</a> <a class="footnote-reference" href="#hate-productizing" id="id1" name="id1" shape="rect">[1]</a> because the <cite>hypervisor</cite>
it injects between hardware and Linux hasn't been very friendly for
power saving.  Modern laptops are too often hot enough without any
extra help.</p>
<p>Well, I'm happy to say that as of now, <a class="reference" href="http://kvm.qumranet.com/" shape="rect">KVM</a> is definitely stable
enough to replace my use of Xen for test setups. I expect that shortly
it will become what I recommend for server use, too. And because it
lets Linux be Linux, instead of doing any oddities with the hardware,
all the powersaving etc goodness still works perfectly.</p>
<p>So far, I've stumbled on two things:</p>
<p>1. The version of KVM in Ubuntu, even <cite>gutsy</cite>, is just too old. With a
bit of fiddling with the patches, KVM v46 built a perfectly working
<tt class="docutils literal"><span class="pre">deb</span></tt>.</p>
<p>2. The <tt class="docutils literal"><span class="pre">isolinux</span></tt> graphical bootup, used in <tt class="docutils literal"><span class="pre">Ubuntu</span></tt>, crashes KVM
(and with v28, it crashes the host machine -- beware!). See the <a class="reference" href="https://bugs.launchpad.net/ubuntu/+bug/83642" shape="rect">bug
report</a>. I got around
that by using <a class="reference" href="http://archive.ubuntu.com/ubuntu/dists/gutsy/main/installer-amd64/current/images/netboot/" shape="rect">mini.iso</a>,
but you could always fall back to <tt class="docutils literal"><span class="pre">debootstrap</span></tt>; that was all we
ever really had with Xen.</p>
<p>And while I have the soapbox: <em>I have a dream</em>. It's KVM running
with SDL/VNC graphics in a window that's <strong>resizeable</strong> all the way
to the virtual machine, with XRandR. Please make it happen!</p>
<p><strong>Patches</strong></p>
<p>So, the edited version of the patches:</p>
<ul class="simple">
<li><a class="reference" href="2007/10/12/01_use_bios_files_in_usr_share_kvm.patch" shape="rect">01_use_bios_files_in_usr_share_kvm.patch</a></li>
<li><a class="reference" href="2007/10/12/06_no_system_linux_kvm_h.patch" shape="rect">06_no_system_linux_kvm_h.patch</a></li>
<li><a class="reference" href="2007/10/12/04_do_not_print_rtc_freq_if_ok.patch" shape="rect">from-debian-qemu/04_do_not_print_rtc_freq_if_ok.patch</a></li>
</ul>
<p>And just remove <tt class="docutils literal"><span class="pre">from-debian-qemu/62_linux_boot_nasm.patch</span></tt>, it
seems to have made it upstream.</p>

<table class="docutils footnote" frame="void" rules="none">
<colgroup span="1"><col class="label" span="1"/><col span="1"/></colgroup>
<tbody valign="top">
<tr><td class="label" rowspan="1" colspan="1"><a class="fn-backref" href="#id1" name="hate-productizing" id="hate-productizing" shape="rect">[1]</a></td><td rowspan="1" colspan="1">What's up with <a class="reference" href="http://www.xensource.com/" shape="rect">http://www.xensource.com/</a>, by the way? That website
is just a pile of horrible non-informative enterprise
speak. Utterly useless, and that tends to alienate the techie
community pretty fast. At least it's alienating me.</td></tr>
</tbody>
</table></div></content><category term="xen" label="xen"/><category term="kvm" label="kvm"/><category term="virtualization" label="virtualization"/><category term="xrandr" label="xrandr"/><category term="idea" label="idea"/></entry><entry><title>Snakepit and gitosis, things I've been working on</title><id>http://eagain.net/#2007-10-12_snakepit-and-gitosis</id><link rel="alternate" href="http://eagain.net/blog/2007/10/12/snakepit-and-gitosis.html" type="text/html"/><updated>2007-10-12T01:46:00+00:00</updated><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p>A brief update of things I've been working on:</p>
<p><strong>Snakepit</strong></p>
<p><cite>Snakepit</cite> is a port of (part of) <a class="reference" href="http://www.hivedb.org/" shape="rect">HiveDB</a> to Python and <a class="reference" href="http://www.sqlalchemy.org/" shape="rect">SQLAlchemy</a>.
It will help you write database-backed applications that need to scale
further than one database server, or even a master-slave setup, can
take you. It's MIT licensed, that is, pretty much as free-for-all as
it can be. And it's still work in progress, so don't be too harsh yet
;)</p>
<p>Read the <a class="reference" href="http://eagain.net/gitweb/?p=snakepit.git;a=blob;f=README.rst" shape="rect">README</a>,
browse the <a class="reference" href="http://eagain.net/gitweb/?p=snakepit.git" shape="rect">gitweb</a>, or
grab the source with <tt class="docutils literal"><span class="pre">git</span> <span class="pre">clone</span> <span class="pre">git://eagain.net/snakepit.git</span></tt></p>
<p><strong>gitosis</strong></p>
<p><tt class="docutils literal"><span class="pre">gitosis</span></tt> aims to make hosting <tt class="docutils literal"><span class="pre">git</span></tt> repos easier and safer. It
manages multiple repositories under one user account, using SSH keys
to identify users. End users do not need shell accounts on the server,
they will talk to one shared account that will not let them run
arbitrary commands. <tt class="docutils literal"><span class="pre">gitosis</span></tt> is licensed under the GPL.</p>
<p>First real release will come as soon as I have to time to go through a
couple of really minor nits. It's been self-hosting for a long time
now.</p>
<p>For more, read the <a class="reference" href="http://eagain.net/gitweb/?p=gitosis.git;a=blob;f=README.rst" shape="rect">README</a>,
browse the <a class="reference" href="http://eagain.net/gitweb/?p=gitosis.git" shape="rect">gitweb</a>,
or grab the source with <tt class="docutils literal"><span class="pre">git</span> <span class="pre">clone</span> <span class="pre">git://eagain.net/gitosis.git</span></tt></p></div></content><category term="database" label="database"/><category term="git" label="git"/><category term="programming" label="programming"/><category term="scalability" label="scalability"/><category term="software" label="software"/></entry><entry><title>Git for Computer Scientists</title><id>http://eagain.net/#2007-03-29_git-for-computer-scientists</id><link rel="alternate" href="http://eagain.net/blog/2007/03/29/git-for-computer-scientists.html" type="text/html"/><published>2007-03-29T18:10:00+00:00</published><updated>2007-03-29T18:14:00+00:00</updated><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml">I wrote a <a class="reference" href="http://eagain.net/articles/git-for-computer-scientists/" shape="rect">brief introduction</a> to <a class="reference" href="http://git.or.cz/" shape="rect">git</a> for people
who are not scared by words like <a class="reference" href="http://en.wikipedia.org/wiki/Directed_acyclic_graph" shape="rect">Directed Acyclic Graph</a>.
Read it here: <a class="reference" href="http://eagain.net/articles/git-for-computer-scientists/" shape="rect">Git for Computer Scientists</a></div></content><category term="git" label="git"/><category term="howto" label="howto"/></entry><entry><title>Howto host git on your Linux box</title><id>http://eagain.net/#2007-03-22_howto-host-git</id><link rel="alternate" href="http://eagain.net/blog/2007/03/22/howto-host-git.html" type="text/html"/><published>2007-03-23T17:43:00+00:00</published><updated>2008-03-19T22:02:00+00:00</updated><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><div class="warning">
<p class="first admonition-title">Warning</p>
<p>This solution is obsolete. Use <a class="reference" href="http://eagain.net/gitweb/?p=gitosis.git;a=blob;f=README.rst" shape="rect">gitosis</a> instead!</p>
<p class="last">Gitosis is basically <tt class="docutils literal"><span class="pre">git-shell-enforce-directory</span></tt>'s big
brother, and an actual software project. <a class="reference" href="http://eagain.net/gitweb/?p=gitosis.git;a=blob;f=README.rst" shape="rect">Use it</a>.</p>
</div>
<p><strong>Updated</strong> to drop <tt class="docutils literal"><span class="pre">--use-separate-remote</span></tt> from <tt class="docutils literal"><span class="pre">git</span> <span class="pre">clone</span></tt>, it's
the default.</p>
<p><strong>Updated</strong> to add <tt class="docutils literal"><span class="pre">--read-only</span></tt> to <tt class="docutils literal"><span class="pre">git-shell-enforce-directory</span></tt>.</p>
<p>I've run repeatedly into cases where I want to provide services to
people without really trusting them. I do not want to give them shell
access. I don't want to even create separate unix user accounts for
them at all. But I do want to make sure the service they use is safe
against e.g. password sniffing.</p>
<p>Instead of trying to run the version control system over HTTPS (like
<a class="reference" href="http://subversion.tigris.org/" shape="rect">Subversion</a>'s <tt class="docutils literal"><span class="pre">mod_dav_svn</span></tt> that
will only work with Apache, which I don't run), I want to run things
through <a class="reference" href="http://openssh.org/" shape="rect">SSH</a>. SSH is the de facto unix tool
for securing communications between machines.</p>
<p>Now, I said I don't want to create a unix user account for every
developer using the version control system. With SSH, this means using
a shared account, usually named by the service it provides: <tt class="docutils literal"><span class="pre">svn</span></tt>,
<tt class="docutils literal"><span class="pre">git</span></tt>, etc. To identify different users of that account, do not give
the account a password, but use SSH keys instead. To avoid giving
people full shell access, use a <tt class="docutils literal"><span class="pre">command="..."</span></tt> when adding their
public key to <tt class="docutils literal"><span class="pre">~/.ssh/authorized_keys</span></tt>.</p>
<p>For Subversion, I submitted an enhancement to add <tt class="docutils literal"><span class="pre">--tunnel-user</span></tt>,
to make sure the commit gets identified as the right user, and then
used <tt class="docutils literal"><span class="pre">command="..."</span></tt> with the with arguments, like this (all on one
line):</p>
<pre class="literal-block">
command="/srv/example.com/repo/svn/svnserve -t
  --root /srv/example.com/repo/svn/view/examplegroup
  --tunnel-user jdoe" ssh-rsa ... jdoe@example.com
</pre>
<p>Where the <tt class="docutils literal"><span class="pre">view</span></tt> directory is a bunch of symlinks to the actual
repositories, allowing me to do group-based access control.</p>
<p>With <a class="reference" href="http://git.or.cz/" shape="rect">git</a>, the author of the changeset is
recorded way before the SSH connection is opened. Without building
some sort of access control in <tt class="docutils literal"><span class="pre">git</span></tt> hooks on the server, every
developer can pretty much ruin the repository by overwriting branches
with bogus commits. What they will not have is access outside of the
repository, or a way to actually remove the old commits from the disk
(unless you run <tt class="docutils literal"><span class="pre">git</span> <span class="pre">prune</span></tt> on the server). The distributed nature
of <tt class="docutils literal"><span class="pre">git</span></tt> makes this reasonably easy to detect, and pretty much
trivial to recover from.  For any real trust in the code, you should
look at signed tags anyway. The included wrapper allows you to have
read-only users, but provides no detailed access control against
developers with write access; they just won't be able to escape to the
rest of the filesystem.</p>
<p>So, with that introduction out of the way, let's get to configuring:</p>
<ol class="arabic">
<li><p class="first">Install <tt class="docutils literal"><span class="pre">git</span></tt> on the server:</p>
<pre class="literal-block">
sudo apt-get install git-core git-doc
</pre>
</li>
<li><p class="first">Create the directory structure store the repositories and related files:</p>
<pre class="literal-block">
sudo install -d -m0755 \
     /srv/example.com/repo/git \
     /srv/example.com/repo/git/.ssh \
     /srv/example.com/repo/git/repos \
     /srv/example.com/repo/git/view
</pre>
</li>
<li><p class="first">Create the shared user account for this service:</p>
<pre class="literal-block">
sudo adduser \
     --system \
     --home /srv/example.com/repo/git \
     --no-create-home \
     --shell /bin/sh \
     --gecos 'git version control' \
     --group \
     --disabled-password \
     git
</pre>
</li>
<li><p class="first">Set up a script that makes sure only relevant <tt class="docutils literal"><span class="pre">git</span></tt> commands can
be run via SSH, and to limit the visible section of the filesystem
to things you actually want to give access to; put this file in
<tt class="docutils literal"><span class="pre">/usr/local/bin/git-shell-enforce-directory</span></tt> (<a class="reference" href="2007/03/22/git-shell-enforce-directory" shape="rect">download</a>) and <tt class="docutils literal"><span class="pre">chmod</span> <span class="pre">a+x</span></tt> it</p>
<div class="py-listing figure">
<pre>
<span class="py-src-comment">#!/usr/bin/python
</span>
<span class="py-src-comment"># Copyright (c) 2007 Tommi Virtanen &lt;tv@eagain.net&gt;
</span><span class="py-src-comment">#
</span><span class="py-src-comment"># Permission is hereby granted, free of charge, to any person
</span><span class="py-src-comment"># obtaining a copy of this software and associated documentation files
</span><span class="py-src-comment"># (the "Software"), to deal in the Software without restriction,
</span><span class="py-src-comment"># including without limitation the rights to use, copy, modify, merge,
</span><span class="py-src-comment"># publish, distribute, sublicense, and/or sell copies of the Software,
</span><span class="py-src-comment"># and to permit persons to whom the Software is furnished to do so,
</span><span class="py-src-comment"># subject to the following conditions:
</span><span class="py-src-comment">#
</span><span class="py-src-comment"># The above copyright notice and this permission notice shall be
</span><span class="py-src-comment"># included in all copies or substantial portions of the Software.
</span><span class="py-src-comment">#
</span><span class="py-src-comment"># THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
</span><span class="py-src-comment"># EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
</span><span class="py-src-comment"># MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
</span><span class="py-src-comment"># NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
</span><span class="py-src-comment"># BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
</span><span class="py-src-comment"># ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
</span><span class="py-src-comment"># CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
</span><span class="py-src-comment"># SOFTWARE.
</span>
<span class="py-src-comment"># Enforce git-shell to only serve repositories
</span><span class="py-src-comment"># in the given directory. The client should refer
</span><span class="py-src-comment"># to them without any directory prefix.
</span><span class="py-src-comment"># Repository names are forced to match ALLOW.
</span>
<span class="py-src-keyword">import</span> <span class="py-src-variable">sys</span>, <span class="py-src-variable">os</span>, <span class="py-src-variable">optparse</span>, <span class="py-src-variable">re</span>

<span class="py-src-keyword">def</span> <span class="py-src-identifier">die</span>(<span class="py-src-parameter">msg</span>):
    <span class="py-src-keyword">print</span> &gt;&gt;<span class="py-src-variable">sys</span>.<span class="py-src-variable">stderr</span>, <span class="py-src-string">'%s: %s'</span> % (<span class="py-src-variable">sys</span>.<span class="py-src-variable">argv</span>[<span class="py-src-number">0</span>], <span class="py-src-variable">msg</span>)
    <span class="py-src-variable">sys</span>.<span class="py-src-variable">exit</span>(<span class="py-src-number">1</span>)

<span class="py-src-keyword">def</span> <span class="py-src-identifier">getParser</span>():
    <span class="py-src-variable">parser</span> = <span class="py-src-variable">optparse</span>.<span class="py-src-variable">OptionParser</span>(
        <span class="py-src-variable">usage</span>=<span class="py-src-string">'%prog [OPTIONS] DIR'</span>,
        <span class="py-src-variable">description</span>=<span class="py-src-string">'Allow restricted git operations under DIR'</span>,
        )
    <span class="py-src-variable">parser</span>.<span class="py-src-variable">add_option</span>(<span class="py-src-string">'--read-only'</span>,
                      <span class="py-src-variable">help</span>=<span class="py-src-string">'disable write operations'</span>,
                      <span class="py-src-variable">action</span>=<span class="py-src-string">'store_true'</span>,
                      <span class="py-src-variable">default</span>=<span class="py-src-variable">False</span>,
                      )
    <span class="py-src-keyword">return</span> <span class="py-src-variable">parser</span>

<span class="py-src-variable">ALLOW_RE</span> = <span class="py-src-variable">re</span>.<span class="py-src-variable">compile</span>(<span class="py-src-string">"^(?P&lt;command&gt;git-(?:receive|upload)-pack) '[a-zA-Z][a-zA-Z0-9@._-]*(/[a-zA-Z][a-zA-Z0-9@._-]*)*'$"</span>)

<span class="py-src-variable">COMMANDS_READONLY</span> = [
    <span class="py-src-string">'git-upload-pack'</span>,
    ]

<span class="py-src-variable">COMMANDS_WRITE</span> = [
    <span class="py-src-string">'git-receive-pack'</span>,
    ]

<span class="py-src-keyword">def</span> <span class="py-src-identifier">main</span>(<span class="py-src-parameter">args</span>):
    <span class="py-src-variable">os</span>.<span class="py-src-variable">umask</span>(<span class="py-src-number">0022</span>)

    <span class="py-src-variable">parser</span> = <span class="py-src-variable">getParser</span>()
    (<span class="py-src-variable">options</span>, <span class="py-src-variable">args</span>) = <span class="py-src-variable">parser</span>.<span class="py-src-variable">parse_args</span>()
    <span class="py-src-keyword">try</span>:
        (<span class="py-src-variable">path</span>,) = <span class="py-src-variable">args</span>
    <span class="py-src-keyword">except</span> <span class="py-src-variable">ValueError</span>:
        <span class="py-src-variable">parser</span>.<span class="py-src-variable">error</span>(<span class="py-src-string">'Missing argument DIR.'</span>)
    <span class="py-src-variable">os</span>.<span class="py-src-variable">chdir</span>(<span class="py-src-variable">path</span>)

    <span class="py-src-variable">cmd</span> = <span class="py-src-variable">os</span>.<span class="py-src-variable">environ</span>.<span class="py-src-variable">get</span>(<span class="py-src-string">'SSH_ORIGINAL_COMMAND'</span>, <span class="py-src-variable">None</span>)
    <span class="py-src-keyword">if</span> <span class="py-src-variable">cmd</span> <span class="py-src-keyword">is</span> <span class="py-src-variable">None</span>:
        <span class="py-src-variable">die</span>(<span class="py-src-string">"Need SSH_ORIGINAL_COMMAND in environment."</span>)

    <span class="py-src-keyword">if</span> <span class="py-src-string">'\n'</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">cmd</span>:
        <span class="py-src-variable">die</span>(<span class="py-src-string">"Command may not contain newlines."</span>)

    <span class="py-src-variable">match</span> = <span class="py-src-variable">ALLOW_RE</span>.<span class="py-src-variable">match</span>(<span class="py-src-variable">cmd</span>)
    <span class="py-src-keyword">if</span> <span class="py-src-variable">match</span> <span class="py-src-keyword">is</span> <span class="py-src-variable">None</span>:
        <span class="py-src-variable">die</span>(<span class="py-src-string">"Command to run looks dangerous"</span>)

    <span class="py-src-variable">allowed</span> = <span class="py-src-variable">list</span>(<span class="py-src-variable">COMMANDS_READONLY</span>)
    <span class="py-src-keyword">if</span> <span class="py-src-keyword">not</span> <span class="py-src-variable">options</span>.<span class="py-src-variable">read_only</span>:
        <span class="py-src-variable">allowed</span>.<span class="py-src-variable">extend</span>(<span class="py-src-variable">COMMANDS_WRITE</span>)

    <span class="py-src-keyword">if</span> <span class="py-src-variable">match</span>.<span class="py-src-variable">group</span>(<span class="py-src-string">'command'</span>) <span class="py-src-keyword">not</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">allowed</span>:
        <span class="py-src-variable">die</span>(<span class="py-src-string">"Command not allowed"</span>)

    <span class="py-src-variable">os</span>.<span class="py-src-variable">execve</span>(<span class="py-src-string">'/usr/bin/git-shell'</span>, [<span class="py-src-string">'git-shell'</span>, <span class="py-src-string">'-c'</span>, <span class="py-src-variable">cmd</span>], {})
    <span class="py-src-variable">die</span>(<span class="py-src-string">"Cannot execute git-shell."</span>)

<span class="py-src-keyword">if</span> <span class="py-src-variable">__name__</span> == <span class="py-src-string">'__main__'</span>:
    <span class="py-src-variable">main</span>(<span class="py-src-variable">args</span>=<span class="py-src-variable">sys</span>.<span class="py-src-variable">argv</span>[<span class="py-src-number">1</span>:])
</pre>
</div>
</li>
<li><p class="first">Create your first repository:</p>
<pre class="literal-block">
cd /srv/example.com/repo/git/repos
sudo install -d -o git -g git -m0700 myproject.git
sudo -H -u git env GIT_DIR=myproject.git git init
</pre>
<p>(with <tt class="docutils literal"><span class="pre">git</span></tt> older than v1.5, use <tt class="docutils literal"><span class="pre">init-db</span></tt> instead of <tt class="docutils literal"><span class="pre">init</span></tt>)</p>
</li>
<li><p class="first">Set up an access control group and give it access to that repository:</p>
<pre class="literal-block">
cd /srv/example.com/repo/git/view
sudo install -d -m0755 mygroup
cd mygroup
sudo ln -s ../../repos/myproject.git myproject.git
</pre>
<p>You can also use subdirectories of <tt class="docutils literal"><span class="pre">view/mygroup</span></tt> to organize
the repositories hierarchically.</p>
<p>Note, one SSH public key will belong to exactly one group, but if
necessary you can create a separate group for each account for
absolute control.</p>
<p>Note, access to repository implies write access to repository, at
least for now. You could make</p>
</li>
<li><p class="first">Get an SSH public key from a developer and authorize them to access
the group:</p>
<pre class="literal-block">
cd /srv/example.com/repo/git
sudo vi .ssh/authorized_keys
</pre>
<p>How the developer generates their key is out of scope here.</p>
<p>Add a line like this, with the public key in it (all on one line,
broken up in the middle of word to make sure there is no
misunderstanding about when to use a space and when not to):</p>
<pre class="literal-block">
command="/usr/local/bin/git-shell-enforce-directory /srv/exampl
  e.com/repo/git/view/mygroup",no-port-forwarding,no-X11-forwar
  ding,no-agent-forwarding,no-pty ssh-rsa ... jdoe@example.com
</pre>
<p>Or to allow only read-only access, add <tt class="docutils literal"><span class="pre">--read-only</span></tt> as an option.</p>
</li>
<li><p class="first">You can now push things to the repository with:</p>
<pre class="literal-block">
git push git@myserver.example.com:myproject.git mybranch:refs/heads/master
</pre>
<p>Note that before the first push, your server-side repository will
not contain even an initial commit, and can't really be cloned.</p>
</li>
<li><p class="first">Now the developer can clone the repository:</p>
<pre class="literal-block">
git clone git@myserver:myproject.git
</pre>
<p>or to avoid some behavior of older git that I consider confusing
(needs <tt class="docutils literal"><span class="pre">git</span></tt> v1.5 or newer):</p>
<pre class="literal-block">
git clone -o myserver git@myserver:myproject.git
</pre>
<p>They will probably want to set up <tt class="docutils literal"><span class="pre">ssh-agent</span></tt> to avoid typing the
passphrase all the time.</p>
</li>
</ol>
<p>And you're done! Good luck with your adventures with <tt class="docutils literal"><span class="pre">git</span></tt>, and
welcome to the 21st century and to distributed version control
systems.</p></div></content><category term="admin" label="admin"/><category term="git" label="git"/><category term="howto" label="howto"/><category term="ssh" label="ssh"/></entry><entry><title>Howto buy a used car in California</title><id>http://eagain.net/#2007-02-20_buy-a-car</id><link rel="alternate" href="http://eagain.net/blog/2007/02/20/buy-a-car.html" type="text/html"/><published>2007-02-20T10:40:00+00:00</published><updated>2007-02-20T23:59:00+00:00</updated><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p>Tomorrow, if everything goes well, I will buy a used car from an
individual in California. Here's a checklist of things for that, to
help others in similar situations. Some things may have already been
omitted because they weren't relevant for me, so you may want to
independently browse the websites I'm using as sources. I'm skipping
everything related to finances and haggling. I'm not covering cases
where the car isn't in good condition. Also, buying from dealerships
is different. Good luck.</p>
<p>(Updated to mention REG 262.)</p>
<ol class="arabic">
<li><p class="first">Find an ad on craigslist or whatever. I liked
<a class="reference" href="http://losangeles.listpic.com/car/" shape="rect">http://losangeles.listpic.com/car/</a></p>
</li>
<li><p class="first">Call the seller. Don't just email, start evaluating the seller.
Things to ask: <a class="reference" href="http://www.carbuyingtips.com/questions.html" shape="rect">http://www.carbuyingtips.com/questions.html</a> (though
that list is horribly extensive; I'd rather just pick the three
best-looking candidates and do that thing on the spot; you'll need
to doublecheck anyway to make sure the seller wasn't lying).</p>
<p>Ask for:</p>
<ul class="simple">
<li>the VIN of the car (usually 17 characters, on windshield)</li>
<li>full name of the seller (you'll need it anyway for the check)</li>
</ul>
</li>
<li><p class="first">Check the <a class="reference" href="http://www.carfax.com/" shape="rect">CARFAX</a> report for the car.
Go for the $24.95 30-day option, you should always look at more
than one car.</p>
<ul class="simple">
<li>compare odometer, accident history etc with what seller said
(better yet, just avoid accident cars, the risk isn't worth it)</li>
<li>make sure it's not an old junk car poorly repaired ("salvage" title)</li>
</ul>
</li>
<li><p class="first">Check the <a class="reference" href="http://www.smogcheck.ca.gov/VEHTESTS/PUBTSTQRY.ASPX" shape="rect">smog check history</a> (fails are
a sign of trouble). In California, in most of the cases, the seller
is required to provide a certificate of check newer than 90 days
old -- it seems the information flow to website is slow enough that
this latest check does not show up, or something.</p>
</li>
<li><p class="first">Check that the car hasn't suffered <a class="reference" href="http://www.autocheck.com/consumers/stormDamageAction.do" shape="rect">storm damage</a>.
(Annoying website demands cookies for a simple form. Suck.)</p>
</li>
<li><p class="first">Make an appointment and go see the car. In good sunlight, you want
to see the car.</p>
<ul class="simple">
<li>for some things to check, <a class="reference" href="http://autorepair.ca.gov/StdPage.asp?Body=/geninfo/publications/Used_Car_Guide-Jan_1996.htm" shape="rect">read more</a>,
especially <cite>On-the-Lot Checklist</cite>, <cite>Road and Test Checklist</cite></li>
<li>you <em>have</em> to drive the car; leave a photocopy of your drivers
license if needed for assurance</li>
<li>bring a car nut friend who knows what to check; play good cop bad cop
(this checklist is not as good, as I am <em>not</em> a car nut)</li>
<li>on the lot:<ul>
<li>paint chips</li>
<li>cracked windows</li>
<li>accident damage</li>
<li>signs of water damage</li>
<li>tires</li>
<li>check the VIN you were given against windshield, doors, engine,
dashboard, major body parts; if they don't match you are
dealing with a criminal</li>
</ul>
</li>
<li>while driving:<ul>
<li>brake hard, is it even</li>
<li>rev the engine, does it sound healthy</li>
<li>make plenty of starts and stops</li>
<li>go through a manual gearbox, there should be no grinding noises</li>
<li>see if the car will drive straight with your hands off the wheel</li>
<li>how does the clutch feel?</li>
<li>listen for noises throughout the test drive</li>
</ul>
</li>
<li>after driving:<ul>
<li>check for leaks</li>
</ul>
</li>
<li>to be really careful, you should check a lot of things like<ul>
<li>AC</li>
<li>windows up/down</li>
<li>radio/cd/speakers</li>
<li>etc</li>
</ul>
</li>
</ul>
</li>
<li><p class="first">Take the car to a mechanic of your choosing for
evaluation. Alternatively, choose to trust on service from brand
name vendor.</p>
</li>
<li><p class="first">Check the price against <a class="reference" href="http://www.kbb.com/" shape="rect">Kelley Blue Book</a>
etc.</p>
</li>
<li><p class="first">Now you're in business. Only bureaucracy and doublechecking left.</p>
</li>
<li><p class="first">If you know what you're willing to pay, go to your bank and get a
cashiers check. Or two alternatives, if that works out. Otherwise,
you will need to go back to the bank after haggling, and without a
deposit the seller may have sold the car to someone else. Or
something. Tuff.</p>
<p>(Some people say haggling at dealerships is easier if you show up
with a cashiers check just a bit short from what they're asking.)</p>
<p>Don't pay with cash, there's even less chance of getting it back
than cancelling a check.</p>
<p>If you happen to be a seller reading this, do the actual sale in a
bank to be sure you aren't being cheated.</p>
</li>
<li><p class="first">Download and print <a class="reference" href="http://www.dmv.ca.gov/forms/formsmost.htm" shape="rect">PDF forms from DMV</a>.</p>
<ul class="simple">
<li><cite>Bill of Sale</cite> (<a class="reference" href="http://www.dmv.ca.gov/forms/reg/reg135.pdf" shape="rect">PDF</a>)</li>
<li><cite>Statement of Facts</cite> may interest you (<a class="reference" href="http://www.dmv.ca.gov/forms/reg/reg256.pdf" shape="rect">PDF</a>)</li>
<li>things the seller needs: <cite>Notice of Release of Liability</cite> (<a class="reference" href="http://www.dmv.ca.gov/forms/reg/reg138.pdf" shape="rect">PDF</a>)</li>
</ul>
</li>
<li><p class="first">In California, sellers are required to provide a smog certificate.
Make sure you get one. Stuff on smog checks:
<a class="reference" href="http://www.autorepair.ca.gov/stdPAge.asp?Menu=/includes/Menu_GenInfo.htm&amp;Body=/Geninfo/Factsheets/Program_Areas.htm" shape="rect">1</a>,
<a class="reference" href="http://www.autorepair.ca.gov/StdPage.asp?Body=/smogcheck/doineed.htm" shape="rect">2</a>,
<a class="reference" href="http://www.dmv.ca.gov/vr/smogfaq.htm" shape="rect">3</a>.
Frankly, I'm still a bit confused myself when <em>I</em> will need to do a smog check.</p>
</li>
<li><p class="first">Check that the registration is current and that the car wasn't
repurchased under the California "Lemon law". (Err, how? As I
understand it should say so in the <cite>Certificate of Title</cite> paper)</p>
</li>
<li><p class="first">The seller should find the "pink slip" aka <cite>Certificate of
Title</cite></p>
<ul class="simple">
<li>if said paper does not have form fields for ownership transfer
and odometer reading, you need form "REG 262" from the DMV, which
is printed on special paper and not available as PDF. SUCK!</li>
<li>check seller name against his drivers license</li>
<li>fill it out with both of your info and both sign it; for
instructions search for "Where do I sign?" on <a class="reference" href="http://www.dmv.ca.gov/pubs/brochures/fast_facts/ffvr32.htm" shape="rect">this page</a></li>
<li>if a bank or something still owns a chunk of the car, their
signature is also needed on the pink slip; I'd be inclined to
avoid the complexity</li>
<li>fill in the odometer value, both sign (<a class="reference" href="http://www.dmv.ca.gov/pubs/brochures/reg/odometer.htm" shape="rect">read more</a>)</li>
<li>seller keeps the <a class="reference" href="http://www.dmv.ca.gov/pubs/brochures/reg/nrl.htm" shape="rect">Notice of Transfer and Release of Liability</a> part and
submits to DMV.</li>
<li>buyer fills in the back of the title to transfer ownership</li>
</ul>
</li>
<li><p class="first">Things to ask for before leaving</p>
<ul class="simple">
<li>is a special wheel lug key needed, get it</li>
<li>are there extra keys</li>
<li>how does the car alarm work</li>
<li>on a convertible, how does the roof work</li>
<li>any "tricks" you should know</li>
</ul>
</li>
<li><p class="first">Now you're ready to leave with your new car! Check that you have</p>
<ul class="simple">
<li><cite>Certificate of Title</cite>, signed by both, also odometer section</li>
<li><cite>Bill of Sale</cite>, signed by both</li>
<li>maintenance records</li>
<li>smog certification</li>
<li>owners manuals, repair manuals</li>
<li>spare tire, jack</li>
</ul>
<p>(sources <a class="reference" href="http://www.dmv.ca.gov/vr/checklists/ownership.htm" shape="rect">1</a>,
<a class="reference" href="http://www.carbuyingtips.com/used.htm" shape="rect">2</a>)</p>
</li>
<li><p class="first">Seller has 5 days to submit <cite>Notice of Transfer and Release of Liability</cite>
to DMV. Do it <a class="reference" href="https://mv.dmv.ca.gov/nrl/welcome.do" shape="rect">online</a>.</p>
</li>
<li><p class="first">Buyer has 10 days days to report ownership change to DMV and max 30
days to pay the fees. Read more: <a class="reference" href="http://www.dmv.ca.gov/vr/vr_info.htm#BM2522" shape="rect">checklist</a>, <a class="reference" href="http://www.dmv.ca.gov/vr/checklists/ownership.htm" shape="rect">things to send
to DMV</a>,
<a class="reference" href="http://www.dmv.ca.gov/pubs/brochures/fast_facts/ffvr32.htm" shape="rect">more info</a>.</p>
</li>
<li><p class="first">Taxes?</p>
</li>
</ol>
<p>General resources:</p>
<ul class="simple">
<li>The other side of the story: <a class="reference" href="http://wehow.ehow.com/how_2003079_sell-used-car-california.html" shape="rect">seller howto</a>. The
person you are buying from should have done all of this, and this is
what you can demand from him. Also, see the links and actual
transaction guidance inside.</li>
<li>Used car buying tips at <a class="reference" href="http://www.carbuyingtips.com/used.htm" shape="rect">http://www.carbuyingtips.com/used.htm</a></li>
</ul></div></content><category term="howto" label="howto"/><category term="car" label="car"/><category term="california" label="california"/><category term="los-angeles" label="los-angeles"/></entry><entry><title>SCALE5x: Talk summary of the OpenWengo talk</title><id>http://eagain.net/#2007-02-10_scale5x-openwengo</id><link rel="alternate" href="http://eagain.net/blog/2007/02/10/scale5x-openwengo.html" type="text/html"/><published>2007-02-10T17:05:00+00:00</published><updated>2007-02-19T19:42:00+00:00</updated><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p>More <a class="reference" href="http://socallinuxexpo.com/scale5x/" shape="rect">SCALE5x</a>: <a class="reference" href="http://socallinuxexpo.com/scale5x/speakers/speakers_neary.php" shape="rect">Dave Neary</a> is talking about <a class="reference" href="http://openwengo.org/" shape="rect">OpenWengo</a>. Note to
self: Wengo = TelCo, WengoPhone = software, OpenWengo = project
developing WengoPhone -- or something. At least it's not just <tt class="docutils literal"><span class="pre">.org</span></tt>
for community and <tt class="docutils literal"><span class="pre">.com</span></tt> for services, even if the names are way too
close to eachother.</p>
<p>Good quote (not his, didn't catch the name):</p>
<blockquote>
"People don't want to buy a quarter-inch drill.
They want a quarter-inch hole!"</blockquote>
<p>He recommended this blog for anyone interested in user interface
design: <a class="reference" href="http://headrush.typepad.com/" shape="rect">http://headrush.typepad.com/</a></p>
<p>Choice quotes:</p>
<blockquote>
<p>"Cross Platform (but sound on Linux is a disaster)"</p>
<p>"Surprisingly, for Microsoft, it's not SIP... pure SIP."
(talking about MSN Messenger)</p>
</blockquote>
<p>They intend to implement XMPP-based transport mechanisms.  Mentioned
<cite>inkboard</cite>, an Inkscape extension(?) for whiteboard-style sharing of
drawing over the internet.</p>
<p>They have games over OpenWengo (I guess XMPP?), like chess.</p>
<p>"Oh did I mention sound on Linux is horrible?"</p>
<p>Heh, we're calling audience members during the talk. From France.
And it didn't work ;)</p>
<p>OpenWengo has cross-platform video conferencing. Wow.</p></div></content><category term="conference" label="conference"/><category term="sip" label="sip"/><category term="scale5x" label="scale5x"/><category term="im" label="im"/><category term="talk" label="talk"/><category term="voip" label="voip"/><category term="software" label="software"/></entry><entry><title>SCALE5x: Talk summary of the horribly named Red Hat Xen talk</title><id>http://eagain.net/#2007-02-10_scale5x-redhat-xen</id><link rel="alternate" href="http://eagain.net/blog/2007/02/10/scale5x-redhat-xen.html" type="text/html"/><published>2007-02-10T15:45:00+00:00</published><updated>2007-02-10T15:49:00+00:00</updated><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p>More <a class="reference" href="http://socallinuxexpo.com/scale5x/" shape="rect">SCALE5x</a>: <a class="reference" href="http://socallinuxexpo.com/scale5x/speakers/speakers_folkwilliams.php" shape="rect">Sam Folk-Williams</a> is doing a talk called <cite>Xen
Virtualization in Red Hat Enterprise Linux 5 and Fedora Core 6: An
overview for System Administrators</cite> (UNGH!). And demonstrates why I
hate "big" companies like Red Hat: they sent a non-technical, but
well-practised, person to talk about Xen. He sounds convincing, but
ended up explaining Xen domU migration <em>without understanding the
concept of shared storage</em>. Gah.</p>
<p>Also note how talk summary promises live demos of Xen integration
features only Red Hat has, and how the actual talk contained no such
thing. If I didn't have wireless right now I'd be annoyed. Thank you
SCALE5x organizers, the wifi is just great.</p></div></content><category term="conference" label="conference"/><category term="redhat" label="redhat"/><category term="admin" label="admin"/><category term="xen" label="xen"/><category term="bad" label="bad"/><category term="scale5x" label="scale5x"/><category term="talk" label="talk"/></entry><entry><title>SCALE5x: Talk summary of Admin++, what root never told you</title><id>http://eagain.net/#2007-02-10_scale5x-admin</id><link rel="alternate" href="http://eagain.net/blog/2007/02/10/scale5x-admin.html" type="text/html"/><updated>2007-02-10T15:19:00+00:00</updated><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p>So I'm at <a class="reference" href="http://socallinuxexpo.com/scale5x/" shape="rect">SCALE5x</a>, listening to <a class="reference" href="http://socallinuxexpo.com/scale5x/speakers/speakers_gorodetzky.php" shape="rect">Ron Gorodetzky</a> talk about what he
learned about sysadmining for <a class="reference" href="http://digg.com/" shape="rect">Digg</a> and <a class="reference" href="http://revision3.com/" shape="rect">Revision3</a> (who try to be an
"Internet television network"; in effect, they distribute loads of big
files). Most of the tools he mentioned I already knew, but it was nice
to get independent reviews of "hey I think this is good". Here's what
I took home from his talk:</p>
<ul>
<li><p class="first">He really thinks highly of the OSCon 2005 talk <cite>Livejournal's
Backend (A history of scaling)</cite> (<a class="reference" href="http://danga.com/words/2005_oscon/oscon-2005.pdf" shape="rect">PDF</a>).</p>
</li>
<li><p class="first">He liked <a class="reference" href="http://danga.com/memcached/" shape="rect">memcached</a> and <a class="reference" href="http://www.danga.com/mogilefs/" shape="rect">MogileFS</a>.</p>
</li>
<li><p class="first">Between the lines I understood Revision3 has outsourced their big
bandwidth use -- the CDNs he mentioned by name were <a class="reference" href="http://www.cachefly.com/" shape="rect">Cachefly</a> (the
color scheme hurts even my eyes and real designers think I'm
colorblind), <a class="reference" href="http://www.bitgravity.com/" shape="rect">BitGravity</a> (caution hideous flash site) and of course
<a class="reference" href="http://www.akamai.com/" shape="rect">Akamai</a>.</p>
</li>
<li><p class="first">He spoke about outsourcing data center operations, using things like
Amazon <a class="reference" href="http://www.amazon.com/ec2" shape="rect">EC2</a> and <a class="reference" href="http://www.amazon.com/s3" shape="rect">S3</a>. I need to come up with a budget and time to
play with EC2.</p>
</li>
<li><p class="first">He stressed the importance of setting up KVMs etc properly for the
data center.</p>
</li>
<li><p class="first">Set up your infrastructure and plan for scaling before you get popular,
because you will be too busy to do them afterwards. That's nice, I like
building things scalable from scratch.</p>
</li>
<li><p class="first">Specific infrastructure management tools:</p>
<ul class="simple">
<li><a class="reference" href="http://reductivelabs.com/projects/puppet/" shape="rect">Puppet</a>
-- seems pretty much a reimplementation of cgengine</li>
<li><a class="reference" href="http://trac.mcs.anl.gov/projects/bcfg2" shape="rect">Bcfg2</a>
-- smells like academentia to me</li>
<li><a class="reference" href="http://trac.t7a.org/isconf/" shape="rect">ISconf</a>
-- from the <cite>Bootstrapping an Infrastructure</cite> people, seems to be
based on the idea of a p2p distributed cache that stores pretty
much a version control history of commands ran.</li>
</ul>
<p>As usual, I haven't yet seen anything that would actually seem to
work in the real world, unless you give up everything you already
have (like package management etc), and do things 100% their way.</p>
<p>His suggestion: as the tools are based on very different worldviews,
look at everything and try to pick the one that matches your
opinions.</p>
</li>
<li><p class="first">One thing he wouldn't skimp on: "Don't skimp on RAM."</p>
</li>
<li><p class="first">At Revision3, they use long-life server hardware and don't upgrade
the servers, instead they go for a full new deployment.</p>
</li>
</ul></div></content><category term="conference" label="conference"/><category term="admin" label="admin"/><category term="scale5x" label="scale5x"/><category term="livejournal" label="livejournal"/><category term="cluster" label="cluster"/><category term="configuration-management" label="configuration-management"/><category term="digg" label="digg"/><category term="talk" label="talk"/></entry><entry><title>IMAP over SSH Howto</title><id>http://eagain.net/#2007-02-09_imap-over-ssh</id><link rel="alternate" href="http://eagain.net/blog/2007/02/09/imap-over-ssh.html" type="text/html"/><updated>2007-02-09T21:11:00+00:00</updated><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p>Tired of managing <tt class="docutils literal"><span class="pre">n+1</span></tt> passwords? Hate having an extra network port
open on that server box? Want to have automated replication of email
to your laptop in a Unix command line geek-friendly fashion?</p>
<p>Here's how to make <a class="reference" href="http://software.complete.org/offlineimap" shape="rect">OfflineIMAP</a> synchronize mail between local and
remote <a class="reference" href="http://cr.yp.to/proto/maildir.html" shape="rect">Maildirs</a>.</p>
<ul>
<li><p class="first">on the client:</p>
<ul>
<li><p class="first">create an SSH key pair with no passphrase:</p>
<pre class="literal-block">
ssh-keygen -t rsa -N '' -f ~/.ssh/imap-preauth-key
</pre>
</li>
</ul>
</li>
<li><p class="first">on the server:</p>
<ul>
<li><p class="first">install <a class="reference" href="http://bincimap.org/" shape="rect">Binc IMAP</a> on the server; no need to have it actually
listen for network connections</p>
</li>
<li><p class="first">I store my mail as <tt class="docutils literal"><span class="pre">~/.Mail</span></tt> on the server; create a
<tt class="docutils literal"><span class="pre">~/.bincimap</span></tt> on the server and adjust to fit:</p>
<pre class="literal-block">
Mailbox {
  depot = "IMAPdir",
  umask = "0077",
  path = ".Mail",
}
</pre>
</li>
<li><p class="first">create a shell script <tt class="docutils literal"><span class="pre">~/bin/imapd-preauth</span></tt> that'll start the
IMAP daemon in a preauthenticated mode; note that OfflineIMAP
wants a certain style of handshake <tt class="docutils literal"><span class="pre">bincimapd</span></tt> doesn't know how
to do, so we fix that with <tt class="docutils literal"><span class="pre">sed</span></tt>:</p>
<pre class="literal-block">
#!/bin/sh
set -e

export BINCIMAP_LOGIN=PREAUTH+FAKE
bincimapd|sed --unbuffered '1s/^FAKE OK PREAUTH/* PREAUTH/'
</pre>
<p>Make the script executable (duh).</p>
</li>
<li><p class="first">authorize the previously generated SSH key to run <em>only</em> the above
script -- add the following to <tt class="docutils literal"><span class="pre">~/.ssh/authorized_keys</span></tt> (split
here for readability, make it all one line; replace THINGS to fit):</p>
<pre class="literal-block">
command="/home/USERNAME/bin/imapd-preauth",no-port-forwarding,
no-X11-forwarding,no-agent-forwarding,no-pty SSHPUBLICKEYHERE
</pre>
</li>
</ul>
</li>
<li><p class="first">on the client:</p>
<ul>
<li><p class="first">tell OfflineIMAP about the preauthenticated IMAP connection:</p>
<pre class="literal-block">
[Account SOMETHING]
localrepository = local-SOMETHING
remoterepository = remote-SOMETHING

[Repository local-SOMETHING]
type = Maildir
localfolders = ~/data/mail/SOMETHING

[Repository remote-SOMETHING]
type = IMAP
remotehost = HOSTNAME
preauthtunnel = env -u SSH_AUTH_SOCK ssh -q -i ~/.ssh/imap-preauth-key %(remotehost)s fake-command
</pre>
</li>
</ul>
</li>
</ul>
<p>That should be it! Have fun.</p>
<p>(And if you just broke it, feel free to give one of the halves to me.)</p></div></content><category term="howto" label="howto"/><category term="email" label="email"/><category term="ssh" label="ssh"/><category term="offlineimap" label="offlineimap"/><category term="offline" label="offline"/><category term="imap" label="imap"/><category term="software" label="software"/></entry><entry><title>Web 2.0 Explained</title><id>http://eagain.net/#2007-02-05_web20-explained</id><link rel="alternate" href="http://eagain.net/blog/2007/02/05/web20-explained.html" type="text/html"/><updated>2007-02-05T08:42:00+00:00</updated><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p>Here's a nice video on what people mean when they say Web 2.0:
<a class="reference" href="http://youtube.com/watch?v=6gmP4nk0EOE" shape="rect">http://youtube.com/watch?v=6gmP4nk0EOE</a></p>
<p>I like the style of it, though it is too fast paced if you
don't already know what it is talking about.</p>
<p>(Found via <a class="reference" href="http://www.mickipedia.com/?p=711" shape="rect">Mickipedia</a>)</p></div></content></entry><entry><title>Six Word Scifi</title><id>http://eagain.net/#2006-12-02_sixwordscifi</id><link rel="alternate" href="http://eagain.net/blog/2006/12/02/sixwordscifi.html" type="text/html"/><updated>2006-12-02T21:38:00+00:00</updated><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p><a class="reference" href="http://sixwordscifi.com/" shape="rect">Sixwordscifi.com</a> is so much fun it has to be wrong, somehow.
Here are my favorites so far (yes, one of them is mine):</p>
<blockquote>
Back me up before I die.<address><a href="http://sixwordscifi.com/archives/uncategorized/boogah/16" shape="rect">boogah</a></address></blockquote>
<blockquote>
Even at light speed, I wait.<address><a href="http://sixwordscifi.com/archives/uncategorized/jefft/33" shape="rect">jefft</a></address></blockquote>
<blockquote>
All alone in his light cone.<address><a href="http://sixwordscifi.com/archives/uncategorized/tv/40" shape="rect">tv</a></address></blockquote></div></content></entry><entry><title>The Phone Killer Phone</title><id>http://eagain.net/#2006-12-02_phone-killer</id><link rel="alternate" href="http://eagain.net/blog/2006/12/02/phone-killer.html" type="text/html"/><updated>2006-12-02T15:46:00+00:00</updated><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p>I now know what I want from my next phone. And it'll totally blow the
whole phone concept out of the water.</p>
<p>Start with a mostly-open hardware platform like <a class="reference" href="http://www.linuxdevices.com/news/NS2986976174.html" shape="rect">Neo1973</a>, add Linux
(<a class="reference" href="http://www.openmoko.com/" shape="rect">OpenMoko</a>) on top. And no need to cram in a clumsy qwerty keypad,
just carry a <a class="reference" href="http://www.frogpad.com/information/bluefroginfo.asp" shape="rect">one-hand keyboard</a> when you care about it. Less clumsy
when you don't want more than a phone, <em>full</em> SSH sweetness when you
want it. The phone itself is purely touch-screen, and the keyboard can
actually get respectable WPM with real keypress feedback. And the big
part is, because the phone is actually <em>Open</em>, plugging all this in
is not a big problem! That's just great!</p></div></content></entry><entry><title>I need a bag</title><id>http://eagain.net/#2006-11-17_a-new-bag</id><link rel="alternate" href="http://eagain.net/blog/2006/11/17/a-new-bag.html" type="text/html"/><published>2006-11-17T11:30:00+00:00</published><updated>2006-11-26T22:14:00+00:00</updated><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p>I have a shoulder strap-style bag, manufactured and used by the German
army, bought as surplus and dyed black. My 12" thinkpad fits perfectly
inside of it. But the strap is sewn in place, and seems to fail every
few years -- and now's the time.</p>
<p>The bag is really good, and I am going to get it fixed, but that
doesn't mean I can't look at alternatives. So, I need something that
fits a 12" thinkpad, isn't too big, preferably comes in black, and is
otherwise non-attention grabbing and doesn't look like it'd contain a
laptop. According to Lenovo, my laptop is about 268x211x20mm.</p>
<p>My options:</p>
<blockquote>
<ul class="simple">
<li>small messenger bag: <a class="reference" href="http://www.chromebags.com/minimetro.php" shape="rect">1</a>, <a class="reference" href="http://www.amazon.com/Manhattan-Portage-West-Side-Laptop/dp/B000BT0ERE/sr=1-5/qid=1163791646/ref=sr_1_5/103-0331664-0463044?ie=UTF8&amp;s=apparel" shape="rect">2</a></li>
<li>hard-shell attache case (not really my style, but a 12" one in
black might rock it -- though I'm not going to buy a 15" case with
internal padding to make a 12" laptop not bounce around, I want a
smaller case too; it'd still be plenty big for the obligatory dead
tree notes and docs; for pics, see <a class="reference" href="http://www.the-gadgeteer.com/review/matias_laptop_armor_cases_review" shape="rect">3</a>, <a class="reference" href="http://www.ebags.com/samsonite_business_cases/4_aluminum_attache_computer_case/product_detail/index.cfm?modelid=15017" shape="rect">4</a></li>
<li><a class="reference" href="http://www.consumating.com/blog/2006/06/the_manliest_purse_ever.html" shape="rect">the manliest purse ever</a> from <a class="reference" href="http://maxpedition.com/" shape="rect">Maxpedition</a>: I'd be willing to
carry that (in black of course;) if it fit a 12"..  And I don't
think it will.</li>
<li>more classical military style: <a class="reference" href="http://www.amazon.com/rtc8612-M-51-Engineers-Field-Olive/dp/B000B5NGT6/sr=1-8/qid=1163725696/ref=sr_1_8/102-0025330-9204136?ie=UTF8&amp;s=apparel" shape="rect">M-51 Engineers Field Bag</a>,
<a class="reference" href="http://www.amazon.com/Urban-Explorer-Black-Canvas-Shoulder/dp/B0006FMIIG/ref=pd_sim_a_4/102-0025330-9204136" shape="rect">Urban Explorer Black Canvas Shoulder</a>, <a class="reference" href="http://www.amazon.com/Military-Style-Canvas-Messenger-Case/dp/B000H29BVK/ref=pd_sbs_a_2/102-0025330-9204136" shape="rect">etc</a>..</li>
<li>something from <a class="reference" href="http://booqbags.com/" shape="rect">http://booqbags.com/</a> but the style doesn't really
smack me in the face with want</li>
<li>something from <a class="reference" href="http://www.timbuk2.com/tb2/retail/catalog.htm?categoryId=1" shape="rect">Timbuk2</a>, but they really don't seem my style</li>
</ul>
</blockquote>
<p>None of those really work for me. I guess considering the bags I
already own, I might go for something I <em>don't</em> have. Mmm, a smallish
hard-shell attache case in black, with a good enough shoulder strap
that I can sling it over my head. That might just do it. Now where do
I get one?</p></div></content></entry><entry><title>A Revver command line video upload tool</title><id>http://eagain.net/#2006-11-13_revver-upload-video</id><link rel="alternate" href="http://eagain.net/blog/2006/11/13/revver-upload-video.html" type="text/html"/><published>2006-11-13T20:27:00+00:00</published><updated>2008-07-20T13:33:00+00:00</updated><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p><strong>Update</strong>: It seems the script had gone missing at some point. <a class="reference" href="2006/11/13/revver-upload-video" shape="rect">It's
back</a>.</p>
<p>As you may or may not have noticed, I do a bunch of stuff for <a class="reference" href="http://revver.com/" shape="rect">Revver</a>.
I ended up writing a sort of a tutorial to the <a class="reference" href="http://developer.revver.com/?page_id=4" shape="rect">Revver API</a>, and as I
like to collect all kinds of code samples here, I thought I should
crossblog it here. The <a class="reference" href="http://developer.revver.com/?p=16" shape="rect">original</a> is on the <a class="reference" href="http://developer.revver.com/" shape="rect">Revver developer blog</a>.</p>
<p>One day, I was on a slow internet connection and wanted to upload a
few files. I wanted something more batch-oriented than the web-based
upload, and I have a personal bias against most current Java runtimes.
So I decided to use the cool new API and write a <a class="reference" href="2006/11/13/revver-upload-video" shape="rect">video upload client</a> , and will walk you through what it does in
this blog entry. Feel free to "just" use the tool, but hopefully this
will also help you in writing your own API clients.</p>
<p>First of all, I wanted to write something that's usable just about
everywhere. I tend to use <a class="reference" href="http://python.org/" shape="rect">Python</a>, so that's what the tool is written
in. The Python standard library didn't seem to be able to do HTTP POST
file upload (think web forms) of large files, so I ended up using
<a class="reference" href="http://curl.haxx.se/" shape="rect">curl</a> for that. This should work on any Linux/OS X/etc box with Python
and curl installed. All you <a class="reference" href="http://ubuntu.com/" shape="rect">Ubuntu</a>/<a class="reference" href="http://debian.org/" shape="rect">Debian</a> people just get to say
<tt class="docutils literal"><span class="pre">sudo</span> <span class="pre">apt-get</span> <span class="pre">install</span> <span class="pre">curl</span></tt> and that's it.</p>
<p>So, let's dive right in. The tool is imaginatively named
<tt class="docutils literal"><span class="pre">revver-upload-video</span></tt>. The first bit is the command line
parser. Don't be intimidated by the length, this is pretty much
boilerplate code, the actual API-using bits are really small. The
full file is 155 lines, total.</p>
<p>There are basically three kinds of options: mandatory, optional and
for developer use. Mandatory options are enforced later on, and
developer options are mostly meant for playing with the <a class="reference" href="http://developer.revver.com/?page_id=4#gettingstarted" shape="rect">staging
environment</a> and reusing upload tokens from previous, failed,
uploads.</p>
<div class="py-listing figure">
<pre>
<span class="py-src-comment">#!/usr/bin/python
</span><span class="py-src-string">"""
Guerrilla command line video upload tool.
"""</span>
<span class="py-src-keyword">import</span> <span class="py-src-variable">optparse</span>, <span class="py-src-variable">getpass</span>, <span class="py-src-variable">urlparse</span>, <span class="py-src-variable">urllib</span>, <span class="py-src-variable">xmlrpclib</span>, <span class="py-src-variable">subprocess</span>

<span class="py-src-keyword">def</span> <span class="py-src-identifier">getParser</span>():
    <span class="py-src-variable">parser</span> = <span class="py-src-variable">optparse</span>.<span class="py-src-variable">OptionParser</span>(
        <span class="py-src-variable">usage</span>=<span class="py-src-string">'%prog --title=TEXT --age-rating=NUM [OPTIONS] FILE..'</span>,
        <span class="py-src-variable">description</span>=<span class="py-src-string">'Upload videos to Revver'</span>)

    <span class="py-src-variable">parser</span>.<span class="py-src-variable">set_defaults</span>(
        <span class="py-src-variable">api_url</span>=<span class="py-src-string">'https://api.revver.com/xml/1.0'</span>,
        <span class="py-src-variable">upload_url</span>=<span class="py-src-string">'http://httpupload.revver.com/'</span>,
        <span class="py-src-variable">login</span>=<span class="py-src-variable">getpass</span>.<span class="py-src-variable">getuser</span>(),
        )
    <span class="py-src-variable">parser</span>.<span class="py-src-variable">add_option</span>(<span class="py-src-string">'--login'</span>,
                      <span class="py-src-variable">help</span>=<span class="py-src-string">'login name to use (default: %s)'</span> %
                      <span class="py-src-variable">parser</span>.<span class="py-src-variable">defaults</span>[<span class="py-src-string">'login'</span>])
    <span class="py-src-variable">parser</span>.<span class="py-src-variable">add_option</span>(<span class="py-src-string">'--passphrase-file'</span>,
                      <span class="py-src-variable">help</span>=<span class="py-src-string">'read passphrase from (prompt if not given)'</span>,
                      <span class="py-src-variable">metavar</span>=<span class="py-src-string">'FILE'</span>)
    <span class="py-src-variable">parser</span>.<span class="py-src-variable">add_option</span>(<span class="py-src-string">'--age-rating'</span>,
                      <span class="py-src-variable">help</span>=<span class="py-src-string">'MPAA age rating (mandatory)'</span>,
                      <span class="py-src-variable">type</span>=<span class="py-src-string">'int'</span>)
    <span class="py-src-variable">parser</span>.<span class="py-src-variable">add_option</span>(<span class="py-src-string">'--title'</span>,
                      <span class="py-src-variable">help</span>=<span class="py-src-string">'title for the video (mandatory)'</span>,
                      <span class="py-src-variable">metavar</span>=<span class="py-src-string">'TEXT'</span>)
    <span class="py-src-variable">parser</span>.<span class="py-src-variable">add_option</span>(<span class="py-src-string">'--tag'</span>,
                      <span class="py-src-variable">help</span>=<span class="py-src-string">'tags (mandatory, repeat for more tags)'</span>,
                      <span class="py-src-variable">metavar</span>=<span class="py-src-string">'KEYWORD'</span>,
                      <span class="py-src-variable">action</span>=<span class="py-src-string">'append'</span>)

    <span class="py-src-variable">parser</span>.<span class="py-src-variable">add_option</span>(<span class="py-src-string">'--author'</span>,
                      <span class="py-src-variable">help</span>=<span class="py-src-string">'author of the video'</span>,
                      <span class="py-src-variable">metavar</span>=<span class="py-src-string">'FULLNAME'</span>)
    <span class="py-src-variable">parser</span>.<span class="py-src-variable">add_option</span>(<span class="py-src-string">'--url'</span>,
                      <span class="py-src-variable">help</span>=<span class="py-src-string">'website for extra info'</span>)
    <span class="py-src-variable">parser</span>.<span class="py-src-variable">add_option</span>(<span class="py-src-string">'--credits'</span>,
                      <span class="py-src-variable">help</span>=<span class="py-src-string">'extra credits'</span>,
                      <span class="py-src-variable">metavar</span>=<span class="py-src-string">'TEXT'</span>)
    <span class="py-src-variable">parser</span>.<span class="py-src-variable">add_option</span>(<span class="py-src-string">'--description'</span>,
                      <span class="py-src-variable">help</span>=<span class="py-src-string">'a brief description'</span>,
                      <span class="py-src-variable">metavar</span>=<span class="py-src-string">'TEXT'</span>)

    <span class="py-src-variable">parser</span>.<span class="py-src-variable">add_option</span>(<span class="py-src-string">'--api-url'</span>,
                      <span class="py-src-variable">help</span>=<span class="py-src-string">'API URL to contact (developers only)'</span>)
    <span class="py-src-variable">parser</span>.<span class="py-src-variable">add_option</span>(<span class="py-src-string">'--upload-url'</span>,
                      <span class="py-src-variable">help</span>=<span class="py-src-string">'Upload URL to send the file to (developers only)'</span>)
    <span class="py-src-variable">parser</span>.<span class="py-src-variable">add_option</span>(<span class="py-src-string">'--upload-token'</span>,
                      <span class="py-src-variable">help</span>=<span class="py-src-string">'use preallocated token (developers only)'</span>,
                      <span class="py-src-variable">metavar</span>=<span class="py-src-string">'HEX'</span>,
                      <span class="py-src-variable">action</span>=<span class="py-src-string">'append'</span>)

    <span class="py-src-keyword">return</span> <span class="py-src-variable">parser</span>
</pre>
</div>
<p>If you've used <tt class="docutils literal"><span class="pre">optparse</span></tt> before, there's not much interesting
there. It just instantiates a parser object and returns it, for the
main function to use. Nothing there touches the Revver API yet.</p>
<p>Next up, we have some utility functions. <tt class="docutils literal"><span class="pre">getPassphrase</span></tt> will read a
passphrase form the file given to <tt class="docutils literal"><span class="pre">--passphrase-file=</span></tt>, or prompt
the user for one. <tt class="docutils literal"><span class="pre">getAPI</span></tt> instantiates an XML-RPC client object
with the login and passphrase, and caches it in <tt class="docutils literal"><span class="pre">options</span></tt> so if you
call <tt class="docutils literal"><span class="pre">getAPI</span></tt> more than once, you're still only prompted for the
passphrase at most once. Nothing in <tt class="docutils literal"><span class="pre">revver-upload-video</span></tt> uses that,
but these are meant to be reusable functions.</p>
<div class="py-listing figure">
<pre>
<span class="py-src-keyword">def</span> <span class="py-src-identifier">getPassphrase</span>(<span class="py-src-parameter">filename</span>=<span class="py-src-parameter">None</span>):
    <span class="py-src-keyword">if</span> <span class="py-src-variable">filename</span> <span class="py-src-keyword">is</span> <span class="py-src-keyword">not</span> <span class="py-src-variable">None</span>:
        <span class="py-src-variable">f</span> = <span class="py-src-variable">file</span>(<span class="py-src-variable">filename</span>)
        <span class="py-src-variable">passphrase</span> = <span class="py-src-variable">f</span>.<span class="py-src-variable">readline</span>().<span class="py-src-variable">rstrip</span>(<span class="py-src-string">'\n'</span>)
        <span class="py-src-variable">f</span>.<span class="py-src-variable">close</span>()
    <span class="py-src-keyword">else</span>:
        <span class="py-src-variable">passphrase</span> = <span class="py-src-variable">getpass</span>.<span class="py-src-variable">getpass</span>(<span class="py-src-string">'Passphrase for video upload: '</span>)

    <span class="py-src-keyword">return</span> <span class="py-src-variable">passphrase</span>

<span class="py-src-keyword">def</span> <span class="py-src-identifier">getAPI</span>(<span class="py-src-parameter">options</span>):
    <span class="py-src-variable">api</span> = <span class="py-src-variable">getattr</span>(<span class="py-src-variable">options</span>, <span class="py-src-string">'api'</span>, <span class="py-src-variable">None</span>)
    <span class="py-src-keyword">if</span> <span class="py-src-variable">api</span> <span class="py-src-keyword">is</span> <span class="py-src-variable">None</span>:
        <span class="py-src-variable">passphrase</span> = <span class="py-src-variable">getPassphrase</span>(<span class="py-src-variable">filename</span>=<span class="py-src-variable">options</span>.<span class="py-src-variable">passphrase_file</span>)

        (<span class="py-src-variable">scheme</span>, <span class="py-src-variable">netloc</span>, <span class="py-src-variable">path</span>, <span class="py-src-variable">query</span>, <span class="py-src-variable">fragment</span>) =
                <span class="py-src-variable">urlparse</span>.<span class="py-src-variable">urlsplit</span>(<span class="py-src-variable">options</span>.<span class="py-src-variable">api_url</span>,
                                  <span class="py-src-variable">allow_fragments</span>=<span class="py-src-variable">False</span>)
        <span class="py-src-variable">query</span> = <span class="py-src-variable">urllib</span>.<span class="py-src-variable">urlencode</span>([(<span class="py-src-string">'login'</span>, <span class="py-src-variable">options</span>.<span class="py-src-variable">login</span>),
                                  (<span class="py-src-string">'passwd'</span>, <span class="py-src-variable">passphrase</span>)])
        <span class="py-src-variable">url</span> = <span class="py-src-variable">urlparse</span>.<span class="py-src-variable">urlunsplit</span>((<span class="py-src-variable">scheme</span>, <span class="py-src-variable">netloc</span>, <span class="py-src-variable">path</span>, <span class="py-src-variable">query</span>, <span class="py-src-variable">fragment</span>))
        <span class="py-src-variable">api</span> = <span class="py-src-variable">xmlrpclib</span>.<span class="py-src-variable">Server</span>(<span class="py-src-variable">url</span>)
        <span class="py-src-variable">options</span>.<span class="py-src-variable">api</span> = <span class="py-src-variable">api</span>
    <span class="py-src-keyword">return</span> <span class="py-src-variable">api</span>
</pre>
</div>
<p>All right, now we're getting to the actual meat. <tt class="docutils literal"><span class="pre">getToken</span></tt> calls
the API method <tt class="docutils literal"><span class="pre">video.getUploadTokens</span></tt> to allocate an <cite>upload
token</cite>, that lets you upload a file to the Revver archive.</p>
<div class="py-listing figure">
<pre>
<span class="py-src-keyword">def</span> <span class="py-src-identifier">getToken</span>(<span class="py-src-parameter">api</span>):
    <span class="py-src-variable">url</span>, <span class="py-src-variable">tokens</span> = <span class="py-src-variable">api</span>.<span class="py-src-variable">video</span>.<span class="py-src-variable">getUploadTokens</span>(<span class="py-src-number">1</span>)
    <span class="py-src-keyword">assert</span> <span class="py-src-variable">len</span>(<span class="py-src-variable">tokens</span>)==<span class="py-src-number">1</span>
    <span class="py-src-variable">token</span> = <span class="py-src-variable">tokens</span>[<span class="py-src-number">0</span>]
    <span class="py-src-keyword">return</span> <span class="py-src-variable">url</span>, <span class="py-src-variable">token</span>
</pre>
</div>
<p><tt class="docutils literal"><span class="pre">createMedia</span></tt> creates a new video in the archive from your uploaded
file by calling <tt class="docutils literal"><span class="pre">video.create</span></tt> in the API. It also adds metadata
like your website URL to the video. Finally, it returns the media id
of the newly-created video.</p>
<div class="py-listing figure">
<pre>
<span class="py-src-keyword">def</span> <span class="py-src-identifier">createMedia</span>(<span class="py-src-parameter">options</span>, <span class="py-src-parameter">token</span>):
    <span class="py-src-variable">data</span> = {}
    <span class="py-src-keyword">if</span> <span class="py-src-variable">options</span>.<span class="py-src-variable">credits</span> <span class="py-src-keyword">is</span> <span class="py-src-keyword">not</span> <span class="py-src-variable">None</span>:
        <span class="py-src-variable">data</span>[<span class="py-src-string">'credits'</span>] = <span class="py-src-variable">options</span>.<span class="py-src-variable">credits</span>
    <span class="py-src-keyword">if</span> <span class="py-src-variable">options</span>.<span class="py-src-variable">url</span> <span class="py-src-keyword">is</span> <span class="py-src-keyword">not</span> <span class="py-src-variable">None</span>:
        <span class="py-src-variable">data</span>[<span class="py-src-string">'url'</span>] = <span class="py-src-variable">options</span>.<span class="py-src-variable">url</span>
    <span class="py-src-keyword">if</span> <span class="py-src-variable">options</span>.<span class="py-src-variable">description</span> <span class="py-src-keyword">is</span> <span class="py-src-keyword">not</span> <span class="py-src-variable">None</span>:
        <span class="py-src-variable">data</span>[<span class="py-src-string">'description'</span>] = <span class="py-src-variable">options</span>.<span class="py-src-variable">description</span>
    <span class="py-src-keyword">if</span> <span class="py-src-variable">options</span>.<span class="py-src-variable">author</span> <span class="py-src-keyword">is</span> <span class="py-src-keyword">not</span> <span class="py-src-variable">None</span>:
        <span class="py-src-variable">data</span>[<span class="py-src-string">'author'</span>] = <span class="py-src-variable">options</span>.<span class="py-src-variable">author</span>
    <span class="py-src-variable">api</span> = <span class="py-src-variable">getAPI</span>(<span class="py-src-variable">options</span>)
    <span class="py-src-variable">media_id</span> = <span class="py-src-variable">api</span>.<span class="py-src-variable">video</span>.<span class="py-src-variable">create</span>(<span class="py-src-variable">token</span>,
                                <span class="py-src-variable">options</span>.<span class="py-src-variable">title</span>,
                                <span class="py-src-variable">options</span>.<span class="py-src-variable">tag</span>,
                                <span class="py-src-variable">options</span>.<span class="py-src-variable">age_rating</span>,
                                <span class="py-src-variable">data</span>)
    <span class="py-src-keyword">return</span> <span class="py-src-variable">media_id</span>
</pre>
</div>
<p>Finally, we have the <tt class="docutils literal"><span class="pre">main</span></tt> function, and the bits that call it when
you run the tool. Here, we actually parse the command line arguments,
enforce the presence of the mandatory options, and bail out unless you
gave it actual files to upload.</p>
<p>For each file given on the command line, we either use one of the
tokens given to us with <tt class="docutils literal"><span class="pre">--upload-token=</span></tt>, or get one from the API
with <tt class="docutils literal"><span class="pre">getToken</span></tt>. Then we join the upload URL and the token to get
the place to upload the file to, and run <tt class="docutils literal"><span class="pre">curl</span></tt> as a subprocess to
do the actual upload. Checking that <tt class="docutils literal"><span class="pre">curl</span></tt> worked takes 8 lines, and
then we use <tt class="docutils literal"><span class="pre">createMedia</span></tt> to actually create the video. And that's
it!</p>
<div class="py-listing figure">
<pre>
<span class="py-src-keyword">def</span> <span class="py-src-identifier">main</span>(<span class="py-src-parameter">progname</span>, <span class="py-src-parameter">args</span>):
    <span class="py-src-variable">parser</span> = <span class="py-src-variable">getParser</span>()
    (<span class="py-src-variable">options</span>, <span class="py-src-variable">args</span>) = <span class="py-src-variable">parser</span>.<span class="py-src-variable">parse_args</span>()

    <span class="py-src-keyword">if</span> <span class="py-src-variable">options</span>.<span class="py-src-variable">login</span> <span class="py-src-keyword">is</span> <span class="py-src-variable">None</span>:
        <span class="py-src-variable">parser</span>.<span class="py-src-variable">error</span>(<span class="py-src-string">'You must pass --login=LOGIN'</span>)
    <span class="py-src-keyword">if</span> <span class="py-src-variable">options</span>.<span class="py-src-variable">title</span> <span class="py-src-keyword">is</span> <span class="py-src-variable">None</span>:
        <span class="py-src-variable">parser</span>.<span class="py-src-variable">error</span>(<span class="py-src-string">'You must pass --title=TEXT'</span>)
    <span class="py-src-keyword">if</span> <span class="py-src-keyword">not</span> <span class="py-src-variable">options</span>.<span class="py-src-variable">tag</span>:
        <span class="py-src-variable">parser</span>.<span class="py-src-variable">error</span>(<span class="py-src-string">'You must pass --tag=KEYWORD'</span>)
    <span class="py-src-keyword">if</span> <span class="py-src-variable">options</span>.<span class="py-src-variable">age_rating</span> <span class="py-src-keyword">is</span> <span class="py-src-variable">None</span>:
        <span class="py-src-variable">parser</span>.<span class="py-src-variable">error</span>(<span class="py-src-string">'You must pass --age-rating=NUM'</span>)
    <span class="py-src-keyword">if</span> <span class="py-src-keyword">not</span> <span class="py-src-variable">args</span>:
        <span class="py-src-variable">parser</span>.<span class="py-src-variable">error</span>(<span class="py-src-string">'Pass files to upload on command line'</span>)

    <span class="py-src-keyword">for</span> <span class="py-src-variable">filename</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">args</span>:
        <span class="py-src-keyword">if</span> <span class="py-src-variable">options</span>.<span class="py-src-variable">upload_token</span>:
            <span class="py-src-variable">token</span> = <span class="py-src-variable">options</span>.<span class="py-src-variable">upload_token</span>.<span class="py-src-variable">pop</span>(<span class="py-src-number">0</span>)
            <span class="py-src-variable">url</span> = <span class="py-src-variable">options</span>.<span class="py-src-variable">upload_url</span>
        <span class="py-src-keyword">else</span>:
            <span class="py-src-variable">api</span> = <span class="py-src-variable">getAPI</span>(<span class="py-src-variable">options</span>)
            <span class="py-src-variable">url</span>, <span class="py-src-variable">token</span> = <span class="py-src-variable">getToken</span>(<span class="py-src-variable">api</span>)
            <span class="py-src-keyword">print</span> <span class="py-src-string">'%s: allocated token %s'</span> % (<span class="py-src-variable">progname</span>, <span class="py-src-variable">token</span>)

        <span class="py-src-variable">upload_url</span> = <span class="py-src-variable">urlparse</span>.<span class="py-src-variable">urljoin</span>(<span class="py-src-variable">url</span>, <span class="py-src-variable">token</span>)
        <span class="py-src-variable">retcode</span> = <span class="py-src-variable">subprocess</span>.<span class="py-src-variable">call</span>([<span class="py-src-string">'curl'</span>,
                                   <span class="py-src-string">'-F'</span>, <span class="py-src-string">'file=@%s'</span> % <span class="py-src-variable">filename</span>,
                                   <span class="py-src-string">'--'</span>,
                                   <span class="py-src-variable">upload_url</span>,
                                   ])
        <span class="py-src-keyword">if</span> <span class="py-src-variable">retcode</span> &lt; <span class="py-src-number">0</span>:
            <span class="py-src-keyword">print</span> &gt;&gt;<span class="py-src-variable">sys</span>.<span class="py-src-variable">stderr</span>, <span class="py-src-string">'%s: upload aborted by signal %d'</span> % (
                <span class="py-src-variable">progname</span>, -<span class="py-src-variable">retcode</span>)
            <span class="py-src-variable">sys</span>.<span class="py-src-variable">exit</span>(<span class="py-src-number">1</span>)
        <span class="py-src-keyword">elif</span> <span class="py-src-variable">retcode</span> &gt; <span class="py-src-number">0</span>:
            <span class="py-src-keyword">print</span> &gt;&gt;<span class="py-src-variable">sys</span>.<span class="py-src-variable">stderr</span>, <span class="py-src-string">'%s: upload failed with code %d'</span> % (
                <span class="py-src-variable">progname</span>, <span class="py-src-variable">retcode</span>)
            <span class="py-src-variable">sys</span>.<span class="py-src-variable">exit</span>(<span class="py-src-number">1</span>)
        <span class="py-src-keyword">print</span> <span class="py-src-string">'%s: used token %s for %s'</span> % (<span class="py-src-variable">progname</span>, <span class="py-src-variable">token</span>, <span class="py-src-variable">filename</span>)

        <span class="py-src-variable">media_id</span> = <span class="py-src-variable">createMedia</span>(<span class="py-src-variable">options</span>, <span class="py-src-variable">token</span>)
        <span class="py-src-keyword">print</span> <span class="py-src-string">'%s: created media %r from %r'</span> % (<span class="py-src-variable">progname</span>,
                                                <span class="py-src-variable">media_id</span>,
                                                <span class="py-src-variable">filename</span>)

<span class="py-src-keyword">if</span> <span class="py-src-variable">__name__</span> == <span class="py-src-string">'__main__'</span>:
    <span class="py-src-keyword">import</span> <span class="py-src-variable">os</span>, <span class="py-src-variable">sys</span>
    <span class="py-src-variable">main</span>(<span class="py-src-variable">progname</span>=<span class="py-src-variable">os</span>.<span class="py-src-variable">path</span>.<span class="py-src-variable">basename</span>(<span class="py-src-variable">sys</span>.<span class="py-src-variable">argv</span>[<span class="py-src-number">0</span>]),
         <span class="py-src-variable">args</span>=<span class="py-src-variable">sys</span>.<span class="py-src-variable">argv</span>[<span class="py-src-number">1</span>:])
</pre>
</div>
<p>At this point, we have all we need to do the same thing as the
web-based upload, or the Java upload client. And you can do something
similar, by yourself. This file is copyright Revver, Inc, but licensed
under the <a class="reference" href="http://www.opensource.org/licenses/mit-license.php" shape="rect">MIT license</a> -- that means you can use it as a base for
writing your software, without any real restrictions. Download the
whole thing here: <a class="reference" href="2006/11/13/revver-upload-video" shape="rect">revver-upload-video</a>.</p></div></content></entry><entry><title>New domain name</title><id>http://eagain.net/#2006-09-30_domain-fun</id><link rel="alternate" href="http://eagain.net/blog/2006/09/30/domain-fun.html" type="text/html"/><published>2006-09-30T20:03:00+00:00</published><updated>2006-11-13T20:17:00+00:00</updated><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p>I had a bit of fun thinking up of puntastic DNS domain names.
I ended up registering <a class="reference" href="http://eagain.net/" shape="rect">eagain.net</a>, the
old <a class="reference" href="http://tv.debian.net/" shape="rect">tv.debian.net</a> name will soon
start redirecting there. Need to set up email too.. Vanity
domains are soo much fun.</p>
<p>For the rare non-nerd reading this, <tt class="docutils literal"><span class="pre">EAGAIN</span></tt> is the error code you
get when you are doing asynchronous programming with non-blocking
sockets and would block next. Err, let's just say "I write async
code and it's a neat insider joke", ok?</p>
<p>For the nerds out there, here's a bunch of wild ideas I had while
figuring out what domain name to register. Many of them are invalid
(too short), and most of the good ones are already taken, but in
case you need some inspiration:</p>
<blockquote>
<ul class="simple">
<li>asyn.ch</li>
<li>dot.at</li>
<li>ex.plo.de / im.plo.de</li>
<li>plea.se</li>
<li>fal.se</li>
<li>belong.us</li>
<li>chi.hu (as in Chihuahua, our dogs..)</li>
<li>celci.us</li>
<li>blo.gr</li>
<li>co.de</li>
<li>co.ff.ee</li>
<li>oh.no</li>
<li>wh.ee</li>
<li>wh.at</li>
<li>sh.it</li>
<li>stre.am (taken)</li>
<li>up.stre.am</li>
<li>down.stre.am</li>
<li>upstre.am</li>
<li>em.ploy.ee</li>
<li>s.tre.am</li>
<li>3x2.net (as in triple-double-w)</li>
<li>tribledouble.net</li>
<li>2by4.net (as in clue-by-four)</li>
<li>be.am (la.ser.be.am, scotty.up.be.am ;-)</li>
<li>dre.am</li>
<li>progr.am</li>
<li>pu.sh</li>
<li>pu.bli.sh</li>
<li>form.at</li>
<li>anon.ymo.us</li>
<li>pla.net</li>
<li>mag.net</li>
<li>carwa.sh</li>
<li>blo.at</li>
<li>cave.at</li>
<li>repe.at</li>
<li>pho.to</li>
<li>oct.et</li>
<li>pron.to</li>
<li>plu.to</li>
<li>lot.to</li>
<li>dit.to</li>
<li>adju.st</li>
<li>ang.st</li>
<li>arti.st</li>
<li>ava.st (type like a pirate)</li>
<li>broadca.st</li>
<li>inner.net</li>
<li>comm.it</li>
<li>mis.info</li>
<li>foc.us</li>
<li>fung.us</li>
<li>geni.us</li>
<li>gur.us</li>
<li>octop.us</li>
<li>styl.us</li>
<li>stat.us</li>
<li>tor.us</li>
<li>line.br</li>
<li>deco.de</li>
<li>while.do</li>
<li>un.do</li>
<li>weir.do</li>
<li>if.then.fi</li>
<li>cook.ie</li>
<li>newb.ie</li>
<li>zomb.ie</li>
<li>disk.io</li>
<li>comm.it</li>
<li>subm.it</li>
<li>bat.ch</li>
<li>bran.ch</li>
<li>epo.ch</li>
<li>fet.ch</li>
<li>zil.ch</li>
<li>gra.ph</li>
<li>boo.st</li>
<li>fibona.cc</li>
<li>from.to</li>
<li>justa.com</li>
</ul>
</blockquote>
<p>And for the Finns:</p>
<blockquote>
<ul class="simple">
<li>mi.au (cat lovers)</li>
<li>vai.nu (dog lovers)</li>
<li>kir.nu</li>
<li>masen.nu</li>
<li>tie.dos.to (taken, by non-Finn)</li>
<li>hit.to</li>
</ul>
</blockquote>
<p>I'm not going to say anything about the Cook Island's subdomain for
commercial entities.</p></div></content><category term="computer" label="computer"/><category term="nerd" label="nerd"/><category term="dns" label="dns"/></entry><entry><title>In case your Xen domU's have networking trouble</title><id>http://eagain.net/#2006-05-21_xen-tcp-hangs</id><link rel="alternate" href="http://eagain.net/blog/2006/05/21/xen-tcp-hangs.html" type="text/html"/><updated>2006-05-21T23:25:00+00:00</updated><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p>If your <cite>domU</cite>s have networking trouble with TCP, or some other
protocol that ends up needing fragmentation such as large ICMP pings,
you need to read this.</p>
<p>If it seems TCP handshakes complete, but no data is transferred --
especially, no actual data gets sent out from the domU -- you're
likely hitting a bug in how Xen interacts with TCP segmentation
offload.</p>
<p>The bug seems to depend on the actual network interface card the
traffic is going out from. I hear tg3 is one of the cards that
triggers it, and I'm seeing it on my home box with 8139too's.</p>
<p>The fix is pretty simple, but hard to figure unless you know what to
look for: inside the domU, run</p>
<blockquote>
ethtool -K eth0 tx off</blockquote>
<p>for each interface affected.</p>
<p>See
<a class="reference" href="http://wiki.xensource.com/xenwiki/XenFaq#head-4ce9767df34fe1c9cf4f85f7e07cb10110eae9b7" shape="rect">http://wiki.xensource.com/xenwiki/XenFaq#head-4ce9767df34fe1c9cf4f85f7e07cb10110eae9b7</a>
for the very small amount of extra information that is out there.</p></div></content><category term="xen" label="xen"/><category term="computer" label="computer"/><category term="networking" label="networking"/></entry><entry><title>iBook--, Thinkpad++</title><id>http://eagain.net/#2006-05-21_thinkpad-rocks</id><link rel="alternate" href="http://eagain.net/blog/2006/05/21/thinkpad-rocks.html" type="text/html"/><published>2006-05-21T22:41:00+00:00</published><updated>2006-05-22T07:36:00+00:00</updated><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p>Life sucks and then your computer breaks. As soon as a new X release
is out, and it seems <a class="reference" href="../blog/2006/04/22/dualhead-ibook.html" shape="rect">dual head on iBook is a possibility</a>, the darn
thing decides to fry its logic board. Again. Thankfully, Apple may
make the repair for free, if the symptoms match the <a class="reference" href="http://www.apple.com/support/ibook/faq/" shape="rect">manufacturing
problem</a>.  Again.</p>
<p>Well, the good news is that after 3 years of using the iBook, I got a
new laptop. A Lenovo Thinkpad x60s, weighing just 1.3kg. It's so light
I always think I forgot to put it in the backpack.</p>
<p>Ubuntu Dapper (flight 7) seems to work pretty well on the
x60s. Trouble spots so far:</p>
<ul>
<li><p class="first">install CD corrupted display during X autoconfig:
screen was in text mode, mostly black, with two or three
character-size grey rectangles -- hitting enter blindly
let it continue and reboot the machine</p>
</li>
<li><p class="first">suspend and hibernation fail on resume</p>
</li>
<li><p class="first">wlan hanged once, and didn't recover until I rebooted into Windows
-- unngh. Look at this:</p>
<pre class="literal-block">
ipw3945: Error sending SCAN_ABORT_CMD: time out after 500ms.
ipw3945: Radio Frequency Kill Switch is On:
Kill switch must be turned off for wireless networking to work.
ipw3945: Error sending ADD_STA: time out after 500ms.
ipw3945: Error sending RATE_SCALE: time out after 500ms.
</pre>
<p>After that, any attempt to use the interface ended with:</p>
<pre class="literal-block">
ADDRCONF(NETDEV_UP): eth1: link is not ready
</pre>
</li>
<li><p class="first">hotplugging the UltraBay docking station does not seem to work in
Linux</p>
</li>
</ul>
<p>I especially love the dual headedness, after fighting with the ATI
driver in the iBook.</p>
<p>Now I need to see about hooking the fingerprint reader up to PAM.</p></div></content><category term="hardware" label="hardware"/><category term="computer" label="computer"/><category term="ubuntu" label="ubuntu"/></entry><entry><title>My iBook has two heads</title><id>http://eagain.net/#2006-04-22_dualhead-ibook</id><link rel="alternate" href="http://eagain.net/blog/2006/04/22/dualhead-ibook.html" type="text/html"/><published>2006-04-23T18:50:00+00:00</published><updated>2006-05-02T22:04:00+00:00</updated><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p>Finally, after two years of <a class="reference" href="../articles/ibook-cloned-monitor/" shape="rect">hacks</a>, my iBook 2.2 knows how to
multihead! And no silly clone mode only, totally different image and
external output at 1600x1200 at 85Hz. This is <em>nice</em>! Thank you
<a class="reference" href="http://x.org/" shape="rect">X.org</a> people for version 7, thank you <a class="reference" href="http://wiki.debian.org/XStrikeForce" shape="rect">X Strike Force</a>!</p>
<p><strong>Update:</strong> well, now suspending fails and booting the machine results
in a black screen in over half of the tries. Bah.</p></div></content><category term="hardware" label="hardware"/><category term="computer" label="computer"/><category term="ibook" label="ibook"/></entry><entry><title>Dog clothing</title><id>http://eagain.net/#2006-02-21_dog-clothes</id><link rel="alternate" href="http://eagain.net/blog/2006/02/21/dog-clothes.html" type="text/html"/><published>2006-02-21T23:51:00+00:00</published><updated>2006-11-17T11:47:00+00:00</updated><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml">I never thought I'd fall for the "small dogs must wear cute clothes"
phenomenon, but I think I'm going to have to buy <a class="reference" href="http://www.yappyfashion.fi/epages/Kaupat.axl/?ObjectPath=/Shops/yappyfashion/Products/Tee3/SubProducts/Tee3-0001" shape="rect">this</a>.</div></content><category term="fun" label="fun"/><category term="dog" label="dog"/></entry><entry><title>render_pattern: Repeat patterns easily in Nevow templates</title><id>http://eagain.net/#2005-12-21_nevow_render_pattern</id><link rel="alternate" href="http://eagain.net/blog/2005/12/21/nevow_render_pattern.html" type="text/html"/><published>2005-12-21T19:18:00+00:00</published><updated>2005-12-27T11:48:00+00:00</updated><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p>After <tt class="docutils literal"><span class="pre">render_fragment</span></tt>, <cite>dialtone</cite> mentioned <tt class="docutils literal"><span class="pre">render_pattern</span></tt>,
that would get one or many patterns from the page and put them in the
current tag. Well, that's easy to write:</p>
<div class="py-listing figure">
<pre>
<span class="py-src-keyword">def</span> <span class="py-src-identifier">render_pattern</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">name</span>):
    <span class="py-src-string">"""
    Find and render a pattern.

    Example:

    &lt;span nevow:pattern="foo"&gt;
      I'm very repetititive.
    &lt;/span&gt;
    &lt;ul&gt;
      &lt;li nevow:render="pattern foo"&gt;
        this text will get removed when rendering
      &lt;/li&gt;
      &lt;li nevow:render="pattern foo"/&gt;
    &lt;/ul&gt;
    """</span>
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">f</span>(<span class="py-src-parameter">ctx</span>, <span class="py-src-parameter">data</span>):
        <span class="py-src-variable">doc</span> = <span class="py-src-variable">self</span>.<span class="py-src-variable">docFactory</span>.<span class="py-src-variable">load</span>(<span class="py-src-variable">ctx</span>)
        <span class="py-src-variable">patterns</span> = <span class="py-src-variable">inevow</span>.<span class="py-src-variable">IQ</span>(<span class="py-src-variable">doc</span>).<span class="py-src-variable">allPatterns</span>(<span class="py-src-variable">name</span>)
        <span class="py-src-keyword">return</span> <span class="py-src-variable">ctx</span>.<span class="py-src-variable">tag</span>.<span class="py-src-variable">clear</span>()[<span class="py-src-variable">patterns</span>]
    <span class="py-src-keyword">return</span> <span class="py-src-variable">f</span>
</pre>
</div>
<p><em>Updated</em> to adapt doc to <tt class="docutils literal"><span class="pre">inevow.IQ</span></tt> before calling
<tt class="docutils literal"><span class="pre">allPatterns</span></tt>.</p></div></content><category term="nevow" label="nevow"/><category term="python" label="python"/><category term="programming" label="programming"/><category term="twisted" label="twisted"/></entry><entry><title>render_fragment: Reusable fragment embedding in Nevow templates</title><id>http://eagain.net/#2005-12-21_nevow_render_fragment</id><link rel="alternate" href="http://eagain.net/blog/2005/12/21/nevow_render_fragment.html" type="text/html"/><published>2005-12-21T18:40:00+00:00</published><updated>2005-12-21T22:45:00+00:00</updated><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p>This <a class="reference" href="http://nevow.org/" shape="rect">Nevow</a> renderer came up on #twisted.web. Thanks to <cite>rwall</cite> and
<cite>dialtone</cite> for input.</p>
<div class="py-listing figure">
<pre>
<span class="py-src-keyword">def</span> <span class="py-src-identifier">render_fragment</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">name</span>):
    <span class="py-src-string">"""
    Find and render a fragment, with optional docFactory.

    Find a fragment factory from self via attributes named
    fragment_* and replace content of current tag with said
    fragment.

    If pattern docFactory is found under this tag, pass it as
    docFactory to the fragment factory.

    Example:

    class MyFrag(rend.Fragment):
        ...

    class MyPage(rend.Page):
        fragment_foo = MyFrag
        ...

    and give MyPage a template with

    &lt;!-- no docFactory --&gt;
    &lt;div nevow:render="fragment foo"&gt;
      this text will get removed when rendering
    &lt;/div&gt;

    &lt;!-- with docFactory --&gt;
    &lt;div nevow:render="fragment foo"&gt;
      this text will get removed when rendering
      &lt;span nevow:pattern="docFactory"&gt;
        but this whole tag will be passed as docFactory to MyFrag.
      &lt;/span&gt;
    &lt;/div&gt;
    """</span>
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">f</span>(<span class="py-src-parameter">ctx</span>, <span class="py-src-parameter">data</span>):
        <span class="py-src-variable">callable</span> = <span class="py-src-variable">getattr</span>(<span class="py-src-variable">self</span>, <span class="py-src-string">'fragment_%s'</span> % <span class="py-src-variable">name</span>, <span class="py-src-variable">None</span>)
        <span class="py-src-keyword">if</span> <span class="py-src-variable">callable</span> <span class="py-src-keyword">is</span> <span class="py-src-variable">None</span>:
            <span class="py-src-variable">callable</span> = <span class="py-src-keyword">lambda</span> <span class="py-src-variable">ctx</span>, *<span class="py-src-variable">args</span>: <span class="py-src-variable">ctx</span>.<span class="py-src-variable">tag</span>[
             <span class="py-src-string">"The fragment named '%s' was not found in %r."</span> % (<span class="py-src-variable">name</span>, <span class="py-src-variable">self</span>)]
        <span class="py-src-variable">kwargs</span> = {}
        <span class="py-src-keyword">try</span>:
             <span class="py-src-variable">docFactory</span> = <span class="py-src-variable">ctx</span>.<span class="py-src-variable">tag</span>.<span class="py-src-variable">onePattern</span>(<span class="py-src-string">'docFactory'</span>)
        <span class="py-src-keyword">except</span> <span class="py-src-variable">stan</span>.<span class="py-src-variable">NodeNotFound</span>:
             <span class="py-src-keyword">pass</span>
        <span class="py-src-keyword">else</span>:
             <span class="py-src-variable">kwargs</span>[<span class="py-src-string">'docFactory'</span>] = <span class="py-src-variable">loaders</span>.<span class="py-src-variable">stan</span>(<span class="py-src-variable">docFactory</span>)
        <span class="py-src-keyword">return</span> <span class="py-src-variable">ctx</span>.<span class="py-src-variable">tag</span>.<span class="py-src-variable">clear</span>()[<span class="py-src-variable">callable</span>(**<span class="py-src-variable">kwargs</span>)]
    <span class="py-src-keyword">return</span> <span class="py-src-variable">f</span>
</pre>
</div></div></content><category term="nevow" label="nevow"/><category term="python" label="python"/><category term="programming" label="programming"/><category term="twisted" label="twisted"/></entry><entry><title>render_if: Conditional Parts in Nevow Templates</title><id>http://eagain.net/#2005-12-17_nevow_render_if</id><link rel="alternate" href="http://eagain.net/blog/2005/12/17/nevow_render_if.html" type="text/html"/><published>2005-12-17T16:59:00+00:00</published><updated>2006-03-22T22:52:00+00:00</updated><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p>This <a class="reference" href="http://nevow.org/" shape="rect">Nevow</a> renderer has saved me a lot of time:</p>
<div class="py-listing figure">
<pre>
<span class="py-src-keyword">def</span> <span class="py-src-identifier">render_if</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">ctx</span>, <span class="py-src-parameter">data</span>):
    <span class="py-src-variable">r</span>=<span class="py-src-variable">ctx</span>.<span class="py-src-variable">tag</span>.<span class="py-src-variable">allPatterns</span>(<span class="py-src-variable">str</span>(<span class="py-src-variable">bool</span>(<span class="py-src-variable">data</span>)))
    <span class="py-src-keyword">return</span> <span class="py-src-variable">ctx</span>.<span class="py-src-variable">tag</span>.<span class="py-src-variable">clear</span>()[<span class="py-src-variable">r</span>]
</pre>
</div>
<p>Use it like this:</p>
<pre class="literal-block">
&lt;nevow:invisible nevow:render="if" nevow:data="items"&gt;
  &lt;ul nevow:pattern="True"
      nevow:render="sequence"&gt;
    &lt;li nevow:pattern="header"&gt;The items are a-coming!&lt;/li&gt;
    &lt;li nevow:pattern="item"&gt;(the items will be here)&lt;/li&gt;
  &lt;/ul&gt;
&lt;/nevow:invisible&gt;
</pre>
<p>And now, if the list returned by <tt class="docutils literal"><span class="pre">data_items</span></tt> is empty, there
will be no <tt class="docutils literal"><span class="pre">&lt;ul&gt;</span></tt> tag at all in the output.</p>
<p>I just realized non-boolean tests may be wanted -- for example, test
if a string matches a regexp. You could do that by mangling the data
before <tt class="docutils literal"><span class="pre">render_if</span></tt>, but that's not nice, because then you don't have
access to the original data inside <tt class="docutils literal"><span class="pre">nevow:pattern="True"</span></tt>. So,
instead let's parametrize the test:</p>
<div class="py-listing figure">
<pre>
<span class="py-src-keyword">def</span> <span class="py-src-identifier">render_ifparam</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">name</span>):
    <span class="py-src-variable">tester</span> = <span class="py-src-variable">getattr</span>(<span class="py-src-variable">self</span>, <span class="py-src-string">'tester_%s'</span> % <span class="py-src-variable">name</span>, <span class="py-src-variable">None</span>)

    <span class="py-src-keyword">if</span> <span class="py-src-variable">tester</span> <span class="py-src-keyword">is</span> <span class="py-src-variable">None</span>:
        <span class="py-src-variable">callable</span> = <span class="py-src-keyword">lambda</span> <span class="py-src-variable">context</span>, <span class="py-src-variable">data</span>: <span class="py-src-variable">context</span>.<span class="py-src-variable">tag</span>[
             <span class="py-src-string">"The tester named '%s' was not found in %r."</span> % (<span class="py-src-variable">name</span>, <span class="py-src-variable">self</span>)]
        <span class="py-src-keyword">return</span> <span class="py-src-variable">callable</span>

    <span class="py-src-keyword">def</span> <span class="py-src-identifier">f</span>(<span class="py-src-parameter">ctx</span>, <span class="py-src-parameter">data</span>):
        <span class="py-src-variable">r</span>=<span class="py-src-variable">ctx</span>.<span class="py-src-variable">tag</span>.<span class="py-src-variable">allPatterns</span>(<span class="py-src-variable">str</span>(<span class="py-src-variable">bool</span>(<span class="py-src-variable">tester</span>(<span class="py-src-variable">data</span>))))
        <span class="py-src-keyword">return</span> <span class="py-src-variable">ctx</span>.<span class="py-src-variable">tag</span>.<span class="py-src-variable">clear</span>()[<span class="py-src-variable">r</span>]

    <span class="py-src-keyword">return</span> <span class="py-src-variable">f</span>
</pre>
</div>
<p>Note how we still cast the return value of the tester to boolean.  You
could avoid that and call the renderer <tt class="docutils literal"><span class="pre">render_switch</span></tt>.  Adding
support for Deferred tests would be quite easy, too.  The only ugly
part is I don't know of any way to make the same renderer work nicely
for <tt class="docutils literal"><span class="pre">nevow:render="if"</span></tt> and <tt class="docutils literal"><span class="pre">nevow:render="ifparam</span> <span class="pre">foo"</span></tt>.</p>
<p>[Updated to add <tt class="docutils literal"><span class="pre">return</span> <span class="pre">f</span></tt>, also renamed second <tt class="docutils literal"><span class="pre">render_if</span></tt> to
<tt class="docutils literal"><span class="pre">render_ifparam</span></tt> to clarify things a bit. Thanks <tt class="docutils literal"><span class="pre">k3mper</span></tt>.]</p></div></content><category term="nevow" label="nevow"/><category term="python" label="python"/><category term="programming" label="programming"/><category term="twisted" label="twisted"/></entry><entry><title>My review of Twisted Network Programming Essentials</title><id>http://eagain.net/#2005-11-29_snakeball</id><link rel="alternate" href="http://eagain.net/blog/2005/11/29/snakeball.html" type="text/html"/><published>2005-11-29T02:12:00+00:00</published><updated>2005-11-30T17:23:00+00:00</updated><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p>I've just finished <a class="reference" href="../articles/review-snakeball/" shape="rect">my review</a> of the book <a class="reference" href="http://fettig.net/tnpe/" shape="rect">Twisted Network
Programming Essentials</a> by Abe Fettig. Now <a class="reference" href="../articles/review-snakeball/" shape="rect">go read it</a>.</p>
<p>You may also be interested in an <a class="reference" href="../articles/review-pynet/" shape="rect">earlier review</a> I wrote about
<a class="reference" href="http://www.complete.org/publications/pynet/" shape="rect">Foundations of Python Network Programming</a>.</p></div></content><category term="python" label="python"/><category term="programming" label="programming"/><category term="twisted" label="twisted"/><category term="book-review" label="book-review"/></entry><entry><title>turku-dev: Kehittäjätapaaminen Turussa</title><id>http://eagain.net/#2005-11-22_turku-dev</id><link rel="alternate" href="http://eagain.net/blog/2005/11/22/turku-dev.html" type="text/html"/><published>2005-11-22T15:48:00+00:00</published><updated>2005-11-25T15:33:00+00:00</updated><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p>This entry is about a local software developer gathering,
and written in Finnish. My apologies if it is complete
gibberish to you, but atleast you can stare at the pretty
ä dots.</p>
<dl class="docutils">
<dt>Mikä?</dt>
<dd><p class="first">Vapaamuotoinen tapaaminen ohjelmistoja työkseen ja/tai
harrastuksekseen tekeville, tai muuten aiheesta kiinnostuneille.</p>
<p>Tutustutaan ihmisiin, puhutaan mukavia, syödään ruokaa.
Jos haluat kertoa hienosta uudesta softasta, jota olet
juuri tekemässä, löydät varmaan jonkun samanmielisen.
Jos tarvitset apua hankalaan ongelmaan, joku varmaan on
joskus tehnyt jotain samankaltaista. Eikä ihmisten
tunteminen ainakaan haittaa urakehitystäkään.</p>
<p class="last">Sillä ei ole väliä onko työkalusi C, Perl, Java, Python,
Ruby vai PHP; tai Linux, BSD, OS X vai jopa Windows.
Vapaat/avoimet ohjelmistot ovat monelle meistä tärkeitä,
joten niiltä et kokonaan pysty välttymään, mutta tarkoitus
on vain saada samanhenkisiä ihmisiä kokoon.</p>
</dd>
<dt>Missä?</dt>
<dd>Turun keskustassa oleva ravintola <a class="reference" href="http://www.ravintolaharald.com/index2.html" shape="rect">Harald</a>, katso <a class="reference" href="http://opaskartta.turku.fi/cgi-bin/GifMap.dll?Theme=-%20Opaskartta&amp;West=69522.7&amp;South=104645.0&amp;East=70422.8&amp;North=105445.1&amp;Height=400&amp;Width=450&amp;Command=DisplayLink&amp;Language=fin&amp;Info=105045.0,69966.7,Harald&amp;Page" shape="rect">kartta</a>.</dd>
</dl>
<blockquote>
Meitä kiinnostaa eniten Turun seudun toiminta, mutta
ajatuksia "road showsta" on heitetty ilmaan, eli jatkossa
kehittäjätapaaminen saattaa olla sinunkin lähikuppilassasi.</blockquote>
<dl class="docutils">
<dt>Koska?</dt>
<dd><p class="first">Nyt lauantaina, 26.11. n. klo 12:30 alkaen. Niin pitkään
kun intoa riittää.</p>
<p class="last">Seuraava kerta varmaan joskus tammikuussa, ja siitä sitten
eteenpäin vaikka kuukauden tai parin välein.</p>
</dd>
<dt>Kuka?</dt>
<dd><p class="first">Tällä tapahtumalla ei ole virallista järjestäjää, eikä se
liity minkään yhdistyksen tjms. toimintaan. Minä aloin asiasta
tutuille puhumaan, Tero Kuusela on tehnyt lähes kaiken
valmistelutyön.</p>
<p class="last">Tällä hetkellä aiheesta kiinnostuneiden ihmisten taustoja ja
kiinnostuksia: VSTKY, Linux-Aktivaattori, Debian, Python,
Linux kernel, Google Summer of Code, jne..</p>
</dd>
<dt>Tahtoo!</dt>
<dd><p class="first">Liity postituslistalle. Listan osoite on
<a class="reference" href="mailto:turku-dev@lists.inoi.fi" shape="rect">turku-dev@lists.inoi.fi</a> ja liittyminen tapahtuu lähettämällä
viesti osoitteeseen <a class="reference" href="mailto:turku-dev-subscribe@lists.inoi.fi" shape="rect">turku-dev-subscribe@lists.inoi.fi</a> ja
vastaamalla vahvistus-pyyntöön.</p>
<p class="last">Ihmismäärän arvioimiseksi pyydän, että ilmoitat tulostasi
etukäteen osoitteeseen Tero Kuusela &lt;<a class="reference" href="mailto:tero@teroajk.net" shape="rect">tero@teroajk.net</a>&gt;.</p>
</dd>
</dl></div></content><author><name>Tommi Virtanen</name><email>tv@inoi.fi</email></author><category term="lang:fi" label="lang:fi"/><category term="finland" label="finland"/><category term="meeting" label="meeting"/><category term="turku-dev" label="turku-dev"/><category term="software-development" label="software-development"/></entry><entry><title>The MochiKit screencast is very nice</title><id>http://eagain.net/#2005-11-21_mochikit</id><link rel="alternate" href="http://eagain.net/blog/2005/11/21/mochikit.html" type="text/html"/><updated>2005-11-21T16:47:00+00:00</updated><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p>"It's simply a more convenient syntax."</p>
<p>"MochiKit is full of more convenient syntax."</p>
<p>The <a class="reference" href="http://mochikit.com/" shape="rect">MochiKit</a> <a class="reference" href="http://mochikit.com/screencasts/MochiKit_Intro-1.html" shape="rect">screencast</a> is great. I think screencasts are a great
way to introduce people to new software.</p></div></content><category term="python" label="python"/><category term="ajax" label="ajax"/><category term="javascript" label="javascript"/><category term="twisted" label="twisted"/></entry><entry><title>I registered at Technorati.com</title><id>http://eagain.net/#2005-11-21_technorati</id><link rel="alternate" href="http://eagain.net/blog/2005/11/21/technorati.html" type="text/html"/><published>2005-11-21T01:37:00+00:00</published><updated>2005-11-21T01:56:00+00:00</updated><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p>I just registered at <a class="reference" href="http://technorati.com/" shape="rect">Technorati</a>, and it requires me to post an
entry claiming my <a class="reference" href="http://technorati.com/claim/iezctc84pk" shape="rect">Technorati Profile</a>.</p>
<p>..and when I registered an alternate URL for it, they required
me to do it all over again:
<a class="reference" href="http://technorati.com/claim/ykh5rdp8gx" shape="rect">Technorati Profile</a>.</p></div></content><category term="technorati" label="technorati"/></entry><entry><title>New website template</title><id>http://eagain.net/#2005-11-20_new-website</id><link rel="alternate" href="http://eagain.net/blog/2005/11/20/new-website.html" type="text/html"/><updated>2005-11-20T17:34:00+00:00</updated><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml">Just finished a new website layout. I'm reasonably pleased with it.</div></content><category term="web" label="web"/></entry><entry><title>Python is confusing</title><id>http://eagain.net/#2005-11-02_python-is-confusing</id><link rel="alternate" href="http://eagain.net/blog/2005/11/02/python-is-confusing.html" type="text/html"/><updated>2005-11-02T14:40:00+00:00</updated><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><div class="py-listing figure">
<pre>
&gt;&gt;&gt; <span class="py-src-keyword">def</span> <span class="py-src-identifier">simple</span>(): <span class="py-src-keyword">yield</span> <span class="py-src-string">'a'</span>
...
&gt;&gt;&gt; <span class="py-src-string">', '</span>.<span class="py-src-variable">join</span>(<span class="py-src-variable">simple</span>())
<span class="py-src-string">'a'</span>
&gt;&gt;&gt; <span class="py-src-keyword">def</span> <span class="py-src-identifier">horrible</span>():
...     <span class="py-src-keyword">if</span> <span class="py-src-string">' '</span> <span class="py-src-keyword">not</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">False</span>: <span class="py-src-keyword">yield</span> <span class="py-src-string">'a'</span>
...
&gt;&gt;&gt; <span class="py-src-string">', '</span>.<span class="py-src-variable">join</span>(<span class="py-src-variable">horrible</span>())
<span class="py-src-variable">Traceback</span> (<span class="py-src-variable">most</span> <span class="py-src-variable">recent</span> <span class="py-src-variable">call</span> <span class="py-src-variable">last</span>):
  <span class="py-src-variable">File</span> <span class="py-src-string">"&lt;stdin&gt;"</span>, <span class="py-src-variable">line</span> <span class="py-src-number">1</span>, <span class="py-src-keyword">in</span><span class="py-src-errortoken"> </span><span class="py-src-errortoken">?</span>
<span class="py-src-variable">TypeError</span>: <span class="py-src-variable">sequence</span> <span class="py-src-variable">expected</span>, <span class="py-src-variable">generator</span> <span class="py-src-variable">found</span>
&gt;&gt;&gt;
</pre>
</div>
<p>But it <em>does</em> accept generators!</p>
<p>(Yes, I know what triggers it to say that. It's still horribly
misleading.)</p></div></content><category term="python" label="python"/><category term="programming" label="programming"/></entry><entry><title>Using nevow.guard the smart way</title><id>http://eagain.net/#2005-09-16_getActionURL</id><link rel="alternate" href="http://eagain.net/blog/2005/09/16/getActionURL.html" type="text/html"/><published>2005-09-16T22:59:00+00:00</published><updated>2005-09-17T00:06:57+00:00</updated><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><pre class="literal-block">
&lt;ronwalf&gt; ok, I give up... How do I get the AVATAR_LOGIN stuck
between the SessionWrapped resource ul and the current
resource url
</pre>
<p>Well, we aim to please.</p>
<div class="py-listing figure">
<pre>
<span class="py-src-keyword">def</span> <span class="py-src-identifier">getActionURL</span>(<span class="py-src-parameter">ctx</span>):
    <span class="py-src-variable">request</span> = <span class="py-src-variable">inevow</span>.<span class="py-src-variable">IRequest</span>(<span class="py-src-variable">ctx</span>)
    <span class="py-src-variable">current</span> = <span class="py-src-variable">url</span>.<span class="py-src-variable">URL</span>.<span class="py-src-variable">fromRequest</span>(<span class="py-src-variable">request</span>).<span class="py-src-variable">clear</span>()
    <span class="py-src-variable">root</span> = <span class="py-src-variable">request</span>.<span class="py-src-variable">getRootURL</span>()
    <span class="py-src-variable">root</span> = <span class="py-src-variable">url</span>.<span class="py-src-variable">URL</span>.<span class="py-src-variable">fromString</span>(<span class="py-src-variable">root</span>)
    <span class="py-src-keyword">assert</span> <span class="py-src-variable">root</span> <span class="py-src-keyword">is</span> <span class="py-src-keyword">not</span> <span class="py-src-variable">None</span>
    <span class="py-src-variable">root</span> = <span class="py-src-variable">root</span>.<span class="py-src-variable">pathList</span>()
    <span class="py-src-variable">me</span> = <span class="py-src-variable">current</span>.<span class="py-src-variable">pathList</span>(<span class="py-src-variable">copy</span>=<span class="py-src-variable">True</span>)
    <span class="py-src-variable">diff</span> = <span class="py-src-variable">len</span>(<span class="py-src-variable">me</span>) - <span class="py-src-variable">len</span>(<span class="py-src-variable">root</span>)
    <span class="py-src-keyword">assert</span> <span class="py-src-variable">diff</span> &gt;= <span class="py-src-number">0</span>
    <span class="py-src-variable">action</span> = <span class="py-src-variable">current</span>
    <span class="py-src-keyword">if</span> <span class="py-src-variable">diff</span> == <span class="py-src-number">1</span>:
        <span class="py-src-variable">action</span> = <span class="py-src-variable">action</span>.<span class="py-src-variable">curdir</span>()
    <span class="py-src-keyword">else</span>:
        <span class="py-src-keyword">while</span> <span class="py-src-variable">diff</span> &gt; <span class="py-src-number">1</span>:
            <span class="py-src-variable">diff</span> -= <span class="py-src-number">1</span>
            <span class="py-src-variable">action</span> = <span class="py-src-variable">action</span>.<span class="py-src-variable">parent</span>()
    <span class="py-src-variable">action</span> = <span class="py-src-variable">action</span>.<span class="py-src-variable">child</span>(<span class="py-src-variable">guard</span>.<span class="py-src-variable">LOGIN_AVATAR</span>)
    <span class="py-src-keyword">for</span> <span class="py-src-variable">element</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">me</span>[<span class="py-src-variable">len</span>(<span class="py-src-variable">root</span>):]:
        <span class="py-src-variable">action</span> = <span class="py-src-variable">action</span>.<span class="py-src-variable">child</span>(<span class="py-src-variable">element</span>)
    <span class="py-src-keyword">return</span> <span class="py-src-variable">action</span>
</pre>
</div>
<p>Comment from ronwalf (on IRC) on 2005-09-17T00:06:57:</p>
<pre class="literal-block">
&lt;ronwalf&gt; Better.  after root = root.pathList()
&lt;ronwalf&gt; if root == ['']: root = []
</pre></div></content><category term="python" label="python"/><category term="programming" label="programming"/><category term="twisted" label="twisted"/></entry><entry><title>Turuxi gathering looks a bit too boring :(</title><id>http://eagain.net/#2005-09-16_turuxi</id><link rel="alternate" href="http://eagain.net/blog/2005/09/16/turuxi.html" type="text/html"/><updated>2005-09-16T15:23:18+00:00</updated><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml">The <a class="reference" href="http://turuxi.org/" shape="rect">Turuxi</a> <a class="reference" href="http://twiki.linux-aktivaattori.org/view/Turuxi/TuruxiTapaaminen16092005" shape="rect">gathering</a> today looks like a bit too boring for me,
personally. I think I'll go xycle instead.</div></content></entry></feed>
