Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python pty.fork - how does it work

Tags:

python

pty

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.

like image 939
webminal.org Avatar asked Oct 26 '10 10:10

webminal.org


People also ask

What is pty in Python?

Source code: Lib/pty.py. The pty module defines operations for handling the pseudo-terminal concept: starting another process and being able to write to and read from its controlling terminal programmatically. Pseudo-terminal handling is highly platform dependent.

What is Ptyprocess?

Launch a subprocess in a pseudo terminal (pty), and interact with both the process and its pty. Sometimes, piping stdin and stdout is not enough.


1 Answers

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!

like image 51
sdaau Avatar answered Oct 26 '22 10:10

sdaau