python pty.fork - how does it work

pty, python

Solution

I think I finally got a minimal example for `pty.fork` in Python - and since I found it extremely difficult to find a similar example, I'm posting it here as an illustration of @joni's answer. It is essentially based on:

- pty - Python os.forkpty why can't I make it work - Stack Overflow

- Weird platform dependent error: Using pty.fork()

- CodeIdol - Thinking about Programming Python, 3rd Edition - Parallel System Tools - Pipes

- Python code coverage: Lib/test/test_pty.py

- [Patches] [ python-Patches-656590 ] /dev/ptmx support for ptys (cygwin) ("master_open() and slave_open have been deprecated since 2.0")

Particularly nasty bits are finding documentation that still refers to `master_open()` which is obsolete; and the fact that `pty.fork` will not spawn a child process, unless the file descriptor (returned by the fork method) is read from by the parent process! (note that in `os.fork` there is no such requirement) Also, it seems that `os.fork` is a bit more portable (read a few comments noting that `pty.fork` doesn't work on some platforms).

Anyways, here's first a script (`pyecho.py`) that acts as an executable (it simply reads lines from standard input, and writes them back in uppercase):

#!/usr/bin/env python
# pyecho.py

import sys;

print "pyecho starting..."

while True:
  print sys.stdin.readline().upper()

... and then, here is the actual script (it will require that pyecho.py is in the same directory):

#!/usr/bin/env python

import sys
import os
import time
import pty

def my_pty_fork():

  # fork this script
  try:
    ( child_pid, fd ) = pty.fork()    # OK
    #~ child_pid, fd = os.forkpty()      # OK
  except OSError as e:
    print str(e)

  #~ print "%d - %d" % (fd, child_pid)
  # NOTE - unlike OS fork; in pty fork we MUST use the fd variable
  #   somewhere (i.e. in parent process; it does not exist for child)
  # ... actually, we must READ from fd in parent process...
  #   if we don't - child process will never be spawned!

  if child_pid == 0:
    print "In Child Process: PID# %s" % os.getpid()
    # note: fd for child is invalid (-1) for pty fork!
    #~ print "%d - %d" % (fd, child_pid)

    # the os.exec replaces the child process
    sys.stdout.flush()
    try:
      #Note: "the first of these arguments is passed to the new program as its own name"
      # so:: "python": actual executable; "ThePythonProgram": name of executable in process list (`ps axf`); "pyecho.py": first argument to executable..
      os.execlp("python","ThePythonProgram","pyecho.py")
    except:
      print "Cannot spawn execlp..."
  else:
    print "In Parent Process: PID# %s" % os.getpid()
    # MUST read from fd; else no spawn of child!
    print os.read(fd, 100) # in fact, this line prints out the "In Child Process..." sentence above!

    os.write(fd,"message one\n")
    print os.read(fd, 100)        # message one
    time.sleep(2)
    os.write(fd,"message two\n")
    print os.read(fd, 10000)      # pyecho starting...\n MESSAGE ONE
    time.sleep(2)
    print os.read(fd, 10000)      # message two \n MESSAGE TWO
    # uncomment to lock (can exit with Ctrl-C)
    #~ while True:
      #~ print os.read(fd, 10000)


if __name__ == "__main__":
    my_pty_fork()

Well, hope this helps someone, Cheers!

Problem

http://docs.python.org/library/pty.html says - pty.fork()¶ Fork. Connect the child’s controlling terminal to a pseudo-terminal. Return value is (pid, fd). Note that the child gets pid 0, and the fd is invalid. The parent’s return value is the pid of the child, and fd is a file descriptor connected to the child’s controlling terminal (and also to the child’s standard input and output). What's does this mean ? Every process has 3 fd (stdin,stdout,stderr). Does this affects these fds now ? will child process won't have any of these fds? I'm confused.--totally.

Original source

Related problems