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

  • We Won The One Award I Actually Care About

    Full Scale made the Inc. 5000 for the fifth year straight, the 12th listing across my three companies. Here is why the one award you cannot buy is worth stopping for.

  • Your Customers Build the Features Now

    I let a tool I liked sit dead for a year rather than build the features I wanted. An MCP server meant I never had to, and your customers can do the same to your product.

  • Get the Size of a Directory in Linux the Easy Way

    du -sh for the quick answer, ncdu for the cleanup, df for the disk itself: every command for checking directory size in Linux, plus why du and df never agree.

  • Vim Search and Replace: The Ultimate Guide

    One :%s command replaces every match in a file before a find dialog would even open. The Vim substitute patterns worth the muscle memory: flags, ranges, capture groups, and multi-file edits.