~ubuntu-branches/ubuntu/karmic/pypy/karmic

« back to all changes in this revision

Viewing changes to pypy/objspace/std/typeobject.py

  • Committer: Bazaar Package Importer
  • Author(s): Alexandre Fayolle
  • Date: 2007-04-13 09:33:09 UTC
  • Revision ID: james.westby@ubuntu.com-20070413093309-yoojh4jcoocu2krz
Tags: upstream-1.0.0
ImportĀ upstreamĀ versionĀ 1.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
from pypy.objspace.std.objspace import *
 
2
from pypy.interpreter.function import Function, StaticMethod
 
3
from pypy.interpreter.argument import Arguments
 
4
from pypy.interpreter import gateway
 
5
from pypy.interpreter.typedef import weakref_descr
 
6
from pypy.objspace.std.stdtypedef import std_dict_descr, issubtypedef, Member
 
7
from pypy.objspace.std.objecttype import object_typedef
 
8
from pypy.objspace.std.dictproxyobject import W_DictProxyObject
 
9
from pypy.rlib.objectmodel import we_are_translated, hint
 
10
from pypy.rlib.rarithmetic import intmask, r_uint
 
11
 
 
12
from copy_reg import _HEAPTYPE
 
13
 
 
14
# from compiler/misc.py
 
15
 
 
16
MANGLE_LEN = 256 # magic constant from compile.c
 
17
 
 
18
def _mangle(name, klass):
 
19
    if not name.startswith('__'):
 
20
        return name
 
21
    if len(name) + 2 >= MANGLE_LEN:
 
22
        return name
 
23
    if name.endswith('__'):
 
24
        return name
 
25
    try:
 
26
        i = 0
 
27
        while klass[i] == '_':
 
28
            i = i + 1
 
29
    except IndexError:
 
30
        return name
 
31
    klass = klass[i:]
 
32
 
 
33
    tlen = len(klass) + len(name)
 
34
    if tlen > MANGLE_LEN:
 
35
        end = len(klass) + MANGLE_LEN-tlen
 
36
        if end < 0:
 
37
            klass = ''     # annotator hint
 
38
        else:
 
39
            klass = klass[:end]
 
40
 
 
41
    return "_%s%s" % (klass, name)
 
42
 
 
43
class VersionTag(object):
 
44
    pass
 
45
 
 
46
class W_TypeObject(W_Object):
 
47
    from pypy.objspace.std.typetype import type_typedef as typedef
 
48
 
 
49
    lazyloaders = {} # can be overridden by specific instances
 
50
 
 
51
    def __init__(w_self, space, name, bases_w, dict_w,
 
52
                 overridetypedef=None):
 
53
        w_self.space = space
 
54
        w_self.name = name
 
55
        w_self.bases_w = bases_w
 
56
        w_self.dict_w = dict_w
 
57
        w_self.ensure_static__new__()
 
58
        w_self.nslots = 0
 
59
        w_self.needsdel = False
 
60
        w_self.w_bestbase = None
 
61
        w_self.weak_subclasses_w = []
 
62
 
 
63
        # make sure there is a __doc__ in dict_w
 
64
        if '__doc__' not in dict_w:
 
65
            dict_w['__doc__'] = space.w_None
 
66
 
 
67
        if overridetypedef is not None:
 
68
            w_self.instancetypedef = overridetypedef
 
69
            w_self.hasdict = overridetypedef.hasdict
 
70
            w_self.weakrefable = overridetypedef.weakrefable
 
71
            w_self.__flags__ = 0 # not a heaptype
 
72
            if overridetypedef.base is not None:
 
73
                w_self.w_bestbase = space.gettypeobject(overridetypedef.base)
 
74
        else:
 
75
            w_self.__flags__ = _HEAPTYPE
 
76
            # initialize __module__ in the dict
 
77
            if '__module__' not in dict_w:
 
78
                try:
 
79
                    caller = space.getexecutioncontext().framestack.top()
 
80
                except IndexError:
 
81
                    w_globals = w_locals = space.newdict()
 
82
                else:
 
83
                    w_globals = caller.w_globals
 
84
                    w_str_name = space.wrap('__name__')
 
85
                    w_name = space.finditem(w_globals, w_str_name)
 
86
                    if w_name is not None:
 
