Skip to content

Commit c0ee51d

Browse files
committed
Resolved conflict for metaclass. (Reference http://www.phyast.pitt.edu/~micheles/python/metatype.html)
1 parent 563e138 commit c0ee51d

File tree

2 files changed

+95
-1
lines changed

2 files changed

+95
-1
lines changed

chapter7/gui.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
"""A module containing simple GUI layouts using wxPython"""
55

66
from abc import ABCMeta, abstractmethod
7+
8+
from noconflict import makecls
79
import time
810

911
import wx
@@ -39,7 +41,7 @@ class constructor, after initializing common
3941
frame. It needs to return the processed RGB
4042
frame to be displayed.
4143
"""
42-
__metaclass__ = ABCMeta
44+
__metaclass__ = makecls()
4345

4446
def __init__(self, capture, title=None, parent=None, id=-1, fps=10):
4547
"""Class constructor

chapter7/noconflict.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
#<noconflict.py>
2+
3+
metadic={}
4+
5+
def _generatemetaclass(bases,metas,priority):
6+
trivial=lambda m: sum([issubclass(M,m) for M in metas],m is type)
7+
# hackish!! m is trivial if it is 'type' or, in the case explicit
8+
# metaclasses are given, if it is a superclass of at least one of them
9+
metabs=tuple([mb for mb in map(type,bases) if not trivial(mb)])
10+
metabases=(metabs+metas, metas+metabs)[priority]
11+
if metabases in metadic: # already generated metaclass
12+
return metadic[metabases]
13+
elif not metabases: # trivial metabase
14+
meta=type
15+
elif len(metabases)==1: # single metabase
16+
meta=metabases[0]
17+
else: # multiple metabases
18+
metaname="_"+''.join([m.__name__ for m in metabases])
19+
meta=makecls()(metaname,metabases,{})
20+
return metadic.setdefault(metabases,meta)
21+
22+
def makecls(*metas,**options):
23+
"""Class factory avoiding metatype conflicts. The invocation syntax is
24+
makecls(M1,M2,..,priority=1)(name,bases,dic). If the base classes have
25+
metaclasses conflicting within themselves or with the given metaclasses,
26+
it automatically generates a compatible metaclass and instantiate it.
27+
If priority is True, the given metaclasses have priority over the
28+
bases' metaclasses"""
29+
30+
priority=options.get('priority',False) # default, no priority
31+
return lambda n,b,d: _generatemetaclass(b,metas,priority)(n,b,d)
32+
33+
#</noconflict.py>
34+
35+
36+
# import inspect
37+
# import types
38+
# import __builtin__
39+
40+
# ############## preliminary: two utility functions #####################
41+
42+
# def skip_redundant(iterable, skipset=None):
43+
# "Redundant items are repeated items or items in the original skipset."
44+
# if skipset is None:
45+
# skipset = set()
46+
# for item in iterable:
47+
# if item not in skipset:
48+
# skipset.add(item)
49+
# yield item
50+
51+
# def remove_redundant(metaclasses):
52+
# skipset = set([types.ClassType])
53+
# for meta in metaclasses: # determines the metaclasses to be skipped
54+
# skipset.update(inspect.getmro(meta)[1:])
55+
# return tuple(skip_redundant(metaclasses, skipset))
56+
57+
# ##################################################################
58+
# ## now the core of the module: two mutually recursive functions ##
59+
# ##################################################################
60+
61+
# memoized_metaclasses_map = {}
62+
63+
# def get_noconflict_metaclass(bases, left_metas, right_metas):
64+
# """Not intended to be used outside of this module, unless you know
65+
# what you are doing."""
66+
# # make tuple of needed metaclasses in specified priority order
67+
# metas = left_metas + tuple(map(type, bases)) + right_metas
68+
# needed_metas = remove_redundant(metas)
69+
70+
# # return existing confict-solving meta, if any
71+
# if needed_metas in memoized_metaclasses_map:
72+
# return memoized_metaclasses_map[needed_metas]
73+
# # nope: compute, memoize and return needed conflict-solving meta
74+
# elif not needed_metas: # wee, a trivial case, happy us
75+
# meta = type
76+
# elif len(needed_metas) == 1: # another trivial case
77+
# meta = needed_metas[0]
78+
# # check for recursion, can happen i.e. for Zope ExtensionClasses
79+
# elif needed_metas == bases:
80+
# raise TypeError("Incompatible root metatypes", needed_metas)
81+
# else: # gotta work ...
82+
# metaname = '_' + ''.join([m.__name__ for m in needed_metas])
83+
# meta = classmaker()(metaname, needed_metas, {})
84+
# memoized_metaclasses_map[needed_metas] = meta
85+
# return meta
86+
87+
# def classmaker(left_metas=(), right_metas=()):
88+
# def make_class(name, bases, adict):
89+
# metaclass = get_noconflict_metaclass(
90+
# bases, left_metas, right_metas)
91+
# return metaclass(name, bases, adict)
92+
# return make_class

0 commit comments

Comments
 (0)