Adventures of a Setup Developer

my musings about setups and other things in my life

  Home  |   Contact  |   Syndication    |   Login
  30 Posts | 1 Stories | 87 Comments | 71 Trackbacks

News

Article Categories

Archives

Post Categories

General

Regular Reads

Tools

Thursday, September 01, 2005 #

I had been experimenting with Ruby/Rake. I took a little while for me to get adjusted to Ruby's syntax. I love closures in Ruby and their effective use in Rake. I was trying if I could do the same in Python. As always, I came up with a very shabby code as the first attempt.

#run target.py
# the global dict to store targets
targets={}
#defining and adding a target
class Target:
    def __init__(self,name='',dependencies=[],fn=None):
        self.name=name
        self.dependencies=dependencies
        self.fn=fn
        self.done=False
        self.success=False

    def run(self):
        if(not self.done):
            self.success=self.fn(self)
            self.done=True
        return self.success

    def describetarget(self):
        print self.fn.__doc__

def addtarget(name,dependencies,callable):
    objtarget=Target(name,dependencies,callable)
    targets[name]=objtarget

# now to add the actual target

def sayHello(target):
    '''Prints hello world with the calling targets' name'''
    print "hello world from " + target.name
    return True
addtarget('sayhello',[],sayHello)

#run the target
targets['sayhello'].run()

As you can clearly see, you would have to first define the function and add it later as the target. For simplicity sake have not written the logic of constructing the dependency tree. You can see that this is nowhere close to writing the same code in Ruby. Consider the following.

Python Code:

def sayHello(target):
    '''Prints hello world with the calling targets' name'''
    print "hello world from " + target.name
    return True
addtarget('sayhello',[],sayHello)

Versus

Rake Code:

task :mytask do |t|
print 'hello world ' + t.name
end

The magic of ruby code is in closures. Python does support closures. Python 2.4 also has a new feature called decorators which can do the same thing. This is what I did in Python using decorators.

#runtargetwithdecorator.py
#global dict to store targets
targets={}

#the target class
class Target:
    def __init__(self,name='',dependencies=[],fn=None):
        self.name=name
        self.dependencies=dependencies
        self.fn=fn
        self.done=False
        self.success=False

    def run(self):
        if(not self.done):
            self.success=self.fn(self)
            self.done=True
        return self.success

#the target decorator function

def target(name,dependencies=[]):
    def decorator(f):
        mytarget=Target(name,dependencies,f)
        targets[name]=mytarget
        return f
    return decorator

#implementing the decorator
@target('mytarget')
def MyFunction(target):
    print "hello world", target.name
    return True


targets['mytarget'].run()

Now consider the following.

Python code:

@target('mytarget')
def MyFunction(target):
   print "hello world", target.name
   return True

versus

Ruby code:

task :mytask do |t|
   print 'hello world ' + t.name
end

Now the python code is a lot more easlier to read than its counterpart in Ruby. Python may not be an effective DSL for build languages but is definitely a lot easier to read and understand than Ruby's code. But you cannot have a empty defaults target defined in python. You should have a valid callable after the @decorator. For example, to my knowledge, you cannot write the following code in python without the following function.

@target('default',['mytarget1','mytarget2'])
def somefn(target):
     pass

But you can write the following code in ruby without any closure method.

task :default => [:mytask1,:mytask2]

In spite of Rake's simplicity, I still would prefer writing Python code. I guess it is just my personal preference. No offence to Rake fans. :-)