(thread_module)
| 1173 | |
| 1174 | |
| 1175 | def patch_thread_module(thread_module): |
| 1176 | # Note: this is needed not just for the tracing, but to have an early way to |
| 1177 | # notify that a thread was created (i.e.: tests_python.test_debugger_json.test_case_started_exited_threads_protocol) |
| 1178 | start_thread_attrs = ["_start_new_thread", "start_new_thread", "start_new"] |
| 1179 | start_joinable_attrs = ["start_joinable_thread", "_start_joinable_thread"] |
| 1180 | check = start_thread_attrs + start_joinable_attrs |
| 1181 | |
| 1182 | replace_attrs = [] |
| 1183 | for attr in check: |
| 1184 | if hasattr(thread_module, attr): |
| 1185 | replace_attrs.append(attr) |
| 1186 | |
| 1187 | if not replace_attrs: |
| 1188 | return |
| 1189 | |
| 1190 | for attr in replace_attrs: |
| 1191 | if attr in start_joinable_attrs: |
| 1192 | if getattr(thread_module, "_original_start_joinable_thread", None) is None: |
| 1193 | _original_start_joinable_thread = thread_module._original_start_joinable_thread = getattr(thread_module, attr) |
| 1194 | else: |
| 1195 | _original_start_joinable_thread = thread_module._original_start_joinable_thread |
| 1196 | else: |
| 1197 | if getattr(thread_module, "_original_start_new_thread", None) is None: |
| 1198 | _original_start_new_thread = thread_module._original_start_new_thread = getattr(thread_module, attr) |
| 1199 | else: |
| 1200 | _original_start_new_thread = thread_module._original_start_new_thread |
| 1201 | |
| 1202 | class ClassWithPydevStartNewThread: |
| 1203 | def pydev_start_new_thread(self, function, args=(), kwargs={}): |
| 1204 | """ |
| 1205 | We need to replace the original thread_module.start_new_thread with this function so that threads started |
| 1206 | through it and not through the threading module are properly traced. |
| 1207 | """ |
| 1208 | return _original_start_new_thread(_UseNewThreadStartup(function, args, kwargs), ()) |
| 1209 | |
| 1210 | class ClassWithPydevStartJoinableThread: |
| 1211 | def pydev_start_joinable_thread(self, function, *args, **kwargs): |
| 1212 | """ |
| 1213 | We need to replace the original thread_module._start_joinable_thread with this function so that threads started |
| 1214 | through it and not through the threading module are properly traced. |
| 1215 | """ |
| 1216 | # Note: only handling the case from threading.py where the handle |
| 1217 | # and daemon flags are passed explicitly. This will fail if some user library |
| 1218 | # actually passes those without being a keyword argument! |
| 1219 | handle = kwargs.pop("handle", None) |
| 1220 | daemon = kwargs.pop("daemon", True) |
| 1221 | return _original_start_joinable_thread(_UseNewThreadStartup(function, args, kwargs), handle=handle, daemon=daemon) |
| 1222 | |
| 1223 | # This is a hack for the situation where the thread_module.start_new_thread is declared inside a class, such as the one below |
| 1224 | # class F(object): |
| 1225 | # start_new_thread = thread_module.start_new_thread |
| 1226 | # |
| 1227 | # def start_it(self): |
| 1228 | # self.start_new_thread(self.function, args, kwargs) |
| 1229 | # So, if it's an already bound method, calling self.start_new_thread won't really receive a different 'self' -- it |
| 1230 | # does work in the default case because in builtins self isn't passed either. |
| 1231 | pydev_start_new_thread = ClassWithPydevStartNewThread().pydev_start_new_thread |
| 1232 | pydev_start_joinable_thread = ClassWithPydevStartJoinableThread().pydev_start_joinable_thread |
no test coverage detected