From 92218e7c458207faab7617e1872212aa514047e2 Mon Sep 17 00:00:00 2001 From: Fabio Zadrozny Date: Thu, 24 Jan 2019 16:27:59 -0200 Subject: [PATCH] Fix issue initializing matplotlib and error when thread is not found. #1041 (#1121) --- .../pydevd/.travis_install_python_deps.sh | 2 +- .../pydevd/_pydevd_bundle/pydevd_comm.py | 4 +-- .../pydevd/_pydevd_bundle/pydevd_utils.py | 7 ++++++ src/ptvsd/_vendored/pydevd/pydevd.py | 24 ++++++++++-------- .../resources/_debugger_case_matplotlib.py | 23 +++++++++++++++++ .../pydevd/tests_python/test_debugger.py | 16 ++++++++++++ .../pydevd/tests_python/test_utilities.py | 25 +++++++++++++++++++ 7 files changed, 88 insertions(+), 13 deletions(-) create mode 100644 src/ptvsd/_vendored/pydevd/tests_python/resources/_debugger_case_matplotlib.py create mode 100644 src/ptvsd/_vendored/pydevd/tests_python/test_utilities.py diff --git a/src/ptvsd/_vendored/pydevd/.travis_install_python_deps.sh b/src/ptvsd/_vendored/pydevd/.travis_install_python_deps.sh index 39aa7939e..8538f1152 100644 --- a/src/ptvsd/_vendored/pydevd/.travis_install_python_deps.sh +++ b/src/ptvsd/_vendored/pydevd/.travis_install_python_deps.sh @@ -33,7 +33,7 @@ if [ "$PYDEVD_PYTHON_VERSION" = "3.6" ]; then fi if [ "$PYDEVD_PYTHON_VERSION" = "3.7" ]; then - conda install --yes pyqt=5 + conda install --yes pyqt=5 matplotlib # Note: track the latest django pip install "django" fi diff --git a/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_comm.py b/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_comm.py index 51df635ff..64f8cb135 100644 --- a/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_comm.py +++ b/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_comm.py @@ -1155,8 +1155,8 @@ def pydevd_find_thread_by_id(thread_id): return i # This can happen when a request comes for a thread which was previously removed. - pydevd_log(1, "Could not find thread %s\n" % thread_id) - pydevd_log(1, "Available: %s\n" % [get_thread_id(t) for t in threads] % thread_id) + pydevd_log(1, "Could not find thread %s\n" % (thread_id,)) + pydevd_log(1, "Available: %s\n" % ([get_thread_id(t) for t in threads],)) except: traceback.print_exc() diff --git a/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_utils.py b/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_utils.py index e3d419fb7..44f15c87d 100644 --- a/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_utils.py +++ b/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_utils.py @@ -43,6 +43,13 @@ def save_main_module(file, module_name): return m +def is_current_thread_main_thread(): + if hasattr(threading, 'main_thread'): + return threading.current_thread() is threading.main_thread() + else: + return isinstance(threading.current_thread(), threading._MainThread) + + def to_number(x): if is_string(x): try: diff --git a/src/ptvsd/_vendored/pydevd/pydevd.py b/src/ptvsd/_vendored/pydevd/pydevd.py index cd1032971..2f2a5e2e8 100644 --- a/src/ptvsd/_vendored/pydevd/pydevd.py +++ b/src/ptvsd/_vendored/pydevd/pydevd.py @@ -45,7 +45,7 @@ from _pydevd_bundle.pydevd_net_command_factory_xml import NetCommandFactory from _pydevd_bundle.pydevd_trace_dispatch import ( trace_dispatch as _trace_dispatch, global_cache_skips, global_cache_frame_skips, fix_top_level_trace_and_get_trace_func) -from _pydevd_bundle.pydevd_utils import save_main_module +from _pydevd_bundle.pydevd_utils import save_main_module, is_current_thread_main_thread from _pydevd_frame_eval.pydevd_frame_eval_main import ( frame_eval_func, dummy_trace_dispatch) import pydev_ipython # @UnusedImport @@ -734,8 +734,9 @@ def check_output_redirect(self): def init_matplotlib_in_debug_console(self): # import hook and patches for matplotlib support in debug console from _pydev_bundle.pydev_import_hook import import_hook_manager - for module in dict_keys(self.mpl_modules_for_patching): - import_hook_manager.add_module_name(module, self.mpl_modules_for_patching.pop(module)) + if is_current_thread_main_thread(): + for module in dict_keys(self.mpl_modules_for_patching): + import_hook_manager.add_module_name(module, self.mpl_modules_for_patching.pop(module)) def init_matplotlib_support(self): # prepare debugger for integration with matplotlib GUI event loop @@ -763,11 +764,12 @@ def return_control(): def _activate_mpl_if_needed(self): if len(self.mpl_modules_for_patching) > 0: - for module in dict_keys(self.mpl_modules_for_patching): - if module in sys.modules: - activate_function = self.mpl_modules_for_patching.pop(module) - activate_function() - self.mpl_in_use = True + if is_current_thread_main_thread(): + for module in dict_keys(self.mpl_modules_for_patching): + if module in sys.modules: + activate_function = self.mpl_modules_for_patching.pop(module) + activate_function() + self.mpl_in_use = True def _call_mpl_hook(self): try: @@ -1158,11 +1160,13 @@ def _do_wait_suspend(self, thread, frame, event, arg, suspend_type, from_this_th info = thread.additional_info if info.pydev_state == STATE_SUSPEND and not self._finish_debugging_session: + in_main_thread = is_current_thread_main_thread() # before every stop check if matplotlib modules were imported inside script code - self._activate_mpl_if_needed() + if in_main_thread: + self._activate_mpl_if_needed() while info.pydev_state == STATE_SUSPEND and not self._finish_debugging_session: - if self.mpl_in_use: + if in_main_thread and self.mpl_in_use: # call input hooks if only matplotlib is in use self._call_mpl_hook() diff --git a/src/ptvsd/_vendored/pydevd/tests_python/resources/_debugger_case_matplotlib.py b/src/ptvsd/_vendored/pydevd/tests_python/resources/_debugger_case_matplotlib.py new file mode 100644 index 000000000..b9733472e --- /dev/null +++ b/src/ptvsd/_vendored/pydevd/tests_python/resources/_debugger_case_matplotlib.py @@ -0,0 +1,23 @@ +from concurrent.futures import ThreadPoolExecutor +import matplotlib +import matplotlib.pyplot as plt + +processed = [] + + +def double(nmbr): + doubled = nmbr * 2 # break here + processed.append(1) + return doubled + + +with ThreadPoolExecutor(max_workers=2) as pool: + futures = [] + + for number in range(3): + future = pool.submit(double, number) + futures.append(future) + +pool.shutdown() +assert len(processed) == 3 +print('TEST SUCEEDED!') diff --git a/src/ptvsd/_vendored/pydevd/tests_python/test_debugger.py b/src/ptvsd/_vendored/pydevd/tests_python/test_debugger.py index c70698471..834a50705 100644 --- a/src/ptvsd/_vendored/pydevd/tests_python/test_debugger.py +++ b/src/ptvsd/_vendored/pydevd/tests_python/test_debugger.py @@ -2828,6 +2828,22 @@ def test_step_over_my_code(case_setup): writer.write_step_over_my_code(hit.thread_id) writer.finished_ok = True + +def test_matplotlib_activation(case_setup): + try: + import matplotlib + except ImportError: + return + + with case_setup.test_file('_debugger_case_matplotlib.py') as writer: + writer.write_add_breakpoint(writer.get_line_index_with_content('break here')) + writer.write_make_initial_run() + for _ in range(3): + hit = writer.wait_for_breakpoint_hit() + writer.write_run_thread(hit.thread_id) + + writer.finished_ok = True + # Jython needs some vars to be set locally. # set JAVA_HOME=c:\bin\jdk1.8.0_172 # set PATH=%PATH%;C:\bin\jython2.7.0\bin diff --git a/src/ptvsd/_vendored/pydevd/tests_python/test_utilities.py b/src/ptvsd/_vendored/pydevd/tests_python/test_utilities.py new file mode 100644 index 000000000..cf6384795 --- /dev/null +++ b/src/ptvsd/_vendored/pydevd/tests_python/test_utilities.py @@ -0,0 +1,25 @@ +import threading +from _pydevd_bundle.pydevd_comm import pydevd_find_thread_by_id + + +def test_is_main_thread(): + from _pydevd_bundle.pydevd_utils import is_current_thread_main_thread + assert is_current_thread_main_thread() + + class NonMainThread(threading.Thread): + + def run(self): + self.is_main_thread = is_current_thread_main_thread() + + non_main_thread = NonMainThread() + non_main_thread.start() + non_main_thread.join() + assert not non_main_thread.is_main_thread + + +def test_find_thread(): + from _pydevd_bundle.pydevd_constants import get_current_thread_id + assert pydevd_find_thread_by_id('123') is None + + assert pydevd_find_thread_by_id( + get_current_thread_id(threading.current_thread())) is threading.current_thread()