############################################################################## # # Copyright (c) 2003 Zope Foundation 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. # ############################################################################## """Class advice. This module was adapted from 'protocols.advice', part of the Python Enterprise Application Kit (PEAK). Please notify the PEAK authors (pje@telecommunity.com and tsarna@sarna.org) if bugs are found or Zope-specific changes are required, so that the PEAK version of this module can be kept in sync. PEAK is a Python application framework that interoperates with (but does not require) Zope 3 and Twisted. It provides tools for manipulating UML models, object-relational persistence, aspect-oriented programming, and more. Visit the PEAK home page at http://peak.telecommunity.com for more information. """ from types import FunctionType __all__ = [ 'determineMetaclass', 'getFrameInfo', 'isClassAdvisor', 'minimalBases', ] import sys def getFrameInfo(frame): """Return (kind,module,locals,globals) for a frame 'kind' is one of "exec", "module", "class", "function call", or "unknown". """ f_locals = frame.f_locals f_globals = frame.f_globals sameNamespace = f_locals is f_globals hasModule = '__module__' in f_locals hasName = '__name__' in f_globals sameName = hasModule and hasName sameName = sameName and f_globals['__name__']==f_locals['__module__'] module = hasName and sys.modules.get(f_globals['__name__']) or None namespaceIsModule = module and module.__dict__ is f_globals if not namespaceIsModule: # some kind of funky exec kind = "exec" elif sameNamespace and not hasModule: kind = "module" elif sameName and not sameNamespace: kind = "class" elif not sameNamespace: kind = "function call" else: # pragma: no cover # How can you have f_locals is f_globals, and have '__module__' set? # This is probably module-level code, but with a '__module__' variable. kind = "unknown" return kind, module, f_locals, f_globals def isClassAdvisor(ob): """True if 'ob' is a class advisor function""" return isinstance(ob,FunctionType) and hasattr(ob,'previousMetaclass') def determineMetaclass(bases, explicit_mc=None): """Determine metaclass from 1+ bases and optional explicit __metaclass__""" meta = [getattr(b,'__class__',type(b)) for b in bases] if explicit_mc is not None: # The explicit metaclass needs to be verified for compatibility # as well, and allowed to resolve the incompatible bases, if any meta.append(explicit_mc) if len(meta)==1: # easy case return meta[0] candidates = minimalBases(meta) # minimal set of metaclasses if len(candidates)>1: # We could auto-combine, but for now we won't... raise TypeError("Incompatible metatypes", bases) # Just one, return it return candidates[0] def minimalBases(classes): """Reduce a list of base classes to its ordered minimum equivalent""" 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