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