##// 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:

r27875:1c3678bf
r28094:fd34cf5f
Show More
compilerop.py
214 lines | 7.5 KiB | text/x-python | PythonLexer
Fernando Perez
Complete implementation of interactive traceback support....
r3175 """Compiler tools with improved interactive support.
Provides compilation machinery similar to codeop, but with caching support so
we can provide interactive tracebacks.
Authors
-------
* Robert Kern
* Fernando Perez
Thomas Kluyver
Change integer options to string options for interactivity.
r3533 * Thomas Kluyver
Fernando Perez
Complete implementation of interactive traceback support....
r3175 """
# Note: though it might be more natural to name this module 'compiler', that
# name is in the stdlib and name collisions with the stdlib tend to produce
# weird problems (often with third-party tools).
#-----------------------------------------------------------------------------
Matthias BUSSONNIER
update copyright to 2011/20xx-2011...
r5390 # Copyright (C) 2010-2011 The IPython Development Team.
Fernando Perez
Complete implementation of interactive traceback support....
r3175 #
# Distributed under the terms of the BSD License.
#
# The full license is in the file COPYING.txt, distributed with this software.
#-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------
# Imports
#-----------------------------------------------------------------------------
# Stdlib imports
Bradley M. Froehle
embed(): Default to the future compile flags of the calling frame.
r7894 import __future__
Thomas Kluyver
Parse user code to AST using compiler flags....
r4795 from ast import PyCF_ONLY_AST
Fernando Perez
Complete implementation of interactive traceback support....
r3175 import codeop
Bradley M. Froehle
embed(): Default to the future compile flags of the calling frame.
r7894 import functools
Fernando Perez
Complete implementation of interactive traceback support....
r3175 import hashlib
import linecache
Bradley M. Froehle
embed(): Default to the future compile flags of the calling frame.
r7894 import operator
Fernando Perez
Complete implementation of interactive traceback support....
r3175 import time
Matthias Bussonnier
draft compat 3.8
r25019 from contextlib import contextmanager
Fernando Perez
Complete implementation of interactive traceback support....
r3175
#-----------------------------------------------------------------------------
Bradley M. Froehle
embed(): Default to the future compile flags of the calling frame.
r7894 # Constants
#-----------------------------------------------------------------------------
luz.paz
Misc. typo fixes ...
r24493 # Roughly equal to PyCF_MASK | PyCF_MASK_OBSOLETE as defined in pythonrun.h,
Bradley M. Froehle
embed(): Default to the future compile flags of the calling frame.
r7894 # this is used as a bitmask to extract future-related code flags.
PyCF_MASK = functools.reduce(operator.or_,
(getattr(__future__, fname).compiler_flag
for fname in __future__.all_feature_names))
#-----------------------------------------------------------------------------
Fernando Perez
Complete implementation of interactive traceback support....
r3175 # Local utilities
#-----------------------------------------------------------------------------
def code_name(code, number=0):
""" Compute a (probably) unique name for code for caching.
luz.paz
Whitespace fixes
r24494
Thomas Kluyver
Fix compilerop to handle unicode input.
r3442 This now expects code to be unicode.
Fernando Perez
Complete implementation of interactive traceback support....
r3175 """
Srinath
Make IPython work with OpenSSL in FIPS mode #10615...
r23794 hash_digest = hashlib.sha1(code.encode("utf-8")).hexdigest()
Fernando Perez
Complete implementation of interactive traceback support....
r3175 # Include the number and 12 characters of the hash in the name. It's
# pretty much impossible that in a single session we'll have collisions
# even with truncated hashes, and the full one makes tracebacks too long
return '<ipython-input-{0}-{1}>'.format(number, hash_digest[:12])
#-----------------------------------------------------------------------------
# Classes and functions
#-----------------------------------------------------------------------------
Thomas Kluyver
Replace run_cell method, and fix up caching compiler so we can cache a string, then compile AST nodes.
r3529 class CachingCompiler(codeop.Compile):
Fernando Perez
Complete implementation of interactive traceback support....
r3175 """A compiler that caches code compiled from interactive statements.
"""
def __init__(self):
Thomas Kluyver
Replace run_cell method, and fix up caching compiler so we can cache a string, then compile AST nodes.
r3529 codeop.Compile.__init__(self)
luz.paz
Whitespace fixes
r24494
martinRenou
Improve error tracebacks so that it shows the cell prompts
r26685 # Caching a dictionary { filename: execution_count } for nicely
# rendered tracebacks. The filename corresponds to the filename
# argument used for the builtins.compile function.
self._filename_map = {}
Matthias Bussonnier
Fix test for Python 3.7+...
r24342
Thomas Kluyver
Parse user code to AST using compiler flags....
r4795 def ast_parse(self, source, filename='<unknown>', symbol='exec'):
Thomas Kluyver
Describe arguments for CachingCompiler.ast_parse
r4796 """Parse code to an AST with the current compiler flags active.
luz.paz
Whitespace fixes
r24494
Thomas Kluyver
Describe arguments for CachingCompiler.ast_parse
r4796 Arguments are exactly the same as ast.parse (in the standard library),
and are passed to the built-in compile function."""
Matthias Bussonnier
Revert 3.7 AST fix...
r24564 return compile(source, filename, symbol, self.flags | PyCF_ONLY_AST, 1)
martinRenou
Allow passing a custom CachingCompiler to the interactive shell...
r26329
Thomas Kluyver
Parse user code to AST using compiler flags....
r4795 def reset_compiler_flags(self):
"""Reset compiler flags to default state."""
# This value is copied from codeop.Compile.__init__, so if that ever
# changes, it will need to be updated.
self.flags = codeop.PyCF_DONT_IMPLY_DEDENT
Fernando Perez
Complete implementation of interactive traceback support....
r3175
@property
def compiler_flags(self):
"""Flags currently active in the compilation process.
"""
Thomas Kluyver
Replace run_cell method, and fix up caching compiler so we can cache a string, then compile AST nodes.
r3529 return self.flags
luz.paz
Whitespace fixes
r24494
martinRenou
Allow passing a custom CachingCompiler to the interactive shell...
r26329 def get_code_name(self, raw_code, transformed_code, number):
"""Compute filename given the code, and the cell number.
Parameters
----------
raw_code : str
Matthias Bussonnier
DOC: Some more autoformatting of docstrings....
r26491 The raw cell code.
martinRenou
Allow passing a custom CachingCompiler to the interactive shell...
r26329 transformed_code : str
Matthias Bussonnier
DOC: Some more autoformatting of docstrings....
r26491 The executable Python source code to cache and compile.
martinRenou
Allow passing a custom CachingCompiler to the interactive shell...
r26329 number : int
Matthias Bussonnier
DOC: Some more autoformatting of docstrings....
r26491 A number which forms part of the code's name. Used for the execution
counter.
martinRenou
Allow passing a custom CachingCompiler to the interactive shell...
r26329
Returns
-------
The computed filename.
"""
return code_name(transformed_code, number)
Jason Grout
Make the formatting of a code block name extendable...
r27875 def format_code_name(self, name):
"""Return a user-friendly label and name for a code block.
Parameters
----------
name : str
The name for the code block returned from get_code_name
Returns
-------
A (label, name) pair that can be used in tracebacks, or None if the default formatting should be used.
"""
if name in self._filename_map:
return "Cell", "In[%s]" % self._filename_map[name]
martinRenou
Allow passing a custom CachingCompiler to the interactive shell...
r26329 def cache(self, transformed_code, number=0, raw_code=None):
Thomas Kluyver
Replace run_cell method, and fix up caching compiler so we can cache a string, then compile AST nodes.
r3529 """Make a name for a block of code, and cache the code.
luz.paz
Whitespace fixes
r24494
Fernando Perez
Complete implementation of interactive traceback support....
r3175 Parameters
----------
martinRenou
Allow passing a custom CachingCompiler to the interactive shell...
r26329 transformed_code : str
Matthias Bussonnier
DOC: Some more autoformatting of docstrings....
r26491 The executable Python source code to cache and compile.
Thomas Kluyver
Replace run_cell method, and fix up caching compiler so we can cache a string, then compile AST nodes.
r3529 number : int
Matthias Bussonnier
DOC: Some more autoformatting of docstrings....
r26491 A number which forms part of the code's name. Used for the execution
counter.
martinRenou
Allow passing a custom CachingCompiler to the interactive shell...
r26329 raw_code : str
Matthias Bussonnier
DOC: Some more autoformatting of docstrings....
r26491 The raw code before transformation, if None, set to `transformed_code`.
luz.paz
Whitespace fixes
r24494
Thomas Kluyver
Replace run_cell method, and fix up caching compiler so we can cache a string, then compile AST nodes.
r3529 Returns
-------
The name of the cached code (as a string). Pass this as the filename
argument to compilation, so that tracebacks are correctly hooked up.
Fernando Perez
Complete implementation of interactive traceback support....
r3175 """
martinRenou
Allow passing a custom CachingCompiler to the interactive shell...
r26329 if raw_code is None:
raw_code = transformed_code
name = self.get_code_name(raw_code, transformed_code, number)
martinRenou
Improve error tracebacks so that it shows the cell prompts
r26685
# Save the execution count
self._filename_map[name] = number
Ben Longbons
Stop monkeypatching `linecache`...
r27845 # Since Python 2.5, setting mtime to `None` means the lines will
# never be removed by `linecache.checkcache`. This means all the
# monkeypatching has *never* been necessary, since this code was
# only added in 2010, at which point IPython had already stopped
# supporting Python 2.4.
#
# Note that `linecache.clearcache` and `linecache.updatecache` may
# still remove our code from the cache, but those show explicit
# intent, and we should not try to interfere. Normally the former
# is never called except when out of memory, and the latter is only
# called for lines *not* in the cache.
martinRenou
Allow passing a custom CachingCompiler to the interactive shell...
r26329 entry = (
len(transformed_code),
Ben Longbons
Stop monkeypatching `linecache`...
r27845 None,
martinRenou
Allow passing a custom CachingCompiler to the interactive shell...
r26329 [line + "\n" for line in transformed_code.splitlines()],
name,
)
Fernando Perez
Complete implementation of interactive traceback support....
r3175 linecache.cache[name] = entry
Thomas Kluyver
Replace run_cell method, and fix up caching compiler so we can cache a string, then compile AST nodes.
r3529 return name
Fernando Perez
Complete implementation of interactive traceback support....
r3175
Matthias Bussonnier
draft compat 3.8
r25019 @contextmanager
def extra_flags(self, flags):
Matthias Bussonnier
some more fixes for 3.8
r25055 ## bits that we'll set to 1
turn_on_bits = ~self.flags & flags
Matthias Bussonnier
draft compat 3.8
r25019 self.flags = self.flags | flags
try:
yield
finally:
Matthias Bussonnier
some more fixes for 3.8
r25055 # turn off only the bits we turned on so that something like
martinRenou
Allow passing a custom CachingCompiler to the interactive shell...
r26329 # __future__ that set flags stays.
Matthias Bussonnier
some more fixes for 3.8
r25055 self.flags &= ~turn_on_bits
Matthias Bussonnier
draft compat 3.8
r25019
Thomas Kluyver
Better support compiling cells with separate __future__ environments
r9140 def check_linecache_ipython(*args):
Ben Longbons
Stop monkeypatching `linecache`...
r27845 """Deprecated since IPython 8.6. Call linecache.checkcache() directly.
It was already not necessary to call this function directly. If no
CachingCompiler had been created, this function would fail badly. If
an instance had been created, this function would've been monkeypatched
into place.
As of IPython 8.6, the monkeypatching has gone away entirely. But there
were still internal callers of this function, so maybe external callers
also existed?
Thomas Kluyver
Better support compiling cells with separate __future__ environments
r9140 """
Ben Longbons
Stop monkeypatching `linecache`...
r27845 import warnings
Matthias Bussonnier
please formatter
r27846
Matthias Bussonnier
add some infor in warning
r27847 warnings.warn(
"Deprecated Since IPython 8.6, Just call linecache.checkcache() directly.",
DeprecationWarning,
stacklevel=2,
)
Ben Longbons
Stop monkeypatching `linecache`...
r27845 linecache.checkcache()