Decorators not worth the effort

Jean-Michel Pichavant jeanmichel at sequans.com
Fri Sep 14 05:28:22 EDT 2012


----- Original Message -----
> On Sep 14, 3:54 am, Jean-Michel Pichavant <jeanmic... at sequans.com>
> wrote:
> > I don't like decorators, I think they're not worth the mental
> > effort.
> 
> Because passing a function to a function is a huge cognitive burden?
> --
> http://mail.python.org/mailman/listinfo/python-list
> 

I was expecting that. Decorators are very popular so I kinda already know that the fault is mine. Now to the reason why I have troubles writing them, I don't know. Every time I did use decorators, I spent way too much time writing it (and debugging it).

I wrote the following one, used to decorate any function that access an equipment, it raises an exception when the timeout expires. The timeout is adapted to the platform, ASIC of FPGA so people don't need to specify everytime one timeout per platform.

In the end it would replace 

def boot(self, timeout=15):
    if FPGA:
        self.sendCmd("bootMe", timeout=timeout*3)
    else:
        self.sendCmd("bootMe", timeout=timeout)

with

@timeout(15)
def boot(self, timeout=None):
    self.sendCmd("bootMe", timeout)

I wrote a nice documentation with sphinx to explain this, how to use it, how it can improve code. After spending hours on the decorator + doc, feedback from my colleagues : What the F... !! 

Decorators are very python specific (probably exists in any dynamic language though, I don't know), in some environment where people need to switch from C to python everyday, decorators add python magic that not everyone is familiar with. For example everyone in the team is able to understand and debug the undecorated version of the above boot method. I'm the only one capable of reading the decorated version. And don't flame my colleagues, they're amazing people (just in case they're reading this :p) who are not python developers, more of users.

Hence my original "decorators are not worth the mental effort". Context specific I must admit.

Cheers,

JM

PS : Here's the decorator, just to give you an idea about how it looks. Small piece of code, but took me more than 2 hours to write it. I removed some sensible parts so I don't expect it to run.

class timeout(object):
        """Substitute the timeout keyword argument with the appropriate value"""
	FACTORS = {
		IcebergConfig().platform.ASIC : 1,
		IcebergConfig().platform.FPGA : 3,
			}

	def __init__(self, asic, fpga=None, palladium=None):
		self.default = asic
		self.fpga = fpga
	
	def _getTimeout(self):
		platform = config().platform
		factor = self.FACTORS[platform.value]
		timeout = {
				platform.ASIC : self.default*factor,
				platform.FPGA : self.fpga or self.default*factor,
				}[platform.value]
		return timeout

	def __call__(self, func):
		def decorated(*args, **kwargs):
			names, _, _, defaults =  inspect.getargspec(func)
			defaults = defaults or []
			if 'timeout' not in names:
				raise ValueError('A "timeout" keyword argument is required')
			if 'timeout' not in kwargs: # means the timeout keyword arg is not in the call
				index = names.index('timeout')
				argsLength = (len(names) - len(defaults))
				if index < argsLength:
					raise NotImplementedError('This decorator does not support non keyword "timeout" argument')
				if index > len(args)-1: # means the timeout has not be passed using a pos argument
					timeoutDef = defaults[index-argsLength]
					if timeoutDef is not None:
						_log.warning("Decorating a function with a default timeout value <> None")
					kwargs['timeout'] = self._getTimeout()
			else:
				_log.warning('Timeout value specified during the call, please check "%s" @timeout decorator.' % func.__name__)
			ret = func(*args, **kwargs)
			return ret
		return decorated



More information about the Python-list mailing list