Source code for core.metaclass
# -*- python -*-
# CODE from ASPN language cookbook
# License BSD
###############################################################################
# Functions from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/204197
# They resolv metaclass conflicts
# Title: SOLVING THE METACLASS CONFLICT
# Submitter: Michele Simionato (other recipes)
# Last Updated: 2005/04/11
# Version no: 1.4
# Category: OOP
###############################################################################
# The simplest case where a metatype conflict happens is the following.
# Consider a class ``A`` with metaclass ``M_A`` and a class ``B`` with
# an independent metaclass ``M_B``; suppose we derive ``C`` from ``A``
# and ``B``. The question is: what is the metaclass of ``C`` ?
# Is it ``M_A`` or ``M_B`` ?
# The correct answer (see the book "Putting metaclasses to work" for a
# thoughtful discussion) is ``M_C``, where ``M_C`` is a metaclass that inherits
# from ``M_A`` and ``M_B``, as in the following graph, where instantiation
# is denoted by colon lines:
# M_A M_B
# : \ / :
# : \ / :
# A M_C B
# \ : /
# \ : /
# C
# However, Python is not that magic, and it does not automatically create
# ``M_C``. Instead, it raises a ``TypeError``, warning the programmer of
# the possible confusion. The metatype conflict can be avoided
# by assegning the correct metaclass to ``C`` by hand:
# >>> class M_AM_B(M_A,M_B): pass
# ...
# >>> class C(A,B):
# ... __metaclass__=M_AM_B
# >>> C,type(C)
# (<class 'C'>, <class 'M_AM_B'>)
# In general, a class ``A(B, C, D , ...)`` can be generated without conflicts
# only if ``type(A)`` is a subclass of each of ``type(B), type(C), ...``
# It is possible to automatically avoid conflicts, by defining a smart
# class factory that generates the correct metaclass by looking at the
# metaclasses of the base classes. This is done via the ``classmaker``
# class factory, wich internally invokes the ``get_noconflict_metaclass``
# function.
# >>> from noconflict import classmaker
# >>> class C(A,B):
# ... __metaclass__=classmaker()
# >>> C
# <class 'C'>
# >>> type(C) # automatically generated metaclass
# <class 'noconflict._M_AM_B'>
# In order to avoid to generate twice the same metaclass, they
# are stored in a dictionary. In particular, when ``_generatemetaclass``
# is invoked with the same arguments it returns the same metaclass.
""" todo """
__license__ = "Cecill-C"
__revision__ = " $Id$ "
import inspect
import types
############## preliminary: two utility functions #####################
[docs]def skip_redundant(iterable, skipset=None):
"Redundant items are repeated items or items in the original skipset."
if skipset is None:
skipset = set()
for item in iterable:
if item not in skipset:
skipset.add(item)
yield item
[docs]def remove_redundant(metaclasses):
"""todo"""
skipset = set([types.ClassType])
for meta in metaclasses: # determines the metaclasses to be skipped
skipset.update(inspect.getmro(meta)[1:])
return tuple(skip_redundant(metaclasses, skipset))
# Store already generated class here
memoized_metaclasses_map = {}