87
                        dict_w['__module__'] = w_name
 
88
            # find the most specific typedef
 
89
            instancetypedef = object_typedef
 
90
            for w_base in bases_w:
 
91
                if not isinstance(w_base, W_TypeObject):
 
92
                    continue
 
93
                if issubtypedef(w_base.instancetypedef, instancetypedef):
 
94
                    if instancetypedef is not w_base.instancetypedef:
 
95
                        instancetypedef = w_base.instancetypedef
 
96
                        w_self.w_bestbase = w_base
 
97
                elif not issubtypedef(instancetypedef, w_base.instancetypedef):
 
98
                    raise OperationError(space.w_TypeError,
 
99
                                space.wrap("instance layout conflicts in "
 
100
                                                    "multiple inheritance"))
 
101
            if not instancetypedef.acceptable_as_base_class:
 
102
                raise OperationError(space.w_TypeError,
 
103
                                     space.wrap("type '%s' is not an "
 
104
                                                "acceptable base class" %
 
105
                                                instancetypedef.name))
 
106
            w_self.instancetypedef = instancetypedef
 
107
            w_self.hasdict = False
 
108
            w_self.weakrefable = False
 
109
            hasoldstylebase = False
 
110
            w_most_derived_base_with_slots = None
 
111
            w_newstyle = None
 
112
            for w_base in bases_w:
 
113
                if not isinstance(w_base, W_TypeObject):
 
114
                    hasoldstylebase = True
 
115
                    continue
 
116
                if not w_newstyle:
 
117
                    w_newstyle = w_base
 
118
                if w_base.nslots != 0:
 
119
                    if w_most_derived_base_with_slots is None:
 
120
                        w_most_derived_base_with_slots = w_base
 
121
                    else:
 
122
                        if space.is_true(space.issubtype(w_base, w_most_derived_base_with_slots)):
 
123
                            w_most_derived_base_with_slots = w_base
 
124
                        elif not space.is_true(space.issubtype(w_most_derived_base_with_slots, w_base)):
 
125
                            raise OperationError(space.w_TypeError,
 
126
                                                 space.wrap("instance layout conflicts in "
 
127
                                                            "multiple inheritance"))
 
128
                w_self.hasdict = w_self.hasdict or w_base.hasdict
 
129
                w_self.needsdel = w_self.needsdel or w_base.needsdel
 
130
                w_self.weakrefable = w_self.weakrefable or w_base.weakrefable
 
131
            if not w_newstyle: # only classic bases
 
132
                raise OperationError(space.w_TypeError,
 
133
                                     space.wrap("a new-style class can't have only classic bases"))
 
134
 
 
135
            if w_most_derived_base_with_slots:
 
136
                nslots = w_most_derived_base_with_slots.nslots
 
137
                w_self.w_bestbase = w_most_derived_base_with_slots
 
138
            else:
 
139
                nslots = 0
 
140
 
 
141
            if w_self.w_bestbase is None:
 
142
                w_self.w_bestbase = w_newstyle
 
143
 
 
144
            wantdict = True
 
145
            wantweakref = True
 
146
            if '__slots__' in dict_w:
 
147
                wantdict = False
 
148
                wantweakref = False
 
149
 
 
150
                w_slots = dict_w['__slots__']
 
151
                if space.is_true(space.isinstance(w_slots, space.w_str)):
 
152
                    if space.int_w(space.len(w_slots)) == 0:
 
153
                        raise OperationError(space.w_TypeError,
 
154
                                             space.wrap('__slots__ must be identifiers'))
 
155
                    slot_names_w = [w_slots]
 
156
                else:
 
157
                    slot_names_w = space.unpackiterable(w_slots)
 
158
                for w_slot_name in slot_names_w:
 
159
                    slot_name = space.str_w(w_slot_name)
 
160
                    # slot_name should be a valid identifier
 
161
                    if len(slot_name) == 0:
 
162
                        raise OperationError(space.w_TypeError,
 
163
                                             space.wrap('__slots__ must be identifiers'))
 
164
                    first_char = slot_name[0]
 
165
                    if not first_char.isalpha() and first_char != '_':
 
166
                        raise OperationError(space.w_TypeError,
 
167
                                             space.wrap('__slots__ must be identifiers'))                        
 
