• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"Test run, coverage 54%."
2
3from idlelib import run
4import io
5import sys
6from test.support import captured_output, captured_stderr
7import unittest
8from unittest import mock
9import idlelib
10from idlelib.idle_test.mock_idle import Func
11
12idlelib.testing = True  # Use {} for executing test user code.
13
14
15class ExceptionTest(unittest.TestCase):
16
17    def test_print_exception_unhashable(self):
18        class UnhashableException(Exception):
19            def __eq__(self, other):
20                return True
21
22        ex1 = UnhashableException('ex1')
23        ex2 = UnhashableException('ex2')
24        try:
25            raise ex2 from ex1
26        except UnhashableException:
27            try:
28                raise ex1
29            except UnhashableException:
30                with captured_stderr() as output:
31                    with mock.patch.object(run, 'cleanup_traceback') as ct:
32                        ct.side_effect = lambda t, e: t
33                        run.print_exception()
34
35        tb = output.getvalue().strip().splitlines()
36        self.assertEqual(11, len(tb))
37        self.assertIn('UnhashableException: ex2', tb[3])
38        self.assertIn('UnhashableException: ex1', tb[10])
39
40    data = (('1/0', ZeroDivisionError, "division by zero\n"),
41            ('abc', NameError, "name 'abc' is not defined. "
42                               "Did you mean: 'abs'?\n"),
43            ('int.reel', AttributeError,
44                 "type object 'int' has no attribute 'reel'. "
45                 "Did you mean: 'real'?\n"),
46            )
47
48    def test_get_message(self):
49        for code, exc, msg in self.data:
50            with self.subTest(code=code):
51                try:
52                    eval(compile(code, '', 'eval'))
53                except exc:
54                    typ, val, tb = sys.exc_info()
55                    actual = run.get_message_lines(typ, val, tb)[0]
56                    expect = f'{exc.__name__}: {msg}'
57                    self.assertEqual(actual, expect)
58
59    @mock.patch.object(run, 'cleanup_traceback',
60                       new_callable=lambda: (lambda t, e: None))
61    def test_get_multiple_message(self, mock):
62        d = self.data
63        data2 = ((d[0], d[1]), (d[1], d[2]), (d[2], d[0]))
64        subtests = 0
65        for (code1, exc1, msg1), (code2, exc2, msg2) in data2:
66            with self.subTest(codes=(code1,code2)):
67                try:
68                    eval(compile(code1, '', 'eval'))
69                except exc1:
70                    try:
71                        eval(compile(code2, '', 'eval'))
72                    except exc2:
73                        with captured_stderr() as output:
74                            run.print_exception()
75                        actual = output.getvalue()
76                        self.assertIn(msg1, actual)
77                        self.assertIn(msg2, actual)
78                        subtests += 1
79        self.assertEqual(subtests, len(data2))  # All subtests ran?
80
81# StdioFile tests.
82
83class S(str):
84    def __str__(self):
85        return '%s:str' % type(self).__name__
86    def __unicode__(self):
87        return '%s:unicode' % type(self).__name__
88    def __len__(self):
89        return 3
90    def __iter__(self):
91        return iter('abc')
92    def __getitem__(self, *args):
93        return '%s:item' % type(self).__name__
94    def __getslice__(self, *args):
95        return '%s:slice' % type(self).__name__
96
97
98class MockShell:
99    def __init__(self):
100        self.reset()
101    def write(self, *args):
102        self.written.append(args)
103    def readline(self):
104        return self.lines.pop()
105    def close(self):
106        pass
107    def reset(self):
108        self.written = []
109    def push(self, lines):
110        self.lines = list(lines)[::-1]
111
112
113class StdInputFilesTest(unittest.TestCase):
114
115    def test_misc(self):
116        shell = MockShell()
117        f = run.StdInputFile(shell, 'stdin')
118        self.assertIsInstance(f, io.TextIOBase)
119        self.assertEqual(f.encoding, 'utf-8')
120        self.assertEqual(f.errors, 'strict')
121        self.assertIsNone(f.newlines)
122        self.assertEqual(f.name, '<stdin>')
123        self.assertFalse(f.closed)
124        self.assertTrue(f.isatty())
125        self.assertTrue(f.readable())
126        self.assertFalse(f.writable())
127        self.assertFalse(f.seekable())
128
129    def test_unsupported(self):
130        shell = MockShell()
131        f = run.StdInputFile(shell, 'stdin')
132        self.assertRaises(OSError, f.fileno)
133        self.assertRaises(OSError, f.tell)
134        self.assertRaises(OSError, f.seek, 0)
135        self.assertRaises(OSError, f.write, 'x')
136        self.assertRaises(OSError, f.writelines, ['x'])
137
138    def test_read(self):
139        shell = MockShell()
140        f = run.StdInputFile(shell, 'stdin')
141        shell.push(['one\n', 'two\n', ''])
142        self.assertEqual(f.read(), 'one\ntwo\n')
143        shell.push(['one\n', 'two\n', ''])
144        self.assertEqual(f.read(-1), 'one\ntwo\n')
145        shell.push(['one\n', 'two\n', ''])
146        self.assertEqual(f.read(None), 'one\ntwo\n')
147        shell.push(['one\n', 'two\n', 'three\n', ''])
148        self.assertEqual(f.read(2), 'on')
149        self.assertEqual(f.read(3), 'e\nt')
150        self.assertEqual(f.read(10), 'wo\nthree\n')
151
152        shell.push(['one\n', 'two\n'])
153        self.assertEqual(f.read(0), '')
154        self.assertRaises(TypeError, f.read, 1.5)
155        self.assertRaises(TypeError, f.read, '1')
156        self.assertRaises(TypeError, f.read, 1, 1)
157
158    def test_readline(self):
159        shell = MockShell()
160        f = run.StdInputFile(shell, 'stdin')
161        shell.push(['one\n', 'two\n', 'three\n', 'four\n'])
162        self.assertEqual(f.readline(), 'one\n')
163        self.assertEqual(f.readline(-1), 'two\n')
164        self.assertEqual(f.readline(None), 'three\n')
165        shell.push(['one\ntwo\n'])
166        self.assertEqual(f.readline(), 'one\n')
167        self.assertEqual(f.readline(), 'two\n')
168        shell.push(['one', 'two', 'three'])
169        self.assertEqual(f.readline(), 'one')
170        self.assertEqual(f.readline(), 'two')
171        shell.push(['one\n', 'two\n', 'three\n'])
172        self.assertEqual(f.readline(2), 'on')
173        self.assertEqual(f.readline(1), 'e')
174        self.assertEqual(f.readline(1), '\n')
175        self.assertEqual(f.readline(10), 'two\n')
176
177        shell.push(['one\n', 'two\n'])
178        self.assertEqual(f.readline(0), '')
179        self.assertRaises(TypeError, f.readlines, 1.5)
180        self.assertRaises(TypeError, f.readlines, '1')
181        self.assertRaises(TypeError, f.readlines, 1, 1)
182
183    def test_readlines(self):
184        shell = MockShell()
185        f = run.StdInputFile(shell, 'stdin')
186        shell.push(['one\n', 'two\n', ''])
187        self.assertEqual(f.readlines(), ['one\n', 'two\n'])
188        shell.push(['one\n', 'two\n', ''])
189        self.assertEqual(f.readlines(-1), ['one\n', 'two\n'])
190        shell.push(['one\n', 'two\n', ''])
191        self.assertEqual(f.readlines(None), ['one\n', 'two\n'])
192        shell.push(['one\n', 'two\n', ''])
193        self.assertEqual(f.readlines(0), ['one\n', 'two\n'])
194        shell.push(['one\n', 'two\n', ''])
195        self.assertEqual(f.readlines(3), ['one\n'])
196        shell.push(['one\n', 'two\n', ''])
197        self.assertEqual(f.readlines(4), ['one\n', 'two\n'])
198
199        shell.push(['one\n', 'two\n', ''])
200        self.assertRaises(TypeError, f.readlines, 1.5)
201        self.assertRaises(TypeError, f.readlines, '1')
202        self.assertRaises(TypeError, f.readlines, 1, 1)
203
204    def test_close(self):
205        shell = MockShell()
206        f = run.StdInputFile(shell, 'stdin')
207        shell.push(['one\n', 'two\n', ''])
208        self.assertFalse(f.closed)
209        self.assertEqual(f.readline(), 'one\n')
210        f.close()
211        self.assertFalse(f.closed)
212        self.assertEqual(f.readline(), 'two\n')
213        self.assertRaises(TypeError, f.close, 1)
214
215
216class StdOutputFilesTest(unittest.TestCase):
217
218    def test_misc(self):
219        shell = MockShell()
220        f = run.StdOutputFile(shell, 'stdout')
221        self.assertIsInstance(f, io.TextIOBase)
222        self.assertEqual(f.encoding, 'utf-8')
223        self.assertEqual(f.errors, 'strict')
224        self.assertIsNone(f.newlines)
225        self.assertEqual(f.name, '<stdout>')
226        self.assertFalse(f.closed)
227        self.assertTrue(f.isatty())
228        self.assertFalse(f.readable())
229        self.assertTrue(f.writable())
230        self.assertFalse(f.seekable())
231
232    def test_unsupported(self):
233        shell = MockShell()
234        f = run.StdOutputFile(shell, 'stdout')
235        self.assertRaises(OSError, f.fileno)
236        self.assertRaises(OSError, f.tell)
237        self.assertRaises(OSError, f.seek, 0)
238        self.assertRaises(OSError, f.read, 0)
239        self.assertRaises(OSError, f.readline, 0)
240
241    def test_write(self):
242        shell = MockShell()
243        f = run.StdOutputFile(shell, 'stdout')
244        f.write('test')
245        self.assertEqual(shell.written, [('test', 'stdout')])
246        shell.reset()
247        f.write('t\xe8\u015b\U0001d599')
248        self.assertEqual(shell.written, [('t\xe8\u015b\U0001d599', 'stdout')])
249        shell.reset()
250
251        f.write(S('t\xe8\u015b\U0001d599'))
252        self.assertEqual(shell.written, [('t\xe8\u015b\U0001d599', 'stdout')])
253        self.assertEqual(type(shell.written[0][0]), str)
254        shell.reset()
255
256        self.assertRaises(TypeError, f.write)
257        self.assertEqual(shell.written, [])
258        self.assertRaises(TypeError, f.write, b'test')
259        self.assertRaises(TypeError, f.write, 123)
260        self.assertEqual(shell.written, [])
261        self.assertRaises(TypeError, f.write, 'test', 'spam')
262        self.assertEqual(shell.written, [])
263
264    def test_write_stderr_nonencodable(self):
265        shell = MockShell()
266        f = run.StdOutputFile(shell, 'stderr', 'iso-8859-15', 'backslashreplace')
267        f.write('t\xe8\u015b\U0001d599\xa4')
268        self.assertEqual(shell.written, [('t\xe8\\u015b\\U0001d599\\xa4', 'stderr')])
269        shell.reset()
270
271        f.write(S('t\xe8\u015b\U0001d599\xa4'))
272        self.assertEqual(shell.written, [('t\xe8\\u015b\\U0001d599\\xa4', 'stderr')])
273        self.assertEqual(type(shell.written[0][0]), str)
274        shell.reset()
275
276        self.assertRaises(TypeError, f.write)
277        self.assertEqual(shell.written, [])
278        self.assertRaises(TypeError, f.write, b'test')
279        self.assertRaises(TypeError, f.write, 123)
280        self.assertEqual(shell.written, [])
281        self.assertRaises(TypeError, f.write, 'test', 'spam')
282        self.assertEqual(shell.written, [])
283
284    def test_writelines(self):
285        shell = MockShell()
286        f = run.StdOutputFile(shell, 'stdout')
287        f.writelines([])
288        self.assertEqual(shell.written, [])
289        shell.reset()
290        f.writelines(['one\n', 'two'])
291        self.assertEqual(shell.written,
292                         [('one\n', 'stdout'), ('two', 'stdout')])
293        shell.reset()
294        f.writelines(['on\xe8\n', 'tw\xf2'])
295        self.assertEqual(shell.written,
296                         [('on\xe8\n', 'stdout'), ('tw\xf2', 'stdout')])
297        shell.reset()
298
299        f.writelines([S('t\xe8st')])
300        self.assertEqual(shell.written, [('t\xe8st', 'stdout')])
301        self.assertEqual(type(shell.written[0][0]), str)
302        shell.reset()
303
304        self.assertRaises(TypeError, f.writelines)
305        self.assertEqual(shell.written, [])
306        self.assertRaises(TypeError, f.writelines, 123)
307        self.assertEqual(shell.written, [])
308        self.assertRaises(TypeError, f.writelines, [b'test'])
309        self.assertRaises(TypeError, f.writelines, [123])
310        self.assertEqual(shell.written, [])
311        self.assertRaises(TypeError, f.writelines, [], [])
312        self.assertEqual(shell.written, [])
313
314    def test_close(self):
315        shell = MockShell()
316        f = run.StdOutputFile(shell, 'stdout')
317        self.assertFalse(f.closed)
318        f.write('test')
319        f.close()
320        self.assertTrue(f.closed)
321        self.assertRaises(ValueError, f.write, 'x')
322        self.assertEqual(shell.written, [('test', 'stdout')])
323        f.close()
324        self.assertRaises(TypeError, f.close, 1)
325
326
327class RecursionLimitTest(unittest.TestCase):
328    # Test (un)install_recursionlimit_wrappers and fixdoc.
329
330    def test_bad_setrecursionlimit_calls(self):
331        run.install_recursionlimit_wrappers()
332        self.addCleanup(run.uninstall_recursionlimit_wrappers)
333        f = sys.setrecursionlimit
334        self.assertRaises(TypeError, f, limit=100)
335        self.assertRaises(TypeError, f, 100, 1000)
336        self.assertRaises(ValueError, f, 0)
337
338    def test_roundtrip(self):
339        run.install_recursionlimit_wrappers()
340        self.addCleanup(run.uninstall_recursionlimit_wrappers)
341
342        # Check that setting the recursion limit works.
343        orig_reclimit = sys.getrecursionlimit()
344        self.addCleanup(sys.setrecursionlimit, orig_reclimit)
345        sys.setrecursionlimit(orig_reclimit + 3)
346
347        # Check that the new limit is returned by sys.getrecursionlimit().
348        new_reclimit = sys.getrecursionlimit()
349        self.assertEqual(new_reclimit, orig_reclimit + 3)
350
351    def test_default_recursion_limit_preserved(self):
352        orig_reclimit = sys.getrecursionlimit()
353        run.install_recursionlimit_wrappers()
354        self.addCleanup(run.uninstall_recursionlimit_wrappers)
355        new_reclimit = sys.getrecursionlimit()
356        self.assertEqual(new_reclimit, orig_reclimit)
357
358    def test_fixdoc(self):
359        # Put here until better place for miscellaneous test.
360        def func(): "docstring"
361        run.fixdoc(func, "more")
362        self.assertEqual(func.__doc__, "docstring\n\nmore")
363        func.__doc__ = None
364        run.fixdoc(func, "more")
365        self.assertEqual(func.__doc__, "more")
366
367
368class HandleErrorTest(unittest.TestCase):
369    # Method of MyRPCServer
370    def test_fatal_error(self):
371        eq = self.assertEqual
372        with captured_output('__stderr__') as err,\
373             mock.patch('idlelib.run.thread.interrupt_main',
374                        new_callable=Func) as func:
375            try:
376                raise EOFError
377            except EOFError:
378                run.MyRPCServer.handle_error(None, 'abc', '123')
379            eq(run.exit_now, True)
380            run.exit_now = False
381            eq(err.getvalue(), '')
382
383            try:
384                raise IndexError
385            except IndexError:
386                run.MyRPCServer.handle_error(None, 'abc', '123')
387            eq(run.quitting, True)
388            run.quitting = False
389            msg = err.getvalue()
390            self.assertIn('abc', msg)
391            self.assertIn('123', msg)
392            self.assertIn('IndexError', msg)
393            eq(func.called, 2)
394
395
396class ExecRuncodeTest(unittest.TestCase):
397
398    @classmethod
399    def setUpClass(cls):
400        cls.addClassCleanup(setattr,run,'print_exception',run.print_exception)
401        cls.prt = Func()  # Need reference.
402        run.print_exception = cls.prt
403        mockrpc = mock.Mock()
404        mockrpc.console.getvar = Func(result=False)
405        cls.ex = run.Executive(mockrpc)
406
407    @classmethod
408    def tearDownClass(cls):
409        assert sys.excepthook == sys.__excepthook__
410
411    def test_exceptions(self):
412        ex = self.ex
413        ex.runcode('1/0')
414        self.assertIs(ex.user_exc_info[0], ZeroDivisionError)
415
416        self.addCleanup(setattr, sys, 'excepthook', sys.__excepthook__)
417        sys.excepthook = lambda t, e, tb: run.print_exception(t)
418        ex.runcode('1/0')
419        self.assertIs(self.prt.args[0], ZeroDivisionError)
420
421        sys.excepthook = lambda: None
422        ex.runcode('1/0')
423        t, e, tb = ex.user_exc_info
424        self.assertIs(t, TypeError)
425        self.assertTrue(isinstance(e.__context__, ZeroDivisionError))
426
427
428if __name__ == '__main__':
429    unittest.main(verbosity=2)
430