1 "Test InteractiveConsole and InteractiveInterpreter from code module" 2 import sys 3 import unittest 4 from textwrap import dedent 5 from contextlib import ExitStack 6 from unittest import mock 7 from test import support 8 9 code = support.import_module('code') 10 11 12 class TestInteractiveConsole(unittest.TestCase): 13 14 def setUp(self): 15 self.console = code.InteractiveConsole() 16 self.mock_sys() 17 18 def mock_sys(self): 19 "Mock system environment for InteractiveConsole" 20 # use exit stack to match patch context managers to addCleanup 21 stack = ExitStack() 22 self.addCleanup(stack.close) 23 self.infunc = stack.enter_context(mock.patch('code.input', 24 create=True)) 25 self.stdout = stack.enter_context(mock.patch('code.sys.stdout')) 26 self.stderr = stack.enter_context(mock.patch('code.sys.stderr')) 27 prepatch = mock.patch('code.sys', wraps=code.sys, spec=code.sys) 28 self.sysmod = stack.enter_context(prepatch) 29 if sys.excepthook is sys.__excepthook__: 30 self.sysmod.excepthook = self.sysmod.__excepthook__ 31 32 def test_ps1(self): 33 self.infunc.side_effect = EOFError('Finished') 34 self.console.interact() 35 self.assertEqual(self.sysmod.ps1, '>>> ') 36 37 def test_ps2(self): 38 self.infunc.side_effect = EOFError('Finished') 39 self.console.interact() 40 self.assertEqual(self.sysmod.ps2, '... ') 41 42 def test_console_stderr(self): 43 self.infunc.side_effect = ["'antioch'", "", EOFError('Finished')] 44 self.console.interact() 45 for call in list(self.stdout.method_calls): 46 if 'antioch' in ''.join(call[1]): 47 break 48 else: 49 raise AssertionError("no console stdout") 50 51 def test_syntax_error(self): 52 self.infunc.side_effect = ["undefined", EOFError('Finished')] 53 self.console.interact() 54 for call in self.stderr.method_calls: 55 if 'NameError' in ''.join(call[1]): 56 break 57 else: 58 raise AssertionError("No syntax error from console") 59 60 def test_sysexcepthook(self): 61 self.infunc.side_effect = ["raise ValueError('')", 62 EOFError('Finished')] 63 hook = mock.Mock() 64 self.sysmod.excepthook = hook 65 self.console.interact() 66 self.assertTrue(hook.called) 67 68 def test_banner(self): 69 # with banner 70 self.infunc.side_effect = EOFError('Finished') 71 self.console.interact(banner='Foo') 72 self.assertEqual(len(self.stderr.method_calls), 3) 73 banner_call = self.stderr.method_calls[0] 74 self.assertEqual(banner_call, ['write', ('Foo\n',), {}]) 75 76 # no banner 77 self.stderr.reset_mock() 78 self.infunc.side_effect = EOFError('Finished') 79 self.console.interact(banner='') 80 self.assertEqual(len(self.stderr.method_calls), 2) 81 82 def test_exit_msg(self): 83 # default exit message 84 self.infunc.side_effect = EOFError('Finished') 85 self.console.interact(banner='') 86 self.assertEqual(len(self.stderr.method_calls), 2) 87 err_msg = self.stderr.method_calls[1] 88 expected = 'now exiting InteractiveConsole...\n' 89 self.assertEqual(err_msg, ['write', (expected,), {}]) 90 91 # no exit message 92 self.stderr.reset_mock() 93 self.infunc.side_effect = EOFError('Finished') 94 self.console.interact(banner='', exitmsg='') 95 self.assertEqual(len(self.stderr.method_calls), 1) 96 97 # custom exit message 98 self.stderr.reset_mock() 99 message = ( 100 'bye! \N{GREEK SMALL LETTER ZETA}\N{CYRILLIC SMALL LETTER ZHE}' 101 ) 102 self.infunc.side_effect = EOFError('Finished') 103 self.console.interact(banner='', exitmsg=message) 104 self.assertEqual(len(self.stderr.method_calls), 2) 105 err_msg = self.stderr.method_calls[1] 106 expected = message + '\n' 107 self.assertEqual(err_msg, ['write', (expected,), {}]) 108 109 110 def test_cause_tb(self): 111 self.infunc.side_effect = ["raise ValueError('') from AttributeError", 112 EOFError('Finished')] 113 self.console.interact() 114 output = ''.join(''.join(call[1]) for call in self.stderr.method_calls) 115 expected = dedent(""" 116 AttributeError 117 118 The above exception was the direct cause of the following exception: 119 120 Traceback (most recent call last): 121 File "<console>", line 1, in <module> 122 ValueError 123 """) 124 self.assertIn(expected, output) 125 126 def test_context_tb(self): 127 self.infunc.side_effect = ["try: ham\nexcept: eggs\n", 128 EOFError('Finished')] 129 self.console.interact() 130 output = ''.join(''.join(call[1]) for call in self.stderr.method_calls) 131 expected = dedent(""" 132 Traceback (most recent call last): 133 File "<console>", line 1, in <module> 134 NameError: name 'ham' is not defined 135 136 During handling of the above exception, another exception occurred: 137 138 Traceback (most recent call last): 139 File "<console>", line 2, in <module> 140 NameError: name 'eggs' is not defined 141 """) 142 self.assertIn(expected, output) 143 144 145 if __name__ == "__main__": 146 unittest.main() 147