[pypy-commit] pypy py3.6: Use same logic as CPython in int.__new__ and fix yet another corner case

rlamy pypy.commits at gmail.com
Thu Aug 22 11:49:40 EDT 2019


Author: Ronan Lamy <ronan.lamy at gmail.com>
Branch: py3.6
Changeset: r97244:1a1112535b61
Date: 2019-08-22 16:47 +0100
http://bitbucket.org/pypy/pypy/changeset/1a1112535b61/

Log:	Use same logic as CPython in int.__new__ and fix yet another corner
	case

diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -296,7 +296,6 @@
                     "expected %s, got %T object", expected, self)
 
     def int(self, space):
-        from pypy.objspace.std.intobject import _new_int
         w_impl = space.lookup(self, '__int__')
         if w_impl is None:
             self._typed_unwrap_error(space, "integer")
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
@@ -9,7 +9,7 @@
 import sys
 
 from rpython.rlib import jit
-from rpython.rlib.objectmodel import instantiate
+from rpython.rlib.objectmodel import instantiate, enforceargs
 from rpython.rlib.rarithmetic import (
     LONG_BIT, intmask, is_valid_int, ovfcheck, r_longlong, r_uint,
     string_to_int)
@@ -851,80 +851,49 @@
             sys.maxint == 2147483647)
 
 
-def _string_to_int_or_long(space, w_inttype, w_source, string, base=10):
+def _string_to_int_or_long(space, w_source, string, base=10):
     try:
-        value = string_to_int(string, base, allow_underscores=True, no_implicit_octal=True)
+        value = string_to_int(
+            string, base, allow_underscores=True, no_implicit_octal=True)
+        return wrapint(space, value)
     except ParseStringError as e:
         raise wrap_parsestringerror(space, e, w_source)
     except ParseStringOverflowError as e:
-        return _retry_to_w_long(space, e.parser, w_inttype, w_source)
+        return _retry_to_w_long(space, e.parser, w_source)
 
-    if space.is_w(w_inttype, space.w_int):
-        w_result = wrapint(space, value)
-    else:
-        w_result = space.allocate_instance(W_IntObject, w_inttype)
-        W_IntObject.__init__(w_result, value)
-    return w_result
 
-
-def _retry_to_w_long(space, parser, w_inttype, w_source):
+def _retry_to_w_long(space, parser, w_source):
     from pypy.objspace.std.longobject import newbigint
     parser.rewind()
     try:
         bigint = rbigint._from_numberstring_parser(parser)
     except ParseStringError as e:
         raise wrap_parsestringerror(space, e, w_source)
-    return newbigint(space, w_inttype, bigint)
+    return newbigint(space, space.w_int, bigint)
 
 
 def _new_int(space, w_inttype, w_x, w_base=None):
-    from pypy.objspace.std.longobject import (
-        W_AbstractLongObject, W_LongObject, newlong, newbigint)
-    if space.config.objspace.std.withsmalllong:
-        from pypy.objspace.std.smalllongobject import W_SmallLongObject
+    w_value = w_x     # 'x' is the keyword argument name in CPython
+    if w_inttype is space.w_int:
+        return _new_baseint(space, w_x, w_base)
     else:
-        W_SmallLongObject = None
+        w_tmp = _new_baseint(space, w_x, w_base)
+        return _as_subint(space, w_inttype, w_tmp)
 
-    w_longval = None
-    w_value = w_x     # 'x' is the keyword argument name in CPython
-    value = 0
+def _new_baseint(space, w_value, w_base=None):
     if w_base is None:
-        #import pdb; pdb.set_trace()
-        # check for easy cases
-        if type(w_value) is W_IntObject:
-            if space.is_w(w_inttype, space.w_int):
-                return w_value
-            value = w_value.intval
-            w_obj = space.allocate_instance(W_IntObject, w_inttype)
-            W_IntObject.__init__(w_obj, value)
-            return w_obj
-        elif type(w_value) is W_LongObject:
-            if space.is_w(w_inttype, space.w_int):
-                return w_value
-            return newbigint(space, w_inttype, w_value.num)
-        elif W_SmallLongObject and type(w_value) is W_SmallLongObject:
-            if space.is_w(w_inttype, space.w_int):
-                return w_value
-            return newbigint(space, w_inttype, space.bigint_w(w_value))
+        if space.is_w(space.type(w_value), space.w_int):
+            assert isinstance(w_value, W_AbstractIntObject)
+            return w_value
         elif space.lookup(w_value, '__int__') is not None:
             w_intvalue = space.int(w_value)