168
                    for c in slot_name:
 
169
                        if not c.isalnum() and c!= '_':
 
170
                            raise OperationError(space.w_TypeError,
 
171
                                                 space.wrap('__slots__ must be identifiers'))
 
172
                    if slot_name == '__dict__':
 
173
                        if wantdict or w_self.hasdict:
 
174
                            raise OperationError(space.w_TypeError,
 
175
                                                 space.wrap("__dict__ slot disallowed: we already got one"))
 
176
                        wantdict = True
 
177
                    elif slot_name == '__weakref__':
 
178
                        if wantweakref or w_self.weakrefable:
 
179
                            raise OperationError(space.w_TypeError,
 
180
                                                 space.wrap("__weakref__ slot disallowed: we already got one"))
 
181
                                                
 
182
                        wantweakref = True
 
183
                    else:
 
184
                        # create member
 
185
                        slot_name = _mangle(slot_name, name)
 
186
                        # Force interning of slot names.
 
187
                        slot_name = space.str_w(space.new_interned_str(slot_name))
 
188
                        w_self.dict_w[slot_name] = space.wrap(Member(nslots, slot_name, w_self))
 
189
                        nslots += 1
 
190
 
 
191
            w_self.nslots = nslots
 
192
                        
 
193
            wantdict = wantdict or hasoldstylebase
 
194
 
 
195
            if wantdict and not w_self.hasdict:
 
196
                w_self.dict_w['__dict__'] = space.wrap(std_dict_descr)
 
197
                w_self.hasdict = True
 
198
            if '__del__' in dict_w:
 
199
                w_self.needsdel = True
 
200
            if wantweakref and not w_self.weakrefable:
 
201
                w_self.dict_w['__weakref__'] = space.wrap(weakref_descr)
 
202
                w_self.weakrefable = True
 
203
            w_type = space.type(w_self)
 
204
            if not space.is_w(w_type, space.w_type):
 
205
                if space.config.objspace.std.withtypeversion:
 
206
                    w_self.version_tag = None
 
207
                w_self.mro_w = []
 
208
                mro_func = space.lookup(w_self, 'mro')
 
209
                mro_func_args = Arguments(space, [w_self])
 
210
                w_mro = space.call_args(mro_func, mro_func_args)
 
211
                w_self.mro_w = space.unpackiterable(w_mro)
 
212
                return
 
213
        w_self.mro_w = w_self.compute_mro()
 
214
        if space.config.objspace.std.withtypeversion:
 
215
            if w_self.instancetypedef.hasdict:
 
216
                w_self.version_tag = None
 
217
            else:
 
218
                w_self.version_tag = VersionTag()
 
219
 
 
220
    def mutated(w_self):
 
221
        space = w_self.space
 
222
        assert space.config.objspace.std.withtypeversion
 
223
        if w_self.version_tag is not None:
 
224
            w_self.version_tag = VersionTag()
 
225
            subclasses_w = w_self.get_subclasses()
 
226
            for w_subclass in subclasses_w:
 
227
                assert isinstance(w_subclass, W_TypeObject)
 
228
                w_subclass.mutated()
 
229
 
 
230
    def ready(w_self):
 
231
        for w_base in w_self.bases_w:
 
232
            if not isinstance(w_base, W_TypeObject):
 
233
                continue
 
234
            w_base.add_subclass(w_self)
 
235
 
 
236
    # compute the most parent class with the same layout as us
 
237
    def get_layout(w_self):
 
238
        w_bestbase = w_self.w_bestbase
 
239
        if w_bestbase is None: # object
 
240
            return w_self
 
241
        if w_self.instancetypedef is not w_bestbase.instancetypedef:
 
242
            return w_self
 
243
        if w_self.nslots == w_bestbase.nslots:
 
244
            return w_bestbase.get_layout()
 
245
        return w_self
 
246
 
 
247
    # compute a tuple that fully describes the instance layout
 
248
    def get_full_instance_layout(w_self):
 
249
        w_layout = w_self.get_layout()
 
250
        return (w_layout, w_self.hasdict, w_self.needsdel, w_self.weakrefable)
 
251
 
 
252
    def compute_mro(w_self):
 
253
        return compute_C3_mro(w_self.space, w_self)
 
