| #IMPORTANT: pydevd_constants must be the 1st thing defined because it'll keep a reference to the original sys._getframe |
| from __future__ import nested_scopes # Jython 2.1 support |
| from pydevd_constants import * # @UnusedWildImport |
| |
| import pydev_monkey_qt |
| pydev_monkey_qt.patch_qt() |
| |
| import traceback |
| |
| from django_debug import DjangoLineBreakpoint |
| from pydevd_frame import add_exception_to_frame |
| import pydev_imports |
| from pydevd_breakpoints import * #@UnusedWildImport |
| import fix_getpass |
| from pydevd_comm import CMD_CHANGE_VARIABLE, \ |
| CMD_EVALUATE_EXPRESSION, \ |
| CMD_EXEC_EXPRESSION, \ |
| CMD_GET_COMPLETIONS, \ |
| CMD_GET_FRAME, \ |
| CMD_GET_VARIABLE, \ |
| CMD_LIST_THREADS, \ |
| CMD_REMOVE_BREAK, \ |
| CMD_RUN, \ |
| CMD_SET_BREAK, \ |
| CMD_SET_NEXT_STATEMENT,\ |
| CMD_STEP_INTO, \ |
| CMD_STEP_OVER, \ |
| CMD_STEP_RETURN, \ |
| CMD_THREAD_CREATE, \ |
| CMD_THREAD_KILL, \ |
| CMD_THREAD_RUN, \ |
| CMD_THREAD_SUSPEND, \ |
| CMD_RUN_TO_LINE, \ |
| CMD_RELOAD_CODE, \ |
| CMD_VERSION, \ |
| CMD_CONSOLE_EXEC, \ |
| CMD_ADD_EXCEPTION_BREAK, \ |
| CMD_REMOVE_EXCEPTION_BREAK, \ |
| CMD_LOAD_SOURCE, \ |
| CMD_ADD_DJANGO_EXCEPTION_BREAK, \ |
| CMD_REMOVE_DJANGO_EXCEPTION_BREAK, \ |
| CMD_SMART_STEP_INTO,\ |
| InternalChangeVariable, \ |
| InternalGetCompletions, \ |
| InternalEvaluateExpression, \ |
| InternalConsoleExec, \ |
| InternalGetFrame, \ |
| InternalGetVariable, \ |
| InternalTerminateThread, \ |
| InternalRunThread, \ |
| InternalStepThread, \ |
| NetCommand, \ |
| NetCommandFactory, \ |
| PyDBDaemonThread, \ |
| _queue, \ |
| ReaderThread, \ |
| SetGlobalDebugger, \ |
| WriterThread, \ |
| PydevdFindThreadById, \ |
| PydevdLog, \ |
| StartClient, \ |
| StartServer, \ |
| InternalSetNextStatementThread, \ |
| ReloadCodeCommand, \ |
| ID_TO_MEANING,\ |
| CMD_SET_PY_EXCEPTION, \ |
| CMD_IGNORE_THROWN_EXCEPTION_AT,\ |
| InternalGetBreakpointException, \ |
| InternalSendCurrExceptionTrace,\ |
| InternalSendCurrExceptionTraceProceeded,\ |
| CMD_ENABLE_DONT_TRACE, \ |
| CMD_GET_FILE_CONTENTS,\ |
| CMD_SET_PROPERTY_TRACE, CMD_RUN_CUSTOM_OPERATION,\ |
| InternalRunCustomOperation, CMD_EVALUATE_CONSOLE_EXPRESSION, InternalEvaluateConsoleExpression,\ |
| InternalConsoleGetCompletions |
| |
| from pydevd_file_utils import NormFileToServer, GetFilenameAndBase |
| import pydevd_file_utils |
| import pydevd_vars |
| import pydevd_vm_type |
| import pydevd_tracing |
| import pydevd_io |
| from pydevd_additional_thread_info import PyDBAdditionalThreadInfo |
| from pydevd_custom_frames import CustomFramesContainer, CustomFramesContainerInit |
| import pydevd_dont_trace |
| import pydevd_traceproperty |
| |
| from _pydev_imps import _pydev_time as time, _pydev_thread |
| |
| import _pydev_threading as threading |
| |
| import os |
| import atexit |
| |
| |
| threadingEnumerate = threading.enumerate |
| threadingCurrentThread = threading.currentThread |
| |
| try: |
| 'dummy'.encode('utf-8') # Added because otherwise Jython 2.2.1 wasn't finding the encoding (if it wasn't loaded in the main thread). |
| except: |
| pass |
| |
| DONT_TRACE = { |
| # commonly used things from the stdlib that we don't want to trace |
| 'Queue.py':1, |
| 'queue.py':1, |
| 'socket.py':1, |
| 'weakref.py':1, |
| 'linecache.py':1, |
| 'threading.py':1, |
| |
| #things from pydev that we don't want to trace |
| '_pydev_execfile.py':1, |
| '_pydev_jython_execfile.py':1, |
| '_pydev_threading':1, |
| '_pydev_Queue':1, |
| 'django_debug.py':1, |
| 'django_frame.py':1, |
| 'pydev_log.py':1, |
| 'pydev_monkey.py':1 , |
| 'pydevd.py':1 , |
| 'pydevd_additional_thread_info.py':1, |
| 'pydevd_comm.py':1, |
| 'pydevd_console.py':1 , |
| 'pydevd_constants.py':1, |
| 'pydevd_custom_frames.py':1, |
| 'pydevd_dont_trace.py':1, |
| 'pydevd_exec.py':1, |
| 'pydevd_exec2.py':1, |
| 'pydevd_file_utils.py':1, |
| 'pydevd_frame.py':1, |
| 'pydevd_import_class.py':1 , |
| 'pydevd_io.py':1 , |
| 'pydevd_psyco_stub.py':1, |
| 'pydevd_referrers.py':1 , |
| 'pydevd_reload.py':1 , |
| 'pydevd_resolver.py':1 , |
| 'pydevd_save_locals.py':1 , |
| 'pydevd_signature.py':1, |
| 'pydevd_stackless.py':1 , |
| 'pydevd_traceproperty.py':1, |
| 'pydevd_tracing.py':1 , |
| 'pydevd_utils.py':1, |
| 'pydevd_vars.py':1, |
| 'pydevd_vm_type.py':1, |
| 'pydevd_xml.py':1, |
| } |
| |
| if IS_PY3K: |
| # if we try to trace io.py it seems it can get halted (see http://bugs.python.org/issue4716) |
| DONT_TRACE['io.py'] = 1 |
| |
| # Don't trace common encodings too |
| DONT_TRACE['cp1252.py'] = 1 |
| DONT_TRACE['utf_8.py'] = 1 |
| |
| |
| connected = False |
| bufferStdOutToServer = False |
| bufferStdErrToServer = False |
| remote = False |
| |
| from _pydev_filesystem_encoding import getfilesystemencoding |
| file_system_encoding = getfilesystemencoding() |
| |
| |
| # Hack for https://sw-brainwy.rhcloud.com/tracker/PyDev/363 (i.e.: calling isAlive() can throw AssertionError under some circumstances) |
| # It is required to debug threads started by start_new_thread in Python 3.4 |
| _temp = threading.Thread() |
| if hasattr(_temp, '_is_stopped'): # Python 3.4 has this |
| def isThreadAlive(t): |
| try: |
| return not t._is_stopped |
| except: |
| return t.isAlive() |
| |
| elif hasattr(_temp, '_Thread__stopped'): # Python 2.7 has this |
| def isThreadAlive(t): |
| try: |
| return not t._Thread__stopped |
| except: |
| return t.isAlive() |
| |
| else: # Haven't checked all other versions, so, let's use the regular isAlive call in this case. |
| def isThreadAlive(t): |
| return t.isAlive() |
| del _temp |
| |
| #======================================================================================================================= |
| # PyDBCommandThread |
| #======================================================================================================================= |
| class PyDBCommandThread(PyDBDaemonThread): |
| |
| def __init__(self, pyDb): |
| PyDBDaemonThread.__init__(self) |
| self._py_db_command_thread_event = pyDb._py_db_command_thread_event |
| self.pyDb = pyDb |
| self.setName('pydevd.CommandThread') |
| |
| def OnRun(self): |
| for i in xrange(1, 10): |
| time.sleep(0.5) #this one will only start later on (because otherwise we may not have any non-daemon threads |
| if self.killReceived: |
| return |
| |
| if self.dontTraceMe: |
| self.pyDb.SetTrace(None) # no debugging on this thread |
| |
| try: |
| while not self.killReceived: |
| try: |
| self.pyDb.processInternalCommands() |
| except: |
| PydevdLog(0, 'Finishing debug communication...(2)') |
| self._py_db_command_thread_event.clear() |
| self._py_db_command_thread_event.wait(0.5) |
| except: |
| pydev_log.debug(sys.exc_info()[0]) |
| |
| #only got this error in interpreter shutdown |
| #PydevdLog(0, 'Finishing debug communication...(3)') |
| |
| |
| def killAllPydevThreads(): |
| threads = DictKeys(PyDBDaemonThread.created_pydb_daemon_threads) |
| for t in threads: |
| if hasattr(t, 'doKillPydevThread'): |
| t.doKillPydevThread() |
| |
| |
| #======================================================================================================================= |
| # PyDBCheckAliveThread |
| #======================================================================================================================= |
| class PyDBCheckAliveThread(PyDBDaemonThread): |
| |
| def __init__(self, pyDb): |
| PyDBDaemonThread.__init__(self) |
| self.pyDb = pyDb |
| self.setName('pydevd.CheckAliveThread') |
| |
| def OnRun(self): |
| if self.dontTraceMe: |
| |
| disable_tracing = True |
| |
| if pydevd_vm_type.GetVmType() == pydevd_vm_type.PydevdVmType.JYTHON and sys.hexversion <= 0x020201f0: |
| # don't run untraced threads if we're in jython 2.2.1 or lower |
| # jython bug: if we start a thread and another thread changes the tracing facility |
| # it affects other threads (it's not set only for the thread but globally) |
| # Bug: http://sourceforge.net/tracker/index.php?func=detail&aid=1870039&group_id=12867&atid=112867 |
| disable_tracing = False |
| |
| if disable_tracing: |
| pydevd_tracing.SetTrace(None) # no debugging on this thread |
| |
| while not self.killReceived: |
| if not self.pyDb.haveAliveThreads(): |
| try: |
| pydev_log.debug("No alive threads, finishing debug session") |
| self.pyDb.FinishDebuggingSession() |
| killAllPydevThreads() |
| except: |
| traceback.print_exc() |
| |
| self.killReceived = True |
| return |
| |
| time.sleep(0.3) |
| |
| def doKillPydevThread(self): |
| pass |
| |
| |
| |
| #======================================================================================================================= |
| # PyDB |
| #======================================================================================================================= |
| class PyDB: |
| """ Main debugging class |
| Lots of stuff going on here: |
| |
| PyDB starts two threads on startup that connect to remote debugger (RDB) |
| The threads continuously read & write commands to RDB. |
| PyDB communicates with these threads through command queues. |
| Every RDB command is processed by calling processNetCommand. |
| Every PyDB net command is sent to the net by posting NetCommand to WriterThread queue |
| |
| Some commands need to be executed on the right thread (suspend/resume & friends) |
| These are placed on the internal command queue. |
| """ |
| |
| |
| def __init__(self): |
| SetGlobalDebugger(self) |
| pydevd_tracing.ReplaceSysSetTraceFunc() |
| self.reader = None |
| self.writer = None |
| self.quitting = None |
| self.cmdFactory = NetCommandFactory() |
| self._cmd_queue = {} # the hash of Queues. Key is thread id, value is thread |
| |
| self.breakpoints = {} |
| self.django_breakpoints = {} |
| |
| self.file_to_id_to_line_breakpoint = {} |
| self.file_to_id_to_django_breakpoint = {} |
| |
| # Note: breakpoints dict should not be mutated: a copy should be created |
| # and later it should be assigned back (to prevent concurrency issues). |
| self.break_on_uncaught_exceptions = {} |
| self.break_on_caught_exceptions = {} |
| |
| self.django_exception_break = {} |
| self.readyToRun = False |
| self._main_lock = _pydev_thread.allocate_lock() |
| self._lock_running_thread_ids = _pydev_thread.allocate_lock() |
| self._py_db_command_thread_event = threading.Event() |
| CustomFramesContainer._py_db_command_thread_event = self._py_db_command_thread_event |
| self._finishDebuggingSession = False |
| self._terminationEventSent = False |
| self.signature_factory = None |
| self.SetTrace = pydevd_tracing.SetTrace |
| self.break_on_exceptions_thrown_in_same_context = False |
| self.ignore_exceptions_thrown_in_lines_with_ignore_exception = True |
| |
| # Suspend debugger even if breakpoint condition raises an exception |
| SUSPEND_ON_BREAKPOINT_EXCEPTION = True |
| self.suspend_on_breakpoint_exception = SUSPEND_ON_BREAKPOINT_EXCEPTION |
| |
| # By default user can step into properties getter/setter/deleter methods |
| self.disable_property_trace = False |
| self.disable_property_getter_trace = False |
| self.disable_property_setter_trace = False |
| self.disable_property_deleter_trace = False |
| |
| #this is a dict of thread ids pointing to thread ids. Whenever a command is passed to the java end that |
| #acknowledges that a thread was created, the thread id should be passed here -- and if at some time we do not |
| #find that thread alive anymore, we must remove it from this list and make the java side know that the thread |
| #was killed. |
| self._running_thread_ids = {} |
| self._set_breakpoints_with_id = False |
| |
| # This attribute holds the file-> lines which have an @IgnoreException. |
| self.filename_to_lines_where_exceptions_are_ignored = {} |
| |
| |
| def haveAliveThreads(self): |
| for t in threadingEnumerate(): |
| if isinstance(t, PyDBDaemonThread): |
| pydev_log.error_once( |
| 'Error in debugger: Found PyDBDaemonThread through threading.enumerate().\n') |
| |
| if getattr(t, 'is_pydev_daemon_thread', False): |
| #Important: Jython 2.5rc4 has a bug where a thread created with thread.start_new_thread won't be |
| #set as a daemon thread, so, we also have to check for the 'is_pydev_daemon_thread' flag. |
| #See: https://github.com/fabioz/PyDev.Debugger/issues/11 |
| continue |
| |
| if isThreadAlive(t) and not t.isDaemon(): |
| return True |
| |
| return False |
| |
| def FinishDebuggingSession(self): |
| self._finishDebuggingSession = True |
| |
| def acquire(self): |
| if PyDBUseLocks: |
| self.lock.acquire() |
| return True |
| |
| def release(self): |
| if PyDBUseLocks: |
| self.lock.release() |
| return True |
| |
| def initializeNetwork(self, sock): |
| try: |
| sock.settimeout(None) # infinite, no timeouts from now on - jython does not have it |
| except: |
| pass |
| self.writer = WriterThread(sock) |
| self.reader = ReaderThread(sock) |
| self.writer.start() |
| self.reader.start() |
| |
| time.sleep(0.1) # give threads time to start |
| |
| def connect(self, host, port): |
| if host: |
| s = StartClient(host, port) |
| else: |
| s = StartServer(port) |
| |
| self.initializeNetwork(s) |
| |
| |
| def getInternalQueue(self, thread_id): |
| """ returns internal command queue for a given thread. |
| if new queue is created, notify the RDB about it """ |
| if thread_id.startswith('__frame__'): |
| thread_id = thread_id[thread_id.rfind('|') + 1:] |
| try: |
| return self._cmd_queue[thread_id] |
| except KeyError: |
| return self._cmd_queue.setdefault(thread_id, _queue.Queue()) #@UndefinedVariable |
| |
| |
| def postInternalCommand(self, int_cmd, thread_id): |
| """ if thread_id is *, post to all """ |
| if thread_id == "*": |
| threads = threadingEnumerate() |
| for t in threads: |
| thread_id = GetThreadId(t) |
| queue = self.getInternalQueue(thread_id) |
| queue.put(int_cmd) |
| |
| else: |
| queue = self.getInternalQueue(thread_id) |
| queue.put(int_cmd) |
| |
| def checkOutputRedirect(self): |
| global bufferStdOutToServer |
| global bufferStdErrToServer |
| |
| if bufferStdOutToServer: |
| initStdoutRedirect() |
| self.checkOutput(sys.stdoutBuf, 1) #@UndefinedVariable |
| |
| if bufferStdErrToServer: |
| initStderrRedirect() |
| self.checkOutput(sys.stderrBuf, 2) #@UndefinedVariable |
| |
| def checkOutput(self, out, outCtx): |
| '''Checks the output to see if we have to send some buffered output to the debug server |
| |
| @param out: sys.stdout or sys.stderr |
| @param outCtx: the context indicating: 1=stdout and 2=stderr (to know the colors to write it) |
| ''' |
| |
| try: |
| v = out.getvalue() |
| |
| if v: |
| self.cmdFactory.makeIoMessage(v, outCtx, self) |
| except: |
| traceback.print_exc() |
| |
| |
| def processInternalCommands(self): |
| '''This function processes internal commands |
| ''' |
| self._main_lock.acquire() |
| try: |
| |
| self.checkOutputRedirect() |
| |
| curr_thread_id = GetThreadId(threadingCurrentThread()) |
| program_threads_alive = {} |
| all_threads = threadingEnumerate() |
| program_threads_dead = [] |
| self._lock_running_thread_ids.acquire() |
| try: |
| for t in all_threads: |
| thread_id = GetThreadId(t) |
| |
| if isinstance(t, PyDBDaemonThread): |
| pydev_log.error_once('Found PyDBDaemonThread in threading.enumerate.') |
| |
| elif getattr(t, 'is_pydev_daemon_thread', False): |
| pass # I.e.: skip the DummyThreads created from pydev daemon threads |
| |
| elif isThreadAlive(t): |
| program_threads_alive[thread_id] = t |
| |
| if not DictContains(self._running_thread_ids, thread_id): |
| if not hasattr(t, 'additionalInfo'): |
| # see http://sourceforge.net/tracker/index.php?func=detail&aid=1955428&group_id=85796&atid=577329 |
| # Let's create the additional info right away! |
| t.additionalInfo = PyDBAdditionalThreadInfo() |
| self._running_thread_ids[thread_id] = t |
| self.writer.addCommand(self.cmdFactory.makeThreadCreatedMessage(t)) |
| |
| |
| queue = self.getInternalQueue(thread_id) |
| cmdsToReadd = [] # some commands must be processed by the thread itself... if that's the case, |
| # we will re-add the commands to the queue after executing. |
| try: |
| while True: |
| int_cmd = queue.get(False) |
| if int_cmd.canBeExecutedBy(curr_thread_id): |
| PydevdLog(2, "processing internal command ", str(int_cmd)) |
| int_cmd.doIt(self) |
| else: |
| PydevdLog(2, "NOT processing internal command ", str(int_cmd)) |
| cmdsToReadd.append(int_cmd) |
| |
| except _queue.Empty: #@UndefinedVariable |
| for int_cmd in cmdsToReadd: |
| queue.put(int_cmd) |
| # this is how we exit |
| |
| |
| thread_ids = list(self._running_thread_ids.keys()) |
| for tId in thread_ids: |
| if not DictContains(program_threads_alive, tId): |
| program_threads_dead.append(tId) |
| finally: |
| self._lock_running_thread_ids.release() |
| |
| for tId in program_threads_dead: |
| try: |
| self.processThreadNotAlive(tId) |
| except: |
| sys.stderr.write('Error iterating through %s (%s) - %s\n' % ( |
| program_threads_alive, program_threads_alive.__class__, dir(program_threads_alive))) |
| raise |
| |
| |
| if len(program_threads_alive) == 0: |
| self.FinishDebuggingSession() |
| for t in all_threads: |
| if hasattr(t, 'doKillPydevThread'): |
| t.doKillPydevThread() |
| |
| finally: |
| self._main_lock.release() |
| |
| |
| def setTracingForUntracedContexts(self, ignore_frame=None, overwrite_prev_trace=False): |
| # Enable the tracing for existing threads (because there may be frames being executed that |
| # are currently untraced). |
| threads = threadingEnumerate() |
| try: |
| for t in threads: |
| # TODO: optimize so that we only actually add that tracing if it's in |
| # the new breakpoint context. |
| additionalInfo = None |
| try: |
| additionalInfo = t.additionalInfo |
| except AttributeError: |
| pass # that's ok, no info currently set |
| |
| if additionalInfo is not None: |
| for frame in additionalInfo.IterFrames(): |
| if frame is not ignore_frame: |
| self.SetTraceForFrameAndParents(frame, overwrite_prev_trace=overwrite_prev_trace) |
| finally: |
| frame = None |
| t = None |
| threads = None |
| additionalInfo = None |
| |
| |
| def consolidate_breakpoints(self, file, id_to_breakpoint, breakpoints): |
| break_dict = {} |
| for breakpoint_id, pybreakpoint in DictIterItems(id_to_breakpoint): |
| break_dict[pybreakpoint.line] = pybreakpoint |
| |
| breakpoints[file] = break_dict |
| |
| |
| def add_break_on_exception( |
| self, |
| exception, |
| notify_always, |
| notify_on_terminate, |
| notify_on_first_raise_only, |
| ): |
| eb = ExceptionBreakpoint( |
| exception, |
| notify_always, |
| notify_on_terminate, |
| notify_on_first_raise_only, |
| ) |
| |
| if eb.notify_on_terminate: |
| cp = self.break_on_uncaught_exceptions.copy() |
| cp[exception] = eb |
| if DebugInfoHolder.DEBUG_TRACE_BREAKPOINTS > 0: |
| pydev_log.error("Exceptions to hook on terminate: %s\n" % (cp,)) |
| self.break_on_uncaught_exceptions = cp |
| |
| if eb.notify_always: |
| cp = self.break_on_caught_exceptions.copy() |
| cp[exception] = eb |
| if DebugInfoHolder.DEBUG_TRACE_BREAKPOINTS > 0: |
| pydev_log.error("Exceptions to hook always: %s\n" % (cp,)) |
| self.break_on_caught_exceptions = cp |
| |
| return eb |
| |
| def update_after_exceptions_added(self, added): |
| updated_on_caught = False |
| updated_on_uncaught = False |
| |
| for eb in added: |
| if not updated_on_uncaught and eb.notify_on_terminate: |
| updated_on_uncaught = True |
| update_exception_hook(self) |
| |
| if not updated_on_caught and eb.notify_always: |
| updated_on_caught = True |
| self.setTracingForUntracedContexts() |
| |
| |
| def processNetCommand(self, cmd_id, seq, text): |
| '''Processes a command received from the Java side |
| |
| @param cmd_id: the id of the command |
| @param seq: the sequence of the command |
| @param text: the text received in the command |
| |
| @note: this method is run as a big switch... after doing some tests, it's not clear whether changing it for |
| a dict id --> function call will have better performance result. A simple test with xrange(10000000) showed |
| that the gains from having a fast access to what should be executed are lost because of the function call in |
| a way that if we had 10 elements in the switch the if..elif are better -- but growing the number of choices |
| makes the solution with the dispatch look better -- so, if this gets more than 20-25 choices at some time, |
| it may be worth refactoring it (actually, reordering the ifs so that the ones used mostly come before |
| probably will give better performance). |
| ''' |
| #print(ID_TO_MEANING[str(cmd_id)], repr(text)) |
| |
| self._main_lock.acquire() |
| try: |
| try: |
| cmd = None |
| if cmd_id == CMD_RUN: |
| self.readyToRun = True |
| |
| elif cmd_id == CMD_VERSION: |
| # response is version number |
| # ide_os should be 'WINDOWS' or 'UNIX'. |
| ide_os = 'WINDOWS' |
| |
| # Breakpoints can be grouped by 'LINE' or by 'ID'. |
| breakpoints_by = 'LINE' |
| |
| splitted = text.split('\t') |
| if len(splitted) == 1: |
| _local_version = splitted |
| |
| elif len(splitted) == 2: |
| _local_version, ide_os = splitted |
| |
| elif len(splitted) == 3: |
| _local_version, ide_os, breakpoints_by = splitted |
| |
| if breakpoints_by == 'ID': |
| self._set_breakpoints_with_id = True |
| else: |
| self._set_breakpoints_with_id = False |
| |
| pydevd_file_utils.set_ide_os(ide_os) |
| |
| cmd = self.cmdFactory.makeVersionMessage(seq) |
| |
| elif cmd_id == CMD_LIST_THREADS: |
| # response is a list of threads |
| cmd = self.cmdFactory.makeListThreadsMessage(seq) |
| |
| elif cmd_id == CMD_THREAD_KILL: |
| int_cmd = InternalTerminateThread(text) |
| self.postInternalCommand(int_cmd, text) |
| |
| elif cmd_id == CMD_THREAD_SUSPEND: |
| # Yes, thread suspend is still done at this point, not through an internal command! |
| t = PydevdFindThreadById(text) |
| if t: |
| additionalInfo = None |
| try: |
| additionalInfo = t.additionalInfo |
| except AttributeError: |
| pass # that's ok, no info currently set |
| |
| if additionalInfo is not None: |
| for frame in additionalInfo.IterFrames(): |
| self.SetTraceForFrameAndParents(frame) |
| del frame |
| |
| self.setSuspend(t, CMD_THREAD_SUSPEND) |
| elif text.startswith('__frame__:'): |
| sys.stderr.write("Can't suspend tasklet: %s\n" % (text,)) |
| |
| elif cmd_id == CMD_THREAD_RUN: |
| t = PydevdFindThreadById(text) |
| if t: |
| thread_id = GetThreadId(t) |
| int_cmd = InternalRunThread(thread_id) |
| self.postInternalCommand(int_cmd, thread_id) |
| |
| elif text.startswith('__frame__:'): |
| sys.stderr.write("Can't make tasklet run: %s\n" % (text,)) |
| |
| |
| elif cmd_id == CMD_STEP_INTO or cmd_id == CMD_STEP_OVER or cmd_id == CMD_STEP_RETURN: |
| # we received some command to make a single step |
| t = PydevdFindThreadById(text) |
| if t: |
| thread_id = GetThreadId(t) |
| int_cmd = InternalStepThread(thread_id, cmd_id) |
| self.postInternalCommand(int_cmd, thread_id) |
| |
| elif text.startswith('__frame__:'): |
| sys.stderr.write("Can't make tasklet step command: %s\n" % (text,)) |
| |
| |
| elif cmd_id == CMD_RUN_TO_LINE or cmd_id == CMD_SET_NEXT_STATEMENT or cmd_id == CMD_SMART_STEP_INTO: |
| # we received some command to make a single step |
| thread_id, line, func_name = text.split('\t', 2) |
| t = PydevdFindThreadById(thread_id) |
| if t: |
| int_cmd = InternalSetNextStatementThread(thread_id, cmd_id, line, func_name) |
| self.postInternalCommand(int_cmd, thread_id) |
| elif thread_id.startswith('__frame__:'): |
| sys.stderr.write("Can't set next statement in tasklet: %s\n" % (thread_id,)) |
| |
| |
| elif cmd_id == CMD_RELOAD_CODE: |
| # we received some command to make a reload of a module |
| module_name = text.strip() |
| |
| thread_id = '*' # Any thread |
| |
| # Note: not going for the main thread because in this case it'd only do the load |
| # when we stopped on a breakpoint. |
| # for tid, t in self._running_thread_ids.items(): #Iterate in copy |
| # thread_name = t.getName() |
| # |
| # print thread_name, GetThreadId(t) |
| # #Note: if possible, try to reload on the main thread |
| # if thread_name == 'MainThread': |
| # thread_id = tid |
| |
| int_cmd = ReloadCodeCommand(module_name, thread_id) |
| self.postInternalCommand(int_cmd, thread_id) |
| |
| |
| elif cmd_id == CMD_CHANGE_VARIABLE: |
| # the text is: thread\tstackframe\tFRAME|GLOBAL\tattribute_to_change\tvalue_to_change |
| try: |
| thread_id, frame_id, scope, attr_and_value = text.split('\t', 3) |
| |
| tab_index = attr_and_value.rindex('\t') |
| attr = attr_and_value[0:tab_index].replace('\t', '.') |
| value = attr_and_value[tab_index + 1:] |
| int_cmd = InternalChangeVariable(seq, thread_id, frame_id, scope, attr, value) |
| self.postInternalCommand(int_cmd, thread_id) |
| |
| except: |
| traceback.print_exc() |
| |
| elif cmd_id == CMD_GET_VARIABLE: |
| # we received some command to get a variable |
| # the text is: thread_id\tframe_id\tFRAME|GLOBAL\tattributes* |
| try: |
| thread_id, frame_id, scopeattrs = text.split('\t', 2) |
| |
| if scopeattrs.find('\t') != -1: # there are attributes beyond scope |
| scope, attrs = scopeattrs.split('\t', 1) |
| else: |
| scope, attrs = (scopeattrs, None) |
| |
| int_cmd = InternalGetVariable(seq, thread_id, frame_id, scope, attrs) |
| self.postInternalCommand(int_cmd, thread_id) |
| |
| except: |
| traceback.print_exc() |
| |
| elif cmd_id == CMD_GET_COMPLETIONS: |
| # we received some command to get a variable |
| # the text is: thread_id\tframe_id\tactivation token |
| try: |
| thread_id, frame_id, scope, act_tok = text.split('\t', 3) |
| |
| int_cmd = InternalGetCompletions(seq, thread_id, frame_id, act_tok) |
| self.postInternalCommand(int_cmd, thread_id) |
| |
| except: |
| traceback.print_exc() |
| |
| elif cmd_id == CMD_GET_FRAME: |
| thread_id, frame_id, scope = text.split('\t', 2) |
| |
| int_cmd = InternalGetFrame(seq, thread_id, frame_id) |
| self.postInternalCommand(int_cmd, thread_id) |
| |
| elif cmd_id == CMD_SET_BREAK: |
| # func name: 'None': match anything. Empty: match global, specified: only method context. |
| # command to add some breakpoint. |
| # text is file\tline. Add to breakpoints dictionary |
| if self._set_breakpoints_with_id: |
| breakpoint_id, type, file, line, func_name, condition, expression = text.split('\t', 6) |
| |
| breakpoint_id = int(breakpoint_id) |
| line = int(line) |
| |
| # We must restore new lines and tabs as done in |
| # AbstractDebugTarget.breakpointAdded |
| condition = condition.replace("@_@NEW_LINE_CHAR@_@", '\n').\ |
| replace("@_@TAB_CHAR@_@", '\t').strip() |
| |
| expression = expression.replace("@_@NEW_LINE_CHAR@_@", '\n').\ |
| replace("@_@TAB_CHAR@_@", '\t').strip() |
| else: |
| #Note: this else should be removed after PyCharm migrates to setting |
| #breakpoints by id (and ideally also provides func_name). |
| type, file, line, condition, expression = text.split('\t', 4) |
| # If we don't have an id given for each breakpoint, consider |
| # the id to be the line. |
| breakpoint_id = line = int(line) |
| if condition.startswith('**FUNC**'): |
| func_name, condition = condition.split('\t', 1) |
| |
| # We must restore new lines and tabs as done in |
| # AbstractDebugTarget.breakpointAdded |
| condition = condition.replace("@_@NEW_LINE_CHAR@_@", '\n').\ |
| replace("@_@TAB_CHAR@_@", '\t').strip() |
| |
| func_name = func_name[8:] |
| else: |
| func_name = 'None' # Match anything if not specified. |
| |
| if not IS_PY3K: # In Python 3, the frame object will have unicode for the file, whereas on python 2 it has a byte-array encoded with the filesystem encoding. |
| file = file.encode(file_system_encoding) |
| |
| file = NormFileToServer(file) |
| |
| if not pydevd_file_utils.exists(file): |
| sys.stderr.write('pydev debugger: warning: trying to add breakpoint'\ |
| ' to file that does not exist: %s (will have no effect)\n' % (file,)) |
| sys.stderr.flush() |
| |
| |
| if len(condition) <= 0 or condition is None or condition == "None": |
| condition = None |
| |
| if len(expression) <= 0 or expression is None or expression == "None": |
| expression = None |
| |
| if type == 'python-line': |
| breakpoint = LineBreakpoint(line, condition, func_name, expression) |
| breakpoints = self.breakpoints |
| file_to_id_to_breakpoint = self.file_to_id_to_line_breakpoint |
| elif type == 'django-line': |
| breakpoint = DjangoLineBreakpoint(file, line, condition, func_name, expression) |
| breakpoints = self.django_breakpoints |
| file_to_id_to_breakpoint = self.file_to_id_to_django_breakpoint |
| else: |
| raise NameError(type) |
| |
| if DebugInfoHolder.DEBUG_TRACE_BREAKPOINTS > 0: |
| pydev_log.debug('Added breakpoint:%s - line:%s - func_name:%s\n' % (file, line, func_name.encode('utf-8'))) |
| sys.stderr.flush() |
| |
| if DictContains(file_to_id_to_breakpoint, file): |
| id_to_pybreakpoint = file_to_id_to_breakpoint[file] |
| else: |
| id_to_pybreakpoint = file_to_id_to_breakpoint[file] = {} |
| |
| id_to_pybreakpoint[breakpoint_id] = breakpoint |
| self.consolidate_breakpoints(file, id_to_pybreakpoint, breakpoints) |
| |
| self.setTracingForUntracedContexts(overwrite_prev_trace=True) |
| |
| elif cmd_id == CMD_REMOVE_BREAK: |
| #command to remove some breakpoint |
| #text is type\file\tid. Remove from breakpoints dictionary |
| breakpoint_type, file, breakpoint_id = text.split('\t', 2) |
| |
| if not IS_PY3K: # In Python 3, the frame object will have unicode for the file, whereas on python 2 it has a byte-array encoded with the filesystem encoding. |
| file = file.encode(file_system_encoding) |
| |
| file = NormFileToServer(file) |
| |
| try: |
| breakpoint_id = int(breakpoint_id) |
| except ValueError: |
| pydev_log.error('Error removing breakpoint. Expected breakpoint_id to be an int. Found: %s' % (breakpoint_id,)) |
| |
| else: |
| if breakpoint_type == 'python-line': |
| breakpoints = self.breakpoints |
| file_to_id_to_breakpoint = self.file_to_id_to_line_breakpoint |
| elif breakpoint_type == 'django-line': |
| breakpoints = self.django_breakpoints |
| file_to_id_to_breakpoint = self.file_to_id_to_django_breakpoint |
| else: |
| raise NameError(breakpoint_type) |
| |
| try: |
| id_to_pybreakpoint = file_to_id_to_breakpoint.get(file, {}) |
| if DebugInfoHolder.DEBUG_TRACE_BREAKPOINTS > 0: |
| existing = id_to_pybreakpoint[breakpoint_id] |
| sys.stderr.write('Removed breakpoint:%s - line:%s - func_name:%s (id: %s)\n' % ( |
| file, existing.line, existing.func_name.encode('utf-8'), breakpoint_id)) |
| |
| del id_to_pybreakpoint[breakpoint_id] |
| self.consolidate_breakpoints(file, id_to_pybreakpoint, breakpoints) |
| except KeyError: |
| pydev_log.error("Error removing breakpoint: Breakpoint id not found: %s id: %s. Available ids: %s\n" % ( |
| file, breakpoint_id, DictKeys(id_to_pybreakpoint))) |
| |
| |
| elif cmd_id == CMD_EVALUATE_EXPRESSION or cmd_id == CMD_EXEC_EXPRESSION: |
| #command to evaluate the given expression |
| #text is: thread\tstackframe\tLOCAL\texpression |
| thread_id, frame_id, scope, expression, trim = text.split('\t', 4) |
| int_cmd = InternalEvaluateExpression(seq, thread_id, frame_id, expression, |
| cmd_id == CMD_EXEC_EXPRESSION, int(trim) == 1) |
| self.postInternalCommand(int_cmd, thread_id) |
| |
| elif cmd_id == CMD_CONSOLE_EXEC: |
| #command to exec expression in console, in case expression is only partially valid 'False' is returned |
| #text is: thread\tstackframe\tLOCAL\texpression |
| |
| thread_id, frame_id, scope, expression = text.split('\t', 3) |
| |
| int_cmd = InternalConsoleExec(seq, thread_id, frame_id, expression) |
| self.postInternalCommand(int_cmd, thread_id) |
| |
| elif cmd_id == CMD_SET_PY_EXCEPTION: |
| # Command which receives set of exceptions on which user wants to break the debugger |
| # text is: break_on_uncaught;break_on_caught;TypeError;ImportError;zipimport.ZipImportError; |
| # This API is optional and works 'in bulk' -- it's possible |
| # to get finer-grained control with CMD_ADD_EXCEPTION_BREAK/CMD_REMOVE_EXCEPTION_BREAK |
| # which allows setting caught/uncaught per exception. |
| # |
| splitted = text.split(';') |
| self.break_on_uncaught_exceptions = {} |
| self.break_on_caught_exceptions = {} |
| added = [] |
| if len(splitted) >= 4: |
| if splitted[0] == 'true': |
| break_on_uncaught = True |
| else: |
| break_on_uncaught = False |
| |
| if splitted[1] == 'true': |
| break_on_caught = True |
| else: |
| break_on_caught = False |
| |
| if splitted[2] == 'true': |
| self.break_on_exceptions_thrown_in_same_context = True |
| else: |
| self.break_on_exceptions_thrown_in_same_context = False |
| |
| if splitted[3] == 'true': |
| self.ignore_exceptions_thrown_in_lines_with_ignore_exception = True |
| else: |
| self.ignore_exceptions_thrown_in_lines_with_ignore_exception = False |
| |
| for exception_type in splitted[4:]: |
| exception_type = exception_type.strip() |
| if not exception_type: |
| continue |
| |
| exception_breakpoint = self.add_break_on_exception( |
| exception_type, |
| notify_always=break_on_caught, |
| notify_on_terminate=break_on_uncaught, |
| notify_on_first_raise_only=False, |
| ) |
| added.append(exception_breakpoint) |
| |
| self.update_after_exceptions_added(added) |
| |
| else: |
| sys.stderr.write("Error when setting exception list. Received: %s\n" % (text,)) |
| |
| elif cmd_id == CMD_GET_FILE_CONTENTS: |
| |
| if not IS_PY3K: # In Python 3, the frame object will have unicode for the file, whereas on python 2 it has a byte-array encoded with the filesystem encoding. |
| text = text.encode(file_system_encoding) |
| |
| if os.path.exists(text): |
| f = open(text, 'r') |
| try: |
| source = f.read() |
| finally: |
| f.close() |
| cmd = self.cmdFactory.makeGetFileContents(seq, source) |
| |
| elif cmd_id == CMD_SET_PROPERTY_TRACE: |
| # Command which receives whether to trace property getter/setter/deleter |
| # text is feature_state(true/false);disable_getter/disable_setter/disable_deleter |
| if text != "": |
| splitted = text.split(';') |
| if len(splitted) >= 3: |
| if self.disable_property_trace is False and splitted[0] == 'true': |
| # Replacing property by custom property only when the debugger starts |
| pydevd_traceproperty.replace_builtin_property() |
| self.disable_property_trace = True |
| # Enable/Disable tracing of the property getter |
| if splitted[1] == 'true': |
| self.disable_property_getter_trace = True |
| else: |
| self.disable_property_getter_trace = False |
| # Enable/Disable tracing of the property setter |
| if splitted[2] == 'true': |
| self.disable_property_setter_trace = True |
| else: |
| self.disable_property_setter_trace = False |
| # Enable/Disable tracing of the property deleter |
| if splitted[3] == 'true': |
| self.disable_property_deleter_trace = True |
| else: |
| self.disable_property_deleter_trace = False |
| else: |
| # User hasn't configured any settings for property tracing |
| pass |
| |
| elif cmd_id == CMD_ADD_EXCEPTION_BREAK: |
| exception, notify_always, notify_on_terminate = text.split('\t', 2) |
| exception_breakpoint = self.add_break_on_exception( |
| exception, |
| notify_always=int(notify_always) > 0, |
| notify_on_terminate = int(notify_on_terminate) == 1, |
| notify_on_first_raise_only=int(notify_always) == 2 |
| ) |
| self.update_after_exceptions_added([exception_breakpoint]) |
| |
| elif cmd_id == CMD_REMOVE_EXCEPTION_BREAK: |
| exception = text |
| try: |
| cp = self.break_on_uncaught_exceptions.copy() |
| DictPop(cp, exception, None) |
| self.break_on_uncaught_exceptions = cp |
| |
| cp = self.break_on_caught_exceptions.copy() |
| DictPop(cp, exception, None) |
| self.break_on_caught_exceptions = cp |
| except: |
| pydev_log.debug("Error while removing exception %s"%sys.exc_info()[0]); |
| update_exception_hook(self) |
| |
| elif cmd_id == CMD_LOAD_SOURCE: |
| path = text |
| try: |
| f = open(path, 'r') |
| source = f.read() |
| self.cmdFactory.makeLoadSourceMessage(seq, source, self) |
| except: |
| return self.cmdFactory.makeErrorMessage(seq, pydevd_tracing.GetExceptionTracebackStr()) |
| |
| elif cmd_id == CMD_ADD_DJANGO_EXCEPTION_BREAK: |
| exception = text |
| |
| self.django_exception_break[exception] = True |
| self.setTracingForUntracedContexts() |
| |
| elif cmd_id == CMD_REMOVE_DJANGO_EXCEPTION_BREAK: |
| exception = text |
| |
| try: |
| del self.django_exception_break[exception] |
| except : |
| pass |
| |
| elif cmd_id == CMD_EVALUATE_CONSOLE_EXPRESSION: |
| # Command which takes care for the debug console communication |
| if text != "": |
| thread_id, frame_id, console_command = text.split('\t', 2) |
| console_command, line = console_command.split('\t') |
| if console_command == 'EVALUATE': |
| int_cmd = InternalEvaluateConsoleExpression(seq, thread_id, frame_id, line) |
| elif console_command == 'GET_COMPLETIONS': |
| int_cmd = InternalConsoleGetCompletions(seq, thread_id, frame_id, line) |
| self.postInternalCommand(int_cmd, thread_id) |
| |
| elif cmd_id == CMD_RUN_CUSTOM_OPERATION: |
| # Command which runs a custom operation |
| if text != "": |
| try: |
| location, custom = text.split('||', 1) |
| except: |
| sys.stderr.write('Custom operation now needs a || separator. Found: %s\n' % (text,)) |
| raise |
| |
| thread_id, frame_id, scopeattrs = location.split('\t', 2) |
| |
| if scopeattrs.find('\t') != -1: # there are attributes beyond scope |
| scope, attrs = scopeattrs.split('\t', 1) |
| else: |
| scope, attrs = (scopeattrs, None) |
| |
| # : style: EXECFILE or EXEC |
| # : encoded_code_or_file: file to execute or code |
| # : fname: name of function to be executed in the resulting namespace |
| style, encoded_code_or_file, fnname = custom.split('\t', 3) |
| int_cmd = InternalRunCustomOperation(seq, thread_id, frame_id, scope, attrs, |
| style, encoded_code_or_file, fnname) |
| self.postInternalCommand(int_cmd, thread_id) |
| |
| elif cmd_id == CMD_IGNORE_THROWN_EXCEPTION_AT: |
| if text: |
| replace = 'REPLACE:' # Not all 3.x versions support u'REPLACE:', so, doing workaround. |
| if not IS_PY3K: |
| replace = unicode(replace) |
| |
| if text.startswith(replace): |
| text = text[8:] |
| self.filename_to_lines_where_exceptions_are_ignored.clear() |
| |
| if text: |
| for line in text.split('||'): # Can be bulk-created (one in each line) |
| filename, line_number = line.split('|') |
| if not IS_PY3K: |
| filename = filename.encode(file_system_encoding) |
| |
| filename = NormFileToServer(filename) |
| |
| if os.path.exists(filename): |
| lines_ignored = self.filename_to_lines_where_exceptions_are_ignored.get(filename) |
| if lines_ignored is None: |
| lines_ignored = self.filename_to_lines_where_exceptions_are_ignored[filename] = {} |
| lines_ignored[int(line_number)] = 1 |
| else: |
| sys.stderr.write('pydev debugger: warning: trying to ignore exception thrown'\ |
| ' on file that does not exist: %s (will have no effect)\n' % (filename,)) |
| |
| elif cmd_id == CMD_ENABLE_DONT_TRACE: |
| if text: |
| true_str = 'true' # Not all 3.x versions support u'str', so, doing workaround. |
| if not IS_PY3K: |
| true_str = unicode(true_str) |
| |
| mode = text.strip() == true_str |
| pydevd_dont_trace.trace_filter(mode) |
| |
| else: |
| #I have no idea what this is all about |
| cmd = self.cmdFactory.makeErrorMessage(seq, "unexpected command " + str(cmd_id)) |
| |
| if cmd is not None: |
| self.writer.addCommand(cmd) |
| del cmd |
| |
| except Exception: |
| traceback.print_exc() |
| cmd = self.cmdFactory.makeErrorMessage(seq, |
| "Unexpected exception in processNetCommand.\nInitial params: %s" % ((cmd_id, seq, text),)) |
| |
| self.writer.addCommand(cmd) |
| finally: |
| self._main_lock.release() |
| |
| def processThreadNotAlive(self, threadId): |
| """ if thread is not alive, cancel trace_dispatch processing """ |
| self._lock_running_thread_ids.acquire() |
| try: |
| thread = self._running_thread_ids.pop(threadId, None) |
| if thread is None: |
| return |
| |
| wasNotified = thread.additionalInfo.pydev_notify_kill |
| if not wasNotified: |
| thread.additionalInfo.pydev_notify_kill = True |
| |
| finally: |
| self._lock_running_thread_ids.release() |
| |
| cmd = self.cmdFactory.makeThreadKilledMessage(threadId) |
| self.writer.addCommand(cmd) |
| |
| |
| def setSuspend(self, thread, stop_reason): |
| thread.additionalInfo.suspend_type = PYTHON_SUSPEND |
| thread.additionalInfo.pydev_state = STATE_SUSPEND |
| thread.stop_reason = stop_reason |
| |
| # If conditional breakpoint raises any exception during evaluation send details to Java |
| if stop_reason == CMD_SET_BREAK and self.suspend_on_breakpoint_exception: |
| self.sendBreakpointConditionException(thread) |
| |
| |
| def sendBreakpointConditionException(self, thread): |
| """If conditional breakpoint raises an exception during evaluation |
| send exception details to java |
| """ |
| thread_id = GetThreadId(thread) |
| conditional_breakpoint_exception_tuple = thread.additionalInfo.conditional_breakpoint_exception |
| # conditional_breakpoint_exception_tuple - should contain 2 values (exception_type, stacktrace) |
| if conditional_breakpoint_exception_tuple and len(conditional_breakpoint_exception_tuple) == 2: |
| exc_type, stacktrace = conditional_breakpoint_exception_tuple |
| int_cmd = InternalGetBreakpointException(thread_id, exc_type, stacktrace) |
| # Reset the conditional_breakpoint_exception details to None |
| thread.additionalInfo.conditional_breakpoint_exception = None |
| self.postInternalCommand(int_cmd, thread_id) |
| |
| |
| def sendCaughtExceptionStack(self, thread, arg, curr_frame_id): |
| """Sends details on the exception which was caught (and where we stopped) to the java side. |
| |
| arg is: exception type, description, traceback object |
| """ |
| thread_id = GetThreadId(thread) |
| int_cmd = InternalSendCurrExceptionTrace(thread_id, arg, curr_frame_id) |
| self.postInternalCommand(int_cmd, thread_id) |
| |
| |
| def sendCaughtExceptionStackProceeded(self, thread): |
| """Sends that some thread was resumed and is no longer showing an exception trace. |
| """ |
| thread_id = GetThreadId(thread) |
| int_cmd = InternalSendCurrExceptionTraceProceeded(thread_id) |
| self.postInternalCommand(int_cmd, thread_id) |
| self.processInternalCommands() |
| |
| |
| def doWaitSuspend(self, thread, frame, event, arg): #@UnusedVariable |
| """ busy waits until the thread state changes to RUN |
| it expects thread's state as attributes of the thread. |
| Upon running, processes any outstanding Stepping commands. |
| """ |
| self.processInternalCommands() |
| |
| message = getattr(thread.additionalInfo, "message", None) |
| |
| cmd = self.cmdFactory.makeThreadSuspendMessage(GetThreadId(thread), frame, thread.stop_reason, message) |
| self.writer.addCommand(cmd) |
| |
| CustomFramesContainer.custom_frames_lock.acquire() |
| try: |
| from_this_thread = [] |
| |
| for frame_id, custom_frame in DictIterItems(CustomFramesContainer.custom_frames): |
| if custom_frame.thread_id == thread.ident: |
| # print >> sys.stderr, 'Frame created: ', frame_id |
| self.writer.addCommand(self.cmdFactory.makeCustomFrameCreatedMessage(frame_id, custom_frame.name)) |
| self.writer.addCommand(self.cmdFactory.makeThreadSuspendMessage(frame_id, custom_frame.frame, CMD_THREAD_SUSPEND, "")) |
| |
| from_this_thread.append(frame_id) |
| |
| finally: |
| CustomFramesContainer.custom_frames_lock.release() |
| |
| |
| |
| info = thread.additionalInfo |
| while info.pydev_state == STATE_SUSPEND and not self._finishDebuggingSession: |
| self.processInternalCommands() |
| time.sleep(0.01) |
| |
| # process any stepping instructions |
| if info.pydev_step_cmd == CMD_STEP_INTO: |
| info.pydev_step_stop = None |
| info.pydev_smart_step_stop = None |
| |
| elif info.pydev_step_cmd == CMD_STEP_OVER: |
| info.pydev_step_stop = frame |
| info.pydev_smart_step_stop = None |
| self.SetTraceForFrameAndParents(frame) |
| |
| elif info.pydev_step_cmd == CMD_SMART_STEP_INTO: |
| self.SetTraceForFrameAndParents(frame) |
| info.pydev_step_stop = None |
| info.pydev_smart_step_stop = frame |
| |
| elif info.pydev_step_cmd == CMD_RUN_TO_LINE or info.pydev_step_cmd == CMD_SET_NEXT_STATEMENT : |
| self.SetTraceForFrameAndParents(frame) |
| |
| if event == 'line' or event == 'exception': |
| #If we're already in the correct context, we have to stop it now, because we can act only on |
| #line events -- if a return was the next statement it wouldn't work (so, we have this code |
| #repeated at pydevd_frame). |
| stop = False |
| curr_func_name = frame.f_code.co_name |
| |
| #global context is set with an empty name |
| if curr_func_name in ('?', '<module>'): |
| curr_func_name = '' |
| |
| if curr_func_name == info.pydev_func_name: |
| line = info.pydev_next_line |
| if frame.f_lineno == line: |
| stop = True |
| else : |
| if frame.f_trace is None: |
| frame.f_trace = self.trace_dispatch |
| frame.f_lineno = line |
| frame.f_trace = None |
| stop = True |
| if stop: |
| info.pydev_state = STATE_SUSPEND |
| self.doWaitSuspend(thread, frame, event, arg) |
| return |
| |
| |
| elif info.pydev_step_cmd == CMD_STEP_RETURN: |
| back_frame = frame.f_back |
| if back_frame is not None: |
| # steps back to the same frame (in a return call it will stop in the 'back frame' for the user) |
| info.pydev_step_stop = frame |
| self.SetTraceForFrameAndParents(frame) |
| else: |
| # No back frame?!? -- this happens in jython when we have some frame created from an awt event |
| # (the previous frame would be the awt event, but this doesn't make part of 'jython', only 'java') |
| # so, if we're doing a step return in this situation, it's the same as just making it run |
| info.pydev_step_stop = None |
| info.pydev_step_cmd = None |
| info.pydev_state = STATE_RUN |
| |
| del frame |
| cmd = self.cmdFactory.makeThreadRunMessage(GetThreadId(thread), info.pydev_step_cmd) |
| self.writer.addCommand(cmd) |
| |
| CustomFramesContainer.custom_frames_lock.acquire() |
| try: |
| # The ones that remained on last_running must now be removed. |
| for frame_id in from_this_thread: |
| # print >> sys.stderr, 'Removing created frame: ', frame_id |
| self.writer.addCommand(self.cmdFactory.makeThreadKilledMessage(frame_id)) |
| |
| finally: |
| CustomFramesContainer.custom_frames_lock.release() |
| |
| def handle_post_mortem_stop(self, additionalInfo, t): |
| pydev_log.debug("We are stopping in post-mortem\n") |
| frame, frames_byid = additionalInfo.pydev_force_stop_at_exception |
| thread_id = GetThreadId(t) |
| pydevd_vars.addAdditionalFrameById(thread_id, frames_byid) |
| try: |
| try: |
| add_exception_to_frame(frame, additionalInfo.exception) |
| self.setSuspend(t, CMD_ADD_EXCEPTION_BREAK) |
| self.doWaitSuspend(t, frame, 'exception', None) |
| except: |
| pydev_log.error("We've got an error while stopping in post-mortem: %s\n"%sys.exc_info()[0]) |
| finally: |
| additionalInfo.pydev_force_stop_at_exception = None |
| pydevd_vars.removeAdditionalFrameById(thread_id) |
| |
| def trace_dispatch(self, frame, event, arg): |
| ''' This is the callback used when we enter some context in the debugger. |
| |
| We also decorate the thread we are in with info about the debugging. |
| The attributes added are: |
| pydev_state |
| pydev_step_stop |
| pydev_step_cmd |
| pydev_notify_kill |
| ''' |
| try: |
| if self._finishDebuggingSession and not self._terminationEventSent: |
| #that was not working very well because jython gave some socket errors |
| try: |
| threads = DictKeys(PyDBDaemonThread.created_pydb_daemon_threads) |
| for t in threads: |
| if hasattr(t, 'doKillPydevThread'): |
| t.doKillPydevThread() |
| except: |
| traceback.print_exc() |
| self._terminationEventSent = True |
| return None |
| |
| filename, base = GetFilenameAndBase(frame) |
| |
| is_file_to_ignore = DictContains(DONT_TRACE, base) #we don't want to debug threading or anything related to pydevd |
| |
| #print('trace_dispatch', base, frame.f_lineno, event, frame.f_code.co_name, is_file_to_ignore) |
| if is_file_to_ignore: |
| return None |
| |
| try: |
| #this shouldn't give an exception, but it could happen... (python bug) |
| #see http://mail.python.org/pipermail/python-bugs-list/2007-June/038796.html |
| #and related bug: http://bugs.python.org/issue1733757 |
| t = threadingCurrentThread() |
| except: |
| frame.f_trace = self.trace_dispatch |
| return self.trace_dispatch |
| |
| try: |
| additionalInfo = t.additionalInfo |
| if additionalInfo is None: |
| raise AttributeError() |
| except: |
| t.additionalInfo = PyDBAdditionalThreadInfo() |
| additionalInfo = t.additionalInfo |
| |
| if additionalInfo is None: |
| return None |
| |
| if additionalInfo.is_tracing: |
| f = frame |
| while f is not None: |
| if 'trace_dispatch' == f.f_code.co_name: |
| _fname, bs = GetFilenameAndBase(f) |
| if bs == 'pydevd_frame.py': |
| return None #we don't wan't to trace code invoked from pydevd_frame.trace_dispatch |
| f = f.f_back |
| |
| # if thread is not alive, cancel trace_dispatch processing |
| if not isThreadAlive(t): |
| self.processThreadNotAlive(GetThreadId(t)) |
| return None # suspend tracing |
| |
| # each new frame... |
| return additionalInfo.CreateDbFrame((self, filename, additionalInfo, t, frame)).trace_dispatch(frame, event, arg) |
| |
| except SystemExit: |
| return None |
| |
| except Exception: |
| # Log it |
| try: |
| if traceback is not None: |
| # This can actually happen during the interpreter shutdown in Python 2.7 |
| traceback.print_exc() |
| except: |
| # Error logging? We're really in the interpreter shutdown... |
| # (https://github.com/fabioz/PyDev.Debugger/issues/8) |
| pass |
| return None |
| |
| if USE_PSYCO_OPTIMIZATION: |
| try: |
| import psyco |
| trace_dispatch = psyco.proxy(trace_dispatch) |
| processNetCommand = psyco.proxy(processNetCommand) |
| processInternalCommands = psyco.proxy(processInternalCommands) |
| doWaitSuspend = psyco.proxy(doWaitSuspend) |
| getInternalQueue = psyco.proxy(getInternalQueue) |
| except ImportError: |
| if hasattr(sys, 'exc_clear'): # jython does not have it |
| sys.exc_clear() # don't keep the traceback (let's keep it clear for when we go to the point of executing client code) |
| |
| if not IS_PY3K and not IS_PY27 and not IS_64_BITS and not sys.platform.startswith("java") and not sys.platform.startswith("cli"): |
| sys.stderr.write("pydev debugger: warning: psyco not available for speedups (the debugger will still work correctly, but a bit slower)\n") |
| sys.stderr.flush() |
| |
| |
| |
| def SetTraceForFrameAndParents(self, frame, also_add_to_passed_frame=True, overwrite_prev_trace=False, dispatch_func=None): |
| if dispatch_func is None: |
| dispatch_func = self.trace_dispatch |
| |
| if also_add_to_passed_frame: |
| self.update_trace(frame, dispatch_func, overwrite_prev_trace) |
| |
| frame = frame.f_back |
| while frame: |
| self.update_trace(frame, dispatch_func, overwrite_prev_trace) |
| |
| frame = frame.f_back |
| del frame |
| |
| def update_trace(self, frame, dispatch_func, overwrite_prev): |
| if frame.f_trace is None: |
| frame.f_trace = dispatch_func |
| else: |
| if overwrite_prev: |
| frame.f_trace = dispatch_func |
| else: |
| try: |
| #If it's the trace_exception, go back to the frame trace dispatch! |
| if frame.f_trace.im_func.__name__ == 'trace_exception': |
| frame.f_trace = frame.f_trace.im_self.trace_dispatch |
| except AttributeError: |
| pass |
| frame = frame.f_back |
| del frame |
| |
| def prepareToRun(self): |
| ''' Shared code to prepare debugging by installing traces and registering threads ''' |
| self.patch_threads() |
| pydevd_tracing.SetTrace(self.trace_dispatch) |
| |
| |
| PyDBCommandThread(self).start() |
| PyDBCheckAliveThread(self).start() |
| |
| |
| def patch_threads(self): |
| try: |
| # not available in jython! |
| threading.settrace(self.trace_dispatch) # for all future threads |
| except: |
| pass |
| |
| from pydev_monkey import patch_thread_modules |
| patch_thread_modules() |
| |
| |
| def run(self, file, globals=None, locals=None, set_trace=True): |
| if os.path.isdir(file): |
| new_target = os.path.join(file, '__main__.py') |
| if os.path.isfile(new_target): |
| file = new_target |
| |
| if globals is None: |
| # patch provided by: Scott Schlesier - when script is run, it does not |
| # use globals from pydevd: |
| # This will prevent the pydevd script from contaminating the namespace for the script to be debugged |
| |
| # pretend pydevd is not the main module, and |
| # convince the file to be debugged that it was loaded as main |
| sys.modules['pydevd'] = sys.modules['__main__'] |
| sys.modules['pydevd'].__name__ = 'pydevd' |
| |
| from imp import new_module |
| m = new_module('__main__') |
| sys.modules['__main__'] = m |
| if hasattr(sys.modules['pydevd'], '__loader__'): |
| setattr(m, '__loader__', getattr(sys.modules['pydevd'], '__loader__')) |
| |
| m.__file__ = file |
| globals = m.__dict__ |
| try: |
| globals['__builtins__'] = __builtins__ |
| except NameError: |
| pass # Not there on Jython... |
| |
| if locals is None: |
| locals = globals |
| |
| if set_trace: |
| # Predefined (writable) attributes: __name__ is the module's name; |
| # __doc__ is the module's documentation string, or None if unavailable; |
| # __file__ is the pathname of the file from which the module was loaded, |
| # if it was loaded from a file. The __file__ attribute is not present for |
| # C modules that are statically linked into the interpreter; for extension modules |
| # loaded dynamically from a shared library, it is the pathname of the shared library file. |
| |
| |
| # I think this is an ugly hack, bug it works (seems to) for the bug that says that sys.path should be the same in |
| # debug and run. |
| if m.__file__.startswith(sys.path[0]): |
| # print >> sys.stderr, 'Deleting: ', sys.path[0] |
| del sys.path[0] |
| |
| # now, the local directory has to be added to the pythonpath |
| # sys.path.insert(0, os.getcwd()) |
| # Changed: it's not the local directory, but the directory of the file launched |
| # The file being run ust be in the pythonpath (even if it was not before) |
| sys.path.insert(0, os.path.split(file)[0]) |
| |
| self.prepareToRun() |
| |
| while not self.readyToRun: |
| time.sleep(0.1) # busy wait until we receive run command |
| |
| |
| pydev_imports.execfile(file, globals, locals) # execute the script |
| |
| return globals |
| |
| def exiting(self): |
| sys.stdout.flush() |
| sys.stderr.flush() |
| self.checkOutputRedirect() |
| cmd = self.cmdFactory.makeExitMessage() |
| self.writer.addCommand(cmd) |
| |
| def wait_for_commands(self, globals): |
| thread = threading.currentThread() |
| import pydevd_frame_utils |
| frame = pydevd_frame_utils.Frame(None, -1, pydevd_frame_utils.FCode("Console", |
| os.path.abspath(os.path.dirname(__file__))), globals, globals) |
| thread_id = GetThreadId(thread) |
| import pydevd_vars |
| pydevd_vars.addAdditionalFrameById(thread_id, {id(frame): frame}) |
| |
| cmd = self.cmdFactory.makeShowConsoleMessage(thread_id, frame) |
| self.writer.addCommand(cmd) |
| |
| while True: |
| self.processInternalCommands() |
| time.sleep(0.01) |
| |
| def set_debug(setup): |
| setup['DEBUG_RECORD_SOCKET_READS'] = True |
| setup['DEBUG_TRACE_BREAKPOINTS'] = 1 |
| setup['DEBUG_TRACE_LEVEL'] = 3 |
| |
| |
| def processCommandLine(argv): |
| """ parses the arguments. |
| removes our arguments from the command line """ |
| setup = {} |
| setup['client'] = '' |
| setup['server'] = False |
| setup['port'] = 0 |
| setup['file'] = '' |
| setup['multiproc'] = False #Used by PyCharm (reuses connection: ssh tunneling) |
| setup['multiprocess'] = False # Used by PyDev (creates new connection to ide) |
| setup['save-signatures'] = False |
| setup['print-in-debugger-startup'] = False |
| setup['cmd-line'] = False |
| i = 0 |
| del argv[0] |
| while (i < len(argv)): |
| if argv[i] == '--port': |
| del argv[i] |
| setup['port'] = int(argv[i]) |
| del argv[i] |
| elif argv[i] == '--vm_type': |
| del argv[i] |
| setup['vm_type'] = argv[i] |
| del argv[i] |
| elif argv[i] == '--client': |
| del argv[i] |
| setup['client'] = argv[i] |
| del argv[i] |
| elif argv[i] == '--server': |
| del argv[i] |
| setup['server'] = True |
| elif argv[i] == '--file': |
| del argv[i] |
| setup['file'] = argv[i] |
| i = len(argv) # pop out, file is our last argument |
| elif argv[i] == '--DEBUG_RECORD_SOCKET_READS': |
| del argv[i] |
| setup['DEBUG_RECORD_SOCKET_READS'] = True |
| elif argv[i] == '--DEBUG': |
| del argv[i] |
| set_debug(setup) |
| elif argv[i] == '--multiproc': |
| del argv[i] |
| setup['multiproc'] = True |
| elif argv[i] == '--multiprocess': |
| del argv[i] |
| setup['multiprocess'] = True |
| elif argv[i] == '--save-signatures': |
| del argv[i] |
| setup['save-signatures'] = True |
| elif argv[i] == '--print-in-debugger-startup': |
| del argv[i] |
| setup['print-in-debugger-startup'] = True |
| elif (argv[i] == '--cmd-line'): |
| del argv[i] |
| setup['cmd-line'] = True |
| else: |
| raise ValueError("unexpected option " + argv[i]) |
| return setup |
| |
| def usage(doExit=0): |
| sys.stdout.write('Usage:\n') |
| sys.stdout.write('pydevd.py --port=N [(--client hostname) | --server] --file executable [file_options]\n') |
| if doExit: |
| sys.exit(0) |
| |
| |
| def initStdoutRedirect(): |
| if not getattr(sys, 'stdoutBuf', None): |
| sys.stdoutBuf = pydevd_io.IOBuf() |
| sys.stdout = pydevd_io.IORedirector(sys.stdout, sys.stdoutBuf) #@UndefinedVariable |
| |
| def initStderrRedirect(): |
| if not getattr(sys, 'stderrBuf', None): |
| sys.stderrBuf = pydevd_io.IOBuf() |
| sys.stderr = pydevd_io.IORedirector(sys.stderr, sys.stderrBuf) #@UndefinedVariable |
| |
| #======================================================================================================================= |
| # settrace |
| #======================================================================================================================= |
| def settrace( |
| host=None, |
| stdoutToServer=False, |
| stderrToServer=False, |
| port=5678, |
| suspend=True, |
| trace_only_current_thread=False, |
| overwrite_prev_trace=False, |
| patch_multiprocessing=False, |
| ): |
| '''Sets the tracing function with the pydev debug function and initializes needed facilities. |
| |
| @param host: the user may specify another host, if the debug server is not in the same machine (default is the local |
| host) |
| |
| @param stdoutToServer: when this is true, the stdout is passed to the debug server |
| |
| @param stderrToServer: when this is true, the stderr is passed to the debug server |
| so that they are printed in its console and not in this process console. |
| |
| @param port: specifies which port to use for communicating with the server (note that the server must be started |
| in the same port). @note: currently it's hard-coded at 5678 in the client |
| |
| @param suspend: whether a breakpoint should be emulated as soon as this function is called. |
| |
| @param trace_only_current_thread: determines if only the current thread will be traced or all current and future |
| threads will also have the tracing enabled. |
| |
| @param overwrite_prev_trace: if True we'll reset the frame.f_trace of frames which are already being traced |
| |
| @param patch_multiprocessing: if True we'll patch the functions which create new processes so that launched |
| processes are debugged. |
| ''' |
| _set_trace_lock.acquire() |
| try: |
| _locked_settrace( |
| host, |
| stdoutToServer, |
| stderrToServer, |
| port, |
| suspend, |
| trace_only_current_thread, |
| overwrite_prev_trace, |
| patch_multiprocessing, |
| ) |
| finally: |
| _set_trace_lock.release() |
| |
| |
| |
| _set_trace_lock = _pydev_thread.allocate_lock() |
| |
| def _locked_settrace( |
| host, |
| stdoutToServer, |
| stderrToServer, |
| port, |
| suspend, |
| trace_only_current_thread, |
| overwrite_prev_trace, |
| patch_multiprocessing, |
| ): |
| if patch_multiprocessing: |
| try: |
| import pydev_monkey #Jython 2.1 can't use it... |
| except: |
| pass |
| else: |
| pydev_monkey.patch_new_process_functions() |
| |
| if host is None: |
| import pydev_localhost |
| host = pydev_localhost.get_localhost() |
| |
| global connected |
| global bufferStdOutToServer |
| global bufferStdErrToServer |
| |
| if not connected : |
| pydevd_vm_type.SetupType() |
| |
| debugger = PyDB() |
| debugger.connect(host, port) # Note: connect can raise error. |
| |
| # Mark connected only if it actually succeeded. |
| connected = True |
| bufferStdOutToServer = stdoutToServer |
| bufferStdErrToServer = stderrToServer |
| |
| if bufferStdOutToServer: |
| initStdoutRedirect() |
| |
| if bufferStdErrToServer: |
| initStderrRedirect() |
| |
| debugger.SetTraceForFrameAndParents(GetFrame(), False, overwrite_prev_trace=overwrite_prev_trace) |
| |
| |
| CustomFramesContainer.custom_frames_lock.acquire() |
| try: |
| for _frameId, custom_frame in DictIterItems(CustomFramesContainer.custom_frames): |
| debugger.SetTraceForFrameAndParents(custom_frame.frame, False) |
| finally: |
| CustomFramesContainer.custom_frames_lock.release() |
| |
| |
| t = threadingCurrentThread() |
| try: |
| additionalInfo = t.additionalInfo |
| except AttributeError: |
| additionalInfo = PyDBAdditionalThreadInfo() |
| t.additionalInfo = additionalInfo |
| |
| while not debugger.readyToRun: |
| time.sleep(0.1) # busy wait until we receive run command |
| |
| # note that we do that through pydevd_tracing.SetTrace so that the tracing |
| # is not warned to the user! |
| pydevd_tracing.SetTrace(debugger.trace_dispatch) |
| |
| if not trace_only_current_thread: |
| # Trace future threads? |
| debugger.patch_threads() |
| |
| # As this is the first connection, also set tracing for any untraced threads |
| debugger.setTracingForUntracedContexts(ignore_frame=GetFrame(), overwrite_prev_trace=overwrite_prev_trace) |
| |
| # Stop the tracing as the last thing before the actual shutdown for a clean exit. |
| atexit.register(stoptrace) |
| |
| #Suspend as the last thing after all tracing is in place. |
| if suspend: |
| debugger.setSuspend(t, CMD_SET_BREAK) |
| |
| PyDBCommandThread(debugger).start() |
| PyDBCheckAliveThread(debugger).start() |
| |
| else: |
| # ok, we're already in debug mode, with all set, so, let's just set the break |
| debugger = GetGlobalDebugger() |
| |
| debugger.SetTraceForFrameAndParents(GetFrame(), False) |
| |
| t = threadingCurrentThread() |
| try: |
| additionalInfo = t.additionalInfo |
| except AttributeError: |
| additionalInfo = PyDBAdditionalThreadInfo() |
| t.additionalInfo = additionalInfo |
| |
| pydevd_tracing.SetTrace(debugger.trace_dispatch) |
| |
| if not trace_only_current_thread: |
| # Trace future threads? |
| debugger.patch_threads() |
| |
| |
| if suspend: |
| debugger.setSuspend(t, CMD_SET_BREAK) |
| |
| |
| def stoptrace(): |
| global connected |
| if connected: |
| pydevd_tracing.RestoreSysSetTraceFunc() |
| sys.settrace(None) |
| try: |
| #not available in jython! |
| threading.settrace(None) # for all future threads |
| except: |
| pass |
| |
| from pydev_monkey import undo_patch_thread_modules |
| undo_patch_thread_modules() |
| |
| debugger = GetGlobalDebugger() |
| |
| if debugger: |
| |
| debugger.SetTraceForFrameAndParents( |
| GetFrame(), also_add_to_passed_frame=True, overwrite_prev_trace=True, dispatch_func=lambda *args:None) |
| debugger.exiting() |
| |
| killAllPydevThreads() |
| |
| connected = False |
| |
| class Dispatcher(object): |
| def __init__(self): |
| self.port = None |
| |
| def connect(self, host, port): |
| self.host = host |
| self.port = port |
| self.client = StartClient(self.host, self.port) |
| self.reader = DispatchReader(self) |
| self.reader.dontTraceMe = False #we run reader in the same thread so we don't want to loose tracing |
| self.reader.run() |
| |
| def close(self): |
| try: |
| self.reader.doKillPydevThread() |
| except : |
| pass |
| |
| class DispatchReader(ReaderThread): |
| def __init__(self, dispatcher): |
| self.dispatcher = dispatcher |
| ReaderThread.__init__(self, self.dispatcher.client) |
| |
| def OnRun(self): |
| dummy_thread = threading.currentThread() |
| dummy_thread.is_pydev_daemon_thread = False |
| return ReaderThread.OnRun(self) |
| |
| def handleExcept(self): |
| ReaderThread.handleExcept(self) |
| |
| def processCommand(self, cmd_id, seq, text): |
| if cmd_id == 99: |
| self.dispatcher.port = int(text) |
| self.killReceived = True |
| |
| |
| DISPATCH_APPROACH_NEW_CONNECTION = 1 # Used by PyDev |
| DISPATCH_APPROACH_EXISTING_CONNECTION = 2 # Used by PyCharm |
| DISPATCH_APPROACH = DISPATCH_APPROACH_NEW_CONNECTION |
| |
| def dispatch(): |
| setup = SetupHolder.setup |
| host = setup['client'] |
| port = setup['port'] |
| if DISPATCH_APPROACH == DISPATCH_APPROACH_EXISTING_CONNECTION: |
| dispatcher = Dispatcher() |
| try: |
| dispatcher.connect(host, port) |
| port = dispatcher.port |
| finally: |
| dispatcher.close() |
| return host, port |
| |
| |
| def settrace_forked(): |
| ''' |
| When creating a fork from a process in the debugger, we need to reset the whole debugger environment! |
| ''' |
| host, port = dispatch() |
| |
| import pydevd_tracing |
| pydevd_tracing.RestoreSysSetTraceFunc() |
| |
| if port is not None: |
| global connected |
| connected = False |
| |
| CustomFramesContainerInit() |
| |
| settrace( |
| host, |
| port=port, |
| suspend=False, |
| trace_only_current_thread=False, |
| overwrite_prev_trace=True, |
| patch_multiprocessing=True, |
| ) |
| |
| #======================================================================================================================= |
| # SetupHolder |
| #======================================================================================================================= |
| class SetupHolder: |
| |
| setup = None |
| |
| |
| #======================================================================================================================= |
| # main |
| #======================================================================================================================= |
| if __name__ == '__main__': |
| |
| # parse the command line. --file is our last argument that is required |
| try: |
| sys.original_argv = sys.argv[:] |
| setup = processCommandLine(sys.argv) |
| SetupHolder.setup = setup |
| except ValueError: |
| traceback.print_exc() |
| usage(1) |
| |
| if setup['print-in-debugger-startup']: |
| try: |
| pid = ' (pid: %s)' % os.getpid() |
| except: |
| pid = '' |
| sys.stderr.write("pydev debugger: starting%s\n" % pid) |
| |
| fix_getpass.fixGetpass() |
| |
| pydev_log.debug("Executing file %s" % setup['file']) |
| pydev_log.debug("arguments: %s"% str(sys.argv)) |
| |
| |
| pydevd_vm_type.SetupType(setup.get('vm_type', None)) |
| |
| if os.getenv('PYCHARM_DEBUG'): |
| set_debug(setup) |
| |
| DebugInfoHolder.DEBUG_RECORD_SOCKET_READS = setup.get('DEBUG_RECORD_SOCKET_READS', False) |
| DebugInfoHolder.DEBUG_TRACE_BREAKPOINTS = setup.get('DEBUG_TRACE_BREAKPOINTS', -1) |
| DebugInfoHolder.DEBUG_TRACE_LEVEL = setup.get('DEBUG_TRACE_LEVEL', -1) |
| |
| port = setup['port'] |
| host = setup['client'] |
| f = setup['file'] |
| fix_app_engine_debug = False |
| |
| |
| try: |
| import pydev_monkey |
| except: |
| pass #Not usable on jython 2.1 |
| else: |
| if setup['multiprocess']: # PyDev |
| pydev_monkey.patch_new_process_functions() |
| |
| elif setup['multiproc']: # PyCharm |
| pydev_log.debug("Started in multiproc mode\n") |
| # Note: we're not inside method, so, no need for 'global' |
| DISPATCH_APPROACH = DISPATCH_APPROACH_EXISTING_CONNECTION |
| |
| dispatcher = Dispatcher() |
| try: |
| dispatcher.connect(host, port) |
| if dispatcher.port is not None: |
| port = dispatcher.port |
| pydev_log.debug("Received port %d\n" %port) |
| pydev_log.info("pydev debugger: process %d is connecting\n"% os.getpid()) |
| |
| try: |
| pydev_monkey.patch_new_process_functions() |
| except: |
| pydev_log.error("Error patching process functions\n") |
| traceback.print_exc() |
| else: |
| pydev_log.error("pydev debugger: couldn't get port for new debug process\n") |
| finally: |
| dispatcher.close() |
| else: |
| pydev_log.info("pydev debugger: starting\n") |
| |
| try: |
| pydev_monkey.patch_new_process_functions_with_warning() |
| except: |
| pydev_log.error("Error patching process functions\n") |
| traceback.print_exc() |
| |
| # Only do this patching if we're not running with multiprocess turned on. |
| if f.find('dev_appserver.py') != -1: |
| if os.path.basename(f).startswith('dev_appserver.py'): |
| appserver_dir = os.path.dirname(f) |
| version_file = os.path.join(appserver_dir, 'VERSION') |
| if os.path.exists(version_file): |
| try: |
| stream = open(version_file, 'r') |
| try: |
| for line in stream.read().splitlines(): |
| line = line.strip() |
| if line.startswith('release:'): |
| line = line[8:].strip() |
| version = line.replace('"', '') |
| version = version.split('.') |
| if int(version[0]) > 1: |
| fix_app_engine_debug = True |
| |
| elif int(version[0]) == 1: |
| if int(version[1]) >= 7: |
| # Only fix from 1.7 onwards |
| fix_app_engine_debug = True |
| break |
| finally: |
| stream.close() |
| except: |
| traceback.print_exc() |
| |
| try: |
| # In the default run (i.e.: run directly on debug mode), we try to patch stackless as soon as possible |
| # on a run where we have a remote debug, we may have to be more careful because patching stackless means |
| # that if the user already had a stackless.set_schedule_callback installed, he'd loose it and would need |
| # to call it again (because stackless provides no way of getting the last function which was registered |
| # in set_schedule_callback). |
| # |
| # So, ideally, if there's an application using stackless and the application wants to use the remote debugger |
| # and benefit from stackless debugging, the application itself must call: |
| # |
| # import pydevd_stackless |
| # pydevd_stackless.patch_stackless() |
| # |
| # itself to be able to benefit from seeing the tasklets created before the remote debugger is attached. |
| import pydevd_stackless |
| pydevd_stackless.patch_stackless() |
| except: |
| pass # It's ok not having stackless there... |
| |
| debugger = PyDB() |
| |
| if setup['cmd-line']: |
| debugger.cmd_line = True |
| |
| |
| if fix_app_engine_debug: |
| sys.stderr.write("pydev debugger: google app engine integration enabled\n") |
| curr_dir = os.path.dirname(__file__) |
| app_engine_startup_file = os.path.join(curr_dir, 'pydev_app_engine_debug_startup.py') |
| |
| sys.argv.insert(1, '--python_startup_script=' + app_engine_startup_file) |
| import json |
| setup['pydevd'] = __file__ |
| sys.argv.insert(2, '--python_startup_args=%s' % json.dumps(setup),) |
| sys.argv.insert(3, '--automatic_restart=no') |
| sys.argv.insert(4, '--max_module_instances=1') |
| |
| # Run the dev_appserver |
| debugger.run(setup['file'], None, None, set_trace=False) |
| else: |
| # as to get here all our imports are already resolved, the psyco module can be |
| # changed and we'll still get the speedups in the debugger, as those functions |
| # are already compiled at this time. |
| try: |
| import psyco |
| except ImportError: |
| if hasattr(sys, 'exc_clear'): # jython does not have it |
| sys.exc_clear() # don't keep the traceback -- clients don't want to see it |
| pass # that's ok, no need to mock psyco if it's not available anyways |
| else: |
| # if it's available, let's change it for a stub (pydev already made use of it) |
| import pydevd_psyco_stub |
| sys.modules['psyco'] = pydevd_psyco_stub |
| |
| if setup['save-signatures']: |
| if pydevd_vm_type.GetVmType() == pydevd_vm_type.PydevdVmType.JYTHON: |
| sys.stderr.write("Collecting run-time type information is not supported for Jython\n") |
| else: |
| # Only import it if we're going to use it! |
| from pydevd_signature import SignatureFactory |
| debugger.signature_factory = SignatureFactory() |
| |
| try: |
| debugger.connect(host, port) |
| except: |
| sys.stderr.write("Could not connect to %s: %s\n" % (host, port)) |
| traceback.print_exc() |
| sys.exit(1) |
| |
| connected = True # Mark that we're connected when started from inside ide. |
| |
| globals = debugger.run(setup['file'], None, None) |
| |
| if setup['cmd-line']: |
| debugger.wait_for_commands(globals) |
| |
| |