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

Skip to content

Commit e0b61b2

Browse files
miss-islingtonakulakov
authored andcommitted
bpo-45234: Fix FileNotFound exception raised instead of IsADirectoryError in shutil.copyfile() (GH-28421) (GH-28508)
This was a regression from fixing BPO-43219. (cherry picked from commit b7eac52) Co-authored-by: andrei kulakov <andrei.avk@gmail.com>
1 parent 7661103 commit e0b61b2

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
@@ -251,36 +251,37 @@ def copyfile(src, dst, *, follow_symlinks=True):
251251
if not follow_symlinks and _islink(src):
252252
os.symlink(os.readlink(src), dst)
253253
else:
254-
try:
255-
with open(src, 'rb') as fsrc, open(dst, 'wb') as fdst:
256-
# macOS
257-
if _HAS_FCOPYFILE:
258-
try:
259-
_fastcopy_fcopyfile(fsrc, fdst, posix._COPYFILE_DATA)
260-
return dst
261-
except _GiveupOnFastCopy:
262-
pass
263-
# Linux
264-
elif _USE_CP_SENDFILE:
265-
try:
266-
_fastcopy_sendfile(fsrc, fdst)
254+
with open(src, 'rb') as fsrc:
255+
try:
256+
with open(dst, 'wb') as fdst:
257+
# macOS
258+
if _HAS_FCOPYFILE:
259+
try:
260+
_fastcopy_fcopyfile(fsrc, fdst, posix._COPYFILE_DATA)
261+
return dst
262+
except _GiveupOnFastCopy:
263+
pass
264+
# Linux
265+
elif _USE_CP_SENDFILE:
266+
try:
267+
_fastcopy_sendfile(fsrc, fdst)
268+
return dst
269+
except _GiveupOnFastCopy:
270+
pass
271+
# Windows, see:
272+
# https://github.com/python/cpython/pull/7160#discussion_r195405230
273+
elif _WINDOWS and file_size > 0:
274+
_copyfileobj_readinto(fsrc, fdst, min(file_size, COPY_BUFSIZE))
267275
return dst
268-
except _GiveupOnFastCopy:
269-
pass
270-
# Windows, see:
271-
# https://github.com/python/cpython/pull/7160#discussion_r195405230
272-
elif _WINDOWS and file_size > 0:
273-
_copyfileobj_readinto(fsrc, fdst, min(file_size, COPY_BUFSIZE))
274-
return dst
275-
276-
copyfileobj(fsrc, fdst)
277-
278-
# Issue 43219, raise a less confusing exception
279-
except IsADirectoryError as e:
280-
if os.path.exists(dst):
281-
raise
282-
else:
283-
raise FileNotFoundError(f'Directory does not exist: {dst}') from e
276+
277+
copyfileobj(fsrc, fdst)
278+
279+
# Issue 43219, raise a less confusing exception
280+
except IsADirectoryError as e:
281+
if not os.path.exists(dst):
282+
raise FileNotFoundError(f'Directory does not exist: {dst}') from e
283+
else:
284+
raise
284285

285286
return dst
286287

Lib/test/test_shutil.py

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

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

11561178
@os_helper.skip_unless_symlink
@@ -1259,6 +1281,24 @@ def test_copyfile_nonexistent_dir(self):
12591281
write_file(src_file, 'foo')
12601282
self.assertRaises(FileNotFoundError, shutil.copyfile, src_file, dst)
12611283

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

12631303
class TestArchives(BaseTest, unittest.TestCase):
12641304

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