asm
This commit is contained in:
@@ -0,0 +1,818 @@
|
||||
#
|
||||
# Copyright (c) 2009- Spyder Kernels Contributors
|
||||
#
|
||||
# Licensed under the terms of the MIT License
|
||||
# (see spyder_kernels/__init__.py for details)
|
||||
# -----------------------------------------------------------------------------
|
||||
#
|
||||
# IMPORTANT NOTE: Don't add a coding line here! It's not necessary for
|
||||
# site files
|
||||
#
|
||||
# Spyder consoles sitecustomize
|
||||
#
|
||||
|
||||
import ast
|
||||
import bdb
|
||||
import io
|
||||
import logging
|
||||
import os
|
||||
import pdb
|
||||
import shlex
|
||||
import sys
|
||||
import time
|
||||
import warnings
|
||||
|
||||
from IPython.core.getipython import get_ipython
|
||||
|
||||
from spyder_kernels.comms.frontendcomm import frontend_request
|
||||
from spyder_kernels.customize.namespace_manager import NamespaceManager
|
||||
from spyder_kernels.customize.spyderpdb import SpyderPdb, get_new_debugger
|
||||
from spyder_kernels.customize.umr import UserModuleReloader
|
||||
from spyder_kernels.py3compat import (
|
||||
PY2, _print, encode, compat_exec, FileNotFoundError)
|
||||
from spyder_kernels.customize.utils import capture_last_Expr, canonic
|
||||
|
||||
if not PY2:
|
||||
from IPython.core.inputtransformer2 import (
|
||||
TransformerManager, leading_indent, leading_empty_lines)
|
||||
else:
|
||||
from IPython.core.inputsplitter import IPythonInputSplitter
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# sys.argv can be missing when Python is embedded, taking care of it.
|
||||
# Fixes Issue 1473 and other crazy crashes with IPython 0.13 trying to
|
||||
# access it.
|
||||
# =============================================================================
|
||||
if not hasattr(sys, 'argv'):
|
||||
sys.argv = ['']
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Main constants
|
||||
# =============================================================================
|
||||
IS_EXT_INTERPRETER = os.environ.get('SPY_EXTERNAL_INTERPRETER') == "True"
|
||||
HIDE_CMD_WINDOWS = os.environ.get('SPY_HIDE_CMD') == "True"
|
||||
SHOW_INVALID_SYNTAX_MSG = True
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Execfile functions
|
||||
#
|
||||
# The definitions for Python 2 on Windows were taken from the IPython project
|
||||
# Copyright (C) The IPython Development Team
|
||||
# Distributed under the terms of the modified BSD license
|
||||
# =============================================================================
|
||||
try:
|
||||
# Python 2
|
||||
import __builtin__ as builtins
|
||||
|
||||
except ImportError:
|
||||
# Python 3
|
||||
import builtins
|
||||
basestring = (str,)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Setting console encoding (otherwise Python does not recognize encoding)
|
||||
# for Windows platforms
|
||||
# =============================================================================
|
||||
if os.name == 'nt' and PY2:
|
||||
try:
|
||||
import locale, ctypes
|
||||
_t, _cp = locale.getdefaultlocale('LANG')
|
||||
try:
|
||||
_cp = int(_cp[2:])
|
||||
ctypes.windll.kernel32.SetConsoleCP(_cp)
|
||||
ctypes.windll.kernel32.SetConsoleOutputCP(_cp)
|
||||
except (ValueError, TypeError):
|
||||
# Code page number in locale is not valid
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Prevent subprocess.Popen calls to create visible console windows on Windows.
|
||||
# See issue #4932
|
||||
# =============================================================================
|
||||
if os.name == 'nt' and HIDE_CMD_WINDOWS:
|
||||
import subprocess
|
||||
creation_flag = 0x08000000 # CREATE_NO_WINDOW
|
||||
|
||||
class SubprocessPopen(subprocess.Popen):
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs['creationflags'] = creation_flag
|
||||
super(SubprocessPopen, self).__init__(*args, **kwargs)
|
||||
|
||||
subprocess.Popen = SubprocessPopen
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Importing user's sitecustomize
|
||||
# =============================================================================
|
||||
try:
|
||||
import sitecustomize #analysis:ignore
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Add default filesystem encoding on Linux to avoid an error with
|
||||
# Matplotlib 1.5 in Python 2 (Fixes Issue 2793)
|
||||
# =============================================================================
|
||||
if PY2 and sys.platform.startswith('linux'):
|
||||
def _getfilesystemencoding_wrapper():
|
||||
return 'utf-8'
|
||||
|
||||
sys.getfilesystemencoding = _getfilesystemencoding_wrapper
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Set PyQt API to #2
|
||||
# =============================================================================
|
||||
if os.environ.get("QT_API") == 'pyqt':
|
||||
try:
|
||||
import sip
|
||||
for qtype in ('QString', 'QVariant', 'QDate', 'QDateTime',
|
||||
'QTextStream', 'QTime', 'QUrl'):
|
||||
sip.setapi(qtype, 2)
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
os.environ.pop('QT_API')
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Patch PyQt4 and PyQt5
|
||||
# =============================================================================
|
||||
# This saves the QApplication instances so that Python doesn't destroy them.
|
||||
# Python sees all the QApplication as differnet Python objects, while
|
||||
# Qt sees them as a singleton (There is only one Application!). Deleting one
|
||||
# QApplication causes all the other Python instances to become broken.
|
||||
# See spyder-ide/spyder/issues/2970
|
||||
try:
|
||||
from PyQt5 import QtWidgets
|
||||
|
||||
class SpyderQApplication(QtWidgets.QApplication):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(SpyderQApplication, self).__init__(*args, **kwargs)
|
||||
# Add reference to avoid destruction
|
||||
# This creates a Memory leak but avoids a Segmentation fault
|
||||
SpyderQApplication._instance_list.append(self)
|
||||
|
||||
SpyderQApplication._instance_list = []
|
||||
QtWidgets.QApplication = SpyderQApplication
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
from PyQt4 import QtGui
|
||||
|
||||
class SpyderQApplication(QtGui.QApplication):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(SpyderQApplication, self).__init__(*args, **kwargs)
|
||||
# Add reference to avoid destruction
|
||||
# This creates a Memory leak but avoids a Segmentation fault
|
||||
SpyderQApplication._instance_list.append(self)
|
||||
|
||||
SpyderQApplication._instance_list = []
|
||||
QtGui.QApplication = SpyderQApplication
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# IPython adjustments
|
||||
# =============================================================================
|
||||
# Patch unittest.main so that errors are printed directly in the console.
|
||||
# See http://comments.gmane.org/gmane.comp.python.ipython.devel/10557
|
||||
# Fixes Issue 1370
|
||||
import unittest
|
||||
from unittest import TestProgram
|
||||
|
||||
class IPyTesProgram(TestProgram):
|
||||
def __init__(self, *args, **kwargs):
|
||||
test_runner = unittest.TextTestRunner(stream=sys.stderr)
|
||||
kwargs['testRunner'] = kwargs.pop('testRunner', test_runner)
|
||||
kwargs['exit'] = False
|
||||
TestProgram.__init__(self, *args, **kwargs)
|
||||
|
||||
unittest.main = IPyTesProgram
|
||||
|
||||
# Ignore some IPython/ipykernel warnings
|
||||
try:
|
||||
warnings.filterwarnings(action='ignore', category=DeprecationWarning,
|
||||
module='ipykernel.ipkernel')
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Turtle adjustments
|
||||
# =============================================================================
|
||||
# This is needed to prevent turtle scripts crashes after multiple runs in the
|
||||
# same IPython Console instance.
|
||||
# See Spyder issue #6278
|
||||
try:
|
||||
import turtle
|
||||
from turtle import Screen, Terminator
|
||||
|
||||
def spyder_bye():
|
||||
try:
|
||||
Screen().bye()
|
||||
turtle.TurtleScreen._RUNNING = True
|
||||
except Terminator:
|
||||
pass
|
||||
turtle.bye = spyder_bye
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Pandas adjustments
|
||||
# =============================================================================
|
||||
try:
|
||||
import pandas as pd
|
||||
|
||||
# Set Pandas output encoding
|
||||
pd.options.display.encoding = 'utf-8'
|
||||
|
||||
# Filter warning that appears for DataFrames with np.nan values
|
||||
# Example:
|
||||
# >>> import pandas as pd, numpy as np
|
||||
# >>> pd.Series([np.nan,np.nan,np.nan],index=[1,2,3])
|
||||
# Fixes Issue 2991
|
||||
# For 0.18-
|
||||
warnings.filterwarnings(action='ignore', category=RuntimeWarning,
|
||||
module='pandas.core.format',
|
||||
message=".*invalid value encountered in.*")
|
||||
# For 0.18.1+
|
||||
warnings.filterwarnings(action='ignore', category=RuntimeWarning,
|
||||
module='pandas.formats.format',
|
||||
message=".*invalid value encountered in.*")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Numpy adjustments
|
||||
# =============================================================================
|
||||
try:
|
||||
# Filter warning that appears when users have 'Show max/min'
|
||||
# turned on and Numpy arrays contain a nan value.
|
||||
# Fixes Issue 7063
|
||||
# Note: It only happens in Numpy 1.14+
|
||||
warnings.filterwarnings(action='ignore', category=RuntimeWarning,
|
||||
module='numpy.core._methods',
|
||||
message=".*invalid value encountered in.*")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Multiprocessing adjustments
|
||||
# =============================================================================
|
||||
# This patch is only needed on Python 3
|
||||
if not PY2:
|
||||
# This could fail with changes in Python itself, so we protect it
|
||||
# with a try/except
|
||||
try:
|
||||
import multiprocessing.spawn
|
||||
_old_preparation_data = multiprocessing.spawn.get_preparation_data
|
||||
|
||||
def _patched_preparation_data(name):
|
||||
"""
|
||||
Patched get_preparation_data to work when all variables are
|
||||
removed before execution.
|
||||
"""
|
||||
try:
|
||||
d = _old_preparation_data(name)
|
||||
except AttributeError:
|
||||
main_module = sys.modules['__main__']
|
||||
# Any string for __spec__ does the job
|
||||
main_module.__spec__ = ''
|
||||
d = _old_preparation_data(name)
|
||||
# On windows, there is no fork, so we need to save the main file
|
||||
# and import it
|
||||
if (os.name == 'nt' and 'init_main_from_path' in d
|
||||
and not os.path.exists(d['init_main_from_path'])):
|
||||
_print(
|
||||
"Warning: multiprocessing may need the main file to exist. "
|
||||
"Please save {}".format(d['init_main_from_path']))
|
||||
# Remove path as the subprocess can't do anything with it
|
||||
del d['init_main_from_path']
|
||||
return d
|
||||
multiprocessing.spawn.get_preparation_data = _patched_preparation_data
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# os adjustments
|
||||
# =============================================================================
|
||||
# This is necessary to have better support for Rich and Colorama.
|
||||
def _patched_get_terminal_size(fd=None):
|
||||
return os.terminal_size((80, 30))
|
||||
|
||||
os.get_terminal_size = _patched_get_terminal_size
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Pdb adjustments
|
||||
# =============================================================================
|
||||
pdb.Pdb = SpyderPdb
|
||||
|
||||
# =============================================================================
|
||||
# User module reloader
|
||||
# =============================================================================
|
||||
__umr__ = UserModuleReloader(namelist=os.environ.get("SPY_UMR_NAMELIST", None))
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Handle Post Mortem Debugging and Traceback Linkage to Spyder
|
||||
# =============================================================================
|
||||
def post_mortem_excepthook(type, value, tb):
|
||||
"""
|
||||
For post mortem exception handling, print a banner and enable post
|
||||
mortem debugging.
|
||||
"""
|
||||
ipython_shell = get_ipython()
|
||||
ipython_shell.showtraceback((type, value, tb))
|
||||
p = pdb.Pdb(ipython_shell.colors)
|
||||
|
||||
if not type == SyntaxError:
|
||||
# wait for stderr to print (stderr.flush does not work in this case)
|
||||
time.sleep(0.1)
|
||||
_print('*' * 40)
|
||||
_print('Entering post mortem debugging...')
|
||||
_print('*' * 40)
|
||||
|
||||
# Inform Spyder about position of exception: pdb.Pdb.interaction() calls
|
||||
# cmd.Cmd.cmdloop(), which calls SpyderPdb.preloop() where
|
||||
# send_initial_notification is handled.
|
||||
p.send_initial_notification = True
|
||||
|
||||
p.reset()
|
||||
frame = tb.tb_next.tb_frame
|
||||
# wait for stdout to print
|
||||
time.sleep(0.1)
|
||||
p.interaction(frame, tb)
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
# runfile and debugfile commands
|
||||
# ==============================================================================
|
||||
def get_current_file_name():
|
||||
"""Get the current file name."""
|
||||
try:
|
||||
return frontend_request(blocking=True).current_filename()
|
||||
except Exception:
|
||||
_print("This command failed to be executed because an error occurred"
|
||||
" while trying to get the current file name from Spyder's"
|
||||
" editor. The error was:\n\n")
|
||||
get_ipython().showtraceback(exception_only=True)
|
||||
return None
|
||||
|
||||
|
||||
def count_leading_empty_lines(cell):
|
||||
"""Count the number of leading empty cells."""
|
||||
if PY2:
|
||||
lines = cell.splitlines(True)
|
||||
else:
|
||||
lines = cell.splitlines(keepends=True)
|
||||
if not lines:
|
||||
return 0
|
||||
for i, line in enumerate(lines):
|
||||
if line and not line.isspace():
|
||||
return i
|
||||
return len(lines)
|
||||
|
||||
|
||||
def transform_cell(code, indent_only=False):
|
||||
"""Transform IPython code to Python code."""
|
||||
number_empty_lines = count_leading_empty_lines(code)
|
||||
if indent_only:
|
||||
# Not implemented for PY2
|
||||
if PY2:
|
||||
return code
|
||||
if not code.endswith('\n'):
|
||||
code += '\n' # Ensure the cell has a trailing newline
|
||||
lines = code.splitlines(keepends=True)
|
||||
lines = leading_indent(leading_empty_lines(lines))
|
||||
code = ''.join(lines)
|
||||
else:
|
||||
if PY2:
|
||||
tm = IPythonInputSplitter()
|
||||
return tm.transform_cell(code)
|
||||
else:
|
||||
tm = TransformerManager()
|
||||
code = tm.transform_cell(code)
|
||||
return '\n' * number_empty_lines + code
|
||||
|
||||
|
||||
def exec_code(code, filename, ns_globals, ns_locals=None, post_mortem=False,
|
||||
exec_fun=None, capture_last_expression=False):
|
||||
"""Execute code and display any exception."""
|
||||
# Tell IPython to hide this frame (>7.16)
|
||||
__tracebackhide__ = True
|
||||
global SHOW_INVALID_SYNTAX_MSG
|
||||
|
||||
if PY2:
|
||||
filename = encode(filename)
|
||||
code = encode(code)
|
||||
|
||||
if exec_fun is None:
|
||||
# Replace by exec when dropping Python 2
|
||||
exec_fun = compat_exec
|
||||
|
||||
ipython_shell = get_ipython()
|
||||
is_ipython = os.path.splitext(filename)[1] == '.ipy'
|
||||
try:
|
||||
if not is_ipython:
|
||||
# TODO: remove the try-except and let the SyntaxError raise
|
||||
# Because there should not be ipython code in a python file
|
||||
try:
|
||||
ast_code = ast.parse(transform_cell(code, indent_only=True))
|
||||
except SyntaxError as e:
|
||||
try:
|
||||
ast_code = ast.parse(transform_cell(code))
|
||||
except SyntaxError:
|
||||
if PY2:
|
||||
raise e
|
||||
else:
|
||||
# Need to call exec to avoid Syntax Error in Python 2.
|
||||
# TODO: remove exec when dropping Python 2 support.
|
||||
exec("raise e from None")
|
||||
else:
|
||||
if SHOW_INVALID_SYNTAX_MSG:
|
||||
_print(
|
||||
"\nWARNING: This is not valid Python code. "
|
||||
"If you want to use IPython magics, "
|
||||
"flexible indentation, and prompt removal, "
|
||||
"we recommend that you save this file with the "
|
||||
".ipy extension.\n")
|
||||
SHOW_INVALID_SYNTAX_MSG = False
|
||||
else:
|
||||
ast_code = ast.parse(transform_cell(code))
|
||||
|
||||
if code.rstrip()[-1:] == ";":
|
||||
# Supress output with ;
|
||||
capture_last_expression = False
|
||||
|
||||
if capture_last_expression:
|
||||
ast_code, capture_last_expression = capture_last_Expr(
|
||||
ast_code, "_spyder_out")
|
||||
|
||||
exec_fun(compile(ast_code, filename, 'exec'), ns_globals, ns_locals)
|
||||
|
||||
if capture_last_expression:
|
||||
out = ns_globals.pop("_spyder_out", None)
|
||||
if out is not None:
|
||||
return out
|
||||
|
||||
except SystemExit as status:
|
||||
# ignore exit(0)
|
||||
if status.code:
|
||||
ipython_shell.showtraceback(exception_only=True)
|
||||
except BaseException as error:
|
||||
if (isinstance(error, bdb.BdbQuit)
|
||||
and ipython_shell.pdb_session):
|
||||
# Ignore BdbQuit if we are debugging, as it is expected.
|
||||
ipython_shell.pdb_session = None
|
||||
elif post_mortem and isinstance(error, Exception):
|
||||
error_type, error, tb = sys.exc_info()
|
||||
post_mortem_excepthook(error_type, error, tb)
|
||||
else:
|
||||
# We ignore the call to exec
|
||||
ipython_shell.showtraceback(tb_offset=1)
|
||||
finally:
|
||||
__tracebackhide__ = "__pdb_exit__"
|
||||
|
||||
|
||||
def get_file_code(filename, save_all=True, raise_exception=False):
|
||||
"""Retrieve the content of a file."""
|
||||
# Get code from spyder
|
||||
try:
|
||||
return frontend_request(blocking=True).get_file_code(
|
||||
filename, save_all=save_all)
|
||||
except Exception:
|
||||
# Maybe this is a local file
|
||||
try:
|
||||
with open(filename, 'r') as f:
|
||||
return f.read()
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
if raise_exception:
|
||||
raise
|
||||
# Else return None
|
||||
return None
|
||||
|
||||
|
||||
def runfile(filename=None, args=None, wdir=None, namespace=None,
|
||||
post_mortem=False, current_namespace=False):
|
||||
"""
|
||||
Run filename
|
||||
args: command line arguments (string)
|
||||
wdir: working directory
|
||||
namespace: namespace for execution
|
||||
post_mortem: boolean, whether to enter post-mortem mode on error
|
||||
current_namespace: if true, run the file in the current namespace
|
||||
"""
|
||||
return _exec_file(
|
||||
filename, args, wdir, namespace,
|
||||
post_mortem, current_namespace, stack_depth=1)
|
||||
|
||||
|
||||
def _exec_file(filename=None, args=None, wdir=None, namespace=None,
|
||||
post_mortem=False, current_namespace=False, stack_depth=0,
|
||||
exec_fun=None, canonic_filename=None):
|
||||
# Tell IPython to hide this frame (>7.16)
|
||||
__tracebackhide__ = True
|
||||
ipython_shell = get_ipython()
|
||||
if filename is None:
|
||||
filename = get_current_file_name()
|
||||
if filename is None:
|
||||
return
|
||||
|
||||
try:
|
||||
filename = filename.decode('utf-8')
|
||||
except (UnicodeError, TypeError, AttributeError):
|
||||
# UnicodeError, TypeError --> eventually raised in Python 2
|
||||
# AttributeError --> systematically raised in Python 3
|
||||
pass
|
||||
if PY2:
|
||||
filename = encode(filename)
|
||||
if __umr__.enabled:
|
||||
__umr__.run()
|
||||
if args is not None and not isinstance(args, basestring):
|
||||
raise TypeError("expected a character buffer object")
|
||||
|
||||
try:
|
||||
file_code = get_file_code(filename, raise_exception=True)
|
||||
except Exception:
|
||||
# Show an error and return None
|
||||
_print(
|
||||
"This command failed to be executed because an error occurred"
|
||||
" while trying to get the file code from Spyder's"
|
||||
" editor. The error was:\n\n")
|
||||
get_ipython().showtraceback(exception_only=True)
|
||||
return
|
||||
|
||||
# Here the remote filename has been used. It must now be valid locally.
|
||||
if canonic_filename is not None:
|
||||
filename = canonic_filename
|
||||
else:
|
||||
filename = canonic(filename)
|
||||
|
||||
with NamespaceManager(filename, namespace, current_namespace,
|
||||
file_code=file_code, stack_depth=stack_depth + 1
|
||||
) as (ns_globals, ns_locals):
|
||||
sys.argv = [filename]
|
||||
if args is not None:
|
||||
for arg in shlex.split(args):
|
||||
sys.argv.append(arg)
|
||||
|
||||
if "multiprocessing" in sys.modules:
|
||||
# See https://github.com/spyder-ide/spyder/issues/16696
|
||||
try:
|
||||
sys.modules['__mp_main__'] = sys.modules['__main__']
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if wdir is not None:
|
||||
if PY2:
|
||||
try:
|
||||
wdir = wdir.decode('utf-8')
|
||||
except (UnicodeError, TypeError):
|
||||
# UnicodeError, TypeError --> eventually raised in Python 2
|
||||
pass
|
||||
if os.path.isdir(wdir):
|
||||
os.chdir(wdir)
|
||||
# See https://github.com/spyder-ide/spyder/issues/13632
|
||||
if "multiprocessing.process" in sys.modules:
|
||||
try:
|
||||
import multiprocessing.process
|
||||
multiprocessing.process.ORIGINAL_DIR = os.path.abspath(
|
||||
wdir)
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
_print("Working directory {} doesn't exist.\n".format(wdir))
|
||||
|
||||
try:
|
||||
if __umr__.has_cython:
|
||||
# Cython files
|
||||
with io.open(filename, encoding='utf-8') as f:
|
||||
ipython_shell.run_cell_magic('cython', '', f.read())
|
||||
else:
|
||||
exec_code(file_code, filename, ns_globals, ns_locals,
|
||||
post_mortem=post_mortem, exec_fun=exec_fun,
|
||||
capture_last_expression=False)
|
||||
finally:
|
||||
sys.argv = ['']
|
||||
|
||||
|
||||
# IPykernel 6.3.0+ shadows our runfile because it depends on the Pydev
|
||||
# debugger, which adds its own runfile to builtins. So we replace it with
|
||||
# our own using exec_lines in start.py
|
||||
if PY2:
|
||||
builtins.runfile = runfile
|
||||
else:
|
||||
builtins.spyder_runfile = runfile
|
||||
|
||||
|
||||
def debugfile(filename=None, args=None, wdir=None, post_mortem=False,
|
||||
current_namespace=False):
|
||||
"""
|
||||
Debug filename
|
||||
args: command line arguments (string)
|
||||
wdir: working directory
|
||||
post_mortem: boolean, included for compatiblity with runfile
|
||||
"""
|
||||
# Tell IPython to hide this frame (>7.16)
|
||||
__tracebackhide__ = True
|
||||
if filename is None:
|
||||
filename = get_current_file_name()
|
||||
if filename is None:
|
||||
return
|
||||
|
||||
shell = get_ipython()
|
||||
if shell.is_debugging():
|
||||
# Recursive
|
||||
code = (
|
||||
"runfile({}".format(repr(filename)) +
|
||||
", args=%r, wdir=%r, current_namespace=%r)" % (
|
||||
args, wdir, current_namespace)
|
||||
)
|
||||
|
||||
shell.pdb_session.enter_recursive_debugger(
|
||||
code, filename, True,
|
||||
)
|
||||
else:
|
||||
debugger = get_new_debugger(filename, True)
|
||||
_exec_file(
|
||||
filename=filename,
|
||||
canonic_filename=debugger.canonic(filename),
|
||||
args=args,
|
||||
wdir=wdir,
|
||||
current_namespace=current_namespace,
|
||||
exec_fun=debugger.run,
|
||||
stack_depth=1,
|
||||
)
|
||||
|
||||
|
||||
builtins.debugfile = debugfile
|
||||
|
||||
|
||||
def runcell(cellname, filename=None, post_mortem=False):
|
||||
"""
|
||||
Run a code cell from an editor as a file.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
cellname : str or int
|
||||
Cell name or index.
|
||||
filename : str
|
||||
Needed to allow for proper traceback links.
|
||||
post_mortem: bool
|
||||
Automatically enter post mortem on exception.
|
||||
"""
|
||||
# Tell IPython to hide this frame (>7.16)
|
||||
__tracebackhide__ = True
|
||||
return _exec_cell(cellname, filename, post_mortem, stack_depth=1)
|
||||
|
||||
|
||||
def _exec_cell(cellname, filename=None, post_mortem=False, stack_depth=0,
|
||||
exec_fun=None, canonic_filename=None):
|
||||
"""
|
||||
Execute a code cell with a given exec function.
|
||||
"""
|
||||
# Tell IPython to hide this frame (>7.16)
|
||||
__tracebackhide__ = True
|
||||
if filename is None:
|
||||
filename = get_current_file_name()
|
||||
if filename is None:
|
||||
return
|
||||
try:
|
||||
filename = filename.decode('utf-8')
|
||||
except (UnicodeError, TypeError, AttributeError):
|
||||
# UnicodeError, TypeError --> eventually raised in Python 2
|
||||
# AttributeError --> systematically raised in Python 3
|
||||
pass
|
||||
ipython_shell = get_ipython()
|
||||
try:
|
||||
# Get code from spyder
|
||||
cell_code = frontend_request(
|
||||
blocking=True).run_cell(cellname, filename)
|
||||
except Exception:
|
||||
_print("This command failed to be executed because an error occurred"
|
||||
" while trying to get the cell code from Spyder's"
|
||||
" editor. The error was:\n\n")
|
||||
get_ipython().showtraceback(exception_only=True)
|
||||
return
|
||||
|
||||
if not cell_code or cell_code.strip() == '':
|
||||
_print("Nothing to execute, this cell is empty.\n")
|
||||
return
|
||||
|
||||
# Trigger `post_execute` to exit the additional pre-execution.
|
||||
# See Spyder PR #7310.
|
||||
ipython_shell.events.trigger('post_execute')
|
||||
file_code = get_file_code(filename, save_all=False)
|
||||
|
||||
# Here the remote filename has been used. It must now be valid locally.
|
||||
if canonic_filename is not None:
|
||||
filename = canonic_filename
|
||||
else:
|
||||
# Normalise the filename
|
||||
filename = canonic(filename)
|
||||
|
||||
with NamespaceManager(filename, current_namespace=True,
|
||||
file_code=file_code, stack_depth=stack_depth + 1
|
||||
) as (ns_globals, ns_locals):
|
||||
return exec_code(cell_code, filename, ns_globals, ns_locals,
|
||||
post_mortem=post_mortem, exec_fun=exec_fun,
|
||||
capture_last_expression=True)
|
||||
|
||||
|
||||
builtins.runcell = runcell
|
||||
|
||||
|
||||
def debugcell(cellname, filename=None, post_mortem=False):
|
||||
"""Debug a cell."""
|
||||
# Tell IPython to hide this frame (>7.16)
|
||||
__tracebackhide__ = True
|
||||
if filename is None:
|
||||
filename = get_current_file_name()
|
||||
if filename is None:
|
||||
return
|
||||
|
||||
shell = get_ipython()
|
||||
if shell.is_debugging():
|
||||
# Recursive
|
||||
code = (
|
||||
"runcell({}, ".format(repr(cellname)) +
|
||||
"{})".format(repr(filename))
|
||||
)
|
||||
shell.pdb_session.enter_recursive_debugger(
|
||||
code, filename, False,
|
||||
)
|
||||
else:
|
||||
debugger = get_new_debugger(filename, False)
|
||||
_exec_cell(
|
||||
cellname=cellname,
|
||||
filename=filename,
|
||||
canonic_filename=debugger.canonic(filename),
|
||||
exec_fun=debugger.run,
|
||||
stack_depth=1
|
||||
)
|
||||
|
||||
|
||||
builtins.debugcell = debugcell
|
||||
|
||||
|
||||
def cell_count(filename=None):
|
||||
"""
|
||||
Get the number of cells in a file.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
filename : str
|
||||
The file to get the cells from. If None, the currently opened file.
|
||||
"""
|
||||
if filename is None:
|
||||
filename = get_current_file_name()
|
||||
if filename is None:
|
||||
raise RuntimeError('Could not get cell count from frontend.')
|
||||
try:
|
||||
# Get code from spyder
|
||||
cell_count = frontend_request(blocking=True).cell_count(filename)
|
||||
return cell_count
|
||||
except Exception:
|
||||
etype, error, tb = sys.exc_info()
|
||||
raise etype(error)
|
||||
|
||||
|
||||
builtins.cell_count = cell_count
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# PYTHONPATH and sys.path Adjustments
|
||||
# =============================================================================
|
||||
# PYTHONPATH is not passed to kernel directly, see spyder-ide/spyder#13519
|
||||
# This allows the kernel to start without crashing if modules in PYTHONPATH
|
||||
# shadow standard library modules.
|
||||
def set_spyder_pythonpath():
|
||||
pypath = os.environ.get('SPY_PYTHONPATH')
|
||||
if pypath:
|
||||
sys.path.extend(pypath.split(os.pathsep))
|
||||
os.environ.update({'PYTHONPATH': pypath})
|
||||
|
||||
set_spyder_pythonpath()
|
||||
Reference in New Issue
Block a user