Home | History | Annotate | Download | only in test
      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