From 158f8e4f256b5ba7f000046726a612ba9919e681 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 17 Mar 2017 21:54:51 +0200 Subject: [PATCH] bpo-29839: Raise ValueError rather than OverflowError in len() for negative values. --- Lib/test/test_builtin.py | 8 ++++++++ Misc/NEWS | 3 +++ Objects/typeobject.c | 19 +++++++++++++------ 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 416316c02852c8..62b631c8b08802 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -770,10 +770,18 @@ class FloatLen: 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()) diff --git a/Misc/NEWS b/Misc/NEWS index b7990c62e4f744..a1a2ca8f6d23f7 100644 --- 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 large negative integer. + - bpo-28856: Fix an oversight that %b format for bytes should support objects follow the buffer protocol. diff --git a/Objects/typeobject.c b/Objects/typeobject.c index d70ced04bfed7c..3bfde9c3dc0623 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -5869,14 +5869,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; }