summaryrefslogtreecommitdiffstats
path: root/Lib/test
diff options
context:
space:
mode:
authorBarney Gale <barney.gale@gmail.com>2024-08-23 19:03:11 (GMT)
committerGitHub <noreply@github.com>2024-08-23 19:03:11 (GMT)
commitd7ae4dc5c14bc014ca0c056dab54c86ba8f395cb (patch)
tree3efe697566e955e5825c726586942c0d8a30e633 /Lib/test
parentbf1b5d323bdb6b1609c6f4b31dcaed621e5d0e2f (diff)
downloadcpython-d7ae4dc5c14bc014ca0c056dab54c86ba8f395cb.zip
cpython-d7ae4dc5c14bc014ca0c056dab54c86ba8f395cb.tar.gz
cpython-d7ae4dc5c14bc014ca0c056dab54c86ba8f395cb.tar.bz2
GH-73991: Disallow copying directory into itself via `pathlib.Path.copy()` (#122924)
Diffstat (limited to 'Lib/test')
-rw-r--r--Lib/test/test_pathlib/test_pathlib_abc.py111
1 files changed, 103 insertions, 8 deletions
diff --git a/Lib/test/test_pathlib/test_pathlib_abc.py b/Lib/test/test_pathlib/test_pathlib_abc.py
index f222fd5..5b71475 100644
--- a/Lib/test/test_pathlib/test_pathlib_abc.py
+++ b/Lib/test/test_pathlib/test_pathlib_abc.py
@@ -1501,19 +1501,20 @@ class DummyPath(PathBase):
raise FileNotFoundError(errno.ENOENT, "File not found", path)
def mkdir(self, mode=0o777, parents=False, exist_ok=False):
- path = str(self.resolve())
- if path in self._directories:
+ path = str(self.parent.resolve() / self.name)
+ parent = str(self.parent.resolve())
+ if path in self._directories or path in self._symlinks:
if exist_ok:
return
else:
raise FileExistsError(errno.EEXIST, "File exists", path)
try:
if self.name:
- self._directories[str(self.parent)].add(self.name)
+ self._directories[parent].add(self.name)
self._directories[path] = set()
except KeyError:
if not parents:
- raise FileNotFoundError(errno.ENOENT, "File not found", str(self.parent)) from None
+ raise FileNotFoundError(errno.ENOENT, "File not found", parent) from None
self.parent.mkdir(parents=True, exist_ok=True)
self.mkdir(mode, parents=False, exist_ok=exist_ok)
@@ -1759,6 +1760,32 @@ class DummyPathTest(DummyPurePathTest):
self.assertEqual(source.readlink(), target.readlink())
@needs_symlinks
+ def test_copy_symlink_to_itself(self):
+ base = self.cls(self.base)
+ source = base / 'linkA'
+ self.assertRaises(OSError, source.copy, source)
+
+ @needs_symlinks
+ def test_copy_symlink_to_existing_symlink(self):
+ base = self.cls(self.base)
+ source = base / 'copySource'
+ target = base / 'copyTarget'
+ source.symlink_to(base / 'fileA')
+ target.symlink_to(base / 'dirC')
+ self.assertRaises(OSError, source.copy, target)
+ self.assertRaises(OSError, source.copy, target, follow_symlinks=False)
+
+ @needs_symlinks
+ def test_copy_symlink_to_existing_directory_symlink(self):
+ base = self.cls(self.base)
+ source = base / 'copySource'
+ target = base / 'copyTarget'
+ source.symlink_to(base / 'fileA')
+ target.symlink_to(base / 'dirC')
+ self.assertRaises(OSError, source.copy, target)
+ self.assertRaises(OSError, source.copy, target, follow_symlinks=False)
+
+ @needs_symlinks
def test_copy_directory_symlink_follow_symlinks_false(self):
base = self.cls(self.base)
source = base / 'linkB'
@@ -1769,6 +1796,42 @@ class DummyPathTest(DummyPurePathTest):
self.assertTrue(target.is_symlink())
self.assertEqual(source.readlink(), target.readlink())
+ @needs_symlinks
+ def test_copy_directory_symlink_to_itself(self):
+ base = self.cls(self.base)
+ source = base / 'linkB'
+ self.assertRaises(OSError, source.copy, source)
+ self.assertRaises(OSError, source.copy, source, follow_symlinks=False)
+
+ @needs_symlinks
+ def test_copy_directory_symlink_into_itself(self):
+ base = self.cls(self.base)
+ source = base / 'linkB'
+ target = base / 'linkB' / 'copyB'
+ self.assertRaises(OSError, source.copy, target)
+ self.assertRaises(OSError, source.copy, target, follow_symlinks=False)
+ self.assertFalse(target.exists())
+
+ @needs_symlinks
+ def test_copy_directory_symlink_to_existing_symlink(self):
+ base = self.cls(self.base)
+ source = base / 'copySource'
+ target = base / 'copyTarget'
+ source.symlink_to(base / 'dirC')
+ target.symlink_to(base / 'fileA')
+ self.assertRaises(FileExistsError, source.copy, target)
+ self.assertRaises(FileExistsError, source.copy, target, follow_symlinks=False)
+
+ @needs_symlinks
+ def test_copy_directory_symlink_to_existing_directory_symlink(self):
+ base = self.cls(self.base)
+ source = base / 'copySource'
+ target = base / 'copyTarget'
+ source.symlink_to(base / 'dirC' / 'dirD')
+ target.symlink_to(base / 'dirC')
+ self.assertRaises(FileExistsError, source.copy, target)
+ self.assertRaises(FileExistsError, source.copy, target, follow_symlinks=False)
+
def test_copy_file_to_existing_file(self):
base = self.cls(self.base)
source = base / 'fileA'
@@ -1782,8 +1845,7 @@ class DummyPathTest(DummyPurePathTest):
base = self.cls(self.base)
source = base / 'fileA'
target = base / 'dirA'
- with self.assertRaises(OSError):
- source.copy(target)
+ self.assertRaises(OSError, source.copy, target)
@needs_symlinks
def test_copy_file_to_existing_symlink(self):
@@ -1823,6 +1885,13 @@ class DummyPathTest(DummyPurePathTest):
self.assertTrue(target.exists())
self.assertEqual(target.read_bytes(), b'')
+ def test_copy_file_to_itself(self):
+ base = self.cls(self.base)
+ source = base / 'empty'
+ source.write_bytes(b'')
+ self.assertRaises(OSError, source.copy, source)
+ self.assertRaises(OSError, source.copy, source, follow_symlinks=False)
+
def test_copy_dir_simple(self):
base = self.cls(self.base)
source = base / 'dirC'
@@ -1909,6 +1978,28 @@ class DummyPathTest(DummyPurePathTest):
self.assertTrue(target.joinpath('fileC').read_text(),
"this is file C\n")
+ def test_copy_dir_to_itself(self):
+ base = self.cls(self.base)
+ source = base / 'dirC'
+ self.assertRaises(OSError, source.copy, source)
+ self.assertRaises(OSError, source.copy, source, follow_symlinks=False)
+
+ def test_copy_dir_to_itself_on_error(self):
+ base = self.cls(self.base)
+ source = base / 'dirC'
+ errors = []
+ source.copy(source, on_error=errors.append)
+ self.assertEqual(len(errors), 1)
+ self.assertIsInstance(errors[0], OSError)
+
+ def test_copy_dir_into_itself(self):
+ base = self.cls(self.base)
+ source = base / 'dirC'
+ target = base / 'dirC' / 'dirD' / 'copyC'
+ self.assertRaises(OSError, source.copy, target)
+ self.assertRaises(OSError, source.copy, target, follow_symlinks=False)
+ self.assertFalse(target.exists())
+
def test_copy_missing_on_error(self):
base = self.cls(self.base)
source = base / 'foo'
@@ -2876,8 +2967,12 @@ class DummyPathWithSymlinks(DummyPath):
raise FileNotFoundError(errno.ENOENT, "File not found", path)
def symlink_to(self, target, target_is_directory=False):
- self._directories[str(self.parent)].add(self.name)
- self._symlinks[str(self)] = str(target)
+ path = str(self.parent.resolve() / self.name)
+ parent = str(self.parent.resolve())
+ if path in self._symlinks:
+ raise FileExistsError(errno.EEXIST, "File exists", path)
+ self._directories[parent].add(self.name)
+ self._symlinks[path] = str(target)
class DummyPathWithSymlinksTest(DummyPathTest):