Skip to content

Commit 68102fb

Browse files
bpo-43008: Make IDLE respect sys.excepthook (GH-24302)
Co-authored-by: Terry Jan Reedy <[email protected]> (cherry picked from commit 7a34380) Co-authored-by: Ken <[email protected]>
1 parent fd668bc commit 68102fb

File tree

5 files changed

+63
-13
lines changed

5 files changed

+63
-13
lines changed

Doc/library/idle.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ View Last Restart
250250
Scroll the shell window to the last Shell restart.
251251

252252
Restart Shell
253-
Restart the shell to clean the environment.
253+
Restart the shell to clean the environment and reset display and exception handling.
254254

255255
Previous History
256256
Cycle through earlier commands in history which match the current entry.

Lib/idlelib/NEWS.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ Released on 2021-02-15?
33
======================================
44

55

6+
bpo-43008: Make IDLE invoke :func:`sys.excepthook` in normal,
7+
2-process mode.
8+
69
bpo-33065: Fix problem debugging user classes with __repr__ method.
710

811
bpo-32631: Finish zzdummy example extension module: make menu entries

Lib/idlelib/idle_test/test_run.py

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
"Test run, coverage 49%."
22

33
from idlelib import run
4+
import io
5+
import sys
6+
from test.support import captured_output, captured_stderr
47
import unittest
58
from unittest import mock
9+
import idlelib
610
from idlelib.idle_test.mock_idle import Func
7-
from test.support import captured_output, captured_stderr
811

9-
import io
10-
import sys
12+
idlelib.testing = True # Use {} for executing test user code.
1113

1214

13-
class RunTest(unittest.TestCase):
15+
class PrintExceptionTest(unittest.TestCase):
1416

1517
def test_print_exception_unhashable(self):
1618
class UnhashableException(Exception):
@@ -351,5 +353,38 @@ def test_fatal_error(self):
351353
self.assertIn('IndexError', msg)
352354
eq(func.called, 2)
353355

356+
357+
class ExecRuncodeTest(unittest.TestCase):
358+
359+
@classmethod
360+
def setUpClass(cls):
361+
cls.addClassCleanup(setattr,run,'print_exception',run.print_exception)
362+
cls.prt = Func() # Need reference.
363+
run.print_exception = cls.prt
364+
mockrpc = mock.Mock()
365+
mockrpc.console.getvar = Func(result=False)
366+
cls.ex = run.Executive(mockrpc)
367+
368+
@classmethod
369+
def tearDownClass(cls):
370+
assert sys.excepthook == sys.__excepthook__
371+
372+
def test_exceptions(self):
373+
ex = self.ex
374+
ex.runcode('1/0')
375+
self.assertIs(ex.user_exc_info[0], ZeroDivisionError)
376+
377+
self.addCleanup(setattr, sys, 'excepthook', sys.__excepthook__)
378+
sys.excepthook = lambda t, e, tb: run.print_exception(t)
379+
ex.runcode('1/0')
380+
self.assertIs(self.prt.args[0], ZeroDivisionError)
381+
382+
sys.excepthook = lambda: None
383+
ex.runcode('1/0')
384+
t, e, tb = ex.user_exc_info
385+
self.assertIs(t, TypeError)
386+
self.assertTrue(isinstance(e.__context__, ZeroDivisionError))
387+
388+
354389
if __name__ == '__main__':
355390
unittest.main(verbosity=2)

Lib/idlelib/run.py

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import threading
1717
import warnings
1818

19+
import idlelib # testing
1920
from idlelib import autocomplete # AutoComplete, fetch_encodings
2021
from idlelib import calltip # Calltip
2122
from idlelib import debugger_r # start_debugger
@@ -542,14 +543,17 @@ class Executive:
542543

543544
def __init__(self, rpchandler):
544545
self.rpchandler = rpchandler
545-
self.locals = __main__.__dict__
546-
self.calltip = calltip.Calltip()
547-
self.autocomplete = autocomplete.AutoComplete()
546+
if idlelib.testing is False:
547+
self.locals = __main__.__dict__
548+
self.calltip = calltip.Calltip()
549+
self.autocomplete = autocomplete.AutoComplete()
550+
else:
551+
self.locals = {}
548552

549553
def runcode(self, code):
550554
global interruptable
551555
try:
552-
self.usr_exc_info = None
556+
self.user_exc_info = None
553557
interruptable = True
554558
try:
555559
exec(code, self.locals)
@@ -562,10 +566,17 @@ def runcode(self, code):
562566
print('SystemExit: ' + str(ob), file=sys.stderr)
563567
# Return to the interactive prompt.
564568
except:
565-
self.usr_exc_info = sys.exc_info()
569+
self.user_exc_info = sys.exc_info() # For testing, hook, viewer.
566570
if quitting:
567571
exit()
568-
print_exception()
572+
if sys.excepthook is sys.__excepthook__:
573+
print_exception()
574+
else:
575+
try:
576+
sys.excepthook(*self.user_exc_info)
577+
except:
578+
self.user_exc_info = sys.exc_info() # For testing.
579+
print_exception()
569580
jit = self.rpchandler.console.getvar("<<toggle-jit-stack-viewer>>")
570581
if jit:
571582
self.rpchandler.interp.open_remote_stack_viewer()
@@ -590,8 +601,8 @@ def get_the_completion_list(self, what, mode):
590601
return self.autocomplete.fetch_completions(what, mode)
591602

592603
def stackviewer(self, flist_oid=None):
593-
if self.usr_exc_info:
594-
typ, val, tb = self.usr_exc_info
604+
if self.user_exc_info:
605+
typ, val, tb = self.user_exc_info
595606
else:
596607
return None
597608
flist = None
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Make IDLE invoke :func:`sys.excepthook` in normal, 2-process mode.

0 commit comments

Comments
 (0)