summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTarek Ziadé <ziade.tarek@gmail.com>2010-05-05 22:39:31 (GMT)
committerTarek Ziadé <ziade.tarek@gmail.com>2010-05-05 22:39:31 (GMT)
commit9623980d01dae6218ef74d00bb9b1e866cfce135 (patch)
treeba80d8c10fb5d3ba9e736a4262ad8358e2ab29e9
parentbc5f75cca215fa52cb6f5dec1c3045ecf6fe24cd (diff)
downloadcpython-9623980d01dae6218ef74d00bb9b1e866cfce135.zip
cpython-9623980d01dae6218ef74d00bb9b1e866cfce135.tar.gz
cpython-9623980d01dae6218ef74d00bb9b1e866cfce135.tar.bz2
Merged revisions 80833 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/branches/py3k ................ r80833 | tarek.ziade | 2010-05-06 00:27:31 +0200 (Thu, 06 May 2010) | 9 lines Merged revisions 80830 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r80830 | tarek.ziade | 2010-05-06 00:15:31 +0200 (Thu, 06 May 2010) | 1 line Fixed #4265: shutil.copyfile() was leaking file descriptors when disk fills ........ ................
-rw-r--r--Lib/shutil.py13
-rw-r--r--Lib/test/test_shutil.py106
-rw-r--r--Misc/NEWS3
3 files changed, 112 insertions, 10 deletions
diff --git a/Lib/shutil.py b/Lib/shutil.py
index 78504b9..2befb47 100644
--- a/Lib/shutil.py
+++ b/Lib/shutil.py
@@ -63,15 +63,10 @@ def copyfile(src, dst):
# XXX What about other special files? (sockets, devices...)
if stat.S_ISFIFO(st.st_mode):
raise SpecialFileError("`%s` is a named pipe" % fn)
- try:
- fsrc = open(src, 'rb')
- fdst = open(dst, 'wb')
- copyfileobj(fsrc, fdst)
- finally:
- if fdst:
- fdst.close()
- if fsrc:
- fsrc.close()
+
+ with open(src, 'rb') as fsrc:
+ with open(dst, 'wb') as fdst:
+ copyfileobj(fsrc, fdst)
def copymode(src, dst):
"""Copy mode bits from src to dst"""
diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py
index a747d6f..e86e1d3 100644
--- a/Lib/test/test_shutil.py
+++ b/Lib/test/test_shutil.py
@@ -415,8 +415,112 @@ class TestMove(unittest.TestCase):
finally:
shutil.rmtree(TESTFN, ignore_errors=True)
+class TestCopyFile(unittest.TestCase):
+
+ _delete = False
+
+ class Faux(object):
+ _entered = False
+ _exited_with = None
+ _raised = False
+ def __init__(self, raise_in_exit=False, suppress_at_exit=True):
+ self._raise_in_exit = raise_in_exit
+ self._suppress_at_exit = suppress_at_exit
+ def read(self, *args):
+ return ''
+ def __enter__(self):
+ self._entered = True
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ self._exited_with = exc_type, exc_val, exc_tb
+ if self._raise_in_exit:
+ self._raised = True
+ raise IOError("Cannot close")
+ return self._suppress_at_exit
+
+ def tearDown(self):
+ if self._delete:
+ del shutil.open
+
+ def _set_shutil_open(self, func):
+ shutil.open = func
+ self._delete = True
+
+ def test_w_source_open_fails(self):
+ def _open(filename, mode='r'):
+ if filename == 'srcfile':
+ raise IOError('Cannot open "srcfile"')
+ assert 0 # shouldn't reach here.
+
+ self._set_shutil_open(_open)
+
+ self.assertRaises(IOError, shutil.copyfile, 'srcfile', 'destfile')
+
+ def test_w_dest_open_fails(self):
+
+ srcfile = self.Faux()
+
+ def _open(filename, mode='r'):
+ if filename == 'srcfile':
+ return srcfile
+ if filename == 'destfile':
+ raise IOError('Cannot open "destfile"')
+ assert 0 # shouldn't reach here.
+
+ self._set_shutil_open(_open)
+
+ shutil.copyfile('srcfile', 'destfile')
+ self.assertTrue(srcfile._entered)
+ self.assertTrue(srcfile._exited_with[0] is IOError)
+ self.assertEqual(srcfile._exited_with[1].args,
+ ('Cannot open "destfile"',))
+
+ def test_w_dest_close_fails(self):
+
+ srcfile = self.Faux()
+ destfile = self.Faux(True)
+
+ def _open(filename, mode='r'):
+ if filename == 'srcfile':
+ return srcfile
+ if filename == 'destfile':
+ return destfile
+ assert 0 # shouldn't reach here.
+
+ self._set_shutil_open(_open)
+
+ shutil.copyfile('srcfile', 'destfile')
+ self.assertTrue(srcfile._entered)
+ self.assertTrue(destfile._entered)
+ self.assertTrue(destfile._raised)
+ self.assertTrue(srcfile._exited_with[0] is IOError)
+ self.assertEqual(srcfile._exited_with[1].args,
+ ('Cannot close',))
+
+ def test_w_source_close_fails(self):
+
+ srcfile = self.Faux(True)
+ destfile = self.Faux()
+
+ def _open(filename, mode='r'):
+ if filename == 'srcfile':
+ return srcfile
+ if filename == 'destfile':
+ return destfile
+ assert 0 # shouldn't reach here.
+
+ self._set_shutil_open(_open)
+
+ self.assertRaises(IOError,
+ shutil.copyfile, 'srcfile', 'destfile')
+ self.assertTrue(srcfile._entered)
+ self.assertTrue(destfile._entered)
+ self.assertFalse(destfile._raised)
+ self.assertTrue(srcfile._exited_with[0] is None)
+ self.assertTrue(srcfile._raised)
+
+
def test_main():
- support.run_unittest(TestShutil, TestMove)
+ support.run_unittest(TestShutil, TestMove, TestCopyFile)
if __name__ == '__main__':
test_main()
diff --git a/Misc/NEWS b/Misc/NEWS
index d819e27..7147f77 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -40,6 +40,9 @@ Core and Builtins
Library
-------
+- Issue #4265: shutil.copyfile() was leaking file descriptors when disk fills.
+ Patch by Tres Seaver.
+
- Issue #8621: uuid.uuid4() returned the same sequence of values in the
parent and any children created using ``os.fork`` on MacOS X 10.6.