[pypy-commit] pypy default: merge the int_w-refactor branch: in a lot of places CPython allows objects with __int__ and __float__ instead of actual ints and floats, while until now pypy disallowed them. We fix it by making space.{int_w, float_w, etc.} accepting those objects by default, and disallowing conversions only when explicitly needed

antocuni noreply at buildbot.pypy.org
Fri Feb 28 16:27:16 CET 2014


Author: Antonio Cuni <anto.cuni at gmail.com>
Branch: 
Changeset: r69551:ccdc7e797e3b
Date: 2014-02-28 16:26 +0100
http://bitbucket.org/pypy/pypy/changeset/ccdc7e797e3b/

Log:	merge the int_w-refactor branch: in a lot of places CPython allows
	objects with __int__ and __float__ instead of actual ints and
	floats, while until now pypy disallowed them. We fix it by making
	space.{int_w,float_w,etc.} accepting those objects by default, and
	disallowing conversions only when explicitly needed

diff --git a/lib-python/2.7/ctypes/test/test_numbers.py b/lib-python/2.7/ctypes/test/test_numbers.py
--- a/lib-python/2.7/ctypes/test/test_numbers.py
+++ b/lib-python/2.7/ctypes/test/test_numbers.py
@@ -105,7 +105,6 @@
             self.assertEqual(ArgType, type(parm))
 
 
-    @xfail
     def test_floats(self):
         # c_float and c_double can be created from
         # Python int, long and float
diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -92,7 +92,7 @@
         i = 2 * HUGEVAL_BYTES
         addrstring = [' '] * i
         while True:
-            n = space.int_w(space.and_(w_id, w_0x0F))
+            n = space.int_w(space.and_(w_id, w_0x0F), allow_conversion=False)
             n += ord('0')
             if n > ord('9'):
                 n += (ord('a') - ord('9') - 1)
@@ -201,16 +201,38 @@
     def unicode_w(self, space):
         self._typed_unwrap_error(space, "unicode")
 
-    def int_w(self, space):
+    def int_w(self, space, allow_conversion=True):
+        # note that W_IntObject.int_w has a fast path and W_FloatObject.int_w
+        # raises w_TypeError
+        w_obj = self
+        if allow_conversion:
+            w_obj = space.int(self)
+        return w_obj._int_w(space)
+
+    def _int_w(self, space):
         self._typed_unwrap_error(space, "integer")
 
-    def float_w(self, space):
+    def float_w(self, space, allow_conversion=True):
+        w_obj = self
+        if allow_conversion:
+            w_obj = space.float(self)
+        return w_obj._float_w(space)
+
+    def _float_w(self, space):
         self._typed_unwrap_error(space, "float")
 
     def uint_w(self, space):
         self._typed_unwrap_error(space, "integer")
 
-    def bigint_w(self, space):
+    def bigint_w(self, space, allow_conversion=True):
+        # note that W_IntObject and W_LongObject have fast paths,
+        # W_FloatObject.rbigint_w raises w_TypeError raises
+        w_obj = self
+        if allow_conversion:
+            w_obj = space.long(self)
+        return w_obj._bigint_w(space)
+
+    def _bigint_w(self, space):
         self._typed_unwrap_error(space, "integer")
 
     def _typed_unwrap_error(self, space, expected):
@@ -220,8 +242,7 @@
     def int(self, space):
         w_impl = space.lookup(self, '__int__')
         if w_impl is None:
-            raise oefmt(space.w_TypeError,
-                        "unsupported operand type for int(): '%T'", self)
+            self._typed_unwrap_error(space, "integer")
         w_result = space.get_and_call_function(w_impl, self)
 
         if (space.isinstance_w(w_result, space.w_int) or
@@ -1210,7 +1231,7 @@
             assert isinstance(w_index_or_slice, W_SliceObject)
             start, stop, step = w_index_or_slice.indices3(self, seqlength)
         else:
-            start = self.int_w(w_index_or_slice)
+            start = self.int_w(w_index_or_slice, allow_conversion=False)
             if start < 0:
                 start += seqlength
             if not (0 <= start < seqlength):
@@ -1231,7 +1252,7 @@
             start, stop, step, length = w_index_or_slice.indices4(self,
                                                                   seqlength)
         else:
-            start = self.int_w(w_index_or_slice)
+            start = self.int_w(w_index_or_slice, allow_conversion=False)
             if start < 0:
                 start += seqlength
             if not (0 <= start < seqlength):
@@ -1255,7 +1276,10 @@
             raise oefmt(self.w_TypeError, "%s must be an integer, not %T",
                         objdescr, w_obj)
         try:
-            index = self.int_w(w_index)
+            # allow_conversion=False it's not really necessary because the
+            # return type of __index__ is already checked by space.index(),
+            # but there is no reason to allow conversions anyway
+            index = self.int_w(w_index, allow_conversion=False)
         except OperationError, err:
             if not err.match(self, self.w_OverflowError):
                 raise
@@ -1272,16 +1296,16 @@
         else:
             return index
 
-    def r_longlong_w(self, w_obj):
-        bigint = self.bigint_w(w_obj)
+    def r_longlong_w(self, w_obj, allow_conversion=True):
+        bigint = self.bigint_w(w_obj, allow_conversion)
         try:
             return bigint.tolonglong()
         except OverflowError:
             raise OperationError(self.w_OverflowError,
                                  self.wrap('integer too large'))
 
-    def r_ulonglong_w(self, w_obj):
-        bigint = self.bigint_w(w_obj)
+    def r_ulonglong_w(self, w_obj, allow_conversion=True):
+        bigint = self.bigint_w(w_obj, allow_conversion)
         try:
             return bigint.toulonglong()
         except OverflowError:
@@ -1348,8 +1372,19 @@
                     'argument must be a string without NUL characters'))
         return rstring.assert_str0(result)
 
