Index: Objects/unicodeobject.c =================================================================== --- Objects/unicodeobject.c (revision 62896) +++ Objects/unicodeobject.c (working copy) @@ -8276,6 +8276,25 @@ static PyObject * unicode_mod(PyObject *v, PyObject *w) { + /* Keep track of our recursion level. We don't want to print out + warnings when calling into PyErr_WarnEx, so use 'level' as + a counter for how we've many times we've called into this + function. This is thread-safe (because we already have the GIL), + but it will skip some output if the GIL is released inside the + warning machinery. The code in warnings.py does not use % + formatting, so this could only be a problem with user code. + Making 'level' into a thread-local variable doesn't seem worth + the hassle or runtime expense. */ + static int level = 0; + + if (level == 0) { + int err; + ++level; + err = PyErr_WarnEx(PyExc_PendingDeprecationWarning, + "% formatting will be deprecated, " + "use .format() instead", 1); + --level; + } if (!PyUnicode_Check(v)) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; Index: Misc/NEWS =================================================================== --- Misc/NEWS (revision 62896) +++ Misc/NEWS (working copy) @@ -12,6 +12,9 @@ Core and Builtins ----------------- +- Issue #2772: Added PendingDeprecationWarning for % formatting. Use + str.format() instead. + Extension Modules ----------------- Index: Lib/warnings.py =================================================================== --- Lib/warnings.py (revision 62896) +++ Lib/warnings.py (working copy) @@ -22,11 +22,12 @@ def formatwarning(message, category, filename, lineno, line=None): """Function to format a warning the standard way.""" - s = "%s:%s: %s: %s\n" % (filename, lineno, category.__name__, message) + s = "{0}:{1}: {2}: {3}\n".format(filename, lineno, + category.__name__, message) line = linecache.getline(filename, lineno) if line is None else line if line: line = line.strip() - s += " %s\n" % line + s += " {0}\n".format(line) return s def filterwarnings(action, message="", category=Warning, module="", lineno=0, @@ -36,7 +37,7 @@ Use assertions to check that all arguments have the right type.""" import re assert action in ("error", "ignore", "always", "default", "module", - "once"), "invalid action: %r" % (action,) + "once"), "invalid action: {0!r}".format(action) assert isinstance(message, str), "message must be a string" assert isinstance(category, type), "category must be a class" assert issubclass(category, Warning), "category must be a Warning subclass" @@ -56,7 +57,7 @@ A simple filter matches all modules and messages. """ assert action in ("error", "ignore", "always", "default", "module", - "once"), "invalid action: %r" % (action,) + "once"), "invalid action: {0!r}".format(action) assert isinstance(lineno, int) and lineno >= 0, \ "lineno must be an int >= 0" item = (action, None, category, None, lineno) @@ -86,7 +87,7 @@ import re parts = arg.split(':') if len(parts) > 5: - raise _OptionError("too many fields (max 5): %r" % (arg,)) + raise _OptionError("too many fields (max 5): {0!r}".format(arg)) while len(parts) < 5: parts.append('') action, message, category, module, lineno = [s.strip() @@ -103,7 +104,7 @@ if lineno < 0: raise ValueError except (ValueError, OverflowError): - raise _OptionError("invalid lineno %r" % (lineno,)) + raise _OptionError("invalid lineno {0!r}".format(lineno)) else: lineno = 0 filterwarnings(action, message, category, module, lineno) @@ -116,7 +117,7 @@ for a in ('default', 'always', 'ignore', 'module', 'once', 'error'): if a.startswith(action): return a - raise _OptionError("invalid action: %r" % (action,)) + raise _OptionError("invalid action: {0!r}".format(action)) # Helper for _setoption() def _getcategory(category): @@ -127,7 +128,8 @@ try: cat = eval(category) except NameError: - raise _OptionError("unknown warning category: %r" % (category,)) + raise _OptionError("unknown warning category: {0!r}".format( + category)) else: i = category.rfind(".") module = category[:i] @@ -135,13 +137,14 @@ try: m = __import__(module, None, None, [klass]) except ImportError: - raise _OptionError("invalid module name: %r" % (module,)) + raise _OptionError("invalid module name: {0!r}".format(module)) try: cat = getattr(m, klass) except AttributeError: - raise _OptionError("unknown warning category: %r" % (category,)) + raise _OptionError("unknown warning category: {0!r}".format( + category)) if not issubclass(cat, Warning): - raise _OptionError("invalid warning category: %r" % (category,)) + raise _OptionError("invalid warning category: {0!r}".format(category)) return cat @@ -245,8 +248,8 @@ else: # Unrecognized actions are errors raise RuntimeError( - "Unrecognized action (%r) in warnings.filters:\n %s" % - (action, item)) + "Unrecognized action ({0!r}) " + "in warnings.filters:\n {1}".format(action, item)) if not hasattr(showwarning, "__call__"): raise TypeError("warnings.showwarning() must be set to a " "function or method")