_Re Pre/Postconditions with decorators

Rittersporn rittersporn at gmail.com
Fri Jan 7 06:09:43 EST 2005


# Google-News won't let be post a follow-up right now!
# Google-Beta-News destroys the formatting :-(
# So I'll start a new thread.

Hi Stephen
I have not read anything about the
"framehack lambda replacement" yet,
but I do compile the pre- and
postconditions. Syntax erros e.g.
will be raised if the module
is compiled. Although I must admit
that your code snippets look more like
compiled code ;-)

Hi Robert
thanks for the link to the Ian Bicking blog.

Hi George,
it would be nice to see how you have tackled
the task.
Maybe we will have a checker
module in Python one day... ;-)

Well, I have attached my latest attempt
to model pre/postconditions (without "framehack
lambda replacement") which does wrap the
original function with a class which delegates
attribute access. Now I can split my
"condition" into pre- and postcondition
and the "tracer" prints the original
function name.
I have also fixed a bug
with keyword arguments.
Major difference compared to
other examples is probably
only that I can refer to function
arguments by name:

class Delegate(object):
	def __init__(self,function):
		self.function=function
	def __getattr__(self,key):
		return getattr(self.function,key)


def condition(pretext,posttext=""):
	precode=compile(pretext or "True","","eval")
	postcode=compile(posttext or "True","","eval")

	# function -> decorated(function)
	def decorate_condition(function):
		argcount=function.func_code.co_argcount
		var=function.func_code.co_varnames[0:argcount]
		class EvalCond(Delegate):
			def __call__(self,*args,**kargs):
				# FIXME: check if "var" always contains ordered list of arguments
				# map arguments and
				args_seq=[(argname,args[pos]) for pos,argname in \
					enumerate(var) if (argname not in kargs)]
				# key-arguments to value
				kargs_seq=[(k,v) for k,v in kargs.iteritems()]
				environment=args_seq+kargs_seq

				# precondition
				assert eval(precode,{},dict(environment)),pretext
				tmp=function(*args,**kargs)
				environment2=environment+[('result',tmp)]

				# postcondition
				assert eval(postcode,{},dict(environment2)),posttext
				return tmp
		return  EvalCond(function)
	return decorate_condition

def trace(function):
	class Trace(Delegate):
		def __call__(self,*args,**kargs):
			print "enter function %s with " % \
				self.function.func_name,args,kargs
			result=self.function(*args,**kargs)
			print "leave function %s with " % \
				self.function.func_name,args,kargs
			return result
	return Trace(function)

def precondition(prgtext):
	return condition(prgtext)

def postcondition(prgtext):
	return condition("",prgtext)


@precondition("number>0 and number<2")
@postcondition("result>=0")
def sqrt(number):
	import math
	return math.sqrt(number)

@trace
@precondition("len(seq)>0 is not None and str(more)")
@postcondition("sum(seq)==result")
def my_sum(seq,more):
	tmp=0
	for element in seq:
		tmp+=element
	return tmp

print sqrt(1.2)
print my_sum([1,2,3],more="more")



More information about the Python-list mailing list