-    def int_w(self, w_obj):
-        return w_obj.int_w(self)
+    def int_w(self, w_obj, allow_conversion=True):
+        """
+        Unwrap an app-level int object into an interpret-level int.
+
+        If allow_conversion==True, w_obj might be of any type which implements
+        __int__, *except* floats which are explicitly rejected. This is the
+        same logic as CPython's PyArg_ParseTuple. If you want to also allow
+        floats, you can call space.int_w(space.int(w_obj)).
+
+        If allow_conversion=False, w_obj needs to be an app-level int or a
+        subclass.
+        """
+        return w_obj.int_w(self, allow_conversion)
 
     def int(self, w_obj):
         return w_obj.int(self)
@@ -1357,11 +1392,19 @@
     def uint_w(self, w_obj):
         return w_obj.uint_w(self)
 
-    def bigint_w(self, w_obj):
-        return w_obj.bigint_w(self)
+    def bigint_w(self, w_obj, allow_conversion=True):
+        """
+        Like int_w, but return a rlib.rbigint object and call __long__ if
+        allow_conversion is True.
+        """
+        return w_obj.bigint_w(self, allow_conversion)
 
-    def float_w(self, w_obj):
-        return w_obj.float_w(self)
+    def float_w(self, w_obj, allow_conversion=True):
+        """
+        Like int_w, but return an interp-level float and call __float__ if
+        allow_conversion is True.
+        """
+        return w_obj.float_w(self, allow_conversion)
 
     def realstr_w(self, w_obj):
         # Like str_w, but only works if w_obj is really of type 'str'.
@@ -1399,20 +1442,10 @@
         return w_obj.ord(self)
 
     # This is all interface for gateway.py.
-    def gateway_int_w(self, w_obj):
-        if self.isinstance_w(w_obj, self.w_float):
-            raise OperationError(self.w_TypeError,
-                            self.wrap("integer argument expected, got float"))
-        return self.int_w(self.int(w_obj))
-
-    def gateway_float_w(self, w_obj):
-        return self.float_w(self.float(w_obj))
-
-    def gateway_r_longlong_w(self, w_obj):
-        if self.isinstance_w(w_obj, self.w_float):
-            raise OperationError(self.w_TypeError,
-                            self.wrap("integer argument expected, got float"))
-        return self.r_longlong_w(self.int(w_obj))
+    gateway_int_w = int_w
+    gateway_float_w = float_w
+    gateway_r_longlong_w = r_longlong_w
+    gateway_r_ulonglong_w = r_ulonglong_w
 
     def gateway_r_uint_w(self, w_obj):
         if self.isinstance_w(w_obj, self.w_float):
@@ -1420,12 +1453,6 @@
                             self.wrap("integer argument expected, got float"))
         return self.uint_w(self.int(w_obj))
 
-    def gateway_r_ulonglong_w(self, w_obj):
-        if self.isinstance_w(w_obj, self.w_float):
-            raise OperationError(self.w_TypeError,
-                            self.wrap("integer argument expected, got float"))
-        return self.r_ulonglong_w(self.int(w_obj))
-
     def gateway_nonnegint_w(self, w_obj):
         # Like space.gateway_int_w(), but raises an app-level ValueError if
         # the integer is negative.  Here for gateway.py.
@@ -1447,7 +1474,7 @@
     def c_uint_w(self, w_obj):
         # Like space.gateway_uint_w(), but raises an app-level OverflowError if
         # the integer does not fit in 32 bits.  Here for gateway.py.
-        value = self.gateway_r_uint_w(w_obj)
+        value = self.uint_w(w_obj)
         if value > UINT_MAX_32_BITS:
             raise OperationError(self.w_OverflowError,
                               self.wrap("expected an unsigned 32-bit integer"))
