mirror of
https://github.com/python/cpython.git
synced 2025-11-25 04:34:37 +00:00
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.
This commit is contained in:
parent
923ad7a948
commit
4e2f714031
5 changed files with 48 additions and 86 deletions
|
|
@ -433,25 +433,12 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
|
||||||
PyAPI_FUNC(Py_ssize_t) PyObject_Length(PyObject *o);
|
PyAPI_FUNC(Py_ssize_t) PyObject_Length(PyObject *o);
|
||||||
#define PyObject_Length PyObject_Size
|
#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
|
Guess the size of object o using len(o) or o.__length_hint__().
|
||||||
both sequence and mapping protocols, the sequence size is
|
If neither of those return a non-negative value, then return the
|
||||||
returned. On error, -1 is returned. If the object provides
|
default value. This function never fails. All exceptions are cleared.
|
||||||
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
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
PyAPI_FUNC(PyObject *) PyObject_GetItem(PyObject *o, PyObject *key);
|
PyAPI_FUNC(PyObject *) PyObject_GetItem(PyObject *o, PyObject *key);
|
||||||
|
|
|
||||||
|
|
@ -523,7 +523,5 @@ class CommonTest(seq_tests.CommonTest):
|
||||||
# Bug #1242657
|
# Bug #1242657
|
||||||
class F(object):
|
class F(object):
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
yield 23
|
|
||||||
def __len__(self):
|
|
||||||
raise KeyboardInterrupt
|
raise KeyboardInterrupt
|
||||||
self.assertRaises(KeyboardInterrupt, list, F())
|
self.assertRaises(KeyboardInterrupt, list, F())
|
||||||
|
|
|
||||||
|
|
@ -82,29 +82,47 @@ PyObject_Length(PyObject *o)
|
||||||
}
|
}
|
||||||
#define PyObject_Length PyObject_Size
|
#define PyObject_Length PyObject_Size
|
||||||
|
|
||||||
Py_ssize_t
|
|
||||||
_PyObject_LengthHint(PyObject *o)
|
|
||||||
{
|
|
||||||
Py_ssize_t rv = PyObject_Size(o);
|
|
||||||
if (rv != -1)
|
|
||||||
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);
|
/* The length hint function returns a non-negative value from o.__len__()
|
||||||
ro = PyObject_CallMethod(o, "__length_hint__", NULL);
|
or o.__length_hint__(). If those methods aren't found or return a negative
|
||||||
if (ro != NULL) {
|
value, then the defaultvalue is returned. This function never fails.
|
||||||
rv = PyInt_AsLong(ro);
|
Accordingly, it will mask exceptions raised in either method.
|
||||||
Py_DECREF(ro);
|
*/
|
||||||
Py_XDECREF(err_type);
|
|
||||||
Py_XDECREF(err_value);
|
Py_ssize_t
|
||||||
Py_XDECREF(err_tb);
|
_PyObject_LengthHint(PyObject *o, Py_ssize_t defaultvalue)
|
||||||
return rv;
|
{
|
||||||
}
|
static PyObject *hintstrobj = NULL;
|
||||||
PyErr_Restore(err_type, err_value, err_tb);
|
PyObject *ro;
|
||||||
|
Py_ssize_t rv;
|
||||||
|
|
||||||
|
/* try o.__len__() */
|
||||||
|
rv = PyObject_Size(o);
|
||||||
|
if (rv >= 0)
|
||||||
|
return rv;
|
||||||
|
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 *
|
PyObject *
|
||||||
|
|
@ -1505,17 +1523,7 @@ PySequence_Tuple(PyObject *v)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/* Guess result size and allocate space. */
|
/* Guess result size and allocate space. */
|
||||||
n = _PyObject_LengthHint(v);
|
n = _PyObject_LengthHint(v, 10);
|
||||||
if (n < 0) {
|
|
||||||
if (PyErr_Occurred()
|
|
||||||
&& !PyErr_ExceptionMatches(PyExc_TypeError)
|
|
||||||
&& !PyErr_ExceptionMatches(PyExc_AttributeError)) {
|
|
||||||
Py_DECREF(it);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
PyErr_Clear();
|
|
||||||
n = 10; /* arbitrary */
|
|
||||||
}
|
|
||||||
result = PyTuple_New(n);
|
result = PyTuple_New(n);
|
||||||
if (result == NULL)
|
if (result == NULL)
|
||||||
goto Fail;
|
goto Fail;
|
||||||
|
|
|
||||||
|
|
@ -794,17 +794,7 @@ listextend(PyListObject *self, PyObject *b)
|
||||||
iternext = *it->ob_type->tp_iternext;
|
iternext = *it->ob_type->tp_iternext;
|
||||||
|
|
||||||
/* Guess a result list size. */
|
/* Guess a result list size. */
|
||||||
n = _PyObject_LengthHint(b);
|
n = _PyObject_LengthHint(b, 8);
|
||||||
if (n < 0) {
|
|
||||||
if (PyErr_Occurred()
|
|
||||||
&& !PyErr_ExceptionMatches(PyExc_TypeError)
|
|
||||||
&& !PyErr_ExceptionMatches(PyExc_AttributeError)) {
|
|
||||||
Py_DECREF(it);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
PyErr_Clear();
|
|
||||||
n = 8; /* arbitrary */
|
|
||||||
}
|
|
||||||
m = Py_Size(self);
|
m = Py_Size(self);
|
||||||
mn = m + n;
|
mn = m + n;
|
||||||
if (mn >= m) {
|
if (mn >= m) {
|
||||||
|
|
|
||||||
|
|
@ -236,15 +236,7 @@ builtin_filter(PyObject *self, PyObject *args)
|
||||||
goto Fail_arg;
|
goto Fail_arg;
|
||||||
|
|
||||||
/* Guess a result list size. */
|
/* Guess a result list size. */
|
||||||
len = _PyObject_LengthHint(seq);
|
len = _PyObject_LengthHint(seq, 8);
|
||||||
if (len < 0) {
|
|
||||||
if (!PyErr_ExceptionMatches(PyExc_TypeError) &&
|
|
||||||
!PyErr_ExceptionMatches(PyExc_AttributeError)) {
|
|
||||||
goto Fail_it;
|
|
||||||
}
|
|
||||||
PyErr_Clear();
|
|
||||||
len = 8; /* arbitrary */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get a result list. */
|
/* Get a result list. */
|
||||||
if (PyList_Check(seq) && seq->ob_refcnt == 1) {
|
if (PyList_Check(seq) && seq->ob_refcnt == 1) {
|
||||||
|
|
@ -905,15 +897,7 @@ builtin_map(PyObject *self, PyObject *args)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update len. */
|
/* Update len. */
|
||||||
curlen = _PyObject_LengthHint(curseq);
|
curlen = _PyObject_LengthHint(curseq, 8);
|
||||||
if (curlen < 0) {
|
|
||||||
if (!PyErr_ExceptionMatches(PyExc_TypeError) &&
|
|
||||||
!PyErr_ExceptionMatches(PyExc_AttributeError)) {
|
|
||||||
goto Fail_2;
|
|
||||||
}
|
|
||||||
PyErr_Clear();
|
|
||||||
curlen = 8; /* arbitrary */
|
|
||||||
}
|
|
||||||
if (curlen > len)
|
if (curlen > len)
|
||||||
len = curlen;
|
len = curlen;
|
||||||
}
|
}
|
||||||
|
|
@ -2243,13 +2227,8 @@ builtin_zip(PyObject *self, PyObject *args)
|
||||||
len = -1; /* unknown */
|
len = -1; /* unknown */
|
||||||
for (i = 0; i < itemsize; ++i) {
|
for (i = 0; i < itemsize; ++i) {
|
||||||
PyObject *item = PyTuple_GET_ITEM(args, 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 (thislen < 0) {
|
||||||
if (!PyErr_ExceptionMatches(PyExc_TypeError) &&
|
|
||||||
!PyErr_ExceptionMatches(PyExc_AttributeError)) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
PyErr_Clear();
|
|
||||||
len = -1;
|
len = -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue