<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="/stylesheets/rss.css" type="text/css"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
  <channel>
    <title>Tom Moertel's Weblog: Tag python</title>
    <link>http://blog.moertel.com/articles/tag/python?tag=python</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>Quality rants on programming theory and stuff geeks like</description>
    <item>
      <title>Thinking algebraically: a functional-programming dividend that pays during your imperative-programming day job</title>
      <description>&lt;p&gt;Although at work I code mostly in Python &amp;#8211; a language from which
&lt;a href="http://www.artima.com/weblogs/viewpost.jsp?thread=98196"&gt;&lt;em&gt;lambda&lt;/em&gt; and &lt;em&gt;map&lt;/em&gt; were nearly removed&lt;/a&gt; &amp;#8211; I still find that functional-programming experience
has its benefits.  One of the
&amp;#8220;functional-programming dividends&amp;#8221; I notice most often is insight
gained from considering problems from an algebraic perspective.&lt;/p&gt;


	&lt;p&gt;Recently, for example, I had a small parsing problem.  I had to
write code that, given a simple grammar specification as input, emits
a regular expression that matches the language generated by the
grammar.&lt;/p&gt;


	&lt;p&gt;Here&amp;#8217;s a simplified version of the problem.  A grammar specification
is limited to a series of one or more &lt;em&gt;atoms&lt;/em&gt;.  For example, &amp;#8220;a b c&amp;#8221; 
represents the atom &amp;#8220;a&amp;#8221;, followed by the atom &amp;#8220;b&amp;#8221;, followed by the
atom &amp;#8220;c&amp;#8221;.  To generate the grammar, the series of atoms is interpreted
such that each atom (except the last) generates a production rule of
the following form:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;atom_rule ::=
  &amp;lt;the literal atom&amp;gt; (SPACE &amp;lt;the next rule&amp;gt; | NOTHING)
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;(SPACE represents literal white space and &lt;span class="caps"&gt;NOTHING&lt;/span&gt; represents an
empty string.)  The grammar as a whole is rooted in the first atom&amp;#8217;s
rule.&lt;/p&gt;


	&lt;p&gt;Thus the specification &amp;#8220;a b c&amp;#8221; represents the following grammar:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;grammar ::= a_rule
a_rule  ::= "a" (SPACE b_rule | NOTHING)
b_rule  ::= "b" (SPACE c_rule | NOTHING)
c_rule  ::= "c" 
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;Note that the final atom&amp;#8217;s production matches only the literal atom
itself: it has no following rule on which to chain.&lt;/p&gt;


	&lt;p&gt;The grammar, in turn, generates the following language:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;a
a b
a b c
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;Thus, given the grammar specification &amp;#8220;a b c&amp;#8221;, my code had to produce
a regular expression that would match &amp;#8220;a&amp;#8221;, &amp;#8220;a b&amp;#8221;, or &amp;#8220;a b c&amp;#8221;.&lt;/p&gt;


	&lt;p&gt;At this point, please stop for a moment and think about this little
programming exercise.  Do any solutions leap to mind?  How would you
approach the problem?  Form your opinions now, because I&amp;#8217;m going to
ask you about them later. (If you&amp;#8217;re feeling especially caffeinated, try
coding a solution before reading on.)&lt;/p&gt;&lt;p&gt;For me, the insight that made the exercise easy was seeing that the
grammar is given by folding a (suitably defined) right-associative
binary operator through the series of atoms.  The relationship might
be easier to see if you substitute away the intermediate production
rules from the grammar above:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;grammar ::= "a" (SPACE "b" (SPACE "c" | NOTHING) | NOTHING)
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;If you squint past the &lt;span class="caps"&gt;SPACE&lt;/span&gt; and &lt;span class="caps"&gt;NOTHING&lt;/span&gt; terms, you&amp;#8217;ll see that the
grammar has the form&lt;/p&gt;


&lt;p&gt;(&lt;em&gt;a&lt;/em&gt; + (&lt;em&gt;b&lt;/em&gt; + (&lt;em&gt;c&lt;/em&gt;)))&lt;/p&gt;

	&lt;p&gt;The + is a binary operator that generates the parts we squinted away.
Once you see what&amp;#8217;s going on structurally, the operator is easy to
define:&lt;/p&gt;


