bpo-45234: Fix FileNotFound exception raised instead of IsADirectoryError in shutil.copyfile() (GH-28421) (GH-28507)

This was a regression from fixing BPO-43219.
(cherry picked from commit b7eac52b46)

Co-authored-by: andrei kulakov <andrei.avk@gmail.com>
This commit is contained in:
Miss Islington (bot) 2021-09-21 15:21:10 -07:00 committed by GitHub
parent df1006b561
commit 09390c837a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 71 additions and 27 deletions

View File

@ -261,8 +261,9 @@ def copyfile(src, dst, *, follow_symlinks=True):
if not follow_symlinks and _islink(src): if not follow_symlinks and _islink(src):
os.symlink(os.readlink(src), dst) os.symlink(os.readlink(src), dst)
else: else:
with open(src, 'rb') as fsrc:
try: try:
with open(src, 'rb') as fsrc, open(dst, 'wb') as fdst: with open(dst, 'wb') as fdst:
# macOS # macOS
if _HAS_FCOPYFILE: if _HAS_FCOPYFILE:
try: try:
@ -287,10 +288,10 @@ def copyfile(src, dst, *, follow_symlinks=True):
# Issue 43219, raise a less confusing exception # Issue 43219, raise a less confusing exception
except IsADirectoryError as e: except IsADirectoryError as e:
if os.path.exists(dst): if not os.path.exists(dst):
raise
else:
raise FileNotFoundError(f'Directory does not exist: {dst}') from e raise FileNotFoundError(f'Directory does not exist: {dst}') from e
else:
raise
return dst return dst

View File

@ -1147,6 +1147,28 @@ def test_copy_return_value(self):
rv = fn(src, os.path.join(dst_dir, 'bar')) rv = fn(src, os.path.join(dst_dir, 'bar'))
self.assertEqual(rv, os.path.join(dst_dir, 'bar')) self.assertEqual(rv, os.path.join(dst_dir, 'bar'))
def test_copy_dir(self):
self._test_copy_dir(shutil.copy)
def test_copy2_dir(self):
self._test_copy_dir(shutil.copy2)
def _test_copy_dir(self, copy_func):
src_dir = self.mkdtemp()
src_file = os.path.join(src_dir, 'foo')
dir2 = self.mkdtemp()
dst = os.path.join(src_dir, 'does_not_exist/')
write_file(src_file, 'foo')
if sys.platform == "win32":
err = PermissionError
else:
err = IsADirectoryError
self.assertRaises(err, copy_func, dir2, src_dir)
# raise *err* because of src rather than FileNotFoundError because of dst
self.assertRaises(err, copy_func, dir2, dst)
copy_func(src_file, dir2) # should not raise exceptions
### shutil.copyfile ### shutil.copyfile
@support.skip_unless_symlink @support.skip_unless_symlink
@ -1253,6 +1275,24 @@ def test_copyfile_nonexistent_dir(self):
write_file(src_file, 'foo') write_file(src_file, 'foo')
self.assertRaises(FileNotFoundError, shutil.copyfile, src_file, dst) self.assertRaises(FileNotFoundError, shutil.copyfile, src_file, dst)
def test_copyfile_copy_dir(self):
# Issue 45234
# test copy() and copyfile() raising proper exceptions when src and/or
# dst are directories
src_dir = self.mkdtemp()
src_file = os.path.join(src_dir, 'foo')
dir2 = self.mkdtemp()
dst = os.path.join(src_dir, 'does_not_exist/')
write_file(src_file, 'foo')
if sys.platform == "win32":
err = PermissionError
else:
err = IsADirectoryError
self.assertRaises(err, shutil.copyfile, src_dir, dst)
self.assertRaises(err, shutil.copyfile, src_file, src_dir)
self.assertRaises(err, shutil.copyfile, dir2, src_dir)
class TestArchives(BaseTest, unittest.TestCase): class TestArchives(BaseTest, unittest.TestCase):

View File

@ -0,0 +1,3 @@
Fixed a regression in :func:`~shutil.copyfile`, :func:`~shutil.copy`,
:func:`~shutil.copy2` raising :exc:`FileNotFoundError` when source is a
directory, which should raise :exc:`IsADirectoryError`