import errno import fcntl import os import pty import signal import sys import termios def handler(sig, f): pass def create_eio_condition(): # SIGINT handler used to produce an EIO. # See https://github.com/python/cpython/issues/135329. try: master_fd, slave_fd = pty.openpty() child_pid = os.fork() if child_pid == 0: try: os.setsid() fcntl.ioctl(slave_fd, termios.TIOCSCTTY, 0) child_process_group_id = os.getpgrp() grandchild_pid = os.fork() if grandchild_pid == 0: os.setpgid(0, 0) # set process group for grandchild os.dup2(slave_fd, 0) # redirect stdin if slave_fd > 2: os.close(slave_fd) # Fork grandchild for terminal control manipulation if os.fork() == 0: sys.exit(0) # exit the child process that was just obtained else: try: os.tcsetpgrp(0, child_process_group_id) except OSError: pass sys.exit(0) else: # Back to child try: os.setpgid(grandchild_pid, grandchild_pid) except ProcessLookupError: pass os.tcsetpgrp(slave_fd, grandchild_pid) if slave_fd > 2: os.close(slave_fd) os.waitpid(grandchild_pid, 0) # Manipulate terminal control to create EIO condition os.tcsetpgrp(master_fd, child_process_group_id) # Now try to read from master - this might cause EIO try: os.read(master_fd, 1) except OSError as e: if e.errno == errno.EIO: print(f"Setup created EIO condition: {e}", file=sys.stderr) sys.exit(0) except Exception as setup_e: print(f"Setup error: {setup_e}", file=sys.stderr) sys.exit(1) else: # Parent process os.close(slave_fd) os.waitpid(child_pid, 0) # Now replace stdin with master_fd and try to read os.dup2(master_fd, 0) os.close(master_fd) # This should now trigger EIO print(f"Unexpectedly got input: {input()!r}", file=sys.stderr) sys.exit(0) except OSError as e: if e.errno == errno.EIO: print(f"Got EIO: {e}", file=sys.stderr) sys.exit(1) elif e.errno == errno.ENXIO: print(f"Got ENXIO (no such device): {e}", file=sys.stderr) sys.exit(1) # Treat ENXIO as success too else: print(f"Got other OSError: errno={e.errno} {e}", file=sys.stderr) sys.exit(2) except EOFError as e: print(f"Got EOFError: {e}", file=sys.stderr) sys.exit(3) except Exception as e: print(f"Got unexpected error: {type(e).__name__}: {e}", file=sys.stderr) sys.exit(4) if __name__ == "__main__": # Set up signal handler for coordination signal.signal(signal.SIGUSR1, lambda *a: create_eio_condition()) print("READY", flush=True) signal.pause()