&lt;p&gt;&lt;em&gt;x&lt;/em&gt; + &lt;em&gt;y&lt;/em&gt;  =  &lt;em&gt;x&lt;/em&gt; (SPACE &lt;em&gt;y&lt;/em&gt; | &lt;span class="caps"&gt;NOTHING&lt;/span&gt;)&lt;/p&gt;

&lt;p&gt;Compare the operator&amp;#8217;s definition with that of the
&lt;code&gt;atom_rule&lt;/code&gt; I presented at the beginning of the article.
They&amp;#8217;re structurally the same: the operator&amp;#8217;s &lt;em&gt;x&lt;/em&gt; and
&lt;em&gt;y&lt;/em&gt; are the atom rule&amp;#8217;s &lt;code&gt;&amp;lt;the literal atom&amp;gt;&lt;/code&gt; and
&lt;code&gt;&amp;lt;the next rule&amp;gt;&lt;/code&gt;.&lt;/p&gt;

	&lt;p&gt;Now all that remains is to generalize the &amp;#8220;a b c&amp;#8221; formula into a
general formula that works for arbitrary grammar specifications.
Fortunately, this work has already been done for us.  The generalized
formula is nothing more than a &lt;a href="http://www.haskell.org/haskellwiki/Fold"&gt;right fold&lt;/a&gt;.  In Haskell, the
particular right-fold flavor we want is called &lt;em&gt;foldr1&lt;/em&gt;.&lt;/p&gt;


	&lt;p&gt;Given a list of atoms, we can use &lt;em&gt;foldr1&lt;/em&gt; to construct its grammar as
follows:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;mk_grammar atoms = foldr1 (+) atoms
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;But Python, our implementation language, does not offer a &lt;em&gt;foldr1&lt;/em&gt;
function.  This wrinkle, however, is another thing we can iron out by
thinking algebraically.  Python doesn&amp;#8217;t have &lt;em&gt;foldr1&lt;/em&gt;, but it does
have a &lt;em&gt;reduce&lt;/em&gt; function, which represents a left fold, equivalent to
Haskell&amp;#8217;s &lt;em&gt;foldl&amp;#8217;&lt;/em&gt; or &lt;em&gt;foldl1&amp;#8217;&lt;/em&gt;.  Because our + operator is strict and
our list of atoms is finite, we can take advantage of the following
identity:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;foldr1 (+) xs == foldl1 (flip (+)) (reverse xs)
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;That is, we can convert a right fold into a left fold by flipping the
arguments of the operator and operating on the list in reverse.  Thus
we can implement the fold we &lt;em&gt;want&lt;/em&gt; in terms of the fold we &lt;em&gt;have&lt;/em&gt;:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;# Python code

def foldr1(f, xs):
    return reduce(flip(f), reversed(xs))

def flip(f):
    def g(x, y):
        return f(y, x)
    return g
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;Now writing a Python-based solution is straightforward:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;def grammar_spec_to_re(spec):
    atoms = grammar_spec_to_atoms(spec)
    atom_res = map(atom_to_re, atoms)
    grammar_re = r'\A%s\Z' % foldr1(op, atom_res)
    return grammar_re

def op(x, y):
    # x + y = x (SPACE y | NOTHING)
    return r'%s(\s+%s)?' % (x, y)

def grammar_spec_to_atoms(spec):
    return spec.split()