254
 
 
255
    def ensure_static__new__(w_self):
 
256
        # special-case __new__, as in CPython:
 
257
        # if it is a Function, turn it into a static method
 
258
        if '__new__' in w_self.dict_w:
 
259
            w_new = w_self.dict_w['__new__']
 
260
            if isinstance(w_new, Function):
 
261
                w_self.dict_w['__new__'] = StaticMethod(w_new)
 
262
 
 
263
    def getdictvalue(w_self, space, w_attr):
 
264
        return w_self.getdictvalue_w(space, space.str_w(w_attr))
 
265
    
 
266
    def getdictvalue_w(w_self, space, attr):
 
267
        w_value = w_self.dict_w.get(attr, None)
 
268
        if w_self.lazyloaders and w_value is None:
 
269
            if attr in w_self.lazyloaders:
 
270
                w_attr = space.new_interned_str(attr)
 
271
                loader = w_self.lazyloaders[attr]
 
272
                del w_self.lazyloaders[attr]
 
273
                w_value = loader()
 
274
                if w_value is not None:   # None means no such attribute
 
275
                    w_self.dict_w[attr] = w_value
 
276
                    return w_value
 
277
        return w_value
 
278
 
 
279
    def lookup(w_self, name):
 
280
        # note that this doesn't call __get__ on the result at all
 
281
        space = w_self.space
 
282
        if space.config.objspace.std.withmethodcache:
 
283
            return w_self.lookup_where_with_method_cache(name)[1]
 
284
 
 
285
        return w_self._lookup(name)
 
286
 
 
287
    def lookup_where(w_self, name):
 
288
        space = w_self.space
 
289
        if space.config.objspace.std.withmethodcache:
 
290
            return w_self.lookup_where_with_method_cache(name)
 
291
 
 
292
        return w_self._lookup_where(name)
 
293
 
 
294
    def _lookup(w_self, key):
 
295
        space = w_self.space
 
296
        for w_class in w_self.mro_w:
 
297
            w_value = w_class.getdictvalue_w(space, key)
 
298
            if w_value is not None:
 
299
                return w_value
 
300
        return None
 
301
 
 
302
    def _lookup_where(w_self, key):
 
303
        # like lookup() but also returns the parent class in which the
 
304
        # attribute was found
 
305
        space = w_self.space
 
306
        for w_class in w_self.mro_w:
 
307
            w_value = w_class.getdictvalue_w(space, key)
 
308
            if w_value is not None:
 
309
                return w_class, w_value
 
310
        return None, None
 
311
 
 
312
    def lookup_where_with_method_cache(w_self, name):
 
313
        space = w_self.space
 
314
        assert space.config.objspace.std.withmethodcache
 
315
        ec = space.getexecutioncontext()
 
316
        version_tag = w_self.version_tag
 
317
        if version_tag is None:
 
318
            tup = w_self._lookup_where(name)
 
319
            return tup
 
320
        SHIFT = r_uint.BITS - space.config.objspace.std.methodcachesizeexp
 
321
        method_hash = r_uint(intmask(id(version_tag) * hash(name))) >> SHIFT
 
322
        cached_version_tag = ec.method_cache_versions[method_hash]
 
323
        if cached_version_tag is version_tag:
 
324
            cached_name = ec.method_cache_names[method_hash]
 
325
            if cached_name is name:
 
326
                tup = ec.method_cache_lookup_where[method_hash]
 
327
                if space.config.objspace.std.withmethodcachecounter:
 
328
                    ec.method_cache_hits[name] = \
 
329
                            ec.method_cache_hits.get(name, 0) + 1
 
330
#                print "hit", w_self, name
 
331
                return tup
 
332
        tup = w_self._lookup_where(name)
 
333
        ec.method_cache_versions[method_hash] = version_tag
 
334
        ec.method_cache_names[method_hash] = name
 
335
        ec.method_cache_lookup_where[method_hash] = tup
 
336
        if space.config.objspace.std.withmethodcachecounter:
 
337
            ec.method_cache_misses[name] = \
 
338
                    ec.method_cache_misses.get(name, 0) + 1
 
339
#        print "miss", w_self, name
 
340
        return tup
 
341
 
 
342
    def check_user_subclass(w_self, w_subtype):
 
