def is_process_alive(pid): from robocorp_ls_core.subprocess_wrapper import subprocess if _is_process_alive(pid): # Check if zombie... try: cmd = ["ps", "-p", str(pid), "-o", "stat"] try: process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) except: log.exception("Error calling: %s." % (cmd, )) else: stdout, _ = process.communicate() stdout = stdout.decode("utf-8", "replace") lines = [line.strip() for line in stdout.splitlines()] if len(lines) > 1: if lines[1].startswith("Z"): return False # It's a zombie except: log.exception("Error checking if process is alive.") return True return False
def test_exit_with_parent_process_died( language_server_process: IRobocorpLanguageServerClient, language_server_io, ws_root_path, initialization_options, ): """ :note: Only check with the language_server_io (because that's in another process). """ from robocorp_ls_core.subprocess_wrapper import subprocess from robocorp_ls_core.basic import is_process_alive from robocorp_ls_core.basic import kill_process_and_subprocesses from robocorp_ls_core.unittest_tools.fixtures import wait_for_test_condition language_server = language_server_io dummy_process = subprocess.Popen( [sys.executable, "-c", "import time;time.sleep(10000)"]) language_server.initialize( ws_root_path, process_id=dummy_process.pid, initialization_options=initialization_options, ) assert is_process_alive(dummy_process.pid) assert is_process_alive(language_server_process.pid) kill_process_and_subprocesses(dummy_process.pid) wait_for_test_condition(lambda: not is_process_alive(dummy_process.pid)) wait_for_test_condition( lambda: not is_process_alive(language_server_process.pid)) language_server_io.require_exit_messages = False
def on_any_event(self, event): from string import Template if self.drop_during_process and self.process and self.process.poll( ) is None: return if event.is_directory: object_type = 'directory' else: object_type = 'file' context = { 'watch_src_path': event.src_path, 'watch_dest_path': '', 'watch_event_type': event.event_type, 'watch_object': object_type, } if self.shell_command is None: if has_attribute(event, 'dest_path'): context.update({'dest_path': event.dest_path}) command = 'echo "${watch_event_type} ${watch_object} from ${watch_src_path} to ${watch_dest_path}"' else: command = 'echo "${watch_event_type} ${watch_object} ${watch_src_path}"' else: if has_attribute(event, 'dest_path'): context.update({'watch_dest_path': event.dest_path}) command = self.shell_command command = Template(command).safe_substitute(**context) self.process = subprocess.Popen(command, shell=True) if self.wait_for_process: self.process.wait()
def test_system_mutex_locked_on_subprocess(): import sys from robocorp_ls_core.subprocess_wrapper import subprocess from robocorp_ls_core.basic import kill_process_and_subprocesses from robocorp_ls_core.system_mutex import SystemMutex from robocorp_ls_core.basic import wait_for_condition code = ''' import sys import time print('initialized') from robocorp_ls_core.system_mutex import SystemMutex mutex = SystemMutex('test_system_mutex_locked_on_subprocess') assert mutex.get_mutex_aquired() print('acquired mutex') sys.stdout.flush() time.sleep(30) ''' p = subprocess.Popen([sys.executable, '-c', code], stdout=subprocess.PIPE, stdin=subprocess.PIPE) wait_for_condition(lambda: p.stdout.readline().strip() == b'acquired mutex') mutex = SystemMutex('test_system_mutex_locked_on_subprocess') assert not mutex.get_mutex_aquired() # i.e.: check that we can acquire the mutex if the related process dies. kill_process_and_subprocesses(p.pid) def acquire_mutex(): mutex = SystemMutex('test_system_mutex_locked_on_subprocess') return mutex.get_mutex_aquired() wait_for_condition(acquire_mutex, timeout=5)
def _popen(cmdline, **kwargs): import subprocess try: return subprocess.Popen(cmdline, **kwargs) except: log.exception("Error running: %s", (" ".join(cmdline))) return None
def start_server_process(args=(), python_exe=None, env=None): """ Calls this __main__ in another process. :param args: The list of arguments for the server process. i.e.: ["-vv", "--log-file=%s" % log_file] """ from robocorp_ls_core.robotframework_log import get_logger from robocorp_ls_core.subprocess_wrapper import subprocess import threading from robotframework_ls.options import Setup log = get_logger(__name__) if python_exe: if not os.path.exists(python_exe): raise RuntimeError("Expected %s to exist" % (python_exe, )) args = [python_exe or sys.executable, "-u", __file__] + list(args) log.debug('Starting server api process with args: "%s"' % ('" "'.join(args), )) environ = os.environ.copy() environ.pop("PYTHONPATH", "") environ.pop("PYTHONHOME", "") environ.pop("VIRTUAL_ENV", "") if env is not None: for key, val in env.items(): environ[key] = val environ["PYTHONIOENCODING"] = "utf-8" environ["PYTHONUNBUFFERED"] = "1" env_log = ["Environ:"] for key, val in environ.items(): env_log.append(" %s=%s" % (key, val)) if Setup.options.DEBUG_PROCESS_ENVIRON: log.debug("\n".join(env_log)) language_server_process = subprocess.Popen( args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, env=environ, bufsize=0, ) t = threading.Thread(target=_stderr_reader, args=(language_server_process.stderr, )) t.setName("Stderr from ServerAPI (%s)" % (args, )) t.setDaemon(True) t.start() return language_server_process
def launch( self, target, debug=True, success=True, terminal="none", args: Optional[Iterable[str]] = None, ): """ :param args: The arguments to the launch (for instance: ["--variable", "my_var:22"] ) """ from robocorp_ls_core.debug_adapter_core.dap.dap_schema import LaunchRequest from robocorp_ls_core.debug_adapter_core.dap.dap_schema import ( LaunchRequestArguments, ) from robocorp_ls_core.debug_adapter_core.dap.dap_schema import LaunchResponse from robocorp_ls_core.debug_adapter_core.dap.dap_schema import ( RunInTerminalRequest, ) from robocorp_ls_core.basic import as_str from robocorp_ls_core.debug_adapter_core.dap.dap_schema import InitializedEvent from robocorp_ls_core.debug_adapter_core.dap.dap_schema import Response from robocorp_ls_core.debug_adapter_core.dap.dap_schema import ProcessEvent launch_args = LaunchRequestArguments(__sessionId="some_id", noDebug=not debug, target=target, terminal=terminal) if args: launch_args.kwargs["args"] = args self.write(LaunchRequest(launch_args)) if terminal == "external": run_in_terminal_request = self.read(RunInTerminalRequest) env = os.environ.copy() for key, val in run_in_terminal_request.arguments.env.to_dict( ).items(): env[as_str(key)] = as_str(val) cwd = run_in_terminal_request.arguments.cwd popen_args = run_in_terminal_request.arguments.args subprocess.Popen(popen_args, cwd=cwd, env=env) if success: # Initialized is sent just before the launch response (at which # point it's possible to send breakpoints). self.read(ProcessEvent) event = self.read(InitializedEvent) assert isinstance(event, InitializedEvent) if success: launch_response = self.read(LaunchResponse) else: launch_response = self.read(Response) assert launch_response.success == success
def dap_process(dap_log_file, dap_process_stderr_file): from robotframework_debug_adapter import __main__ from robocorp_ls_core.basic import kill_process_and_subprocesses env = os.environ.copy() env["ROBOTFRAMEWORK_DAP_LOG_LEVEL"] = "3" env["ROBOTFRAMEWORK_DAP_LOG_FILENAME"] = dap_log_file dap_process = subprocess.Popen( [sys.executable, "-u", __main__.__file__], stdout=subprocess.PIPE, stderr=dap_process_stderr_file, stdin=subprocess.PIPE, env=env, ) assert dap_process.returncode is None yield dap_process if dap_process.returncode is None: kill_process_and_subprocesses(dap_process.pid)
def not_supported_test_launch_in_external_terminal( debugger_api: _DebuggerAPI, rcc_config_location: str ): """ This is an integrated test of the debug adapter. It communicates with it as if it was VSCode. Note: we don't currently support launching in an external terminal because there's no easy way to get the pid (it'd be possible to do that by creating a wrapper script which would then really launch rcc and then it'd connect back to some port and provide the pid of the process which was spawned, but the value gained vs the effort to do so seems low, which means we can only run without a terminal for now so that we have an easy way of tracking the RCC process pid). """ from robocorp_ls_core.debug_adapter_core.dap.dap_schema import TerminatedEvent from robocorp_ls_core.debug_adapter_core.dap.dap_schema import RunInTerminalRequest import os from robocorp_ls_core.basic import as_str from robocorp_ls_core.subprocess_wrapper import subprocess debugger_api.initialize(rcc_config_location=rcc_config_location) robot = debugger_api.get_dap_case_file("minimal/robot.yaml") debugger_api.launch(robot, "task2", debug=False, terminal="external") debugger_api.configuration_done() run_in_terminal_request = debugger_api.read(RunInTerminalRequest) env = os.environ.copy() for key, val in run_in_terminal_request.arguments.env.to_dict().items(): env[as_str(key)] = as_str(val) cwd = run_in_terminal_request.arguments.cwd popen_args = run_in_terminal_request.arguments.args subprocess.Popen(popen_args, cwd=cwd, env=env) # i.e.: Big timeout because creating the environment may be slow. debugger_api.read(TerminatedEvent, timeout=120)
def create_language_server_process(log_file, __main__module): from robocorp_ls_core.basic import kill_process_and_subprocesses from robocorp_ls_core.subprocess_wrapper import subprocess language_server_process = subprocess.Popen( [ sys.executable, "-u", __main__module.__file__, "-vv", "--log-file=%s" % log_file, ], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, ) stderr = language_server_process.stderr buf = [] t = threading.Thread(target=_stderr_reader, args=(stderr, buf)) t.name = "Stderr reader" t.start() returncode = language_server_process.poll() assert returncode is None try: yield language_server_process finally: t.join(1) stderr_contents = b"".join(buf).decode("utf-8") if stderr_contents: sys.stderr.write( f"Found stderr contents: >>\n{stderr_contents}\n<<") returncode = language_server_process.poll() if returncode is None: kill_process_and_subprocesses(language_server_process.pid)
def start(self): self.process = subprocess.Popen(self.command, preexec_fn=os.setsid)