diff options
author | Raymond Hettinger <python@rcn.com> | 2007-12-06 00:56:53 (GMT) |
---|---|---|
committer | Raymond Hettinger <python@rcn.com> | 2007-12-06 00:56:53 (GMT) |
commit | 4e2f714031654eb4174393454c008961b636f539 (patch) | |
tree | e415e4318c986483c73d9e5e01b9434a7be9ce83 | |
parent | 923ad7a9488c9b514c8e27315179ada1d142f3e5 (diff) | |
download | cpython-4e2f714031654eb4174393454c008961b636f539.zip cpython-4e2f714031654eb4174393454c008961b636f539.tar.gz cpython-4e2f714031654eb4174393454c008961b636f539.tar.bz2 |
Fix Issue 1045.
Factor-out common calling code by simplifying the length_hint API.
Speed-up the function by caching the PyObject_String for the attribute lookup.
-rw-r--r-- | Include/abstract.h | 21 | ||||
-rw-r--r-- | Lib/test/list_tests.py | 2 | ||||
-rw-r--r-- | Objects/abstract.c | 68 | ||||
-rw-r--r-- | Objects/listobject.c | 12 | ||||
-rw-r--r-- | Python/bltinmodule.c | 27 |
5 files changed, 46 insertions, 84 deletions
diff --git a/Include/abstract.h b/Include/abstract.h index 29f091e..764d7d8 100644 --- a/Include/abstract.h +++ b/Include/abstract.h @@ -433,25 +433,12 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ PyAPI_FUNC(Py_ssize_t) PyObject_Length(PyObject *o); #define PyObject_Length PyObject_Size - PyAPI_FUNC(Py_ssize_t) _PyObject_LengthHint(PyObject *o); + PyAPI_FUNC(Py_ssize_t) _PyObject_LengthHint(PyObject *o, Py_ssize_t); /* - Return the size of object o. If the object, o, provides - both sequence and mapping protocols, the sequence size is - returned. On error, -1 is returned. If the object provides - a __length_hint__() method, its value is returned. This is an - internal undocumented API provided for performance reasons; - for compatibility, don't use it outside the core. This is the - equivalent to the Python expression: - try: - return len(o) - except (AttributeError, TypeError): - exc_type, exc_value, exc_tb = sys.exc_info() - try: - return o.__length_hint__() - except: - pass - raise exc_type, exc_value, exc_tb + Guess the size of object o using len(o) or o.__length_hint__(). + If neither of those return a non-negative value, then return the + default value. This function never fails. All exceptions are cleared. */ PyAPI_FUNC(PyObject *) PyObject_GetItem(PyObject *o, PyObject *key); diff --git a/Lib/test/list_tests.py b/Lib/test/list_tests.py index a0011a4..88bc49d 100644 --- a/Lib/test/list_tests.py +++ b/Lib/test/list_tests.py @@ -523,7 +523,5 @@ class CommonTest(seq_tests.CommonTest): # Bug #1242657 class F(object): def __iter__(self): - yield 23 - def __len__(self): raise KeyboardInterrupt self.assertRaises(KeyboardInterrupt, list, F()) diff --git a/Objects/abstract.c b/Objects/abstract.c index 1cb4ef8..4c8ef83 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -82,29 +82,47 @@ PyObject_Length(PyObject *o) } #define PyObject_Length PyObject_Size + +/* The length hint function returns a non-negative value from o.__len__() + or o.__length_hint__(). If those methods aren't found or return a negative + value, then the defaultvalue is returned. This function never fails. + Accordingly, it will mask exceptions raised in either method. +*/ + Py_ssize_t -_PyObject_LengthHint(PyObject *o) +_PyObject_LengthHint(PyObject *o, Py_ssize_t defaultvalue) { - Py_ssize_t rv = PyObject_Size(o); - if (rv != -1) + static PyObject *hintstrobj = NULL; + PyObject *ro; + Py_ssize_t rv; + + /* try o.__len__() */ + rv = PyObject_Size(o); + if (rv >= 0) return rv; - if (PyErr_ExceptionMatches(PyExc_TypeError) || - PyErr_ExceptionMatches(PyExc_AttributeError)) { - PyObject *err_type, *err_value, *err_tb, *ro; - - PyErr_Fetch(&err_type, &err_value, &err_tb); - ro = PyObject_CallMethod(o, "__length_hint__", NULL); - if (ro != NULL) { - rv = PyInt_AsLong(ro); - Py_DECREF(ro); - Py_XDECREF(err_type); - Py_XDECREF(err_value); - Py_XDECREF(err_tb); - return rv; - } - PyErr_Restore(err_type, err_value, err_tb); + if (PyErr_Occurred()) + PyErr_Clear(); + + /* cache a hashed version of the attribute string */ + if (hintstrobj == NULL) { + hintstrobj = PyString_InternFromString("__length_hint__"); + if (hintstrobj == NULL) + goto defaultcase; } - return -1; + + /* try o.__length_hint__() */ + ro = PyObject_CallMethodObjArgs(o, hintstrobj, NULL); + if (ro == NULL) + goto defaultcase; + rv = PyInt_AsLong(ro); + Py_DECREF(ro); + if (rv >= 0) + return rv; + +defaultcase: + if (PyErr_Occurred()) + PyErr_Clear(); + return defaultvalue; } PyObject * @@ -1505,17 +1523,7 @@ PySequence_Tuple(PyObject *v) return NULL; /* Guess result size and allocate space. */ - n = _PyObject_LengthHint(v); - if (n < 0) { - if (PyErr_Occurred() - && !PyErr_ExceptionMatches(PyExc_TypeError) - && !PyErr_ExceptionMatches(PyExc_AttributeError)) { - Py_DECREF(it); - return NULL; - } - PyErr_Clear(); - n = 10; /* arbitrary */ - } + n = _PyObject_LengthHint(v, 10); result = PyTuple_New(n); if (result == NULL) goto Fail; diff --git a/Objects/listobject.c b/Objects/listobject.c index 8389a86..ca767da 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -794,17 +794,7 @@ listextend(PyListObject *self, PyObject *b) iternext = *it->ob_type->tp_iternext; /* Guess a result list size. */ - n = _PyObject_LengthHint(b); - if (n < 0) { - if (PyErr_Occurred() - && !PyErr_ExceptionMatches(PyExc_TypeError) - && !PyErr_ExceptionMatches(PyExc_AttributeError)) { - Py_DECREF(it); - return NULL; - } - PyErr_Clear(); - n = 8; /* arbitrary */ - } + n = _PyObject_LengthHint(b, 8); m = Py_Size(self); mn = m + n; if (mn >= m) { diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 1667d37..e1242fd 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -236,15 +236,7 @@ builtin_filter(PyObject *self, PyObject *args) goto Fail_arg; /* Guess a result list size. */ - len = _PyObject_LengthHint(seq); - if (len < 0) { - if (!PyErr_ExceptionMatches(PyExc_TypeError) && - !PyErr_ExceptionMatches(PyExc_AttributeError)) { - goto Fail_it; - } - PyErr_Clear(); - len = 8; /* arbitrary */ - } + len = _PyObject_LengthHint(seq, 8); /* Get a result list. */ if (PyList_Check(seq) && seq->ob_refcnt == 1) { @@ -905,15 +897,7 @@ builtin_map(PyObject *self, PyObject *args) } /* Update len. */ - curlen = _PyObject_LengthHint(curseq); - if (curlen < 0) { - if (!PyErr_ExceptionMatches(PyExc_TypeError) && - !PyErr_ExceptionMatches(PyExc_AttributeError)) { - goto Fail_2; - } - PyErr_Clear(); - curlen = 8; /* arbitrary */ - } + curlen = _PyObject_LengthHint(curseq, 8); if (curlen > len) len = curlen; } @@ -2243,13 +2227,8 @@ builtin_zip(PyObject *self, PyObject *args) len = -1; /* unknown */ for (i = 0; i < itemsize; ++i) { PyObject *item = PyTuple_GET_ITEM(args, i); - Py_ssize_t thislen = _PyObject_LengthHint(item); + Py_ssize_t thislen = _PyObject_LengthHint(item, -1); if (thislen < 0) { - if (!PyErr_ExceptionMatches(PyExc_TypeError) && - !PyErr_ExceptionMatches(PyExc_AttributeError)) { - return NULL; - } - PyErr_Clear(); len = -1; break; } |