ASPN ActiveState Programmer Network
sign in | join ActiveState, a division of Sophos
/ Home / Perl / PHP / Python / Tcl / XSLT /
/ Safari / My ASPN /
Cookbooks | Documentation | Mailing Lists | Modules | News Feeds | Products | User Groups | Web Services
Submit Recipe
My Recipes

All Recipes
All Cookbooks


View by Category

Title: eiffelmethod
Submitter: Andres Tuells (other recipes)
Last Updated: 2001/11/21
Version no: 1.0
Category: OOP

 

3 stars 3 vote(s)


Description:

An eiffel like method, with preconditions and postconditions, in python2.2

Source: Text Source

"""
Implemetation of eiffel like methods (methods with preconditions and postconditions).

eiffelmethod is a new descriptor that implements eiffel like methods. It accepts a method and 
optional pre and post conditions. fI the pre or post conditions are not given it searchs methodName_pre
and methodName_post.
"""

import types 

class eiffelmethod(object):
    def __init__(self, method, pre=None, post = None):
        self._method = method
        self._pre = pre
        self._post = post
    def __get__(self, inst, type=None):
    	result = EiffelMethodWraper(inst,self._method,self._pre, self._post) 
    	setattr(inst, self._method.__name__,result)
        return result

class EiffelMethodWraper:
    def __init__(self, inst, method, pre, post):
        self._inst = inst
        self._method = method
        if not pre:
        	pre = getattr(inst,method.__name__+"_pre",None)
        	if pre: pre = pre.im_func
        self._pre = pre
        if not post:
        	post = getattr(inst,method.__name__+"_post",None)
        	if post: post = post.im_func
        self._post = post
   
    def __call__(self, *args, **kargs):
        if self._pre:
        	apply(self._pre,(self._inst,)+args, kargs)
        result = apply(self._method,(self._inst,)+args, kargs)
        if self._post:
			apply(self._post,(self._inst,result)+args, kargs)
        return result

def _test():
    class C:
        def f(self, arg):
            return arg+1
        def f_pre(self, arg):
            assert arg>0
        def f_post(self, result, arg):
            assert result>arg
        f = eiffelmethod(f,f_pre,f_post)
    c = C()
    c.f(1)
    try:
    	c.f(-1)
    except AssertionError:
    	pass
    else:
    	raise "c.f(-1) bad implemented"
    print "OK"


if __name__=='__main__':
    _test()

Discussion:

A.M. Kuchling introduces descriptors in What's New in Python 2.2. He proposed a new descriptor for eiffel type methods, but he doesn't give an implementation. This implementation is based on GvR Eiffel MetaClass.



Add comment

Number of comments: 3

Inheritance?, Gregor Rayman, 2002/12/17
The problem with this implementation is that it does not consider inheritance. When I override the method f in a subclass, the pre_f and post_f methods will not be called anymore. When I choose a different name for the original and the eiffelized method like here:

class C:
        def _f(self, arg):
            return arg+1
        def _f_pre(self, arg):
            assert arg>0
        def _f_post(self, result, arg):
            assert result>arg
        f = eiffelmethod(f,f_pre,f_post)
and then override the method _f in a subclass:
class D:
        def _f(self, arg):
            return arg * 2

d = D()
then d.f(10) will call the method of class C and return 11 instead of 20.
Add comment

Inheritance works., Not specified Not specified, 2003/01/18
When you override a method in a subclass you're changing what it does. If it does something else, it should have different pre and post conditions.

You're free to eiffelize your new method.

The implementation detail I *do* disagree with is that if you don't specify pre/post explicitly it will getattr the class instance to find it. In that case you can run into funky behavior when subclassing if you take advantage of this lazy feature. For example, if class A method f is eiffelized with a precondition and postcondition, class B(A) method f is eiffelized with just a precondition then the eiffelization of class B(A) method f will find the postcondition for class A method f and assume that it still applies.

I'd probably do it differently myself.. I'd allow either str or callable arguments, have a pre/post condition chain (with each returning self so you could chain them up in one line for convenience), and also allow you to specify what it throws.. This way you could have chains of pre/postconditions:

class NumberIsNotOdd(Exception):
    pass
def even_to_odd(even):
    return even + 1
even_to_odd = eiffelize(odd)
even_to_odd.preCondition('int(even)').preCondition(lambda even:not (even & 1))
even_to_odd.postCondition(lambda rval:rval & 1, NumberIsNotOdd)

Perhaps it would even be nice to be able to say that certain pre or postconditions should be run even when __debug__ == 0
Add comment

Why eiffelize?, Gregor Rayman, 2003/02/07
If I override a method in a subclass, I am changig its implementation, I am neither changig its semantics nor its contract. So, perhaps I could add further pre- and post-condition, but I cannot get out of the contract the superclass subscribed to. (Liskov's substitution principle)

Anyway, if overriding the method should ivalidate the contract specified in the superclass, why to "eiffelize" it at all? Why not simply put the pre- and post-condition in the mehod itself? Why eiffelmethod and not simply assert?
Add comment

SEARCH
advanced | search help


Highest rated recipes:

1. Finite State Machine ...

2. General Class for ...

3. Lightweight XML ...

4. Reading and writing mbox ...

5. How to Set Environment ...

6. automatically upgrade ...

7. Stateful Objects use ...

8. Directory Walker ...

9. IPy Notify

10. Generator Attributes


Privacy Policy | Email Opt-out | Feedback | Syndication
© 2004 ActiveState, a division of Sophos All rights reserved