summaryrefslogtreecommitdiff
path: root/pypers/meta/noconflict.txt
blob: f6b1c64ab1f008d810c77bf36edeb1acece87ce9 (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
65
66
#<noconflict.py>

def memoize(f):
    """This closure remembers all f invocations"""
    argskw,result = [],[]
    def _(*args,**kw): 
        akw=args,kw
        try: # returns a previously stored result
            return result[argskw.index(akw)]
        except ValueError: # there is no previously stored result
            argskw.append(akw) # update argskw
            result.append(f(*args,**kw)) # update result
            return result[-1] # return the new result
    _.argskw=argskw #makes the argskw list accessible outside
    _.result=result #makes the result list accessible outside
    return _

def clsfactory(*metas,**options):
    """Class factory avoiding metatype conflicts. The invocation syntax is
    clsfactory(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
    trivial=lambda m: sum([issubclass(M,m) for M in metas]) # hackish lambda:
    # m is trivial if it is a superclass of at least one of the explicit metas

    def generatemetaclass(bases):
        metabases=tuple([mb for mb in map(type,bases) if not trivial(mb)])
        print metabases; raise SystemExit()
        metabases=(metabases+metas, metas+metabases)[priority]
        metaname="_"+''.join([m.__name__ for m in metabases])
        if not metabases: # trivial metabase
            return type 
        elif len(metabases)==1: # single metabase
            return metabases[0]
        else: # multiple metabases
            # create new metaclass,shift possible conflict to meta-metaclasses
            return clsfactory()(metaname,metabases,{})

    generatemetaclass=memoize(generatemetaclass) 

    return lambda name,bases,dic: generatemetaclass(bases)(name,bases,dic)

#</noconflict.py>

>>> from noconflict import clsfactory
>>> class MM1(type): pass
...
>>> class MM2(type): pass
...
>>> class M1(type):
...     __metaclass__=MM1
>>> class M2(type): 
...     __metaclass__=MM2
>>> class A: __metaclass__=M1
...
>>> class B: __metaclass__=M2
...
>>> class C(A,B):
...     __metaclass__=clsfactory()
>>> print C,type(C),type(type(C))
<class 'C'> <class 'noconflict._M1M2'> <class 'noconflict._MM1MM2'>