343
        space = w_self.space
 
344
        if not isinstance(w_subtype, W_TypeObject):
 
345
            raise OperationError(space.w_TypeError,
 
346
                space.wrap("X is not a type object (%s)" % (
 
347
                    space.type(w_subtype).getname(space, '?'))))
 
348
        if not space.is_true(space.issubtype(w_subtype, w_self)):
 
349
            raise OperationError(space.w_TypeError,
 
350
                space.wrap("%s.__new__(%s): %s is not a subtype of %s" % (
 
351
                    w_self.name, w_subtype.name, w_subtype.name, w_self.name)))
 
352
        if w_self.instancetypedef is not w_subtype.instancetypedef:
 
353
            raise OperationError(space.w_TypeError,
 
354
                space.wrap("%s.__new__(%s) is not safe, use %s.__new__()" % (
 
355
                    w_self.name, w_subtype.name, w_subtype.name)))
 
356
        return w_subtype
 
357
 
 
358
    def _freeze_(w_self):
 
359
        "NOT_RPYTHON.  Forces the lazy attributes to be computed."
 
360
        if 'lazyloaders' in w_self.__dict__:
 
361
            for attr in w_self.lazyloaders.keys():
 
362
                w_self.getdictvalue_w(w_self.space, attr)
 
363
            del w_self.lazyloaders
 
364
        return False
 
365
 
 
366
    def getdict(w_self): # returning a dict-proxy!
 
367
        if w_self.lazyloaders:
 
368
            w_self._freeze_()    # force un-lazification
 
369
        space = w_self.space
 
370
        dictspec = []
 
371
        for key, w_value in w_self.dict_w.items():
 
372
            dictspec.append((space.wrap(key), w_value))
 
373
        newdic = space.newdict()
 
374
        newdic.initialize_content(dictspec)
 
375
        return W_DictProxyObject(newdic)
 
376
 
 
377
    def unwrap(w_self, space):
 
378
        if w_self.instancetypedef.fakedcpytype is not None:
 
379
            return w_self.instancetypedef.fakedcpytype
 
380
        from pypy.objspace.std.model import UnwrapError
 
381
        raise UnwrapError(w_self)
 
382
 
 
383
    def is_heaptype(w_self):
 
384
        w_self = hint(w_self, deepfreeze=True)
 
385
        return w_self.__flags__&_HEAPTYPE
 
386
 
 
387
    def get_module(w_self):
 
388
        space = w_self.space
 
389
        if w_self.is_heaptype() and '__module__' in w_self.dict_w:
 
390
            return w_self.dict_w['__module__']
 
391
        else:
 
392
            # for non-heap types, CPython checks for a module.name in the
 
393
            # type name.  That's a hack, so we're allowed to use a different
 
394
            # hack...
 
395
            if ('__module__' in w_self.dict_w and
 
396
                space.is_true(space.isinstance(w_self.dict_w['__module__'],
 
397
                                               space.w_str))):
 
398
                return w_self.dict_w['__module__']
 
399
            return space.wrap('__builtin__')
 
400
 
 
401
    def add_subclass(w_self, w_subclass):
 
402
        space = w_self.space
 
403
        from pypy.module._weakref.interp__weakref import basic_weakref
 
404
        w_newref = basic_weakref(space, w_subclass)
 
405
        
 
406
        for i in range(len(w_self.weak_subclasses_w)):
 
407
            w_ref = w_self.weak_subclasses_w[i]
 
408
            ob = space.call_function(w_ref)
 
409
            if space.is_w(ob, space.w_None):
 
410
                w_self.weak_subclasses_w[i] = w_newref
 
411
                return
 
412
        else:
 
413
            w_self.weak_subclasses_w.append(w_newref)
 
414
 
 
415
    def remove_subclass(w_self, w_subclass):
 
416
        space = w_self.space
 
417
 
 
418
        for i in range(len(w_self.weak_subclasses_w)):
 
419
            w_ref = w_self.weak_subclasses_w[i]
 
420
            ob = space.call_function(w_ref)
 
421
            if space.is_w(ob, w_subclass):
 
422
                del w_self.weak_subclasses_w[i]
 
423
                return
 
424
 
 
425
    def get_subclasses(w_self):
 
426
        space = w_self.space
 
