It is my understanding that, in Python 3000, certain functions and methods that currently return lists will return some sort of view type (e.g. dict.values()) or an iterator (e.g. zip). So certain usage patterns will no longer be supported e.g. d.keys().sort().

The attached patch, which is a diff against the subversion "trunk" of Python 2.x, tries to warn the user about these kind of future-unsafe usage patterns. It works by storing the type that the list will become in the future, at creation time, and checking to see if called list functions will be supported by that type in the future.

Currently the patch if very incomplete and the idea itself may be flawed. But I thought it was interesting to run against my own code to see what potential problems it has. Example:

...
Type "help", "copyright", "credits" or "license" for more information.
>>> d = {"apple" : "sweet", "orange" : "tangy"}
>>> "juicy" in d.values()
False
>>> d.keys().sort()
__main__:1: DeprecationWarning: dictionary view will not support sort
>>> "a" in zip([1,2,3,4], "abcd")
__main__:1: DeprecationWarning: iterator will not support contains
False

Cheers,
Brian
Index: Python/bltinmodule.c
===================================================================
--- Python/bltinmodule.c        (revision 51629)
+++ Python/bltinmodule.c        (working copy)
@@ -1570,7 +1570,7 @@
                goto Fail;
        }
 
-       v = PyList_New(n);
+       v = PyList_NewFutureType(n, PY_BECOME_ITER);
        if (v == NULL)
                goto Fail;
 
@@ -1678,7 +1678,7 @@
                                "range() result has too many items");
                return NULL;
        }
