| import sys |
| from pydevd_constants import * #@UnusedWildImport |
| from _pydev_imps import _pydev_thread |
| from pydevd_frame import PyDBFrame |
| import weakref |
| |
| #======================================================================================================================= |
| # AbstractPyDBAdditionalThreadInfo |
| #======================================================================================================================= |
| class AbstractPyDBAdditionalThreadInfo: |
| def __init__(self): |
| self.pydev_state = STATE_RUN |
| self.pydev_step_stop = None |
| self.pydev_step_cmd = None |
| self.pydev_notify_kill = False |
| self.pydev_force_stop_at_exception = None |
| self.pydev_smart_step_stop = None |
| self.pydev_django_resolve_frame = None |
| self.is_tracing = False |
| self.conditional_breakpoint_exception = None |
| |
| |
| def IterFrames(self): |
| raise NotImplementedError() |
| |
| def CreateDbFrame(self, args): |
| #args = mainDebugger, filename, base, additionalInfo, t, frame |
| raise NotImplementedError() |
| |
| def __str__(self): |
| return 'State:%s Stop:%s Cmd: %s Kill:%s' % (self.pydev_state, self.pydev_step_stop, self.pydev_step_cmd, self.pydev_notify_kill) |
| |
| |
| #======================================================================================================================= |
| # PyDBAdditionalThreadInfoWithCurrentFramesSupport |
| #======================================================================================================================= |
| class PyDBAdditionalThreadInfoWithCurrentFramesSupport(AbstractPyDBAdditionalThreadInfo): |
| |
| def IterFrames(self): |
| #sys._current_frames(): dictionary with thread id -> topmost frame |
| return sys._current_frames().values() #return a copy... don't know if it's changed if we did get an iterator |
| |
| #just create the db frame directly |
| CreateDbFrame = PyDBFrame |
| |
| #======================================================================================================================= |
| # PyDBAdditionalThreadInfoWithoutCurrentFramesSupport |
| #======================================================================================================================= |
| class PyDBAdditionalThreadInfoWithoutCurrentFramesSupport(AbstractPyDBAdditionalThreadInfo): |
| |
| def __init__(self): |
| AbstractPyDBAdditionalThreadInfo.__init__(self) |
| #That's where the last frame entered is kept. That's needed so that we're able to |
| #trace contexts that were previously untraced and are currently active. So, the bad thing |
| #is that the frame may be kept alive longer than it would if we go up on the frame stack, |
| #and is only disposed when some other frame is removed. |
| #A better way would be if we could get the topmost frame for each thread, but that's |
| #not possible (until python 2.5 -- which is the PyDBAdditionalThreadInfoWithCurrentFramesSupport version) |
| #Or if the user compiled threadframe (from http://www.majid.info/mylos/stories/2004/06/10/threadframe.html) |
| |
| #NOT RLock!! (could deadlock if it was) |
| self.lock = _pydev_thread.allocate_lock() |
| self._acquire_lock = self.lock.acquire |
| self._release_lock = self.lock.release |
| |
| #collection with the refs |
| d = {} |
| self.pydev_existing_frames = d |
| try: |
| self._iter_frames = d.iterkeys |
| except AttributeError: |
| self._iter_frames = d.keys |
| |
| |
| def _OnDbFrameCollected(self, ref): |
| ''' |
| Callback to be called when a given reference is garbage-collected. |
| ''' |
| self._acquire_lock() |
| try: |
| del self.pydev_existing_frames[ref] |
| finally: |
| self._release_lock() |
| |
| |
| def _AddDbFrame(self, db_frame): |
| self._acquire_lock() |
| try: |
| #create the db frame with a callback to remove it from the dict when it's garbage-collected |
| #(could be a set, but that's not available on all versions we want to target). |
| r = weakref.ref(db_frame, self._OnDbFrameCollected) |
| self.pydev_existing_frames[r] = r |
| finally: |
| self._release_lock() |
| |
| |
| def CreateDbFrame(self, args): |
| #the frame must be cached as a weak-ref (we return the actual db frame -- which will be kept |
| #alive until its trace_dispatch method is not referenced anymore). |
| #that's a large workaround because: |
| #1. we can't have weak-references to python frame object |
| #2. only from 2.5 onwards we have _current_frames support from the interpreter |
| db_frame = PyDBFrame(args) |
| db_frame.frame = args[-1] |
| self._AddDbFrame(db_frame) |
| return db_frame |
| |
| |
| def IterFrames(self): |
| #We cannot use yield (because of the lock) |
| self._acquire_lock() |
| try: |
| ret = [] |
| |
| for weak_db_frame in self._iter_frames(): |
| try: |
| ret.append(weak_db_frame().frame) |
| except AttributeError: |
| pass # ok, garbage-collected already |
| return ret |
| finally: |
| self._release_lock() |
| |
| def __str__(self): |
| return 'State:%s Stop:%s Cmd: %s Kill:%s Frames:%s' % (self.pydev_state, self.pydev_step_stop, self.pydev_step_cmd, self.pydev_notify_kill, len(self.IterFrames())) |
| |
| #======================================================================================================================= |
| # NOW, WE HAVE TO DEFINE WHICH THREAD INFO TO USE |
| # (whether we have to keep references to the frames or not) |
| # from version 2.5 onwards, we can use sys._current_frames to get a dict with the threads |
| # and frames, but to support other versions, we can't rely on that. |
| #======================================================================================================================= |
| if hasattr(sys, '_current_frames'): |
| PyDBAdditionalThreadInfo = PyDBAdditionalThreadInfoWithCurrentFramesSupport |
| else: |
| try: |
| import threadframe #@UnresolvedImport |
| sys._current_frames = threadframe.dict |
| assert sys._current_frames is threadframe.dict #Just check if it was correctly set |
| PyDBAdditionalThreadInfo = PyDBAdditionalThreadInfoWithCurrentFramesSupport |
| except: |
| #If all fails, let's use the support without frames |
| PyDBAdditionalThreadInfo = PyDBAdditionalThreadInfoWithoutCurrentFramesSupport |
| |