8000 bpo-45234: Fix FileNotFound exception raised instead of IsADirectoryE… · python/cpython@09390c8 · GitHub
[go: up one dir, main page]

Skip to content

Commit 09390c8

Browse files
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 b7eac52) Co-authored-by: andrei kulakov <andrei.avk@gmail.com>
1 parent df1006b commit 09390c8

File tree

3 files changed

+73
-29
lines changed

3 files changed

+73
-29
lines changed

Lib/shutil.py

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -261,36 +261,37 @@ def copyfile(src, dst, *, follow_symlinks=True):
261261
if not follow_symlinks and _islink(src):
262262
os.symlink(os.readlink(src), dst)
263263
else:
264-
try:
265-
with open(src, 'rb') as fsrc, open(dst, 'wb') as fdst:
266-
# macOS
267-
if _HAS_FCOPYFILE:
268-
try:
269-
_fastcopy_fcopyfile(fsrc, fdst, posix._COPYFILE_DATA)
270-
return dst
271-
except _GiveupOnFastCopy:
272-
pass
273-
# Linux
274-
elif _USE_CP_SENDFILE:
275-
try:
276-
_fastcopy_sendfile(fsrc, fdst)
264+
with open(src, 'rb') as fsrc:
265+
try:
266+
with open(dst, 'wb') as fdst:
267+
# macOS
268+
if _HAS_FCOPYFILE:
269+
try:
270+
_fastcopy_fcopyfile(fsrc, fdst, posix._COPYFILE_DATA)
271+
return dst
272+
except _GiveupOnFastCopy:
273+
pass
274+
# Linux
275+
elif _USE_CP_SENDFILE:
276+
try:
277+
_fastcopy_sendfile(fsrc, fdst)
278+
return dst
279+
except _GiveupOnFastCopy:
280+
pass
281+
# Windows, see:
282+
# https://github.com/python/cpython/pull/7160#discussion_r195405230
283+
elif _WINDOWS and file_size > 0:
284+
_copyfileobj_readinto(fsrc, fdst, min(file_size, COPY_BUFSIZE))
277285
return dst
278-
except _GiveupOnFastCopy:
279-
pass
280-
# Windows, see:
281-
# https://github.com/python/cpython/pull/7160#discussion_r195405230
282-
elif _WINDOWS and file_size > 0:
283-
_copyfileobj_readinto(fsrc, fdst, min(file_size, COPY_BUFSIZE))
284-
return dst
285-
286-
copyfileobj(fsrc, fdst)
287-
288-
# Issue 43219, raise a less confusing exception
289-
except IsADirectoryError as e:
290-
if os.path.exists(dst):
291-
raise
292-
else:
293-
raise FileNotFoundError(f'Directory does not exist: {dst}') from e
286+
287+
copyfileobj(fsrc, fdst)
288+
289+
# Issue 43219, raise a less confusing exception
290+
except IsADirectoryError as e:
291+
if not os.path.exists(dst):
292+
raise FileNotFoundError(f'Directory does not exist: {dst}') from e
293+
else:
294+
raise
294295

295296
return dst
296297

Lib/test/test_shutil.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,6 +1147,28 @@ def test_copy_return_value(self):
11471147
rv = fn(src, os.path.join(dst_dir, 'bar'))
11481148
self.assertEqual(rv, os.path.join(dst_dir, 'bar'))
11491149

1150+
def test_copy_dir(self):
1151+
self._test_copy_dir(shutil.copy)
1152+
1153+
def test_copy2_dir(self):
1154+
self._test_copy_dir(shutil.copy2)
1155+
1156+
def _test_copy_dir(self, copy_func):
1157+
src_dir = self.mkdtemp()
1158+
src_file = os.path.join(src_dir, 'foo')
1159+
dir2 = self.mkdtemp()
1160+
dst = os.path.join(src_dir, 'does_not_exist/')
1161+
write_file(src_file, 'foo')
1162+
if sys.platform == "win32":
1163+
err = PermissionError
1164+
else:
1165+
err = IsADirectoryError
1166+
self.assertRaises(err, copy_func, dir2, src_dir)
1167+
1168+
# raise *err* because of src rather than FileNotFoundError because of dst
1169+
self.assertRaises(err, copy_func, dir2, dst)
1170+
copy_func(src_file, dir2) # should not raise exceptions
1171+
11501172
### shutil.copyfile
11511173

11521174
@support.skip_unless_symlink
@@ -1253,6 +1275,24 @@ def test_copyfile_nonexistent_dir(self):
12531275
write_file(src_file, 'foo')
12541276
self.assertRaises(FileNotFoundError, shutil.copyfile, src_file, dst)
12551277

1278+
def test_copyfile_copy_dir(self):
1279+
# Issue 45234
1280+
# test copy() and copyfile() raising proper exceptions when src and/or
1281+
# dst are directories
1282+
src_dir = self.mkdtemp()
1283+
src_file = os.path.join(src_dir, 'foo')
1284+
dir2 = self.mkdtemp()
1285+
dst = os.path.join(src_dir, 'does_not_exist/')
1286+
write_file(src_file, 'foo')
1287+
if sys.platform == "win32":
1288+
err = PermissionError
1289+
else:
1290+
err = IsADirectoryError
1291+
1292+
self.assertRaises(err, shutil.copyfile, src_dir, dst)
1293+
self.assertRaises(err, shutil.copyfile, src_file, src_dir)
1294+
self.assertRaises(err, shutil.copyfile, dir2, src_dir)
1295+
12561296

12571297
class TestArchives(BaseTest, unittest.TestCase):
12581298

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fixed a regression in :func:`~shutil.copyfile`, :func:`~shutil.copy`,
2+
:func:`~shutil.copy2` raising :exc:`FileNotFoundError` when source is a
3+
directory, which should raise :exc:`IsADirectoryError`

0 commit comments

Comments
 (0)
0