def atom_to_re(atom):
    return re.escape(atom)
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;Using our solution, let&amp;#8217;s compile the &amp;#8220;a b c&amp;#8221; grammar specification
into its corresponding regular expression:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; print grammar_spec_to_re('a b c')
\Aa(\s+b(\s+c)?)?\Z
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;And that&amp;#8217;s basically how I solved the problem.&lt;/p&gt;


	&lt;p&gt;To play around with the solution, here&amp;#8217;s a small helper class that compiles a grammar specification into a regular expression and then tests strings for matching the regexp:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;class GrammarMatcher(object):
    def __init__(self, spec):
        self.re = re.compile(grammar_spec_to_re(spec))
    def __call__(self, s):
        return not not self.re.match(s)
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;Now, let&amp;#8217;s try out the regular expression generated for the grammar specification &amp;#8220;a b c&amp;#8221; :&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; matcher = GrammarMatcher('a b c')
&amp;gt;&amp;gt;&amp;gt; matcher('')
False
&amp;gt;&amp;gt;&amp;gt; matcher('a')
True
&amp;gt;&amp;gt;&amp;gt; matcher('ab')
False
&amp;gt;&amp;gt;&amp;gt; matcher('a b')
True
&amp;gt;&amp;gt;&amp;gt; matcher('a  b')
True
&amp;gt;&amp;gt;&amp;gt; matcher('a b c')
True
&amp;gt;&amp;gt;&amp;gt; matcher('a b c d')
False
&amp;gt;&amp;gt;&amp;gt; matcher('a c')
False
&amp;gt;&amp;gt;&amp;gt; matcher('b')
False
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;Now, those questions I promised.  If you&amp;#8217;re a functional programmer,
did a fold-based solution leap out at you?  (Did you think of the
&lt;em&gt;problem&lt;/em&gt; in terms of folds?)  If you&amp;#8217;re not a functional programmer,
how did you see the problem?  Did the solution above seem twisted,
confusing, or overly clever?&lt;/p&gt;


	&lt;p&gt;(There are no right or wrong answers.  I&amp;#8217;m just curious about how
people with different backgrounds view the problem.)&lt;/p&gt;


&lt;div class="update"&gt;
&lt;strong&gt;Update:&lt;/strong&gt; Edited to clarify that the problem is to convert a grammar specification into a regular expression, not just test whether a string matches a specified grammar.
&lt;/div&gt;</description>
      <pubDate>Wed, 20 Aug 2008 21:50:00 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:a80d7a49-0278-48b0-badb-ffe38a216762</guid>
      <author>Tom Moertel</author>
      <link>http://blog.moertel.com/articles/2008/08/20/thinking-algebraically-a-functional-programming-dividend-that-pays-during-your-imperative-programming-day-job</link>
      <category>functional programming</category>
      <category>haskell</category>
      <category>fp</category>
      <category>python</category>
      <category>folds</category>
      <trackback:ping>http://blog.moertel.com/articles/trackback/766</trackback:ping>
    </item>
    <item>
      <title>Property checking with Python's nose testing framework</title>
      <description>&lt;p&gt;At work recently I was writing some tests with Python&amp;#8217;s out-of-the-box
unit-testing framework
&lt;a href="http://docs.python.org/lib/module-unittest.html"&gt;unittest&lt;/a&gt;.  I&amp;#8217;m new
to Python and accustomed to Perl and Haskell&amp;#8217;s testing frameworks,
which are lightweight and let you write tests without much
hoop-jumping.  In particular,
&lt;a href="http://www.cs.chalmers.se/~rjmh/QuickCheck/"&gt;QuickCheck&lt;/a&gt; and
&lt;a href="http://search.cpan.org/dist/Test-LectroTest/"&gt;LectroTest&lt;/a&gt; make it easy
to test at the property level instead of the test-case level.
With unittest, I was having to write a lot of code
to get the same level of abstraction.&lt;/p&gt;


	&lt;p&gt;By &amp;#8220;property level,&amp;#8221; here&amp;#8217;s what I mean.  Say I&amp;#8217;m testing this thing,
let&amp;#8217;s call it a &lt;em&gt;subscriber pool&lt;/em&gt;.  It has two fundamental properties:&lt;/p&gt;


	&lt;ol&gt;
	&lt;li&gt;&lt;strong&gt;Subscribe.&lt;/strong&gt; For all initial states of the pool, if you call &lt;em&gt;subscribe&lt;/em&gt;(&lt;em&gt;user&lt;/em&gt;), then, assuming there have been no other operations on the pool, &lt;em&gt;user&lt;/em&gt; must be in the pool.&lt;/li&gt;
		&lt;li&gt;&lt;strong&gt;Unsubscribe.&lt;/strong&gt; For all initial states of the pool, if you call &lt;em&gt;unsubscribe&lt;/em&gt;(&lt;em&gt;user&lt;/em&gt;), then, assuming there have been no other operations on the pool, &lt;em&gt;user&lt;/em&gt; must not be in the pool.&lt;/li&gt;
	&lt;/ol&gt;


	&lt;p&gt;That&amp;#8217;s it.  If my implementation satisfies both properties, it&amp;#8217;s
