############################################################################## # # Copyright (c) 2004, 2005 Zope Corporation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## # metaclass taken from PEAK. # Martijn doesn't pretend to understand this. from weakref import WeakValueDictionary from types import ClassType def makeClass(name,bases,dict): """makeClass(name, bases, dict) - enhanced class creation """ # Create either a "classic" Python class, an ExtensionClass, or a new-style # class with autogenerated metaclass, based on the nature of the base # classes involved name = str(name) # De-unicode metaclasses = [getattr(b,'__class__',type(b)) for b in bases] if dict.has_key('__metaclass__'): metaclasses.insert(0,dict['__metaclass__']) if dict.has_key('__metaclasses__'): metaclasses[0:0] = list(dict['__metaclasses__']) metaclasses = normalizeBases(metaclasses) if metaclasses: # If we have metaclasses, it's not a classic class, so derive a # single metaclass, and ask it to create the class. if len(metaclasses)==1: metaclass = metaclasses[0] else: metaclass = derivedMeta(metaclasses) return metaclass(name,bases,dict) # No metaclasses, it's a classic class, so use 'new.classobj' from new import classobj; return classobj(name,bases,dict) def normalizeBases(allBases): return minimalBases([b for b in allBases if b is not makeClass]) def minimalBases(classes): """Reduce a list of base classes to its ordered minimum equivalent""" classes = [c for c in classes if c is not ClassType] candidates = [] for m in classes: for n in classes: if issubclass(n,m) and m is not n: break else: # m has no subclasses in 'classes' if m in candidates: candidates.remove(m) # ensure that we're later in the list candidates.append(m) return candidates metaReg = WeakValueDictionary() def derivedMeta(metaclasses): metaclasses = tuple(metaclasses) derived = metaReg.get(metaclasses) if derived is None: normalized = tuple(normalizeBases(metaclasses)) derived = metaReg.get(normalized) if derived is None: if len(normalized)==1: derived = normalized[0] else: derived = metaFromBases(normalized)( '_'.join([n.__name__ for n in normalized]), metaclasses, {} ) try: metaReg[normalized] = derived except TypeError: pass # Some metatypes can't be weakref'd try: metaReg[metaclasses] = derived except TypeError: pass return derived def metaFromBases(bases): meta = tuple([getattr(b,'__class__',type(b)) for b in bases]) if meta==bases: raise TypeError("Incompatible root metatypes",bases) return derivedMeta(meta)