|
| 1 | +#<noconflict.py> |
| 2 | + |
| 3 | +metadic={} |
| 4 | + |
| 5 | +def _generatemetaclass(bases,metas,priority): |
| 6 | + trivial=lambda m: sum([issubclass(M,m) for M in metas],m is type) |
| 7 | + # hackish!! m is trivial if it is 'type' or, in the case explicit |
| 8 | + # metaclasses are given, if it is a superclass of at least one of them |
| 9 | + metabs=tuple([mb for mb in map(type,bases) if not trivial(mb)]) |
| 10 | + metabases=(metabs+metas, metas+metabs)[priority] |
| 11 | + if metabases in metadic: # already generated metaclass |
| 12 | + return metadic[metabases] |
| 13 | + elif not metabases: # trivial metabase |
| 14 | + meta=type |
| 15 | + elif len(metabases)==1: # single metabase |
| 16 | + meta=metabases[0] |
| 17 | + else: # multiple metabases |
| 18 | + metaname="_"+''.join([m.__name__ for m in metabases]) |
| 19 | + meta=makecls()(metaname,metabases,{}) |
| 20 | + return metadic.setdefault(metabases,meta) |
| 21 | + |
| 22 | +def makecls(*metas,**options): |
| 23 | + """Class factory avoiding metatype conflicts. The invocation syntax is |
| 24 | + makecls(M1,M2,..,priority=1)(name,bases,dic). If the base classes have |
| 25 | + metaclasses conflicting within themselves or with the given metaclasses, |
| 26 | + it automatically generates a compatible metaclass and instantiate it. |
| 27 | + If priority is True, the given metaclasses have priority over the |
| 28 | + bases' metaclasses""" |
| 29 | + |
| 30 | + priority=options.get('priority',False) # default, no priority |
| 31 | + return lambda n,b,d: _generatemetaclass(b,metas,priority)(n,b,d) |
| 32 | + |
| 33 | +#</noconflict.py> |
| 34 | + |
| 35 | + |
| 36 | +# import inspect |
| 37 | +# import types |
| 38 | +# import __builtin__ |
| 39 | + |
| 40 | +# ############## preliminary: two utility functions ##################### |
| 41 | + |
| 42 | +# def skip_redundant(iterable, skipset=None): |
| 43 | +# "Redundant items are repeated items or items in the original skipset." |
| 44 | +# if skipset is None: |
| 45 | +# skipset = set() |
| 46 | +# for item in iterable: |
| 47 | +# if item not in skipset: |
| 48 | +# skipset.add(item) |
| 49 | +# yield item |
| 50 | + |
| 51 | +# def remove_redundant(metaclasses): |
| 52 | +# skipset = set([types.ClassType]) |
| 53 | +# for meta in metaclasses: # determines the metaclasses to be skipped |
| 54 | +# skipset.update(inspect.getmro(meta)[1:]) |
| 55 | +# return tuple(skip_redundant(metaclasses, skipset)) |
| 56 | + |
| 57 | +# ################################################################## |
| 58 | +# ## now the core of the module: two mutually recursive functions ## |
| 59 | +# ################################################################## |
| 60 | + |
| 61 | +# memoized_metaclasses_map = {} |
| 62 | + |
| 63 | +# def get_noconflict_metaclass(bases, left_metas, right_metas): |
| 64 | +# """Not intended to be used outside of this module, unless you know |
| 65 | +# what you are doing.""" |
| 66 | +# # make tuple of needed metaclasses in specified priority order |
| 67 | +# metas = left_metas + tuple(map(type, bases)) + right_metas |
| 68 | +# needed_metas = remove_redundant(metas) |
| 69 | + |
| 70 | +# # return existing confict-solving meta, if any |
| 71 | +# if needed_metas in memoized_metaclasses_map: |
| 72 | +# return memoized_metaclasses_map[needed_metas] |
| 73 | +# # nope: compute, memoize and return needed conflict-solving meta |
| 74 | +# elif not needed_metas: # wee, a trivial case, happy us |
| 75 | +# meta = type |
| 76 | +# elif len(needed_metas) == 1: # another trivial case |
| 77 | +# meta = needed_metas[0] |
| 78 | +# # check for recursion, can happen i.e. for Zope ExtensionClasses |
| 79 | +# elif needed_metas == bases: |
| 80 | +# raise TypeError("Incompatible root metatypes", needed_metas) |
| 81 | +# else: # gotta work ... |
| 82 | +# metaname = '_' + ''.join([m.__name__ for m in needed_metas]) |
| 83 | +# meta = classmaker()(metaname, needed_metas, {}) |
| 84 | +# memoized_metaclasses_map[needed_metas] = meta |
| 85 | +# return meta |
| 86 | + |
| 87 | +# def classmaker(left_metas=(), right_metas=()): |
| 88 | +# def make_class(name, bases, adict): |
| 89 | +# metaclass = get_noconflict_metaclass( |
| 90 | +# bases, left_metas, right_metas) |
| 91 | +# return metaclass(name, bases, adict) |
| 92 | +# return make_class |
0 commit comments