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