@@ -40,6 +40,9 @@ def master_open():
4040 Open a pty master and return the fd, and the filename of the slave end.
4141 Deprecated, use openpty() instead."""
4242
43+ import warnings
44+ warnings .warn ("Use pty.openpty() instead." , DeprecationWarning , stacklevel = 2 ) # Remove API in 3.14
45+
4346 try :
4447 master_fd , slave_fd = os .openpty ()
4548 except (AttributeError , OSError ):
@@ -69,6 +72,9 @@ def slave_open(tty_name):
6972 opened filedescriptor.
7073 Deprecated, use openpty() instead."""
7174
75+ import warnings
76+ warnings .warn ("Use pty.openpty() instead." , DeprecationWarning , stacklevel = 2 ) # Remove API in 3.14
77+
7278 result = os .open (tty_name , os .O_RDWR )
7379 try :
7480 from fcntl import ioctl , I_PUSH
@@ -101,32 +107,14 @@ def fork():
101107 master_fd , slave_fd = openpty ()
102108 pid = os .fork ()
103109 if pid == CHILD :
104- # Establish a new session.
105- os .setsid ()
106110 os .close (master_fd )
107-
108- # Slave becomes stdin/stdout/stderr of child.
109- os .dup2 (slave_fd , STDIN_FILENO )
110- os .dup2 (slave_fd , STDOUT_FILENO )
111- os .dup2 (slave_fd , STDERR_FILENO )
112- if slave_fd > STDERR_FILENO :
113- os .close (slave_fd )
114-
115- # Explicitly open the tty to make it become a controlling tty.
116- tmp_fd = os .open (os .ttyname (STDOUT_FILENO ), os .O_RDWR )
117- os .close (tmp_fd )
111+ os .login_tty (slave_fd )
118112 else :
119113 os .close (slave_fd )
120114
121115 # Parent and child process.
122116 return pid , master_fd
123117
124- def _writen (fd , data ):
125- """Write all the data to a descriptor."""
126- while data :
127- n = os .write (fd , data )
128- data = data [n :]
129-
130118def _read (fd ):
131119 """Default read function."""
132120 return os .read (fd , 1024 )
@@ -136,9 +124,42 @@ def _copy(master_fd, master_read=_read, stdin_read=_read):
136124 Copies
137125 pty master -> standard output (master_read)
138126 standard input -> pty master (stdin_read)"""
139- fds = [master_fd , STDIN_FILENO ]
140- while fds :
141- rfds , _wfds , _xfds = select (fds , [], [])
127+ if os .get_blocking (master_fd ):
128+ # If we write more than tty/ndisc is willing to buffer, we may block
129+ # indefinitely. So we set master_fd to non-blocking temporarily during
130+ # the copy operation.
131+ os .set_blocking (master_fd , False )
132+ try :
133+ _copy (master_fd , master_read = master_read , stdin_read = stdin_read )
134+ finally :
135+ # restore blocking mode for backwards compatibility
136+ os .set_blocking (master_fd , True )
137+ return
138+ high_waterlevel = 4096
139+ stdin_avail = master_fd != STDIN_FILENO
140+ stdout_avail = master_fd != STDOUT_FILENO
141+ i_buf = b''
142+ o_buf = b''
143+ while 1 :
144+ rfds = []
145+ wfds = []
146+ if stdin_avail and len (i_buf ) < high_waterlevel :
147+ rfds .append (STDIN_FILENO )
148+ if stdout_avail and len (o_buf ) < high_waterlevel :
149+ rfds .append (master_fd )
150+ if stdout_avail and len (o_buf ) > 0 :
151+ wfds .append (STDOUT_FILENO )
152+ if len (i_buf ) > 0 :
153+ wfds .append (master_fd )
154+
155+ rfds , wfds , _xfds = select (rfds , wfds , [])
156+
157+ if STDOUT_FILENO in wfds :
158+ try :
159+ n = os .write (STDOUT_FILENO , o_buf )
160+ o_buf = o_buf [n :]
161+ except OSError :
162+ stdout_avail = False
142163
143164 if master_fd in rfds :
144165 # Some OSes signal EOF by returning an empty byte string,
@@ -150,19 +171,22 @@ def _copy(master_fd, master_read=_read, stdin_read=_read):
150171 if not data : # Reached EOF.
151172 return # Assume the child process has exited and is
152173 # unreachable, so we clean up.
153- else :
154- os .write (STDOUT_FILENO , data )
174+ o_buf += data
175+
176+ if master_fd in wfds :
177+ n = os .write (master_fd , i_buf )
178+ i_buf = i_buf [n :]
155179
156- if STDIN_FILENO in rfds :
180+ if stdin_avail and STDIN_FILENO in rfds :
157181 data = stdin_read (STDIN_FILENO )
158182 if not data :
159- fds . remove ( STDIN_FILENO )
183+ stdin_avail = False
160184 else :
161- _writen ( master_fd , data )
185+ i_buf += data
162186
163187def spawn (argv , master_read = _read , stdin_read = _read ):
164188 """Create a spawned process."""
165- if type (argv ) == type ( '' ):
189+ if isinstance (argv , str ):
166190 argv = (argv ,)
167191 sys .audit ('pty.spawn' , argv )
168192
0 commit comments