- Issue #6939: Fix file I/O objects in the `io` module to keep the original
  file position when calling `truncate()`.  It would previously change the
  file position to the given argument, which goes against the tradition of
  ftruncate() and other truncation APIs.  Patch by Pascal Chambon.
diff --git a/Lib/_pyio.py b/Lib/_pyio.py
index 49fbe19..a1c21d3 100644
--- a/Lib/_pyio.py
+++ b/Lib/_pyio.py
@@ -867,7 +867,7 @@
         elif pos < 0:
             raise ValueError("negative truncate position %r" % (pos,))
         del self._buffer[pos:]
-        return self.seek(pos)
+        return pos
 
     def readable(self):
         return True
@@ -1226,8 +1226,7 @@
         if pos is None:
             pos = self.tell()
         # Use seek to flush the read buffer.
-        self.seek(pos)
-        return BufferedWriter.truncate(self)
+        return BufferedWriter.truncate(self, pos)
 
     def read(self, n=None):
         if n is None:
@@ -1727,8 +1726,7 @@
         self.flush()
         if pos is None:
             pos = self.tell()
-        self.seek(pos)
-        return self.buffer.truncate()
+        return self.buffer.truncate(pos)
 
     def detach(self):
         if self.buffer is None:
diff --git a/Lib/test/test_fileio.py b/Lib/test/test_fileio.py
index 91c5251..cdc3123 100644
--- a/Lib/test/test_fileio.py
+++ b/Lib/test/test_fileio.py
@@ -329,6 +329,17 @@
             f.close()
             self.fail("no error for invalid mode: %s" % bad_mode)
 
+    def testTruncate(self):
+        f = _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, os.SEEK_END), 5)
+        f.truncate(15)
+        self.assertEqual(f.tell(), 5)
+        self.assertEqual(f.seek(0, os.SEEK_END), 15)
+
     def testTruncateOnWindows(self):
         def bug801631():
             # SF bug <http://www.python.org/sf/801631>
diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py
index 6f136f2..b91c0a1 100644
--- a/Lib/test/test_io.py
+++ b/Lib/test/test_io.py
@@ -234,6 +234,11 @@
 
     def write_ops(self, f):
         self.assertEqual(f.write(b"blah."), 5)
+        f.truncate(0)
+        self.assertEqual(f.tell(), 5)
+        f.seek(0)
+
+        self.assertEqual(f.write(b"blah."), 5)
         self.assertEqual(f.seek(0), 0)
         self.assertEqual(f.write(b"Hello."), 6)
         self.assertEqual(f.tell(), 6)
@@ -244,8 +249,9 @@
         self.assertEqual(f.write(b"h"), 1)
         self.assertEqual(f.seek(-1, 2), 13)
         self.assertEqual(f.tell(), 13)
+
         self.assertEqual(f.truncate(12), 12)
-        self.assertEqual(f.tell(), 12)
+        self.assertEqual(f.tell(), 13)
         self.assertRaises(TypeError, f.seek, 0.0)
 
     def read_ops(self, f, buffered=False):
@@ -290,7 +296,7 @@
         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)
-        self.assertEqual(f.tell(), self.LARGE + 1)
+        self.assertEqual(f.tell(), self.LARGE + 2)
         self.assertEqual(f.seek(0, 2), self.LARGE + 1)
         self.assertEqual(f.seek(-1, 2), self.LARGE)
         self.assertEqual(f.read(2), b"x")
@@ -988,7 +994,7 @@
             bufio = self.tp(raw, 8)
             bufio.write(b"abcdef")
             self.assertEqual(bufio.truncate(3), 3)
-            self.assertEqual(bufio.tell(), 3)
+            self.assertEqual(bufio.tell(), 6)
         with self.open(support.TESTFN, "rb", buffering=0) as f:
             self.assertEqual(f.read(), b"abc")
 
@@ -1374,6 +1380,14 @@
             self.assertEqual(s,
                 b"A" + b"B" * overwrite_size + b"A" * (9 - overwrite_size))
 
+    def test_truncate_after_read_or_write(self):
+        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)
+        self.assertEqual(bufio.write(b"BB"), 2) # the write buffer increases
+        self.assertEqual(bufio.truncate(), 4)
+
     def test_misbehaved_io(self):
         BufferedReaderTest.test_misbehaved_io(self)
         BufferedWriterTest.test_misbehaved_io(self)
diff --git a/Lib/test/test_largefile.py b/Lib/test/test_largefile.py
index c4faead..16da8d8 100644
--- a/Lib/test/test_largefile.py
+++ b/Lib/test/test_largefile.py
@@ -125,7 +125,7 @@
             f.seek(42)
             f.truncate(newsize)
             if self.new_io:
-                self.assertEqual(f.tell(), newsize)  # else wasn't truncated
+                self.assertEqual(f.tell(), 42)
             f.seek(0, 2)
             self.assertEqual(f.tell(), newsize)
             # XXX truncate(larger than true size) is ill-defined
@@ -133,7 +133,7 @@
             f.seek(0)
             f.truncate(1)
             if self.new_io:
-                self.assertEqual(f.tell(), 1)       # else pointer moved
+                self.assertEqual(f.tell(), 0)       # else pointer moved
             f.seek(0)
             self.assertEqual(len(f.read()), 1)  # else wasn't truncated
 
diff --git a/Lib/test/test_memoryio.py b/Lib/test/test_memoryio.py
index c2190b6..d06f1c9 100644
--- a/Lib/test/test_memoryio.py
+++ b/Lib/test/test_memoryio.py
@@ -76,7 +76,7 @@
         self.assertEqual(f.seek(0), 0)
         self.assertEqual(f.write(t("h")), 1)
         self.assertEqual(f.truncate(12), 12)
-        self.assertEqual(f.tell(), 12)
+        self.assertEqual(f.tell(), 1)
 
     def test_write(self):
         buf = self.buftype("hello world\n")
@@ -127,7 +127,8 @@
         # truncate() accepts long objects
         self.assertEqual(memio.truncate(4L), 4)
         self.assertEqual(memio.getvalue(), buf[:4])
-        self.assertEqual(memio.tell(), 4)
+        self.assertEqual(memio.tell(), 6)
+        memio.seek(0, 2)
         memio.write(buf)
         self.assertEqual(memio.getvalue(), buf[:4] + buf)
         pos = memio.tell()