-       v = PyList_New(n);
+       v = PyList_NewFutureType(n, PY_BECOME_ITER);
        if (v == NULL)
                return NULL;
        for (i = 0; i < n; i++) {
@@ -2120,7 +2120,7 @@
        Py_ssize_t len;    /* guess at result length */
 
        if (itemsize == 0)
-               return PyList_New(0);
+               return PyList_NewFutureType(0, PY_BECOME_ITER);
 
        /* args must be a tuple */
        assert(PyTuple_Check(args));
@@ -2148,7 +2148,7 @@
        /* allocate result list */
        if (len < 0)
                len = 10;       /* arbitrary */
-       if ((ret = PyList_New(len)) == NULL)
+       if ((ret = PyList_NewFutureType(len, PY_BECOME_ITER)) == NULL)
                return NULL;
 
        /* obtain iterators */
Index: Include/listobject.h
===================================================================
--- Include/listobject.h        (revision 51629)
+++ Include/listobject.h        (working copy)
@@ -19,6 +19,12 @@
 extern "C" {
 #endif
 
+/* Constants representing the types that may be used instead of a list
+   in Python 3000 */
+#define PY_REMAIN_LIST     0x01  /* List will remain a list in Py2K */
+#define PY_BECOME_DICTVIEW 0x02  /* List will become a "view" on a dict */
+#define PY_BECOME_ITER     0x04  /* List will become an iterator */
+
 typedef struct {
     PyObject_VAR_HEAD
     /* Vector of pointers to list elements.  list[0] is ob_item[0], etc. */
@@ -36,6 +42,7 @@
      * the list is not yet visible outside the function that builds it.
      */
     Py_ssize_t allocated;
+    int future_type; /* The type the object will have in Py3K */
 } PyListObject;
 
 PyAPI_DATA(PyTypeObject) PyList_Type;
@@ -44,6 +51,7 @@
 #define PyList_CheckExact(op) ((op)->ob_type == &PyList_Type)
 
 PyAPI_FUNC(PyObject *) PyList_New(Py_ssize_t size);
+PyAPI_FUNC(PyObject *) PyList_NewFutureType(Py_ssize_t size, int future_type);
 PyAPI_FUNC(Py_ssize_t) PyList_Size(PyObject *);
 PyAPI_FUNC(PyObject *) PyList_GetItem(PyObject *, Py_ssize_t);
 PyAPI_FUNC(int) PyList_SetItem(PyObject *, Py_ssize_t, PyObject *);
@@ -57,6 +65,9 @@
 PyAPI_FUNC(PyObject *) _PyList_Extend(PyListObject *, PyObject *);
 
 /* Macro, trading safety for speed */
+/* XXX These functions do not (yet) trigger future usage warnings.
+   So e.g. range(100)[0] will slip though
+*/
 #define PyList_GET_ITEM(op, i) (((PyListObject *)(op))->ob_item[i])
 #define PyList_SET_ITEM(op, i, v) (((PyListObject *)(op))->ob_item[i] = (v))
 #define PyList_GET_SIZE(op)    (((PyListObject *)(op))->ob_size)
Index: Objects/dictobject.c
===================================================================
--- Objects/dictobject.c        (revision 51629)
+++ Objects/dictobject.c        (working copy)
@@ -1003,7 +1003,7 @@
 
   again:
        n = mp->ma_used;
-       v = PyList_New(n);
+       v = PyList_NewFutureType(n, PY_BECOME_DICTVIEW);
        if (v == NULL)
                return NULL;
        if (n != mp->ma_used) {
@@ -1037,7 +1037,7 @@
 
   again:
        n = mp->ma_used;
-       v = PyList_New(n);
+       v = PyList_NewFutureType(n, PY_BECOME_DICTVIEW);
        if (v == NULL)
                return NULL;
        if (n != mp->ma_used) {
@@ -1076,7 +1076,7 @@
         */
   again:
        n = mp->ma_used;
-       v = PyList_New(n);
+       v = PyList_NewFutureType(n, PY_BECOME_DICTVIEW);
        if (v == NULL)
                return NULL;
        for (i = 0; i < n; i++) {
Index: Objects/listobject.c
===================================================================
--- Objects/listobject.c        (revision 51629)
+++ Objects/listobject.c        (working copy)
@@ -8,6 +8,49 @@
 #include <sys/types.h>         /* For size_t */
 #endif
 
+static int warn_future_usage(PyListObject *self,
+                                                int supported_types, char 
*operation)
+{
+       char message[256];
+
+       if ((((PyListObject *) self)->future_type & supported_types) == 0)
+       {
+               switch (self->future_type) {
+                       case PY_BECOME_DICTVIEW:
+                               PyOS_snprintf(message, sizeof(message), 
+                                       "dictionary view will not support %s", 
+                                       operation);
+                               break;
+                       case PY_BECOME_ITER:
+                               PyOS_snprintf(message, sizeof(message), 
+                                       "iterator will not support %s", 
+                                       operation);
+                               break;
+                       default: /* This shouldn't happen */
+                               PyErr_BadInternalCall();
+                               return -1;
+               }
+
+               /* XXX This should be PyExc_PendingDeprecationWarning */
+               if (PyErr_WarnEx(PyExc_DeprecationWarning, message, 1) < 0)
+                       return -1;
+       }
+
+       return 0;
+}
+
+#define WARN_LIST_USAGE(self, supported_types, operation) \
+       if (warn_future_usage((PyListObject *) self, \
+                           supported_types, operation) < 0) \
+               return NULL;
+
+#define WARN_LIST_USAGE_INT(self, supported_types, operation) \
+       if (warn_future_usage((PyListObject *) self, \
+                                                  supported_types, operation) 
< 0) \
+               return -1;
+
+#define PyList_Check(op) PyObject_TypeCheck(op, &PyList_Type)
+
 /* Ensure ob_item has room for at least newsize elements, and set
  * ob_size to newsize.  If newsize > ob_size on entry, the content
  * of the new slots at exit is undefined heap trash; it's the caller's
@@ -116,10 +159,29 @@
        }
        op->ob_size = size;
        op->allocated = size;
+       op->future_type = PY_REMAIN_LIST;
        _PyObject_GC_TRACK(op);
        return (PyObject *) op;
 }
 
+PyObject *
+PyList_NewFutureType(Py_ssize_t size, int future_type)
+{
+       PyListObject *op = (PyListObject *) PyList_New(size);
+       if (op == NULL)
+               return NULL;
+       else {
+               if (future_type == 0)
+               {
+                       Py_DECREF(op);
+                       PyErr_BadInternalCall();
+                       return NULL;
+               }
+               op->future_type = future_type;
+               return (PyObject *) op;
+       }
+}
+
 Py_ssize_t
 PyList_Size(PyObject *op)
 {
@@ -369,6 +431,7 @@
 static Py_ssize_t
 list_length(PyListObject *a)
 {
+       WARN_LIST_USAGE_INT(a, PY_REMAIN_LIST | PY_BECOME_DICTVIEW, "len");
        return a->ob_size;
 }
 
@@ -378,6 +441,7 @@
        Py_ssize_t i;
        int cmp;
 
+       WARN_LIST_USAGE_INT(a, PY_REMAIN_LIST | PY_BECOME_DICTVIEW, "contains");
        for (i = 0, cmp = 0 ; cmp == 0 && i < a->ob_size; ++i)
                cmp = PyObject_RichCompareBool(el, PyList_GET_ITEM(a, i),
                                                   Py_EQ);
@@ -387,6 +451,7 @@
 static PyObject *
 list_item(PyListObject *a, Py_ssize_t i)
 {
+       WARN_LIST_USAGE(a, PY_REMAIN_LIST, "item indexing");
        if (i < 0 || i >= a->ob_size) {
                if (indexerr == NULL)
                        indexerr = PyString_FromString(
@@ -404,6 +469,8 @@
        PyListObject *np;
        PyObject **src, **dest;
        Py_ssize_t i, len;
+
+       WARN_LIST_USAGE(a, PY_REMAIN_LIST, "slicing");
        if (ilow < 0)
                ilow = 0;
        else if (ilow > a->ob_size)
@@ -444,6 +511,9 @@
        Py_ssize_t i;
        PyObject **src, **dest;
        PyListObject *np;
+
+       WARN_LIST_USAGE(a, PY_REMAIN_LIST, "concatenation");
+
        if (!PyList_Check(bb)) {
                PyErr_Format(PyExc_TypeError,
                          "can only concatenate list (not \"%.200s\") to list",
@@ -484,6 +554,8 @@
        PyListObject *np;
        PyObject **p, **items;
        PyObject *elem;
+
+       WARN_LIST_USAGE(a, PY_REMAIN_LIST, "repitition");
        if (n < 0)
                n = 0;
        size = a->ob_size * n;
@@ -521,6 +593,8 @@
 {
        Py_ssize_t i;
        PyObject **item = a->ob_item;
+
+       WARN_LIST_USAGE_INT(a, PY_REMAIN_LIST, "clear");
        if (item != NULL) {
                /* Because XDECREF can recursively invoke operations on
                   this list, we make it empty first. */
@@ -565,6 +639,9 @@
        Py_ssize_t k;
        size_t s;
        int result = -1;        /* guilty until proved innocent */
+
+       WARN_LIST_USAGE_INT(a, PY_REMAIN_LIST, "slicing");
+
 #define b ((PyListObject *)v)
        if (v == NULL)
                n = 0;
@@ -658,9 +735,9 @@
 {
        PyObject **items;
        Py_ssize_t size, i, j, p;
+       size = PyList_GET_SIZE(self);
 
-
-       size = PyList_GET_SIZE(self);
+       WARN_LIST_USAGE(self, PY_REMAIN_LIST, "repeat");
        if (size == 0) {
                Py_INCREF(self);
                return (PyObject *)self;
@@ -692,6 +769,8 @@
 list_ass_item(PyListObject *a, Py_ssize_t i, PyObject *v)
 {
        PyObject *old_value;
+
+       WARN_LIST_USAGE_INT(a, PY_REMAIN_LIST, "item assignment");
        if (i < 0 || i >= a->ob_size) {
                PyErr_SetString(PyExc_IndexError,
                                "list assignment index out of range");
@@ -711,6 +790,8 @@
 {
        Py_ssize_t i;
        PyObject *v;
+
+       WARN_LIST_USAGE(self, PY_REMAIN_LIST, "insert");
        if (!PyArg_ParseTuple(args, "nO:insert", &i, &v))
                return NULL;
        if (ins1(self, i, v) == 0)
@@ -721,6 +802,7 @@
 static PyObject *
 listappend(PyListObject *self, PyObject *v)
 {
+       WARN_LIST_USAGE(self, PY_REMAIN_LIST, "append");
        if (app1(self, v) == 0)
                Py_RETURN_NONE;
        return NULL;
@@ -736,6 +818,7 @@
        Py_ssize_t i;
        PyObject *(*iternext)(PyObject *);
 
+       WARN_LIST_USAGE(self, PY_REMAIN_LIST, "extend");
        /* Special cases:
           1) lists and tuples which can use PySequence_Fast ops
           2) extending self to self requires making a copy first
@@ -851,6 +934,7 @@
 {
        PyObject *result;
 
+       WARN_LIST_USAGE(self, PY_REMAIN_LIST, "concatentation");
        result = listextend(self, other);
        if (result == NULL)
                return result;
@@ -866,6 +950,7 @@
        PyObject *v, *arg = NULL;
        int status;
 
+       WARN_LIST_USAGE(self, PY_REMAIN_LIST, "pop");
        if (!PyArg_UnpackTuple(args, "pop", 0, 1, &arg))
                return NULL;
        if (arg != NULL) {
@@ -1995,6 +2080,8 @@
        PyObject *key, *value, *kvpair;
        static char *kwlist[] = {"cmp", "key", "reverse", 0};
 
+       WARN_LIST_USAGE(self, PY_REMAIN_LIST, "sort");
+
        assert(self != NULL);
        assert (PyList_Check(self));
        if (args != NULL) {
@@ -2163,6 +2250,7 @@
 static PyObject *
 listreverse(PyListObject *self)
 {
+       WARN_LIST_USAGE(self, PY_REMAIN_LIST, "reverse");
        if (self->ob_size > 1)
                reverse_slice(self->ob_item, self->ob_item + self->ob_size);
        Py_RETURN_NONE;
@@ -2213,6 +2301,7 @@
        Py_ssize_t i, start=0, stop=self->ob_size;
        PyObject *v;
 
+       WARN_LIST_USAGE(self, PY_REMAIN_LIST, "index");
        if (!PyArg_ParseTuple(args, "O|O&O&:index", &v,
                                    _PyEval_SliceIndex, &start,
                                    _PyEval_SliceIndex, &stop))
@@ -2244,6 +2333,7 @@
        Py_ssize_t count = 0;
        Py_ssize_t i;
 
+       WARN_LIST_USAGE(self, PY_REMAIN_LIST, "count");
        for (i = 0; i < self->ob_size; i++) {
                int cmp = PyObject_RichCompareBool(self->ob_item[i], v, Py_EQ);
                if (cmp > 0)
@@ -2259,6 +2349,7 @@
 {
        Py_ssize_t i;
 
+       WARN_LIST_USAGE(self, PY_REMAIN_LIST, "remove");
        for (i = 0; i < self->ob_size; i++) {
                int cmp = PyObject_RichCompareBool(self->ob_item[i], v, Py_EQ);
                if (cmp > 0) {
@@ -2372,6 +2463,7 @@
               self->allocated == 0 || self->allocated == -1);
 
        /* Empty previous contents */
+       self->future_type = PY_REMAIN_LIST;
        if (self->ob_item != NULL) {
                (void)list_clear(self);
        }
@@ -2456,6 +2548,8 @@
 static PyObject *
 list_subscript(PyListObject* self, PyObject* item)
 {
+       WARN_LIST_USAGE(self, PY_REMAIN_LIST, "__getitem__");
+
        if (PyIndex_Check(item)) {
                Py_ssize_t i;
                i = PyNumber_AsSsize_t(item, PyExc_IndexError);
@@ -2505,6 +2599,7 @@
 static int
 list_ass_subscript(PyListObject* self, PyObject* item, PyObject* value)
 {
+       WARN_LIST_USAGE_INT(self, PY_REMAIN_LIST, "item assignment");
        if (PyIndex_Check(item)) {
                Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
                if (i == -1 && PyErr_Occurred())
@@ -2874,6 +2969,7 @@
 {
        listreviterobject *it;
 
+       WARN_LIST_USAGE(seq, PY_REMAIN_LIST, "reversed");
        it = PyObject_GC_New(listreviterobject, &PyListRevIter_Type);
        if (it == NULL)
                return NULL;
_______________________________________________
Python-3000 mailing list
[email protected]
http://mail.python.org/mailman/listinfo/python-3000
Unsubscribe: 
http://mail.python.org/mailman/options/python-3000/archive%40mail-archive.com

Reply via email to