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 del self.sysmod.ps1 32 del self.sysmod.ps2 33 34 def test_ps1(self): 35 self.infunc.side_effect = EOFError('Finished') 36 self.console.interact() 37 self.assertEqual(self.sysmod.ps1, '>>> ') 38 self.sysmod.ps1 = 'custom1> ' 39 self.console.interact() 40 self.assertEqual(self.sysmod.ps1, 'custom1> ') 41 42 def test_ps2(self): 43 self.infunc.side_effect = EOFError('Finished') 44 self.console.interact() 45 self.assertEqual(self.sysmod.ps2, '... ') 46 self.sysmod.ps1 = 'custom2> ' 47 self.console.interact() 48 self.assertEqual(self.sysmod.ps1, 'custom2> ') 49 50 def test_console_stderr(self): 51 self.infunc.side_effect = ["'antioch'", "", EOFError('Finished')] 52 self.console.interact() 53 for call in list(self.stdout.method_calls): 54 if 'antioch' in ''.join(call[1]): 55 break 56 else: 57 raise AssertionError("no console stdout") 58 59 def test_syntax_error(self): 60 self.infunc.side_effect = ["undefined", EOFError('Finished')] 61 self.console.interact() 62 for call in self.stderr.method_calls: 63 if 'NameError' in ''.join(call[1]): 64 break 65 else: 66 raise AssertionError("No syntax error from console") 67 68 def test_sysexcepthook(self): 69 self.infunc.side_effect = ["raise ValueError('')", 70 EOFError('Finished')] 71 hook = mock.Mock() 72 self.sysmod.excepthook = hook 73 self.console.interact() 74 self.assertTrue(hook.called) 75 76 def test_banner(self): 77 # with banner 78 self.infunc.side_effect = EOFError('Finished') 79 self.console.interact(banner='Foo') 80 self.assertEqual(len(self.stderr.method_calls), 3) 81 banner_call = self.stderr.method_calls[0] 82 self.assertEqual(banner_call, ['write', ('Foo\n',), {}]) 83 84 # no banner 85 self.stderr.reset_mock() 86 self.infunc.side_effect = EOFError('Finished') 87 self.console.interact(banner='') 88 self.assertEqual(len(self.stderr.method_calls), 2) 89 90 def test_exit_msg(self): 91 # default exit message 92 self.infunc.side_effect = EOFError('Finished') 93 self.console.interact(banner='') 94 self.assertEqual(len(self.stderr.method_calls), 2) 95 err_msg = self.stderr.method_calls[1] 96 expected = 'now exiting InteractiveConsole...\n' 97 self.assertEqual(err_msg, ['write', (expected,), {}]) 98 99 # no exit message 100 self.stderr.reset_mock() 101 self.infunc.side_effect = EOFError('Finished') 102 self.console.interact(banner='', exitmsg='') 103 self.assertEqual(len(self.stderr.method_calls), 1) 104 105 # custom exit message 106 self.stderr.reset_mock() 107 message = ( 108 'bye! \N{GREEK SMALL LETTER ZETA}\N{CYRILLIC SMALL LETTER ZHE}' 109 ) 110 self.infunc.side_effect = EOFError('Finished') 111 self.console.interact(banner='', exitmsg=message) 112 self.assertEqual(len(self.stderr.method_calls), 2) 113 err_msg = self.stderr.method_calls[1] 114 expected = message + '\n' 115 self.assertEqual(err_msg, ['write', (expected,), {}]) 116 117 118 def test_cause_tb(self): 119 self.infunc.side_effect = ["raise ValueError('') from AttributeError", 120 EOFError('Finished')] 121 self.console.interact() 122 output = ''.join(''.join(call[1]) for call in self.stderr.method_calls) 123 expected = dedent(""" 124 AttributeError 125 126 The above exception was the direct cause of the following exception: 127 128 Traceback (most recent call last): 129 File "<console>", line 1, in <module> 130 ValueError 131 """) 132 self.assertIn(expected, output) 133 134 def test_context_tb(self): 135 self.infunc.side_effect = ["try: ham\nexcept: eggs\n", 136 EOFError('Finished')] 137 self.console.interact() 138 output = ''.join(''.join(call[1]) for call in self.stderr.method_calls) 139 expected = dedent(""" 140 Traceback (most recent call last): 141 File "<console>", line 1, in <module> 142 NameError: name 'ham' is not defined 143 144 During handling of the above exception, another exception occurred: 145 146 Traceback (most recent call last): 147 File "<console>", line 2, in <module> 148 NameError: name 'eggs' is not defined 149 """) 150 self.assertIn(expected, output) 151 152 153 if __name__ == "__main__": 154 unittest.main() 155