@@ -1457,7 +1484,7 @@
         # Like space.gateway_int_w(), but raises an app-level ValueError if
         # the integer is negative or does not fit in 32 bits.  Here
         # for gateway.py.
-        value = self.gateway_int_w(w_obj)
+        value = self.int_w(w_obj)
         if value < 0:
             raise OperationError(self.w_ValueError,
                                  self.wrap("expected a non-negative integer"))
@@ -1466,22 +1493,22 @@
                                  self.wrap("expected a 32-bit integer"))
         return value
 
-    def truncatedint_w(self, w_obj):
+    def truncatedint_w(self, w_obj, allow_conversion=True):
         # Like space.gateway_int_w(), but return the integer truncated
         # instead of raising OverflowError.  For obscure cases only.
         try:
-            return self.int_w(w_obj)
+            return self.int_w(w_obj, allow_conversion)
         except OperationError, e:
             if not e.match(self, self.w_OverflowError):
                 raise
             from rpython.rlib.rarithmetic import intmask
             return intmask(self.bigint_w(w_obj).uintmask())
 
-    def truncatedlonglong_w(self, w_obj):
+    def truncatedlonglong_w(self, w_obj, allow_conversion=True):
         # Like space.gateway_r_longlong_w(), but return the integer truncated
         # instead of raising OverflowError.
         try:
-            return self.r_longlong_w(w_obj)
+            return self.r_longlong_w(w_obj, allow_conversion)
         except OperationError, e:
             if not e.match(self, self.w_OverflowError):
                 raise
diff --git a/pypy/interpreter/main.py b/pypy/interpreter/main.py
--- a/pypy/interpreter/main.py
+++ b/pypy/interpreter/main.py
@@ -134,7 +134,7 @@
                     exitcode = 0
                 else:
                     try:
-                        exitcode = space.int_w(w_exitcode)
+                        exitcode = space.int_w(w_exitcode, allow_conversion=False)
                     except OperationError:
                         # not an integer: print it to stderr
                         msg = space.str_w(space.str(w_exitcode))
diff --git a/pypy/interpreter/test/test_argument.py b/pypy/interpreter/test/test_argument.py
--- a/pypy/interpreter/test/test_argument.py
+++ b/pypy/interpreter/test/test_argument.py
@@ -106,7 +106,7 @@
     def len(self, x):
         return len(x)
 
-    def int_w(self, x):
+    def int_w(self, x, allow_conversion=True):
         return x
 
     def eq_w(self, x, y):
diff --git a/pypy/interpreter/test/test_gateway.py b/pypy/interpreter/test/test_gateway.py
--- a/pypy/interpreter/test/test_gateway.py
+++ b/pypy/interpreter/test/test_gateway.py
@@ -457,6 +457,8 @@
                        space.mul(space.wrap(sys.maxint), space.wrap(-7)))
 
     def test_interp2app_unwrap_spec_typechecks(self):
+        from rpython.rlib.rarithmetic import r_longlong
+
         space = self.space
         w = space.wrap
         def g3_id(space, x):
@@ -491,6 +493,12 @@
         raises(gateway.OperationError,space.call_function,w_app_g3_f,w(None))
         raises(gateway.OperationError,space.call_function,w_app_g3_f,w("foo"))
 
+        app_g3_r = gateway.interp2app_temp(g3_id,
+                                           unwrap_spec=[gateway.ObjSpace,
+                                                        r_longlong])
+        w_app_g3_r = space.wrap(app_g3_r)
+        raises(gateway.OperationError,space.call_function,w_app_g3_r,w(1.0))
+
     def test_interp2app_unwrap_spec_unicode(self):
         space = self.space
         w = space.wrap
diff --git a/pypy/interpreter/test/test_objspace.py b/pypy/interpreter/test/test_objspace.py
--- a/pypy/interpreter/test/test_objspace.py
+++ b/pypy/interpreter/test/test_objspace.py
@@ -167,6 +167,40 @@
         self.space.setattr(w_oldstyle, self.space.wrap("__call__"), w_func)
         assert is_callable(w_oldstyle)
 
