1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
|
def get_main_thread_instance(threading):
if hasattr(threading, 'main_thread'):
return threading.main_thread()
else:
# On Python 2 we don't really have an API to get the main thread,
# so, we just get it from the 'shutdown' bound method.
return threading._shutdown.im_self
def get_main_thread_id(unlikely_thread_id=None):
'''
:param unlikely_thread_id:
Pass to mark some thread id as not likely the main thread.
:return tuple(thread_id, critical_warning)
'''
import sys
import os
current_frames = sys._current_frames()
possible_thread_ids = []
for thread_ident, frame in current_frames.items():
while frame.f_back is not None:
frame = frame.f_back
basename = os.path.basename(frame.f_code.co_filename)
if basename.endswith(('.pyc', '.pyo')):
basename = basename[:-1]
if (frame.f_code.co_name, basename) in [
('_run_module_as_main', 'runpy.py'),
('_run_module_as_main', '<frozen runpy>'),
('run_module_as_main', 'runpy.py'),
('run_module', 'runpy.py'),
('run_path', 'runpy.py'),
]:
# This is the case for python -m <module name> (this is an ideal match, so,
# let's return it).
return thread_ident, ''
if frame.f_code.co_name == '<module>':
if frame.f_globals.get('__name__') == '__main__':
possible_thread_ids.insert(0, thread_ident) # Add with higher priority
continue
# Usually the main thread will be started in the <module>, whereas others would
# be started in another place (but when Python is embedded, this may not be
# correct, so, just add to the available possibilities as we'll have to choose
# one if there are multiple).
possible_thread_ids.append(thread_ident)
if len(possible_thread_ids) > 0:
if len(possible_thread_ids) == 1:
return possible_thread_ids[0], '' # Ideal: only one match
while unlikely_thread_id in possible_thread_ids:
possible_thread_ids.remove(unlikely_thread_id)
if len(possible_thread_ids) == 1:
return possible_thread_ids[0], '' # Ideal: only one match
elif len(possible_thread_ids) > 1:
# Bad: we can't really be certain of anything at this point.
return possible_thread_ids[0], \
'Multiple thread ids found (%s). Choosing main thread id randomly (%s).' % (
possible_thread_ids, possible_thread_ids[0])
# If we got here we couldn't discover the main thread id.
return None, 'Unable to discover main thread id.'
def fix_main_thread_id(on_warn=lambda msg:None, on_exception=lambda msg:None, on_critical=lambda msg:None):
# This means that we weren't able to import threading in the main thread (which most
# likely means that the main thread is paused or in some very long operation).
# In this case we'll import threading here and hotfix what may be wrong in the threading
# module (if we're on Windows where we create a thread to do the attach and on Linux
# we are not certain on which thread we're executing this code).
#
# The code below is a workaround for https://bugs.python.org/issue37416
import sys
import threading
try:
with threading._active_limbo_lock:
main_thread_instance = get_main_thread_instance(threading)
if sys.platform == 'win32':
# On windows this code would be called in a secondary thread, so,
# the current thread is unlikely to be the main thread.
if hasattr(threading, '_get_ident'):
unlikely_thread_id = threading._get_ident() # py2
else:
unlikely_thread_id = threading.get_ident() # py3
else:
unlikely_thread_id = None
main_thread_id, critical_warning = get_main_thread_id(unlikely_thread_id)
if main_thread_id is not None:
main_thread_id_attr = '_ident'
if not hasattr(main_thread_instance, main_thread_id_attr):
main_thread_id_attr = '_Thread__ident'
assert hasattr(main_thread_instance, main_thread_id_attr)
if main_thread_id != getattr(main_thread_instance, main_thread_id_attr):
# Note that we also have to reset the '_tstack_lock' for a regular lock.
# This is needed to avoid an error on shutdown because this lock is bound
# to the thread state and will be released when the secondary thread
# that initialized the lock is finished -- making an assert fail during
# process shutdown.
main_thread_instance._tstate_lock = threading._allocate_lock()
main_thread_instance._tstate_lock.acquire()
# Actually patch the thread ident as well as the threading._active dict
# (we should have the _active_limbo_lock to do that).
threading._active.pop(getattr(main_thread_instance, main_thread_id_attr), None)
setattr(main_thread_instance, main_thread_id_attr, main_thread_id)
threading._active[getattr(main_thread_instance, main_thread_id_attr)] = main_thread_instance
# Note: only import from pydevd after the patching is done (we want to do the minimum
# possible when doing that patching).
on_warn('The threading module was not imported by user code in the main thread. The debugger will attempt to work around https://bugs.python.org/issue37416.')
if critical_warning:
on_critical('Issue found when debugger was trying to work around https://bugs.python.org/issue37416:\n%s' % (critical_warning,))
except:
on_exception('Error patching main thread id.')
def attach(port, host, protocol='', debug_mode=''):
try:
import sys
fix_main_thread = 'threading' not in sys.modules
if fix_main_thread:
def on_warn(msg):
from _pydev_bundle import pydev_log
pydev_log.warn(msg)
def on_exception(msg):
from _pydev_bundle import pydev_log
pydev_log.exception(msg)
def on_critical(msg):
from _pydev_bundle import pydev_log
pydev_log.critical(msg)
fix_main_thread_id(on_warn=on_warn, on_exception=on_exception, on_critical=on_critical)
else:
from _pydev_bundle import pydev_log # @Reimport
pydev_log.debug('The threading module is already imported by user code.')
if protocol:
from _pydevd_bundle import pydevd_defaults
pydevd_defaults.PydevdCustomization.DEFAULT_PROTOCOL = protocol
if debug_mode:
from _pydevd_bundle import pydevd_defaults
pydevd_defaults.PydevdCustomization.DEBUG_MODE = debug_mode
import pydevd
# I.e.: disconnect/reset if already connected.
pydevd.SetupHolder.setup = None
py_db = pydevd.get_global_debugger()
if py_db is not None:
py_db.dispose_and_kill_all_pydevd_threads(wait=False)
# pydevd.DebugInfoHolder.DEBUG_TRACE_LEVEL = 3
pydevd.settrace(
port=port,
host=host,
stdoutToServer=True,
stderrToServer=True,
overwrite_prev_trace=True,
suspend=False,
trace_only_current_thread=False,
patch_multiprocessing=False,
)
except:
import traceback
traceback.print_exc()
|