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