Created
September 13, 2012 09:11
-
-
Save beniwohli/3713091 to your computer and use it in GitHub Desktop.
Combine hvad and mptt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from project.utils import classmaker | |
class Category(TranslatableModel, MPTTModel): | |
# https://github.com/ojii/django-nani/issues/39 | |
__metaclass__ = classmaker() | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
great stuff, thanks