Factor-out common calling code by simplifying the length_hint API.
Speed-up the function by caching the PyObject_String for the attribute lookup.
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);
# Bug #1242657
class F(object):
def __iter__(self):
- yield 23
- def __len__(self):
raise KeyboardInterrupt
self.assertRaises(KeyboardInterrupt, list, F())
}
#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 *
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;
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) {
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) {
}
/* 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;
}
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;
}