Skip to content

Commit 5aec839

Browse files
barneygaleplashchynski
authored andcommitted
pythonGH-130614: pathlib ABCs: parametrize test suite for path copying (python#131168)
Test copying from `Path` and `ReadableZipPath` (types of `_ReadablePath`) to `Path` and `WritableZipPath` (types of `_WritablePath`).
1 parent 66cbaa3 commit 5aec839

File tree

3 files changed

+173
-139
lines changed

3 files changed

+173
-139
lines changed

Lib/pathlib/_os.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ def copy_file(source, target, follow_symlinks=True, preserve_metadata=False):
248248
"""
249249
info = source.info
250250
if not follow_symlinks and info.is_symlink():
251-
target.symlink_to(source.readlink(), info.is_dir())
251+
target.symlink_to(str(source.readlink()), info.is_dir())
252252
if preserve_metadata:
253253
target._write_info(info, follow_symlinks=False)
254254
elif info.is_dir():

Lib/test/test_pathlib/test_copy.py

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
"""
2+
Tests for copying from pathlib.types._ReadablePath to _WritablePath.
3+
"""
4+
5+
import contextlib
6+
import unittest
7+
8+
from pathlib import Path
9+
10+
from test.test_pathlib.support.local_path import LocalPathGround, WritableLocalPath
11+
from test.test_pathlib.support.zip_path import ZipPathGround, ReadableZipPath, WritableZipPath
12+
13+
14+
class CopyTestBase:
15+
def setUp(self):
16+
self.source_root = self.source_ground.setup()
17+
self.source_ground.create_hierarchy(self.source_root)
18+
self.target_root = self.target_ground.setup(local_suffix="_target")
19+
20+
def tearDown(self):
21+
self.source_ground.teardown(self.source_root)
22+
self.target_ground.teardown(self.target_root)
23+
24+
def test_copy_file(self):
25+
source = self.source_root / 'fileA'
26+
target = self.target_root / 'copyA'
27+
result = source.copy(target)
28+
self.assertEqual(result, target)
29+
self.assertTrue(self.target_ground.isfile(target))
30+
self.assertEqual(self.source_ground.readbytes(source),
31+
self.target_ground.readbytes(result))
32+
33+
def test_copy_file_empty(self):
34+
source = self.source_root / 'empty'
35+
target = self.target_root / 'copyA'
36+
self.source_ground.create_file(source, b'')
37+
result = source.copy(target)
38+
self.assertEqual(result, target)
39+
self.assertTrue(self.target_ground.isfile(target))
40+
self.assertEqual(self.target_ground.readbytes(result), b'')
41+
42+
def test_copy_file_to_existing_file(self):
43+
source = self.source_root / 'fileA'
44+
target = self.target_root / 'copyA'
45+
self.target_ground.create_file(target, b'this is a copy\n')
46+
with contextlib.ExitStack() as stack:
47+
if isinstance(target, WritableZipPath):
48+
stack.enter_context(self.assertWarns(UserWarning))
49+
result = source.copy(target)
50+
self.assertEqual(result, target)
51+
self.assertTrue(self.target_ground.isfile(target))
52+
self.assertEqual(self.source_ground.readbytes(source),
53+
self.target_ground.readbytes(result))
54+
55+
def test_copy_file_to_directory(self):
56+
if not isinstance(self.target_root, WritableLocalPath):
57+
self.skipTest('needs local target')
58+
source = self.source_root / 'fileA'
59+
target = self.target_root / 'copyA'
60+
self.target_ground.create_dir(target)
61+
self.assertRaises(OSError, source.copy, target)
62+
63+
def test_copy_file_to_itself(self):
64+
source = self.source_root / 'fileA'
65+
self.assertRaises(OSError, source.copy, source)
66+
self.assertRaises(OSError, source.copy, source, follow_symlinks=False)
67+
68+
def test_copy_dir(self):
69+
source = self.source_root / 'dirC'
70+
target = self.target_root / 'copyC'
71+
result = source.copy(target)
72+
self.assertEqual(result, target)
73+
self.assertTrue(self.target_ground.isdir(target))
74+
self.assertTrue(self.target_ground.isfile(target / 'fileC'))
75+
self.assertEqual(self.target_ground.readtext(target / 'fileC'), 'this is file C\n')
76+
self.assertTrue(self.target_ground.isdir(target / 'dirD'))
77+
self.assertTrue(self.target_ground.isfile(target / 'dirD' / 'fileD'))
78+
self.assertEqual(self.target_ground.readtext(target / 'dirD' / 'fileD'), 'this is file D\n')
79+
80+
def test_copy_dir_follow_symlinks_true(self):
81+
if not self.source_ground.can_symlink:
82+
self.skipTest('needs symlink support on source')
83+
source = self.source_root / 'dirC'
84+
target = self.target_root / 'copyC'
85+
self.source_ground.create_symlink(source / 'linkC', 'fileC')
86+
self.source_ground.create_symlink(source / 'linkD', 'dirD')
87+
result = source.copy(target)
88+
self.assertEqual(result, target)
89+
self.assertTrue(self.target_ground.isdir(target))
90+
self.assertFalse(self.target_ground.islink(target / 'linkC'))
91+
self.assertTrue(self.target_ground.isfile(target / 'linkC'))
92+
self.assertEqual(self.target_ground.readtext(target / 'linkC'), 'this is file C\n')
93+
self.assertFalse(self.target_ground.islink(target / 'linkD'))
94+
self.assertTrue(self.target_ground.isdir(target / 'linkD'))
95+
self.assertTrue(self.target_ground.isfile(target / 'linkD' / 'fileD'))
96+
self.assertEqual(self.target_ground.readtext(target / 'linkD' / 'fileD'), 'this is file D\n')
97+
98+
def test_copy_dir_follow_symlinks_false(self):
99+
if not self.source_ground.can_symlink:
100+
self.skipTest('needs symlink support on source')
101+
if not self.target_ground.can_symlink:
102+
self.skipTest('needs symlink support on target')
103+
source = self.source_root / 'dirC'
104+
target = self.target_root / 'copyC'
105+
self.source_ground.create_symlink(source / 'linkC', 'fileC')
106+
self.source_ground.create_symlink(source / 'linkD', 'dirD')
107+
result = source.copy(target, follow_symlinks=False)
108+
self.assertEqual(result, target)
109+
self.assertTrue(self.target_ground.isdir(target))
110+
self.assertTrue(self.target_ground.islink(target / 'linkC'))
111+
self.assertEqual(self.target_ground.readlink(target / 'linkC'), 'fileC')
112+
self.assertTrue(self.target_ground.islink(target / 'linkD'))
113+
self.assertEqual(self.target_ground.readlink(target / 'linkD'), 'dirD')
114+
115+
def test_copy_dir_to_existing_directory(self):
116+
if not isinstance(self.target_root, WritableLocalPath):
117+
self.skipTest('needs local target')
118+
source = self.source_root / 'dirC'
119+
target = self.target_root / 'copyC'
120+
self.target_ground.create_dir(target)
121+
self.assertRaises(FileExistsError, source.copy, target)
122+
123+
def test_copy_dir_to_itself(self):
124+
source = self.source_root / 'dirC'
125+
self.assertRaises(OSError, source.copy, source)
126+
self.assertRaises(OSError, source.copy, source, follow_symlinks=False)
127+
128+
def test_copy_dir_into_itself(self):
129+
source = self.source_root / 'dirC'
130+
target = self.source_root / 'dirC' / 'dirD' / 'copyC'
131+
self.assertRaises(OSError, source.copy, target)
132+
self.assertRaises(OSError, source.copy, target, follow_symlinks=False)
133+
134+
def test_copy_into(self):
135+
source = self.source_root / 'fileA'
136+
target_dir = self.target_root / 'dirA'
137+
self.target_ground.create_dir(target_dir)
138+
result = source.copy_into(target_dir)
139+
self.assertEqual(result, target_dir / 'fileA')
140+
self.assertTrue(self.target_ground.isfile(result))
141+
self.assertEqual(self.source_ground.readbytes(source),
142+
self.target_ground.readbytes(result))
143+
144+
def test_copy_into_empty_name(self):
145+
source = self.source_root.with_segments()
146+
target_dir = self.target_root / 'dirA'
147+
self.target_ground.create_dir(target_dir)
148+
self.assertRaises(ValueError, source.copy_into, target_dir)
149+
150+
151+
class ZipToZipPathCopyTest(CopyTestBase, unittest.TestCase):
152+
source_ground = ZipPathGround(ReadableZipPath)
153+
target_ground = ZipPathGround(WritableZipPath)
154+
155+
156+
class ZipToLocalPathCopyTest(CopyTestBase, unittest.TestCase):
157+
source_ground = ZipPathGround(ReadableZipPath)
158+
target_ground = LocalPathGround(Path)
159+
160+
161+
class LocalToZipPathCopyTest(CopyTestBase, unittest.TestCase):
162+
source_ground = LocalPathGround(Path)
163+
target_ground = ZipPathGround(WritableZipPath)
164+
165+
166+
class LocalToLocalPathCopyTest(CopyTestBase, unittest.TestCase):
167+
source_ground = LocalPathGround(Path)
168+
target_ground = LocalPathGround(Path)
169+
170+
171+
if __name__ == "__main__":
172+
unittest.main()

Lib/test/test_pathlib/test_pathlib_abc.py

Lines changed: 0 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -343,144 +343,6 @@ class RWPathTest(WritablePathTest, ReadablePathTest):
343343
cls = DummyRWPath
344344
can_symlink = False
345345

346-
def test_copy_file(self):
347-
base = self.cls(self.base)
348-
source = base / 'fileA'
349-
target = base / 'copyA'
350-
result = source.copy(target)
351-
self.assertEqual(result, target)
352-
self.assertTrue(result.info.exists())
353-
self.assertEqual(source.read_text(), result.read_text())
354-
355-
def test_copy_file_to_existing_file(self):
356-
base = self.cls(self.base)
357-
source = base / 'fileA'
358-
target = base / 'dirB' / 'fileB'
359-
result = source.copy(target)
360-
self.assertEqual(result, target)
361-
self.assertTrue(result.info.exists())
362-
self.assertEqual(source.read_text(), result.read_text())
363-
364-
def test_copy_file_to_existing_directory(self):
365-
base = self.cls(self.base)
366-
source = base / 'fileA'
367-
target = base / 'dirA'
368-
self.assertRaises(OSError, source.copy, target)
369-
370-
def test_copy_file_empty(self):
371-
base = self.cls(self.base)
372-
source = base / 'empty'
373-
target = base / 'copyA'
374-
source.write_bytes(b'')
375-
result = source.copy(target)
376-
self.assertEqual(result, target)
377-
self.assertTrue(result.info.exists())
378-
self.assertEqual(result.read_bytes(), b'')
379-
380-
def test_copy_file_to_itself(self):
381-
base = self.cls(self.base)
382-
source = base / 'empty'
383-
source.write_bytes(b'')
384-
self.assertRaises(OSError, source.copy, source)
385-
self.assertRaises(OSError, source.copy, source, follow_symlinks=False)
386-
387-
def test_copy_dir_simple(self):
388-
base = self.cls(self.base)
389-
source = base / 'dirC'
390-
target = base / 'copyC'
391-
result = source.copy(target)
392-
self.assertEqual(result, target)
393-
self.assertTrue(result.info.is_dir())
394-
self.assertTrue(result.joinpath('dirD').info.is_dir())
395-
self.assertTrue(result.joinpath('dirD', 'fileD').info.is_file())
396-
self.assertEqual(result.joinpath('dirD', 'fileD').read_text(),
397-
"this is file D\n")
398-
self.assertTrue(result.joinpath('fileC').info.is_file())
399-
self.assertTrue(result.joinpath('fileC').read_text(),
400-
"this is file C\n")
401-
402-
def test_copy_dir_complex(self, follow_symlinks=True):
403-
def ordered_walk(path):
404-
for dirpath, dirnames, filenames in path.walk(follow_symlinks=follow_symlinks):
405-
dirnames.sort()
406-
filenames.sort()
407-
yield dirpath, dirnames, filenames
408-
base = self.cls(self.base)
409-
source = base / 'dirC'
410-
411-
if self.can_symlink:
412-
# Add some symlinks
413-
source.joinpath('linkC').symlink_to('fileC')
414-
source.joinpath('linkD').symlink_to('dirD', target_is_directory=True)
415-
416-
# Perform the copy
417-
target = base / 'copyC'
418-
result = source.copy(target, follow_symlinks=follow_symlinks)
419-
self.assertEqual(result, target)
420-
421-
# Compare the source and target trees
422-
source_walk = ordered_walk(source)
423-
target_walk = ordered_walk(result)
424-
for source_item, target_item in zip(source_walk, target_walk, strict=True):
425-
self.assertEqual(source_item[0].parts[len(source.parts):],
426-
target_item[0].parts[len(target.parts):]) # dirpath
427-
self.assertEqual(source_item[1], target_item[1]) # dirnames
428-
self.assertEqual(source_item[2], target_item[2]) # filenames
429-
# Compare files and symlinks
430-
for filename in source_item[2]:
431-
source_file = source_item[0].joinpath(filename)
432-
target_file = target_item[0].joinpath(filename)
433-
if follow_symlinks or not source_file.info.is_symlink():
434-
# Regular file.
435-
self.assertEqual(source_file.read_bytes(), target_file.read_bytes())
436-
elif source_file.info.is_dir():
437-
# Symlink to directory.
438-
self.assertTrue(target_file.info.is_dir())
439-
self.assertEqual(source_file.readlink(), target_file.readlink())
440-
else:
441-
# Symlink to file.
442-
self.assertEqual(source_file.read_bytes(), target_file.read_bytes())
443-
self.assertEqual(source_file.readlink(), target_file.readlink())
444-
445-
def test_copy_dir_complex_follow_symlinks_false(self):
446-
self.test_copy_dir_complex(follow_symlinks=False)
447-
448-
def test_copy_dir_to_existing_directory(self):
449-
base = self.cls(self.base)
450-
source = base / 'dirC'
451-
target = base / 'copyC'
452-
target.mkdir()
453-
target.joinpath('dirD').mkdir()
454-
self.assertRaises(FileExistsError, source.copy, target)
455-
456-
def test_copy_dir_to_itself(self):
457-
base = self.cls(self.base)
458-
source = base / 'dirC'
459-
self.assertRaises(OSError, source.copy, source)
460-
self.assertRaises(OSError, source.copy, source, follow_symlinks=False)
461-
462-
def test_copy_dir_into_itself(self):
463-
base = self.cls(self.base)
464-
source = base / 'dirC'
465-
target = base / 'dirC' / 'dirD' / 'copyC'
466-
self.assertRaises(OSError, source.copy, target)
467-
self.assertRaises(OSError, source.copy, target, follow_symlinks=False)
468-
self.assertFalse(target.info.exists())
469-
470-
def test_copy_into(self):
471-
base = self.cls(self.base)
472-
source = base / 'fileA'
473-
target_dir = base / 'dirA'
474-
result = source.copy_into(target_dir)
475-
self.assertEqual(result, target_dir / 'fileA')
476-
self.assertTrue(result.info.exists())
477-
self.assertEqual(source.read_text(), result.read_text())
478-
479-
def test_copy_into_empty_name(self):
480-
source = self.cls('')
481-
target_dir = self.base
482-
self.assertRaises(ValueError, source.copy_into, target_dir)
483-
484346

485347
class ReadablePathWalkTest(unittest.TestCase):
486348
cls = DummyReadablePath

0 commit comments

Comments
 (0)