correct.  (This is a simplified version of my real testing problem,
which required additional property checks.)&lt;/p&gt;


	&lt;p&gt;To test whether my implementation satisfies each property, I must
write individual test cases that together &amp;#8220;cover&amp;#8221; the property.  For
example, to test whether the Subscribe property holds, I might write
four test cases:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;class SubscribeProperty(unittest.TestCase):

    def setUp(self):
        initialize_pool()

    def tearDown(self):
        destroy_pool()

    def testEmpty(self):
        load_pool_with_members([])
        subscribe("1")
        self.assert_("1" in pool_members())

    def testOtherGuyAlreadyInPool(self):
        load_pool_with_members(["2"])
        subscribe("1")
        self.assert_("1" in pool_members())

    def testSubscriberAlreadyInPool(self):
        load_pool_with_members(["1"])
        subscribe("1")
        self.assert_("1" in pool_members())

    def testSubscriberAndOtherGuyAlreadyInPool(self):
        load_pool_with_members(["1", "2"])
        subscribe("1")
        self.assert_("1" in pool_members())
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;Every one of the test cases has the same form.  The repetition
makes me want to refactor the whole thing.&lt;/p&gt;


	&lt;p&gt;Okay, let&amp;#8217;s do it:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;INITIAL_POOL_STATES = [[], ["2"], ["1"], ["1", "2"]]

class SubscribeProperty(unittest.TestCase):

    def setUp(self):
        initialize_pool()

    def tearDown(self):
        destroy_pool()

    def testSubscribe(self):
        for case in INITIAL_POOL_STATES:
            self.setUp()
            try:
                load_pool_with_members(case)
                subscribe("1")
                self.assert_("1" in pool_members(), case)
            finally:
                self.tearDown()
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;We&amp;#8217;re fighting a bit with the testing framework because our notion of
when set-up and tear-down should occur doesn&amp;#8217;t match its own, but
otherwise our code is looking much more manageable.  In particular, if
we want to extend our property-check coverage with additional initial pool
states, we don&amp;#8217;t need to write additional tests; instead, we can just
extend a single list.&lt;/p&gt;


	&lt;p&gt;But we&amp;#8217;re only halfway done.  We must also check the Unsubscribe
property.  The code for it is virtually the same as for Subscribe, but
with &lt;em&gt;subscribe&lt;/em&gt; becoming &lt;em&gt;unsubscribe&lt;/em&gt; and &lt;em&gt;in&lt;/em&gt; becoming &lt;em&gt;not in&lt;/em&gt;.
Let&amp;#8217;s add it to our class:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;class SubscriberPoolProperties(unittest.TestCase):

    def setUp(self):
        initialize_pool()

    def tearDown(self):
        destroy_pool()

    def testSubscribe(self):
        for case in INITIAL_POOL_STATES:
            self.setUp()
            try:
                load_pool_with_members(case)
                subscribe("1")
                self.assert_("1" in pool_members(), case)
            finally:
                self.tearDown()

    def testUnsubscribe(self):
        for case in INITIAL_POOL_STATES:
            self.setUp()
            try:
                load_pool_with_members(case)
                unsubscribe("1")
                self.assert_("1" not in pool_members(), case)
            finally:
                self.tearDown()
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;And now let&amp;#8217;s factor out the new redundancy:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;class SubscriberPoolProperties(unittest.TestCase):

    def setUp(self):
        initialize_pool()

    def tearDown(self):
        destroy_pool()

    def testSubscribe(self):
        def testfn(case):
            subscribe("1")
            self.assert_("1" in pool_members(), case)
        self._forall_test_cases(testfn)

    def testUnsubscribe(self):
        def testfn(case):
            unsubscribe("1")
            self.assert_("1" not in pool_members(), case)
        self._forall_test_cases(testfn)

    def _forall_test_cases(self, testfn):
        for case in INITIAL_POOL_STATES:
            self.setUp()
            try:
                load_pool_with_members(case)
                testfn(case)
            finally:
                self.tearDown()
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;It&amp;#8217;s not bad, but it&amp;#8217;s not great either.  There&amp;#8217;s still a
lot of noise in that code.&lt;/p&gt;


	&lt;p&gt;After discussing the situation with my more-Pythonic
