mirror of
https://github.com/gryf/pygtktalog.git
synced 2026-03-26 22:03:30 +01:00
66 lines
2.5 KiB
Python
66 lines
2.5 KiB
Python
# Author: Michele Simionato <michelesimionato@libero.it>
|
|
# Copyright (C) 2004 by Michele Simionato
|
|
# License: Python License (version not specified)
|
|
# Last Updated: 2nd of March 2007, 10:23 GMT
|
|
#
|
|
# Any serious user of metaclasses has been bitten at least once by
|
|
# the infamous metaclass/metatype conflict. This script contains a
|
|
# general recipe to solve the problem, as well as some theory and
|
|
# some examples.
|
|
|
|
|
|
import inspect, types, __builtin__
|
|
|
|
############## preliminary: two utility functions #####################
|
|
|
|
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
|
|
|
|
|
|
def remove_redundant(metaclasses):
|
|
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))
|
|
|
|
##################################################################
|
|
## now the core of the module: two mutually recursive functions ##
|
|
##################################################################
|
|
|
|
memoized_metaclasses_map = {}
|
|
|
|
def get_noconflict_metaclass(bases, left_metas, right_metas):
|
|
"""Not intended to be used outside of this module, unless you know
|
|
what you are doing."""
|
|
# make tuple of needed metaclasses in specified priority order
|
|
metas = left_metas + tuple(map(type, bases)) + right_metas
|
|
needed_metas = remove_redundant(metas)
|
|
|
|
# return existing confict-solving meta, if any
|
|
if needed_metas in memoized_metaclasses_map:
|
|
return memoized_metaclasses_map[needed_metas]
|
|
# nope: compute, memoize and return needed conflict-solving meta
|
|
elif not needed_metas: # wee, a trivial case, happy us
|
|
meta = type
|
|
elif len(needed_metas) == 1: # another trivial case
|
|
meta = needed_metas[0]
|
|
# check for recursion, can happen i.e. for Zope ExtensionClasses
|
|
elif needed_metas == bases:
|
|
raise TypeError("Incompatible root metatypes", needed_metas)
|
|
else: # gotta work ...
|
|
metaname = '_' + ''.join([m.__name__ for m in needed_metas])
|
|
meta = classmaker()(metaname, needed_metas, {})
|
|
memoized_metaclasses_map[needed_metas] = meta
|
|
return meta
|
|
|
|
def classmaker(left_metas=(), right_metas=()):
|
|
def make_class(name, bases, adict):
|
|
metaclass = get_noconflict_metaclass(bases, left_metas, right_metas)
|
|
return metaclass(name, bases, adict)
|
|
return make_class
|