[Python-checkins] python/dist/src/Demo/newmetaclasses Eiffel.py,NONE,1.1 Enum.py,NONE,1.1
jhylton@users.sourceforge.net
jhylton@users.sourceforge.net
Thu, 11 Jul 2002 14:08:08 -0700
Update of /cvsroot/python/python/dist/src/Demo/newmetaclasses
In directory usw-pr-cvs1:/tmp/cvs-serv21592
Added Files:
Eiffel.py Enum.py
Log Message:
Add Enum and Eiffel examples using new-style classes.
--- NEW FILE: Eiffel.py ---
"""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)
--- NEW FILE: Enum.py ---
"""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()