colleague &lt;a href="http://apipes.blogspot.com/"&gt;Tim Lesher&lt;/a&gt;, I took his
advice to check out the &lt;a href="http://somethingaboutorange.com/mrl/projects/nose/"&gt;nose testing framework&lt;/a&gt;.&lt;/p&gt;


	&lt;p&gt;One of the things I liked right away about nose was that it supports
&lt;a href="http://somethingaboutorange.com/mrl/projects/nose/#test-generators"&gt;test
generators&lt;/a&gt;,
which would let me represent each property-check as a generator that
yields the test cases needed to check the property.  Also, set-up and
tear-down would automatically occur per &lt;em&gt;generated&lt;/em&gt; test, so I
wouldn&amp;#8217;t have to invoke them manually.&lt;/p&gt;


	&lt;p&gt;Once I got familiar with nose, it was easy to create a decorator to
represent the forall-test-cases idiom:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;def forall_cases(cases):
    def decorate(testfn):
        def gen():
            for case in cases:
                yield testfn, case
        gen.__name__ = "test_%s_for_a_case" % testfn.__name__
        return gen
    return decorate
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;Note that this decorator is not specific to our subscriber-pool tests.
It can be used in any situation where we need to check a property
across a collection of cases.  In fact, I keep this little gem in a
&amp;#8220;nosehelpers&amp;#8221; library, where I reuse it all the time.  Here&amp;#8217;s an
example of how to use it to check the trivial property that
&lt;em&gt;x&lt;/em&gt;&amp;#160;=&amp;#160;&lt;em&gt;x&lt;/em&gt; for all &lt;em&gt;x&lt;/em&gt; in 0&amp;#8211;99:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;@forall_cases(range(100))
def check_self_equality(x):
    assert x == x
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;Now, back to our testing problem.  Here&amp;#8217;s how we can use the
decorator to check the Subscribe property:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;@forall_cases(INITIAL_POOL_STATES)
@with_setup(initialize_pool, destroy_pool)
def check_subscribe(case):
    load_pool_with_members(case)
    subscribe("1")
    assert "1" in pool_members()
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;(The &lt;em&gt;with_setup&lt;/em&gt; decorator is defined by nose and tells nose to run
the given set-up and tear-down functions before and after each of the
generated test cases.)&lt;/p&gt;


	&lt;p&gt;Not bad.  The only problem I have with that code is that it mixes the
&amp;#8220;For all initial states of the pool&amp;#8221; part of the property definition
into the &amp;#8220;if you call &lt;em&gt;subscribe&lt;/em&gt;(&lt;em&gt;user&lt;/em&gt;), then &amp;#8230;&amp;#8221; part.  I&amp;#8217;d like
the code to be more explicit about which part defines the scope of the
property claim and which part defines the test for whether the claim
holds for any particular test case within that scope.&lt;/p&gt;


	&lt;p&gt;Fortunately, we can build upon our existing decorator to create
exactly what we need:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;def forall_initial_pools(testfn):
    @forall_cases(INITIAL_POOL_STATES)
    @with_setup(initialize_pool, destroy_pool)
    def setup_case_and_test_it(case):
        load_pool_with_members(case)
        testfn(case)
    setup_case_and_test_it.__name__ = \
        "test_%s_for_a_subscriber_case" % testfn.__name__
    return setup_case_and_test_it
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;Here&amp;#8217;s what the decorator does.  When you apply it to a test function
&lt;em&gt;testfn&lt;/em&gt;, it returns a test generator that yields a property-check
test for each of the initial pool states.  For each, it sets up a new
pool, loads it with the initial subscribers (as given by the
corresponding test case), runs the given check function &lt;em&gt;testfn&lt;/em&gt;, and
then cleans up after itself.&lt;/p&gt;


	&lt;p&gt;With this decorator, our Pythonic property definitions now mirror
the human-language definitions from the start of the article:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;@forall_initial_pools
def check_subscribe(case):
    subscribe("1")
    assert "1" in pool_members()

