bpo-29839: Raise ValueError rather than OverflowError in len() for negative values...
authorSerhiy Storchaka <storchaka@gmail.com>
Sun, 16 Apr 2017 06:37:18 +0000 (09:37 +0300)
committerGitHub <noreply@github.com>
Sun, 16 Apr 2017 06:37:18 +0000 (09:37 +0300)
Lib/test/test_builtin.py
Misc/NEWS
Objects/typeobject.c

index d2420e9dda1e07f8ae30b35873514a478f8ec172..6613ca3a8ca5737de3ea8825e0d8d68c8298c83a 100644 (file)
@@ -770,10 +770,18 @@ class BuiltinTest(unittest.TestCase):
             def __len__(self):
                 return 4.5
         self.assertRaises(TypeError, len, FloatLen())
+        class NegativeLen:
+            def __len__(self):
+                return -10
+        self.assertRaises(ValueError, len, NegativeLen())
         class HugeLen:
             def __len__(self):
                 return sys.maxsize + 1
         self.assertRaises(OverflowError, len, HugeLen())
+        class HugeNegativeLen:
+            def __len__(self):
+                return -sys.maxsize-10
+        self.assertRaises(ValueError, len, HugeNegativeLen())
         class NoLenMethod(object): pass
         self.assertRaises(TypeError, len, NoLenMethod())
 
index da40d8f5a007c05a04a9f30a2a9588f80ac47167..5981b3a1c795bc084824fd11443cdaca4bbdd263 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,9 @@ What's New in Python 3.7.0 alpha 1?
 Core and Builtins
 -----------------
 
+- bpo-29839: len() now raises ValueError rather than OverflowError if
+  __len__() returned a large negative integer.
+
 - bpo-11913: README.rst is now included in the list of distutils standard
   READMEs and therefore included in source distributions.
 
index ed50946a262aa0c900a1da7d96a017b44a86895a..89fe82c7c39a7d8a72131e82a29c4c24abec3fa4 100644 (file)
@@ -5924,14 +5924,21 @@ slot_sq_length(PyObject *self)
 
     if (res == NULL)
         return -1;
-    len = PyNumber_AsSsize_t(res, PyExc_OverflowError);
-    Py_DECREF(res);
-    if (len < 0) {
-        if (!PyErr_Occurred())
-            PyErr_SetString(PyExc_ValueError,
-                            "__len__() should return >= 0");
+
+    Py_SETREF(res, PyNumber_Index(res));
+    if (res == NULL)
+        return -1;
+
+    assert(PyLong_Check(res));
+    if (Py_SIZE(res) < 0) {
+        PyErr_SetString(PyExc_ValueError,
+                        "__len__() should return >= 0");
         return -1;
     }
+
+    len = PyNumber_AsSsize_t(res, PyExc_OverflowError);
+    assert(len >= 0 || PyErr_ExceptionMatches(PyExc_OverflowError));
+    Py_DECREF(res);
     return len;
 }