diff --git a/Include/ceval.h b/Include/ceval.h index f46160158e52af..d637e0c5b647ef 100644 --- a/Include/ceval.h +++ b/Include/ceval.h @@ -30,6 +30,7 @@ PyAPI_FUNC(PyObject *) PyEval_GetGlobals(void); PyAPI_FUNC(PyObject *) PyEval_GetLocals(void); PyAPI_FUNC(struct _frame *) PyEval_GetFrame(void); PyAPI_FUNC(int) PyEval_GetRestricted(void); +PyAPI_FUNC(int) _Py3kWarn_NextOpcode(void); /* Look at the current frame's (if any) code's co_flags, and turn on the corresponding compiler flags in cf->cf_flags. Return 1 if any diff --git a/Lib/contextlib.py b/Lib/contextlib.py index f05205b01c2f7c..7cac00244d0f6e 100644 --- a/Lib/contextlib.py +++ b/Lib/contextlib.py @@ -14,14 +14,14 @@ def __init__(self, gen): def __enter__(self): try: - return self.gen.next() + return next(self.gen) except StopIteration: raise RuntimeError("generator didn't yield") def __exit__(self, type, value, traceback): if type is None: try: - self.gen.next() + next(self.gen) except StopIteration: return else: diff --git a/Lib/test/test_py3kwarn.py b/Lib/test/test_py3kwarn.py index 2ea08c5491c662..afd3c1b420bce3 100644 --- a/Lib/test/test_py3kwarn.py +++ b/Lib/test/test_py3kwarn.py @@ -221,6 +221,34 @@ def test_sort_cmp_arg(self): w.reset() self.assertWarning(sorted(lst, cmp), w, expected) + def test_next_method(self): + expected = 'iterator.next() is not supported in 3.x; use __next__() instead' + it = iter(range(5)) + with check_py3k_warnings() as w: + self.assertWarning(it.next(), w, expected) + + def test_intern(self): + expected = 'intern() is not supported in 3.x: use sys.intern() instead' + with check_py3k_warnings() as w: + self.assertWarning(intern('pygrate-next-method'), w, expected) + + def test_range_materialization(self): + expected = 'range() may require list materialization in 3.x' + with check_py3k_warnings() as w: + self.assertWarning(range(5) + [5], w, expected) + + def test_xrange_materialization(self): + expected = 'xrange() may require list materialization in 3.x' + with check_py3k_warnings() as w: + items = xrange(5) + self.assertWarning(None, w, expected) + + def test_dict_listlike_materialization(self): + expected = 'dict.keys() may require list materialization in 3.x' + d = {'a': 1, 'b': 2} + with check_py3k_warnings() as w: + self.assertWarning(d.keys()[0], w, expected) + def test_sys_exc_clear(self): expected = 'sys.exc_clear() not supported in 3.x; use except clauses' with check_py3k_warnings() as w: @@ -288,9 +316,10 @@ def test_bytesio_truncate(self): from io import BytesIO x = BytesIO(b'AAAAAA') expected = "BytesIO.truncate() does not shift the file pointer: use seek(0) before doing truncate(0)" - self.assertWarning(x.truncate(0), w, expected) - w.reset() - self.assertNoWarning(x.truncate(-1), w) + with check_py3k_warnings() as w: + self.assertWarning(x.truncate(0), w, expected) + w.reset() + self.assertNoWarning(x.truncate(-1), w) def test_file_open(self): expected = ("The builtin 'file()'/'open()' function is not supported in 3.x, " diff --git a/Objects/dictobject.c b/Objects/dictobject.c index c15c5f32167b91..936627a9cffc81 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -8,6 +8,7 @@ */ #include "Python.h" +#include "opcode.h" /* Set a key error with the specified argument, wrapping it in a @@ -27,6 +28,15 @@ set_key_error(PyObject *arg) /* Define this out if you don't want conversion statistics on exit. */ #undef SHOW_CONVERSION_COUNTS +#define WARN_DICT_LISTLIKE(MSG) \ + do { \ + int nextop = _Py3kWarn_NextOpcode(); \ + if ((nextop == BINARY_SUBSCR || nextop == STORE_SUBSCR || \ + nextop == BINARY_ADD || nextop == INPLACE_ADD) && \ + PyErr_WarnPy3k((MSG), 1) < 0) \ + return NULL; \ + } while (0) + /* See large comment block below. This must be >= 1. */ #define PERTURB_SHIFT 5 @@ -1301,6 +1311,8 @@ dict_keys(register PyDictObject *mp) PyDictEntry *ep; Py_ssize_t mask, n; + WARN_DICT_LISTLIKE("dict.keys() may require list materialization in 3.x"); + again: n = mp->ma_used; v = PyList_New(n); @@ -1335,6 +1347,8 @@ dict_values(register PyDictObject *mp) PyDictEntry *ep; Py_ssize_t mask, n; + WARN_DICT_LISTLIKE("dict.values() may require list materialization in 3.x"); + again: n = mp->ma_used; v = PyList_New(n); @@ -1370,6 +1384,8 @@ dict_items(register PyDictObject *mp) PyObject *item, *key, *value; PyDictEntry *ep; + WARN_DICT_LISTLIKE("dict.items() may require list materialization in 3.x"); + /* Preallocate the list of tuples, to avoid allocations during * the loop over the items, which could trigger GC, which * could resize the dict. :-( diff --git a/Objects/rangeobject.c b/Objects/rangeobject.c index d21d1627312d65..0e62f7bbdbf8f0 100644 --- a/Objects/rangeobject.c +++ b/Objects/rangeobject.c @@ -1,6 +1,7 @@ /* Range object implementation */ #include "Python.h" +#include "opcode.h" typedef struct { PyObject_HEAD @@ -67,8 +68,15 @@ range_new(PyTypeObject *type, PyObject *args, PyObject *kw) rangeobject *obj; long ilow = 0, ihigh = 0, istep = 1; unsigned long n; + int nextop; - if (PyErr_WarnPy3k_WithFix("xrange() is not supported in 3.x", "use range() instead", 1) < 0) + nextop = _Py3kWarn_NextOpcode(); + if (nextop == GET_ITER && + PyErr_WarnPy3k_WithFix("xrange() is not supported in 3.x", + "use range() instead", 1) < 0) + return NULL; + if (nextop != GET_ITER && + PyErr_WarnPy3k("xrange() may require list materialization in 3.x", 1) < 0) return NULL; if (!_PyArg_NoKeywords("xrange()", kw)) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 1c8958c49a3bf4..e1a5b8eff86b13 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -4857,6 +4857,10 @@ wrap_next(PyObject *self, PyObject *args, void *wrapped) if (!check_num_args(args, 0)) return NULL; + if (Py_TYPE(self)->tp_iter != NULL && + PyErr_WarnPy3k("iterator.next() is not supported in 3.x; " + "use __next__() instead", 1) < 0) + return NULL; res = (*func)(self); if (res == NULL && !PyErr_Occurred()) PyErr_SetNone(PyExc_StopIteration); diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 1275c027cce4eb..f2ec830f7a43e9 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -6,6 +6,7 @@ #include "node.h" #include "code.h" #include "eval.h" +#include "opcode.h" #include #include /* for DBL_MANT_DIG and friends */ @@ -1282,6 +1283,9 @@ builtin_intern(PyObject *self, PyObject *args) "can't intern subclass of string"); return NULL; } + if (PyErr_WarnPy3k("intern() is not supported in 3.x: use sys.intern() instead", + 1) < 0) + return NULL; Py_INCREF(s); PyString_InternInPlace(&s); return s; @@ -1970,9 +1974,15 @@ builtin_range(PyObject *self, PyObject *args) long ilow = 0, ihigh = 0, istep = 1; long bign; Py_ssize_t i, n; + int nextop; PyObject *v; + nextop = _Py3kWarn_NextOpcode(); + if ((nextop == BINARY_ADD || nextop == INPLACE_ADD) && + PyErr_WarnPy3k("range() may require list materialization in 3.x", 1) < 0) + return NULL; + if (PyTuple_Size(args) <= 1) { if (!PyArg_ParseTuple(args, "l;range() requires 1-3 int arguments", diff --git a/Python/ceval.c b/Python/ceval.c index e1140a8e401243..2af1ef7a42fed6 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -19,6 +19,53 @@ #include +int +_Py3kWarn_NextOpcode(void) +{ + PyFrameObject *frame; + char *code; + Py_ssize_t n; + int offset; + int op; + int steps; + + frame = PyEval_GetFrame(); + if (frame == NULL || frame->f_code == NULL) + return -1; + if (PyString_AsStringAndSize(frame->f_code->co_code, &code, &n) < 0) { + PyErr_Clear(); + return -1; + } + + offset = frame->f_lasti; + if (offset < 0 || offset >= n) + return -1; + + op = (unsigned char)code[offset]; + offset += 1; + if (HAS_ARG(op)) + offset += 2; + + /* These warnings only care about the immediate consumer of the + just-evaluated call result. Looking ahead a handful of opcodes is + enough to skip trivial stack setup before we either find a relevant + consumer or hit a stop opcode showing the value has already been + stored, returned, or discarded. */ + for (steps = 0; steps < 8 && offset >= 0 && offset < n; steps++) { + op = (unsigned char)code[offset]; + if (op == BINARY_ADD || op == INPLACE_ADD || op == GET_ITER || + op == BINARY_SUBSCR || op == STORE_SUBSCR) + return op; + if (op == RETURN_VALUE || op == STORE_NAME || op == STORE_FAST || + op == STORE_GLOBAL || op == STORE_ATTR || op == POP_TOP) + return -1; + offset += 1; + if (HAS_ARG(op)) + offset += 2; + } + return -1; +} + #ifndef WITH_TSC #define READ_TIMESTAMP(var)