]> granicus.if.org Git - python/commitdiff
Fix Issue 1045.
authorRaymond Hettinger <python@rcn.com>
Thu, 6 Dec 2007 00:56:53 +0000 (00:56 +0000)
committerRaymond Hettinger <python@rcn.com>
Thu, 6 Dec 2007 00:56:53 +0000 (00:56 +0000)
Factor-out common calling code by simplifying the length_hint API.
Speed-up the function by caching the PyObject_String for the attribute lookup.

Include/abstract.h
Lib/test/list_tests.py
Objects/abstract.c
Objects/listobject.c
Python/bltinmodule.c

index 29f091e2ace2ff6e3eb10cb2aff8166bea3d1689..764d7d861eb830f153a143d6c054a5c2845cebbd 100644 (file)
@@ -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);
index a0011a46fbf02a64cb733f569142ffd5a0cc8ace..88bc49d6fdaa8becaf1283a3de77bc905c5932fb 100644 (file)
@@ -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())
index 1cb4ef819a24873aeeb3e77bfd64969456cf96c6..4c8ef8361ff05c4547416799b0adad1697fb1567 100644 (file)
@@ -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;
index 8389a86ea713efa143269f3f80df71f91d4194b7..ca767da9dcc5f011605085bb83a08b6ae64cc965 100644 (file)
@@ -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) {
index 1667d37742afee3d2e4717edf4d3b83fcdde7424..e1242fdede475489623fa1ee8b313e6a903504eb 100644 (file)
@@ -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;
                }