@forall_initial_pools
def check_unsubscribe(case):
    unsubscribe("1")
    assert "1" not in pool_members()
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;And that&amp;#8217;s pretty much the solution I ended up using at work.  There,
as opposed to here, I got to reuse my decorators for many more tests,
making them all the more worth their small implementation price.&lt;/p&gt;</description>
      <pubDate>Wed, 19 Mar 2008 22:34:00 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:48416d65-7ef2-4453-9adc-6ee13f8e85b3</guid>
      <author>Tom Moertel</author>
      <link>http://blog.moertel.com/articles/2008/03/19/property-checking-with-pythons-nose-testing-framework</link>
      <category>testing</category>
      <category>testing</category>
      <category>python</category>
      <category>nose</category>
      <category>unittest</category>
      <category>properties</category>
      <trackback:ping>http://blog.moertel.com/articles/trackback/707</trackback:ping>
    </item>
    <item>
      <title>How to download photos and movies from the Palm Centro to a Linux desktop</title>
      <description>&lt;p&gt;I recently got a Palm Centro smartphone, and so far I love it. Like
most modern cell phones, it has a built-in camera and takes decent
snapshots and even records short movies. It&amp;#8217;s great for
spur-of-the-moment shots when I don&amp;#8217;t have my real camera.  The
trick &amp;#8211; and there&amp;#8217;s always a trick when it comes to cell phones &amp;#8211; is getting
the photos off the camera and onto my computer.&lt;/p&gt;


	&lt;p&gt;To get at my pictures, Sprint would prefer that I sign up for their
ludicrously expensive &amp;#8220;PictureMail&amp;#8221; service.  Leave it to weasely
telecom execs to come up with another way to squeeze money from
teenagers: charge them $5 each month for the &amp;#8220;privilege&amp;#8221; of sharing
their pictures with friends.  This fee, of course, is in addition to the
fee for &amp;#8220;unlimited&amp;#8221; mobile Internet use.  I guess picture bits are
somehow more expensive to move over the air than other kinds of bits.&lt;/p&gt;


	&lt;p&gt;In any case, my next goal after
getting my &lt;a href="http://blog.moertel.com/articles/2007/10/31/how-to-hotsync-the-palm-centro-with-a-fedora-7-linux-desktop-via-usb"&gt;Centro to hotsync with my Linux
workstation&lt;/a&gt; was to figure out how
to download my photos and movies.&lt;/p&gt;


	&lt;p&gt;After a bit of hacking, I figured out that the Centro stores images in
a typical digital-camera-image (DCIM) hierarchy.  For
example, I have a 4-GB microSD card installed in my Centro, and I
store my photos in the &amp;#8220;Palm&amp;#8221; album on it.  This album ends up stored
in the /DCIM/Palm directory on the card.&lt;/p&gt;


	&lt;p&gt;Using the pilot-xfer program
from the &lt;a href="http://www.pilot-link.org/"&gt;pilot-link&lt;/a&gt; project, I was able
to find the directory and its contents.  The trick was to use the
sparsely documented &amp;#8211;D flag to work with the Centro&amp;#8217;s virtual
filesystem.  Here, for example, is how I list the contents of the Palm album:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;$ pilot-xfer -p usb: -D /DCIM/Palm -l

   Listening for incoming connection on usb:... connected!

   Directory of /DCIM/Palm...
        652 Fri Nov  2 08:17:06 2007  Album.db
     292053 Fri Nov  2 09:04:20 2007  Photo_110207_001.jpg
      78493 Fri Nov  2 08:17:06 2007  Video_110207_001.3g2
         20 Wed Oct 31 12:09:20 2007  Thumbnail.db

   Thank you for using pilot-link.
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;Here, you can see that I have one photo and one movie in the album.
(Movies are stored in .3g2 files that contain &lt;span class="caps"&gt;MPEG4&lt;/span&gt; video.)&lt;/p&gt;


	&lt;p&gt;To download the files, I again turned to pilot-xfer, this time using the
&amp;#8211;f (fetch) flag to fetch a list of files.
Here, for example, I&amp;#8217;ll fetch the image from the listing above:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;$ pilot-xfer -p usb: -D /DCIM/Palm -f Photo_110207_001.jpg

   Listening for incoming connection on usb:... connected!

   Fetching '/DCIM/Palm' ... (292053 bytes)   285 KiB total.

   Thank you for using pilot-link.
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;So that&amp;#8217;s the process.  It&amp;#8217;s kind of clunky, so I wrote a small Python
program to automate it.  (I&amp;#8217;m learning Python.  If you&amp;#8217;re a Pythonista, please
consider critiquing my code. I would be especially thankful if you
could point out any helpful idioms that I may have overlooked.)&lt;/p&gt;


	&lt;p&gt;Here&amp;#8217;s how to use the program:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;$ get-pilot-photos.py --help
