1
# -*- test-case-name: axiom.test.test_slotmachine -*-
5
def borrow(C, slotgather=lambda _slots: None):
7
Use implementation, but not identity or inheritance, from a class. This is
8
useful when you want to avoid mucking with class metadata - in particular,
9
if you want to have a class which has no slots (and the semantics
10
associated with that, i.e. no unintended attributes) which inherits from
11
old-style and/or potentially __dict__-ful mixins.
19
if C.__name__.endswith("(Borrowed)"):
20
slotgather(C.__all_slots__)
24
allslots = list(D.get('__slots__', ()))
25
oldslots = allslots[:]
29
for oldslot in oldslots:
30
# member_descriptor let's hope
35
def gatherforme(stuff):
37
allslots.extend(stuff)
41
for base in C.__bases__:
42
basetuple.append(borrow(base, gatherforme))
44
allslots = dict.fromkeys(allslots).keys()
45
D['__all_slots__'] = allslots
46
new = type(C.__name__+"(Borrowed)", tuple(basetuple), D)
52
class SlotMetaMachine(type):
53
def __new__(meta, name, bases, dictionary):
54
dictionary['__name__'] = name
55
slots = ['__weakref__'] + list(
56
meta.determineSchema.im_func(meta, dictionary))
58
borrowedBases = tuple([borrow(x, slots.extend) for x in bases])
61
slots = dict.fromkeys(slots).keys() # uniquify
64
for base in borrowedBases:
65
baseslot = getattr(base, slot, _NOSLOT)
66
if baseslot is not _NOSLOT:
67
if hasattr(baseslot, '__get__') or hasattr(baseslot, '__set__'):
68
# It's a descriptor; if we get in the way, we'll break
69
# it - if we don't get in the way, it leads to
70
# surprising behavior. perhaps further consideration is
72
if slot == '__weakref__':
73
# ALL new-style classes have a useless 'weakref'
74
# descriptor. Don't avoid clobbering it, because
75
# it is pretty much designed to be clobbered.
78
underlay = '_conflict_'+slot
79
dictionary[slot] = DescriptorWithDefault(baseslot, underlay)
85
assert '__weakref__' in slots, "__weakref__ not in %r for %r" % (slots, name)
86
dictionary['__slots__'] = slots
87
nt = type.__new__(meta, name, borrowedBases, dictionary)
90
"SlotMachine with __dict__ (this should be impossible)")
93
def determineSchema(meta, dictionary):
94
if '__slots__' in dictionary:
96
"When using SlotMachine, specify 'slots' not '__slots__'")
97
return dictionary.get("slots", [])
100
class DescriptorWithDefault(object):
101
def __init__(self, default, original):
102
self.original = original
103
self.default = default
105
def __get__(self, oself, type=None):
109
return getattr(oself, self.original, self.default)
111
def __set__(self, oself, value):
112
setattr(oself, self.original, value)
114
def __delete__(self, oself):
115
delattr(oself, self.original)
118
class Attribute(object):
119
def __init__(self, doc=''):
122
def requiredSlots(self, modname, classname, attrname):
127
class SetOnce(Attribute):
129
def __init__(self, doc='', default=_RAISE):
130
Attribute.__init__(self)
131
if default is _RAISE:
134
self.default = (default,)
136
def requiredSlots(self, modname, classname, attrname):
138
t = self.trueattr = ('_' + self.name)
141
def __set__(self, iself, value):
142
if not hasattr(iself, self.trueattr):
143
setattr(iself, self.trueattr, value)
145
raise AttributeError('%s.%s may only be set once' % (
146
type(iself).__name__, self.name))
148
def __get__(self, iself, type=None):
149
if type is not None and iself is None:
151
return getattr(iself, self.trueattr, *self.default)
153
class SchemaMetaMachine(SlotMetaMachine):
155
def determineSchema(meta, dictionary):
156
attrs = dictionary['__attributes__'] = []
157
cn = dictionary['__name__']
158
mn = dictionary['__module__']
159
dictitems = dictionary.items()
160
dictitems.sort() # deterministic ordering is important because
161
# we generate SQL schemas from these attribute
164
# this does NOT traverse the class hierarchy. The SlotMetaMachine base
165
# class does all the hierarchy-traversing work in its __new__. Do not
166
# add any hierarchy traversal here; it will almost certainly be broken
167
# in surprising ways (as if borrow() weren't surprising enough) -glyph
169
for k, v in dictitems:
170
if isinstance(v, Attribute):
172
for slot in v.requiredSlots(mn, cn, k):
178
__metaclass__ = SchemaMetaMachine
181
__metaclass__ = SlotMetaMachine
184
class _structlike(list):
188
def _name2slot(self, name):
189
return self.__names__.index(name)
191
def __init__(self, *args, **kw):
192
super(_structlike, self).__init__(args)
193
self.extend([None] * (len(self.__names__) - len(args)))
194
for k, v in kw.iteritems():
195
self[self._name2slot(k)] = v
197
def __getattr__(self, attr):
199
return self[self._name2slot(attr)]
200
except (IndexError, ValueError):
201
raise AttributeError(attr)
203
def __setattr__(self, attr, value):
205
self[self._name2slot(attr)] = value
207
raise AttributeError(attr)