427
        subclasses_w = []
 
428
        for w_ref in w_self.weak_subclasses_w:
 
429
            w_ob = space.call_function(w_ref)
 
430
            if not space.is_w(w_ob, space.w_None):
 
431
                subclasses_w.append(w_ob)
 
432
        return subclasses_w
 
433
 
 
434
 
 
435
    # for now, weakref support for W_TypeObject is hard to get automatically
 
436
    _lifeline_ = None
 
437
    def getweakref(self):
 
438
        return self._lifeline_
 
439
    def setweakref(self, space, weakreflifeline):
 
440
        self._lifeline_ = weakreflifeline
 
441
 
 
442
 
 
443
def call__Type(space, w_type, __args__):
 
444
    # special case for type(x)
 
445
    if space.is_w(w_type, space.w_type):
 
446
        try:
 
447
            w_obj, = __args__.fixedunpack(1)
 
448
        except ValueError:
 
449
            pass
 
450
        else:
 
451
            return space.type(w_obj)
 
452
    # invoke the __new__ of the type
 
453
    w_newfunc = space.getattr(w_type, space.wrap('__new__'))
 
454
    w_newobject = space.call_args(w_newfunc, __args__.prepend(w_type))
 
455
    # maybe invoke the __init__ of the type
 
456
    if space.is_true(space.isinstance(w_newobject, w_type)):
 
457
        w_descr = space.lookup(w_newobject, '__init__')
 
458
        w_result = space.get_and_call_args(w_descr, w_newobject, __args__)
 
459
##         if not space.is_w(w_result, space.w_None):
 
460
##             raise OperationError(space.w_TypeError,
 
461
##                                  space.wrap("__init__() should return None"))
 
462
    return w_newobject
 
463
 
 
464
def issubtype__Type_Type(space, w_type1, w_type2):
 
465
    return space.newbool(w_type2 in w_type1.mro_w)
 
466
 
 
467
def repr__Type(space, w_obj):
 
468
    w_mod = w_obj.get_module()
 
469
    if not space.is_true(space.isinstance(w_mod, space.w_str)):
 
470
        mod = None
 
471
    else:
 
472
        mod = space.str_w(w_mod)
 
473
    if not w_obj.is_heaptype() or (mod is not None and mod == '__builtin__'):
 
474
        kind = 'type'
 
475
    else:
 
476
        kind = 'class'
 
477
    if mod is not None and mod !='__builtin__':
 
478
        return space.wrap("<%s '%s.%s'>" % (kind, mod, w_obj.name))
 
479
    else:
 
480
        return space.wrap("<%s '%s'>" % (kind, w_obj.name))
 
481
 
 
482
def getattr__Type_ANY(space, w_type, w_name):
 
483
    name = space.str_w(w_name)
 
484
    w_descr = space.lookup(w_type, name)
 
485
    if w_descr is not None:
 
486
        if space.is_data_descr(w_descr):
 
487
            return space.get(w_descr,w_type)
 
488
    w_value = w_type.lookup(name)
 
489
    if w_value is not None:
 
490
        # __get__(None, type): turns e.g. functions into unbound methods
 
491
        return space.get(w_value, space.w_None, w_type)
 
492
    if w_descr is not None:
 
493
        return space.get(w_descr,w_type)
 
494
    msg = "type object '%s' has no attribute '%s'" %(w_type.name, name)
 
495
    raise OperationError(space.w_AttributeError, space.wrap(msg))
 
496
 
 
497
def setattr__Type_ANY_ANY(space, w_type, w_name, w_value):
 
498
    # Note. This is exactly the same thing as descroperation.descr__setattr__,
 
499
    # but it is needed at bootstrap to avoid a call to w_type.getdict() which
 
500
    # would un-lazify the whole type.
 
501
    if space.config.objspace.std.withtypeversion:
 
502
        w_type.mutated()
 
503
    name = space.str_w(w_name)
 
504
    w_descr = space.lookup(w_type, name)
 
505
    if w_descr is not None:
 
506
        if space.is_data_descr(w_descr):
 
507
            space.set(w_descr, w_type, w_value)
 
508
            return
 
509
    
 
510
    if not w_type.is_heaptype():
 
511
        msg = "can't set attributes on type object '%s'" %(w_type.name,)
 