+    def test_int_w(self):
+        space = self.space
+        w_x = space.wrap(42)
+        assert space.int_w(w_x) == 42
+        assert space.int_w(w_x, allow_conversion=False) == 42
+        #
+        w_x = space.wrap(44.0)
+        space.raises_w(space.w_TypeError, space.int_w, w_x)
+        space.raises_w(space.w_TypeError, space.int_w, w_x, allow_conversion=False)
+        #
+        w_instance = self.space.appexec([], """():
+            class MyInt(object):
+                def __int__(self):
+                    return 43
+            return MyInt()
+        """)
+        assert space.int_w(w_instance) == 43
+        space.raises_w(space.w_TypeError, space.int_w, w_instance, allow_conversion=False)
+        #
+        w_instance = self.space.appexec([], """():
+            class MyInt(object):
+                def __int__(self):
+                    return 43
+
+            class AnotherInt(object):
+                def __int__(self):
+                    return MyInt()
+
+            return AnotherInt()
+        """)
+        space.raises_w(space.w_TypeError, space.int_w, w_instance)
+        space.raises_w(space.w_TypeError, space.int_w, w_instance, allow_conversion=False)
+
+
     def test_interp_w(self):
         w = self.space.wrap
         w_bltinfunction = self.space.builtin.get('len')
diff --git a/pypy/module/__builtin__/test/test_buffer.py b/pypy/module/__builtin__/test/test_buffer.py
--- a/pypy/module/__builtin__/test/test_buffer.py
+++ b/pypy/module/__builtin__/test/test_buffer.py
@@ -170,6 +170,19 @@
                 for step in indices[1:]:
                     assert b[start:stop:step] == s[start:stop:step]
 
+    def test_getitem_only_ints(self):
+        class MyInt(object):
+          def __init__(self, x):
+            self.x = x
+
+          def __int__(self):
+            return self.x
+
+        buf = buffer('hello world')
+        raises(TypeError, "buf[MyInt(0)]")
+        raises(TypeError, "buf[MyInt(0):MyInt(5)]")
+
+
 class AppTestMemoryView:
     def test_basic(self):
         v = memoryview("abc")
diff --git a/pypy/module/_cffi_backend/misc.py b/pypy/module/_cffi_backend/misc.py
--- a/pypy/module/_cffi_backend/misc.py
+++ b/pypy/module/_cffi_backend/misc.py
@@ -131,13 +131,13 @@
     if space.is_w(space.type(w_ob), space.w_int):   # shortcut
         return space.int_w(w_ob)
     try:
-        bigint = space.bigint_w(w_ob)
+        bigint = space.bigint_w(w_ob, allow_conversion=False)
     except OperationError, e:
         if not e.match(space, space.w_TypeError):
             raise
         if _is_a_float(space, w_ob):
             raise
-        bigint = space.bigint_w(space.int(w_ob))
+        bigint = space.bigint_w(space.int(w_ob), allow_conversion=False)
     try:
         return bigint.tolonglong()
     except OverflowError:
@@ -148,13 +148,13 @@
     if space.is_w(space.type(w_ob), space.w_int):   # shortcut
         return space.int_w(w_ob)
     try:
-        bigint = space.bigint_w(w_ob)
+        bigint = space.bigint_w(w_ob, allow_conversion=False)
     except OperationError, e:
         if not e.match(space, space.w_TypeError):
             raise
         if _is_a_float(space, w_ob):
             raise
-        bigint = space.bigint_w(space.int(w_ob))
+        bigint = space.bigint_w(space.int(w_ob), allow_conversion=False)
     try:
         return bigint.toint()
     except OverflowError:
@@ -171,13 +171,13 @@
             raise OperationError(space.w_OverflowError, space.wrap(neg_msg))
         return r_ulonglong(value)
     try:
-        bigint = space.bigint_w(w_ob)
+        bigint = space.bigint_w(w_ob, allow_conversion=False)
     except OperationError, e:
         if not e.match(space, space.w_TypeError):
             raise
         if strict and _is_a_float(space, w_ob):
             raise
-        bigint = space.bigint_w(space.int(w_ob))
+        bigint = space.bigint_w(space.int(w_ob), allow_conversion=False)
     if strict:
         try:
             return bigint.toulonglong()
@@ -196,13 +196,13 @@
             raise OperationError(space.w_OverflowError, space.wrap(neg_msg))
         return r_uint(value)
     try:
-        bigint = space.bigint_w(w_ob)
+        bigint = space.bigint_w(w_ob, allow_conversion=False)
     except OperationError, e:
         if not e.match(space, space.w_TypeError):
             raise
         if strict and _is_a_float(space, w_ob):
             raise
-        bigint = space.bigint_w(space.int(w_ob))
+        bigint = space.bigint_w(space.int(w_ob), allow_conversion=False)
     if strict:
         try:
             return bigint.touint()
diff --git a/pypy/module/_cffi_backend/test/_backend_test_c.py b/pypy/module/_cffi_backend/test/_backend_test_c.py
--- a/pypy/module/_cffi_backend/test/_backend_test_c.py
+++ b/pypy/module/_cffi_backend/test/_backend_test_c.py
@@ -1418,8 +1418,10 @@
     p = newp(BStructPtr, [12])
     assert p.a1 == 12
     e = py.test.raises(TypeError, newp, BStructPtr, [None])
