diff --git a/spyder_kernels/customize/spydercustomize.py b/spyder_kernels/customize/spydercustomize.py index 1595be9d..b1399523 100644 --- a/spyder_kernels/customize/spydercustomize.py +++ b/spyder_kernels/customize/spydercustomize.py @@ -24,17 +24,15 @@ import time import warnings -from IPython import __version__ as ipy_version from IPython.core.getipython import get_ipython from IPython.core.inputtransformer2 import ( TransformerManager, leading_indent, leading_empty_lines) -from spyder_kernels.comms.frontendcomm import CommError, frontend_request +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.customize.utils import ( - capture_last_Expr, normalise_filename) +from spyder_kernels.customize.utils import capture_last_Expr, canonic logger = logging.getLogger(__name__) @@ -446,18 +444,23 @@ def exec_code(code, filename, ns_globals, ns_locals=None, post_mortem=False, __tracebackhide__ = "__pdb_exit__" -def get_file_code(filename, save_all=True): - """Retrive the content of a file.""" +def get_file_code(filename, save_all=True, raise_exception=False): + """Retrieve the content of a file.""" # Get code from spyder try: - file_code = frontend_request(blocking=True).get_file_code( + return frontend_request(blocking=True).get_file_code( filename, save_all=save_all) - except (CommError, TimeoutError, RuntimeError, FileNotFoundError): - file_code = None - if file_code is None: - with open(filename, 'r') as f: - return f.read() - return file_code + 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, @@ -477,7 +480,7 @@ def runfile(filename=None, args=None, wdir=None, namespace=None, def _exec_file(filename=None, args=None, wdir=None, namespace=None, post_mortem=False, current_namespace=False, stack_depth=0, - exec_fun=None): + exec_fun=None, canonic_filename=None): # Tell IPython to hide this frame (>7.16) __tracebackhide__ = True ipython_shell = get_ipython() @@ -485,18 +488,14 @@ def _exec_file(filename=None, args=None, wdir=None, namespace=None, filename = get_current_file_name() if filename is None: return - else: - # get_debugger replaces \\ by / so we must undo that here - # Otherwise code caching doesn't work - if os.name == 'nt': - filename = filename.replace('/', '\\') if __umr__.enabled: __umr__.run() if args is not None and not isinstance(args, str): raise TypeError("expected a character buffer object") + try: - file_code = get_file_code(filename) + file_code = get_file_code(filename, raise_exception=True) except Exception: print( "This command failed to be executed because an error occurred" @@ -504,13 +503,12 @@ def _exec_file(filename=None, args=None, wdir=None, namespace=None, " editor. The error was:\n\n") get_ipython().showtraceback(exception_only=True) return - if file_code is None: - print("Could not get code from editor.\n") - return - # Normalise the filename - filename = os.path.abspath(filename) - filename = os.path.normcase(filename) + # 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 @@ -580,7 +578,7 @@ def debugfile(filename=None, args=None, wdir=None, post_mortem=False, if shell.is_debugging(): # Recursive code = ( - "runfile({}".format(repr(normalise_filename(filename))) + + "runfile({}".format(repr(filename)) + ", args=%r, wdir=%r, current_namespace=%r)" % ( args, wdir, current_namespace) ) @@ -591,11 +589,13 @@ def debugfile(filename=None, args=None, wdir=None, post_mortem=False, else: debugger = get_new_debugger(filename, True) _exec_file( - filename=debugger.canonic(filename), - args=args, wdir=wdir, + filename=filename, + canonic_filename=debugger.canonic(filename), + args=args, + wdir=wdir, current_namespace=current_namespace, exec_fun=debugger.run, - stack_depth=1 + stack_depth=1, ) @@ -621,7 +621,7 @@ def runcell(cellname, filename=None, post_mortem=False): def _exec_cell(cellname, filename=None, post_mortem=False, stack_depth=0, - exec_fun=None): + exec_fun=None, canonic_filename=None): """ Execute a code cell with a given exec function. """ @@ -631,11 +631,6 @@ def _exec_cell(cellname, filename=None, post_mortem=False, stack_depth=0, filename = get_current_file_name() if filename is None: return - else: - # get_debugger replaces \\ by / so we must undo that here - # Otherwise code caching doesn't work - if os.name == 'nt': - filename = filename.replace('/', '\\') ipython_shell = get_ipython() try: # Get code from spyder @@ -655,14 +650,14 @@ def _exec_cell(cellname, filename=None, post_mortem=False, stack_depth=0, # Trigger `post_execute` to exit the additional pre-execution. # See Spyder PR #7310. ipython_shell.events.trigger('post_execute') - try: - file_code = get_file_code(filename, save_all=False) - except Exception: - file_code = None + file_code = get_file_code(filename, save_all=False) - # Normalise the filename - filename = os.path.abspath(filename) - filename = os.path.normcase(filename) + # 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 @@ -689,7 +684,7 @@ def debugcell(cellname, filename=None, post_mortem=False): # Recursive code = ( "runcell({}, ".format(repr(cellname)) + - "{})".format(repr(normalise_filename(filename))) + "{})".format(repr(filename)) ) shell.pdb_session.enter_recursive_debugger( code, filename, False, @@ -698,7 +693,8 @@ def debugcell(cellname, filename=None, post_mortem=False): debugger = get_new_debugger(filename, False) _exec_cell( cellname=cellname, - filename=debugger.canonic(filename), + filename=filename, + canonic_filename=debugger.canonic(filename), exec_fun=debugger.run, stack_depth=1 ) diff --git a/spyder_kernels/customize/spyderpdb.py b/spyder_kernels/customize/spyderpdb.py index 22a483f1..051a7580 100755 --- a/spyder_kernels/customize/spyderpdb.py +++ b/spyder_kernels/customize/spyderpdb.py @@ -11,7 +11,6 @@ import bdb import builtins import logging -import os import sys import traceback from collections import namedtuple @@ -93,6 +92,9 @@ def __init__(self, completekey='tab', stdin=None, stdout=None, # has no effect in previous versions. self.report_skipped = False + # Keep track of remote filename + self.remote_filename = None + # --- Methods overriden for code execution def print_exclamation_warning(self): """Print pdb warning for exclamation mark.""" @@ -713,6 +715,8 @@ def publish_pdb_state(self): # Get filename and line number of the current frame fname = self.canonic(frame.f_code.co_filename) + if fname == self.mainpyfile and self.remote_filename is not None: + fname = self.remote_filename lineno = frame.f_lineno if self._previous_step == (fname, lineno): @@ -792,11 +796,8 @@ def enter_recursive_debugger(self, code, filename, debugger.use_rawinput = self.use_rawinput debugger.prompt = "(%s) " % self.prompt.strip() - filename = debugger.canonic(filename) - debugger._wait_for_mainpyfile = True - debugger.mainpyfile = filename + debugger.set_remote_filename(filename) debugger.continue_if_has_breakpoints = continue_if_has_breakpoints - debugger._user_requested_quit = False # Enter recursive debugger sys.call_tracing(debugger.run, (code, globals, locals)) @@ -811,14 +812,16 @@ def enter_recursive_debugger(self, code, filename, # but the parent debugger (self) is not aware of this. self._previous_step = None + def set_remote_filename(self, filename): + """Set remote filename to signal Spyder on mainpyfile.""" + self.remote_filename = filename + self.mainpyfile = self.canonic(filename) + self._wait_for_mainpyfile = True + def get_new_debugger(filename, continue_if_has_breakpoints): """Get a new debugger.""" debugger = SpyderPdb() - - filename = debugger.canonic(filename) - debugger._wait_for_mainpyfile = True - debugger.mainpyfile = filename + debugger.set_remote_filename(filename) debugger.continue_if_has_breakpoints = continue_if_has_breakpoints - debugger._user_requested_quit = False return debugger diff --git a/spyder_kernels/customize/utils.py b/spyder_kernels/customize/utils.py index a9e64cbd..e87b302e 100644 --- a/spyder_kernels/customize/utils.py +++ b/spyder_kernels/customize/utils.py @@ -117,9 +117,15 @@ def capture_last_Expr(code_ast, out_varname): return code_ast, capture_last_expression -def normalise_filename(filename): - """Normalise path for window.""" - # Recursive - if os.name == 'nt': - return filename.replace('\\', '/') - return filename +def canonic(filename): + """ + Return canonical form of filename. + + This is a copy of bdb.canonic, so that the debugger will process + filenames in the same way + """ + if filename == "<" + filename[1:-1] + ">": + return filename + canonic = os.path.abspath(filename) + canonic = os.path.normcase(canonic) + return canonic