forked from python/cpython
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Examples of metaprogramming in pure Python.
- Loading branch information
1 parent
558f66f
commit bff110f
Showing
2 changed files
with
291 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
"""Enumeration metaclass.""" | ||
|
||
import string | ||
|
||
class EnumMetaClass: | ||
"""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__(self, name, bases, dict): | ||
"""Constructor -- create an enumeration. | ||
Called at the end of the class statement. The arguments are | ||
the name of the new class, a tuple containing the base | ||
classes, and a dictionary containing everything that was | ||
entered in the class' namespace during execution of the class | ||
statement. In the above example, it would be {'red': 1, | ||
'green': 2, 'blue': 3}. | ||
""" | ||
for base in bases: | ||
if base.__class__ is not EnumMetaClass: | ||
raise TypeError, "Enumeration base class must be enumeration" | ||
bases = filter(lambda x: x is not Enum, bases) | ||
self.__name__ = name | ||
self.__bases__ = bases | ||
self.__dict = {} | ||
for key, value in dict.items(): | ||
self.__dict[key] = EnumInstance(name, key, value) | ||
|
||
def __getattr__(self, name): | ||
"""Return an enumeration value. | ||
For example, Color.red returns the value corresponding to red. | ||
XXX Perhaps the values should be created in the constructor? | ||
This looks in the class dictionary and if it is not found | ||
there asks the base classes. | ||
The special attribute __members__ returns the list of names | ||
defined in this class (it does not merge in the names defined | ||
in base classes). | ||
""" | ||
if name == '__members__': | ||
return self.__dict.keys() | ||
|
||
try: | ||
return self.__dict[name] | ||
except KeyError: | ||
for base in self.__bases__: | ||
try: | ||
return getattr(base, name) | ||
except AttributeError: | ||
continue | ||
|
||
raise AttributeError, name | ||
|
||
def __repr__(self): | ||
s = self.__name__ | ||
if self.__bases__: | ||
s = s + '(' + string.join(map(lambda x: x.__name__, | ||
self.__bases__), ", ") + ')' | ||
if self.__dict: | ||
list = [] | ||
for key, value in self.__dict.items(): | ||
list.append("%s: %s" % (key, int(value))) | ||
s = "%s: {%s}" % (s, string.join(list, ", ")) | ||
return s | ||
|
||
|
||
class EnumInstance: | ||
"""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 __init__(self, classname, enumname, value): | ||
self.__classname = classname | ||
self.__enumname = enumname | ||
self.__value = value | ||
|
||
def __int__(self): | ||
return self.__value | ||
|
||
def __repr__(self): | ||
return "EnumInstance(%s, %s, %s)" % (`self.__classname`, | ||
`self.__enumname`, | ||
`self.__value`) | ||
|
||
def __str__(self): | ||
return "%s.%s" % (self.__classname, self.__enumname) | ||
|
||
def __cmp__(self, other): | ||
return cmp(self.__value, int(other)) | ||
|
||
|
||
# Create the base class for enumerations. | ||
# It is an empty enumeration. | ||
Enum = EnumMetaClass("Enum", (), {}) | ||
|
||
|
||
def _test(): | ||
|
||
class Color(Enum): | ||
red = 1 | ||
green = 2 | ||
blue = 3 | ||
|
||
print Color.red | ||
print dir(Color) | ||
|
||
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 | ||
|
||
if __name__ == '__main__': | ||
_test() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
"""Tracing metaclass.""" | ||
|
||
import types | ||
|
||
class TraceMetaClass: | ||
"""Metaclass for tracing. | ||
Classes defined using this metaclass have an automatic tracing | ||
feature -- by setting the __trace_output__ instance (or class) | ||
variable to a file object, trace messages about all calls are | ||
written to the file. The trace formatting can be changed by | ||
defining a suitable __trace_call__ method. | ||
""" | ||
|
||
__inited = 0 | ||
|
||
def __init__(self, name, bases, dict): | ||
self.__name__ = name | ||
self.__bases__ = bases | ||
self.__dict = dict | ||
# XXX Can't define __dict__, alas | ||
self.__inited = 1 | ||
|
||
def __getattr__(self, name): | ||
try: | ||
return self.__dict[name] | ||
except KeyError: | ||
for base in self.__bases__: | ||
try: | ||
return getattr(base, name) | ||
except AttributeError: | ||
pass | ||
raise AttributeError, name | ||
|
||
def __setattr__(self, name, value): | ||
if not self.__inited: | ||
self.__dict__[name] = value | ||
else: | ||
self.__dict[name] = value | ||
|
||
def __call__(self, *args, **kw): | ||
inst = TracingInstance() | ||
inst.__meta_init__(self) | ||
try: | ||
init = inst.__getattr__('__init__') | ||
except AttributeError: | ||
init = lambda: None | ||
apply(init, args, kw) | ||
return inst | ||
|
||
__trace_output__ = None | ||
|
||
class TracingInstance: | ||
"""Helper class to represent an instance of a tracing class.""" | ||
|
||
def __trace_call__(self, fp, fmt, *args): | ||
fp.write((fmt+'\n') % args) | ||
|
||
def __meta_init__(self, klass): | ||
self.__class = klass | ||
|
||
def __getattr__(self, name): | ||
# Invoked for any attr not in the instance's __dict__ | ||
try: | ||
raw = self.__class.__getattr__(name) | ||
except AttributeError: | ||
raise AttributeError, name | ||
if type(raw) != types.FunctionType: | ||
return raw | ||
# It's a function | ||
fullname = self.__class.__name__ + "." + name | ||
if not self.__trace_output__ or name == '__trace_call__': | ||
return NotTracingWrapper(fullname, raw, self) | ||
else: | ||
return TracingWrapper(fullname, raw, self) | ||
|
||
class NotTracingWrapper: | ||
def __init__(self, name, func, inst): | ||
self.__name__ = name | ||
self.func = func | ||
self.inst = inst | ||
def __call__(self, *args, **kw): | ||
return apply(self.func, (self.inst,) + args, kw) | ||
|
||
class TracingWrapper(NotTracingWrapper): | ||
def __call__(self, *args, **kw): | ||
self.inst.__trace_call__(self.inst.__trace_output__, | ||
"calling %s, inst=%s, args=%s, kw=%s", | ||
self.__name__, self.inst, args, kw) | ||
try: | ||
rv = apply(self.func, (self.inst,) + args, kw) | ||
except: | ||
t, v, tb = sys.exc_info() | ||
self.inst.__trace_call__(self.inst.__trace_output__, | ||
"returning from %s with exception %s: %s", | ||
self.__name__, t, v) | ||
raise t, v, tb | ||
else: | ||
self.inst.__trace_call__(self.inst.__trace_output__, | ||
"returning from %s with value %s", | ||
self.__name__, rv) | ||
return rv | ||
|
||
Traced = TraceMetaClass('Traced', (), {'__trace_output__': None}) | ||
|
||
|
||
def _test(): | ||
import sys | ||
class C(Traced): | ||
def __init__(self, x=0): self.x = x | ||
def m1(self, x): self.x = x | ||
def m2(self, y): return self.x + y | ||
C.__trace_output__ = sys.stdout | ||
x = C(4321) | ||
print x | ||
print x.x | ||
print x.m1(100) | ||
print x.m1(10) | ||
print x.m2(33) | ||
print x.m1(5) | ||
print x.m2(4000) | ||
print x.x | ||
|
||
if __name__ == '__main__': | ||
_test() |