From 2a1224df3f63f3c4a4f399b39d3a4198006b232e Mon Sep 17 00:00:00 2001 From: Joannah Nanjekye Date: Fri, 15 Mar 2024 00:55:47 +0000 Subject: [PATCH] Cherry-pick with fixes, to make string warning ready --- Doc/c-api/exceptions.rst | 13 +- Doc/library/warnings.rst | 2 +- Include/warnings.h | 6 + Lib/base64.py | 6 +- Lib/email/test/test_email.py | 1 + Lib/hmac.py | 6 + Lib/idlelib/idle_test/test_warning.py | 3 + Lib/test/string_tests.py | 19 ++ Lib/test/test_asyncore.py | 1 + Lib/test/test_atexit.py | 1 + Lib/test/test_base64.py | 12 + Lib/test/test_binascii.py | 11 +- Lib/test/test_class.py | 2 +- Lib/test/test_complex.py | 4 + Lib/test/test_future5.py | 1 + Lib/test/test_grammar.py | 94 ++++++- Lib/test/test_hmac.py | 35 ++- Lib/test/test_multiprocessing.py | 4 +- Lib/test/test_optparse.py | 1 + Lib/test/test_peepholer.py | 2 +- Lib/test/test_popen2.py | 3 + Lib/test/test_py3kwarn.py | 72 +++++ Lib/test/test_sort.py | 5 + Lib/test/test_subprocess.py | 3 + Lib/test/test_test_support.py | 1 + Lib/test/test_thread.py | 55 ++++ Lib/test/test_threading.py | 21 +- Lib/test/test_timeit.py | 2 + Lib/test/test_undocumented_details.py | 2 +- Lib/test/test_warnings.py | 64 ++++- Lib/test/test_xrange.py | 7 + Lib/threading.py | 15 + Lib/tokenize.py | 3 + Lib/unittest/test/test_result.py | 4 +- Lib/warnings.py | 192 ++++++++++++- Modules/_io/bytesio.c | 4 + Modules/binascii.c | 5 + Modules/threadmodule.c | 32 +++ Objects/abstract.c | 8 + Objects/dictobject.c | 18 ++ Objects/exceptions.c | 3 +- Objects/fileobject.c | 4 + Objects/object.c | 9 + Objects/rangeobject.c | 3 + Objects/stringobject.c | 11 + PC/os2emx/python27.def | 1 + Parser/tokenizer.c | 38 ++- Python/_warnings.c | 382 +++++++++++++++++++++++++- Python/ast.c | 64 ++++- Python/bltinmodule.c | 1 + 50 files changed, 1214 insertions(+), 42 deletions(-) diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst index aeca3572ea467a..b8bf50fcfc1933 100644 --- a/Doc/c-api/exceptions.rst +++ b/Doc/c-api/exceptions.rst @@ -319,6 +319,12 @@ is a separate error indicator for each thread. and *registry* arguments may be set to *NULL* to get the default effect described there. +.. c:function:: int PyErr_WarnExplicit_WithFix(PyObject *category, const char *message, const char *fix, const char *filename, int lineno, const char *module, PyObject *registry) + + Issue a warning message and a potential fix. This warning is the + same as `PyErr_WarnExplicit` but adds a *fix* argument to allow + for `Py3xWarning` warnings to suggest potential fixes for Python + 3.x incompatible code. .. c:function:: int PyErr_WarnPy3k(char *message, int stacklevel) @@ -327,6 +333,11 @@ is a separate error indicator for each thread. .. versionadded:: 2.6 +.. c:function:: int PyErr_WarnPy3k_WithFix(char *message, char *fix, int stacklevel) + + Issue a :exc:`DeprecationWarning` with the given *message* and *stacklevel* + if the :c:data:`Py_Py3kWarningFlag` flag is enabled. + .. c:function:: int PyErr_CheckSignals() @@ -715,7 +726,7 @@ the variables: +------------------------------------------+---------------------------------+----------+ | :c:data:`PyExc_UserWarning` | :exc:`UserWarning` | | +------------------------------------------+---------------------------------+----------+ -| :c:data:`PyExc_3xWarning` | :exc:`Py3xWarning` | | +| :c:data:`PyExc_3xWarning` | :exc:`Py3xWarning` | | +------------------------------------------+---------------------------------+----------+ Notes: diff --git a/Doc/library/warnings.rst b/Doc/library/warnings.rst index 6f014f35653410..ab70761496c9a7 100644 --- a/Doc/library/warnings.rst +++ b/Doc/library/warnings.rst @@ -95,7 +95,7 @@ following warnings category classes are currently defined: | | bytes and bytearray. | +----------------------------------+-----------------------------------------------+ | :exc:`Py3xWarning` | Base class for warnings about 3.x | - | compatibility | | + | compatibility. | | +----------------------------------+-----------------------------------------------+ While these are technically built-in exceptions, they are documented here, diff --git a/Include/warnings.h b/Include/warnings.h index 0818d7a117eefc..08bee92d7d9cfb 100644 --- a/Include/warnings.h +++ b/Include/warnings.h @@ -7,12 +7,18 @@ extern "C" { PyAPI_FUNC(void) _PyWarnings_Init(void); PyAPI_FUNC(int) PyErr_WarnEx(PyObject *, const char *, Py_ssize_t); +PyAPI_FUNC(int) PyErr_WarnEx_WithFix(PyObject *, const char *, const char *, Py_ssize_t); PyAPI_FUNC(int) PyErr_WarnExplicit(PyObject *, const char *, const char *, int, const char *, PyObject *); +PyAPI_FUNC(int) PyErr_WarnExplicit_WithFix(PyObject *, const char *, const char *, const char *, int, + const char *, PyObject *); #define PyErr_WarnPy3k(msg, stacklevel) \ (Py_Py3kWarningFlag ? PyErr_WarnEx(PyExc_DeprecationWarning, msg, stacklevel) : 0) +#define PyErr_WarnPy3k_WithFix(msg, fix, stacklevel) \ + (Py_Py3kWarningFlag ? PyErr_WarnEx_WithFix(PyExc_DeprecationWarning, msg, fix, stacklevel) : 0) + /* DEPRECATED: Use PyErr_WarnEx() instead. */ #define PyErr_Warn(category, msg) PyErr_WarnEx(category, msg, 1) diff --git a/Lib/base64.py b/Lib/base64.py index 38bc61ee984c13..7fd4c3cca32c55 100755 --- a/Lib/base64.py +++ b/Lib/base64.py @@ -9,6 +9,7 @@ import struct import string import binascii +from warnings import warnpy3k_with_fix __all__ = [ @@ -29,7 +30,6 @@ _translation = [chr(_x) for _x in range(256)] EMPTYSTRING = '' - def _translate(s, altchars): translation = _translation[:] for k, v in altchars.items(): @@ -316,6 +316,8 @@ def decode(input, output): def encodestring(s): """Encode a string into multiple lines of base-64 data.""" + warnpy3k_with_fix("base64.encodestring is not supported in 3.x", + "use base64.encodebytes instead", stacklevel=2) pieces = [] for i in range(0, len(s), MAXBINSIZE): chunk = s[i : i + MAXBINSIZE] @@ -325,6 +327,8 @@ def encodestring(s): def decodestring(s): """Decode a string.""" + warnpy3k_with_fix("base64.decodestring is not supported in 3.x", + "use base64.decodebytes instead", stacklevel=2) return binascii.a2b_base64(s) diff --git a/Lib/email/test/test_email.py b/Lib/email/test/test_email.py index 2efe44ac5a73f8..749c3aceaf65d2 100644 --- a/Lib/email/test/test_email.py +++ b/Lib/email/test/test_email.py @@ -241,6 +241,7 @@ def test_get_decoded_uu_payload(self): msg.set_payload('foo') eq(msg.get_payload(decode=True), 'foo') + @unittest.skipIf(sys.py3kwarning, "messaging confuses log") def test_decode_bogus_uu_payload_quietly(self): msg = Message() msg.set_payload('begin 664 foo.txt\n%' % id(d)) + @unittest.skipIf(sys.py3kwarning, "messaging confuses log") def test_log(self): d = asyncore.dispatcher() diff --git a/Lib/test/test_atexit.py b/Lib/test/test_atexit.py index bde898744b6a38..acbce370875165 100644 --- a/Lib/test/test_atexit.py +++ b/Lib/test/test_atexit.py @@ -24,6 +24,7 @@ def tearDown(self): sys.stderr = self.save_stderr atexit._exithandlers = self.save_handlers + @unittest.skipIf(sys.py3kwarning, "messaging confuses log") def test_args(self): atexit.register(self.h1) atexit.register(self.h4) diff --git a/Lib/test/test_base64.py b/Lib/test/test_base64.py index 6e67dc0ac189cb..57d9cddb7d106b 100644 --- a/Lib/test/test_base64.py +++ b/Lib/test/test_base64.py @@ -1,6 +1,8 @@ import unittest from test import test_support import base64 +import sys +import warnings @@ -21,6 +23,11 @@ def test_encodestring(self): # Non-bytes eq(base64.encodestring(bytearray('abc')), 'YWJj\n') + if sys.py3kwarning: + with warnings.catch_warnings(record=True) as w: + warnings.filterwarnings('always', category=Py3xWarning) + base64.encodestring("") + def test_decodestring(self): eq = self.assertEqual eq(base64.decodestring("d3d3LnB5dGhvbi5vcmc=\n"), "www.python.org") @@ -37,6 +44,11 @@ def test_decodestring(self): # Non-bytes eq(base64.decodestring(bytearray("YWJj\n")), "abc") + if sys.py3kwarning: + with warnings.catch_warnings(record=True) as w: + warnings.filterwarnings('always', category=Py3xWarning) + base64.decodestring('') + def test_encode(self): eq = self.assertEqual from cStringIO import StringIO diff --git a/Lib/test/test_binascii.py b/Lib/test/test_binascii.py index 209126b255fd0b..fb704f5a196555 100644 --- a/Lib/test/test_binascii.py +++ b/Lib/test/test_binascii.py @@ -4,6 +4,8 @@ import unittest import binascii import array +import sys +import warnings # Note: "*_hex" functions are aliases for "(un)hexlify" b2a_functions = ['b2a_base64', 'b2a_hex', 'b2a_hqx', 'b2a_qp', 'b2a_uu', @@ -171,9 +173,12 @@ def test_hex(self): self.assertRaises(TypeError, binascii.a2b_hex, t[:-1]) self.assertRaises(TypeError, binascii.a2b_hex, t[:-1] + 'q') - # Verify the treatment of Unicode strings - if test_support.have_unicode: - self.assertEqual(binascii.hexlify(unicode('a', 'ascii')), '61') + if sys.py3kwarning: + with warnings.catch_warnings(record=True) as w: + warnings.filterwarnings('always', category=Py3xWarning) + if test_support.have_unicode: + self.assertEqual(binascii.hexlify(unicode('a', 'ascii')), '61') + self.assertEqual(binascii.b2a_hex(unicode('a', 'ascii')), '61') def test_qp(self): type2test = self.type2test diff --git a/Lib/test/test_class.py b/Lib/test/test_class.py index 5cd138d2899097..c11a1cbcad465c 100644 --- a/Lib/test/test_class.py +++ b/Lib/test/test_class.py @@ -4,6 +4,7 @@ from test import test_support + testmeths = [ # Binary operations @@ -549,7 +550,6 @@ def __eq__(self, other): return 1 self.assertRaises(TypeError, hash, C2()) - def testSFBug532646(self): # Test for SF bug 532646 diff --git a/Lib/test/test_complex.py b/Lib/test/test_complex.py index 02b292f4bbfe3f..3ba6583b13a572 100644 --- a/Lib/test/test_complex.py +++ b/Lib/test/test_complex.py @@ -3,6 +3,10 @@ from random import random from math import atan2, isnan, copysign +import sys + +if sys.py3kwarning: + sys.setrecursionlimit(1 << 30) INF = float("inf") NAN = float("nan") diff --git a/Lib/test/test_future5.py b/Lib/test/test_future5.py index 4984070c47bed2..5efdb5a070235a 100644 --- a/Lib/test/test_future5.py +++ b/Lib/test/test_future5.py @@ -11,6 +11,7 @@ class TestMultipleFeatures(unittest.TestCase): def test_unicode_literals(self): self.assertIsInstance("", unicode) + @unittest.skipIf(sys.py3kwarning, "messaging confuses log") def test_print_function(self): with test_support.captured_output("stderr") as s: print("foo", file=sys.stderr) diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py index 228586ece02a58..9e97ae8f03e09b 100644 --- a/Lib/test/test_grammar.py +++ b/Lib/test/test_grammar.py @@ -65,6 +65,20 @@ def test_plain_integers(self): else: self.fail('Weird maxint value %r' % maxint) + if sys.py3kwarning: + with warnings.catch_warnings(record=True) as w: + warnings.filterwarnings('always', category=Py3xWarning) + self.assertEqual(034, 28) + self.assertEqual(01, 1) + for warning in w: + self.assertTrue(Py3xWarning is w.category) + self.assertEqual(str(w.message), "using just a '0' prefix for octal literals is not supported in 3.x: " \ + "use the '0o' prefix for octal integers") + self.assertEqual(len(w), 2) + self.assertIn("using just a '0' prefix for octal literals is not supported in 3.x:: \n" + "use the '0o' prefix for octal integers", + str(oct.exception)) + def test_long_integers(self): x = 0L x = 0l @@ -383,6 +397,12 @@ def test_print_stmt(self): save_stdout = sys.stdout sys.stdout = StringIO.StringIO() + # test printing to an instance + class Gulp: + def write(self, msg): pass + + gulp = Gulp() + print 1, 2, 3 print 1, 2, 3, print @@ -396,17 +416,40 @@ def test_print_stmt(self): print >> sys.stdout, 0 or 1, 0 or 1, print >> sys.stdout, 0 or 1 - # test printing to an instance - class Gulp: - def write(self, msg): pass - - gulp = Gulp() print >> gulp, 1, 2, 3 print >> gulp, 1, 2, 3, print >> gulp print >> gulp, 0 or 1, 0 or 1, print >> gulp, 0 or 1 + if sys.py3kwarning: + with warnings.catch_warnings(record=True) as w: + warnings.filterwarnings('always', category=Py3xWarning) + + print 1, 2, 3 + print 1, 2, 3, + print + print 0 or 1, 0 or 1, + print 0 or 1 + + # 'print' '>>' test ',' + print >> sys.stdout, 1, 2, 3 + print >> sys.stdout, 1, 2, 3, + print >> sys.stdout + print >> sys.stdout, 0 or 1, 0 or 1, + print >> sys.stdout, 0 or 1 + + print >> gulp, 1, 2, 3 + print >> gulp, 1, 2, 3, + print >> gulp + print >> gulp, 0 or 1, 0 or 1, + print >> gulp, 0 or 1 + + for warning in w: + self.assertTrue(Py3xWarning is w.category) + self.assertEqual(str(w.message), "print must be called as a function, not a statement in 3.x", + "You can fix this now by using parentheses for arguments to 'print'") + # test print >> None def driver(): oldstdout = sys.stdout @@ -1026,6 +1069,26 @@ def test_dictcomps(self): nums = [1, 2, 3] self.assertEqual({i:i+1 for i in nums}, {1: 2, 2: 3, 3: 4}) + def test_listcomp_py3k_paren(self): + [x for x in [1, 2, 2]] + if sys.py3kwarning: + with warnings.catch_warnings(record=True) as w: + warnings.filterwarnings('always', category=Py3xWarning) + [x for x in 1, 2, 3] + + def test_listcomps_py3k_scope(self): + def foo(): print([x for x in [1, 2, 2]]) + if sys.py3kwarning: + with warnings.catch_warnings(record=True) as w: + warnings.filterwarnings('always', category=Py3xWarning) + def foo(): x = 0; [x for x in [1, 2, 2]]; print(x) + def foo(): x = 0; print(x); [x for x in [1, 2, 2]] + def foo(): + x = 0 + if x > 0: + [x for x in [1, 2, 2]] + print(x) + def test_listcomps(self): # list comprehension tests nums = [1, 2, 3, 4, 5] @@ -1198,6 +1261,27 @@ def test_paren_evaluation(self): self.assertFalse((False is 2) is 3) self.assertFalse(False is 2 is 3) + def test_py3x_unicode_warnings_u(self): + if sys.py3kwarning: + with warnings.catch_warnings(record=True) as w: + warnings.filterwarnings('always', category=Py3xWarning) + self.assertEqual(u'foo', u'foo') + for warning in w: + self.assertTrue(Py3xWarning is w.category) + self.assertEqual(str(w.message), "the unicode type is not supported in 3.x: " \ + "use native strings for compatibility or " \ + "a 'u' or 'b' prefix if a native string is not required") + + def test_py3x_unicode_warnings_ur(self): + if sys.py3kwarning: + with warnings.catch_warnings(record=True) as w: + warnings.filterwarnings('always', category=Py3xWarning) + self.assertEqual(ur'foo', u'foo') + for warning in w: + self.assertTrue(Py3xWarning is w.category) + self.assertEqual(str(w.message), "the 'ur' prefix in string literals is not supported in 3.x: ", + "use a 'u' and two backslashes for a literal backslash") + def test_main(): run_unittest(TokenTests, GrammarTests) diff --git a/Lib/test/test_hmac.py b/Lib/test/test_hmac.py index 8f639a383e176c..f22d2120392a6b 100644 --- a/Lib/test/test_hmac.py +++ b/Lib/test/test_hmac.py @@ -12,37 +12,37 @@ def test_md5_vectors(self): # Test the HMAC module against test vectors from the RFC. def md5test(key, data, digest): - h = hmac.HMAC(key, data) + h = hmac.HMAC(key, data, digestmod=hashlib.sha1) self.assertEqual(h.hexdigest().upper(), digest.upper()) md5test(chr(0x0b) * 16, "Hi There", - "9294727A3638BB1C13F48EF8158BFC9D") + "675B0B3A1B4DDF4E124872DA6C2F632BFED957E9") md5test("Jefe", "what do ya want for nothing?", - "750c783e6ab0b503eaa86e310a5db738") + "EFFCDF6AE5EB2FA2D27416D5F184DF9C259A7C79") md5test(chr(0xAA)*16, chr(0xDD)*50, - "56be34521d144c88dbb8c733f0e8b3f6") + "D730594D167E35D5956FD8003D0DB3D3F46DC7BB") md5test("".join([chr(i) for i in range(1, 26)]), chr(0xCD) * 50, - "697eaf0aca3a3aea3a75164746ffaa79") + "4C9007F4026250C6BC8414F9BF50C86C2D7235DA") md5test(chr(0x0C) * 16, "Test With Truncation", - "56461ef2342edc00f9bab995690efd4c") + "37268B7E21E84DA5720C53C4BA03AD1104039FA7") md5test(chr(0xAA) * 80, "Test Using Larger Than Block-Size Key - Hash Key First", - "6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd") + "AA4AE5E15272D00E95705637CE8A3B55ED402112") md5test(chr(0xAA) * 80, ("Test Using Larger Than Block-Size Key " "and Larger Than One Block-Size Data"), - "6f630fad67cda0ee1fb1f562db3aa53e") + "E8E99D0F45237D786D6BBAA7965C7808BBFF1A91") def test_sha_vectors(self): def shatest(key, data, digest): @@ -232,14 +232,14 @@ def test_normal(self): # Standard constructor call. failed = 0 try: - h = hmac.HMAC("key") + h = hmac.HMAC("key", digestmod=hashlib.sha1) except: self.fail("Standard constructor call raised exception.") def test_withtext(self): # Constructor call with text. try: - h = hmac.HMAC("key", "hash this!") + h = hmac.HMAC("key", "hash this!", digestmod=hashlib.sha1) except: self.fail("Constructor call with text argument raised exception.") @@ -250,6 +250,13 @@ def test_withmodule(self): except: self.fail("Constructor call with hashlib.sha1 raised exception.") + def test_3k_no_digest(self): + import sys + if sys.py3kwarning: + with warnings.catch_warnings(record=True) as w: + warnings.filterwarnings('always', category=Py3xWarning) + h = hmac.HMAC("key", "", hashlib.sha1) + class SanityTestCase(unittest.TestCase): def test_default_is_md5(self): @@ -262,7 +269,7 @@ def test_exercise_all_methods(self): # Exercising all methods once. # This must not raise any exceptions try: - h = hmac.HMAC("my secret key") + h = hmac.HMAC("my secret key", digestmod=hashlib.sha1) h.update("compute the hash of this text!") dig = h.digest() dig = h.hexdigest() @@ -274,7 +281,7 @@ class CopyTestCase(unittest.TestCase): def test_attributes(self): # Testing if attributes are of same type. - h1 = hmac.HMAC("key") + h1 = hmac.HMAC("key", digestmod=hashlib.sha1) h2 = h1.copy() self.assertTrue(h1.digest_cons == h2.digest_cons, "digest constructors don't match.") @@ -285,7 +292,7 @@ def test_attributes(self): def test_realcopy(self): # Testing if the copy method created a real copy. - h1 = hmac.HMAC("key") + h1 = hmac.HMAC("key", digestmod=hashlib.sha1) h2 = h1.copy() # Using id() in case somebody has overridden __cmp__. self.assertTrue(id(h1) != id(h2), "No real copy of the HMAC instance.") @@ -296,7 +303,7 @@ def test_realcopy(self): def test_equality(self): # Testing if the copy has the same digests. - h1 = hmac.HMAC("key") + h1 = hmac.HMAC("key", digestmod=hashlib.sha1) h1.update("some random text") h2 = h1.copy() self.assertTrue(h1.digest() == h2.digest(), diff --git a/Lib/test/test_multiprocessing.py b/Lib/test/test_multiprocessing.py index c82ccfa7f0bf67..34942c514dc415 100644 --- a/Lib/test/test_multiprocessing.py +++ b/Lib/test/test_multiprocessing.py @@ -336,6 +336,7 @@ def _test_sys_exit(cls, reason, testfn): sys.stderr = open(testfn, 'w') sys.exit(reason) + @unittest.skipIf(sys.py3kwarning, "messaging confuses log") def test_sys_exit(self): # See Issue 13854 if self.TYPE == 'threads': @@ -658,7 +659,8 @@ def test_no_import_lock_contention(self): except Queue.Empty: self.fail("Probable regression on import lock contention;" " see Issue #22853") - + + @unittest.skipIf(sys.py3kwarning, "messaging confuses log") def test_queue_feeder_donot_stop_onexc(self): # bpo-30414: verify feeder handles exceptions correctly if self.TYPE != 'processes': diff --git a/Lib/test/test_optparse.py b/Lib/test/test_optparse.py index 91c60da4a519b1..15054354838f55 100644 --- a/Lib/test/test_optparse.py +++ b/Lib/test/test_optparse.py @@ -23,6 +23,7 @@ BadOptionError, OptionValueError, Values from optparse import _match_abbrev from optparse import _parse_num +from test.test_support import run_unittest, check_py3k_warnings, warnings retype = type(re.compile('')) diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index 7e05f49737f557..7d621430f79e28 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -2,7 +2,7 @@ import sys from cStringIO import StringIO import unittest - + def disassemble(func): f = StringIO() tmp = sys.stdout diff --git a/Lib/test/test_popen2.py b/Lib/test/test_popen2.py index 4a745e241cd35e..e38859ce249b5e 100644 --- a/Lib/test/test_popen2.py +++ b/Lib/test/test_popen2.py @@ -73,6 +73,7 @@ def test_popen2(self): r, w = popen2.popen2(self.cmd) self.validate_output(self.teststr, self.expected, r, w) + @unittest.skipIf(sys.py3kwarning, "messaging confuses log") def test_popen3(self): if os.name == 'posix': r, w, e = popen2.popen3([self.cmd]) @@ -94,6 +95,7 @@ def test_os_popen2(self): w, r = os.popen2(self.cmd) self.validate_output(self.teststr, self.expected, r, w) + @unittest.skipIf(sys.py3kwarning, "messaging confuses log") def test_os_popen3(self): # same test as test_popen3(), but using the os.popen*() API if os.name == 'posix': @@ -109,6 +111,7 @@ def test_os_popen3(self): w, r, e = os.popen3(self.cmd) self.validate_output(self.teststr, self.expected, r, w, e) + @unittest.skipIf(sys.py3kwarning, "messaging confuses log") def test_os_popen4(self): if os.name == 'posix': w, r = os.popen4([self.cmd]) diff --git a/Lib/test/test_py3kwarn.py b/Lib/test/test_py3kwarn.py index efcd1d2f9d8447..94b8e3e0ca0b9f 100644 --- a/Lib/test/test_py3kwarn.py +++ b/Lib/test/test_py3kwarn.py @@ -112,6 +112,42 @@ def test_dict_inequality_comparisons(self): w.reset() self.assertWarning({2:3} >= {}, w, expected) + def test_dict_viewkeys(self): + expected = 'dict.viewkeys() is not supported in 3.x: use dict.keys() instead' + with check_py3k_warnings() as w: + d = {} + d.viewkeys() + + def test_dict_viewvalues(self): + expected = 'dict.viewvalues() is not supported in 3.x: use dict.values() instead' + with check_py3k_warnings() as w: + d = {} + d.viewvalues() + + def test_dict_viewitems(self): + expected = 'dict.viewitems() is not supported in 3.x: use dict.items() instead' + with check_py3k_warnings() as w: + d = {} + d.viewitems() + + def test_dict_iterkeys(self): + expected = 'dict.iterkeys() is not supported in 3.x: use dict.keys() instead' + with check_py3k_warnings() as w: + d = {} + d.iterkeys() + + def test_dict_itervalues(self): + expected = 'dict.itervalues() is not supported in 3.x: use dict.values() instead' + with check_py3k_warnings() as w: + d = {} + d.itervalues() + + def test_dict_iteritems(self): + expected = 'dict.iteritems() is not supported in 3.x: use dict.items() instead' + with check_py3k_warnings() as w: + d = {} + d.iteritems() + def test_cell_inequality_comparisons(self): expected = 'cell comparisons not supported in 3.x' def f(x): @@ -247,6 +283,36 @@ def test_file_xreadlines(self): with check_py3k_warnings() as w: self.assertWarning(f.xreadlines(), w, expected) + 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) + + def test_file_open(self): + expected = ("The builtin 'file()'/'open()' function is not supported in 3.x, " + "use the 'io.open()' function instead with the encoding keyword argument") + with check_py3k_warnings() as w: + with open(__file__) as f: + f.read() + + def test_tokenize(self): + import tokenize + import io + expected = "tokenize() changed in 3.x: use generate_tokens() instead." + with check_py3k_warnings() as w: + tokenize.tokenize(io.BytesIO('1 + 2').readline) + + + def test_file(self): + expected = ("The builtin 'file()'/'open()' function is not supported in 3.x, " + "use the 'io.open()' function instead with the encoding keyword argument") + with check_py3k_warnings() as w: + with file(__file__) as f: + f.read() + def test_hash_inheritance(self): with check_py3k_warnings() as w: # With object as the base class @@ -313,6 +379,12 @@ def test_nonascii_bytes_literals(self): with check_py3k_warnings((expected, SyntaxWarning)): exec "b'\xbd'" + def test_raise_three_components(self): + expected = """the raise clause with three components is not supported in 3.x; \ + use 'raise' with a single object""" + with check_py3k_warnings() as w: + excType, excValue, excTraceback = sys.exc_info() + class TestStdlibRemovals(unittest.TestCase): diff --git a/Lib/test/test_sort.py b/Lib/test/test_sort.py index d75abc49173f40..590f6f4a4d693d 100644 --- a/Lib/test/test_sort.py +++ b/Lib/test/test_sort.py @@ -278,6 +278,11 @@ def test_main(verbose=None): ("the cmp argument is not supported", DeprecationWarning)): test_support.run_unittest(*test_classes) + with test_support.check_py3k_warnings( + ("the cmp method is not supported in 3.x" + "implement the function to a utility library", Py3xWarning)): + test_support.run_unittest(*test_classes) + # verify reference counting if verbose and hasattr(sys, "gettotalrefcount"): import gc diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index 0c215f75e06d2c..93ee6599b009fd 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -1115,9 +1115,11 @@ def test_close_fd_2(self): def test_close_fds_0_1(self): self.check_close_std_fds([0, 1]) + @unittest.skipIf(sys.py3kwarning, "messaging confuses log") def test_close_fds_0_2(self): self.check_close_std_fds([0, 2]) + @unittest.skipIf(sys.py3kwarning, "messaging confuses log") def test_close_fds_1_2(self): self.check_close_std_fds([1, 2]) @@ -1176,6 +1178,7 @@ def check_swap_fds(self, stdin_no, stdout_no, stderr_no): # When duping fds, if there arises a situation where one of the fds is # either 0, 1 or 2, it is possible that it is overwritten (#12607). # This tests all combinations of this. + @unittest.skipIf(sys.py3kwarning, "messaging confuses log") def test_swap_fds(self): self.check_swap_fds(0, 1, 2) self.check_swap_fds(0, 2, 1) diff --git a/Lib/test/test_test_support.py b/Lib/test/test_test_support.py index f9192a7c1f0aa8..406ec1c6b86993 100644 --- a/Lib/test/test_test_support.py +++ b/Lib/test/test_test_support.py @@ -310,6 +310,7 @@ def test_captured_stdout(self): print "hello" self.assertEqual(stdout.getvalue(), "hello\n") + @unittest.skipIf(sys.py3kwarning, "messaging confuses log") def test_captured_stderr(self): with support.captured_stderr() as stderr: print >>sys.stderr, "hello" diff --git a/Lib/test/test_thread.py b/Lib/test/test_thread.py index 93690a60b2ff58..d4d441bcfb19c7 100644 --- a/Lib/test/test_thread.py +++ b/Lib/test/test_thread.py @@ -2,10 +2,12 @@ import unittest import random from test import support +from test.test_support import check_py3k_warnings thread = support.import_module('thread') import time import sys import weakref +import warnings from test import lock_tests @@ -157,6 +159,59 @@ def mywrite(self, *args): started.acquire() self.assertIn("Traceback", stderr.getvalue()) + def test_py3k_thread_module(self): + if sys.py3kwarning: + with warnings.catch_warnings(record=True) as w: + warnings.filterwarnings('always', category=Py3xWarning) + import thread + + def test_py3k_thread_module_get_ident(self): + if sys.py3kwarning: + with warnings.catch_warnings(record=True) as w: + warnings.filterwarnings('always', category=Py3xWarning) + thread.get_ident() + + def test_py3k_thread_module_start_new_thread(self): + if sys.py3kwarning: + with warnings.catch_warnings(record=True) as w: + warnings.filterwarnings('always', category=Py3xWarning) + def f(): + ident.append(threading.currentThread().ident) + done.set() + thread.start_new_thread((f), ()) + + def test_py3k_thread_module_allocate(self): + if sys.py3kwarning: + with warnings.catch_warnings(record=True) as w: + warnings.filterwarnings('always', category=Py3xWarning) + thread.allocate_lock() + + def test_py3k_thread_module_exit_thread(self): + if sys.py3kwarning: + with warnings.catch_warnings(record=True) as w: + warnings.filterwarnings('always', category=Py3xWarning) + with self.assertRaises(SystemExit): + thread.exit_thread() + + def test_py3k_thread_module_interrupt_main(self): + if sys.py3kwarning: + with warnings.catch_warnings(record=True) as w: + warnings.filterwarnings('always', category=Py3xWarning) + with self.assertRaises(KeyboardInterrupt): + thread.interrupt_main() + + def test_py3k_thread_module_count(self): + if sys.py3kwarning: + with warnings.catch_warnings(record=True) as w: + warnings.filterwarnings('always', category=Py3xWarning) + thread._count() + + def test_py3k_thread_module_stack_size(self): + if sys.py3kwarning: + with warnings.catch_warnings(record=True) as w: + warnings.filterwarnings('always', category=Py3xWarning) + thread.stack_size() + class Barrier: def __init__(self, num_threads): diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index 4c21e6baa76cbe..96332a7544bd4c 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -1,8 +1,9 @@ # Very rudimentary test of threading module import test.test_support -from test.test_support import verbose, cpython_only +from test.test_support import verbose, cpython_only, check_py3k_warnings from test.script_helper import assert_python_ok +import warnings import random import re @@ -480,6 +481,24 @@ def test_BoundedSemaphore_limit(self): t.join() self.assertRaises(ValueError, bs.release) + def test_threading_module_method_rename(self): + import threading + if sys.py3kwarning: + with warnings.catch_warnings(record=True) as w: + warnings.filterwarnings('always', category=Py3xWarning) + threading. _get_ident() + if sys.py3kwarning: + with warnings.catch_warnings(record=True) as w: + warnings.filterwarnings('always', category=Py3xWarning) + def f(): + ident.append(threading.currentThread().ident) + done.set() + threading._start_new_thread((f), ()) + if sys.py3kwarning: + with warnings.catch_warnings(record=True) as w: + warnings.filterwarnings('always', category=Py3xWarning) + threading._allocate_lock() + class ThreadJoinOnShutdown(BaseTestCase): # Between fork() and exec(), only async-safe functions are allowed (issues diff --git a/Lib/test/test_timeit.py b/Lib/test/test_timeit.py index 3a3359c9699b48..f0498f26dcc2d7 100644 --- a/Lib/test/test_timeit.py +++ b/Lib/test/test_timeit.py @@ -301,11 +301,13 @@ def test_main_very_verbose(self): 10000 loops, best of 3: 50 usec per loop """)) + @unittest.skipIf(sys.py3kwarning, "messaging confuses log") def test_main_exception(self): with captured_stderr() as error_stringio: s = self.run_main(switches=['1.0/0.0']) self.assert_exc_string(error_stringio.getvalue(), 'ZeroDivisionError') + @unittest.skipIf(sys.py3kwarning, "messaging confuses log") def test_main_exception_fixed_reps(self): with captured_stderr() as error_stringio: s = self.run_main(switches=['-n1', '1.0/0.0']) diff --git a/Lib/test/test_undocumented_details.py b/Lib/test/test_undocumented_details.py index a8fc280c67a1ed..eb2cac726b88a0 100644 --- a/Lib/test/test_undocumented_details.py +++ b/Lib/test/test_undocumented_details.py @@ -1,6 +1,6 @@ from test.test_support import run_unittest, check_py3k_warnings import unittest - + class TestImplementationComparisons(unittest.TestCase): def test_type_comparisons(self): diff --git a/Lib/test/test_warnings.py b/Lib/test/test_warnings.py index a9568f64c55bda..63f1e4bfb15dba 100644 --- a/Lib/test/test_warnings.py +++ b/Lib/test/test_warnings.py @@ -70,7 +70,8 @@ class PublicAPITests(BaseTest): def test_module_all_attribute(self): self.assertTrue(hasattr(self.module, '__all__')) - target_api = ["warn", "warn_explicit", "showwarning", + target_api = ["warn", "warn_explicit", "warn_explicit_with_fix" + "showwarning", "showwarningwithfix", "formatwarningwithfix", "formatwarning", "filterwarnings", "simplefilter", "resetwarnings", "catch_warnings"] self.assertSetEqual(set(self.module.__all__), @@ -161,6 +162,23 @@ def test_once(self): 42) self.assertEqual(len(w), 0) + def test_once_with_fix(self): + with original_warnings.catch_warnings(record=True, + module=self.module) as w: + self.module.resetwarnings() + self.module.filterwarnings("once", category=UserWarning) + message = UserWarning("FilterTests.test_once") + fix = 'some fix' + self.module.warn_explicit_with_fix(message, fix, UserWarning, "test_warnings.py", + 42) + del w[:] + self.module.warn_explicit_with_fix(message, fix, UserWarning, "test_warnings.py", + 13) + self.assertEqual(len(w), 0) + self.module.warn_explicit_with_fix(message, fix, UserWarning, "test_warnings2.py", + 42) + self.assertEqual(len(w), 0) + def test_inheritance(self): with original_warnings.catch_warnings(module=self.module) as w: self.module.resetwarnings() @@ -230,7 +248,8 @@ class PyFilterTests(BaseTest, FilterTests): class WarnTests(unittest.TestCase): - """Test warnings.warn() and warnings.warn_explicit().""" + """Test warnings.warn(), warnings.warn_explicit() + and warnings.warn_explicit_with_fix().""" def test_message(self): with original_warnings.catch_warnings(record=True, @@ -644,6 +663,25 @@ def test_formatwarning(self): self.assertEqual(expect, self.module.formatwarning(message, category, file_name, line_num, file_line)) + def test_formatwarningwithfix(self): + message = "msg" + fix = 'fix' + category = Warning + file_name = os.path.splitext(warning_tests.__file__)[0] + '.py' + line_num = 3 + file_line = linecache.getline(file_name, line_num).strip() + format = "%s:%s: %s: %s: %s\n %s\n" + expect = format % (file_name, line_num, category.__name__, message, + fix, file_line) + self.assertEqual(expect, self.module.formatwarningwithfix(message, fix, + category, file_name, line_num)) + # Test the 'line' argument. + file_line += " for the win!" + expect = format % (file_name, line_num, category.__name__, message, + fix, file_line) + self.assertEqual(expect, self.module.formatwarningwithfix(message, fix, + category, file_name, line_num, file_line)) + @test_support.requires_unicode def test_formatwarning_unicode_msg(self): message = u"msg" @@ -722,6 +760,28 @@ def test_showwarning(self): file_object, expected_file_line) self.assertEqual(expect, file_object.getvalue()) + def test_showwarningwithfix(self): + file_name = os.path.splitext(warning_tests.__file__)[0] + '.py' + line_num = 3 + expected_file_line = linecache.getline(file_name, line_num).strip() + message = 'msg' + fix = 'fix' + category = Warning + file_object = StringIO.StringIO() + expect = self.module.formatwarningwithfix(message, fix, category, file_name, + line_num) + self.module.showwarningwithfix(message, fix, category, file_name, line_num, + file_object) + self.assertEqual(file_object.getvalue(), expect) + # Test 'line' argument. + expected_file_line += "for the win!" + expect = self.module.formatwarningwithfix(message, fix, category, file_name, + line_num, expected_file_line) + file_object = StringIO.StringIO() + self.module.showwarningwithfix(message, fix, category, file_name, line_num, + file_object, expected_file_line) + self.assertEqual(expect, file_object.getvalue()) + class CWarningsDisplayTests(BaseTest, WarningsDisplayTests): module = c_warnings diff --git a/Lib/test/test_xrange.py b/Lib/test/test_xrange.py index 83c0e415b7f493..52c2592c7095d5 100644 --- a/Lib/test/test_xrange.py +++ b/Lib/test/test_xrange.py @@ -1,6 +1,7 @@ # Python test set -- built-in functions import test.test_support, unittest +from test.test_support import check_py3k_warnings import sys import pickle import itertools @@ -179,6 +180,12 @@ def test_repr(self): r_out = eval(repr(r)) self.assert_xranges_equivalent(r, r_out) + def test_xrange_3k(self): + if sys.py3kwarning: + with warnings.catch_warnings(record=True) as w: + warnings.filterwarnings('always', category=Py3xWarning) + xrange(1, 10000) + def test_range_iterators(self): # see issue 7298 limits = [base + jiggle diff --git a/Lib/threading.py b/Lib/threading.py index b08374a9639c4e..08144314df4128 100644 --- a/Lib/threading.py +++ b/Lib/threading.py @@ -32,6 +32,21 @@ 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Thread', 'Timer', 'setprofile', 'settrace', 'local', 'stack_size'] +def _get_ident(): + warnings.warnpy3k_with_fix("_get_ident() is renamed in 3.x", "use get_ident() instead", + stacklevel=2) + thread.get_ident + +def _start_new_thread(): + warnings.warnpy3k_with_fix("_start_new_thread() is removed in 3.x", "use start_new_thread() instead", + stacklevel=2) + thread.start_new_thread + +def _allocate_lock(): + warnings.warnpy3k_with_fix("_allocate_lock() is removed in 3.x", "use allocate_lock instead", + stacklevel=2) + thread.allocate_lock + _start_new_thread = thread.start_new_thread _allocate_lock = thread.allocate_lock _get_ident = thread.get_ident diff --git a/Lib/tokenize.py b/Lib/tokenize.py index d426cd2df52a04..02dc90bf74c2c4 100644 --- a/Lib/tokenize.py +++ b/Lib/tokenize.py @@ -29,6 +29,7 @@ from itertools import chain import string, re from token import * +import warnings import token __all__ = [x for x in dir(token) if not x.startswith("_")] @@ -166,6 +167,8 @@ def tokenize(readline, tokeneater=printtoken): called once for each token, with five arguments, corresponding to the tuples generated by generate_tokens(). """ + warnings.warnpy3k_with_fix("tokenize() changed in 3.x", "use generate_tokens() instead.", + stacklevel=2) try: tokenize_loop(readline, tokeneater) except StopTokenizing: diff --git a/Lib/unittest/test/test_result.py b/Lib/unittest/test/test_result.py index 2a596cdb6f9ebb..e52ab96bc430ad 100644 --- a/Lib/unittest/test/test_result.py +++ b/Lib/unittest/test/test_result.py @@ -395,7 +395,8 @@ def testBufferOutputOff(self): self.assertIs(real_out, sys.stdout) self.assertIs(real_err, sys.stderr) - + + @unittest.skipIf(sys.py3kwarning, "messaging confuses log") def testBufferOutputStartTestAddSuccess(self): real_out = self._real_out real_err = self._real_err @@ -450,6 +451,7 @@ def getStartedResult(self): result.startTest(self) return result + @unittest.skipIf(sys.py3kwarning, "messaging confuses log") def testBufferOutputAddErrorOrFailure(self): unittest.result.traceback = MockTraceback self.addCleanup(restore_traceback) diff --git a/Lib/warnings.py b/Lib/warnings.py index 84f111d6c97fef..282292c835d5f2 100644 --- a/Lib/warnings.py +++ b/Lib/warnings.py @@ -7,8 +7,12 @@ import sys import types -__all__ = ["warn", "warn_explicit", "showwarning", - "formatwarning", "filterwarnings", "simplefilter", +if not sys.py3kwarning: + sys.setrecursionlimit(1 << 30) + +__all__ = ["warn", "warn_explicit", "warn_explicit_with_fix", + "showwarning", "showwarningwithfix", "formatwarning", + "formatwarningwithfix", "filterwarnings", "simplefilter", "resetwarnings", "catch_warnings"] @@ -22,6 +26,16 @@ def warnpy3k(message, category=None, stacklevel=1): category = DeprecationWarning warn(message, category, stacklevel+1) +def warnpy3k_with_fix(message, fix, category=None, stacklevel=1): + """Issue a deprecation warning for Python 3.x related changes and a fix. + + Warnings are omitted unless Python is started with the -3 option. + """ + if sys.py3kwarning: + if category is None: + category = DeprecationWarning + warn_with_fix(message, fix, category, stacklevel+1) + def _show_warning(message, category, filename, lineno, file=None, line=None): """Hook to write a warning to a file; replace if you like.""" if file is None: @@ -37,6 +51,21 @@ def _show_warning(message, category, filename, lineno, file=None, line=None): # triggered. showwarning = _show_warning +def _show_warning_with_fix(message, fix, category, filename, lineno, file=None, line=None): + """Hook to write a warning to a file; replace if you like.""" + if file is None: + file = sys.stderr + if file is None: + # sys.stderr is None - warnings get lost + return + try: + file.write(formatwarningwithfix(message, fix, category, filename, lineno, line)) + except (IOError, UnicodeError): + pass # the file (probably stderr) is invalid - this warning gets lost. +# Keep a working version around in case the deprecation of the old API is +# triggered. +showwarningwithfix = _show_warning_with_fix + def formatwarning(message, category, filename, lineno, line=None): """Function to format a warning the standard way.""" try: @@ -64,6 +93,34 @@ def formatwarning(message, category, filename, lineno, line=None): s = "%s:%s" % (filename, s) return s +def formatwarningwithfix(message, fix, category, filename, lineno, line=None): + """Function to format a warning the standard way with a fix.""" + try: + unicodetype = unicode + except NameError: + unicodetype = () + try: + message = str(message) + fix = str(fix) + except UnicodeEncodeError: + pass + s = "%s: %s: %s: %s\n" % (lineno, category.__name__, message, fix) + line = linecache.getline(filename, lineno) if line is None else line + if line: + line = line.strip() + if isinstance(s, unicodetype) and isinstance(line, str): + line = unicode(line, 'latin1') + s += " %s\n" % line + if isinstance(s, unicodetype) and isinstance(filename, str): + enc = sys.getfilesystemencoding() + if enc: + try: + filename = unicode(filename, enc) + except UnicodeDecodeError: + pass + s = "%s:%s" % (filename, s) + return s + def filterwarnings(action, message="", category=Warning, module="", lineno=0, append=0): """Insert an entry into the list of warnings filters (at the front). @@ -299,6 +356,110 @@ def warn_explicit(message, category, filename, lineno, # Print message and context showwarning(message, category, filename, lineno) +def warn_with_fix(message, fix, category=None, stacklevel=1): + """Issue a warning, or maybe ignore it or raise an exception.""" + # Check if message is already a Warning object + if isinstance(message, Warning): + category = message.__class__ + # Check category argument + if category is None: + category = UserWarning + assert issubclass(category, Warning) + # Get context information + try: + caller = sys._getframe(stacklevel) + except ValueError: + globals = sys.__dict__ + lineno = 1 + else: + globals = caller.f_globals + lineno = caller.f_lineno + if '__name__' in globals: + module = globals['__name__'] + else: + module = "" + filename = globals.get('__file__') + if filename: + fnl = filename.lower() + if fnl.endswith((".pyc", ".pyo")): + filename = filename[:-1] + else: + if module == "__main__": + try: + filename = sys.argv[0] + except AttributeError: + # embedded interpreters don't have sys.argv, see bug #839151 + filename = '__main__' + if not filename: + filename = module + registry = globals.setdefault("__warningregistry__", {}) + warn_explicit_with_fix(message, fix, category, filename, lineno, module, registry, globals) + +def warn_explicit_with_fix(message, fix, category, filename, lineno, + module=None, registry=None, module_globals=None): + lineno = int(lineno) + if module is None: + module = filename or "" + if module[-3:].lower() == ".py": + module = module[:-3] # XXX What about leading pathname? + if registry is None: + registry = {} + if isinstance(message, Warning): + text = str(message) + category = message.__class__ + else: + text = message + message = category(message) + key = (text, category, lineno) + # Quick test for common case + if registry.get(key): + return + # Search the filters + for item in filters: + action, msg, cat, mod, ln = item + if ((msg is None or msg.match(text)) and + issubclass(category, cat) and + (mod is None or mod.match(module)) and + (ln == 0 or lineno == ln)): + break + else: + action = defaultaction + # Early exit actions + if action == "ignore": + registry[key] = 1 + return + + # Prime the linecache for formatting, in case the + # "file" is actually in a zipfile or something. + linecache.getlines(filename, module_globals) + + if action == "error": + raise message + # Other actions + if action == "once": + registry[key] = 1 + oncekey = (text, category) + if onceregistry.get(oncekey): + return + onceregistry[oncekey] = 1 + elif action == "always": + pass + elif action == "module": + registry[key] = 1 + altkey = (text, category, 0) + if registry.get(altkey): + return + registry[altkey] = 1 + elif action == "default": + registry[key] = 1 + else: + # Unrecognized actions are errors + raise RuntimeError( + "Unrecognized action (%r) in warnings.filters:\n %s" % + (action, item)) + # Print message and context + showwarningwithfix(message, fix, category, filename, lineno) + class WarningMessage(object): @@ -322,6 +483,29 @@ def __str__(self): "line : %r}" % (self.message, self._category_name, self.filename, self.lineno, self.line)) +class WarningMessageWithFix(object): + + """Holds the result of a single showwarning() call.""" + + _WARNING_DETAILS = ("message", "fix", "category", "filename", "lineno", "file", + "line") + + def __init__(self, message, fix, category, filename, lineno, file=None, + line=None): + self.message = message + self.fix = fix + self.category = category + self.filename = filename + self.lineno = lineno + self.file = file + self.line = line + self._category_name = category.__name__ if category else None + + def __str__(self): + return ("{message : %r, fix : %r, category : %r, filename : %r, lineno : %s, " + "line : %r}" % (self.message, self._category_name, + self.filename, self.lineno, self.line)) + class catch_warnings(object): @@ -372,6 +556,8 @@ def __enter__(self): log = [] def showwarning(*args, **kwargs): log.append(WarningMessage(*args, **kwargs)) + def showwarningwithfix(*args, **kwargs): + log.append(WarningMessageWithFix(*args, **kwargs)) self._module.showwarning = showwarning return log else: @@ -395,7 +581,7 @@ def __exit__(self, *exc_info): _warnings_defaults = False try: from _warnings import (filters, default_action, once_registry, - warn, warn_explicit) + warn, warn_explicit, warn_explicit_with_fix) defaultaction = default_action onceregistry = once_registry _warnings_defaults = True diff --git a/Modules/_io/bytesio.c b/Modules/_io/bytesio.c index 0ee4b80434c804..289aaf3e9d410b 100644 --- a/Modules/_io/bytesio.c +++ b/Modules/_io/bytesio.c @@ -447,6 +447,10 @@ bytesio_truncate(bytesio *self, PyObject *args) size = PyNumber_AsSsize_t(arg, PyExc_OverflowError); if (size == -1 && PyErr_Occurred()) return NULL; + if (size == 0 && PyErr_WarnPy3k_WithFix("BytesIO.truncate() does not shift the file pointer", + "use seek(0) before doing truncate(0)", 1) < 0){ + return NULL; + } } else if (arg == Py_None) { /* Truncate to current position if no argument is passed. */ diff --git a/Modules/binascii.c b/Modules/binascii.c index 1e785fa8faaa5f..16838114ae178c 100644 --- a/Modules/binascii.c +++ b/Modules/binascii.c @@ -1051,6 +1051,11 @@ binascii_hexlify(PyObject *self, PyObject *args) argbuf = parg.buf; arglen = parg.len; + if (PyUnicode_Check(args)) + if (PyErr_WarnPy3k_WithFix("The hexlify() module expects bytes in 3.x", + "use the 'b' prefix on the string", 1) < 0) + return NULL; + assert(arglen >= 0); if (arglen > PY_SSIZE_T_MAX / 2) { PyBuffer_Release(&parg); diff --git a/Modules/threadmodule.c b/Modules/threadmodule.c index 82eff0072b3903..bf71852b8179b2 100644 --- a/Modules/threadmodule.c +++ b/Modules/threadmodule.c @@ -657,6 +657,10 @@ thread_PyThread_start_new_thread(PyObject *self, PyObject *fargs) struct bootstate *boot; long ident; + if (PyErr_WarnPy3k_WithFix("thread.start_new_thread is removed in 3.x", + "use the threading._start_new_thread instead", 1)) + return NULL; + if (!PyArg_UnpackTuple(fargs, "start_new_thread", 2, 3, &func, &args, &keyw)) return NULL; @@ -718,6 +722,10 @@ printed unless the exception is SystemExit.\n"); static PyObject * thread_PyThread_exit_thread(PyObject *self) { + if (PyErr_WarnPy3k_WithFix("thread.exit is removed in 3.x", + "no equivalent method exists, raising SystemExit will exit a thread", 1)) + return NULL; + PyErr_SetNone(PyExc_SystemExit); return NULL; } @@ -732,6 +740,10 @@ thread to exit silently unless the exception is caught."); static PyObject * thread_PyThread_interrupt_main(PyObject * self) { + if (PyErr_WarnPy3k_WithFix("thread.interrupt_main is removed in 3.x", + "no equivalent method exists, raising KeyboardInterrupt will interruot the main thread", 1)) + return NULL; + PyErr_SetInterrupt(); Py_INCREF(Py_None); return Py_None; @@ -749,6 +761,10 @@ static lockobject *newlockobject(void); static PyObject * thread_PyThread_allocate_lock(PyObject *self) { + if (PyErr_WarnPy3k_WithFix("thread.allocate_lock is removed in 3.x", + "use the threading._allocate_lock instead", 1)) + return NULL; + return (PyObject *) newlockobject(); } @@ -762,6 +778,10 @@ static PyObject * thread_get_ident(PyObject *self) { long ident; + if (PyErr_WarnPy3k_WithFix("thread.get_ident is removed in 3.x", + "use the threading.get_ident instead", 1)) + return NULL; + ident = PyThread_get_thread_ident(); if (ident == -1) { PyErr_SetString(ThreadError, "no current thread ident"); @@ -784,6 +804,10 @@ A thread's identity may be reused for another thread after it exits."); static PyObject * thread__count(PyObject *self) { + if (PyErr_WarnPy3k_WithFix("thread.count is removed in 3.x", + "use the threading._count instead", 1)) + return NULL; + return PyInt_FromLong(nb_threads); } @@ -806,6 +830,10 @@ thread_stack_size(PyObject *self, PyObject *args) Py_ssize_t new_size = 0; int rc; + if (PyErr_WarnPy3k_WithFix("thread.stack_size is removed in 3.x", + "use the threading.stack_size instead", 1)) + return NULL; + if (!PyArg_ParseTuple(args, "|n:stack_size", &new_size)) return NULL; @@ -904,6 +932,10 @@ initthread(void) { PyObject *m, *d; + if (PyErr_WarnPy3k_WithFix("In 3.x, the thread module is removed", + "use the threading module instead", 1)) + return; + /* Initialize types: */ if (PyType_Ready(&localdummytype) < 0) return; diff --git a/Objects/abstract.c b/Objects/abstract.c index 75c1a10473576c..1c68e8ccdad98f 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -2938,6 +2938,14 @@ PyObject_IsInstance(PyObject *inst, PyObject *cls) { static PyObject *name = NULL; + if ((PyTypeObject *)cls == &PyBaseString_Type) { + if (Py_Py3kWarningFlag && + PyErr_WarnEx_WithFix(PyExc_Py3xWarning, + "the basestring type is not supported in 3.x", + "import a third party library like six and use a compatible type like string_types", 1) < 0) { + return -1; + } + } /* Quick test for an exact match */ if (Py_TYPE(inst) == (PyTypeObject *)cls) return 1; diff --git a/Objects/dictobject.c b/Objects/dictobject.c index c544ecd8c2d254..c15c5f32167b91 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -2216,18 +2216,27 @@ static PyObject *dictiter_new(PyDictObject *, PyTypeObject *); static PyObject * dict_iterkeys(PyDictObject *dict) { + if (PyErr_WarnPy3k_WithFix("dict.iterkeys() is not supported in 3.x", + "use dict.keys() instead", 1) < 0) + return NULL; return dictiter_new(dict, &PyDictIterKey_Type); } static PyObject * dict_itervalues(PyDictObject *dict) { + if (PyErr_WarnPy3k_WithFix("dict.itervalues() is not supported in 3.x", + "use dict.values() instead", 1) < 0) + return NULL; return dictiter_new(dict, &PyDictIterValue_Type); } static PyObject * dict_iteritems(PyDictObject *dict) { + if (PyErr_WarnPy3k_WithFix("dict.iteritems() is not supported in 3.x", + "use dict.items() instead", 1) < 0) + return NULL; return dictiter_new(dict, &PyDictIterItem_Type); } @@ -3195,6 +3204,9 @@ PyTypeObject PyDictKeys_Type = { static PyObject * dictkeys_new(PyObject *dict) { + if (PyErr_WarnPy3k_WithFix("dict.viewkeys() is not supported in 3.x", + "use dict.keys() instead", 1) < 0) + return NULL; return dictview_new(dict, &PyDictKeys_Type); } @@ -3284,6 +3296,9 @@ PyTypeObject PyDictItems_Type = { static PyObject * dictitems_new(PyObject *dict) { + if (PyErr_WarnPy3k_WithFix("dict.viewitems() is not supported in 3.x", + "use dict.items() instead", 1) < 0) + return NULL; return dictview_new(dict, &PyDictItems_Type); } @@ -3349,5 +3364,8 @@ PyTypeObject PyDictValues_Type = { static PyObject * dictvalues_new(PyObject *dict) { + if (PyErr_WarnPy3k_WithFix("dict.viewvalues() is not supported in 3.x", + "use dict.values() instead", 1) < 0) + return NULL; return dictview_new(dict, &PyDictValues_Type); } diff --git a/Objects/exceptions.c b/Objects/exceptions.c index 1788b08fe67439..9633de14ab0895 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -1982,7 +1982,8 @@ SimpleExtendsException(PyExc_Warning, SyntaxWarning, /* - * 3xWarning extends Warning + * Py3xWarning extends Warning + * XXX(nanjekyejoannah): Suppress this warning for legacy tests for now */ SimpleExtendsException(PyExc_Warning, Py3xWarning, "Base class for warnings about 3.x compatibility."); diff --git a/Objects/fileobject.c b/Objects/fileobject.c index b524f09e0a396e..7ceae1d15c6043 100644 --- a/Objects/fileobject.c +++ b/Objects/fileobject.c @@ -2417,6 +2417,10 @@ file_new(PyTypeObject *type, PyObject *args, PyObject *kwds) static int file_init(PyObject *self, PyObject *args, PyObject *kwds) { + if (PyErr_WarnPy3k_WithFix("The builtin 'open()' function is not supported in 3.x, ", + "use the 'io.open()' function instead with the encoding keyword argument", 1) < 0) + goto Error; + PyFileObject *foself = (PyFileObject *)self; int ret = 0; static char *kwlist[] = {"name", "mode", "buffering", 0}; diff --git a/Objects/object.c b/Objects/object.c index 65366b0b351b4c..0fff92c5ab3412 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -860,6 +860,15 @@ PyObject_Compare(PyObject *v, PyObject *w) { int result; + if (Py_Py3kWarningFlag) { + if(PyErr_WarnEx_WithFix(PyExc_Py3xWarning, "the cmp method is not supported in 3.x", + "you can either provide your own alternative or use a third party library with a " + "backwards compatible fix", 1) < 0) { + return -1; + } + return -1; + } + if (v == NULL || w == NULL) { PyErr_BadInternalCall(); return -1; diff --git a/Objects/rangeobject.c b/Objects/rangeobject.c index 888069a433a507..d21d1627312d65 100644 --- a/Objects/rangeobject.c +++ b/Objects/rangeobject.c @@ -68,6 +68,9 @@ range_new(PyTypeObject *type, PyObject *args, PyObject *kw) long ilow = 0, ihigh = 0, istep = 1; unsigned long n; + if (PyErr_WarnPy3k_WithFix("xrange() is not supported in 3.x", "use range() instead", 1) < 0) + return NULL; + if (!_PyArg_NoKeywords("xrange()", kw)) return NULL; diff --git a/Objects/stringobject.c b/Objects/stringobject.c index fd477627f6e62e..fa69ee6619b2fc 100644 --- a/Objects/stringobject.c +++ b/Objects/stringobject.c @@ -1029,6 +1029,17 @@ string_concat(register PyStringObject *a, register PyObject *bb) { register Py_ssize_t size; register PyStringObject *op; + if (a->ob_type != bb->ob_type) { + char msgbuf[256]; + sprintf(msgbuf, "The first string is '%.200s' while the second is '%.200s': "\ + "mixed bytes, str and unicode operands cannot be used in string concatenation in Python 3.x", + Py_TYPE(a)->tp_name, Py_TYPE(bb)->tp_name); + char *fix = "convert the operand(s) so that they are the same type."; + if (Py_Py3kWarningFlag && + PyErr_WarnEx_WithFix(PyExc_Py3xWarning, msgbuf, fix, 1) < 0) { + return NULL; + } + } if (!PyString_Check(bb)) { #ifdef Py_USING_UNICODE if (PyUnicode_Check(bb)) diff --git a/PC/os2emx/python27.def b/PC/os2emx/python27.def index 424435fdf4e1e4..06090494415b6d 100644 --- a/PC/os2emx/python27.def +++ b/PC/os2emx/python27.def @@ -914,6 +914,7 @@ EXPORTS "_PyErr_BadInternalCall" "PyErr_Warn" "PyErr_WarnExplicit" + "PyErr_WarnExplicit_WithFix" ; From python27_s.lib(frozen) "PyImport_FrozenModules" diff --git a/Parser/tokenizer.c b/Parser/tokenizer.c index 89666611940cf8..f3b400fc82b6e0 100644 --- a/Parser/tokenizer.c +++ b/Parser/tokenizer.c @@ -1390,8 +1390,27 @@ tok_get(register struct tok_state *tok, char **p_start, char **p_end) case 'u': case 'U': c = tok_nextc(tok); - if (c == 'r' || c == 'R') + if (c == 'r' || c == 'R') { + if (Py_Py3kWarningFlag) { + if (PyErr_WarnExplicit_WithFix(PyExc_Py3xWarning, "the 'ur' prefix in string literals is not supported in 3.x", + "use a 'u' and two backslashes for a literal backslash", tok->filename, tok->lineno, + NULL, NULL)) { + return NULL; + } + } c = tok_nextc(tok); + } + else { + if (Py_Py3kWarningFlag) { + if (PyErr_WarnExplicit_WithFix(PyExc_Py3xWarning, "the unicode type is not supported in 3.x", + "use native strings for compatibility or " + "a 'u' or 'b' prefix if a native string is not required", + tok->filename, tok->lineno, NULL, NULL)) { + return NULL; + } + } + + } if (c == '"' || c == '\'') goto letter_quote; break; @@ -1435,12 +1454,29 @@ tok_get(register struct tok_state *tok, char **p_start, char **p_end) if (c == '0') { /* Hex, octal or binary -- maybe. */ c = tok_nextc(tok); + if (Py_Py3kWarningFlag) { + if (PyErr_WarnExplicit(PyExc_Py3xWarning, + "octal literals are not supported in 3.x;\n" + "drop the leading 0", + tok->filename, tok->lineno, NULL, NULL)) { + return ERRORTOKEN; + } + } if (c == '.') goto fraction; #ifndef WITHOUT_COMPLEX if (c == 'j' || c == 'J') goto imaginary; #endif + if (c != 'o' || c != 'O') { + char buf[100]; + if (Py_Py3kWarningFlag) { + if (PyErr_WarnExplicit_WithFix(PyExc_Py3xWarning, "using just a '0' prefix for octal literals is not supported in 3.x", + "use the '0o' prefix for octal integers, if you intended the integer to be decimal", tok->filename, tok->lineno, NULL, NULL)) { + return NULL; + } + } + } if (c == 'x' || c == 'X') { /* Hex */ diff --git a/Python/_warnings.c b/Python/_warnings.c index 37875e56199af3..4b9147880206b8 100644 --- a/Python/_warnings.c +++ b/Python/_warnings.c @@ -245,6 +245,55 @@ update_registry(PyObject *registry, PyObject *text, PyObject *category, return rc; } +static void +show_warning_with_fix(PyObject *filename, int lineno, PyObject *text, PyObject *fix_txt, PyObject + *category, PyObject *sourceline) +{ + PyObject *f_stderr; + PyObject *name; + char lineno_str[128]; + + PyOS_snprintf(lineno_str, sizeof(lineno_str), ":%d: ", lineno); + + name = PyObject_GetAttrString(category, "__name__"); + if (name == NULL) /* XXX Can an object lack a '__name__' attribute? */ + return; + + f_stderr = PySys_GetObject("stderr"); + if (f_stderr == NULL) { + fprintf(stderr, "lost sys.stderr\n"); + Py_DECREF(name); + return; + } + + /* Print "filename:lineno: category: text\n" */ + PyFile_WriteObject(filename, f_stderr, Py_PRINT_RAW); + PyFile_WriteString(lineno_str, f_stderr); + PyFile_WriteObject(name, f_stderr, Py_PRINT_RAW); + PyFile_WriteString(": ", f_stderr); + PyFile_WriteObject(text, f_stderr, Py_PRINT_RAW); + PyFile_WriteString(": ", f_stderr); + PyFile_WriteObject(fix_txt, f_stderr, Py_PRINT_RAW); + PyFile_WriteString(".", f_stderr); + PyFile_WriteString("\n", f_stderr); + Py_XDECREF(name); + + /* Print " source_line\n" */ + if (sourceline) { + char *source_line_str = PyString_AS_STRING(sourceline); + while (*source_line_str == ' ' || *source_line_str == '\t' || + *source_line_str == '\014') + source_line_str++; + + PyFile_WriteString(source_line_str, f_stderr); + PyFile_WriteString("\n", f_stderr); + } + else + _Py_DisplaySourceLine(f_stderr, PyString_AS_STRING(filename), + lineno, 2); + PyErr_Clear(); +} + static void show_warning(PyObject *filename, int lineno, PyObject *text, PyObject *category, PyObject *sourceline) @@ -443,6 +492,167 @@ warn_explicit(PyObject *category, PyObject *message, return result; /* Py_None or NULL. */ } +static PyObject * +warn_explicit_with_fix(PyObject *category, PyObject *message, + PyObject *fix, PyObject *filename, int lineno, + PyObject *module, PyObject *registry, PyObject *sourceline) +{ + PyObject *key = NULL, *fix_txt = NULL, *text = NULL, *result = NULL, *lineno_obj = NULL; + PyObject *item = Py_None; + const char *action; + int rc, rc_fix; + + if (registry && !PyDict_Check(registry) && (registry != Py_None)) { + PyErr_SetString(PyExc_TypeError, "'registry' must be a dict or None"); + return NULL; + } + + if (module == NULL) { + module = normalize_module(filename); + if (module == NULL) + return NULL; + } + else + Py_INCREF(module); + + Py_INCREF(message); + Py_INCREF(fix); + rc = PyObject_IsInstance(message, PyExc_Warning); + rc_fix = PyObject_IsInstance(fix, PyExc_Warning); + if (rc == -1 || rc_fix == -1) { + goto cleanup; + } + if (rc == 1) { + text = PyObject_Str(message); + if (text == NULL) + goto cleanup; + category = (PyObject*)message->ob_type; + } + else { + text = message; + message = PyObject_CallFunction(category, "O", message); + if (message == NULL) + goto cleanup; + } + + if (rc_fix == 1) { + fix_txt = PyObject_Str(fix); + if (fix_txt == NULL) + goto cleanup; + category = (PyObject*)fix->ob_type; + } + else { + fix_txt = fix; + fix = PyObject_CallFunction(category, "O", fix); + if (fix == NULL) + goto cleanup; + } + + lineno_obj = PyInt_FromLong(lineno); + if (lineno_obj == NULL) + goto cleanup; + + key = PyTuple_Pack(4, text, fix_txt, category, lineno_obj); + if (key == NULL) + goto cleanup; + + if ((registry != NULL) && (registry != Py_None)) { + rc = already_warned(registry, key, 0); + if (rc == -1) + goto cleanup; + else if (rc == 1) + goto return_none; + } + + action = get_filter(category, text, lineno, module, &item); + if (action == NULL) + goto cleanup; + + if (strcmp(action, "error") == 0) { + PyErr_SetObject(category, message); + goto cleanup; + } + + rc = 0; + if (strcmp(action, "always") != 0) { + if (registry != NULL && registry != Py_None && + PyDict_SetItem(registry, key, Py_True) < 0) + goto cleanup; + else if (strcmp(action, "ignore") == 0) + goto return_none; + else if (strcmp(action, "once") == 0) { + if (registry == NULL || registry == Py_None) { + registry = get_once_registry(); + if (registry == NULL) + goto cleanup; + } + rc = update_registry(registry, text, category, 0); + } + else if (strcmp(action, "module") == 0) { + if (registry != NULL && registry != Py_None) + rc = update_registry(registry, text, category, 0); + } + else if (strcmp(action, "default") != 0) { + PyObject *to_str = PyObject_Str(item); + const char *err_str = "???"; + + if (to_str != NULL) + err_str = PyString_AS_STRING(to_str); + PyErr_Format(PyExc_RuntimeError, + "Unrecognized action (%s) in warnings.filters:\n %s", + action, err_str); + Py_XDECREF(to_str); + goto cleanup; + } + } + + if (rc == 1) + goto return_none; + if (rc == 0) { + PyObject *show_fxn = get_warnings_attr("showwarningwithfix"); + if (show_fxn == NULL) { + if (PyErr_Occurred()) + goto cleanup; + show_warning_with_fix(filename, lineno, text, fix_txt, category, sourceline); + } + else { + PyObject *res; + + if (!PyMethod_Check(show_fxn) && !PyFunction_Check(show_fxn)) { + PyErr_SetString(PyExc_TypeError, + "warnings.showwarningwithfix() must be set to a " + "function or method"); + Py_DECREF(show_fxn); + goto cleanup; + } + + res = PyObject_CallFunctionObjArgs(show_fxn, message, fix, category, + filename, lineno_obj, + NULL); + Py_DECREF(show_fxn); + Py_XDECREF(res); + if (res == NULL) + goto cleanup; + } + } + else + goto cleanup; + + return_none: + result = Py_None; + Py_INCREF(result); + + cleanup: + Py_XDECREF(key); + Py_XDECREF(text); + Py_XDECREF(fix_txt); + Py_XDECREF(lineno_obj); + Py_DECREF(module); + Py_XDECREF(message); + Py_XDECREF(fix); + return result; /* Py_None or NULL. */ +} + /* filename, module, and registry are new refs, globals is borrowed */ /* Returns 0 on error (no new refs), 1 on success */ static int @@ -607,6 +817,23 @@ do_warn(PyObject *message, PyObject *category, Py_ssize_t stack_level) return res; } +static PyObject * +do_warn_with_fix(PyObject *message, PyObject *fix, PyObject *category, Py_ssize_t stack_level) +{ + PyObject *filename, *module, *registry, *res; + int lineno; + + if (!setup_context(stack_level, &filename, &lineno, &module, ®istry)) + return NULL; + + res = warn_explicit_with_fix(category, message, fix, filename, lineno, module, registry, + NULL); + Py_DECREF(filename); + Py_DECREF(registry); + Py_DECREF(module); + return res; +} + static PyObject * warnings_warn(PyObject *self, PyObject *args, PyObject *kwds) { @@ -710,6 +937,93 @@ warnings_warn_explicit(PyObject *self, PyObject *args, PyObject *kwds) registry, NULL); } +static PyObject * +warnings_warn_explicit_with_fix(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwd_list[] = {"message", "fix", "category", "filename", "lineno", + "module", "registry", "module_globals", 0}; + PyObject *message; + PyObject *fix; + PyObject *category; + PyObject *filename; + int lineno; + PyObject *module = NULL; + PyObject *registry = NULL; + PyObject *module_globals = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOOOi|OOO:warn_explicit_with_fix", + kwd_list, &message, &fix, &category, &filename, &lineno, &module, + ®istry, &module_globals)) + return NULL; + + if (module_globals) { + static PyObject *get_source_name = NULL; + static PyObject *splitlines_name = NULL; + PyObject *loader; + PyObject *module_name; + PyObject *source; + PyObject *source_list; + PyObject *source_line; + PyObject *returned; + + if (get_source_name == NULL) { + get_source_name = PyString_InternFromString("get_source"); + if (!get_source_name) + return NULL; + } + if (splitlines_name == NULL) { + splitlines_name = PyString_InternFromString("splitlines"); + if (!splitlines_name) + return NULL; + } + + /* Check/get the requisite pieces needed for the loader. */ + loader = PyDict_GetItemString(module_globals, "__loader__"); + module_name = PyDict_GetItemString(module_globals, "__name__"); + + if (loader == NULL || module_name == NULL) + goto standard_call; + + /* Make sure the loader implements the optional get_source() method. */ + if (!PyObject_HasAttrString(loader, "get_source")) + goto standard_call; + /* Call get_source() to get the source code. */ + source = PyObject_CallMethodObjArgs(loader, get_source_name, + module_name, NULL); + if (!source) + return NULL; + else if (source == Py_None) { + Py_DECREF(Py_None); + goto standard_call; + } + + /* Split the source into lines. */ + source_list = PyObject_CallMethodObjArgs((PyObject *)&PyString_Type, + splitlines_name, source, + NULL); + Py_DECREF(source); + if (!source_list) + return NULL; + + /* Get the source line. */ + source_line = PyList_GetItem(source_list, lineno-1); + if (!source_line) { + Py_DECREF(source_list); + return NULL; + } + + /* Handle the warning. */ + returned = warn_explicit_with_fix(category, message, fix, filename, lineno, module, + registry, source_line); + Py_DECREF(source_list); + return returned; + } + + standard_call: + return warn_explicit_with_fix(category, message, fix, filename, lineno, module, + registry, NULL); +} + /* Function to issue a warning message; may raise an exception. */ int @@ -732,8 +1046,28 @@ PyErr_WarnEx(PyObject *category, const char *text, Py_ssize_t stack_level) return 0; } -/* PyErr_Warn is only for backwards compatibility and will be removed. - Use PyErr_WarnEx instead. */ +/* Function to issue a warning message; may raise an exception. */ +int +PyErr_WarnEx_WithFix(PyObject *category, const char *text, const char *fix_txt, Py_ssize_t stack_level) +{ + PyObject *res; + PyObject *message = PyString_FromString(text); + PyObject *fix = PyString_FromString(fix_txt); + if (message == NULL) + return -1; + + if (category == NULL) + category = PyExc_RuntimeWarning; + + res = do_warn_with_fix(message, fix, category, stack_level); + Py_DECREF(message); + Py_DECREF(fix); + if (res == NULL) + return -1; + Py_DECREF(res); + + return 0; +} #undef PyErr_Warn @@ -779,6 +1113,44 @@ PyErr_WarnExplicit(PyObject *category, const char *text, return ret; } +/* Warning with explicit origin and a fix*/ +int +PyErr_WarnExplicit_WithFix(PyObject *category, const char *text, + const char *fix_str, const char *filename_str, int lineno, + const char *module_str, PyObject *registry) +{ + PyObject *res; + PyObject *message = PyString_FromString(text); + PyObject *fix = PyString_FromString(fix_str); + PyObject *filename = PyString_FromString(filename_str); + PyObject *module = NULL; + int ret = -1; + + if (message == NULL || filename == NULL || fix == NULL) + goto exit; + if (module_str != NULL) { + module = PyString_FromString(module_str); + if (module == NULL) + goto exit; + } + + if (category == NULL) + category = PyExc_RuntimeWarning; + res = warn_explicit_with_fix(category, message, fix, filename, lineno, module, registry, + NULL); + if (res == NULL) + goto exit; + Py_DECREF(res); + ret = 0; + + exit: + Py_XDECREF(message); + Py_XDECREF(fix); + Py_XDECREF(module); + Py_XDECREF(filename); + return ret; +} + PyDoc_STRVAR(warn_doc, "Issue a warning, or maybe ignore it or raise an exception."); @@ -786,11 +1158,17 @@ PyDoc_STRVAR(warn_doc, PyDoc_STRVAR(warn_explicit_doc, "Low-level inferface to warnings functionality."); +PyDoc_STRVAR(warn_explicit_with_fix_doc, +"Low-level inferface to warnings functionality with a 'fix' argument."); + static PyMethodDef warnings_functions[] = { {"warn", (PyCFunction)warnings_warn, METH_VARARGS | METH_KEYWORDS, warn_doc}, {"warn_explicit", (PyCFunction)warnings_warn_explicit, METH_VARARGS | METH_KEYWORDS, warn_explicit_doc}, + {"warn_explicit_with_fix", (PyCFunction)warnings_warn_explicit_with_fix, + METH_VARARGS | METH_KEYWORDS, warn_explicit_with_fix_doc}, + /* XXX(brett.cannon): add showwarning? */ /* XXX(brett.cannon): Reasonable to add formatwarning? */ {NULL, NULL} /* sentinel */ diff --git a/Python/ast.c b/Python/ast.c index d27eec004bb572..110dc713f6cb29 100644 --- a/Python/ast.c +++ b/Python/ast.c @@ -130,6 +130,19 @@ ast_warn(struct compiling *c, const node *n, char *msg) return 1; } +static int +ast_3x_warn(struct compiling *c, const node *n, char *msg, char *fix) +{ + if (PyErr_WarnExplicit_WithFix(PyExc_Py3xWarning, msg, fix, c->c_filename, LINENO(n), + NULL, NULL) < 0) { + /* if -Werr, change it to a SyntaxError */ + if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_Py3xWarning)) + ast_error(n, msg); + return 0; + } + return 1; +} + static int forbidden_check(struct compiling *c, const node *n, const char *x) { @@ -890,13 +903,23 @@ ast_for_decorators(struct compiling *c, const node *n) return decorator_seq; } +static int +compiler_islistcomp(stmt_ty s) +{ + if (s->kind != Expr_kind) + return 0; + return s->v.Expr.value->kind == ListComp_kind; +} + static stmt_ty ast_for_funcdef(struct compiling *c, const node *n, asdl_seq *decorator_seq) { /* funcdef: 'def' NAME parameters ':' suite */ identifier name; arguments_ty args; - asdl_seq *body; + asdl_seq *body, *lstcomp, *var_of_int; + int nc, i, y, islistcomp; + stmt_ty st, var_st; int name_i = 1; REQ(n, funcdef); @@ -912,6 +935,26 @@ ast_for_funcdef(struct compiling *c, const node *n, asdl_seq *decorator_seq) body = ast_for_suite(c, CHILD(n, name_i + 3)); if (!body) return NULL; + + if (Py_Py3kWarningFlag) { + nc = asdl_seq_LEN(body); + for (i = 0; i < nc; i++) { + st = (stmt_ty)asdl_seq_GET(body, i); + islistcomp = compiler_islistcomp(st); + if (islistcomp) { + lstcomp = asdl_seq_GET(body, i); + var_of_int = asdl_seq_GET(lstcomp, 3); + for (y=i; y < nc; y++) { + var_st = (stmt_ty)asdl_seq_GET(body, y); + if ((var_st == var_of_int) && + !ast_3x_warn(c, n, "This listcomp does not leak a variable in 3.x", + "assign the variable before use")) + return 0; + } + } + } + } + return FunctionDef(name, args, body, decorator_seq, LINENO(n), n->n_col_offset, c->c_arena); @@ -1423,8 +1466,9 @@ ast_for_atom(struct compiling *c, const node *n) case LSQB: /* list (or list comprehension) */ ch = CHILD(n, 1); - if (TYPE(ch) == RSQB) + if (TYPE(ch) == RSQB) { return List(NULL, Load, LINENO(n), n->n_col_offset, c->c_arena); + } REQ(ch, listmaker); if (NCH(ch) == 1 || TYPE(CHILD(ch, 1)) == COMMA) { @@ -1435,6 +1479,10 @@ ast_for_atom(struct compiling *c, const node *n) return List(elts, Load, LINENO(n), n->n_col_offset, c->c_arena); } else + if (Py_Py3kWarningFlag && + !ast_3x_warn(c, n, "list comp without parenthesis is invalid in 3.x", + "use parenthesis for list items more than one")) + return NULL; return ast_for_listcomp(c, ch); case LBRACE: { /* dictorsetmaker: @@ -2281,6 +2329,12 @@ ast_for_print_stmt(struct compiling *c, const node *n) int i, j, values_count, start = 1; REQ(n, print_stmt); + if (Py_Py3kWarningFlag && TYPE(CHILD(n, 1)) != LPAR) { + if (!ast_3x_warn(c, n, + "print must be called as a function, not a statement in 3.x", + "You can fix this now by using parentheses for arguments to 'print'")) + return NULL; + } if (NCH(n) >= 2 && TYPE(CHILD(n, 1)) == RIGHTSHIFT) { dest = ast_for_expr(c, CHILD(n, 2)); if (!dest) @@ -2405,6 +2459,12 @@ ast_for_flow_stmt(struct compiling *c, const node *n) else if (NCH(ch) == 6) { expr_ty expr1, expr2, expr3; + if (Py_Py3kWarningFlag && + !ast_3x_warn(c, n, "the raise clause with three components is not supported in 3.x", + "use 'raise' with a single object")) { + return NULL; + } + expr1 = ast_for_expr(c, CHILD(ch, 1)); if (!expr1) return NULL; diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 6d47de1fb20e32..1275c027cce4eb 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -1507,6 +1507,7 @@ builtin_open(PyObject *self, PyObject *args, PyObject *kwds) return PyObject_Call((PyObject*)&PyFile_Type, args, kwds); } + PyDoc_STRVAR(open_doc, "open(name[, mode[, buffering]]) -> file object\n\ \n\