##// END OF EJS Templates
Swallow potential exceptions from showtraceback()...
Swallow potential exceptions from showtraceback() The nbgrader project is aware of a form of cheating where students disrupt `InteractiveShell.showtraceback` in hopes of hiding exceptions to avoid losing points. They have implemented a solution to prevent this cheating from working on the client side, and have some tests to demonstrate this technique: https://github.com/jupyter/nbgrader/blob/main/nbgrader/tests/apps/files/submitted-cheat-attempt.ipynb https://github.com/jupyter/nbgrader/blob/main/nbgrader/tests/apps/files/submitted-cheat-attempt-alternative.ipynb In essence, these attacks import the interactive shell and erase the traceback handler so that their failing tests won't report failures. import IPython.core.interactiveshell IPython.core.interactiveshell.InteractiveShell.showtraceback = None The problem is that this causes an exception inside the kernel, leading to a stalled execution. The kernel has stopped working, but the client continues to wait for messages. So far, nbgrader's solution to this is to require a timeout value so the client can eventually decide it is done. This prevents allowing a value of `None` for `Execute.timeout` because this would cause a test case to infinitely hang. This commit addresses the problem by making `InteractiveShell._run_cell` a little more protective around it's call to `showtraceback()`. There is already a try/except block around running the cell. This commit adds a finally clause so that the method will _always_ return an `ExecutionResult`, even if a new exception is thrown within the except clause. For the record, the exception thrown is: TypeError: 'NoneType' object is not callable Accepting this change will allow nbgrader to update `nbgrader.preprocessors.Execute` to support a type of `Integer(allow_none=True)` as the parent `NotebookClient` intended. Discussion about this is ongoing in jupyter/nbgrader#1690.

File last commit:

r26820:eadea372
r28094:fd34cf5f
Show More
qt.py
85 lines | 3.1 KiB | text/x-python | PythonLexer
Thomas Kluyver
Try to fix Qt event loop, take III...
r22654 import sys
Christoph
Disable qt inputhook backend when DISPLAY is not set on linux, because the qt lib does not handle this correct
r24529 import os
Thomas A Caswell
ENH: add support for Qt6 input hooks...
r26683 from IPython.external.qt_for_kernel import QtCore, QtGui, enum_helper
Talley Lambert
add patch
r26355 from IPython import get_ipython
Thomas Kluyver
Write & borrow some inputhooks for prompt_toolkit
r21934
Thomas Kluyver
Create a QApplication for inputhook if one doesn't already exist...
r22707 # If we create a QApplication, keep a reference to it so that it doesn't get
# garbage collected.
_appref = None
Christoph
Disable qt inputhook backend when DISPLAY is not set on linux, because the qt lib does not handle this correct
r24529 _already_warned = False
Thomas A Caswell
FIX: re-order qt eventloop hook a bit...
r23367
Thomas A Caswell
FIX: make sure the QSocketNotifier is disabled...
r25035
Thomas A Caswell
ENH: add support for Qt6 input hooks...
r26683 def _exec(obj):
# exec on PyQt6, exec_ elsewhere.
obj.exec() if hasattr(obj, "exec") else obj.exec_()
Talley Lambert
add patch
r26355 def _reclaim_excepthook():
shell = get_ipython()
if shell is not None:
sys.excepthook = shell.excepthook
Thomas Kluyver
Write & borrow some inputhooks for prompt_toolkit
r21934 def inputhook(context):
Thomas Kluyver
Create a QApplication for inputhook if one doesn't already exist...
r22707 global _appref
Thomas Kluyver
Write & borrow some inputhooks for prompt_toolkit
r21934 app = QtCore.QCoreApplication.instance()
if not app:
Talley Lambert
formatting
r26356 if sys.platform == 'linux':
if not os.environ.get('DISPLAY') \
and not os.environ.get('WAYLAND_DISPLAY'):
Christoph
Disable qt inputhook backend when DISPLAY is not set on linux, because the qt lib does not handle this correct
r24529 import warnings
global _already_warned
if not _already_warned:
_already_warned = True
warnings.warn(
Talley Lambert
formatting
r26356 'The DISPLAY or WAYLAND_DISPLAY environment variable is '
'not set or empty and Qt5 requires this environment '
'variable. Deactivate Qt5 code.'
Christoph
Disable qt inputhook backend when DISPLAY is not set on linux, because the qt lib does not handle this correct
r24529 )
return
Thomas A Caswell
ENH: add support for Qt6 input hooks...
r26683 try:
Thomas A Caswell
STY: apply darker
r26684 QtCore.QApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)
Thomas A Caswell
ENH: add support for Qt6 input hooks...
r26683 except AttributeError: # Only for Qt>=5.6, <6.
pass
try:
QtCore.QApplication.setHighDpiScaleFactorRoundingPolicy(
Thomas A Caswell
STY: apply darker
r26684 QtCore.Qt.HighDpiScaleFactorRoundingPolicy.PassThrough
)
Thomas A Caswell
ENH: add support for Qt6 input hooks...
r26683 except AttributeError: # Only for Qt>=5.14.
pass
Thomas Kluyver
Create a QApplication for inputhook if one doesn't already exist...
r22707 _appref = app = QtGui.QApplication([" "])
Talley Lambert
add patch
r26355
# "reclaim" IPython sys.excepthook after event loop starts
# without this, it defaults back to BaseIPythonApplication.excepthook
# and exceptions in the Qt event loop are rendered without traceback
# formatting and look like "bug in IPython".
QtCore.QTimer.singleShot(0, _reclaim_excepthook)
Thomas Kluyver
Write & borrow some inputhooks for prompt_toolkit
r21934 event_loop = QtCore.QEventLoop(app)
Thomas Kluyver
Try to fix Qt event loop, take III...
r22654
Talley Lambert
formatting
r26356 if sys.platform == 'win32':
Thomas Kluyver
Try to fix Qt event loop, take III...
r22654 # The QSocketNotifier method doesn't appear to work on Windows.
# Use polling instead.
timer = QtCore.QTimer()
timer.timeout.connect(event_loop.quit)
while not context.input_is_ready():
timer.start(50) # 50 ms
KIU Shueng Chuan
use _exec wrapper for qt win32 inputhook
r26820 _exec(event_loop)
Thomas Kluyver
Try to fix Qt event loop, take III...
r22654 timer.stop()
else:
# On POSIX platforms, we can use a file descriptor to quit the event
# loop when there is input ready to read.
Thomas A Caswell
ENH: add support for Qt6 input hooks...
r26683 notifier = QtCore.QSocketNotifier(
Thomas A Caswell
STY: apply darker
r26684 context.fileno(), enum_helper("QtCore.QSocketNotifier.Type").Read
Thomas A Caswell
ENH: add support for Qt6 input hooks...
r26683 )
Thomas A Caswell
FIX: make sure the QSocketNotifier is disabled...
r25035 try:
# connect the callback we care about before we turn it on
Matthias Bussonnier
Add comments to explain the lambda:...
r25812 # lambda is necessary as PyQT inspect the function signature to know
# what arguments to pass to. See https://github.com/ipython/ipython/pull/12355
Talley Lambert
fix notifier.activated connection
r25791 notifier.activated.connect(lambda: event_loop.exit())
Thomas A Caswell
FIX: make sure the QSocketNotifier is disabled...
r25035 notifier.setEnabled(True)
# only start the event loop we are not already flipped
if not context.input_is_ready():
Thomas A Caswell
ENH: add support for Qt6 input hooks...
r26683 _exec(event_loop)
Thomas A Caswell
FIX: make sure the QSocketNotifier is disabled...
r25035 finally:
notifier.setEnabled(False)