[Jython-checkins] jython: Close and delete files every time in test_file2k.

jeff.allen jython-checkins at python.org
Wed Nov 20 21:07:41 CET 2013


http://hg.python.org/jython/rev/c84270fca342
changeset:   7155:c84270fca342
user:        Jeff Allen <ja.py at farowl.co.uk>
date:        Wed Nov 20 08:00:26 2013 +0000
summary:
  Close and delete files every time in test_file2k.
Each test now closes and deletes its test file, not relying on deterministic
garbage collection. On Windows, open files cannot be deleted, and so sloppiness
in one test used to create a cascade of "unlink" errors in subsequent tests.

files:
  Lib/test/test_file2k.py |  190 +++++++++++++++------------
  1 files changed, 108 insertions(+), 82 deletions(-)


diff --git a/Lib/test/test_file2k.py b/Lib/test/test_file2k.py
--- a/Lib/test/test_file2k.py
+++ b/Lib/test/test_file2k.py
@@ -1,7 +1,7 @@
 import sys
 import os
+import errno
 import unittest
-import itertools
 import time
 from array import array
 from weakref import proxy
@@ -190,14 +190,29 @@
 
 class OtherFileTests(unittest.TestCase):
 
+    def setUp(self):
+        # (Jython addition) track open file so we can clean up
+        self.f = None
+        self.filename = TESTFN
+
+    def tearDown(self):
+        # (Jython addition) clean up to prevent errors cascading
+        if self.f:
+            self.f.close()
+        try:
+            os.remove(self.filename)
+        except EnvironmentError as ee:
+            if ee.errno != errno.ENOENT:
+                raise ee
+
     def testOpenDir(self):
         this_dir = os.path.dirname(__file__) or os.curdir
         for mode in (None, "w"):
             try:
                 if mode:
-                    f = open(this_dir, mode)
+                    self.f = open(this_dir, mode)
                 else:
-                    f = open(this_dir)
+                    self.f = open(this_dir)
             except IOError as e:
                 self.assertEqual(e.filename, this_dir)
             else:
@@ -207,7 +222,7 @@
         # check invalid mode strings
         for mode in ("", "aU", "wU+"):
             try:
-                f = open(TESTFN, mode)
+                self.f = f = open(TESTFN, mode)
             except ValueError:
                 pass
             else:
@@ -218,7 +233,7 @@
         # Issue3965: avoid a crash on Windows when filename is unicode
         for name in (TESTFN, unicode(TESTFN), unicode(TESTFN + '\t')):
             try:
-                f = open(name, "rr")
+                self.f = f = open(name, "rr")
             except (IOError, ValueError):
                 pass
             else:
@@ -236,7 +251,7 @@
 
     def testUnicodeOpen(self):
         # verify repr works for unicode too
-        f = open(unicode(TESTFN), "w")
+        self.f = f = open(unicode(TESTFN), "w")
         self.assertTrue(repr(f).startswith("<open file u'" + TESTFN))
         f.close()
         os.unlink(TESTFN)
@@ -245,7 +260,7 @@
         # verify that we get a sensible error message for bad mode argument
         bad_mode = "qwerty"
         try:
-            f = open(TESTFN, bad_mode)
+            self.f = f = open(TESTFN, bad_mode)
         except ValueError, msg:
             if msg.args[0] != 0:
                 s = str(msg)
@@ -262,11 +277,11 @@
         # misbehaviour especially with repeated close() calls
         for s in (-1, 0, 1, 512):
             try:
-                f = open(TESTFN, 'w', s)
+                self.f = f = open(TESTFN, 'w', s)
                 f.write(str(s))
                 f.close()
                 f.close()
-                f = open(TESTFN, 'r', s)
+                self.f = f = open(TESTFN, 'r', s)
                 d = int(f.read())
                 f.close()
                 f.close()
@@ -275,16 +290,15 @@
             self.assertEqual(d, s)
 
     def testTruncateOnWindows(self):
-        os.unlink(TESTFN)
 
         def bug801631():
             # SF bug <http://www.python.org/sf/801631>
             # "file.truncate fault on windows"
-            f = open(TESTFN, 'wb')
+            self.f = f = open(TESTFN, 'wb')
             f.write('12345678901')   # 11 bytes
             f.close()
 
