Skip to content
This repository was archived by the owner on Dec 20, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion all/ptyprocess/__init__.py
100755 → 100644
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Run a subprocess in a pseudo terminal"""
from .ptyprocess import PtyProcess, PtyProcessUnicode, PtyProcessError

__version__ = '0.5.2'
__version__ = '0.7.0'
5 changes: 3 additions & 2 deletions all/ptyprocess/_fork_pty.py
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import errno

from pty import (STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO, CHILD)
from .util import PtyProcessError

def fork_pty():
'''This implements a substitute for the forkpty system call. This
Expand Down Expand Up @@ -63,7 +64,7 @@ def pty_make_controlling_tty(tty_fd):
try:
fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY)
os.close(fd)
raise ExceptionPexpect("OSError of errno.ENXIO should be raised.")
raise PtyProcessError("OSError of errno.ENXIO should be raised.")
except OSError as err:
if err.errno != errno.ENXIO:
raise
Expand All @@ -74,4 +75,4 @@ def pty_make_controlling_tty(tty_fd):

# Verify we now have a controlling tty.
fd = os.open("/dev/tty", os.O_WRONLY)
os.close(fd)
os.close(fd)
41 changes: 27 additions & 14 deletions all/ptyprocess/ptyprocess.py
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
# Constants
from pty import (STDIN_FILENO, CHILD)

from .util import which
from .util import which, PtyProcessError

_platform = sys.platform.lower()

Expand Down Expand Up @@ -62,11 +62,18 @@ def _make_eof_intr():
# inherit EOF and INTR definitions from controlling process.
try:
from termios import VEOF, VINTR
try:
fd = sys.__stdin__.fileno()
except ValueError:
# ValueError: I/O operation on closed file
fd = sys.__stdout__.fileno()
fd = None
for name in 'stdin', 'stdout':
stream = getattr(sys, '__%s__' % name, None)
if stream is None or not hasattr(stream, 'fileno'):
continue
try:
fd = stream.fileno()
except ValueError:
continue
if fd is None:
# no fd, raise ValueError to fallback on CEOF, CINTR
raise ValueError("No stream has a fileno")
intr = ord(termios.tcgetattr(fd)[6][VINTR])
eof = ord(termios.tcgetattr(fd)[6][VEOF])
except (ImportError, OSError, IOError, ValueError, termios.error):
Expand All @@ -83,14 +90,11 @@ def _make_eof_intr():
_INTR = _byte(intr)
_EOF = _byte(eof)

class PtyProcessError(Exception):
"""Generic error class for this package."""

# setecho and setwinsize are pulled out here because on some platforms, we need
# to do this from the child before we exec()

def _setecho(fd, state):
errmsg = 'setecho() may not be called on this platform'
errmsg = 'setecho() may not be called on this platform (it may still be possible to enable/disable echo when spawning the child process)'

try:
attr = termios.tcgetattr(fd)
Expand Down Expand Up @@ -176,7 +180,7 @@ def __init__(self, pid, fd):
@classmethod
def spawn(
cls, argv, cwd=None, env=None, echo=True, preexec_fn=None,
dimensions=(24, 80)):
dimensions=(24, 80), pass_fds=()):
'''Start the given command in a child process in a pseudo terminal.

This does all the fork/exec type of stuff for a pty, and returns an
Expand All @@ -188,6 +192,10 @@ def spawn(

Dimensions of the psuedoterminal used for the subprocess can be
specified as a tuple (rows, cols), or the default (24, 80) will be used.

By default, all file descriptors except 0, 1 and 2 are closed. This
behavior can be overridden with pass_fds, a list of file descriptors to
keep open between the parent and the child.
'''
# Note that it is difficult for this method to fail.
# You cannot detect if the child process cannot start.
Expand Down Expand Up @@ -253,9 +261,14 @@ def spawn(

# Do not allow child to inherit open file descriptors from parent,
# with the exception of the exec_err_pipe_write of the pipe
max_fd = resource.getrlimit(resource.RLIMIT_NOFILE)[0]
os.closerange(3, exec_err_pipe_write)
os.closerange(exec_err_pipe_write+1, max_fd)
# and pass_fds.
# Impose ceiling on max_fd: AIX bugfix for users with unlimited
# nofiles where resource.RLIMIT_NOFILE is 2^63-1 and os.closerange()
# occasionally raises out of range error
max_fd = min(1048576, resource.getrlimit(resource.RLIMIT_NOFILE)[0])
spass_fds = sorted(set(pass_fds) | {exec_err_pipe_write})
for pair in zip([2] + spass_fds, spass_fds + [max_fd]):
os.closerange(pair[0]+1, pair[1])

if cwd is not None:
os.chdir(cwd)
Expand Down
6 changes: 5 additions & 1 deletion all/ptyprocess/util.py
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,8 @@ def _access_check(fn, mode):
name = os.path.join(dir, thefile)
if _access_check(name, mode):
return name
return None
return None


class PtyProcessError(Exception):
"""Generic error class for this package."""