The Occasional Occurence

Getting Version Information from Mercurial

May 13, 2010 at 04:52 PM | categories: Python, work, computing, Software

In an application I'm working on at work I want to be able to display various bits of version information in the UI. This goes for both production deployments from Python EGG files and in development running straight out of the repository.

We use Mercurial for revision control so it is a logical choice for a version information source. The result of some hacking is my first Mercurial extension and commit hook.

Mercurial Version Info Plugin.

See the README file rendered at that link for the details. Click here to download version 1.0.

cw

My Take on Multiple Constructors

March 17, 2010 at 05:38 PM | categories: Python, Software, computing, General

I noticed the same question on c.l.p that Steve Ferg responded to on his blog. I was feeling too lazy to respond to the thread earlier but I thought I'd throw my idea up on the ol' blog before wrapping up for the day.

I think this is a classic use-case for class methods. Here is my implementation.

class Vector(object):
    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z

    @classmethod
    def from_sequence(cls, sequence):
        return cls(*sequence)

    @classmethod
    def from_vector(cls, vec):
        return cls(vec.x, vec.y, vec.z)

    def __repr__(self):
        return "Vector(%s, %s, %s)" % (self.x, self.y, self.z)

if __name__ == '__main__':
    print Vector(1,2,3)
    print Vector.from_sequence([4,5,6])
    print Vector.from_sequence((7,8,9))
    v = Vector(10, 11, 12)
    print Vector.from_vector(v);

Here is the output from running the script:

Vector(1, 2, 3)
Vector(4, 5, 6)
Vector(7, 8, 9)
Vector(10, 11, 12)

I like the classmethod route because it is obvious what the code is doing, it makes it easy to add new from_\* methods and keeps the general __init__ method clean.

cw

That Sucks

March 17, 2010 at 12:25 AM | categories: stories, Family, General

Spent the morning working from a coffee shop. I was feeling hip and fresh.

I came home and was going to whip up a sammich for lunch and then get back to work.

Opened the bread to make the sammich and then had to go and bring the puppy back in the house. Eli had "accidentally" let him out. Oh well. I bring the dog in and return to the kitchen.

I got the ham and swiss laid out on the bread. Then I get called back out because Eli let the dog out again. Bring the dog and Eli in this time.

Back to making the sammich. It was taking longer than planned (something like 8 minutes had elapsed) but it was gonna be gooooood. I proceeded to drop some succulent bean sprouts onto my delicious lunch creation.

Panicked shouts from the living room.

"Curtis and Eli are peeing in the front yard!"

This just won't do.

I go and bring the boys in and deliver a fatherly lecture on the right and wrong times and places to pee outside. I send them to their room to clean it up. Super dad.

Super hungry dad.

I get back to the kitchen to finish making the sammich.

The dog ate the sammich.

That Sucks.

cw

Tagged Revision Log Messages in Mercurial

March 12, 2010 at 01:49 PM | categories: work, Software, computing, General

I work at YouGov and we use the Mercurial for revision control.

We tag every release in a MAJOR.MINOR.BUGFIX format. 2.32.2 for example.

Recently I wanted to get a summary of the commit log messages for each of the tagged revisions. Here is the magic command that got me the output that I wanted:

# get all log entries for releases tagged with a 2.31 MAJOR.MINOR
hg tags | grep 2\.31 | cut -c33-36 | xargs -L1 hg log -r

cw

Evolution of a Haskell Function

March 11, 2010 at 01:23 PM | categories: Python, Software, computing, General

I'm going through Real World Haskell trying to get a handle on the Haskell programming language. Python is my current language of choice, but I like to learn new programming languages too.

So last night I was going over the chapter that introduces 'let', 'where', 'case' and guards and I wanted to try them out. I contrived a simple situation where I thought I could use them.

I made some Contestants and wanted to see if they were "valid" for some definition of the word. I chose that they had to have a real value for age (not Missing) and be 30 years old or older (I'm pretty sure the game is called "Who Wants to Be a 30-year-old Computer Nerd?").

Here is my implementation.

-- my parameterized type
data Perhaps a = Is a
               | Missing
                 deriving (Show)

-- definition of a Contestant
data Contestant = Contestant {
                    name ::  String,
                    age  ::  Perhaps Int
                } deriving (Show)

-- some contestants
contestant1 = Contestant "Christian" (Is 31)
contestant2 = Contestant "Sarah" Missing
contestant3 = Contestant "Curtis" (Is 6)

-- the validation logic
contestantValid :: Contestant -> Bool
contestantValid c =
    case age c of
        Missing      -> False
        Is true_age  -> oldenough true_age
            where
                oldenough a
                    | a >= 30 = True
                    | a     <     30  = False

-- some code to print the results of running the validation against contestants
main = do
    print $ (contestant1, contestantValid contestant1)
    print $ (contestant2, contestantValid contestant2)
    print $ (contestant3, contestantValid contestant3)

I was pretty proud of myself. I used 'case', 'where' and guards (not to mention parameterized types - I reimplemented Maybe/Just/Nothing as Perhaps/Is/Missing).

But man, it seemed like a lot of code to do something simple.

As I was falling asleep I realized how I could simplify it. I could use pattern matching to easily handle the Missing case. It would also let me pull the age out of a Contestant with ease too.

Here is the contestantValid function updated to reflect that.

contestantValid :: Contestant -> Bool
contestantValid (Contestant _ Missing) = False
contestantValid (Contestant _ (Is age))
                                | age >= 30 = True
                                | age     <     30  = False

Quite a bit simpler. I patted myself on the back. It was less fancy than the first version, but at least I had some guards in there.

Then it hit me. I didn't need the guards.

contestantValid :: Contestant -> Bool
contestantValid (Contestant _ Missing) = False
contestantValid (Contestant _ (Is age)) = age >= 30

Well, so much for 'where', 'case' and guards. It sure is a boring function now, but I think the process of how I got there is interesting.

cw

« Previous Page -- Next Page »