summaryrefslogtreecommitdiff
path: root/pypers/pep318/working/noconflict.py
blob: e14a1a76932cfab1619e7552fbb57d3c4d67c051 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
"""
Avoids metaclass conflicts by providing an additional built-in, makecls,
to be used as __metaclass__=makecls(*metaclasses). Returns a class factory.
"""

metadic={}

anyTrue=sum

def remove_redundant(bases): 
    ls=list(bases); nonredundant={}
    for c in bases:
        if anyTrue([issubclass(C,c) and c is not C for C in ls],
                   c in nonredundant):
            ls.remove(c) # if c is less specific or duplicated
        else: 
            nonredundant[c]=True
    return tuple(ls)

def _generatemetaclass(bases,metas,priority):
    metabs=tuple(map(type,bases))
    metabases=remove_redundant((metabs+metas, metas+metabs)[priority])
    if metabases in metadic: # already generated metaclass
        return metadic[metabases]
    elif not metabases: # trivial metabase
        meta=type 
    elif len(metabases)==1: # single metabase
        meta=metabases[0]
    else: # multiple metabases
        metaname=''.join([m.__name__ for m in metabases])
        meta=makecls()(metaname,metabases,{})
    return metadic.setdefault(metabases,meta)

def makecls(*metas,**options):
    """Class factory avoiding metatype conflicts. The invocation syntax is
    makecls(M1,M2,..,priority=1)(name,bases,dic). If the base classes have 
    metaclasses conflicting within themselves or with the given metaclasses, 
    it automatically generates a compatible metaclass and instantiate it. 
    If priority is True, the given metaclasses have priority over the 
    bases metaclasses."""

    priority=options.get('priority',False) # default, no priority
    def clsfactory(n,b,d):
        # workaround to allow redefinition of meta-meta.__call__
        meta=_generatemetaclass(b,metas,priority)
        return type.__call__(meta,n,b,d) # calls __new__ and __init__
    return clsfactory
          
#class Noconflict(type):
#    """Meta-metaclass tweaking the metaclass calling in such a way to
#    avoid conflicts"""
#    def __call__(mcl,n,b,d):
#        return makecls(mcl)(n,b,d)
      
if __name__=='__main__': # test              
    import __builtin__
    __builtin__.type=Noconflict('Type',(type,),{})
    class M1(type): pass
    class M2(type): pass
    class C1: __metaclass__=M1
    class C2: __metaclass__=M2
    class C(C1,C2): pass
    #__metaclass__=makecls()
#    assert type(C),__name__=='_M1M2'