PageRenderTime 223ms CodeModel.GetById 60ms app.highlight 136ms RepoModel.GetById 15ms app.codeStats 1ms

/pypy/objspace/descroperation.py

https://bitbucket.org/pypy/pypy/
Python | 874 lines | 699 code | 91 blank | 84 comment | 182 complexity | 2e46d9cdc344cfcc826886a738febf1e MD5 | raw file
  1import operator
  2from pypy.interpreter.error import OperationError, oefmt
  3from pypy.interpreter.baseobjspace import ObjSpace
  4from pypy.interpreter.function import Function, Method, FunctionWithFixedCode
  5from pypy.interpreter.argument import Arguments
  6from pypy.interpreter.typedef import default_identity_hash
  7from rpython.tool.sourcetools import compile2, func_with_new_name
  8from pypy.module.__builtin__.interp_classobj import W_InstanceObject
  9from rpython.rlib.objectmodel import specialize
 10from rpython.rlib import jit
 11
 12def object_getattribute(space):
 13    "Utility that returns the app-level descriptor object.__getattribute__."
 14    w_src, w_getattribute = space.lookup_in_type_where(space.w_object,
 15                                                       '__getattribute__')
 16    return w_getattribute
 17object_getattribute._annspecialcase_ = 'specialize:memo'
 18
 19def object_setattr(space):
 20    "Utility that returns the app-level descriptor object.__setattr__."
 21    w_src, w_setattr = space.lookup_in_type_where(space.w_object,
 22                                                  '__setattr__')
 23    return w_setattr
 24object_setattr._annspecialcase_ = 'specialize:memo'
 25
 26def object_delattr(space):
 27    "Utility that returns the app-level descriptor object.__delattr__."
 28    w_src, w_delattr = space.lookup_in_type_where(space.w_object,
 29                                                  '__delattr__')
 30    return w_delattr
 31object_delattr._annspecialcase_ = 'specialize:memo'
 32
 33def object_hash(space):
 34    "Utility that returns the app-level descriptor object.__hash__."
 35    w_src, w_hash = space.lookup_in_type_where(space.w_object,
 36                                                  '__hash__')
 37    return w_hash
 38object_hash._annspecialcase_ = 'specialize:memo'
 39
 40def type_eq(space):
 41    "Utility that returns the app-level descriptor type.__eq__."
 42    w_src, w_eq = space.lookup_in_type_where(space.w_type,
 43                                             '__eq__')
 44    return w_eq
 45type_eq._annspecialcase_ = 'specialize:memo'
 46
 47def list_iter(space):
 48    "Utility that returns the app-level descriptor list.__iter__."
 49    w_src, w_iter = space.lookup_in_type_where(space.w_list,
 50                                               '__iter__')
 51    return w_iter
 52list_iter._annspecialcase_ = 'specialize:memo'
 53
 54def tuple_iter(space):
 55    "Utility that returns the app-level descriptor tuple.__iter__."
 56    w_src, w_iter = space.lookup_in_type_where(space.w_tuple,
 57                                               '__iter__')
 58    return w_iter
 59tuple_iter._annspecialcase_ = 'specialize:memo'
 60
 61def raiseattrerror(space, w_obj, name, w_descr=None):
 62    if w_descr is None:
 63        raise oefmt(space.w_AttributeError,
 64                    "'%T' object has no attribute '%s'", w_obj, name)
 65    else:
 66        raise oefmt(space.w_AttributeError,
 67                    "'%T' object attribute '%s' is read-only", w_obj, name)
 68
 69# Helpers for old-style and mix-style mixup
 70
 71def _same_class_w(space, w_obj1, w_obj2, w_typ1, w_typ2):
 72    if (space.is_oldstyle_instance(w_obj1) and
 73        space.is_oldstyle_instance(w_obj2)):
 74        assert isinstance(w_obj1, W_InstanceObject)
 75        assert isinstance(w_obj2, W_InstanceObject)
 76        return space.is_w(w_obj1.w_class, w_obj2.w_class)
 77    return space.is_w(w_typ1, w_typ2)
 78
 79
 80class Object(object):
 81    def descr__getattribute__(space, w_obj, w_name):
 82        name = space.str_w(w_name)
 83        w_descr = space.lookup(w_obj, name)
 84        if w_descr is not None:
 85            if space.is_data_descr(w_descr):
 86                # Only override if __get__ is defined, too, for compatibility
 87                # with CPython.
 88                w_get = space.lookup(w_descr, "__get__")
 89                if w_get is not None:
 90                    w_type = space.type(w_obj)
 91                    return space.get_and_call_function(w_get, w_descr, w_obj,
 92                                                       w_type)
 93        w_value = w_obj.getdictvalue(space, name)
 94        if w_value is not None:
 95            return w_value
 96        if w_descr is not None:
 97            return space.get(w_descr, w_obj)
 98        raiseattrerror(space, w_obj, name)
 99
