1 import unittest 2 import sys 3 from test.test_support import check_py3k_warnings, CleanImport, run_unittest 4 import warnings 5 6 if not sys.py3kwarning: 7 raise unittest.SkipTest('%s must be run with the -3 flag' % __name__) 8 9 try: 10 from test.test_support import __warningregistry__ as _registry 11 except ImportError: 12 def check_deprecated_module(module_name): 13 return False 14 else: 15 past_warnings = _registry.keys() 16 del _registry 17 def check_deprecated_module(module_name): 18 """Lookup the past warnings for module already loaded using 19 test_support.import_module(..., deprecated=True) 20 """ 21 return any(module_name in msg and ' removed' in msg 22 and issubclass(cls, DeprecationWarning) 23 and (' module' in msg or ' package' in msg) 24 for (msg, cls, line) in past_warnings) 25 26 def reset_module_registry(module): 27 try: 28 registry = module.__warningregistry__ 29 except AttributeError: 30 pass 31 else: 32 registry.clear() 33 34 class TestPy3KWarnings(unittest.TestCase): 35 36 def assertWarning(self, _, warning, expected_message): 37 self.assertEqual(str(warning.message), expected_message) 38 39 def assertNoWarning(self, _, recorder): 40 self.assertEqual(len(recorder.warnings), 0) 41 42 def test_backquote(self): 43 expected = 'backquote not supported in 3.x; use repr()' 44 with check_py3k_warnings((expected, SyntaxWarning)): 45 exec "`2`" in {} 46 47 def test_paren_arg_names(self): 48 expected = 'parenthesized argument names are invalid in 3.x' 49 def check(s): 50 with check_py3k_warnings((expected, SyntaxWarning)): 51 exec s in {} 52 check("def f((x)): pass") 53 check("def f((((x))), (y)): pass") 54 check("def f((x), (((y))), m=32): pass") 55 # Something like def f((a, (b))): pass will raise the tuple 56 # unpacking warning. 57 58 def test_forbidden_names(self): 59 # So we don't screw up our globals 60 def safe_exec(expr): 61 def f(**kwargs): pass 62 exec expr in {'f' : f} 63 64 tests = [("True", "assignment to True or False is forbidden in 3.x"), 65 ("False", "assignment to True or False is forbidden in 3.x"), 66 ("nonlocal", "nonlocal is a keyword in 3.x")] 67 with check_py3k_warnings(('', SyntaxWarning)) as w: 68 for keyword, expected in tests: 69 safe_exec("{0} = False".format(keyword)) 70 self.assertWarning(None, w, expected) 71 w.reset() 72 try: 73 safe_exec("obj.{0} = True".format(keyword)) 74 except NameError: 75 pass 76 self.assertWarning(None, w, expected) 77 w.reset() 78 safe_exec("def {0}(): pass".format(keyword)) 79 self.assertWarning(None, w, expected) 80 w.reset() 81 safe_exec("class {0}: pass".format(keyword)) 82 self.assertWarning(None, w, expected) 83 w.reset() 84 safe_exec("def f({0}=43): pass".format(keyword)) 85 self.assertWarning(None, w, expected) 86 w.reset() 87 88 89 def test_type_inequality_comparisons(self): 90 expected = 'type inequality comparisons not supported in 3.x' 91 with check_py3k_warnings() as w: 92 self.assertWarning(int < str, w, expected) 93 w.reset() 94 self.assertWarning(type < object, w, expected) 95 96 def test_object_inequality_comparisons(self): 97 expected = 'comparing unequal types not supported in 3.x' 98 with check_py3k_warnings() as w: 99 self.assertWarning(str < [], w, expected) 100 w.reset() 101 self.assertWarning(object() < (1, 2), w, expected) 102 103 def test_dict_inequality_comparisons(self): 104 expected = 'dict inequality comparisons not supported in 3.x' 105 with check_py3k_warnings() as w: 106 self.assertWarning({} < {2:3}, w, expected) 107 w.reset() 108 self.assertWarning({} <= {}, w, expected) 109 w.reset() 110 self.assertWarning({} > {2:3}, w, expected) 111 w.reset() 112 self.assertWarning({2:3} >= {}, w, expected) 113 114 def test_cell_inequality_comparisons(self): 115 expected = 'cell comparisons not supported in 3.x' 116 def f(x): 117 def g(): 118 return x 119 return g 120 cell0, = f(0).func_closure 121 cell1, = f(1).func_closure 122 with check_py3k_warnings() as w: 123 self.assertWarning(cell0 == cell1, w, expected) 124 w.reset() 125 self.assertWarning(cell0 < cell1, w, expected) 126 127 def test_code_inequality_comparisons(self): 128 expected = 'code inequality comparisons not supported in 3.x' 129 def f(x): 130 pass 131 def g(x): 132 pass 133 with check_py3k_warnings() as w: 134 self.assertWarning(f.func_code < g.func_code, w, expected) 135 w.reset() 136 self.assertWarning(f.func_code <= g.func_code, w, expected) 137 w.reset() 138 self.assertWarning(f.func_code >= g.func_code, w, expected) 139 w.reset() 140 self.assertWarning(f.func_code > g.func_code, w, expected) 141 142 def test_builtin_function_or_method_comparisons(self): 143 expected = ('builtin_function_or_method ' 144 'order comparisons not supported in 3.x') 145 func = eval 146 meth = {}.get 147 with check_py3k_warnings() as w: 148 self.assertWarning(func < meth, w, expected) 149 w.reset() 150 self.assertWarning(func > meth, w, expected) 151 w.reset() 152 self.assertWarning(meth <= func, w, expected) 153 w.reset() 154 self.assertWarning(meth >= func, w, expected) 155 w.reset() 156 self.assertNoWarning(meth == func, w) 157 self.assertNoWarning(meth != func, w) 158 lam = lambda x: x 159 self.assertNoWarning(lam == func, w) 160 self.assertNoWarning(lam != func, w) 161 162 def test_frame_attributes(self): 163 template = "%s has been removed in 3.x" 164 f = sys._getframe(0) 165 for attr in ("f_exc_traceback", "f_exc_value", "f_exc_type"): 166 expected = template % attr 167 with check_py3k_warnings() as w: 168 self.assertWarning(getattr(f, attr), w, expected) 169 w.reset() 170 self.assertWarning(setattr(f, attr, None), w, expected) 171 172 def test_sort_cmp_arg(self): 173 expected = "the cmp argument is not supported in 3.x" 174 lst = range(5) 175 cmp = lambda x,y: -1 176 177 with check_py3k_warnings() as w: 178 self.assertWarning(lst.sort(cmp=cmp), w, expected) 179 w.reset() 180 self.assertWarning(sorted(lst, cmp=cmp), w, expected) 181 w.reset() 182 self.assertWarning(lst.sort(cmp), w, expected) 183 w.reset() 184 self.assertWarning(sorted(lst, cmp), w, expected) 185 186 def test_sys_exc_clear(self): 187 expected = 'sys.exc_clear() not supported in 3.x; use except clauses' 188 with check_py3k_warnings() as w: 189 self.assertWarning(sys.exc_clear(), w, expected) 190 191 def test_methods_members(self): 192 expected = '__members__ and __methods__ not supported in 3.x' 193 class C: 194 __methods__ = ['a'] 195 __members__ = ['b'] 196 c = C() 197 with check_py3k_warnings() as w: 198 self.assertWarning(dir(c), w, expected) 199 200 def test_softspace(self): 201 expected = 'file.softspace not supported in 3.x' 202 with file(__file__) as f: 203 with check_py3k_warnings() as w: 204 self.assertWarning(f.softspace, w, expected) 205 def set(): 206 f.softspace = 0 207 with check_py3k_warnings() as w: 208 self.assertWarning(set(), w, expected) 209 210 def test_slice_methods(self): 211 class Spam(object): 212 def __getslice__(self, i, j): pass 213 def __setslice__(self, i, j, what): pass 214 def __delslice__(self, i, j): pass 215 class Egg: 216 def __getslice__(self, i, h): pass 217 def __setslice__(self, i, j, what): pass 218 def __delslice__(self, i, j): pass 219 220 expected = "in 3.x, __{0}slice__ has been removed; use __{0}item__" 221 222 for obj in (Spam(), Egg()): 223 with check_py3k_warnings() as w: 224 self.assertWarning(obj[1:2], w, expected.format('get')) 225 w.reset() 226 del obj[3:4] 227 self.assertWarning(None, w, expected.format('del')) 228 w.reset() 229 obj[4:5] = "eggs" 230 self.assertWarning(None, w, expected.format('set')) 231 232 def test_tuple_parameter_unpacking(self): 233 expected = "tuple parameter unpacking has been removed in 3.x" 234 with check_py3k_warnings((expected, SyntaxWarning)): 235 exec "def f((a, b)): pass" 236 237 def test_buffer(self): 238 expected = 'buffer() not supported in 3.x' 239 with check_py3k_warnings() as w: 240 self.assertWarning(buffer('a'), w, expected) 241 242 def test_file_xreadlines(self): 243 expected = ("f.xreadlines() not supported in 3.x, " 244 "try 'for line in f' instead") 245 with file(__file__) as f: 246 with check_py3k_warnings() as w: 247 self.assertWarning(f.xreadlines(), w, expected) 248 249 def test_hash_inheritance(self): 250 with check_py3k_warnings() as w: 251 # With object as the base class 252 class WarnOnlyCmp(object): 253 def __cmp__(self, other): pass 254 self.assertEqual(len(w.warnings), 0) 255 w.reset() 256 class WarnOnlyEq(object): 257 def __eq__(self, other): pass 258 self.assertEqual(len(w.warnings), 1) 259 self.assertWarning(None, w, 260 "Overriding __eq__ blocks inheritance of __hash__ in 3.x") 261 w.reset() 262 class WarnCmpAndEq(object): 263 def __cmp__(self, other): pass 264 def __eq__(self, other): pass 265 self.assertEqual(len(w.warnings), 1) 266 self.assertWarning(None, w, 267 "Overriding __eq__ blocks inheritance of __hash__ in 3.x") 268 w.reset() 269 class NoWarningOnlyHash(object): 270 def __hash__(self): pass 271 self.assertEqual(len(w.warnings), 0) 272 # With an intermediate class in the heirarchy 273 class DefinesAllThree(object): 274 def __cmp__(self, other): pass 275 def __eq__(self, other): pass 276 def __hash__(self): pass 277 class WarnOnlyCmp(DefinesAllThree): 278 def __cmp__(self, other): pass 279 self.assertEqual(len(w.warnings), 0) 280 w.reset() 281 class WarnOnlyEq(DefinesAllThree): 282 def __eq__(self, other): pass 283 self.assertEqual(len(w.warnings), 1) 284 self.assertWarning(None, w, 285 "Overriding __eq__ blocks inheritance of __hash__ in 3.x") 286 w.reset() 287 class WarnCmpAndEq(DefinesAllThree): 288 def __cmp__(self, other): pass 289 def __eq__(self, other): pass 290 self.assertEqual(len(w.warnings), 1) 291 self.assertWarning(None, w, 292 "Overriding __eq__ blocks inheritance of __hash__ in 3.x") 293 w.reset() 294 class NoWarningOnlyHash(DefinesAllThree): 295 def __hash__(self): pass 296 self.assertEqual(len(w.warnings), 0) 297 298 def test_operator(self): 299 from operator import isCallable, sequenceIncludes 300 301 callable_warn = ("operator.isCallable() is not supported in 3.x. " 302 "Use hasattr(obj, '__call__').") 303 seq_warn = ("operator.sequenceIncludes() is not supported " 304 "in 3.x. Use operator.contains().") 305 with check_py3k_warnings() as w: 306 self.assertWarning(isCallable(self), w, callable_warn) 307 w.reset() 308 self.assertWarning(sequenceIncludes(range(3), 2), w, seq_warn) 309 310 311 class TestStdlibRemovals(unittest.TestCase): 312 313 # test.testall not tested as it executes all unit tests as an 314 # import side-effect. 315 all_platforms = ('audiodev', 'imputil', 'mutex', 'user', 'new', 'rexec', 316 'Bastion', 'compiler', 'dircache', 'mimetools', 317 'fpformat', 'ihooks', 'mhlib', 'statvfs', 'htmllib', 318 'sgmllib', 'rfc822', 'sunaudio') 319 inclusive_platforms = {'irix' : ('pure', 'AL', 'al', 'CD', 'cd', 'cddb', 320 'cdplayer', 'CL', 'cl', 'DEVICE', 'GL', 321 'gl', 'ERRNO', 'FILE', 'FL', 'flp', 'fl', 322 'fm', 'GET', 'GLWS', 'imgfile', 'IN', 323 'IOCTL', 'jpeg', 'panel', 'panelparser', 324 'readcd', 'SV', 'torgb', 'WAIT'), 325 'darwin' : ('autoGIL', 'Carbon', 'OSATerminology', 326 'icglue', 'Nav', 327 # MacOS should (and does) give a Py3kWarning, but one of the 328 # earlier tests already imports the MacOS extension which causes 329 # this test to fail. Disabling the test for 'MacOS' avoids this 330 # spurious test failure. 331 #'MacOS', 332 'aepack', 333 'aetools', 'aetypes', 'applesingle', 334 'appletrawmain', 'appletrunner', 335 'argvemulator', 'bgenlocations', 336 'EasyDialogs', 'macerrors', 'macostools', 337 'findertools', 'FrameWork', 'ic', 338 'gensuitemodule', 'icopen', 'macresource', 339 'MiniAEFrame', 'pimp', 'PixMapWrapper', 340 'terminalcommand', 'videoreader', 341 '_builtinSuites', 'CodeWarrior', 342 'Explorer', 'Finder', 'Netscape', 343 'StdSuites', 'SystemEvents', 'Terminal', 344 'cfmfile', 'bundlebuilder', 'buildtools', 345 'ColorPicker', 'Audio_mac'), 346 'sunos5' : ('sunaudiodev', 'SUNAUDIODEV'), 347 } 348 optional_modules = ('bsddb185', 'Canvas', 'dl', 'linuxaudiodev', 'imageop', 349 'sv', 'bsddb', 'dbhash') 350 351 def check_removal(self, module_name, optional=False): 352 """Make sure the specified module, when imported, raises a 353 DeprecationWarning and specifies itself in the message.""" 354 with CleanImport(module_name), warnings.catch_warnings(): 355 warnings.filterwarnings("error", ".+ (module|package) .+ removed", 356 DeprecationWarning, __name__) 357 warnings.filterwarnings("error", ".+ removed .+ (module|package)", 358 DeprecationWarning, __name__) 359 try: 360 __import__(module_name, level=0) 361 except DeprecationWarning as exc: 362 self.assertIn(module_name, exc.args[0], 363 "%s warning didn't contain module name" 364 % module_name) 365 except ImportError: 366 if not optional: 367 self.fail("Non-optional module {0} raised an " 368 "ImportError.".format(module_name)) 369 else: 370 # For extension modules, check the __warningregistry__. 371 # They won't rerun their init code even with CleanImport. 372 if not check_deprecated_module(module_name): 373 self.fail("DeprecationWarning not raised for {0}" 374 .format(module_name)) 375 376 def test_platform_independent_removals(self): 377 # Make sure that the modules that are available on all platforms raise 378 # the proper DeprecationWarning. 379 for module_name in self.all_platforms: 380 self.check_removal(module_name) 381 382 def test_platform_specific_removals(self): 383 # Test the removal of platform-specific modules. 384 for module_name in self.inclusive_platforms.get(sys.platform, []): 385 self.check_removal(module_name, optional=True) 386 387 def test_optional_module_removals(self): 388 # Test the removal of modules that may or may not be built. 389 for module_name in self.optional_modules: 390 self.check_removal(module_name, optional=True) 391 392 def test_os_path_walk(self): 393 msg = "In 3.x, os.path.walk is removed in favor of os.walk." 394 def dumbo(where, names, args): pass 395 for path_mod in ("ntpath", "macpath", "os2emxpath", "posixpath"): 396 mod = __import__(path_mod) 397 reset_module_registry(mod) 398 with check_py3k_warnings() as w: 399 mod.walk("crashers", dumbo, None) 400 self.assertEqual(str(w.message), msg) 401 402 def test_reduce_move(self): 403 from operator import add 404 # reduce tests may have already triggered this warning 405 reset_module_registry(unittest.case) 406 with warnings.catch_warnings(): 407 warnings.filterwarnings("error", "reduce") 408 self.assertRaises(DeprecationWarning, reduce, add, range(10)) 409 410 def test_mutablestring_removal(self): 411 # UserString.MutableString has been removed in 3.0. 412 import UserString 413 # UserString tests may have already triggered this warning 414 reset_module_registry(UserString) 415 with warnings.catch_warnings(): 416 warnings.filterwarnings("error", ".*MutableString", 417 DeprecationWarning) 418 self.assertRaises(DeprecationWarning, UserString.MutableString) 419 420 421 def test_main(): 422 run_unittest(TestPy3KWarnings, 423 TestStdlibRemovals) 424 425 if __name__ == '__main__': 426 test_main() 427