3 ############## preliminary: two utility functions #####################
5 def skip_redundant(iterable
, skipset
=None):
6 "Redundant items are repeated items or items in the original skipset."
7 if skipset
is None: skipset
= set()
9 if item
not in skipset
:
14 def remove_redundant(metaclasses
):
16 for meta
in metaclasses
: # determines the metaclasses to be skipped
17 skipset
.update(inspect
.getmro(meta
)[1:])
18 return tuple(skip_redundant(metaclasses
, skipset
))
20 ##################################################################
21 ## now the core of the module: two mutually recursive functions ##
22 ##################################################################
24 memoized_metaclasses_map
= {}
26 def get_noconflict_metaclass(bases
, left_metas
, right_metas
):
27 """Not intended to be used outside of this module, unless you know
28 what you are doing."""
29 # make tuple of needed metaclasses in specified priority order
30 metas
= left_metas
+ tuple(map(type, bases
)) + right_metas
31 needed_metas
= remove_redundant(metas
)
33 # return existing confict-solving meta, if any
34 if needed_metas
in memoized_metaclasses_map
:
35 return memoized_metaclasses_map
[needed_metas
]
36 # nope: compute, memoize and return needed conflict-solving meta
37 elif not needed_metas
: # wee, a trivial case, happy us
39 elif len(needed_metas
) == 1: # another trivial case
40 meta
= needed_metas
[0]
41 # check for recursion, can happen i.e. for Zope ExtensionClasses
42 elif needed_metas
== bases
:
43 raise TypeError("Incompatible root metatypes", needed_metas
)
44 else: # gotta work ...
45 metaname
= '_' + ''.join([m
.__name
__ for m
in needed_metas
])
46 meta
= classmaker()(metaname
, needed_metas
, {})
47 memoized_metaclasses_map
[needed_metas
] = meta
50 def classmaker(left_metas
=(), right_metas
=()):
51 def make_class(name
, bases
, adict
):
52 print ("make_class", name
)
53 metaclass
= get_noconflict_metaclass(bases
, left_metas
, right_metas
)
54 return metaclass(name
, bases
, adict
)