From a00331636bb8361f8c11ea9f279781fb6cc98e15 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Thu, 11 Jul 2002 21:08:06 +0000 Subject: Add Enum and Eiffel examples using new-style classes. --- Demo/newmetaclasses/Eiffel.py | 145 ++++++++++++++++++++++++++++++++++ Demo/newmetaclasses/Enum.py | 178 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 323 insertions(+) create mode 100644 Demo/newmetaclasses/Eiffel.py create mode 100644 Demo/newmetaclasses/Enum.py (limited to 'Demo/newmetaclasses') diff --git a/Demo/newmetaclasses/Eiffel.py b/Demo/newmetaclasses/Eiffel.py new file mode 100644 index 0000000000..c9944feac6 --- /dev/null +++ b/Demo/newmetaclasses/Eiffel.py @@ -0,0 +1,145 @@ +"""Support Eiffel-style preconditions and postconditions.""" + +from new import function + +class EiffelBaseMetaClass(type): + + 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 = [] + prenpost = [] + for k, v in dict.iteritems(): + if k.endswith('_pre') or k.endswith('_post'): + assert isinstance(v, function) + prenpost.append(k) + 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) + + convert_methods = classmethod(convert_methods) + + 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 + + make_eiffel_method = staticmethod(make_eiffel_method) + +class EiffelMetaClass1(EiffelBaseMetaClass): + # an implementation of the "eiffel" meta class that uses nested functions + + def __new__(meta, name, bases, dict): + meta.convert_methods(dict) + return super(EiffelMetaClass1, meta).__new__(meta, name, bases, dict) + +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(EiffelMetaClass1): + # 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 + +if __name__ == "__main__": + _test(EiffelMetaClass1) + _test(EiffelMetaClass2) + diff --git a/Demo/newmetaclasses/Enum.py b/Demo/newmetaclasses/Enum.py new file mode 100644 index 0000000000..8a00b59f21 --- /dev/null +++ b/Demo/newmetaclasses/Enum.py @@ -0,0 +1,178 @@ +"""Enumeration metaclass.""" + +class EnumMetaclass(type): + """Metaclass for enumeration. + + To define your own enumeration, do something like + + class Color(Enum): + red = 1 + green = 2 + blue = 3 + + Now, Color.red, Color.green and Color.blue behave totally + different: they are enumerated values, not integers. + + Enumerations cannot be instantiated; however they can be + subclassed. + """ + + def __init__(cls, name, bases, dict): + super(EnumMetaclass, cls).__init__(name, bases, dict) + cls._members = [] + for attr in dict.keys(): + if not (attr.startswith('__') and attr.endswith('__')): + enumval = EnumInstance(name, attr, dict[attr]) + setattr(cls, attr, enumval) + cls._members.append(attr) + + def __getattr__(cls, name): + if name == "__members__": + return cls._members + raise AttributeError, name + + def __repr__(cls): + s1 = s2 = "" + enumbases = [base.__name__ for base in cls.__bases__ + if isinstance(base, EnumMetaclass) and not base is Enum] + if enumbases: + s1 = "(%s)" % ", ".join(enumbases) + enumvalues = ["%s: %d" % (val, getattr(cls, val)) + for val in cls._members] + if enumvalues: + s2 = ": {%s}" % ", ".join(enumvalues) + return "%s%s%s" % (cls.__name__, s1, s2) + +class FullEnumMetaclass(EnumMetaclass): + """Metaclass for full enumerations. + + A full enumeration displays all the values defined in base classes. + """ + + def __init__(cls, name, bases, dict): + super(FullEnumMetaclass, cls).__init__(name, bases, dict) + for obj in cls.__mro__: + if isinstance(obj, EnumMetaclass): + for attr in obj._members: + # XXX inefficient + if not attr in cls._members: + cls._members.append(attr) + +class EnumInstance(int): + """Class to represent an enumeration value. + + EnumInstance('Color', 'red', 12) prints as 'Color.red' and behaves + like the integer 12 when compared, but doesn't support arithmetic. + + XXX Should it record the actual enumeration rather than just its + name? + """ + + def __new__(cls, classname, enumname, value): + return int.__new__(cls, value) + + def __init__(self, classname, enumname, value): + self.__classname = classname + self.__enumname = enumname + + def __repr__(self): + return "EnumInstance(%s, %s, %d)" % (self.__classname, self.__enumname, + self) + + def __str__(self): + return "%s.%s" % (self.__classname, self.__enumname) + +class Enum: + __metaclass__ = EnumMetaclass + +class FullEnum: + __metaclass__ = FullEnumMetaclass + +def _test(): + + class Color(Enum): + red = 1 + green = 2 + blue = 3 + + print Color.red + + print `Color.red` + print Color.red == Color.red + print Color.red == Color.blue + print Color.red == 1 + print Color.red == 2 + + class ExtendedColor(Color): + white = 0 + orange = 4 + yellow = 5 + purple = 6 + black = 7 + + print ExtendedColor.orange + print ExtendedColor.red + + print Color.red == ExtendedColor.red + + class OtherColor(Enum): + white = 4 + blue = 5 + + class MergedColor(Color, OtherColor): + pass + + print MergedColor.red + print MergedColor.white + + print Color + print ExtendedColor + print OtherColor + print MergedColor + +def _test2(): + + class Color(FullEnum): + red = 1 + green = 2 + blue = 3 + + print Color.red + + print `Color.red` + print Color.red == Color.red + print Color.red == Color.blue + print Color.red == 1 + print Color.red == 2 + + class ExtendedColor(Color): + white = 0 + orange = 4 + yellow = 5 + purple = 6 + black = 7 + + print ExtendedColor.orange + print ExtendedColor.red + + print Color.red == ExtendedColor.red + + class OtherColor(FullEnum): + white = 4 + blue = 5 + + class MergedColor(Color, OtherColor): + pass + + print MergedColor.red + print MergedColor.white + + print Color + print ExtendedColor + print OtherColor + print MergedColor + +if __name__ == '__main__': + _test() + _test2() + -- cgit v1.2.1