-            f = open(TESTFN,'rb+')
+            self.f = f = open(TESTFN,'rb+')
             data = f.read(5)
             if data != '12345':
                 self.fail("Read on file opened for update failed %r" % data)
@@ -339,7 +353,7 @@
             bag.close()
             # Test for appropriate errors mixing read* and iteration
             for methodname, args in methods:
-                f = open(TESTFN)
+                self.f = f = open(TESTFN)
                 if f.next() != filler:
                     self.fail, "Broken testfile"
                 meth = getattr(f, methodname)
@@ -359,7 +373,7 @@
             # ("h", "a", "m", "\n"), so 4096 lines of that should get us
             # exactly on the buffer boundary for any power-of-2 buffersize
             # between 4 and 16384 (inclusive).
-            f = open(TESTFN)
+            self.f = f = open(TESTFN)
             for i in range(nchunks):
                 f.next()
             testline = testlines.pop(0)
@@ -401,7 +415,7 @@
                 self.fail("readlines() after next() with empty buffer "
                           "failed. Got %r, expected %r" % (line, testline))
             # Reading after iteration hit EOF shouldn't hurt either
-            f = open(TESTFN)
+            self.f = f = open(TESTFN)
             try:
                 for line in f:
                     pass
@@ -441,10 +455,14 @@
     # (including close()) concurrently without crashing the Python interpreter.
     # See #815646, #595601
 
+    # Modified for Jython so that each worker thread holds *and closes* its own
+    # file object, since we cannot rely on immediate garbage collection closing
+    # files. (Open file objects prevent deletion of TESTFN on Windows at least.)
+
     def setUp(self):
         self._threads = test_support.threading_setup()
-        self.f = None
         self.filename = TESTFN
+        self.exc_info = None
         with open(self.filename, "w") as f:
             f.write("\n".join("0123456789"))
         self._count_lock = threading.Lock()
@@ -453,35 +471,32 @@
         self.use_buffering = False
 
     def tearDown(self):
-        if self.f:
-            try:
-                self.f.close()
-            except (EnvironmentError, ValueError):
-                pass
         try:
             os.remove(self.filename)
-        except EnvironmentError:
-            pass
+        except EnvironmentError as ee:
+            # (Jython addition) detect failure common on Windows, on missing
+            # close, that creates spurious errors in subsequent tests.
+            if ee.errno != errno.ENOENT:
+                raise ee
         test_support.threading_cleanup(*self._threads)
 
     def _create_file(self):
         if self.use_buffering:
-            self.f = open(self.filename, "w+", buffering=1024*16)
+            return open(self.filename, "w+", buffering=1024*16)
         else:
-            self.f = open(self.filename, "w+")
+            return open(self.filename, "w+")
 
-    def _close_file(self):
+    def _close_file(self, f):
         with self._count_lock:
             self.close_count += 1
-        self.f.close()
+        f.close()
         with self._count_lock:
             self.close_success_count += 1
 
-    def _close_and_reopen_file(self):
-        self._close_file()
-        # if close raises an exception thats fine, self.f remains valid so
-        # we don't need to reopen.
-        self._create_file()
+    # Close one file object and return a new one
+    def _close_and_reopen_file(self, f):
+        self._close_file(f)
+        return self._create_file()
 
     def _run_workers(self, func, nb_workers, duration=0.2):
         with self._count_lock:
@@ -508,104 +523,116 @@
                 t.join()
 
     def _test_close_open_io(self, io_func, nb_workers=5):
+
         def worker():
-            self._create_file()
-            funcs = itertools.cycle((
-                lambda: io_func(),
-                lambda: self._close_and_reopen_file(),
-            ))
-            for f in funcs:
-                if not self.do_continue:
-                    break
-                try:
-                    f()
-                except (IOError, ValueError):
-                    pass
+            # Each worker has its own currently open file object
+            myfile = None
+            try:
+                myfile = self._create_file()
+                while self.do_continue:
+                    io_func(myfile)
+                    myfile = self._close_and_reopen_file(myfile)
+            except Exception as e:
+                # Stop the test (other threads) and remember why
+                self.do_continue = False
+                self.exc_info = sys.exc_info()
+            # Finally close the last file object
+            if myfile:
+                self._close_file(myfile)
+
         self._run_workers(worker, nb_workers)
