def listen(address, settrace_kwargs): # Errors below are logged with level="info", because the caller might be catching # and handling exceptions, and we don't want to spam their stderr unnecessarily. import subprocess server_access_token = compat.force_str(codecs.encode(os.urandom(32), "hex")) try: endpoints_listener = sockets.create_server("127.0.0.1", 0, timeout=10) except Exception as exc: log.swallow_exception("Can't listen for adapter endpoints:") raise RuntimeError("can't listen for adapter endpoints: " + str(exc)) endpoints_host, endpoints_port = endpoints_listener.getsockname() log.info( "Waiting for adapter endpoints on {0}:{1}...", endpoints_host, endpoints_port ) host, port = address adapter_args = [ sys.executable, os.path.dirname(adapter.__file__), "--for-server", str(endpoints_port), "--host", host, "--port", str(port), "--server-access-token", server_access_token, ] if log.log_dir is not None: adapter_args += ["--log-dir", log.log_dir] log.info("debugpy.listen() spawning adapter: {0!j}", adapter_args) # On Windows, detach the adapter from our console, if any, so that it doesn't # receive Ctrl+C from it, and doesn't keep it open once we exit. creationflags = 0 if sys.platform == "win32": creationflags |= 0x08000000 # CREATE_NO_WINDOW creationflags |= 0x00000200 # CREATE_NEW_PROCESS_GROUP # Adapter will outlive this process, so we shouldn't wait for it. However, we # need to ensure that the Popen instance for it doesn't get garbage-collected # by holding a reference to it in a non-local variable, to avoid triggering # https://bugs.python.org/issue37380. try: global _adapter_process _adapter_process = subprocess.Popen( adapter_args, close_fds=True, creationflags=creationflags ) if os.name == "posix": # It's going to fork again to daemonize, so we need to wait on it to # clean it up properly. _adapter_process.wait() else: # Suppress misleading warning about child process still being alive when # this process exits (https://bugs.python.org/issue38890). _adapter_process.returncode = 0 pydevd.add_dont_terminate_child_pid(_adapter_process.pid) except Exception as exc: log.swallow_exception("Error spawning debug adapter:", level="info") raise RuntimeError("error spawning debug adapter: " + str(exc)) try: sock, _ = endpoints_listener.accept() try: sock.settimeout(None) sock_io = sock.makefile("rb", 0) try: endpoints = json.loads(sock_io.read().decode("utf-8")) finally: sock_io.close() finally: sockets.close_socket(sock) except socket.timeout: log.swallow_exception("Timed out waiting for adapter to connect:", level="info") raise RuntimeError("timed out waiting for adapter to connect") except Exception as exc: log.swallow_exception("Error retrieving adapter endpoints:", level="info") raise RuntimeError("error retrieving adapter endpoints: " + str(exc)) log.info("Endpoints received from adapter: {0!j}", endpoints) if "error" in endpoints: raise RuntimeError(str(endpoints["error"])) try: server_host = str(endpoints["server"]["host"]) server_port = int(endpoints["server"]["port"]) client_host = str(endpoints["client"]["host"]) client_port = int(endpoints["client"]["port"]) except Exception as exc: log.swallow_exception( "Error parsing adapter endpoints:\n{0!j}\n", endpoints, level="info" ) raise RuntimeError("error parsing adapter endpoints: " + str(exc)) log.info( "Adapter is accepting incoming client connections on {0}:{1}", client_host, client_port, ) _settrace( host=server_host, port=server_port, wait_for_ready_to_run=False, block_until_connected=True, access_token=server_access_token, **settrace_kwargs ) log.info("pydevd is connected to adapter at {0}:{1}", server_host, server_port) return client_host, client_port
if hasattr(os, 'getppid'): print('%screated %s (child of %s)' % ('\t' * (4 - n), os.getpid(), os.getppid())) else: print('%screated %s' % ('\t' * (4 - n), os.getpid())) elif 'check-subprocesses' in sys.argv or 'check-subprocesses-ignore-pid' in sys.argv: # Recursively create a process tree such as: # - parent (this process) # - p3 # - p2 # - p1 # - p0 # - p3 # - p2 # - p1 # - p0 p0 = subprocess.Popen( [sys.executable, __file__, 'launch-subprocesses', '3']) p1 = subprocess.Popen( [sys.executable, __file__, 'launch-subprocesses', '3']) if 'check-subprocesses-ignore-pid' in sys.argv: import pydevd pydevd.add_dont_terminate_child_pid(p0.pid) print('created', os.getpid()) while True: time.sleep(.1)
def enable_attach(dont_trace_start_patterns, dont_trace_end_patterns): if hasattr(enable_attach, "called"): raise RuntimeError( "enable_attach() can only be called once per process.") server_access_token = compat.force_str(codecs.encode( os.urandom(32), "hex")) import subprocess adapter_args = [ sys.executable, _ADAPTER_PATH, "--for-server", "--host", options.host, "--port", str(options.port), "--server-access-token", server_access_token, ] if log.log_dir is not None: adapter_args += ["--log-dir", log.log_dir] log.info("enable_attach() spawning adapter: {0!r}", adapter_args) # Adapter will outlive this process, so we shouldn't wait for it. However, we do # need to ensure that the Popen instance for it doesn't get garbage-collected, to # avoid triggering https://bugs.python.org/issue37380. enable_attach.process = process = subprocess.Popen(adapter_args, bufsize=0, stdout=subprocess.PIPE) line = process.stdout.readline() if isinstance(line, bytes): line = line.decode("utf-8") connection_details = json.JSONDecoder().decode(line) log.info("Connection details received from adapter: {0!r}", connection_details) host = "127.0.0.1" # This should always be loopback address. port = connection_details["server"]["port"] pydevd.settrace( host=host, port=port, suspend=False, patch_multiprocessing=options.multiprocess, wait_for_ready_to_run=False, block_until_connected=True, dont_trace_start_patterns=dont_trace_start_patterns, dont_trace_end_patterns=dont_trace_end_patterns, access_token=server_access_token, client_access_token=options.client_access_token, ) log.info("pydevd debug client connected to: {0}:{1}", host, port) # Ensure that we ignore the adapter process when terminating the debugger. pydevd.add_dont_terminate_child_pid(process.pid) options.port = connection_details["ide"]["port"] enable_attach.called = True log.info("ptvsd debug server running at: {0}:{1}", options.host, options.port) return options.host, options.port
def enable_attach(dont_trace_start_patterns, dont_trace_end_patterns): if hasattr(enable_attach, "called"): raise RuntimeError("enable_attach() can only be called once per process.") import subprocess adapter_args = [ sys.executable, _ADAPTER_PATH, "--host", server_opts.host, "--port", str(server_opts.port), "--for-enable-attach", ] if common_opts.log_dir is not None: adapter_args += ["--log-dir", common_opts.log_dir] log.info("enable_attach() spawning adapter: {0!r}", adapter_args) # Adapter life time is expected to be longer than this process, # so never wait on the adapter process process = subprocess.Popen(adapter_args, bufsize=0, stdout=subprocess.PIPE) line = process.stdout.readline() if isinstance(line, bytes): line = line.decode("utf-8") connection_details = json.JSONDecoder().decode(line) log.info("Connection details received from adapter: {0!r}", connection_details) host = "127.0.0.1" # This should always be loopback address. port = connection_details["server"]["port"] pydevd.settrace( host=host, port=port, suspend=False, patch_multiprocessing=server_opts.multiprocess, wait_for_ready_to_run=False, block_until_connected=True, dont_trace_start_patterns=dont_trace_start_patterns, dont_trace_end_patterns=dont_trace_end_patterns, ) log.info("pydevd debug client connected to: {0}:{1}", host, port) # Ensure that we ignore the adapter process when terminating the debugger. pydevd.add_dont_terminate_child_pid(process.pid) server_opts.port = connection_details["ide"]["port"] listener_file = os.getenv("PTVSD_LISTENER_FILE") if listener_file is not None: with open(listener_file, "w") as f: json.dump({"host": server_opts.host, "port": server_opts.port}, f) enable_attach.called = True log.info( "ptvsd debug server running at: {0}:{1}", server_opts.host, server_opts.port ) return server_opts.host, server_opts.port
def enable_attach(dont_trace_start_patterns, dont_trace_end_patterns): if hasattr(enable_attach, "called"): raise RuntimeError("enable_attach() can only be called once per process.") host, port = pydevd._enable_attach( ("127.0.0.1", 0), dont_trace_start_patterns=dont_trace_start_patterns, dont_trace_end_paterns=dont_trace_end_patterns, patch_multiprocessing=server_opts.multiprocess, ) log.info("pydevd debug server running at: {0}:{1}", host, port) class _DAPMessagesListener(pydevd.IDAPMessagesListener): def before_send(self, msg): pass def after_receive(self, msg): try: if msg["command"] == "setDebuggerProperty": port_queue.put(msg["arguments"]["adapterPort"]) except KeyError: pass port_queue = queue.Queue() pydevd.add_dap_messages_listener(_DAPMessagesListener()) with pydevd.skip_subprocess_arg_patch(): import subprocess adapter_args = [ sys.executable, os.path.join(os.path.dirname(ptvsd.__file__), "adapter"), "--host", server_opts.host, "--port", str(server_opts.port), "--for-server-on-port", str(port), ] if common_opts.log_dir is not None: adapter_args += ["--log-dir", common_opts.log_dir] log.info("enable_attach() spawning adapter: {0!r}", adapter_args) # Adapter life time is expected to be longer than this process, # so never wait on the adapter process process = subprocess.Popen(adapter_args, bufsize=0) # Ensure that we ignore the adapter process when terminating the # debugger. pydevd.add_dont_terminate_child_pid(process.pid) server_opts.port = port_queue.get(True, _QUEUE_TIMEOUT) listener_file = os.getenv("PTVSD_LISTENER_FILE") if listener_file is not None: with open(listener_file, "w") as f: json.dump({"host": server_opts.host, "port": server_opts.port}, f) enable_attach.called = True log.info( "ptvsd debug server running at: {0}:{1}", server_opts.host, server_opts.port ) return server_opts.host, server_opts.port