def __init__(self, sock): from ptvsd.adapter import sessions self.server = None """The Server component, if this debug server belongs to Session. """ self.pid = None stream = messaging.JsonIOStream.from_socket(sock, str(self)) self.channel = messaging.JsonMessageChannel(stream, self) self.channel.start() try: info = self.channel.request("pydevdSystemInfo") process_info = info("process", json.object()) self.pid = process_info("pid", int) self.ppid = process_info("ppid", int, optional=True) if self.ppid == (): self.ppid = None self.channel.name = stream.name = str(self) with _lock: if any(conn.pid == self.pid for conn in _connections): raise KeyError( fmt("{0} is already connected to this adapter", self)) _connections.append(self) _connections_changed.set() except Exception: log.exception("Failed to accept incoming server connection:") # If we couldn't retrieve all the necessary info from the debug server, # or there's a PID clash, we don't want to track this debuggee anymore, # but we want to continue accepting connections. self.channel.close() return parent_session = sessions.get(self.ppid) if parent_session is None: log.info("No active debug session for parent process of {0}.", self) else: try: parent_session.ide.notify_of_subprocess(self) except Exception: # This might fail if the IDE concurrently disconnects from the parent # session. We still want to keep the connection around, in case the # IDE reconnects later. If the parent session was "launch", it'll take # care of closing the remaining server connections. log.exception("Failed to notify parent session about {0}:", self)
def __init__(self, sock): from ptvsd.adapter import sessions self.disconnected = False self.server = None """The Server component, if this debug server belongs to Session. """ self.pid = None stream = messaging.JsonIOStream.from_socket(sock, str(self)) self.channel = messaging.JsonMessageChannel(stream, self) self.channel.start() try: self.authenticate() info = self.channel.request("pydevdSystemInfo") process_info = info("process", json.object()) self.pid = process_info("pid", int) self.ppid = process_info("ppid", int, optional=True) if self.ppid == (): self.ppid = None self.channel.name = stream.name = str(self) ptvsd_dir = os.path.dirname(os.path.dirname(ptvsd.__file__)) # Note: we must check if 'ptvsd' is not already in sys.modules because the # evaluation of an import at the wrong time could deadlock Python due to # its import lock. # # So, in general this evaluation shouldn't do anything. It's only # important when pydevd attaches automatically to a subprocess. In this # case, we have to make sure that ptvsd is properly put back in the game # for users to be able to use it.v # # In this case (when the import is needed), this evaluation *must* be done # before the configurationDone request is sent -- if this is not respected # it's possible that pydevd already started secondary threads to handle # commands, in which case it's very likely that this command would be # evaluated at the wrong thread and the import could potentially deadlock # the program. # # Note 2: the sys module is guaranteed to be in the frame globals and # doesn't need to be imported. inject_ptvsd = """ if 'ptvsd' not in sys.modules: sys.path.insert(0, {ptvsd_dir!r}) try: import ptvsd finally: del sys.path[0] """ inject_ptvsd = fmt(inject_ptvsd, ptvsd_dir=ptvsd_dir) try: self.channel.request("evaluate", {"expression": inject_ptvsd}) except messaging.MessageHandlingError: # Failure to inject is not a fatal error - such a subprocess can # still be debugged, it just won't support "import ptvsd" in user # code - so don't terminate the session. log.exception("Failed to inject ptvsd into {0}:", self, level="warning") with _lock: # The server can disconnect concurrently before we get here, e.g. if # it was force-killed. If the disconnect() handler has already run, # don't register this server or report it, since there's nothing to # deregister it. if self.disconnected: return if any(conn.pid == self.pid for conn in _connections): raise KeyError( fmt("{0} is already connected to this adapter", self)) _connections.append(self) _connections_changed.set() except Exception: log.exception("Failed to accept incoming server connection:") self.channel.close() # If this was the first server to connect, and the main thread is inside # wait_until_disconnected(), we want to unblock it and allow it to exit. dont_wait_for_first_connection() # If we couldn't retrieve all the necessary info from the debug server, # or there's a PID clash, we don't want to track this debuggee anymore, # but we want to continue accepting connections. return parent_session = sessions.get(self.ppid) if parent_session is None: log.info("No active debug session for parent process of {0}.", self) else: try: parent_session.ide.notify_of_subprocess(self) except Exception: # This might fail if the IDE concurrently disconnects from the parent # session. We still want to keep the connection around, in case the # IDE reconnects later. If the parent session was "launch", it'll take # care of closing the remaining server connections. log.exception("Failed to notify parent session about {0}:", self)