~divmod-dev/divmod.org/1304710-storeless-adapter

« back to all changes in this revision

Viewing changes to Axiom/axiom/slotmachine.py

  • Committer: glyph
  • Date: 2005-07-28 22:09:16 UTC
  • Revision ID: svn-v4:866e43f7-fbfc-0310-8f2a-ec88d1da2979:trunk:2
move this repository to a more official-looking URL

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- test-case-name: axiom.test.test_slotmachine -*-
 
2
 
 
3
_borrowed = {}
 
4
 
 
5
def borrow(C, slotgather=lambda _slots: None):
 
6
    """
 
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.
 
12
    """
 
13
    if C is object:
 
14
        return C
 
15
 
 
16
    if C in _borrowed:
 
17
        C = _borrowed[C]
 
18
 
 
19
    if C.__name__.endswith("(Borrowed)"):
 
20
        slotgather(C.__all_slots__)
 
21
        return C
 
22
 
 
23
    D = dict(C.__dict__)
 
24
    allslots = list(D.get('__slots__', ()))
 
25
    oldslots = allslots[:]
 
26
 
 
27
    slotgather(oldslots)
 
28
 
 
29
    for oldslot in oldslots:
 
30
        # member_descriptor let's hope
 
31
        del D[oldslot]
 
32
 
 
33
    D['__slots__'] = ()
 
34
 
 
35
    def gatherforme(stuff):
 
36
        slotgather(stuff)
 
37
        allslots.extend(stuff)
 
38
 
 
39
    basetuple = []
 
40
 
 
41
    for base in C.__bases__:
 
42
        basetuple.append(borrow(base, gatherforme))
 
43
 
 
44
    allslots = dict.fromkeys(allslots).keys()
 
45
    D['__all_slots__'] = allslots
 
46
    new = type(C.__name__+"(Borrowed)", tuple(basetuple), D)
 
47
    _borrowed[C] = new
 
48
    return new
 
49
 
 
50
_NOSLOT = object()
 
51
 
 
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))
 
57
        if bases:
 
58
            borrowedBases = tuple([borrow(x, slots.extend) for x in bases])
 
59
        else:
 
60
            borrowedBases = ()
 
61
        slots = dict.fromkeys(slots).keys() # uniquify
 
62
        fin = []
 
63
        for slot in slots:
 
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
 
71
                        # in order.
 
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.
 
76
                            fin.append(slot)
 
77
                        break
 
78
                    underlay = '_conflict_'+slot
 
79
                    dictionary[slot] = DescriptorWithDefault(baseslot, underlay)
 
80
                    fin.append(underlay)
 
81
                    break
 
82
            else:
 
83
                fin.append(slot)
 
84
        slots = fin
 
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)
 
88
        if nt.__dictoffset__:
 
89
            raise AssertionError(
 
90
                "SlotMachine with __dict__ (this should be impossible)")
 
91
        return nt
 
92
 
 
93
    def determineSchema(meta, dictionary):
 
94
        if '__slots__' in dictionary:
 
95
            raise AssertionError(
 
96
                "When using SlotMachine, specify 'slots' not '__slots__'")
 
97
        return dictionary.get("slots", [])
 
98
 
 
99
 
 
100
class DescriptorWithDefault(object):
 
101
    def __init__(self, default, original):
 
102
        self.original = original
 
103
        self.default = default
 
104
 
 
105
    def __get__(self, oself, type=None):
 
106
        if type is not None:
 
107
            if oself is None:
 
108
                return self.default
 
109
        return getattr(oself, self.original, self.default)
 
110
 
 
111
    def __set__(self, oself, value):
 
112
        setattr(oself, self.original, value)
 
113
 
 
114
    def __delete__(self, oself):
 
115
        delattr(oself, self.original)
 
116
 
 
117
 
 
118
class Attribute(object):
 
119
    def __init__(self, doc=''):
 
120
        self.doc = doc
 
121
 
 
122
    def requiredSlots(self, modname, classname, attrname):
 
123
        self.name = attrname
 
124
        yield attrname
 
125
 
 
126
_RAISE = object()
 
127
class SetOnce(Attribute):
 
128
 
 
129
    def __init__(self, doc='', default=_RAISE):
 
130
        Attribute.__init__(self)
 
131
        if default is _RAISE:
 
132
            self.default = ()
 
133
        else:
 
134
            self.default = (default,)
 
135
 
 
136
    def requiredSlots(self, modname, classname, attrname):
 
137
        self.name = attrname
 
138
        t = self.trueattr = ('_' + self.name)
 
139
        yield t
 
140
 
 
141
    def __set__(self, iself, value):
 
142
        if not hasattr(iself, self.trueattr):
 
143
            setattr(iself, self.trueattr, value)
 
144
        else:
 
145
            raise AttributeError('%s.%s may only be set once' % (
 
146
                    type(iself).__name__, self.name))
 
147
 
 
148
    def __get__(self, iself, type=None):
 
149
        if type is not None and iself is None:
 
150
            return self
 
151
        return getattr(iself, self.trueattr, *self.default)
 
152
 
 
153
class SchemaMetaMachine(SlotMetaMachine):
 
154
 
 
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
 
162
                                # lists
 
163
 
 
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
 
168
 
 
169
        for k, v in dictitems:
 
170
            if isinstance(v, Attribute):
 
171
                attrs.append((k, v))
 
172
                for slot in v.requiredSlots(mn, cn, k):
 
173
                    if slot == k:
 
174
                        del dictionary[k]
 
175
                    yield slot
 
176
 
 
177
class SchemaMachine:
 
178
    __metaclass__ = SchemaMetaMachine
 
179
 
 
180
class SlotMachine:
 
181
    __metaclass__ = SlotMetaMachine
 
182
 
 
183
 
 
184
class _structlike(list):
 
185
    __names__ = []
 
186
    __slots__ = []
 
187
 
 
188
    def _name2slot(self, name):
 
189
        return self.__names__.index(name)
 
190
 
 
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
 
196
 
 
197
    def __getattr__(self, attr):
 
198
        try:
 
199
            return self[self._name2slot(attr)]
 
200
        except (IndexError, ValueError):
 
201
            raise AttributeError(attr)
 
202
 
 
203
    def __setattr__(self, attr, value):
 
204
        try:
 
205
            self[self._name2slot(attr)] = value
 
206
        except ValueError:
 
207
            raise AttributeError(attr)
 
208