##// END OF EJS Templates
Swallow potential exceptions from showtraceback() (#13934)...
Swallow potential exceptions from showtraceback() (#13934) 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. ```python 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:

r27381:8491abce
r28101:e548ee23 merge
Show More
release
85 lines | 2.0 KiB | text/plain | TextLexer
#!/usr/bin/env python3
"""IPython release script.
This should ONLY be run at real release time.
"""
from __future__ import print_function
import os
from glob import glob
from subprocess import call
import sys
from toollib import (get_ipdir, pjoin, cd, execfile, sh, archive,
archive_user, archive_dir)
# Get main ipython dir, this will raise if it doesn't pass some checks
ipdir = get_ipdir()
tooldir = pjoin(ipdir, 'tools')
distdir = pjoin(ipdir, 'dist')
# Where I keep static backups of each release
ipbackupdir = os.path.expanduser('~/ipython/backup')
if not os.path.exists(ipbackupdir):
os.makedirs(ipbackupdir)
# Start in main IPython dir
cd(ipdir)
# Load release info
version = None
execfile(pjoin('IPython','core','release.py'), globals())
# Build site addresses for file uploads
release_site = '%s/release/%s' % (archive, version)
backup_site = '%s/backup/' % archive
# Start actual release process
print()
print('Releasing IPython')
print('=================')
print()
print('Version:', version)
print()
print('Source IPython directory:', ipdir)
print()
# Perform local backup, go to tools dir to run it.
cd(tooldir)
if 'upload' in sys.argv:
cd(distdir)
# do not upload OS specific files like .DS_Store
to_upload = glob('*.whl')+glob('*.tar.gz')
# Make target dir if it doesn't exist
print('1. Uploading IPython to archive.ipython.org')
sh('ssh %s "mkdir -p %s/release/%s" ' % (archive_user, archive_dir, version))
sh('scp *.tar.gz *.whl %s' % release_site)
print('2. Uploading backup files...')
cd(ipbackupdir)
sh('scp `ls -1tr *tgz | tail -1` %s' % backup_site)
print('3. Uploading to PyPI using twine')
cd(distdir)
call(['twine', 'upload', '--verbose'] + to_upload)
else:
# Build, but don't upload
# Make backup tarball
sh('python make_tarball.py')
sh('mv ipython-*.tgz %s' % ipbackupdir)
# Build release files
sh('./build_release')
cd(ipdir)
print("`./release upload` to upload source distribution on PyPI and ipython archive")
sys.exit(0)