From 5fbaba36be4b2910e954183a1acad934f0c8a89f Mon Sep 17 00:00:00 2001 From: Elizaveta Shashkova Date: Mon, 20 Apr 2015 15:21:39 +0300 Subject: [PATCH] Support matplotlib interactive mode if it was imported inside script. Also support it for debugger without using consoles. (PY-15520) --- python/helpers/pydev/pydevd.py | 66 +++++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 21 deletions(-) diff --git a/python/helpers/pydev/pydevd.py b/python/helpers/pydev/pydevd.py index a10de2716297..a7130d7cfc9e 100644 --- a/python/helpers/pydev/pydevd.py +++ b/python/helpers/pydev/pydevd.py @@ -378,7 +378,10 @@ class PyDB: self.has_plugin_line_breaks = False self.has_plugin_exception_breaks = False - self.use_hooks_in_debug_console = False + # matplotlib support in debugger and debug console + self.mpl_in_use = False + self.mpl_hooks_in_debug_console = False + self.mpl_modules_for_patching = {} def get_plugin_lazy_init(self): if self.plugin is None and SUPPORT_PLUGINS: @@ -516,29 +519,33 @@ class PyDB: def init_matplotlib_in_debug_console(self): - # import hook and patches for matplotlib support - class _DebugConsoleHelper: + # import hook and patches for matplotlib support in debug console + from pydev_import_hook import import_hook_manager + for module in DictKeys(self.mpl_modules_for_patching): + import_hook_manager.add_module_name(module, DictPop(self.mpl_modules_for_patching, module)) + + def init_matplotlib_support(self): + # prepare debugger for integration with matplotlib GUI event loop + from pydev_ipython.matplotlibtools import activate_matplotlib, activate_pylab, activate_pyplot, do_enable_gui + # enable_gui_function in activate_matplotlib should be called in main thread. Unlike integrated console, + # in the debug console we have no interpreter instance with exec_queue, but we run this code in the main + # thread and can call it directly. + class _MatplotlibHelper: _return_control_osc = False def return_control(): # Some of the input hooks (e.g. Qt4Agg) check return control without doing # a single operation, so we don't return True on every # call when the debug hook is in place to allow the GUI to run - _DebugConsoleHelper._return_control_osc = not _DebugConsoleHelper._return_control_osc - return _DebugConsoleHelper._return_control_osc + _MatplotlibHelper._return_control_osc = not _MatplotlibHelper._return_control_osc + return _MatplotlibHelper._return_control_osc from pydev_ipython.inputhook import set_return_control_callback set_return_control_callback(return_control) - from pydev_import_hook import import_hook_manager - from pydev_ipython.matplotlibtools import activate_matplotlib, activate_pylab, activate_pyplot, do_enable_gui - import_hook_manager.add_module_name("matplotlib", lambda: activate_matplotlib(do_enable_gui)) - # enable_gui_function in activate_matplotlib should be called in main thread. Unlike integrated console, - # in the debug console we have no interpreter instance with exec_queue, but we run this code in the main - # thread and can call it directly. - import_hook_manager.add_module_name("pylab", activate_pylab) - import_hook_manager.add_module_name("pyplot", activate_pyplot) - self.use_hooks_in_debug_console = True + self.mpl_modules_for_patching = {"matplotlib": lambda: activate_matplotlib(do_enable_gui), + "matplotlib.pyplot": activate_pyplot, + "pylab": activate_pylab } def processInternalCommands(self): @@ -582,13 +589,15 @@ class PyDB: while True: int_cmd = queue.get(False) - if not self.use_hooks_in_debug_console and isinstance(int_cmd, InternalConsoleExec): - # patch matplotlib if only debug console was started + if not self.mpl_hooks_in_debug_console and isinstance(int_cmd, InternalConsoleExec): + # add import hooks for matplotlib patches if only debug console was started try: self.init_matplotlib_in_debug_console() + self.mpl_hooks_in_debug_console = True + self.mpl_in_use = True except: - sys.stderr.write("Matplotlib support in debug console failed\n") - pydev_log.error("Error in matplotlib init %s\n" % sys.exc_info()[0]) + pydev_log.error("Matplotlib support in debug console failed\n") + traceback.print_exc() if int_cmd.canBeExecutedBy(curr_thread_id): PydevdLog(2, "processing internal command ", str(int_cmd)) @@ -1429,9 +1438,19 @@ class PyDB: imported = False info = thread.additionalInfo + + if info.pydev_state == STATE_SUSPEND and not self._finishDebuggingSession: + # before every stop check if matplotlib modules were imported inside script code + if len(self.mpl_modules_for_patching) > 0: + for module in DictKeys(self.mpl_modules_for_patching): + if module in sys.modules: + activate_function = DictPop(self.mpl_modules_for_patching, module) + activate_function() + self.mpl_in_use = True + while info.pydev_state == STATE_SUSPEND and not self._finishDebuggingSession: - if self.use_hooks_in_debug_console: - # import and call input hooks if only debug console was started + if self.mpl_in_use: + # call input hooks if only matplotlib is in use try: if not imported: from pydev_ipython.inputhook import get_inputhook @@ -1440,7 +1459,7 @@ class PyDB: if inputhook: inputhook() except: - pydev_log.error("Error while calling matplotlib input hooks in debug console %s\n" % sys.exc_info()[0]) + pass self.processInternalCommands() time.sleep(0.01) @@ -1752,6 +1771,11 @@ class PyDB: while not self.readyToRun: time.sleep(0.1) # busy wait until we receive run command + try: + self.init_matplotlib_support() + except: + sys.stderr.write("Matplotlib support in debugger failed\n") + traceback.print_exc() pydev_imports.execfile(file, globals, locals) # execute the script