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