-            if isinstance(w_intvalue, W_IntObject):
-                if type(w_intvalue) is not W_IntObject:
-                    w_intvalue = wrapint(space, w_intvalue.intval)
-                return _new_int(space, w_inttype, w_intvalue)
-            elif isinstance(w_intvalue, W_AbstractLongObject):
-                if type(w_intvalue) is not W_LongObject:
-                    w_intvalue = newlong(space, w_intvalue.asbigint())
-                return _new_int(space, w_inttype, w_intvalue)
-            else:
-                # shouldn't happen
-                raise oefmt(space.w_RuntimeError,
-                    "internal error in int.__new__()")
+            return _ensure_baseint(space, w_intvalue)
         elif space.lookup(w_value, '__trunc__') is not None:
             w_obj = space.trunc(w_value)
-            if not space.is_w(space.type(w_obj), space.w_int):
+            if not space.isinstance_w(w_obj, space.w_int):
                 w_obj = space.int(w_obj)
-            return _from_intlike(space, w_inttype, w_obj)
+            assert isinstance(w_obj, W_AbstractIntObject)
+            return _ensure_baseint(space, w_obj)
         elif space.isinstance_w(w_value, space.w_unicode):
             from pypy.objspace.std.unicodeobject import unicode_to_decimal_w
             try:
@@ -933,10 +902,10 @@
                 raise oefmt(space.w_ValueError,
                             'invalid literal for int() with base 10: %R',
                             w_value)
-            return _string_to_int_or_long(space, w_inttype, w_value, b)
+            return _string_to_int_or_long(space, w_value, b)
         elif (space.isinstance_w(w_value, space.w_bytearray) or
               space.isinstance_w(w_value, space.w_bytes)):
-            return _string_to_int_or_long(space, w_inttype, w_value,
+            return _string_to_int_or_long(space, w_value,
                                           space.charbuf_w(w_value))
         else:
             # If object supports the buffer interface
@@ -949,7 +918,7 @@
                             "int() argument must be a string, a bytes-like "
                             "object or a number, not '%T'", w_value)
             else:
-                return _string_to_int_or_long(space, w_inttype, w_value, buf)
+                return _string_to_int_or_long(space, w_value, buf)
     else:
         try:
             base = space.getindex_w(w_base, None)
@@ -973,14 +942,40 @@
             raise oefmt(space.w_TypeError,
                         "int() can't convert non-string with explicit base")
 
-        return _string_to_int_or_long(space, w_inttype, w_value, s, base)
+        return _string_to_int_or_long(space, w_value, s, base)
 
+ at enforceargs(None, None, W_AbstractIntObject, typecheck=False)
+def _as_subint(space, w_inttype, w_value):
+    from pypy.objspace.std.longobject import W_LongObject, newbigint
+    if space.config.objspace.std.withsmalllong:
+        from pypy.objspace.std.smalllongobject import W_SmallLongObject
+    else:
+        W_SmallLongObject = None
+    if type(w_value) is W_IntObject:
+        w_obj = space.allocate_instance(W_IntObject, w_inttype)
+        W_IntObject.__init__(w_obj, w_value.intval)
+        return w_obj
+    elif type(w_value) is W_LongObject:
+        return newbigint(space, w_inttype, w_value.num)
+    elif W_SmallLongObject and type(w_value) is W_SmallLongObject:
+        return newbigint(space, w_inttype, space.bigint_w(w_value))
 
-def _from_intlike(space, w_inttype, w_intlike):
-    if space.is_w(w_inttype, space.w_int):
-        return w_intlike
-    from pypy.objspace.std.longobject import newbigint
-    return newbigint(space, w_inttype, space.bigint_w(w_intlike))
+#@enforceargs(None, W_AbstractIntObject, typecheck=False)
+def _ensure_baseint(space, w_intvalue):
+    from pypy.objspace.std.longobject import (
+        W_LongObject, W_AbstractLongObject, newlong)
+    if isinstance(w_intvalue, W_IntObject):
+        if type(w_intvalue) is not W_IntObject:
+            w_intvalue = wrapint(space, w_intvalue.intval)
+        return w_intvalue
+    elif isinstance(w_intvalue, W_AbstractLongObject):
+        if type(w_intvalue) is not W_LongObject:
+            w_intvalue = newlong(space, w_intvalue.asbigint())
+        return w_intvalue
+    else:
+        # shouldn't happen
+        raise oefmt(space.w_RuntimeError,
+            "internal error in int.__new__()")
 
 
 W_AbstractIntObject.typedef = TypeDef("int",
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
@@ -533,6 +533,19 @@
             assert n == 1
             assert type(n) is int
 
+    def test_trunc_returns_int_subclass_2(self):
+        class BadInt:
+            def __int__(self):
+                return True
+
+        class TruncReturnsBadInt:
+            def __trunc__(self):
+                return BadInt()
+        bad_int = TruncReturnsBadInt()
+        n = int(bad_int)
+        assert n == 1
+        assert type(n) is int
+
     def test_int_before_string(self):
         class Integral(str):
             def __int__(self):


More information about the pypy-commit mailing list