-    assert ("an integer is required" in str(e.value) or
-        "unsupported operand type for int(): 'NoneType'" in str(e.value)) #PyPy
+    msg = str(e.value)
+    assert ("an integer is required" in msg or  # CPython
+            "unsupported operand type for int(): 'NoneType'" in msg or  # old PyPys
+            "expected integer, got NoneType object" in msg) # newer PyPys
     py.test.raises(TypeError, 'p.a1 = "def"')
     if sys.version_info < (3,):
         BEnum2 = new_enum_type(unicode("foo"), (unicode('abc'),), (5,), BInt)
diff --git a/pypy/module/_rawffi/alt/test/test_funcptr.py b/pypy/module/_rawffi/alt/test/test_funcptr.py
--- a/pypy/module/_rawffi/alt/test/test_funcptr.py
+++ b/pypy/module/_rawffi/alt/test/test_funcptr.py
@@ -185,6 +185,10 @@
         set_val_to_ptr(ptr2, 123)
         assert get_dummy() == 123
         set_val_to_ptr(ptr2, 0)
+        #
+        class OldStyle:
+            pass
+        raises(TypeError, "set_val_to_ptr(OldStyle(), 0)")
 
     def test_convert_strings_to_char_p(self):
         """
diff --git a/pypy/module/_rawffi/alt/type_converter.py b/pypy/module/_rawffi/alt/type_converter.py
--- a/pypy/module/_rawffi/alt/type_converter.py
+++ b/pypy/module/_rawffi/alt/type_converter.py
@@ -25,7 +25,7 @@
             assert libffi.IS_32_BIT
             self._longlong(w_ffitype, w_obj)
         elif w_ffitype.is_signed():
-            intval = space.truncatedint_w(w_obj)
+            intval = space.truncatedint_w(w_obj, allow_conversion=False)
             self.handle_signed(w_ffitype, w_obj, intval)
         elif self.maybe_handle_char_or_unichar_p(w_ffitype, w_obj):
             # the object was already handled from within
@@ -33,16 +33,16 @@
             pass
         elif w_ffitype.is_pointer():
             w_obj = self.convert_pointer_arg_maybe(w_obj, w_ffitype)
-            intval = space.truncatedint_w(w_obj)
+            intval = space.truncatedint_w(w_obj, allow_conversion=False)
             self.handle_pointer(w_ffitype, w_obj, intval)
         elif w_ffitype.is_unsigned():
-            uintval = r_uint(space.truncatedint_w(w_obj))
+            uintval = r_uint(space.truncatedint_w(w_obj, allow_conversion=False))
             self.handle_unsigned(w_ffitype, w_obj, uintval)
         elif w_ffitype.is_char():
-            intval = space.int_w(space.ord(w_obj))
+            intval = space.int_w(space.ord(w_obj), allow_conversion=False)
             self.handle_char(w_ffitype, w_obj, intval)
         elif w_ffitype.is_unichar():
-            intval = space.int_w(space.ord(w_obj))
+            intval = space.int_w(space.ord(w_obj), allow_conversion=False)
             self.handle_unichar(w_ffitype, w_obj, intval)
         elif w_ffitype.is_double():
             self._float(w_ffitype, w_obj)
@@ -60,20 +60,20 @@
     def _longlong(self, w_ffitype, w_obj):
         # a separate function, which can be seen by the jit or not,
         # depending on whether longlongs are supported
-        longlongval = self.space.truncatedlonglong_w(w_obj)
+        longlongval = self.space.truncatedlonglong_w(w_obj, allow_conversion=False)
         self.handle_longlong(w_ffitype, w_obj, longlongval)
 
     def _float(self, w_ffitype, w_obj):
         # a separate function, which can be seen by the jit or not,
         # depending on whether floats are supported
-        floatval = self.space.float_w(w_obj)
+        floatval = self.space.float_w(w_obj, allow_conversion=False)
         self.handle_float(w_ffitype, w_obj, floatval)
 
     def _singlefloat(self, w_ffitype, w_obj):
         # a separate function, which can be seen by the jit or not,
         # depending on whether singlefloats are supported
         from rpython.rlib.rarithmetic import r_singlefloat
-        floatval = self.space.float_w(w_obj)
+        floatval = self.space.float_w(w_obj, allow_conversion=False)
         singlefloatval = r_singlefloat(floatval)
         self.handle_singlefloat(w_ffitype, w_obj, singlefloatval)
 
diff --git a/pypy/module/array/test/test_array.py b/pypy/module/array/test/test_array.py
--- a/pypy/module/array/test/test_array.py
+++ b/pypy/module/array/test/test_array.py
@@ -1034,6 +1034,18 @@
         assert len(b) == 13
         assert str(b[12]) == "-0.0"
 
+    def test_getitem_only_ints(self):
+        class MyInt(object):
+          def __init__(self, x):
+            self.x = x
+
+          def __int__(self):
+            return self.x
+
+        a = self.array('i', [1, 2, 3, 4, 5, 6])
+        raises(TypeError, "a[MyInt(0)]")
+        raises(TypeError, "a[MyInt(0):MyInt(5)]")
+
 
 class AppTestArrayBuiltinShortcut(AppTestArray):
     spaceconfig = AppTestArray.spaceconfig.copy()
diff --git a/pypy/module/cppyy/test/test_zjit.py b/pypy/module/cppyy/test/test_zjit.py
--- a/pypy/module/cppyy/test/test_zjit.py
+++ b/pypy/module/cppyy/test/test_zjit.py
@@ -120,7 +120,7 @@
             return FakeInt(int(obj))
         assert 0
 
-    def float_w(self, w_obj):
+    def float_w(self, w_obj, allow_conversion=True):
         assert isinstance(w_obj, FakeFloat)
         return w_obj.val
 
@@ -141,7 +141,7 @@
     def is_w(self, w_one, w_two):
         return w_one is w_two
 
-    def int_w(self, w_obj):
+    def int_w(self, w_obj, allow_conversion=True):
         assert isinstance(w_obj, FakeInt)
         return w_obj.val
 
diff --git a/pypy/module/micronumpy/boxes.py b/pypy/module/micronumpy/boxes.py
--- a/pypy/module/micronumpy/boxes.py
+++ b/pypy/module/micronumpy/boxes.py
@@ -375,7 +375,7 @@
     pass
 
 class W_IntegerBox(W_NumberBox):
-    def int_w(self, space):
+    def _int_w(self, space):
         return space.int_w(self.descr_int(space))
 
 class W_SignedIntegerBox(W_IntegerBox):
diff --git a/pypy/module/micronumpy/compile.py b/pypy/module/micronumpy/compile.py
--- a/pypy/module/micronumpy/compile.py
+++ b/pypy/module/micronumpy/compile.py
@@ -151,11 +151,11 @@
         assert isinstance(w_obj, boxes.W_GenericBox)
         return self.float(w_obj.descr_float(self))
 
-    def float_w(self, w_obj):
+    def float_w(self, w_obj, allow_conversion=True):
         assert isinstance(w_obj, FloatObject)
         return w_obj.floatval
 
-    def int_w(self, w_obj):
+    def int_w(self, w_obj, allow_conversion=True):
         if isinstance(w_obj, IntObject):
             return w_obj.intval
         elif isinstance(w_obj, FloatObject):
diff --git a/pypy/module/posix/interp_posix.py b/pypy/module/posix/interp_posix.py
--- a/pypy/module/posix/interp_posix.py
+++ b/pypy/module/posix/interp_posix.py
@@ -868,8 +868,8 @@
         args_w = space.fixedview(w_tuple)
         if len(args_w) != 2:
             raise OperationError(space.w_TypeError, space.wrap(msg))
-        actime = space.float_w(args_w[0])
-        modtime = space.float_w(args_w[1])
+        actime = space.float_w(args_w[0], allow_conversion=False)
+        modtime = space.float_w(args_w[1], allow_conversion=False)
         dispatch_filename(rposix.utime, 2)(space, w_path, (actime, modtime))
     except OSError, e:
         raise wrap_oserror2(space, e, w_path)
diff --git a/pypy/module/struct/test/test_struct.py b/pypy/module/struct/test/test_struct.py
--- a/pypy/module/struct/test/test_struct.py
+++ b/pypy/module/struct/test/test_struct.py
@@ -7,7 +7,7 @@
 
 
 class AppTestStruct(object):
-    spaceconfig = dict(usemodules=['struct'])
+    spaceconfig = dict(usemodules=['struct', 'micronumpy'])
 
     def setup_class(cls):
         """
