diff -r 3ebeeed1eb28 Lib/_pyio.py --- a/Lib/_pyio.py Fri Apr 10 16:22:14 2015 +0300 +++ b/Lib/_pyio.py Fri Apr 10 18:59:17 2015 +0300 @@ -1421,7 +1421,8 @@ class FileIO(RawIOBase): raise ValueError('negative file descriptor') else: fd = -1 - + if 0 in os.fsencode(file): + raise ValueError('embedded null character') if not isinstance(mode, str): raise TypeError('invalid mode: %s' % (mode,)) if not set(mode) <= set('xrwab+'): @@ -1637,18 +1638,19 @@ class FileIO(RawIOBase): self._checkClosed() return os.lseek(self._fd, 0, SEEK_CUR) - def truncate(self, size=None): - """Truncate the file to at most size bytes. - - Size defaults to the current file position, as returned by tell(). - The current file position is changed to the value of size. - """ - self._checkClosed() - self._checkWritable() - if size is None: - size = self.tell() - os.ftruncate(self._fd, size) - return size + if hasattr(os, 'ftruncate'): + def truncate(self, size=None): + """Truncate the file to at most size bytes. + + Size defaults to the current file position, as returned by tell(). + The current file position is changed to the value of size. + """ + self._checkClosed() + self._checkWritable() + if size is None: + size = self.tell() + os.ftruncate(self._fd, size) + return size def close(self): """Close the file. diff -r 3ebeeed1eb28 Lib/test/support/__init__.py --- a/Lib/test/support/__init__.py Fri Apr 10 16:22:14 2015 +0300 +++ b/Lib/test/support/__init__.py Fri Apr 10 18:59:17 2015 +0300 @@ -88,7 +88,7 @@ except ImportError: "skip_unless_symlink", "requires_gzip", "requires_bz2", "requires_lzma", "bigmemtest", "bigaddrspacetest", "cpython_only", "get_attribute", "requires_IEEE_754", "skip_unless_xattr", "requires_zlib", - "anticipate_failure", "load_package_tests", + "anticipate_failure", "load_package_tests", "skipIfRaises", # sys "is_jython", "check_impl_detail", # network @@ -2052,6 +2052,13 @@ def args_from_interpreter_flags(): settings in sys.flags and sys.warnoptions.""" return subprocess._args_from_interpreter_flags() +@contextlib.contextmanager +def skipIfRaises(exceptions): + try: + yield + except exceptions as exc: + raise unittest.SkipTest(str(exc)) + #============================================================ # Support for assertions about logging. #============================================================ diff -r 3ebeeed1eb28 Lib/test/test_file.py --- a/Lib/test/test_file.py Fri Apr 10 16:22:14 2015 +0300 +++ b/Lib/test/test_file.py Fri Apr 10 18:59:17 2015 +0300 @@ -7,7 +7,7 @@ from weakref import proxy import io import _pyio as pyio -from test.support import TESTFN, run_unittest +from test.support import TESTFN, run_unittest, skipIfRaises from collections import UserList class AutoFileTests: @@ -199,7 +199,8 @@ class OtherFileTests: if f.tell() != 5: self.fail("File pos after read wrong %d" % f.tell()) - f.truncate() + with skipIfRaises(io.UnsupportedOperation): + f.truncate() if f.tell() != 5: self.fail("File pos after ftruncate wrong %d" % f.tell()) diff -r 3ebeeed1eb28 Lib/test/test_fileio.py --- a/Lib/test/test_fileio.py Fri Apr 10 16:22:14 2015 +0300 +++ b/Lib/test/test_fileio.py Fri Apr 10 18:59:17 2015 +0300 @@ -9,7 +9,8 @@ from array import array from weakref import proxy from functools import wraps -from test.support import TESTFN, check_warnings, run_unittest, make_bad_fd, cpython_only +from test.support import (TESTFN, check_warnings, run_unittest, make_bad_fd, + cpython_only, skipIfRaises) from collections import UserList import _io # C implementation of io @@ -279,7 +280,8 @@ class AutoFileTests: @ClosedFDRaises def testErrnoOnClosedTruncate(self, f): - f.truncate(0) + with skipIfRaises(io.UnsupportedOperation): + f.truncate(0) @ClosedFD def testErrnoOnClosedSeekable(self, f): @@ -455,37 +457,36 @@ class OtherFileTests: self.fail("no error for invalid mode: %s" % bad_mode) def testTruncate(self): - f = self.FileIO(TESTFN, 'w') - f.write(bytes(bytearray(range(10)))) - self.assertEqual(f.tell(), 10) - f.truncate(5) - self.assertEqual(f.tell(), 10) - self.assertEqual(f.seek(0, io.SEEK_END), 5) - f.truncate(15) - self.assertEqual(f.tell(), 5) - self.assertEqual(f.seek(0, io.SEEK_END), 15) - f.close() + with self.FileIO(TESTFN, 'w') as f: + f.write(bytes(bytearray(range(10)))) + self.assertEqual(f.tell(), 10) + with skipIfRaises(io.UnsupportedOperation): + f.truncate(5) + self.assertEqual(f.tell(), 10) + self.assertEqual(f.seek(0, io.SEEK_END), 5) + f.truncate(15) + self.assertEqual(f.tell(), 5) + self.assertEqual(f.seek(0, io.SEEK_END), 15) def testTruncateOnWindows(self): def bug801631(): # SF bug # "file.truncate fault on windows" - f = self.FileIO(TESTFN, 'w') - f.write(bytes(range(11))) - f.close() + with self.FileIO(TESTFN, 'w') as f: + f.write(bytes(range(11))) - f = self.FileIO(TESTFN,'r+') - data = f.read(5) - if data != bytes(range(5)): - self.fail("Read on file opened for update failed %r" % data) - if f.tell() != 5: - self.fail("File pos after read wrong %d" % f.tell()) + with self.FileIO(TESTFN,'r+') as f: + data = f.read(5) + if data != bytes(range(5)): + self.fail("Read on file opened for update failed %r" % data) + if f.tell() != 5: + self.fail("File pos after read wrong %d" % f.tell()) - f.truncate() - if f.tell() != 5: - self.fail("File pos after ftruncate wrong %d" % f.tell()) + with skipIfRaises(io.UnsupportedOperation): + f.truncate() + if f.tell() != 5: + self.fail("File pos after ftruncate wrong %d" % f.tell()) - f.close() size = os.path.getsize(TESTFN) if size != 5: self.fail("File size after ftruncate wrong %d" % size) diff -r 3ebeeed1eb28 Lib/test/test_io.py --- a/Lib/test/test_io.py Fri Apr 10 16:22:14 2015 +0300 +++ b/Lib/test/test_io.py Fri Apr 10 18:59:17 2015 +0300 @@ -271,7 +271,8 @@ class IOTest(unittest.TestCase): def write_ops(self, f): self.assertEqual(f.write(b"blah."), 5) - f.truncate(0) + with support.skipIfRaises(self.UnsupportedOperation): + f.truncate(0) self.assertEqual(f.tell(), 5) f.seek(0) @@ -329,7 +330,8 @@ class IOTest(unittest.TestCase): self.assertEqual(f.write(b"xxx"), 3) self.assertEqual(f.tell(), self.LARGE + 3) self.assertEqual(f.seek(-1, 1), self.LARGE + 2) - self.assertEqual(f.truncate(), self.LARGE + 2) + with support.skipIfRaises(self.UnsupportedOperation): + self.assertEqual(f.truncate(), self.LARGE + 2) self.assertEqual(f.tell(), self.LARGE + 2) self.assertEqual(f.seek(0, 2), self.LARGE + 2) self.assertEqual(f.truncate(self.LARGE + 1), self.LARGE + 1) @@ -1410,7 +1412,8 @@ class BufferedWriterTest(unittest.TestCa with self.open(support.TESTFN, self.write_mode, buffering=0) as raw: bufio = self.tp(raw, 8) bufio.write(b"abcdef") - self.assertEqual(bufio.truncate(3), 3) + with support.skipIfRaises(self.UnsupportedOperation): + self.assertEqual(bufio.truncate(3), 3) self.assertEqual(bufio.tell(), 6) with self.open(support.TESTFN, "rb", buffering=0) as f: self.assertEqual(f.read(), b"abc") @@ -1904,7 +1907,8 @@ class BufferedRandomTest(BufferedReaderT raw = self.BytesIO(b"A" * 10) bufio = self.tp(raw, 100) self.assertEqual(bufio.read(2), b"AA") # the read buffer gets filled - self.assertEqual(bufio.truncate(), 2) + with support.skipIfRaises(self.UnsupportedOperation): + self.assertEqual(bufio.truncate(), 2) self.assertEqual(bufio.write(b"BB"), 2) # the write buffer increases self.assertEqual(bufio.truncate(), 4) @@ -2419,35 +2423,34 @@ class TextIOWrapperTest(unittest.TestCas def test_basic_io(self): for chunksize in (1, 2, 3, 4, 5, 15, 16, 17, 31, 32, 33, 63, 64, 65): for enc in "ascii", "latin-1", "utf-8" :# , "utf-16-be", "utf-16-le": - f = self.open(support.TESTFN, "w+", encoding=enc) - f._CHUNK_SIZE = chunksize - self.assertEqual(f.write("abc"), 3) - f.close() - f = self.open(support.TESTFN, "r+", encoding=enc) - f._CHUNK_SIZE = chunksize - self.assertEqual(f.tell(), 0) - self.assertEqual(f.read(), "abc") - cookie = f.tell() - self.assertEqual(f.seek(0), 0) - self.assertEqual(f.read(None), "abc") - f.seek(0) - self.assertEqual(f.read(2), "ab") - self.assertEqual(f.read(1), "c") - self.assertEqual(f.read(1), "") - self.assertEqual(f.read(), "") - self.assertEqual(f.tell(), cookie) - self.assertEqual(f.seek(0), 0) - self.assertEqual(f.seek(0, 2), cookie) - self.assertEqual(f.write("def"), 3) - self.assertEqual(f.seek(cookie), cookie) - self.assertEqual(f.read(), "def") - if enc.startswith("utf"): - self.multi_line_test(f, enc) - f.close() + with self.open(support.TESTFN, "w+", encoding=enc) as f: + f._CHUNK_SIZE = chunksize + self.assertEqual(f.write("abc"), 3) + with self.open(support.TESTFN, "r+", encoding=enc) as f: + f._CHUNK_SIZE = chunksize + self.assertEqual(f.tell(), 0) + self.assertEqual(f.read(), "abc") + cookie = f.tell() + self.assertEqual(f.seek(0), 0) + self.assertEqual(f.read(None), "abc") + f.seek(0) + self.assertEqual(f.read(2), "ab") + self.assertEqual(f.read(1), "c") + self.assertEqual(f.read(1), "") + self.assertEqual(f.read(), "") + self.assertEqual(f.tell(), cookie) + self.assertEqual(f.seek(0), 0) + self.assertEqual(f.seek(0, 2), cookie) + self.assertEqual(f.write("def"), 3) + self.assertEqual(f.seek(cookie), cookie) + self.assertEqual(f.read(), "def") + if enc.startswith("utf"): + self.multi_line_test(f, enc) def multi_line_test(self, f, enc): f.seek(0) - f.truncate() + with support.skipIfRaises(self.io.UnsupportedOperation): + f.truncate() sample = "s\xff\u0fff\uffff" wlines = [] for size in (0, 1, 2, 3, 4, 5, 30, 31, 32, 33, 62, 63, 64, 65, 1000): diff -r 3ebeeed1eb28 Lib/test/test_largefile.py --- a/Lib/test/test_largefile.py Fri Apr 10 16:22:14 2015 +0300 +++ b/Lib/test/test_largefile.py Fri Apr 10 18:59:17 2015 +0300 @@ -5,7 +5,7 @@ import os import stat import sys import unittest -from test.support import TESTFN, requires, unlink +from test.support import TESTFN, requires, unlink, skipIfRaises import io # C implementation of io import _pyio as pyio # Python implementation of io @@ -103,7 +103,8 @@ class LargeFileTest: # Cut it back via seek + truncate with no argument. newsize = size - 10 f.seek(newsize) - f.truncate() + with skipIfRaises(io.UnsupportedOperation): + f.truncate() self.assertEqual(f.tell(), newsize) # else pointer moved f.seek(0, 2) self.assertEqual(f.tell(), newsize) # else wasn't truncated