blob: 06a3f47b65ad6cf98aa7582680bde40cfcbfc061 [file] [log] [blame]
from __future__ import nested_scopes
def set_trace_in_qt():
import pydevd_tracing
from pydevd_comm import GetGlobalDebugger
debugger = GetGlobalDebugger()
if debugger is not None:
pydevd_tracing.SetTrace(debugger.trace_dispatch)
_patched_qt = False
def patch_qt():
'''
This method patches qt (PySide, PyQt4, PyQt5) so that we have hooks to set the tracing for QThread.
'''
# Avoid patching more than once
global _patched_qt
if _patched_qt:
return
_patched_qt = True
# Ok, we have an issue here:
# PyDev-452: Selecting PyQT API version using sip.setapi fails in debug mode
# http://pyqt.sourceforge.net/Docs/PyQt4/incompatible_apis.html
# Mostly, if the user uses a different API version (i.e.: v2 instead of v1),
# that has to be done before importing PyQt4/5 modules (PySide doesn't have this issue
# as it only implements v2).
patch_qt_on_import = None
try:
import PySide
except:
try:
import PyQt4
patch_qt_on_import = 'PyQt4'
except:
try:
import PyQt5
patch_qt_on_import = 'PyQt5'
except:
return
if patch_qt_on_import:
_patch_import_to_patch_pyqt_on_import(patch_qt_on_import)
else:
_internal_patch_qt()
def _patch_import_to_patch_pyqt_on_import(patch_qt_on_import):
# I don't like this approach very much as we have to patch __import__, but I like even less
# asking the user to configure something in the client side...
# So, our approach is to patch PyQt4/5 right before the user tries to import it (at which
# point he should've set the sip api version properly already anyways).
dotted = patch_qt_on_import + '.'
original_import = __import__
def patched_import(name, *args, **kwargs):
if patch_qt_on_import == name or name.startswith(dotted):
builtins.__import__ = original_import
_internal_patch_qt() # Patch it only when the user would import the qt module
return original_import(name, *args, **kwargs)
try:
import builtins
except ImportError:
import __builtin__ as builtins
builtins.__import__ = patched_import
def _internal_patch_qt():
try:
from PySide import QtCore
except:
try:
from PyQt4 import QtCore
except:
try:
from PyQt5 import QtCore
except:
return
_original_thread_init = QtCore.QThread.__init__
_original_runnable_init = QtCore.QRunnable.__init__
_original_QThread = QtCore.QThread
class FuncWrapper:
def __init__(self, original):
self._original = original
def __call__(self, *args, **kwargs):
set_trace_in_qt()
return self._original(*args, **kwargs)
class StartedSignalWrapper: # Wrapper for the QThread.started signal
def __init__(self, thread, original_started):
self.thread = thread
self.original_started = original_started
def connect(self, func, *args, **kwargs):
return self.original_started.connect(FuncWrapper(func), *args, **kwargs)
def disconnect(self, *args, **kwargs):
return self.original_started.disconnect(*args, **kwargs)
def emit(self, *args, **kwargs):
return self.original_started.emit(*args, **kwargs)
class ThreadWrapper(QtCore.QThread): # Wrapper for QThread
def __init__(self, *args, **kwargs):
_original_thread_init(self)
# In PyQt5 the program hangs when we try to call original run method of QThread class.
# So we need to distinguish instances of QThread class and instances of QThread inheritors.
if self.__class__.run == _original_QThread.run:
self.run = self._exec_run
else:
self._original_run = self.run
self.run = self._new_run
self._original_started = self.started
self.started = StartedSignalWrapper(self, self.started)
def _exec_run(self):
set_trace_in_qt()
return self.exec_()
def _new_run(self):
set_trace_in_qt()
return self._original_run()
class RunnableWrapper(QtCore.QRunnable): # Wrapper for QRunnable
def __init__(self, *args, **kwargs):
_original_runnable_init(self)
self._original_run = self.run
self.run = self._new_run
def _new_run(self):
set_trace_in_qt()
return self._original_run()
QtCore.QThread = ThreadWrapper
QtCore.QRunnable = RunnableWrapper