@@ -19,7 +19,7 @@
             return struct
         """)
         cls.w_native_is_bigendian = cls.space.wrap(native_is_bigendian)
-
+        cls.w_runappdirect = cls.space.wrap(cls.runappdirect)
 
     def test_error(self):
         """
@@ -384,6 +384,19 @@
         assert self.struct.unpack("ii", b) == (62, 12)
         raises(self.struct.error, self.struct.unpack, "i", b)
 
+    def test___float__(self):
+        class MyFloat(object):
+            def __init__(self, x):
+                self.x = x
+            def __float__(self):
+                return self.x
+
+        obj = MyFloat(42.3)
+        data = self.struct.pack('d', obj)
+        obj2, = self.struct.unpack('d', data)
+        assert type(obj2) is float
+        assert obj2 == 42.3
+
 
 class AppTestStructBuffer(object):
     spaceconfig = dict(usemodules=['struct', '__pypy__'])
diff --git a/pypy/objspace/fake/objspace.py b/pypy/objspace/fake/objspace.py
--- a/pypy/objspace/fake/objspace.py
+++ b/pypy/objspace/fake/objspace.py
@@ -45,13 +45,13 @@
     def unicode_w(self, space):
         return NonConstant(u"foobar")
 
-    def int_w(self, space):
+    def int_w(self, space, allow_conversion=True):
         return NonConstant(-42)
 
     def uint_w(self, space):
         return r_uint(NonConstant(42))
 
