def test_signal_handling(self): expecting_code = 0 expecting_signal = 0 expecting_value = 0 found_signal = False def handle_signal(siginfo): nonlocal found_signal if expecting_signal: self.ae(siginfo.si_signo, expecting_signal) if expecting_code is not None: self.ae(siginfo.si_code, expecting_code) self.ae(siginfo.sival_int, expecting_value) if expecting_code in (CLD_EXITED, CLD_KILLED): p.wait(1) p.stdin.close() found_signal = True def assert_signal(): nonlocal found_signal found_signal = False st = time.monotonic() while time.monotonic() - st < 5: for (fd, event) in poll.poll(10): if fd == signal_read_fd: read_signals(signal_read_fd, handle_signal) if found_signal: break self.assertTrue(found_signal, f'Failed to to get SIGCHLD for signal {signal}') def t(signal, q, expecting_sig=signal.SIGCHLD): nonlocal expecting_code, found_signal, expecting_signal expecting_code = q expecting_signal = expecting_sig.value if signal is not None: p.send_signal(signal) assert_signal() poll = select.poll() p = subprocess.Popen([kitty_exe(), '+runpy', 'input()'], stderr=subprocess.DEVNULL, stdin=subprocess.PIPE) signal_read_fd = install_signal_handlers(signal.SIGCHLD, signal.SIGUSR1)[0] try: poll.register(signal_read_fd, select.POLLIN) t(signal.SIGINT, CLD_KILLED) p = subprocess.Popen([kitty_exe(), '+runpy', 'input()'], stderr=subprocess.DEVNULL, stdin=subprocess.PIPE) p.stdin.close() t(None, os.CLD_EXITED) expecting_code = None expecting_signal = signal.SIGUSR1.value os.kill(os.getpid(), signal.SIGUSR1) assert_signal() expecting_value = 17 if has_sigqueue else 0 sigqueue(os.getpid(), signal.SIGUSR1.value, expecting_value) assert_signal() finally: remove_signal_handlers()
def fork_prewarm_process(opts: Options, use_exec: bool = False) -> Optional[PrewarmProcess]: stdin_read, stdin_write = safe_pipe() stdout_read, stdout_write = safe_pipe() death_notify_read, death_notify_write = safe_pipe() if use_exec: import subprocess tp = subprocess.Popen([ kitty_exe(), '+runpy', f'from kitty.prewarm import exec_main; exec_main({stdin_read}, {stdout_write}, {death_notify_write})' ], pass_fds=(stdin_read, stdout_write, death_notify_write)) child_pid = tp.pid tp.returncode = 0 # prevent a warning when the popen object is deleted with the process still running else: child_pid = os.fork() if child_pid: # master os.close(stdin_read) os.close(stdout_write) os.close(death_notify_write) p = PrewarmProcess(child_pid, stdin_write, stdout_read, death_notify_read) if use_exec: p.reload_kitty_config() return p # child os.close(stdin_write) os.close(stdout_read) os.close(death_notify_read) set_options(opts) exec_main(stdin_read, stdout_write, death_notify_write) raise SystemExit(0)
def test_prewarming(self): from kitty.prewarm import fork_prewarm_process cwd = tempfile.gettempdir() env = {'TEST_ENV_PASS': '******'} cols = 117 stdin_data = 'from_stdin' pty = self.create_pty(cols=cols) ttyname = os.ttyname(pty.slave_fd) opts = get_options() opts.config_overrides = 'font_family prewarm', p = fork_prewarm_process(opts, use_exec=True) if p is None: return p.take_from_worker_fd(create_file=True) child = p(pty.slave_fd, [kitty_exe(), '+runpy', """\ import os, json; from kitty.utils import *; from kitty.fast_data_types import get_options; print(json.dumps({ 'cterm': os.ctermid(), 'ttyname': os.ttyname(sys.stdout.fileno()), 'cols': read_screen_size().cols, 'cwd': os.getcwd(), 'env': os.environ.get('TEST_ENV_PASS'), 'pid': os.getpid(), 'font_family': get_options().font_family, 'stdin': sys.stdin.read(), 'done': 'hello', }, indent=2))"""], cwd=cwd, env=env, stdin_data=stdin_data) self.assertFalse(pty.screen_contents().strip()) p.mark_child_as_ready(child.child_id) pty.wait_till(lambda: 'hello' in pty.screen_contents()) data = json.loads(pty.screen_contents()) self.ae(data['cols'], cols) self.assertTrue(data['cterm']) self.ae(data['ttyname'], ttyname) self.ae(os.path.realpath(data['cwd']), os.path.realpath(cwd)) self.ae(data['env'], env['TEST_ENV_PASS']) self.ae(data['font_family'], 'prewarm') self.ae(int(p.from_worker.readline()), data['pid'])
def test_prewarming(self): from kittens.prewarm.main import PrewarmProcess p = PrewarmProcess(create_file_to_read_from_worker=True) cwd = tempfile.gettempdir() env = {'TEST_ENV_PASS': '******'} cols = 117 stdin_data = 'from_stdin' pty = self.create_pty(cols=cols) ttyname = os.ttyname(pty.slave_fd) child = p(pty.slave_fd, [ kitty_exe(), '+runpy', """import os, json; from kitty.utils import *; print(json.dumps({ 'cterm': os.ctermid(), 'ttyname': os.ttyname(sys.stdout.fileno()), 'cols': read_screen_size().cols, 'cwd': os.getcwd(), 'env': os.environ.get('TEST_ENV_PASS'), 'pid': os.getpid(), 'stdin': sys.stdin.read(), 'done': 'hello', }, indent=2))""" ], cwd=cwd, env=env, stdin_data=stdin_data) self.assertFalse(pty.screen_contents().strip()) p.mark_child_as_ready(child.child_id) pty.wait_till(lambda: 'hello' in pty.screen_contents()) data = json.loads(pty.screen_contents()) self.ae(data['cols'], cols) self.assertTrue(data['cterm']) self.ae(data['ttyname'], ttyname) self.ae(os.path.realpath(data['cwd']), os.path.realpath(cwd)) self.ae(data['env'], env['TEST_ENV_PASS']) self.ae(int(p.from_worker.readline()), data['pid'])
def test_exe(self) -> None: from kitty.constants import kitty_exe exe = kitty_exe() self.assertTrue(os.access(exe, os.X_OK)) self.assertTrue(os.path.isfile(exe)) self.assertIn('kitty', os.path.basename(exe))
def cmd_to_run_python_code(self, code): from kitty.constants import kitty_exe return [kitty_exe(), '+runpy', code]