+        if self.exc_info:
+            # Some worker saved an exception: re-raise it now
+            raise self.exc_info[0], self.exc_info[1], self.exc_info[2]
+
         if test_support.verbose:
             # Useful verbose statistics when tuning this test to take
             # less time to run but still ensuring that its still useful.
             #
             # the percent of close calls that raised an error
-            percent = 100. - 100.*self.close_success_count/self.close_count
+            percent = 100.
+            if self.close_count > 0:
+                percent -= 100.*self.close_success_count/self.close_count
             print self.close_count, ('%.4f ' % percent),
 
+    # Each test function defines an operation on the worker's file object
+
     def test_close_open(self):
-        def io_func():
+        def io_func(f):
             pass
         self._test_close_open_io(io_func)
 
     def test_close_open_flush(self):
-        def io_func():
-            self.f.flush()
+        def io_func(f):
+            f.flush()
         self._test_close_open_io(io_func)
 
     def test_close_open_iter(self):
-        def io_func():
-            list(iter(self.f))
+        def io_func(f):
+            list(iter(f))
         self._test_close_open_io(io_func)
 
     def test_close_open_isatty(self):
-        def io_func():
-            self.f.isatty()
+        def io_func(f):
+            f.isatty()
         self._test_close_open_io(io_func)
 
     def test_close_open_print(self):
-        def io_func():
-            print >> self.f, ''
+        def io_func(f):
+            print >> f, ''
         self._test_close_open_io(io_func)
 
     @unittest.skipIf(test_support.is_jython, "FIXME: Not working on Jython")
     def test_close_open_print_buffered(self):
         self.use_buffering = True
-        def io_func():
-            print >> self.f, ''
+        def io_func(f):
+            print >> f, ''
         self._test_close_open_io(io_func)
 
     def test_close_open_read(self):
-        def io_func():
-            self.f.read(0)
+        def io_func(f):
+            f.read(0)
         self._test_close_open_io(io_func)
 
     def test_close_open_readinto(self):
-        def io_func():
+        def io_func(f):
             a = array('c', 'xxxxx')
-            self.f.readinto(a)
+            f.readinto(a)
         self._test_close_open_io(io_func)
 
     def test_close_open_readline(self):
-        def io_func():
-            self.f.readline()
+        def io_func(f):
+            f.readline()
         self._test_close_open_io(io_func)
 
     def test_close_open_readlines(self):
-        def io_func():
-            self.f.readlines()
+        def io_func(f):
+            f.readlines()
         self._test_close_open_io(io_func)
 
     def test_close_open_seek(self):
-        def io_func():
-            self.f.seek(0, 0)
+        def io_func(f):
+            f.seek(0, 0)
         self._test_close_open_io(io_func)
 
     def test_close_open_tell(self):
-        def io_func():
-            self.f.tell()
+        def io_func(f):
+            f.tell()
         self._test_close_open_io(io_func)
 
     def test_close_open_truncate(self):
-        def io_func():
-            self.f.truncate()
+        def io_func(f):
+            f.truncate()
         self._test_close_open_io(io_func)
 
     def test_close_open_write(self):
-        def io_func():
-            self.f.write('')
+        def io_func(f):
+            f.write('')
         self._test_close_open_io(io_func)
 
     def test_close_open_writelines(self):
-        def io_func():
-            self.f.writelines('')
+        def io_func(f):
+            f.writelines('')
         self._test_close_open_io(io_func)
 
 
@@ -683,14 +710,13 @@
 
 
 def test_main():
-    # Historically, these tests have been sloppy about removing TESTFN.
-    # So get rid of it no matter what.
-    try:
-        run_unittest(AutoFileTests, OtherFileTests, FileSubclassTests,
-            FileThreadingTests, StdoutTests)
-    finally:
-        if os.path.exists(TESTFN):
-            os.unlink(TESTFN)
+    run_unittest(
+             AutoFileTests, 
+             OtherFileTests, 
+             FileSubclassTests,
+             FileThreadingTests, 
+             StdoutTests
+         )
 
 if __name__ == '__main__':
     test_main()

-- 
Repository URL: http://hg.python.org/jython


More information about the Jython-checkins mailing list