Viewing file: Eiffel.py (3.63 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
"""Support Eiffel-style preconditions and postconditions."""
from types import FunctionType as function
class EiffelBaseMetaClass(type):
def __new__(meta, name, bases, dict): meta.convert_methods(dict) return super(EiffelBaseMetaClass, meta).__new__(meta, name, bases, dict)
@classmethod def convert_methods(cls, dict): """Replace functions in dict with EiffelMethod wrappers.
The dict is modified in place.
If a method ends in _pre or _post, it is removed from the dict regardless of whether there is a corresponding method. """ # find methods with pre or post conditions methods = [] for k, v in dict.iteritems(): if k.endswith('_pre') or k.endswith('_post'): assert isinstance(v, function) elif isinstance(v, function): methods.append(k) for m in methods: pre = dict.get("%s_pre" % m) post = dict.get("%s_post" % m) if pre or post: dict[k] = cls.make_eiffel_method(dict[m], pre, post)
class EiffelMetaClass1(EiffelBaseMetaClass): # an implementation of the "eiffel" meta class that uses nested functions
@staticmethod def make_eiffel_method(func, pre, post): def method(self, *args, **kwargs): if pre: pre(self, *args, **kwargs) x = func(self, *args, **kwargs) if post: post(self, x, *args, **kwargs) return x
if func.__doc__: method.__doc__ = func.__doc__
return method
class EiffelMethodWrapper:
def __init__(self, inst, descr): self._inst = inst self._descr = descr
def __call__(self, *args, **kwargs): return self._descr.callmethod(self._inst, args, kwargs)
class EiffelDescriptor(object):
def __init__(self, func, pre, post): self._func = func self._pre = pre self._post = post
self.__name__ = func.__name__ self.__doc__ = func.__doc__
def __get__(self, obj, cls): return EiffelMethodWrapper(obj, self)
def callmethod(self, inst, args, kwargs): if self._pre: self._pre(inst, *args, **kwargs) x = self._func(inst, *args, **kwargs) if self._post: self._post(inst, x, *args, **kwargs) return x
class EiffelMetaClass2(EiffelBaseMetaClass): # an implementation of the "eiffel" meta class that uses descriptors
make_eiffel_method = EiffelDescriptor
def _test(metaclass): class Eiffel: __metaclass__ = metaclass
class Test(Eiffel):
def m(self, arg): """Make it a little larger""" return arg + 1
def m2(self, arg): """Make it a little larger""" return arg + 1
def m2_pre(self, arg): assert arg > 0
def m2_post(self, result, arg): assert result > arg
class Sub(Test): def m2(self, arg): return arg**2 def m2_post(self, Result, arg): super(Sub, self).m2_post(Result, arg) assert Result < 100
t = Test() t.m(1) t.m2(1) try: t.m2(0) except AssertionError: pass else: assert False
s = Sub() try: s.m2(1) except AssertionError: pass # result == arg else: assert False try: s.m2(10) except AssertionError: pass # result == 100 else: assert False s.m2(5)
if __name__ == "__main__": _test(EiffelMetaClass1) _test(EiffelMetaClass2)
|