1import unittest
2import sys
3from test.test_support import check_py3k_warnings, CleanImport, run_unittest
4import warnings
5
6if not sys.py3kwarning:
7    raise unittest.SkipTest('%s must be run with the -3 flag' % __name__)
8
9try:
10    from test.test_support import __warningregistry__ as _registry
11except ImportError:
12    def check_deprecated_module(module_name):
13        return False
14else:
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
26def reset_module_registry(module):
27    try:
28        registry = module.__warningregistry__
29    except AttributeError:
30        pass
31    else:
32        registry.clear()
33
34class 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
311class 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
421def test_main():
422    run_unittest(TestPy3KWarnings,
423                 TestStdlibRemovals)
424
425if __name__ == '__main__':
426    test_main()
427