PageRenderTime 72ms CodeModel.GetById 40ms app.highlight 10ms RepoModel.GetById 20ms app.codeStats 0ms

/Demo/newmetaclasses/Eiffel.py

http://unladen-swallow.googlecode.com/
Python | 141 lines | 100 code | 28 blank | 13 comment | 14 complexity | bde1de96db24147988b8440c15bf7019 MD5 | raw file
  1"""Support Eiffel-style preconditions and postconditions."""
  2
  3from types import FunctionType as function
  4
  5class EiffelBaseMetaClass(type):
  6
  7    def __new__(meta, name, bases, dict):
  8        meta.convert_methods(dict)
  9        return super(EiffelBaseMetaClass, meta).__new__(meta, name, bases,
 10                                                        dict)
 11
 12    @classmethod
 13    def convert_methods(cls, dict):
 14        """Replace functions in dict with EiffelMethod wrappers.
 15
 16        The dict is modified in place.
 17
 18        If a method ends in _pre or _post, it is removed from the dict
 19        regardless of whether there is a corresponding method.
 20        """
 21        # find methods with pre or post conditions
 22        methods = []
 23        for k, v in dict.iteritems():
 24            if k.endswith('_pre') or k.endswith('_post'):
 25                assert isinstance(v, function)
 26            elif isinstance(v, function):
 27                methods.append(k)
 28        for m in methods:
 29            pre = dict.get("%s_pre" % m)
 30            post = dict.get("%s_post" % m)
 31            if pre or post:
 32                dict[k] = cls.make_eiffel_method(dict[m], pre, post)
 33
 34class EiffelMetaClass1(EiffelBaseMetaClass):
 35    # an implementation of the "eiffel" meta class that uses nested functions
 36
 37    @staticmethod
 38    def make_eiffel_method(func, pre, post):
 39        def method(self, *args, **kwargs):
 40            if pre:
 41                pre(self, *args, **kwargs)
 42            x = func(self, *args, **kwargs)
 43            if post:
 44                post(self, x, *args, **kwargs)
 45            return x
 46
 47        if func.__doc__:
 48            method.__doc__ = func.__doc__
 49
 50        return method
 51
 52class EiffelMethodWrapper:
 53
 54    def __init__(self, inst, descr):
 55        self._inst = inst
 56        self._descr = descr
 57
 58    def __call__(self, *args, **kwargs):
 59        return self._descr.callmethod(self._inst, args, kwargs)
 60
 61class EiffelDescriptor(object):
 62
 63    def __init__(self, func, pre, post):
 64        self._func = func
 65        self._pre = pre
 66        self._post = post
 67
 68        self.__name__ = func.__name__
 69        self.__doc__ = func.__doc__
 70
 71    def __get__(self, obj, cls):
 72        return EiffelMethodWrapper(obj, self)
 73
 74    def callmethod(self, inst, args, kwargs):
 75        if self._pre:
 76            self._pre(inst, *args, **kwargs)
 77        x = self._func(inst, *args, **kwargs)
 78        if self._post:
 79            self._post(inst, x, *args, **kwargs)
 80        return x
 81
 82class EiffelMetaClass2(EiffelBaseMetaClass):
 83    # an implementation of the "eiffel" meta class that uses descriptors
 84
 85    make_eiffel_method = EiffelDescriptor
 86
 87def _test(metaclass):
 88    class Eiffel:
 89        __metaclass__ = metaclass
 90
 91    class Test(Eiffel):
 92
 93        def m(self, arg):
 94            """Make it a little larger"""
 95            return arg + 1
 96
 97        def m2(self, arg):
 98            """Make it a little larger"""
 99            return arg + 1
100
101        def m2_pre(self, arg):
102            assert arg > 0
103
104        def m2_post(self, result, arg):
105            assert result > arg
106
107    class Sub(Test):
108        def m2(self, arg):
109            return arg**2
110        def m2_post(self, Result, arg):
111            super(Sub, self).m2_post(Result, arg)
112            assert Result < 100
113
114    t = Test()
115    t.m(1)
116    t.m2(1)
117    try:
118        t.m2(0)
119    except AssertionError:
120        pass
121    else:
122        assert False
123
124    s = Sub()
125    try:
126        s.m2(1)
127    except AssertionError:
128        pass # result == arg
129    else:
130        assert False
131    try:
132        s.m2(10)
133    except AssertionError:
134        pass # result ==  100
135    else:
136        assert False
137    s.m2(5)
138
139if __name__ == "__main__":
140    _test(EiffelMetaClass1)
141    _test(EiffelMetaClass2)