512
        raise OperationError(space.w_TypeError, space.wrap(msg))
 
513
    w_type.dict_w[name] = w_value
 
514
 
 
515
def delattr__Type_ANY(space, w_type, w_name):
 
516
    if space.config.objspace.std.withtypeversion:
 
517
        w_type.mutated()
 
518
    if w_type.lazyloaders:
 
519
        w_type._freeze_()    # force un-lazification
 
520
    name = space.str_w(w_name)
 
521
    w_descr = space.lookup(w_type, name)
 
522
    if w_descr is not None:
 
523
        if space.is_data_descr(w_descr):
 
524
            space.delete(w_descr, w_type)
 
525
            return
 
526
    if not w_type.is_heaptype():
 
527
        msg = "can't delete attributes on type object '%s'" %(w_type.name,)
 
528
        raise OperationError(space.w_TypeError, space.wrap(msg))
 
529
    try:
 
530
        del w_type.dict_w[name]
 
531
        return
 
532
    except KeyError:
 
533
        raise OperationError(space.w_AttributeError, w_name)
 
534
 
 
535
 
 
536
# ____________________________________________________________
 
537
 
 
538
 
 
539
abstract_mro = gateway.applevel("""
 
540
    def abstract_mro(klass):
 
541
        # abstract/classic mro
 
542
        mro = []
 
543
        stack = [klass]
 
544
        while stack:
 
545
            klass = stack.pop()
 
546
            if klass not in mro:
 
547
                mro.append(klass)
 
548
                if not isinstance(klass.__bases__, tuple):
 
549
                    raise TypeError, '__bases__ must be a tuple'
 
550
                stack += klass.__bases__[::-1]
 
551
        return mro
 
552
""", filename=__file__).interphook("abstract_mro")
 
553
 
 
554
def get_mro(space, klass):
 
555
    if isinstance(klass, W_TypeObject):
 
556
        return list(klass.mro_w)
 
557
    else:
 
558
        return space.unpackiterable(abstract_mro(space, klass))
 
559
 
 
560
 
 
561
def compute_C3_mro(space, cls):
 
562
    order = []
 
563
    orderlists = [get_mro(space, base) for base in cls.bases_w]
 
564
    orderlists.append([cls] + cls.bases_w)
 
565
    while orderlists:
 
566
        for candidatelist in orderlists:
 
567
            candidate = candidatelist[0]
 
568
            if mro_blockinglist(candidate, orderlists) is GOODCANDIDATE:
 
569
                break    # good candidate
 
570
        else:
 
571
            return mro_error(space, orderlists)  # no candidate found
 
572
        assert candidate not in order
 
573
        order.append(candidate)
 
574
        for i in range(len(orderlists)-1, -1, -1):
 
575
            if orderlists[i][0] == candidate:
 
576
                del orderlists[i][0]
 
577
                if len(orderlists[i]) == 0:
 
578
                    del orderlists[i]
 
579
    return order
 
580
 
 
581
GOODCANDIDATE = []
 
582
 
 
583
def mro_blockinglist(candidate, orderlists):
 
584
    for lst in orderlists:
 
585
        if candidate in lst[1:]:
 
586
            return lst
 
587
    return GOODCANDIDATE # good candidate
 
588
 
 
589
def mro_error(space, orderlists):
 
590
    cycle = []
 
591
    candidate = orderlists[-1][0]
 
592
    if candidate in orderlists[-1][1:]:
 
593
        # explicit error message for this specific case
 
594
        raise OperationError(space.w_TypeError,
 
595
            space.wrap("duplicate base class " + candidate.getname(space,"?")))
 
596
    while candidate not in cycle:
 
597
        cycle.append(candidate)
 
598
        nextblockinglist = mro_blockinglist(candidate, orderlists)
 
599
        candidate = nextblockinglist[0]
 
600
    del cycle[:cycle.index(candidate)]
 
601
    cycle.append(candidate)
 
602
    cycle.reverse()
 
603
    names = [cls.getname(space, "?") for cls in cycle]
 
604
    raise OperationError(space.w_TypeError,
 
605
        space.wrap("cycle among base classes: " + ' < '.join(names)))
 
606
 
 
607
# ____________________________________________________________
 
608
 
 
609
register_all(vars())