vendor/Five/1.2b-r20590

view metaclass.py @ 0:3673ed425f80

Vendor import of Five 1.2b+ (r20590)
author fguillaume
date Fri, 02 Dec 2005 20:25:42 +0000
parents
children
line source
1 ##############################################################################
2 #
3 # Copyright (c) 2004, 2005 Zope Corporation and Contributors.
4 # All Rights Reserved.
5 #
6 # This software is subject to the provisions of the Zope Public License,
7 # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
8 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11 # FOR A PARTICULAR PURPOSE.
12 #
13 ##############################################################################
14 # metaclass taken from PEAK.
15 # Martijn doesn't pretend to understand this.
16 from weakref import WeakValueDictionary
17 from types import ClassType
19 def makeClass(name,bases,dict):
20 """makeClass(name, bases, dict) - enhanced class creation
21 """
23 # Create either a "classic" Python class, an ExtensionClass, or a new-style
24 # class with autogenerated metaclass, based on the nature of the base
25 # classes involved
27 name = str(name) # De-unicode
29 metaclasses = [getattr(b,'__class__',type(b)) for b in bases]
31 if dict.has_key('__metaclass__'):
32 metaclasses.insert(0,dict['__metaclass__'])
34 if dict.has_key('__metaclasses__'):
35 metaclasses[0:0] = list(dict['__metaclasses__'])
37 metaclasses = normalizeBases(metaclasses)
39 if metaclasses:
41 # If we have metaclasses, it's not a classic class, so derive a
42 # single metaclass, and ask it to create the class.
44 if len(metaclasses)==1:
45 metaclass = metaclasses[0]
46 else:
47 metaclass = derivedMeta(metaclasses)
49 return metaclass(name,bases,dict)
51 # No metaclasses, it's a classic class, so use 'new.classobj'
53 from new import classobj; return classobj(name,bases,dict)
55 def normalizeBases(allBases):
56 return minimalBases([b for b in allBases if b is not makeClass])
58 def minimalBases(classes):
59 """Reduce a list of base classes to its ordered minimum equivalent"""
61 classes = [c for c in classes if c is not ClassType]
62 candidates = []
64 for m in classes:
65 for n in classes:
66 if issubclass(n,m) and m is not n:
67 break
68 else:
69 # m has no subclasses in 'classes'
70 if m in candidates:
71 candidates.remove(m) # ensure that we're later in the list
72 candidates.append(m)
74 return candidates
76 metaReg = WeakValueDictionary()
78 def derivedMeta(metaclasses):
79 metaclasses = tuple(metaclasses)
80 derived = metaReg.get(metaclasses)
82 if derived is None:
83 normalized = tuple(normalizeBases(metaclasses))
84 derived = metaReg.get(normalized)
86 if derived is None:
87 if len(normalized)==1:
88 derived = normalized[0]
89 else:
90 derived = metaFromBases(normalized)(
91 '_'.join([n.__name__ for n in normalized]),
92 metaclasses, {}
93 )
94 try: metaReg[normalized] = derived
95 except TypeError: pass # Some metatypes can't be weakref'd
97 try: metaReg[metaclasses] = derived
98 except TypeError: pass
100 return derived
102 def metaFromBases(bases):
103 meta = tuple([getattr(b,'__class__',type(b)) for b in bases])
104 if meta==bases: raise TypeError("Incompatible root metatypes",bases)
105 return derivedMeta(meta)