-    def bigint_w(self, space):
+    def bigint_w(self, space, allow_conversion=True):
         from rpython.rlib.rbigint import rbigint
         return rbigint.fromint(NonConstant(42))
 
@@ -117,7 +117,7 @@
     def _freeze_(self):
         return True
 
-    def float_w(self, w_obj):
+    def float_w(self, w_obj, allow_conversion=True):
         is_root(w_obj)
         return NonConstant(42.5)
 
diff --git a/pypy/objspace/std/floatobject.py b/pypy/objspace/std/floatobject.py
--- a/pypy/objspace/std/floatobject.py
+++ b/pypy/objspace/std/floatobject.py
@@ -34,7 +34,16 @@
     def unwrap(self, space):
         return self.floatval
 
-    def float_w(self, space):
+    def int_w(self, space, allow_conversion=True):
+        self._typed_unwrap_error(space, "integer")
+
+    def bigint_w(self, space, allow_conversion=True):
+        self._typed_unwrap_error(space, "integer")
+
+    def float_w(self, space, allow_conversion=True):
+        return self.floatval
+
+    def _float_w(self, space):
         return self.floatval
 
     def int(self, space):
diff --git a/pypy/objspace/std/intobject.py b/pypy/objspace/std/intobject.py
--- a/pypy/objspace/std/intobject.py
+++ b/pypy/objspace/std/intobject.py
@@ -42,7 +42,8 @@
             return False
         if self.user_overridden_class or w_other.user_overridden_class:
             return self is w_other
-        return space.int_w(self) == space.int_w(w_other)
+        return (space.int_w(self, allow_conversion=False) ==
+                space.int_w(w_other, allow_conversion=False))
 
     def immutable_unique_id(self, space):
         if self.user_overridden_class:
@@ -309,9 +310,13 @@
         """representation for debugging purposes"""
         return "%s(%d)" % (self.__class__.__name__, self.intval)
 
-    def int_w(self, space):
+    def int_w(self, space, allow_conversion=True):
         return int(self.intval)
-    unwrap = int_w
+
+    def _int_w(self, space):
+        return int(self.intval)
+
+    unwrap = _int_w
 
     def uint_w(self, space):
         intval = self.intval
@@ -320,12 +325,18 @@
                         "cannot convert negative integer to unsigned")
         return r_uint(intval)
 
-    def bigint_w(self, space):
+    def bigint_w(self, space, allow_conversion=True):
         return rbigint.fromint(self.intval)
 
-    def float_w(self, space):
+    def _bigint_w(self, space):
+        return rbigint.fromint(self.intval)
+
+    def float_w(self, space, allow_conversion=True):
         return float(self.intval)
 
+    # note that we do NOT implement _float_w, because __float__ cannot return
+    # an int
+
     def int(self, space):
         if type(self) is W_IntObject:
             return self
@@ -665,7 +676,7 @@
             # int_w is effectively what we want in this case,
             # we cannot construct a subclass of int instance with an
             # an overflowing long
-            value = space.int_w(w_obj)
+            value = space.int_w(w_obj, allow_conversion=False)
         elif space.isinstance_w(w_value, space.w_str):
             value, w_longval = _string_to_int_or_long(space, w_value,
                                                       space.str_w(w_value))
diff --git a/pypy/objspace/std/longobject.py b/pypy/objspace/std/longobject.py
--- a/pypy/objspace/std/longobject.py
+++ b/pypy/objspace/std/longobject.py
@@ -244,7 +244,7 @@
     def fromrarith_int(i):
         return W_LongObject(rbigint.fromrarith_int(i))
 
-    def int_w(self, space):
+    def _int_w(self, space):
         try:
             return self.num.toint()
         except OverflowError:
@@ -261,10 +261,16 @@
             raise oefmt(space.w_OverflowError,
                         "long int too large to convert to unsigned int")
 
-    def bigint_w(self, space):
+    def bigint_w(self, space, allow_conversion=True):
         return self.num
 
-    def float_w(self, space):
+    def _bigint_w(self, space):
+        return self.num
+
+    def float_w(self, space, allow_conversion=True):
+        return self.tofloat(space)
+
+    def _float_w(self, space):
         return self.tofloat(space)
 
     def int(self, space):
