Ruby/Rake and Python Decorators

Ruby/Rake and Python Decorators

I had been experimenting with Ruby/. 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.:-)

This article is part of the GWB Archives. Original Author: Vagmi Mudumbai

New on Geeks with Blogs