[Python-3000] Warning about future-unsafe usage patterns in Python 2.x e.g. dict.keys().sort()
Guido van Rossum
guido at python.org
Mon Aug 28 18:22:52 CEST 2006
Not much time to review the patch, but +1 on this -- I've described
this a few times in my Py3k talk, glad that some code is forthcoming
now!
--Guido
On 8/28/06, Brian Quinlan <brian at sweetapp.com> wrote:
> 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
> Python-3000 at python.org
> http://mail.python.org/mailman/listinfo/python-3000
> Unsubscribe: http://mail.python.org/mailman/options/python-3000/guido%40python.org
>
>
>
--
--Guido van Rossum (home page: http://www.python.org/~guido/)
More information about the Python-3000
mailing list