Skip to content

Commit c2f93dc

Browse files
committed
Remove native popen() and fdopen(), replacing them with subprocess calls.
Fix a path to an assert in fileio_read(). Some misc tweaks.
1 parent d8595fe commit c2f93dc

File tree

7 files changed

+59
-1552
lines changed

7 files changed

+59
-1552
lines changed

Lib/io.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ def open(file, mode="r", buffering=None, *, encoding=None, newline=None):
120120
(appending and "a" or "") +
121121
(updating and "+" or ""))
122122
if buffering is None:
123+
buffering = -1
124+
if buffering < 0:
123125
buffering = DEFAULT_BUFFER_SIZE
124126
# XXX Should default to line buffering if os.isatty(raw.fileno())
125127
try:
@@ -446,8 +448,8 @@ class BufferedIOBase(IOBase):
446448
implementation, but wrap one.
447449
"""
448450

449-
def read(self, n: int = -1) -> bytes:
450-
"""read(n: int = -1) -> bytes. Read and return up to n bytes.
451+
def read(self, n: int = None) -> bytes:
452+
"""read(n: int = None) -> bytes. Read and return up to n bytes.
451453
452454
If the argument is omitted, None, or negative, reads and
453455
returns all data until EOF.
@@ -717,6 +719,8 @@ def __init__(self, raw,
717719
self._write_buf = b""
718720

719721
def write(self, b):
722+
if not isinstance(b, bytes):
723+
b = bytes(b)
720724
# XXX we can implement some more tricks to try and avoid partial writes
721725
if len(self._write_buf) > self.buffer_size:
722726
# We're full, so let's pre-flush the buffer

Lib/os.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -696,3 +696,41 @@ def urandom(n):
696696
bs += read(_urandomfd, n - len(bs))
697697
close(_urandomfd)
698698
return bs
699+
700+
# Supply os.popen()
701+
def popen(cmd, mode="r", buffering=None):
702+
if not isinstance(cmd, basestring):
703+
raise TypeError("invalid cmd type (%s, expected string)" % type(cmd))
704+
if mode not in ("r", "w"):
705+
raise ValueError("invalid mode %r" % mode)
706+
import subprocess, io
707+
if mode == "r":
708+
proc = subprocess.Popen(cmd,
709+
shell=True,
710+
stdout=subprocess.PIPE,
711+
bufsize=buffering)
712+
return _wrap_close(io.TextIOWrapper(proc.stdout), proc)
713+
else:
714+
proc = subprocess.Popen(cmd,
715+
shell=True,
716+
stdin=subprocess.PIPE,
717+
bufsize=buffering)
718+
return _wrap_close(io.TextIOWrapper(proc.stdin), proc)
719+
720+
# Helper for popen() -- a proxy for a file whose close waits for the process
721+
class _wrap_close:
722+
def __init__(self, stream, proc):
723+
self._stream = stream
724+
self._proc = proc
725+
def close(self):
726+
self._stream.close()
727+
return self._proc.wait() << 8 # Shift left to match old behavior
728+
def __getattr__(self, name):
729+
return getattr(self._stream, name)
730+
731+
# Supply os.fdopen() (used by subprocess!)
732+
def fdopen(fd, mode="r", buffering=-1):
733+
if not isinstance(fd, int):
734+
raise TypeError("invalid fd type (%s, expected integer)" % type(fd))
735+
import io
736+
return io.open(fd, mode, buffering)

Lib/subprocess.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -246,11 +246,11 @@ class Popen(args, bufsize=0, executable=None,
246246
try:
247247
retcode = call("mycmd" + " myarg", shell=True)
248248
if retcode < 0:
249-
print >>sys.stderr, "Child was terminated by signal", -retcode
249+
print("Child was terminated by signal", -retcode, file=sys.stderr)
250250
else:
251-
print >>sys.stderr, "Child returned", retcode
252-
except OSError, e:
253-
print >>sys.stderr, "Execution failed:", e
251+
print("Child returned", retcode, file=sys.stderr)
252+
except OSError as e:
253+
print("Execution failed:", e, file=sys.stderr)
254254
255255
256256
Replacing os.spawn*
@@ -539,6 +539,8 @@ def __init__(self, args, bufsize=0, executable=None,
539539
os.close(errread)
540540
errread = None
541541

542+
if bufsize == 0:
543+
bufsize = 1 # Nearly unbuffered (XXX for now)
542544
if p2cwrite is not None:
543545
self.stdin = os.fdopen(p2cwrite, 'wb', bufsize)
544546
if c2pread is not None:
@@ -1007,6 +1009,7 @@ def _execute_child(self, args, executable, preexec_fn, close_fds,
10071009
if data:
10081010
os.waitpid(self.pid, 0)
10091011
child_exception = pickle.loads(data)
1012+
print("exc:", child_exception)
10101013
raise child_exception
10111014

10121015

Lib/test/test_io.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,7 @@ def testNewlines(self):
478478
[ '\r\n', input_lines ],
479479
]
480480

481-
encodings = ('utf-8', 'bz2')
481+
encodings = ('utf-8', 'latin-1')
482482

483483
# Try a range of pad sizes to test the case where \r is the last
484484
# character in TextIOWrapper._pending_line.

Lib/test/test_tempfile.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -730,7 +730,7 @@ def test_bound_methods(self):
730730
write("a" * 35)
731731
write("b" * 35)
732732
seek(0, 0)
733-
self.failUnless(read(70) == 'a'*35 + 'b'*35)
733+
self.assertEqual(read(70), 'a'*35 + 'b'*35)
734734

735735
test_classes.append(test_SpooledTemporaryFile)
736736

Modules/_fileio.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,12 @@ fileio_read(PyFileIOObject *self, PyObject *args)
375375
if (!PyArg_ParseTuple(args, "i", &size))
376376
return NULL;
377377

378+
if (size < 0) {
379+
PyErr_SetString(PyExc_ValueError,
380+
"negative read count");
381+
return NULL;
382+
}
383+
378384
bytes = PyBytes_FromStringAndSize(NULL, size);
379385
if (bytes == NULL)
380386
return NULL;

0 commit comments

Comments
 (0)