This commit was manufactured by cvs2svn to create tag 'r221c2'.
[python/dscho.git] / Lib / pty.py
bloba2f21c9bf6ef44e301ef277b20e768d955ffdcd4
1 """Pseudo terminal utilities."""
3 # Bugs: No signal handling. Doesn't set slave termios and window size.
4 # Only tested on Linux.
5 # See: W. Richard Stevens. 1992. Advanced Programming in the
6 # UNIX Environment. Chapter 19.
7 # Author: Steen Lumholt -- with additions by Guido.
9 from select import select
10 import os
12 # Absurd: import termios and then delete it. This is to force an attempt
13 # to import pty to raise an ImportError on platforms that lack termios.
14 # Without this explicit import of termios here, some other module may
15 # import tty first, which in turn imports termios and dies with an
16 # ImportError then. But since tty *does* exist across platforms, that
17 # leaves a damaged module object for tty in sys.modules, and the import
18 # of tty here then appears to work despite that the tty imported is junk.
19 import termios
20 del termios
22 import tty
24 __all__ = ["openpty","fork","spawn"]
26 STDIN_FILENO = 0
27 STDOUT_FILENO = 1
28 STDERR_FILENO = 2
30 CHILD = 0
32 def openpty():
33 """openpty() -> (master_fd, slave_fd)
34 Open a pty master/slave pair, using os.openpty() if possible."""
36 try:
37 return os.openpty()
38 except (AttributeError, OSError):
39 pass
40 master_fd, slave_name = _open_terminal()
41 slave_fd = slave_open(slave_name)
42 return master_fd, slave_fd
44 def master_open():
45 """master_open() -> (master_fd, slave_name)
46 Open a pty master and return the fd, and the filename of the slave end.
47 Deprecated, use openpty() instead."""
49 try:
50 master_fd, slave_fd = os.openpty()
51 except (AttributeError, OSError):
52 pass
53 else:
54 slave_name = os.ttyname(slave_fd)
55 os.close(slave_fd)
56 return master_fd, slave_name
58 return _open_terminal()
60 def _open_terminal():
61 """Open pty master and return (master_fd, tty_name).
62 SGI and generic BSD version, for when openpty() fails."""
63 try:
64 import sgi
65 except ImportError:
66 pass
67 else:
68 try:
69 tty_name, master_fd = sgi._getpty(os.O_RDWR, 0666, 0)
70 except IOError, msg:
71 raise os.error, msg
72 return master_fd, tty_name
73 for x in 'pqrstuvwxyzPQRST':
74 for y in '0123456789abcdef':
75 pty_name = '/dev/pty' + x + y
76 try:
77 fd = os.open(pty_name, os.O_RDWR)
78 except os.error:
79 continue
80 return (fd, '/dev/tty' + x + y)
81 raise os.error, 'out of pty devices'
83 def slave_open(tty_name):
84 """slave_open(tty_name) -> slave_fd
85 Open the pty slave and acquire the controlling terminal, returning
86 opened filedescriptor.
87 Deprecated, use openpty() instead."""
89 return os.open(tty_name, os.O_RDWR)
91 def fork():
92 """fork() -> (pid, master_fd)
93 Fork and make the child a session leader with a controlling terminal."""
95 try:
96 pid, fd = os.forkpty()
97 except (AttributeError, OSError):
98 pass
99 else:
100 if pid == CHILD:
101 try:
102 os.setsid()
103 except OSError:
104 # os.forkpty() already set us session leader
105 pass
106 return pid, fd
108 master_fd, slave_fd = openpty()
109 pid = os.fork()
110 if pid == CHILD:
111 # Establish a new session.
112 os.setsid()
113 os.close(master_fd)
115 # Slave becomes stdin/stdout/stderr of child.
116 os.dup2(slave_fd, STDIN_FILENO)
117 os.dup2(slave_fd, STDOUT_FILENO)
118 os.dup2(slave_fd, STDERR_FILENO)
119 if (slave_fd > STDERR_FILENO):
120 os.close (slave_fd)
122 # Parent and child process.
123 return pid, master_fd
125 def _writen(fd, data):
126 """Write all the data to a descriptor."""
127 while data != '':
128 n = os.write(fd, data)
129 data = data[n:]
131 def _read(fd):
132 """Default read function."""
133 return os.read(fd, 1024)
135 def _copy(master_fd, master_read=_read, stdin_read=_read):
136 """Parent copy loop.
137 Copies
138 pty master -> standard output (master_read)
139 standard input -> pty master (stdin_read)"""
140 while 1:
141 rfds, wfds, xfds = select(
142 [master_fd, STDIN_FILENO], [], [])
143 if master_fd in rfds:
144 data = master_read(master_fd)
145 os.write(STDOUT_FILENO, data)
146 if STDIN_FILENO in rfds:
147 data = stdin_read(STDIN_FILENO)
148 _writen(master_fd, data)
150 def spawn(argv, master_read=_read, stdin_read=_read):
151 """Create a spawned process."""
152 if type(argv) == type(''):
153 argv = (argv,)
154 pid, master_fd = fork()
155 if pid == CHILD:
156 apply(os.execlp, (argv[0],) + argv)
157 mode = tty.tcgetattr(STDIN_FILENO)
158 tty.setraw(STDIN_FILENO)
159 try:
160 _copy(master_fd, master_read, stdin_read)
161 except IOError:
162 tty.tcsetattr(STDIN_FILENO, tty.TCSAFLUSH, mode)