Usage: get-pilot-photos.py [options]

Options:
  -h, --help            show this help message and exit
  -s SRCDIR, --srcdir=SRCDIR
                        VFS dir on Palm device from which to fetch images
  -d DESTDIR, --destdir=DESTDIR
                        Where to save the images on your computer
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;Both the &amp;#8212;srcdir and &amp;#8212;dstdir options are optional.  If you
omit the first, the program will download photos and movies from the
/DCIM/Palm album.  If you omit the second, the program will save the
downloads to a new, timestamped directory within your home directory.&lt;/p&gt;


	&lt;p&gt;That&amp;#8217;s it.  The code is below.&lt;/p&gt;&lt;pre&gt;&lt;code style="font-size: smaller"&gt;#!/usr/bin/env python

# get-pilot-photos.py -
# Download photos and movies from my Palm Centro via pilot-link
#
# Tom Moertel &amp;lt;tom@moertel.com&amp;gt;
# 2007-11-01
#
# Copyright 2007 Thomas G. Moertel
#
# This program is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of
# the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# See &amp;lt;http://www.gnu.org/licenses/&amp;gt; for more.

import os
import optparse
import re
import subprocess
import time

PILOT_XFER = 'pilot-xfer'
DEFAULT_PALM_IMAGE_DIR = '/DCIM/Palm'

class PhotoImporter(object):

    def __init__(self, src_dir, dest_dir=None):
        self.src_dir = src_dir
        self.dest_dir = dest_dir or self.get_image_dir()

    def run(self):
        print 'Finding images in %s on your Palm device.' % self.src_dir
        print 'Begin hotsync now...'
        images = self.get_image_list()
        if len(images) == 0:
            print 'No images were found.  Done.'
            return
        print 'Found %s images' % len(images)
        print 'Waiting for hotsync to complete...'
        time.sleep(10) # give 1st hotsync time to complete
        print 'Begin another hotsync now...'
        self.fetch_images(images)
        print 'Done.  The images were fetched to the following directory:'
        print self.dest_dir

    def get_image_list(self):
        cmdline = [PILOT_XFER, '-p',  'usb:', '-D', self.src_dir, '-l']
        proc = subprocess.Popen(cmdline, stdout=subprocess.PIPE)
        listing = proc.stdout.read()
        proc.wait()
        return re.findall(r'\b\S+\.(?:jpg|3g2)\b', listing)

    def fetch_images(self, images):
        cmdline = [PILOT_XFER, '-p',  'usb:', '-D', self.src_dir, '-f'] + images
        subprocess.Popen(cmdline, cwd=self.dest_dir).wait()

    def get_image_dir(self):
        root = os.getenv('HOME') or tempfile.mkdtemp()
        now = time.strftime("%Y-%m-%d--%H.%M.%S", time.localtime())
        dir = os.path.join(root, 'images', 'unsorted-pix', now)
        os.makedirs(dir, mode=0771)
        return dir

def main():
    p = optparse.OptionParser()
    p.add_option('--srcdir', '-s', default=DEFAULT_PALM_IMAGE_DIR,
                 help='VFS dir on Palm device from which to fetch images')
    p.add_option('--destdir', '-d',
                 help='Where to save the images on your computer')
    opts, args = p.parse_args()
    PhotoImporter(opts.srcdir, opts.destdir).run()

if __name__ == '__main__':
    main()
&lt;/code&gt;&lt;/pre&gt;</description>
      <pubDate>Fri, 02 Nov 2007 15:32:00 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:92fcb359-e7e8-41dc-a5b9-86caffcde1f5</guid>
      <author>Tom Moertel</author>
      <link>http://blog.moertel.com/articles/2007/11/02/how-to-download-photos-and-movies-from-the-palm-centro-to-a-linux-desktop</link>
      <category>hacks</category>
      <category>movies</category>
      <category>palm</category>
      <category>centro</category>
      <category>hotsync</category>
      <category>images</category>
      <category>download</category>
      <category>python</category>
      <trackback:ping>http://blog.moertel.com/articles/trackback/616</trackback:ping>
    </item>
  </channel>
</rss>
