#!/usr/bin/python import types, sets def get_classdict(cls): "get name:attr dict from a class" names = dir(cls) classdict = {} for name in names: classdict[name]=getattr(cls, name) return classdict def get_methods(classdict): "get a set of nonsystem methods from a classdict" methods = sets.Set() for (k,v) in classdict.items(): if k.startswith('__'): continue if type(v) is types.MethodType or type(v) is types.FunctionType: methods.add(k) return methods class TraitClass(type): """Metaclass for classes that may not have state, whose conflicting trait methods must be resolved in extenders, and who may not extend classes which are not traits.""" def __init__(cls, clsname, bases, classdict): type.__init__(cls, clsname, bases, classdict) cls.conflict_methods = sets.Set() all_methods = sets.Set() for c in cls.get_method_lists(bases, classdict): methods = get_methods(c) cls.conflict_methods |= all_methods & methods all_methods |= methods #propagate conflicting methods from base classes for c in bases: if 'conflict_methods' in dir(c): cls.conflict_methods |= c.conflict_methods for c in bases: #if c == object: continue if type(c) != TraitClass: cls.handle_base_class(c) def get_method_lists(cls, bases, classdict): "traits check for conflicts from parents and from self" return map(get_classdict, bases)+[classdict,] def handle_base_class(cls, c): "enforce only inheritting traits" raise "Trait %s cannot inherit from a class that is not a Trait"%cls.__name__,c.__name__ def __new__(cls, clsname, bases, classdict): #if a nonexplicit class extends nontraits, make it a trait user if classdict.get('__metaclass__') != TraitClass and 0 < len([c for c in bases if type(c) != TraitClass]): classdict['__metaclass__']=TraitUserClass #if class is actually a trait, make readonly else: classdict['__slots__']=[] return type.__new__(cls, clsname, bases, classdict) class TraitUserClass(TraitClass): """Metaclass for mostly normal classes, that must use single inheritance of nontraits, and that implement trait conflict handling""" def __init__(cls, clsname, bases, classdict): TraitClass.__init__(cls, clsname, bases, classdict) unresolved = getattr(cls, 'conflict_methods',sets.Set()) - sets.Set(classdict) - sets.Set(dir(getattr(cls,'nontrait_base',None))) if len(unresolved) > 0: raise "conflicting methods",unresolved def get_method_lists(cls, bases, classdict): "check for conflicts in parent traits" nontrait_base = getattr(cls, 'nontrait_base', None) return [ get_classdict(c) for c in bases if c != nontrait_base] def handle_base_class(cls, c): "enforce single inheritance of nontraits" nontrait_base=getattr(cls,'nontrait_base',None) if nontrait_base: raise "Trait-using class %s can extend no more than one class that is not a Trait"%cls.__name__, c.__name__ cls.nontrait_base = c def __new__(cls, clsname, bases, classdict): #python weirdness; __metaclass__ doesn't override parent's here if classdict['__metaclass__'] == TraitClass: raise "Trait %s cannot inherit from a class that is a TraitUser"%clsname return type.__new__(cls, clsname, bases, classdict) class Trait: __metaclass__ = TraitClass #convenience object class TraitUser: __metaclass__ = TraitUserClass #convenience object