By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
443,923 Members | 1,279 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 443,923 IT Pros & Developers. It's quick & easy.

Pre/Postconditions with decorators

P: n/a
My humble attempt to model pre/postconditions with
decorators :-) It's also my first experiment
with decorators.
If you have any ideas or thoughts on how to
improve the code snippet, I'll be happy to
learn more.

Enjoy :-)

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

# function -> decorated(function)
def decorate_condition(function):
# FIXME: Does not work with wrapped functions
argcount=function.func_code.co_argcount
var=function.func_code.co_varnames[0:argcount]

# arguments -> closure(assertion)
def evaluate_condition(*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)]
# key-arguments to value
kargs_seq=[(k,v) for k,v in kargs.itervalues()]
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 evaluate_condition

return decorate_condition

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

@condition("list(seq) is not None","sum(seq)==result")
def my_sum(seq):
tmp=0
for element in seq:
tmp+=element
return tmp

print sqrt(1.2)
print my_sum([1,2,3])
Jul 18 '05 #1
Share this Question
Share on Google+
7 Replies


P: n/a
On 6 Jan 2005 13:33:42 -0800, Rittersporn <ri*********@gmail.com> wrote:
@condition("number>0 and number<2","result>=0")
def sqrt(number):
import math
return math.sqrt(number)

@condition("list(seq) is not None","sum(seq)==result")
def my_sum(seq):
tmp=0
for element in seq:
tmp+=element
return tmp

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


I think it would be nicer to have the pre and post conditions being compilable.

@condition((list(seq) is not None for seq in args), (sum(seq)==result
for ((seq,), result) in (args, result))

or something silly like that.

Personally, I'd prefer this:
@precondition(list(seq) is not None for seq in args)
@postcondition(sum(seq)==result for ((seq,), result) in (args, result))

(this is, of course, using the framehack lambda replacement presented
in a thread about a week ago).

Stephen.
Stephen.
Jul 18 '05 #2

P: n/a
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")

Jul 18 '05 #3

P: n/a
> 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... ;-)


I posted my module on http://rafb.net/paste/results/voZYTG78.html and its unit test on
http://rafb.net/paste/results/MYxMQW95.html. Any feedback will be appreciated.

George
Jul 18 '05 #4

P: n/a
Thank you very much. It is really a very elegant piece of code :-)

Eiffel (language) has both type checking and design by contract.
Python lacks both.
Your module tackles type checking, I
tried to execute arbitrary code before
and after function execution to realize
runtime assertions.

I wonder if this should be separated in a Python
"checker"-module.
Both are part of the interface "contract".

Ciao

Jul 18 '05 #5

P: n/a
Eiffel (language) has both type checking and design by contract.
Python lacks both.


Actually, Python is strongly typed. It's just dynamically instead of
statically typed.

Skip
Jul 18 '05 #6

P: n/a
On Fri, 7 Jan 2005 20:01:50 +0200, George Sakkis <gs*****@rutgers.edu> wrote:
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... ;-)


I posted my module on http://rafb.net/paste/results/voZYTG78.html and its unit test on
http://rafb.net/paste/results/MYxMQW95.html. Any feedback will be appreciated.


Okay, nice.

Unresolved Problems:
1) How do you handle duck types, i.e. a method that accepts StringIO,
cStringIO or any other object that has a .readlines(), .seek() and
..read() method?
2) How do you turn off the type checking for production code?

Otherwise I quite like it. Please publish it somewhere.

Stephen.
Jul 18 '05 #7

P: n/a
Stephen Thorne <st************@gmail.com> writes:
Unresolved Problems:
1) How do you handle duck types, i.e. a method that accepts StringIO,
cStringIO or any other object that has a .readlines(), .seek() and
.read() method?
That should really be done through having those classes inherit a
file-operations mixin, or else through interfaces (which might get
added to Python).
2) How do you turn off the type checking for production code?


It should be left on. Leaving it in for development and turning it
off for production is like wearing a parachute during ground training
and taking it off once you're in the air.
Jul 18 '05 #8

This discussion thread is closed

Replies have been disabled for this discussion.