diff --git a/pypy/objspace/std/smalllongobject.py b/pypy/objspace/std/smalllongobject.py
--- a/pypy/objspace/std/smalllongobject.py
+++ b/pypy/objspace/std/smalllongobject.py
@@ -44,7 +44,7 @@
     def __repr__(self):
         return '<W_SmallLongObject(%d)>' % self.longlong
 
-    def int_w(self, space):
+    def _int_w(self, space):
         a = self.longlong
         b = intmask(a)
         if b == a:
@@ -63,10 +63,13 @@
         raise oefmt(space.w_OverflowError,
                     "long int too large to convert to unsigned int")
 
-    def bigint_w(self, space):
+    def bigint_w(self, space, allow_conversion=True):
         return self.asbigint()
 
-    def float_w(self, space):
+    def _bigint_w(self, space):
+        return self.asbigint()
+
+    def _float_w(self, space):
         return float(self.longlong)
 
     def int(self, space):
diff --git a/pypy/objspace/std/test/test_dictmultiobject.py b/pypy/objspace/std/test/test_dictmultiobject.py
--- a/pypy/objspace/std/test/test_dictmultiobject.py
+++ b/pypy/objspace/std/test/test_dictmultiobject.py
@@ -1046,7 +1046,7 @@
         assert isinstance(string, str)
         return string
 
-    def int_w(self, integer):
+    def int_w(self, integer, allow_conversion=True):
         assert isinstance(integer, int)
         return integer
 
diff --git a/pypy/objspace/std/test/test_intobject.py b/pypy/objspace/std/test/test_intobject.py
--- a/pypy/objspace/std/test/test_intobject.py
+++ b/pypy/objspace/std/test/test_intobject.py
@@ -553,6 +553,15 @@
         assert 3 .__coerce__(4) == (3, 4)
         assert 3 .__coerce__(4L) == NotImplemented
 
+    def test_fake_int_as_base(self):
+        class MyInt(object):
+            def __init__(self, x):
+                self.x = x
+            def __int__(self):
+                return self.x
+
+        base = MyInt(24)
+        assert int('10', base) == 24
 
 class AppTestIntShortcut(AppTestInt):
     spaceconfig = {"objspace.std.intshortcut": True}
diff --git a/pypy/objspace/std/test/test_listobject.py b/pypy/objspace/std/test/test_listobject.py
--- a/pypy/objspace/std/test/test_listobject.py
+++ b/pypy/objspace/std/test/test_listobject.py
@@ -842,6 +842,26 @@
         except TypeError:
             pass
 
+    def test_mul___index__(self):
+        class MyInt(object):
+          def __init__(self, x):
+            self.x = x
+
+          def __int__(self):
+            return self.x
+
+        class MyIndex(object):
+          def __init__(self, x):
+            self.x = x
+
+          def __index__(self):
+            return self.x
+
+        assert [0] * MyIndex(3) == [0, 0, 0]
+        raises(TypeError, "[0]*MyInt(3)")
+        raises(TypeError, "[0]*MyIndex(MyInt(3))")
+
+
     def test_index(self):
         c = range(10)
         assert c.index(0) == 0
diff --git a/pypy/objspace/std/test/test_stringformat.py b/pypy/objspace/std/test/test_stringformat.py
--- a/pypy/objspace/std/test/test_stringformat.py
+++ b/pypy/objspace/std/test/test_stringformat.py
@@ -186,6 +186,22 @@
     def test_broken_unicode(self):
         raises(UnicodeDecodeError, 'Názov: %s'.__mod__, u'Jerry')
 
+    def test___int__(self):
+        class MyInt(object):
+            def __init__(self, x):
+                self.x = x
+            def __int__(self):
+                return self.x
+        #
+        x = MyInt(65)
+        assert '%c' % x == 'A'
+
+
+class Foo(object):
+    def __cmp__(self, other):
+        return MyInt(0)
+
+
 class AppTestWidthPrec:
     def test_width(self):
         a = 'a'
diff --git a/pypy/objspace/test/test_descroperation.py b/pypy/objspace/test/test_descroperation.py
--- a/pypy/objspace/test/test_descroperation.py
+++ b/pypy/objspace/test/test_descroperation.py
@@ -721,8 +721,19 @@
                 return CannotConvertToBool()
         x = X()
         raises(MyError, "'foo' in x")
-        
-            
+
+    def test___cmp___fake_int(self):
+        class MyInt(object):
+            def __init__(self, x):
+                self.x = x
+            def __int__(self):
+                return self.x
+        class X(object):
+            def __cmp__(self, other):
+                return MyInt(0)
+
+        assert X() == 'hello'
+
 
 class AppTestWithBuiltinShortcut(AppTest_Descroperation):
     spaceconfig = {'objspace.std.builtinshortcut': True}


More information about the pypy-commit mailing list