Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-spyder-kernels for openSUSE:Factory checked in at 2022-07-26 19:45:02 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-spyder-kernels (Old) and /work/SRC/openSUSE:Factory/.python-spyder-kernels.new.1533 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-spyder-kernels" Tue Jul 26 19:45:02 2022 rev:35 rq:991040 version:2.3.2 Changes: -------- --- /work/SRC/openSUSE:Factory/python-spyder-kernels/python-spyder-kernels.changes 2022-05-31 15:47:55.548009871 +0200 +++ /work/SRC/openSUSE:Factory/.python-spyder-kernels.new.1533/python-spyder-kernels.changes 2022-07-26 19:45:04.549765431 +0200 @@ -1,0 +2,21 @@ +Mon Jul 25 12:31:50 UTC 2022 - Ben Greiner <c...@bnavigator.de> + +- Update to version 2.3.2 + * Fixed issue 394 - The variable explorer is broken while + debugging + * PR 399: Increase minimal required version of jupyter_client to + 7.3.4, by @ccordoba12 + * PR 398: Fix module namespace, by @impact27 + * PR 395: Fix running namespace and improve eventloop integration + while debugging, by @impact27 (394) + * PR 389: Fix debug filename path for remote debugging, by + @impact27 (18330) + * PR 388: Fix getting args from functions or methods, by + @ccordoba12 + * PR 386: Fix flaky test, by @impact27 + * PR 381: Eliminate unnecessary updates of the Variable Explorer + while debugging, by @rear1019 + * PR 378: Append paths that come from Spyder's Python path + manager to the end of sys.path, by @mrclary + +------------------------------------------------------------------- Old: ---- python-spyder-kernels-2.3.1.tar.gz New: ---- python-spyder-kernels-2.3.2.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-spyder-kernels.spec ++++++ --- /var/tmp/diff_new_pack.MzBuxs/_old 2022-07-26 19:45:05.017693125 +0200 +++ /var/tmp/diff_new_pack.MzBuxs/_new 2022-07-26 19:45:05.021692507 +0200 @@ -21,7 +21,7 @@ # flaky for obs, only test locally %bcond_with dasktest Name: python-spyder-kernels -Version: 2.3.1 +Version: 2.3.2 Release: 0 Summary: Jupyter kernels for Spyder's console License: MIT @@ -29,7 +29,6 @@ URL: https://github.com/spyder-ide/spyder-kernels # PyPI tarballs do not include the tests: https://github.com/spyder-ide/spyder-kernels/issues/66 Source: %{url}/archive/v%{version}.tar.gz#/%{name}-%{version}.tar.gz -BuildRequires: %{python_module base >= 3.7} BuildRequires: %{python_module setuptools} BuildRequires: fdupes BuildRequires: python-rpm-macros @@ -40,7 +39,7 @@ BuildRequires: %{python_module flaky} BuildRequires: %{python_module ipykernel >= 6.9.2 with %python-ipykernel < 7} BuildRequires: %{python_module ipython >= 7.31.1} -BuildRequires: %{python_module jupyter_client >= 7.3.1 with %python-jupyter_client < 8} +BuildRequires: %{python_module jupyter_client >= 7.3.4 with %python-jupyter_client < 8} BuildRequires: %{python_module matplotlib} BuildRequires: %{python_module numpy} BuildRequires: %{python_module pandas} @@ -58,7 +57,7 @@ Requires: python-pyzmq >= 22.1 Requires: python-wurlitzer >= 1.0.3 Requires: (python-ipykernel >= 6.9.2 with python-ipykernel < 7) -Requires: (python-jupyter_client >= 7.3.1 with python-jupyter_client < 8) +Requires: (python-jupyter_client >= 7.3.4 with python-jupyter_client < 8) BuildArch: noarch %python_subpackages ++++++ python-spyder-kernels-2.3.1.tar.gz -> python-spyder-kernels-2.3.2.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spyder-kernels-2.3.1/CHANGELOG.md new/spyder-kernels-2.3.2/CHANGELOG.md --- old/spyder-kernels-2.3.1/CHANGELOG.md 2022-05-21 18:36:00.000000000 +0200 +++ new/spyder-kernels-2.3.2/CHANGELOG.md 2022-07-06 18:22:03.000000000 +0200 @@ -1,5 +1,30 @@ # History of changes +## Version 2.3.2 (2022-07-06) + +### Issues Closed + +* [Issue 394](https://github.com/spyder-ide/spyder-kernels/issues/394) - The variable explorer is broken while debugging ([PR 395](https://github.com/spyder-ide/spyder-kernels/pull/395) by [@impact27](https://github.com/impact27)) + +In this release 1 issue was closed. + +### Pull Requests Merged + +* [PR 399](https://github.com/spyder-ide/spyder-kernels/pull/399) - PR: Increase minimal required version of jupyter_client to 7.3.4, by [@ccordoba12](https://github.com/ccordoba12) +* [PR 398](https://github.com/spyder-ide/spyder-kernels/pull/398) - PR: Fix module namespace, by [@impact27](https://github.com/impact27) +* [PR 395](https://github.com/spyder-ide/spyder-kernels/pull/395) - PR: Fix running namespace and improve eventloop integration while debugging, by [@impact27](https://github.com/impact27) ([394](https://github.com/spyder-ide/spyder-kernels/issues/394)) +* [PR 389](https://github.com/spyder-ide/spyder-kernels/pull/389) - PR: Fix debug filename path for remote debugging, by [@impact27](https://github.com/impact27) ([18330](https://github.com/spyder-ide/spyder/issues/18330)) +* [PR 388](https://github.com/spyder-ide/spyder-kernels/pull/388) - PR: Fix getting args from functions or methods, by [@ccordoba12](https://github.com/ccordoba12) +* [PR 386](https://github.com/spyder-ide/spyder-kernels/pull/386) - PR: Fix flaky test, by [@impact27](https://github.com/impact27) +* [PR 381](https://github.com/spyder-ide/spyder-kernels/pull/381) - PR: Eliminate unnecessary updates of the Variable Explorer while debugging, by [@rear1019](https://github.com/rear1019) +* [PR 378](https://github.com/spyder-ide/spyder-kernels/pull/378) - PR: Append paths that come from Spyder's Python path manager to the end of `sys.path`, by [@mrclary](https://github.com/mrclary) + +In this release 8 pull requests were closed. + + +---- + + ## Version 2.3.1 (2022-05-21) ### Pull Requests Merged diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spyder-kernels-2.3.1/requirements/posix.txt new/spyder-kernels-2.3.2/requirements/posix.txt --- old/spyder-kernels-2.3.1/requirements/posix.txt 2022-05-21 18:36:00.000000000 +0200 +++ new/spyder-kernels-2.3.2/requirements/posix.txt 2022-07-06 18:22:03.000000000 +0200 @@ -1,6 +1,6 @@ cloudpickle ipykernel>=6.9.2,<7 ipython>=7.31.1,<8 -jupyter_client>=7.3.1,<8 +jupyter_client>=7.3.4,<8 pyzmq>=22.1.0 wurlitzer>=1.0.3 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spyder-kernels-2.3.1/requirements/windows.txt new/spyder-kernels-2.3.2/requirements/windows.txt --- old/spyder-kernels-2.3.1/requirements/windows.txt 2022-05-21 18:36:00.000000000 +0200 +++ new/spyder-kernels-2.3.2/requirements/windows.txt 2022-07-06 18:22:03.000000000 +0200 @@ -1,5 +1,5 @@ cloudpickle ipykernel>=6.9.2,<7 ipython>=7.31.1,<8 -jupyter_client>=7.3.1,<8 +jupyter_client>=7.3.4,<8 pyzmq>=22.1.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spyder-kernels-2.3.1/setup.py new/spyder-kernels-2.3.2/setup.py --- old/spyder-kernels-2.3.1/setup.py 2022-05-21 18:36:00.000000000 +0200 +++ new/spyder-kernels-2.3.2/setup.py 2022-07-06 18:22:03.000000000 +0200 @@ -44,7 +44,7 @@ 'ipython<6; python_version<"3"', 'ipython>=7.31.1,<8; python_version>="3"', 'jupyter-client>=5.3.4,<6; python_version<"3"', - 'jupyter-client>=7.3.1,<8; python_version>="3"', + 'jupyter-client>=7.3.4,<8; python_version>="3"', 'pyzmq>=17,<20; python_version<"3"', 'pyzmq>=22.1.0; python_version>="3"', 'wurlitzer>=1.0.3;platform_system!="Windows"', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spyder-kernels-2.3.1/spyder_kernels/_version.py new/spyder-kernels-2.3.2/spyder_kernels/_version.py --- old/spyder-kernels-2.3.1/spyder_kernels/_version.py 2022-05-21 18:36:00.000000000 +0200 +++ new/spyder-kernels-2.3.2/spyder_kernels/_version.py 2022-07-06 18:22:03.000000000 +0200 @@ -8,5 +8,5 @@ """Version File.""" -VERSION_INFO = (2, 3, 1) +VERSION_INFO = (2, 3, 2) __version__ = '.'.join(map(str, VERSION_INFO)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spyder-kernels-2.3.1/spyder_kernels/console/kernel.py new/spyder-kernels-2.3.2/spyder_kernels/console/kernel.py --- old/spyder-kernels-2.3.1/spyder_kernels/console/kernel.py 2022-05-21 18:36:00.000000000 +0200 +++ new/spyder-kernels-2.3.2/spyder_kernels/console/kernel.py 2022-07-06 18:22:03.000000000 +0200 @@ -24,8 +24,8 @@ # Local imports from spyder_kernels.py3compat import ( - TEXT_TYPES, to_text_string, PY3, input, TimeoutError) -from spyder_kernels.comms.frontendcomm import FrontendComm, CommError + TEXT_TYPES, to_text_string, PY3) +from spyder_kernels.comms.frontendcomm import FrontendComm from spyder_kernels.utils.iofuncs import iofunctions from spyder_kernels.utils.mpl import ( MPL_BACKENDS_FROM_SPYDER, MPL_BACKENDS_TO_SPYDER, INLINE_FIGURE_FORMATS) @@ -96,7 +96,6 @@ self.namespace_view_settings = {} self._mpl_backend_error = None self._running_namespace = None - self._pdb_input_line = None self.faulthandler_handle = None # -- Public API ----------------------------------------------------------- @@ -306,21 +305,6 @@ return self.shell.pdb_session.do_complete(code, cursor_pos) return self._do_complete(code, cursor_pos) - def publish_pdb_state(self, step): - """ - Publish Variable Explorer state and Pdb step through - send_spyder_msg. - """ - state = dict( - namespace_view=self.get_namespace_view(), - var_properties=self.get_var_properties(), - step=step - ) - try: - self.frontend_call(blocking=False).pdb_state(state) - except (CommError, TimeoutError): - logger.debug("Could not send Pdb state to the frontend.") - def set_spyder_breakpoints(self, breakpoints): """ Handle a message from the frontend @@ -352,10 +336,10 @@ def pdb_input_reply(self, line, echo_stack_entry=True): """Get a pdb command from the frontend.""" - if self.shell.pdb_session: - self.shell.pdb_session._disable_next_stack_entry = ( - not echo_stack_entry) - self._pdb_input_line = line + debugger = self.shell.pdb_session + if debugger: + debugger._disable_next_stack_entry = not echo_stack_entry + debugger._cmd_input_line = line if self.eventloop: # Interrupting the eventloop is only implemented when a message is # received on the shell channel, but this message is queued and @@ -365,47 +349,6 @@ # stop the eventloop. This will call back `_interrupt_eventloop`. self.frontend_call().request_interrupt_eventloop() - def cmd_input(self, prompt=''): - """ - Special input function for commands. - Runs the eventloop while debugging. - """ - # Only works if the comm is open and this is a pdb prompt. - if not self.frontend_comm.is_open() or not self.shell.is_debugging(): - return input(prompt) - - # Flush output before making the request. - sys.stderr.flush() - sys.stdout.flush() - - # Send the input request. - self._pdb_input_line = None - self.frontend_call().pdb_input(prompt) - - # Allow GUI event loop to update - if PY3: - is_main_thread = ( - threading.current_thread() is threading.main_thread()) - else: - is_main_thread = isinstance( - threading.current_thread(), threading._MainThread) - - # Get input by running eventloop - if is_main_thread and self.eventloop: - while self._pdb_input_line is None: - eventloop = self.eventloop - if eventloop: - eventloop(self) - else: - break - - # Get input by blocking - if self._pdb_input_line is None: - self.frontend_comm.wait_until( - lambda: self._pdb_input_line is not None) - - return self._pdb_input_line - def _interrupt_eventloop(self): """Interrupts the eventloop.""" # Receiving the request is enough to stop the eventloop. @@ -634,11 +577,12 @@ sys.path.remove(path) # Add new paths - # We do this in reverse order as we use `sys.path.insert(1, path)`. - # This ensures the end result has the correct path order. - for path, active in reversed(new_path_dict.items()): - if active: - sys.path.insert(1, path) + pypath = [path for path, active in new_path_dict.items() if active] + if pypath: + sys.path.extend(pypath) + os.environ.update({'PYTHONPATH': os.pathsep.join(pypath)}) + else: + os.environ.pop('PYTHONPATH', None) # -- Private API --------------------------------------------------- # --- For the Variable Explorer @@ -650,17 +594,21 @@ both locals() and globals() for current frame when debugging """ ns = {} - if self._running_namespace is None: + if self.shell.is_debugging() and self.shell.pdb_session.prompt_waiting: + # Stopped at a pdb prompt ns.update(self.shell.user_ns) + ns.update(self.shell._pdb_locals) else: - # This is true when a file is executing. - running_globals, running_locals = self._running_namespace - ns.update(running_globals) - if running_locals is not None: - ns.update(running_locals) + # Give access to the running namespace if there is one + if self._running_namespace is None: + ns.update(self.shell.user_ns) + else: + # This is true when a file is executing. + running_globals, running_locals = self._running_namespace + ns.update(running_globals) + if running_locals is not None: + ns.update(running_locals) - # Add debugging locals - ns.update(self.shell._pdb_locals) # Add magics to ns so we can show help about them on the Help # plugin if with_magics: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spyder-kernels-2.3.1/spyder_kernels/console/tests/test_console_kernel.py new/spyder-kernels-2.3.2/spyder_kernels/console/tests/test_console_kernel.py --- old/spyder-kernels-2.3.1/spyder_kernels/console/tests/test_console_kernel.py 2022-05-21 18:36:00.000000000 +0200 +++ new/spyder-kernels-2.3.2/spyder_kernels/console/tests/test_console_kernel.py 2022-07-06 18:22:03.000000000 +0200 @@ -831,6 +831,7 @@ # test pdb pdb_obj = SpyderPdb() pdb_obj.curframe = inspect.currentframe() + pdb_obj.prompt_waiting = True pdb_obj.completenames = lambda *ignore: ['baba'] kernel.shell.pdb_session = pdb_obj match = kernel.do_complete('ba', 2) @@ -889,6 +890,7 @@ """ pdb_obj = SpyderPdb() pdb_obj.curframe = inspect.currentframe() + pdb_obj.prompt_waiting = True pdb_obj.curframe_locals = pdb_obj.curframe.f_locals kernel.shell.pdb_session = pdb_obj @@ -915,6 +917,7 @@ """ pdb_obj = SpyderPdb() pdb_obj.curframe = inspect.currentframe() + pdb_obj.prompt_waiting = True pdb_obj.curframe_locals = pdb_obj.curframe.f_locals kernel.shell.pdb_session = pdb_obj @@ -941,6 +944,7 @@ kernel.shell.user_ns["test"] = 0 pdb_obj = SpyderPdb() pdb_obj.curframe = inspect.currentframe() + pdb_obj.prompt_waiting = True pdb_obj.curframe_locals = pdb_obj.curframe.f_locals kernel.shell.pdb_session = pdb_obj @@ -1022,6 +1026,7 @@ baba = 1 pdb_obj = SpyderPdb() pdb_obj.curframe = inspect.currentframe() + pdb_obj.prompt_waiting = True pdb_obj.curframe_locals = pdb_obj.curframe.f_locals kernel.shell.pdb_session = pdb_obj @@ -1059,6 +1064,7 @@ a = 1 pdb_obj = SpyderPdb() pdb_obj.curframe = inspect.currentframe() + pdb_obj.prompt_waiting = True pdb_obj.curframe_locals = pdb_obj.curframe.f_locals kernel.shell.pdb_session = pdb_obj @@ -1116,6 +1122,8 @@ if backend is not None: client.execute("%matplotlib {}".format(backend)) client.get_shell_msg(timeout=TIMEOUT) + client.execute("import time; time.sleep(.1)") + client.get_shell_msg(timeout=TIMEOUT) # Get backend code = "backend = get_ipython().kernel.get_mpl_interactive_backend()" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spyder-kernels-2.3.1/spyder_kernels/customize/namespace_manager.py new/spyder-kernels-2.3.2/spyder_kernels/customize/namespace_manager.py --- old/spyder-kernels-2.3.1/spyder_kernels/customize/namespace_manager.py 2022-05-21 18:36:00.000000000 +0200 +++ new/spyder-kernels-2.3.2/spyder_kernels/customize/namespace_manager.py 2022-07-06 18:22:03.000000000 +0200 @@ -6,6 +6,7 @@ import linecache import os.path +import types import sys from IPython.core.getipython import get_ipython @@ -13,6 +14,25 @@ from spyder_kernels.py3compat import PY2 +def new_main_mod(filename, modname): + """ + Reimplemented from IPython/core/interactiveshell.py to avoid caching + and clearing recursive namespace. + """ + filename = os.path.abspath(filename) + + main_mod = types.ModuleType( + modname, + doc="Module created for script run in IPython") + + main_mod.__file__ = filename + # It seems pydoc (and perhaps others) needs any module instance to + # implement a __nonzero__ method + main_mod.__nonzero__ = lambda : True + + return main_mod + + class NamespaceManager(object): """ Get a namespace and set __file__ to filename for this namespace. @@ -29,6 +49,7 @@ self.current_namespace = current_namespace self._previous_filename = None self._previous_main = None + self._previous_running_namespace = None self._reset_main = False self._file_code = file_code ipython_shell = get_ipython() @@ -49,9 +70,7 @@ self._previous_filename = self.ns_globals['__file__'] self.ns_globals['__file__'] = self.filename else: - - main_mod = ipython_shell.new_main_mod( - self.filename, '__main__') + main_mod = new_main_mod(self.filename, '__main__') self.ns_globals = main_mod.__dict__ self.ns_locals = None # Needed to allow pickle to reference main @@ -61,6 +80,8 @@ self._reset_main = True # Save current namespace for access by variable explorer + self._previous_running_namespace = ( + ipython_shell.kernel._running_namespace) ipython_shell.kernel._running_namespace = ( self.ns_globals, self.ns_locals) @@ -86,7 +107,8 @@ Reset the namespace. """ ipython_shell = get_ipython() - ipython_shell.kernel._running_namespace = None + ipython_shell.kernel._running_namespace = ( + self._previous_running_namespace) if self._previous_filename: self.ns_globals['__file__'] = self._previous_filename elif '__file__' in self.ns_globals: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spyder-kernels-2.3.1/spyder_kernels/customize/spydercustomize.py new/spyder-kernels-2.3.2/spyder_kernels/customize/spydercustomize.py --- old/spyder-kernels-2.3.1/spyder_kernels/customize/spydercustomize.py 2022-05-21 18:36:00.000000000 +0200 +++ new/spyder-kernels-2.3.2/spyder_kernels/customize/spydercustomize.py 2022-07-06 18:22:03.000000000 +0200 @@ -13,7 +13,6 @@ import ast import bdb -import cmd import io import logging import os @@ -23,17 +22,15 @@ import time import warnings -from IPython import __version__ as ipy_version from IPython.core.getipython import get_ipython -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.py3compat import ( - TimeoutError, PY2, _print, encode, compat_exec, FileNotFoundError) -from spyder_kernels.customize.utils import ( - capture_last_Expr, normalise_filename) + PY2, _print, encode, compat_exec, FileNotFoundError) +from spyder_kernels.customize.utils import capture_last_Expr, canonic if not PY2: from IPython.core.inputtransformer2 import ( @@ -330,18 +327,8 @@ # ============================================================================= # Pdb adjustments # ============================================================================= -def cmd_input(prompt=''): - return get_ipython().kernel.cmd_input(prompt) - - pdb.Pdb = SpyderPdb -if PY2: - cmd.raw_input = cmd_input -else: - cmd.input = cmd_input - - # ============================================================================= # User module reloader # ============================================================================= @@ -505,18 +492,23 @@ __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, @@ -536,7 +528,7 @@ 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() @@ -544,11 +536,6 @@ 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('/', '\\') try: filename = filename.decode('utf-8') @@ -562,22 +549,23 @@ __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) + 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 - 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 @@ -655,7 +643,7 @@ 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) ) @@ -666,11 +654,13 @@ 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, ) @@ -696,7 +686,7 @@ 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. """ @@ -706,11 +696,6 @@ 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('/', '\\') try: filename = filename.decode('utf-8') except (UnicodeError, TypeError, AttributeError): @@ -736,14 +721,14 @@ # 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 @@ -770,7 +755,7 @@ # Recursive code = ( "runcell({}, ".format(repr(cellname)) + - "{})".format(repr(normalise_filename(filename))) + "{})".format(repr(filename)) ) shell.pdb_session.enter_recursive_debugger( code, filename, False, @@ -779,7 +764,8 @@ 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 ) @@ -814,12 +800,15 @@ # ============================================================================= -# Extend sys.path with paths that come from Spyder +# 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: - pathlist = pypath.split(os.pathsep) - sys.path.extend(pathlist) + sys.path.extend(pypath.split(os.pathsep)) + os.environ.update({'PYTHONPATH': pypath}) set_spyder_pythonpath() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spyder-kernels-2.3.1/spyder_kernels/customize/spyderpdb.py new/spyder-kernels-2.3.2/spyder_kernels/customize/spyderpdb.py --- old/spyder-kernels-2.3.1/spyder_kernels/customize/spyderpdb.py 2022-05-21 18:36:00.000000000 +0200 +++ new/spyder-kernels-2.3.2/spyder_kernels/customize/spyderpdb.py 2022-07-06 18:22:03.000000000 +0200 @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- # # Copyright (c) 2009- Spyder Kernels Contributors # @@ -9,9 +10,9 @@ import ast import bdb import logging -import os import sys import traceback +import threading from collections import namedtuple from IPython.core.autocall import ZMQExitAutocall @@ -20,7 +21,8 @@ from spyder_kernels.comms.frontendcomm import CommError, frontend_request from spyder_kernels.customize.utils import path_is_library, capture_last_Expr -from spyder_kernels.py3compat import TimeoutError, PY2, _print, isidentifier +from spyder_kernels.py3compat import ( + TimeoutError, PY2, _print, isidentifier, PY3, input) if not PY2: from IPython.core.inputtransformer2 import TransformerManager @@ -92,10 +94,23 @@ self._pdb_breaking = False self._frontend_notified = False + # content of tuple: (filename, line number) + self._previous_step = None + # Don't report hidden frames for IPython 7.24+. This attribute # has no effect in previous versions. self.report_skipped = False + + # Keep track of remote filename + self.remote_filename = None + + # State of the prompt + self.prompt_waiting = False + + # Line received from the frontend + self._cmd_input_line = None + # --- Methods overriden for code execution def print_exclamation_warning(self): """Print pdb warning for exclamation mark.""" @@ -107,10 +122,6 @@ def default(self, line): """ Default way of running pdb statment. - - The only difference with Pdb.default is that if line contains multiple - statments, the code will be compiled with 'exec'. It will not print the - result but will run without failing. """ execute_events = self.pdb_execute_events if line[:1] == '!': @@ -542,6 +553,12 @@ return result # --- Methods overriden by us for Spyder integration + def postloop(self): + # postloop() is called when the debugger???s input prompt exists. Reset + # _previous_step so that publish_pdb_state() actually notifies Spyder + # about a changed frame the next the input prompt is entered again. + self._previous_step = None + def preloop(self): """Ask Spyder for breakpoints before the first prompt is created.""" try: @@ -554,7 +571,7 @@ if self.starting: self.set_spyder_breakpoints(pdb_settings['breakpoints']) if self.send_initial_notification: - self.notify_spyder() + self.publish_pdb_state() except (CommError, TimeoutError): logger.debug("Could not get breakpoints from the frontend.") super(SpyderPdb, self).preloop() @@ -625,6 +642,75 @@ "For copying text while debugging, use Ctrl+Shift+C", file=self.stdout) + + def cmdloop(self, intro=None): + """ + Repeatedly issue a prompt, accept input, parse an initial prefix + off the received input, and dispatch to action methods, passing them + the remainder of the line as argument. + """ + self.preloop() + if intro is not None: + self.intro = intro + if self.intro: + self.stdout.write(str(self.intro)+"\n") + stop = None + while not stop: + if self.cmdqueue: + line = self.cmdqueue.pop(0) + else: + try: + self.prompt_waiting = True + line = self.cmd_input(self.prompt) + except EOFError: + line = 'EOF' + self.prompt_waiting = False + line = self.precmd(line) + stop = self.onecmd(line) + stop = self.postcmd(stop, line) + self.postloop() + + def cmd_input(self, prompt=''): + """ + Get input from frontend. Blocks until return + """ + kernel = get_ipython().kernel + # Only works if the comm is open + if not kernel.frontend_comm.is_open(): + return input(prompt) + + # Flush output before making the request. + sys.stderr.flush() + sys.stdout.flush() + + # Send the input request. + self._cmd_input_line = None + kernel.frontend_call().pdb_input(prompt) + + # Allow GUI event loop to update + if PY3: + is_main_thread = ( + threading.current_thread() is threading.main_thread()) + else: + is_main_thread = isinstance( + threading.current_thread(), threading._MainThread) + + # Get input by running eventloop + if is_main_thread and kernel.eventloop: + while self._cmd_input_line is None: + eventloop = kernel.eventloop + if eventloop: + eventloop(kernel) + else: + break + + # Get input by blocking + if self._cmd_input_line is None: + kernel.frontend_comm.wait_until( + lambda: self._cmd_input_line is not None) + + return self._cmd_input_line + def precmd(self, line): """ Hook method executed just before the command line is @@ -644,9 +730,11 @@ def postcmd(self, stop, line): """ - Notify spyder on any pdb command. + Notify spyder about (possibly) changed frame + + Note: The PDB commands ???up???, ???down??? and ???jump??? change the current frame. """ - self.notify_spyder() + self.publish_pdb_state() return super(SpyderPdb, self).postcmd(stop, line) if PY2: @@ -743,11 +831,16 @@ logger.debug( "Could not send a Pdb continue call to the frontend.") - def notify_spyder(self): - """Send kernel state to the frontend.""" + def publish_pdb_state(self): + """ + Send debugger state (frame position) to the frontend. + + The state is only sent if it has changed since the last update. + """ frame = self.curframe if frame is None: + self._previous_step = None return # Get filename and line number of the current frame @@ -757,14 +850,24 @@ fname = unicode(fname, "utf-8") except TypeError: pass + 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): + return + # Set step of the current frame (if any) step = {} + self._previous_step = None if isinstance(fname, basestring) and isinstance(lineno, int): step = dict(fname=fname, lineno=lineno) + self._previous_step = (fname, lineno) - get_ipython().kernel.publish_pdb_state(step) + try: + frontend_request(blocking=False).pdb_state(dict(step=step)) + except (CommError, TimeoutError): + logger.debug("Could not send Pdb state to the frontend.") def run(self, cmd, globals=None, locals=None): """Debug a statement executed via the exec() function. @@ -808,11 +911,8 @@ 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)) @@ -821,14 +921,22 @@ self.lastcmd = debugger.lastcmd get_ipython().pdb_session = self + # Reset _previous_step so that publish_pdb_state() called from within + # postcmd() notifies Spyder about a changed debugger position. The reset + # is required because the recursive debugger might change the position, + # 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spyder-kernels-2.3.1/spyder_kernels/customize/utils.py new/spyder-kernels-2.3.2/spyder_kernels/customize/utils.py --- old/spyder-kernels-2.3.1/spyder_kernels/customize/utils.py 2022-05-21 18:36:00.000000000 +0200 +++ new/spyder-kernels-2.3.2/spyder_kernels/customize/utils.py 2022-07-06 18:22:03.000000000 +0200 @@ -117,9 +117,15 @@ 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 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spyder-kernels-2.3.1/spyder_kernels/utils/dochelpers.py new/spyder-kernels-2.3.2/spyder_kernels/utils/dochelpers.py --- old/spyder-kernels-2.3.1/spyder_kernels/utils/dochelpers.py 2022-05-21 18:36:00.000000000 +0200 +++ new/spyder-kernels-2.3.2/spyder_kernels/utils/dochelpers.py 2022-07-06 18:22:03.000000000 +0200 @@ -238,7 +238,8 @@ func_obj = getattr(obj, '__init__') else: return [] - if not hasattr(func_obj, 'func_code'): + + if not hasattr(func_obj, '__code__'): # Builtin: try to extract info from doc args = getargsfromdoc(func_obj) if args is not None: @@ -246,24 +247,29 @@ else: # Example: PyQt5 return getargsfromdoc(obj) - args, _, _ = inspect.getargs(func_obj.func_code) + + args, _, _ = inspect.getargs(func_obj.__code__) if not args: return getargsfromdoc(obj) - + # Supporting tuple arguments in def statement: for i_arg, arg in enumerate(args): if isinstance(arg, list): args[i_arg] = "(%s)" % ", ".join(arg) - + defaults = get_func_defaults(func_obj) if defaults is not None: for index, default in enumerate(defaults): - args[index+len(args)-len(defaults)] += '='+repr(default) + args[index + len(args) - len(defaults)] += '=' + repr(default) + if inspect.isclass(obj) or inspect.ismethod(obj): if len(args) == 1: return None - if 'self' in args: - args.remove('self') + + # Remove 'self' from args + if 'self' in args: + args.remove('self') + return args diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spyder-kernels-2.3.1/spyder_kernels/utils/tests/test_dochelpers.py new/spyder-kernels-2.3.2/spyder_kernels/utils/tests/test_dochelpers.py --- old/spyder-kernels-2.3.1/spyder_kernels/utils/tests/test_dochelpers.py 2022-05-21 18:36:00.000000000 +0200 +++ new/spyder-kernels-2.3.2/spyder_kernels/utils/tests/test_dochelpers.py 2022-07-06 18:22:03.000000000 +0200 @@ -9,9 +9,9 @@ """ Tests for dochelpers.py """ + # Standard library imports import os -import sys # Test library imports import pytest @@ -27,57 +27,29 @@ pass -@pytest.mark.skipif(not PY2, reason="It fails in Python 3") +@pytest.mark.skipif(PY2 or os.name == 'nt', + reason="Only works on Linux and Mac") def test_dochelpers(): """Test dochelpers.""" + assert getargtxt(Test.method) == ['x, ', 'y=2'] assert not getargtxt(Test.__init__) - if PY2: - assert getargtxt(Test.method) == ['x, ', 'y=2'] - assert getdoc(sorted) == {'note': 'Function of __builtin__ module', - 'argspec': u'(iterable, cmp=None, key=None, ' - 'reverse=False)', - 'docstring': u'sorted(iterable, cmp=None, ' - 'key=None, reverse=False) --> ' - 'new sorted list', - 'name': 'sorted'} - assert getargtxt(sorted) == ['iterable, ', ' cmp=None, ', - ' key=None, ', ' reverse=False'] - else: - assert not getargtxt(Test.method) - if os.name == 'nt': - assert getdoc(sorted) == {'note': 'Function of builtins module', - 'argspec': '(...)', - 'docstring': 'Return a new list ' - 'containing ' - 'all items from the ' - 'iterable in ascending ' - 'order.\n\nA custom ' - 'key function can be ' - 'supplied to customise the ' - 'sort order, and ' - 'the\nreverse flag can be ' - 'set to request the result ' - 'in descending order.', - 'name': 'sorted'} - else: - assert getdoc(sorted) == {'note': 'Function of builtins module', - 'argspec': '(...)', - 'docstring': 'Return a new list ' - 'containing ' - 'all items from the ' - 'iterable in ascending ' - 'order.\n\nA custom ' - 'key function can be ' - 'supplied to customize the ' - 'sort order, and ' - 'the\nreverse flag can be ' - 'set to request the result ' - 'in descending order.', - 'name': 'sorted'} - assert not getargtxt(sorted) + + assert getdoc(sorted) == { + 'note': 'Function of builtins module', + 'argspec': '(...)', + 'docstring': 'Return a new list containing all items from the ' + 'iterable in ascending order.\n\nA custom key function ' + 'can be supplied to customize the sort order, and the\n' + 'reverse flag can be set to request the result in ' + 'descending order.', + 'name': 'sorted' + } + assert not getargtxt(sorted) + assert isdefined('numpy.take', force_import=True) assert isdefined('__import__') - assert not isdefined('.keys', force_import=True) + assert not isdefined('zzz', force_import=True) + assert getobj('globals') == 'globals' assert not getobj('globals().keys') assert getobj('+scipy.signal.') == 'scipy.signal'