100    def descr__setattr__(space, w_obj, w_name, w_value):
101        name = space.str_w(w_name)
102        w_descr = space.lookup(w_obj, name)
103        if w_descr is not None:
104            if space.is_data_descr(w_descr):
105                space.set(w_descr, w_obj, w_value)
106                return
107        if w_obj.setdictvalue(space, name, w_value):
108            return
109        raiseattrerror(space, w_obj, name, w_descr)
110
111    def descr__delattr__(space, w_obj, w_name):
112        name = space.str_w(w_name)
113        w_descr = space.lookup(w_obj, name)
114        if w_descr is not None:
115            if space.is_data_descr(w_descr):
116                space.delete(w_descr, w_obj)
117                return
118        if w_obj.deldictvalue(space, name):
119            return
120        raiseattrerror(space, w_obj, name, w_descr)
121
122    def descr__init__(space, w_obj, __args__):
123        pass
124
125contains_jitdriver = jit.JitDriver(name='contains',
126        greens=['w_type'], reds='auto')
127
128class DescrOperation(object):
129    # This is meant to be a *mixin*.
130
131    def is_data_descr(space, w_obj):
132        return (space.lookup(w_obj, '__set__') is not None or
133                space.lookup(w_obj, '__delete__') is not None)
134
135    def get_and_call_args(space, w_descr, w_obj, args):
136        # a special case for performance and to avoid infinite recursion
137        if isinstance(w_descr, Function):
138            return w_descr.call_obj_args(w_obj, args)
139        else:
140            w_impl = space.get(w_descr, w_obj)
141            return space.call_args(w_impl, args)
142
143    def get_and_call_function(space, w_descr, w_obj, *args_w):
144        typ = type(w_descr)
145        # a special case for performance and to avoid infinite recursion
146        if typ is Function or typ is FunctionWithFixedCode:
147            # isinstance(typ, Function) would not be correct here:
148            # for a BuiltinFunction we must not use that shortcut, because a
149            # builtin function binds differently than a normal function
150            # see test_builtin_as_special_method_is_not_bound
151            # in interpreter/test/test_function.py
152
153            # the fastcall paths are purely for performance, but the resulting
154            # increase of speed is huge
155            return w_descr.funccall(w_obj, *args_w)
156        else:
157            args = Arguments(space, list(args_w))
158            w_impl = space.get(w_descr, w_obj)
159            return space.call_args(w_impl, args)
160
161    def call_args(space, w_obj, args):
162        # two special cases for performance
163        if isinstance(w_obj, Function):
164            return w_obj.call_args(args)
165        if isinstance(w_obj, Method):
166            return w_obj.call_args(args)
167        w_descr = space.lookup(w_obj, '__call__')
168        if w_descr is None:
169            raise oefmt(space.w_TypeError,
170                        "'%T' object is not callable", w_obj)
171        return space.get_and_call_args(w_descr, w_obj, args)
172
173    def get(space, w_descr, w_obj, w_type=None):
174        w_get = space.lookup(w_descr, '__get__')
175        if w_get is None:
176            return w_descr
177        if w_type is None:
178            w_type = space.type(w_obj)
179        return space.get_and_call_function(w_get, w_descr, w_obj, w_type)
180
181    def set(space, w_descr, w_obj, w_val):
182        w_set = space.lookup(w_descr, '__set__')
183        if w_set is None:
184            raise oefmt(space.w_AttributeError,
185                        "'%T' object is not a descriptor with set", w_descr)
186        return space.get_and_call_function(w_set, w_descr, w_obj, w_val)
187
188    def delete(space, w_descr, w_obj):
189        w_delete = space.lookup(w_descr, '__delete__')
190        if w_delete is None:
191            raise oefmt(space.w_AttributeError,
192                        "'%T' object is not a descriptor with delete", w_descr)
193        return space.get_and_call_function(w_delete, w_descr, w_obj)
194
195    def getattr(space, w_obj, w_name):
196        # may be overridden in StdObjSpace
197        w_descr = space.lookup(w_obj, '__getattribute__')
198        return space._handle_getattribute(w_descr, w_obj, w_name)
199
200    def _handle_getattribute(space, w_descr, w_obj, w_name):
201        try:
202            if w_descr is None:   # obscure case
203                raise OperationError(space.w_AttributeError, space.w_None)
204            return space.get_and_call_function(w_descr, w_obj, w_name)
205        except OperationError as e:
206            if not e.match(space, space.w_AttributeError):
207                raise
208            w_descr = space.lookup(w_obj, '__getattr__')
209            if w_descr is None:
210                raise
211            return space.get_and_call_function(w_descr, w_obj, w_name)
212
213    def setattr(space, w_obj, w_name, w_val):
214        w_descr = space.lookup(w_obj, '__setattr__')
215        if w_descr is None:
216            raise oefmt(space.w_AttributeError,
217                        "'%T' object is readonly", w_obj)
218        return space.get_and_call_function(w_descr, w_obj, w_name, w_val)
219
220    def delattr(space, w_obj, w_name):
221        w_descr = space.lookup(w_obj, '__delattr__')
222        if w_descr is None:
223            raise oefmt(space.w_AttributeError,
224                        "'%T' object does not support attribute removal",
225                        w_obj)
226        return space.get_and_call_function(w_descr, w_obj, w_name)
227
228    def is_true(space, w_obj):
229        w_descr = space.lookup(w_obj, "__nonzero__")
230        if w_descr is None:
231            w_descr = space.lookup(w_obj, "__len__")
232            if w_descr is None:
233                return True
234            # call __len__
235            w_res = space.get_and_call_function(w_descr, w_obj)
236            return space._check_len_result(w_res) != 0
237        # call __nonzero__
238        w_res = space.get_and_call_function(w_descr, w_obj)
239        # more shortcuts for common cases
240        if space.is_w(w_res, space.w_False):
241            return False
242        if space.is_w(w_res, space.w_True):
243            return True
244        w_restype = space.type(w_res)
245        # Note there is no check for bool here because the only possible
246        # instances of bool are w_False and w_True, which are checked above.
247        if space.is_w(w_restype, space.w_int):
248            return space.int_w(w_res) != 0
249        else:
250            raise oefmt(space.w_TypeError,
251                        "__nonzero__ should return bool or integer")
252
253    def nonzero(space, w_obj):
254        if space.is_true(w_obj):
255            return space.w_True
256        else:
257            return space.w_False
258
259    def len(space, w_obj):
260        w_descr = space.lookup(w_obj, '__len__')
261        if w_descr is None:
262            raise oefmt(space.w_TypeError, "'%T' has no length", w_obj)
263        w_res = space.get_and_call_function(w_descr, w_obj)
264        return space.wrap(space._check_len_result(w_res))
265
266    def _check_len_result(space, w_obj):
267        # Will complain if result is too big.
268        result = space.int_w(space.int(w_obj))
269        if result < 0:
270            raise oefmt(space.w_ValueError, "__len__() should return >= 0")
271        return result
272
273    def iter(space, w_obj):
274        w_descr = space.lookup(w_obj, '__iter__')
275        if w_descr is None:
276            if space.type(w_obj).flag_map_or_seq != 'M':
277                w_descr = space.lookup(w_obj, '__getitem__')
278            if w_descr is None:
279                raise oefmt(space.w_TypeError,
280                            "'%T' object is not iterable", w_obj)
281            return space.newseqiter(w_obj)
282        w_iter = space.get_and_call_function(w_descr, w_obj)
283        w_next = space.lookup(w_iter, 'next')
284        if w_next is None:
285            raise oefmt(space.w_TypeError, "iter() returned non-iterator")
286        return w_iter
287
288    def next(space, w_obj):
289        w_descr = space.lookup(w_obj, 'next')
290        if w_descr is None:
291            raise oefmt(space.w_TypeError,
292                        "'%T' object is not an iterator", w_obj)
293        return space.get_and_call_function(w_descr, w_obj)
294
295    def getitem(space, w_obj, w_key):
296        w_descr = space.lookup(w_obj, '__getitem__')
297        if w_descr is None:
298            raise oefmt(space.w_TypeError,
299                        "'%T' object is not subscriptable", w_obj)
300        return space.get_and_call_function(w_descr, w_obj, w_key)
301
302    def setitem(space, w_obj, w_key, w_val):
303        w_descr = space.lookup(w_obj, '__setitem__')
304        if w_descr is None:
305            raise oefmt(space.w_TypeError,
306                        "'%T' object does not support item assignment", w_obj)
307        return space.get_and_call_function(w_descr, w_obj, w_key, w_val)
308
309    def delitem(space, w_obj, w_key):
310        w_descr = space.lookup(w_obj, '__delitem__')
311        if w_descr is None:
312            raise oefmt(space.w_TypeError,
313                        "'%T' object does not support item deletion", w_obj)
314        return space.get_and_call_function(w_descr, w_obj, w_key)
315
316    def getslice(space, w_obj, w_start, w_stop):
317        w_descr = space.lookup(w_obj, '__getslice__')
318        if w_descr is None:
319            w_slice = space.newslice(w_start, w_stop, space.w_None)
320            return space.getitem(w_obj, w_slice)
321        w_start, w_stop = old_slice_range(space, w_obj, w_start, w_stop)
322        return space.get_and_call_function(w_descr, w_obj, w_start, w_stop)
323
324    def setslice(space, w_obj, w_start, w_stop, w_sequence):
325        w_descr = space.lookup(w_obj, '__setslice__')
326        if w_descr is None:
327            w_slice = space.newslice(w_start, w_stop, space.w_None)
328            return space.setitem(w_obj, w_slice, w_sequence)
329        w_start, w_stop = old_slice_range(space, w_obj, w_start, w_stop)
330        return space.get_and_call_function(w_descr, w_obj, w_start, w_stop, w_sequence)
331
332    def delslice(space, w_obj, w_start, w_stop):
333        w_descr = space.lookup(w_obj, '__delslice__')
334        if w_descr is None:
335            w_slice = space.newslice(w_start, w_stop, space.w_None)
336            return space.delitem(w_obj, w_slice)
337        w_start, w_stop = old_slice_range(space, w_obj, w_start, w_stop)
338        return space.get_and_call_function(w_descr, w_obj, w_start, w_stop)
339
340    def format(space, w_obj, w_format_spec):
341        w_descr = space.lookup(w_obj, '__format__')
342        if w_descr is None:
343            raise oefmt(space.w_TypeError,
344                        "'%T' object does not define __format__", w_obj)
345        w_res = space.get_and_call_function(w_descr, w_obj, w_format_spec)
346        if not space.isinstance_w(w_res, space.w_basestring):
347            raise oefmt(space.w_TypeError,
348                        "%T.__format__ must return string or unicode, not %T",
349                        w_obj, w_res)
350        return w_res
351
352    def pow(space, w_obj1, w_obj2, w_obj3):
353        w_typ1 = space.type(w_obj1)
354        w_typ2 = space.type(w_obj2)
355        w_left_src, w_left_impl = space.lookup_in_type_where(w_typ1, '__pow__')
356        if _same_class_w(space, w_obj1, w_obj2, w_typ1, w_typ2):
357            w_right_impl = None
358        else:
359            w_right_src, w_right_impl = space.lookup_in_type_where(w_typ2, '__rpow__')
360            # sse binop_impl
361            if (w_left_src is not w_right_src
362                and space.issubtype_w(w_typ2, w_typ1)):
363                if (w_left_src and w_right_src and
364                    not space.abstract_issubclass_w(w_left_src, w_right_src) and
365                    not space.abstract_issubclass_w(w_typ1, w_right_src)):
366                    w_obj1, w_obj2 = w_obj2, w_obj1
367                    w_left_impl, w_right_impl = w_right_impl, w_left_impl
368        if w_left_impl is not None:
369            if space.is_w(w_obj3, space.w_None):
370                w_res = space.get_and_call_function(w_left_impl, w_obj1, w_obj2)
371            else:
372                w_res = space.get_and_call_function(w_left_impl, w_obj1, w_obj2, w_obj3)
373            if _check_notimplemented(space, w_res):
374                return w_res
375        if w_right_impl is not None:
376            if space.is_w(w_obj3, space.w_None):
377                w_res = space.get_and_call_function(w_right_impl, w_obj2, w_obj1)
378            else:
379                w_res = space.get_and_call_function(w_right_impl, w_obj2, w_obj1,
380                                                   w_obj3)
381            if _check_notimplemented(space, w_res):
382                return w_res
383
384        raise oefmt(space.w_TypeError, "operands do not support **")
385
386    def inplace_pow(space, w_lhs, w_rhs):
387        w_impl = space.lookup(w_lhs, '__ipow__')
388        if w_impl is not None:
389            w_res = space.get_and_call_function(w_impl, w_lhs, w_rhs)
390            if _check_notimplemented(space, w_res):
391                return w_res
392        return space.pow(w_lhs, w_rhs, space.w_None)
393
394    def contains(space, w_container, w_item):
395        w_descr = space.lookup(w_container, '__contains__')
396        if w_descr is not None:
397            w_result = space.get_and_call_function(w_descr, w_container, w_item)
398            return space.nonzero(w_result)
399        return space._contains(w_container, w_item)
400
401    def _contains(space, w_container, w_item):
402        w_iter = space.iter(w_container)
403        w_type = space.type(w_iter)
404        while 1:
405            contains_jitdriver.jit_merge_point(w_type=w_type)
406            try:
407                w_next = space.next(w_iter)
408            except OperationError as e:
409                if not e.match(space, space.w_StopIteration):
410                    raise
411                return space.w_False
412            if space.eq_w(w_item, w_next):
413                return space.w_True
414
415    def hash(space, w_obj):
416        w_hash = space.lookup(w_obj, '__hash__')
417        if w_hash is None:
418            # xxx there used to be logic about "do we have __eq__ or __cmp__"
419            # here, but it does not really make sense, as 'object' has a
420            # default __hash__.  This path should only be taken under very
421            # obscure circumstances.
422            return default_identity_hash(space, w_obj)
423        if space.is_w(w_hash, space.w_None):
424            raise oefmt(space.w_TypeError,
425                        "'%T' objects are unhashable", w_obj)
426        w_result = space.get_and_call_function(w_hash, w_obj)
427
428        # issue 2346 : returns now -2 for hashing -1 like cpython
429        if space.isinstance_w(w_result, space.w_int):
430            h = space.int_w(w_result)
431        elif space.isinstance_w(w_result, space.w_long):
432            bigint = space.bigint_w(w_result)
433            h = bigint.hash()
434        else:
435            raise oefmt(space.w_TypeError,
436                        "__hash__() should return an int or long")
437        # turn -1 into -2 without using a condition, which would
438        # create a potential bridge in the JIT
439        h -= (h == -1)
440        return space.wrap(h)
441
442    def cmp(space, w_v, w_w):
443
444        if space.is_w(w_v, w_w):
445            return space.wrap(0)
446
447        # The real comparison
448        if space.is_w(space.type(w_v), space.type(w_w)):
449            # for object of the same type, prefer __cmp__ over rich comparison.
450            w_cmp = space.lookup(w_v, '__cmp__')
451            w_res = _invoke_binop(space, w_cmp, w_v, w_w)
452            if w_res is not None:
453                return w_res
454        # fall back to rich comparison.
455        if space.eq_w(w_v, w_w):
456            return space.wrap(0)
457        elif space.is_true(space.lt(w_v, w_w)):
458            return space.wrap(-1)
459        return space.wrap(1)
460
461    def coerce(space, w_obj1, w_obj2):
462        w_res = space.try_coerce(w_obj1, w_obj2)
463        if w_res is None:
464            raise oefmt(space.w_TypeError, "coercion failed")
465        return w_res
466
467    def try_coerce(space, w_obj1, w_obj2):
468        """Returns a wrapped 2-tuple or a real None if it failed."""
469        w_typ1 = space.type(w_obj1)
470        w_typ2 = space.type(w_obj2)
471        w_left_src, w_left_impl = space.lookup_in_type_where(w_typ1, '__coerce__')
472        if space.is_w(w_typ1, w_typ2):
473            w_right_impl = None
474        else:
475            w_right_src, w_right_impl = space.lookup_in_type_where(w_typ2, '__coerce__')
476            if (w_left_src is not w_right_src
477                and space.issubtype_w(w_typ2, w_typ1)):
478                w_obj1, w_obj2 = w_obj2, w_obj1
479                w_left_impl, w_right_impl = w_right_impl, w_left_impl
480
481        w_res = _invoke_binop(space, w_left_impl, w_obj1, w_obj2)
482        if w_res is None or space.is_w(w_res, space.w_None):
483            w_res = _invoke_binop(space, w_right_impl, w_obj2, w_obj1)
484            if w_res is None  or space.is_w(w_res, space.w_None):
485                return None
486            if (not space.isinstance_w(w_res, space.w_tuple) or
487                space.len_w(w_res) != 2):
488                raise oefmt(space.w_TypeError,
489                            "coercion should return None or 2-tuple")
490            w_res = space.newtuple([space.getitem(w_res, space.wrap(1)), space.getitem(w_res, space.wrap(0))])
491        elif (not space.isinstance_w(w_res, space.w_tuple) or
492            space.len_w(w_res) != 2):
493            raise oefmt(space.w_TypeError,
494                        "coercion should return None or 2-tuple")
495        return w_res
496
497    def issubtype_w(space, w_sub, w_type):
498        return space._type_issubtype(w_sub, w_type)
499
500    def issubtype(space, w_sub, w_type):
501        return space.wrap(space._type_issubtype(w_sub, w_type))
502
503    @specialize.arg_or_var(2)
504    def isinstance_w(space, w_inst, w_type):
505        return space._type_isinstance(w_inst, w_type)
506
507    @specialize.arg_or_var(2)
508    def isinstance(space, w_inst, w_type):
509        return space.wrap(space.isinstance_w(w_inst, w_type))
510
511
512# helpers
513
514def _check_notimplemented(space, w_obj):
515    return not space.is_w(w_obj, space.w_NotImplemented)
516
517def _invoke_binop(space, w_impl, w_obj1, w_obj2):
518    if w_impl is not None:
519        w_res = space.get_and_call_function(w_impl, w_obj1, w_obj2)
520        if _check_notimplemented(space, w_res):
521            return w_res
522    return None
523
524# helper for invoking __cmp__
525
526def _conditional_neg(space, w_obj, flag):
527    if flag:
528        return space.neg(w_obj)
529    else:
530        return w_obj
531
532def _cmp(space, w_obj1, w_obj2, symbol):
533    w_typ1 = space.type(w_obj1)
534    w_typ2 = space.type(w_obj2)
535    w_left_src, w_left_impl = space.lookup_in_type_where(w_typ1, '__cmp__')
536    do_neg1 = False
537    do_neg2 = True
538    if space.is_w(w_typ1, w_typ2):
539        w_right_impl = None
540    else:
541        w_right_src, w_right_impl = space.lookup_in_type_where(w_typ2, '__cmp__')
542        if (w_left_src is not w_right_src
543            and space.issubtype_w(w_typ2, w_typ1)):
544            w_obj1, w_obj2 = w_obj2, w_obj1
545            w_left_impl, w_right_impl = w_right_impl, w_left_impl
546            do_neg1, do_neg2 = do_neg2, do_neg1
547
548    w_res = _invoke_binop(space, w_left_impl, w_obj1, w_obj2)
549    if w_res is not None:
550        return _conditional_neg(space, w_res, do_neg1)
551    w_res = _invoke_binop(space, w_right_impl, w_obj2, w_obj1)
552    if w_res is not None:
553        return _conditional_neg(space, w_res, do_neg2)
554    # fall back to internal rules
555    if space.is_w(w_obj1, w_obj2):
556        return space.wrap(0)
557    if space.is_w(w_obj1, space.w_None):
558        return space.wrap(-1)
559    if space.is_w(w_obj2, space.w_None):
560        return space.wrap(1)
561    if space.is_w(w_typ1, w_typ2):
562        #print "WARNING, comparison by address!"
563        lt = _id_cmpr(space, w_obj1, w_obj2, symbol)
564    else:
565        #print "WARNING, comparison by type name!"
566
567        # the CPython rule is to compare type names; numbers are
568        # smaller.  So we compare the types by the following key:
569        #   (not_a_number_flag, type_name, type_id)
570        num1 = number_check(space, w_obj1)
571        num2 = number_check(space, w_obj2)
572        if num1 != num2:
573            lt = num1      # if obj1 is a number, it is Lower Than obj2
574        else:
575            name1 = w_typ1.getname(space)
576            name2 = w_typ2.getname(space)
577            if name1 != name2:
578                lt = name1 < name2
579            else:
580                lt = _id_cmpr(space, w_typ1, w_typ2, symbol)
581    if lt:
582        return space.wrap(-1)
583    else:
584        return space.wrap(1)
585
586def _id_cmpr(space, w_obj1, w_obj2, symbol):
587    if symbol == "==":
588        return not space.is_w(w_obj1, w_obj2)
589    elif symbol == "!=":
590        return space.is_w(w_obj1, w_obj2)
591    w_id1 = space.id(w_obj1)
592    w_id2 = space.id(w_obj2)
593    return space.is_true(space.lt(w_id1, w_id2))
594
595
596def number_check(space, w_obj):
597    # avoid this as much as possible.  It checks if w_obj "looks like"
598    # it might be a number-ish thing.
599    return (space.lookup(w_obj, '__int__') is not None or
600            space.lookup(w_obj, '__float__') is not None)
601
602
603
604# what is the maximum value slices can get on CPython?
605# we need to stick to that value, because fake.py etc.
606class Temp(object):
607    def __getslice__(self, i, j):
608        return j
609slice_max = Temp()[:]
610del Temp
611
612def old_slice_range_getlength(space, w_obj):
613    # NB. the language ref is inconsistent with the new-style class
614    # behavior when w_obj doesn't implement __len__(), so we just
615    # follow cpython. Also note that CPython slots make it easier
616    # to check for object implementing it or not. We just catch errors
617    # so this behavior is slightly different
618    try:
619        return space.len(w_obj)
620    except OperationError as e:
621        if not ((e.match(space, space.w_AttributeError) or
622                 e.match(space, space.w_TypeError))):
623            raise
624    return None
625
626def old_slice_range(space, w_obj, w_start, w_stop):
627    """Only for backward compatibility for __getslice__()&co methods."""
628    w_length = None
629    if space.is_w(w_start, space.w_None):
630        w_start = space.wrap(0)
631    else:
632        start = space.getindex_w(w_start, None)
633        w_start = space.wrap(start)
634        if start < 0:
635            w_length = old_slice_range_getlength(space, w_obj)
636            if w_length is not None:
637                w_start = space.add(w_start, w_length)
638    if space.is_w(w_stop, space.w_None):
639        w_stop = space.wrap(slice_max)
640    else:
641        stop = space.getindex_w(w_stop, None)
642        w_stop = space.wrap(stop)
643        if stop < 0:
644            if w_length is None:
645                w_length = old_slice_range_getlength(space, w_obj)
646            if w_length is not None:
647                w_stop = space.add(w_stop, w_length)
648    return w_start, w_stop
649
650# regular methods def helpers
651
652def _make_binop_impl(symbol, specialnames):
653    left, right = specialnames
654    errormsg = "unsupported operand type(s) for %s: '%%N' and '%%N'" % (
655        symbol.replace('%', '%%'),)
656    seq_bug_compat = (symbol == '+' or symbol == '*')
657
658    def binop_impl(space, w_obj1, w_obj2):
659        w_typ1 = space.type(w_obj1)
660        w_typ2 = space.type(w_obj2)
661        w_left_src, w_left_impl = space.lookup_in_type_where(w_typ1, left)
662        if _same_class_w(space, w_obj1, w_obj2, w_typ1, w_typ2):
663            w_right_impl = None
664        else:
665            w_right_src, w_right_impl = space.lookup_in_type_where(w_typ2, right)
666            # the logic to decide if the reverse operation should be tried
667            # before the direct one is very obscure.  For now, and for
668            # sanity reasons, we just compare the two places where the
669            # __xxx__ and __rxxx__ methods where found by identity.
670            # Note that space.is_w() is potentially not happy if one of them
671            # is None...
672            if w_right_src and (w_left_src is not w_right_src) and w_left_src:
673                # 'seq_bug_compat' is for cpython bug-to-bug compatibility:
674                # see objspace/std/test/test_unicodeobject.*concat_overrides
675                # and objspace/test/test_descrobject.*rmul_overrides.
676                # For cases like "unicode + string subclass".
677                if ((seq_bug_compat and w_typ1.flag_sequence_bug_compat
678                                    and not w_typ2.flag_sequence_bug_compat)
679                        # the non-bug-compat part is the following check:
680                        or space.issubtype_w(w_typ2, w_typ1)):
681                    if (not space.abstract_issubclass_w(w_left_src, w_right_src) and
682                        not space.abstract_issubclass_w(w_typ1, w_right_src)):
683                        w_obj1, w_obj2 = w_obj2, w_obj1
684                        w_left_impl, w_right_impl = w_right_impl, w_left_impl
685
686        w_res = _invoke_binop(space, w_left_impl, w_obj1, w_obj2)
687        if w_res is not None:
688            return w_res
689        w_res = _invoke_binop(space, w_right_impl, w_obj2, w_obj1)
690        if w_res is not None:
691            return w_res
692        raise oefmt(space.w_TypeError, errormsg, w_typ1, w_typ2)
693
694    return func_with_new_name(binop_impl, "binop_%s_impl"%left.strip('_'))
695
696def _make_comparison_impl(symbol, specialnames):
697    left, right = specialnames
698    op = getattr(operator, left)
699    def comparison_impl(space, w_obj1, w_obj2):
700        w_typ1 = space.type(w_obj1)
701        w_typ2 = space.type(w_obj2)
702        w_left_src, w_left_impl = space.lookup_in_type_where(w_typ1, left)
703        w_first = w_obj1
704        w_second = w_obj2
705        #
706        if left == right and _same_class_w(space, w_obj1, w_obj2,
707                                           w_typ1, w_typ2):
708            # for __eq__ and __ne__, if the objects have the same
709            # (old-style or new-style) class, then don't try the
710            # opposite method, which is the same one.
711            w_right_impl = None
712        else:
713            # in all other cases, try the opposite method.
714            w_right_src, w_right_impl = space.lookup_in_type_where(w_typ2,right)
715            if space.is_w(w_typ1, w_typ2):
716                # if the type is the same, *or* if both are old-style classes,
717                # then don't reverse: try left first, right next.
718                pass
719            elif space.issubtype_w(w_typ2, w_typ1):
720                # for new-style classes, if typ2 is a subclass of typ1.
721                w_obj1, w_obj2 = w_obj2, w_obj1
722                w_left_impl, w_right_impl = w_right_impl, w_left_impl
723
724        w_res = _invoke_binop(space, w_left_impl, w_obj1, w_obj2)
725        if w_res is not None:
726            return w_res
727        w_res = _invoke_binop(space, w_right_impl, w_obj2, w_obj1)
728        if w_res is not None:
729            return w_res
730        # fallback: lt(a, b) <= lt(cmp(a, b), 0) ...
731        w_res = _cmp(space, w_first, w_second, symbol)
732        res = space.int_w(w_res)
733        return space.wrap(op(res, 0))
734
735    return func_with_new_name(comparison_impl, 'comparison_%s_impl'%left.strip('_'))
736
737def _make_inplace_impl(symbol, specialnames):
738    specialname, = specialnames
739    assert specialname.startswith('__i') and specialname.endswith('__')
740    noninplacespacemethod = specialname[3:-2]
741    if noninplacespacemethod in ['or', 'and']:
742        noninplacespacemethod += '_'     # not too clean
743    seq_bug_compat = (symbol == '+=' or symbol == '*=')
744    rhs_method = '__r' + specialname[3:]
745
746    def inplace_impl(space, w_lhs, w_rhs):
747        w_impl = space.lookup(w_lhs, specialname)
748        if w_impl is not None:
749            # 'seq_bug_compat' is for cpython bug-to-bug compatibility:
750            # see objspace/test/test_descrobject.*rmul_overrides.
751            # For cases like "list += object-overriding-__radd__".
752            if (seq_bug_compat and space.type(w_lhs).flag_sequence_bug_compat
753                           and not space.type(w_rhs).flag_sequence_bug_compat):
754                w_res = _invoke_binop(space, space.lookup(w_rhs, rhs_method),
755                                      w_rhs, w_lhs)
756                if w_res is not None:
757                    return w_res
758                # xxx if __radd__ is defined but returns NotImplemented,
759                # then it might be called again below.  Oh well, too bad.
760                # Anyway that's a case where we're likely to end up in
761                # a TypeError.
762            #
763            w_res = space.get_and_call_function(w_impl, w_lhs, w_rhs)
764            if _check_notimplemented(space, w_res):
765                return w_res
766        # XXX fix the error message we get here
767        return getattr(space, noninplacespacemethod)(w_lhs, w_rhs)
768
769    return func_with_new_name(inplace_impl, 'inplace_%s_impl'%specialname.strip('_'))
770
771def _make_unaryop_impl(symbol, specialnames):
772    specialname, = specialnames
773    errormsg = "unsupported operand type for unary %s: '%%T'" % symbol
774    def unaryop_impl(space, w_obj):
775        w_impl = space.lookup(w_obj, specialname)
776        if w_impl is None:
777            raise oefmt(space.w_TypeError, errormsg, w_obj)
778        return space.get_and_call_function(w_impl, w_obj)
779    return func_with_new_name(unaryop_impl, 'unaryop_%s_impl'%specialname.strip('_'))
780
781# the following seven operations are really better to generate with
782# string-templating (and maybe we should consider this for
783# more of the above manually-coded operations as well)
784
785for targetname, specialname, checkerspec in [
786    ('index', '__index__', ("space.w_int", "space.w_long")),
787    ('long', '__long__', ("space.w_int", "space.w_long")),
788    ('float', '__float__', ("space.w_float",))]:
789
790    l = ["space.isinstance_w(w_result, %s)" % x
791                for x in checkerspec]
792    checker = " or ".join(l)
793    if targetname == 'index':
794        msg = "'%%T' object cannot be interpreted as an index"
795    else:
796        msg = "unsupported operand type for %(targetname)s(): '%%T'"
797    msg = msg % locals()
798    source = """if 1:
799        def %(targetname)s(space, w_obj):
800            w_impl = space.lookup(w_obj, %(specialname)r)
801            if w_impl is None:
802                raise oefmt(space.w_TypeError,
803                            %(msg)r,
804                            w_obj)
805            w_result = space.get_and_call_function(w_impl, w_obj)
806
807            if %(checker)s:
808                return w_result
809            raise oefmt(space.w_TypeError,
810                        "%(specialname)s returned non-%(targetname)s (type "
811                        "'%%T')", w_result)
812        assert not hasattr(DescrOperation, %(targetname)r)
813        DescrOperation.%(targetname)s = %(targetname)s
814        del %(targetname)s
815        \n""" % locals()
816    exec compile2(source)
817
818for targetname, specialname in [
819    ('str', '__str__'),
820    ('repr', '__repr__'),
821    ('oct', '__oct__'),
822    ('hex', '__hex__')]:
823
824    source = """if 1:
825        def %(targetname)s(space, w_obj):
826            w_impl = space.lookup(w_obj, %(specialname)r)
827            if w_impl is None:
828                raise oefmt(space.w_TypeError,
829                            "unsupported operand type for %(targetname)s(): "
830                            "'%%T'", w_obj)
831            w_result = space.get_and_call_function(w_impl, w_obj)
832
833            if space.isinstance_w(w_result, space.w_str):
834                return w_result
835            try:
836                result = space.str_w(w_result)
837            except OperationError, e:
838                if not e.match(space, space.w_TypeError):
839                    raise
840                raise oefmt(space.w_TypeError,
841                            "%(specialname)s returned non-%(targetname)s "
842                            "(type '%%T')", w_result)
843            else:
844                # re-wrap the result as a real string
845                return space.wrap(result)
846        assert not hasattr(DescrOperation, %(targetname)r)
847        DescrOperation.%(targetname)s = %(targetname)s
848        del %(targetname)s
849        \n""" % locals()
850    exec compile2(source)
851
852# add default operation implementations for all still missing ops
853
854for _name, _symbol, _arity, _specialnames in ObjSpace.MethodTable:
855    if not hasattr(DescrOperation, _name):
856        _impl_maker = None
857        if _arity == 2 and _name in ['lt', 'le', 'gt', 'ge', 'ne', 'eq']:
858            #print "comparison", _specialnames
859            _impl_maker = _make_comparison_impl
860        elif _arity == 2 and _name.startswith('inplace_'):
861            #print "inplace", _specialnames
862            _impl_maker = _make_inplace_impl
863        elif _arity == 2 and len(_specialnames) == 2:
864            #print "binop", _specialnames
865            _impl_maker = _make_binop_impl
866        elif _arity == 1 and len(_specialnames) == 1 and _name != 'int':
867            #print "unaryop", _specialnames
868            _impl_maker = _make_unaryop_impl
869        if _impl_maker:
870            setattr(DescrOperation,_name,_impl_maker(_symbol,_specialnames))
871        elif _name not in ['is_', 'id','type','issubtype', 'int',
872                           # not really to be defined in DescrOperation
873                           'ord', 'unichr', 'unicode']:
874            raise Exception("missing def for operation %s" % _name)