[Python-3000] Warning about future-unsafe usage patterns in Python 2.x e.g. dict.keys().sort() (original) (raw)
Guido van Rossum guido at python.org
Mon Aug 28 18:22:52 CEST 2006
- Previous message: [Python-3000] Warning about future-unsafe usage patterns in Python 2.x e.g. dict.keys().sort()
- Next message: [Python-3000] Making more effective use of slice objects in Py3k
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
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 = PyListNew(n); + v = PyListNewFutureType(n, PYBECOMEITER); if (v == NULL) goto Fail; @@ -1678,7 +1678,7 @@ "range() result has too many items"); return NULL; } - v = PyListNew(n); + v = PyListNewFutureType(n, PYBECOMEITER); if (v == NULL) return NULL; for (i = 0; i < n; i++) {_ _@@ -2120,7 +2120,7 @@_ _Pyssizet len; /* guess at result length */_ _if (itemsize == 0)_ _- return PyListNew(0);_ _+ return PyListNewFutureType(0, PYBECOMEITER);_ _/* args must be a tuple */_ _assert(PyTupleCheck(args));_ _@@ -2148,7 +2148,7 @@_ _/* allocate result list */_ _if (len < 0)_ _len = 10; /* arbitrary */_ _- if ((ret = PyListNew(len)) == NULL)_ _+ if ((ret = PyListNewFutureType(len, PYBECOMEITER)) == 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 PYREMAINLIST 0x01 /* List will remain a list in Py2K */_ _+#define PYBECOMEDICTVIEW 0x02 /* List will become a "view" on a dict */_ _+#define PYBECOMEITER 0x04 /* List will become an iterator */_ _+_ _typedef struct {_ _PyObjectVARHEAD_ _/* Vector of pointers to list elements. list[0] is obitem[0], etc. */_ _@@ -36,6 +42,7 @@_ _* the list is not yet visible outside the function that builds it._ _*/_ _Pyssizet allocated;_ _+ int futuretype; /* The type the object will have in Py3K */_ _} PyListObject;_ _PyAPIDATA(PyTypeObject) PyListType;_ _@@ -44,6 +51,7 @@_ _#define PyListCheckExact(op) ((op)->obtype == &PyListType) PyAPIFUNC(PyObject *) PyListNew(Pyssizet size); +PyAPIFUNC(PyObject *) PyListNewFutureType(Pyssizet size, int futuretype); PyAPIFUNC(Pyssizet) PyListSize(PyObject *); PyAPIFUNC(PyObject *) PyListGetItem(PyObject *, Pyssizet); PyAPIFUNC(int) PyListSetItem(PyObject *, Pyssizet, PyObject *); @@ -57,6 +65,9 @@ PyAPIFUNC(PyObject *) PyListExtend(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 PyListGETITEM(op, i) (((PyListObject *)(op))->obitem[i]) #define PyListSETITEM(op, i, v) (((PyListObject *)(op))->obitem[i] = (v)) #define PyListGETSIZE(op) (((PyListObject *)(op))->obsize) Index: Objects/dictobject.c =================================================================== --- Objects/dictobject.c (revision 51629) +++ Objects/dictobject.c (working copy) @@ -1003,7 +1003,7 @@ again: n = mp->maused; - v = PyListNew(n); + v = PyListNewFutureType(n, PYBECOMEDICTVIEW); if (v == NULL) return NULL; if (n != mp->maused) { @@ -1037,7 +1037,7 @@ again: n = mp->maused; - v = PyListNew(n); + v = PyListNewFutureType(n, PYBECOMEDICTVIEW); if (v == NULL) return NULL; if (n != mp->maused) { @@ -1076,7 +1076,7 @@ */ again: n = mp->maused; - v = PyListNew(n); + v = PyListNewFutureType(n, PYBECOMEDICTVIEW); 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 sizet */ #endif +static int warnfutureusage(PyListObject *self, + int supportedtypes, char *operation) +{ + char message[256]; + + if ((((PyListObject *) self)->futuretype & supportedtypes) == 0) + { + switch (self->futuretype) { + case PYBECOMEDICTVIEW: + PyOSsnprintf(message, sizeof(message), + "dictionary view will not support %s", + operation); + break; + case PYBECOMEITER: + PyOSsnprintf(message, sizeof(message), + "iterator will not support %s", + operation); + break; + default: /* This shouldn't happen */ + PyErrBadInternalCall(); + return -1; + } + + /* XXX This should be PyExcPendingDeprecationWarning */ + if (PyErrWarnEx(PyExcDeprecationWarning, message, 1) < 0)_ _+ return -1;_ _+ }_ _+_ _+ return 0;_ _+}_ _+_ _+#define WARNLISTUSAGE(self, supportedtypes, operation) _ _+ if (warnfutureusage((PyListObject *) self, _ _+ supportedtypes, operation) < 0) _ _+ return NULL;_ _+_ _+#define WARNLISTUSAGEINT(self, supportedtypes, operation) _ _+ if (warnfutureusage((PyListObject *) self, _ _+ supportedtypes, operation) < 0) _ _+ return -1;_ _+_ _+#define PyListCheck(op) PyObjectTypeCheck(op, &PyListType)_ _+_ _/* Ensure obitem has room for at least newsize elements, and set_ _* obsize to newsize. If newsize > obsize on entry, the content * of the new slots at exit is undefined heap trash; it's the caller's @@ -116,10 +159,29 @@ } op->obsize = size; op->allocated = size; + op->futuretype = PYREMAINLIST; PyObjectGCTRACK(op); return (PyObject *) op; } +PyObject * +PyListNewFutureType(Pyssizet size, int futuretype) +{ + PyListObject *op = (PyListObject *) PyListNew(size); + if (op == NULL) + return NULL; + else { + if (futuretype == 0) + { + PyDECREF(op); + PyErrBadInternalCall(); + return NULL; + } + op->futuretype = futuretype; + return (PyObject *) op; + } +} + Pyssizet PyListSize(PyObject *op) { @@ -369,6 +431,7 @@ static Pyssizet listlength(PyListObject *a) { + WARNLISTUSAGEINT(a, PYREMAINLIST | PYBECOMEDICTVIEW, "len"); return a->obsize; } @@ -378,6 +441,7 @@ Pyssizet i; int cmp; + WARNLISTUSAGEINT(a, PYREMAINLIST | PYBECOMEDICTVIEW, "contains"); for (i = 0, cmp = 0 ; cmp == 0 && i < a->obsize; ++i) cmp = PyObjectRichCompareBool(el, PyListGETITEM(a, i), PyEQ); @@ -387,6 +451,7 @@ static PyObject * listitem(PyListObject *a, Pyssizet i) { + WARNLISTUSAGE(a, PYREMAINLIST, "item indexing"); if (i < 0 || i >= a->obsize) { if (indexerr == NULL) indexerr = PyStringFromString( @@ -404,6 +469,8 @@ PyListObject *np; PyObject **src, **dest; Pyssizet i, len; + + WARNLISTUSAGE(a, PYREMAINLIST, "slicing"); if (ilow < 0)_ _ilow = 0;_ _else if (ilow > a->obsize) @@ -444,6 +511,9 @@ Pyssizet i; PyObject **src, **dest; PyListObject *np; + + WARNLISTUSAGE(a, PYREMAINLIST, "concatenation"); + if (!PyListCheck(bb)) { PyErrFormat(PyExcTypeError, "can only concatenate list (not "%.200s") to list", @@ -484,6 +554,8 @@ PyListObject *np; PyObject **p, **items; PyObject *elem; + + WARNLISTUSAGE(a, PYREMAINLIST, "repitition"); if (n < 0)_ _n = 0;_ _size = a->obsize * n; @@ -521,6 +593,8 @@ { Pyssizet i; PyObject **item = a->obitem; + + WARNLISTUSAGEINT(a, PYREMAINLIST, "clear"); if (item != NULL) { /* Because XDECREF can recursively invoke operations on this list, we make it empty first. */ @@ -565,6 +639,9 @@ Pyssizet k; sizet s; int result = -1; /* guilty until proved innocent */ + + WARNLISTUSAGEINT(a, PYREMAINLIST, "slicing"); + #define b ((PyListObject *)v) if (v == NULL) n = 0; @@ -658,9 +735,9 @@ { PyObject **items; Pyssizet size, i, j, p; + size = PyListGETSIZE(self); - - size = PyListGETSIZE(self); + WARNLISTUSAGE(self, PYREMAINLIST, "repeat"); if (size == 0) { PyINCREF(self); return (PyObject *)self; @@ -692,6 +769,8 @@ listassitem(PyListObject *a, Pyssizet i, PyObject *v) { PyObject *oldvalue; + + WARNLISTUSAGEINT(a, PYREMAINLIST, "item assignment"); if (i < 0 || i >= a->obsize) { PyErrSetString(PyExcIndexError, "list assignment index out of range"); @@ -711,6 +790,8 @@ { Pyssizet i; PyObject *v; + + WARNLISTUSAGE(self, PYREMAINLIST, "insert"); if (!PyArgParseTuple(args, "nO:insert", &i, &v)) return NULL; if (ins1(self, i, v) == 0) @@ -721,6 +802,7 @@ static PyObject * listappend(PyListObject *self, PyObject *v) { + WARNLISTUSAGE(self, PYREMAINLIST, "append"); if (app1(self, v) == 0) PyRETURNNONE; return NULL; @@ -736,6 +818,7 @@ Pyssizet i; PyObject *(*iternext)(PyObject *); + WARNLISTUSAGE(self, PYREMAINLIST, "extend"); /* Special cases: 1) lists and tuples which can use PySequenceFast ops 2) extending self to self requires making a copy first @@ -851,6 +934,7 @@ { PyObject *result; + WARNLISTUSAGE(self, PYREMAINLIST, "concatentation"); result = listextend(self, other); if (result == NULL) return result; @@ -866,6 +950,7 @@ PyObject *v, *arg = NULL; int status; + WARNLISTUSAGE(self, PYREMAINLIST, "pop"); if (!PyArgUnpackTuple(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}; + WARNLISTUSAGE(self, PYREMAINLIST, "sort"); + assert(self != NULL); assert (PyListCheck(self)); if (args != NULL) { @@ -2163,6 +2250,7 @@ static PyObject * listreverse(PyListObject *self) { + WARNLISTUSAGE(self, PYREMAINLIST, "reverse"); if (self->obsize > 1) reverseslice(self->obitem, self->obitem + self->obsize); PyRETURNNONE; @@ -2213,6 +2301,7 @@ Pyssizet i, start=0, stop=self->obsize; PyObject *v; + WARNLISTUSAGE(self, PYREMAINLIST, "index"); if (!PyArgParseTuple(args, "O|O&O&:index", &v, PyEvalSliceIndex, &start, PyEvalSliceIndex, &stop)) @@ -2244,6 +2333,7 @@ Pyssizet count = 0; Pyssizet i; + WARNLISTUSAGE(self, PYREMAINLIST, "count"); for (i = 0; i < self->obsize; i++) { int cmp = PyObjectRichCompareBool(self->obitem[i], v, PyEQ); if (cmp > 0) @@ -2259,6 +2349,7 @@ { Pyssizet i; + WARNLISTUSAGE(self, PYREMAINLIST, "remove"); for (i = 0; i < self->obsize; i++) { int cmp = PyObjectRichCompareBool(self->obitem[i], v, PyEQ); if (cmp > 0) { @@ -2372,6 +2463,7 @@ self->allocated == 0 || self->allocated == -1); /* Empty previous contents */ + self->futuretype = PYREMAINLIST; if (self->obitem != NULL) { (void)listclear(self); } @@ -2456,6 +2548,8 @@ static PyObject * listsubscript(PyListObject* self, PyObject* item) { + WARNLISTUSAGE(self, PYREMAINLIST, "getitem"); + if (PyIndexCheck(item)) { Pyssizet i; i = PyNumberAsSsizet(item, PyExcIndexError); @@ -2505,6 +2599,7 @@ static int listasssubscript(PyListObject* self, PyObject* item, PyObject* value) { + WARNLISTUSAGEINT(self, PYREMAINLIST, "item assignment"); if (PyIndexCheck(item)) { Pyssizet i = PyNumberAsSsizet(item, PyExcIndexError); if (i == -1 && PyErrOccurred()) @@ -2874,6 +2969,7 @@ { listreviterobject *it; + WARNLISTUSAGE(seq, PYREMAINLIST, "reversed"); it = PyObjectGCNew(listreviterobject, &PyListRevIterType); 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/)
- Previous message: [Python-3000] Warning about future-unsafe usage patterns in Python 2.x e.g. dict.keys().sort()
- Next message: [Python-3000] Making more effective use of slice objects in Py3k
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]