Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit db6a998

Browse filesBrowse files
authored
GH-130614: pathlib ABCs: revise test suite for writable paths (#131112)
Test `pathlib.types._WritablePath` in a dedicated test module. These tests cover `WritableZipPath`, `WritableLocalPath` and `Path`, where the former two classes are implementations of `_WritablePath` for use in tests.
1 parent ea57ffa commit db6a998
Copy full SHA for db6a998

File tree

4 files changed

+178
-43
lines changed
Filter options

4 files changed

+178
-43
lines changed

‎Lib/test/test_pathlib/support/local_path.py

Copy file name to clipboardExpand all lines: Lib/test/test_pathlib/support/local_path.py
+22-1Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""
2-
Implementation of ReadablePath for local paths, for use in pathlib tests.
2+
Implementations of ReadablePath and WritablePath for local paths, for use in
3+
pathlib tests.
34
45
LocalPathGround is also defined here. It helps establish the "ground truth"
56
about local paths in tests.
@@ -143,3 +144,23 @@ def iterdir(self):
143144

144145
def readlink(self):
145146
return self.with_segments(os.readlink(self))
147+
148+
149+
class WritableLocalPath(pathlib.types._WritablePath, LexicalPath):
150+
"""
151+
Simple implementation of a WritablePath class for local filesystem paths.
152+
"""
153+
154+
__slots__ = ()
155+
156+
def __fspath__(self):
157+
return str(self)
158+
159+
def __open_wb__(self, buffering=-1):
160+
return open(self, 'wb')
161+
162+
def mkdir(self, mode=0o777):
163+
os.mkdir(self, mode)
164+
165+
def symlink_to(self, target, target_is_directory=False):
166+
os.symlink(target, self, target_is_directory)

‎Lib/test/test_pathlib/support/zip_path.py

Copy file name to clipboardExpand all lines: Lib/test/test_pathlib/support/zip_path.py
+47-1Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""
2-
Implementation of ReadablePath for zip file members, for use in pathlib tests.
2+
Implementations of ReadablePath and WritablePath for zip file members, for use
3+
in pathlib tests.
34
45
ZipPathGround is also defined here. It helps establish the "ground truth"
56
about zip file members in tests.
@@ -276,3 +277,48 @@ def readlink(self):
276277
elif not info.is_symlink():
277278
raise OSError(errno.EINVAL, "Not a symlink", self)
278279
return self.with_segments(self.zip_file.read(info.zip_info).decode())
280+
281+
282+
class WritableZipPath(pathlib.types._WritablePath):
283+
"""
284+
Simple implementation of a WritablePath class for .zip files.
285+
"""
286+
287+
__slots__ = ('_segments', 'zip_file')
288+
parser = posixpath
289+
290+
def __init__(self, *pathsegments, zip_file):
291+
self._segments = pathsegments
292+
self.zip_file = zip_file
293+
294+
def __hash__(self):
295+
return hash((str(self), self.zip_file))
296+
297+
def __eq__(self, other):
298+
if not isinstance(other, WritableZipPath):
299+
return NotImplemented
300+
return str(self) == str(other) and self.zip_file is other.zip_file
301+
302+
def __str__(self):
303+
if not self._segments:
304+
return ''
305+
return self.parser.join(*self._segments)
306+
307+
def __repr__(self):
308+
return f'{type(self).__name__}({str(self)!r}, zip_file={self.zip_file!r})'
309+
310+
def with_segments(self, *pathsegments):
311+
return type(self)(*pathsegments, zip_file=self.zip_file)
312+
313+
def __open_wb__(self, buffering=-1):
314+
return self.zip_file.open(str(self), 'w')
315+
316+
def mkdir(self, mode=0o777):
317+
self.zip_file.mkdir(str(self), mode)
318+
319+
def symlink_to(self, target, target_is_directory=False):
320+
zinfo = zipfile.ZipInfo(str(self))._for_archive(self.zip_file)
321+
zinfo.external_attr = stat.S_IFLNK << 16
322+
if target_is_directory:
323+
zinfo.external_attr |= 0x10
324+
self.zip_file.writestr(zinfo, str(target))

‎Lib/test/test_pathlib/test_pathlib_abc.py

Copy file name to clipboardExpand all lines: Lib/test/test_pathlib/test_pathlib_abc.py
-41Lines changed: 0 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -336,10 +336,6 @@ def test_glob_windows(self):
336336
class WritablePathTest(JoinablePathTest):
337337
cls = DummyWritablePath
338338

339-
def test_is_writable(self):
340-
p = self.cls(self.base)
341-
self.assertIsInstance(p, _WritablePath)
342-
343339

344340
class DummyRWPath(DummyWritablePath, DummyReadablePath):
345341
__slots__ = ()
@@ -349,43 +345,6 @@ class RWPathTest(WritablePathTest, ReadablePathTest):
349345
cls = DummyRWPath
350346
can_symlink = False
351347

352-
def test_read_write_bytes(self):
353-
p = self.cls(self.base)
354-
(p / 'fileA').write_bytes(b'abcdefg')
355-
self.assertEqual((p / 'fileA').read_bytes(), b'abcdefg')
356-
# Check that trying to write str does not truncate the file.
357-
self.assertRaises(TypeError, (p / 'fileA').write_bytes, 'somestr')
358-
self.assertEqual((p / 'fileA').read_bytes(), b'abcdefg')
359-
360-
def test_read_write_text(self):
361-
p = self.cls(self.base)
362-
(p / 'fileA').write_text('äbcdefg', encoding='latin-1')
363-
self.assertEqual((p / 'fileA').read_text(
364-
encoding='utf-8', errors='ignore'), 'bcdefg')
365-
# Check that trying to write bytes does not truncate the file.
366-
self.assertRaises(TypeError, (p / 'fileA').write_text, b'somebytes')
367-
self.assertEqual((p / 'fileA').read_text(encoding='latin-1'), 'äbcdefg')
368-
369-
def test_write_text_with_newlines(self):
370-
p = self.cls(self.base)
371-
# Check that `\n` character change nothing
372-
(p / 'fileA').write_text('abcde\r\nfghlk\n\rmnopq', newline='\n')
373-
self.assertEqual((p / 'fileA').read_bytes(),
374-
b'abcde\r\nfghlk\n\rmnopq')
375-
# Check that `\r` character replaces `\n`
376-
(p / 'fileA').write_text('abcde\r\nfghlk\n\rmnopq', newline='\r')
377-
self.assertEqual((p / 'fileA').read_bytes(),
378-
b'abcde\r\rfghlk\r\rmnopq')
379-
# Check that `\r\n` character replaces `\n`
380-
(p / 'fileA').write_text('abcde\r\nfghlk\n\rmnopq', newline='\r\n')
381-
self.assertEqual((p / 'fileA').read_bytes(),
382-
b'abcde\r\r\nfghlk\r\n\rmnopq')
383-
# Check that no argument passed will change `\n` to `os.linesep`
384-
os_linesep_byte = bytes(os.linesep, encoding='ascii')
385-
(p / 'fileA').write_text('abcde\nfghlk\n\rmnopq')
386-
self.assertEqual((p / 'fileA').read_bytes(),
387-
b'abcde' + os_linesep_byte + b'fghlk' + os_linesep_byte + b'\rmnopq')
388-
389348
def test_copy_file(self):
390349
base = self.cls(self.base)
391350
source = base / 'fileA'

‎Lib/test/test_pathlib/test_write.py

Copy file name to clipboard
+109Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
"""
2+
Tests for pathlib.types._WritablePath
3+
"""
4+
5+
import io
6+
import os
7+
import unittest
8+
9+
from pathlib import Path
10+
from pathlib.types import _WritablePath
11+
from pathlib._os import magic_open
12+
13+
from test.test_pathlib.support.local_path import WritableLocalPath, LocalPathGround
14+
from test.test_pathlib.support.zip_path import WritableZipPath, ZipPathGround
15+
16+
17+
class WriteTestBase:
18+
def setUp(self):
19+
self.root = self.ground.setup()
20+
21+
def tearDown(self):
22+
self.ground.teardown(self.root)
23+
24+
def test_is_writable(self):
25+
self.assertIsInstance(self.root, _WritablePath)
26+
27+
def test_open_w(self):
28+
p = self.root / 'fileA'
29+
with magic_open(p, 'w') as f:
30+
self.assertIsInstance(f, io.TextIOBase)
31+
f.write('this is file A\n')
32+
self.assertEqual(self.ground.readtext(p), 'this is file A\n')
33+
34+
def test_open_wb(self):
35+
p = self.root / 'fileA'
36+
with magic_open(p, 'wb') as f:
37+
#self.assertIsInstance(f, io.BufferedWriter)
38+
f.write(b'this is file A\n')
39+
self.assertEqual(self.ground.readbytes(p), b'this is file A\n')
40+
41+
def test_write_bytes(self):
42+
p = self.root / 'fileA'
43+
p.write_bytes(b'abcdefg')
44+
self.assertEqual(self.ground.readbytes(p), b'abcdefg')
45+
# Check that trying to write str does not truncate the file.
46+
self.assertRaises(TypeError, p.write_bytes, 'somestr')
47+
self.assertEqual(self.ground.readbytes(p), b'abcdefg')
48+
49+
def test_write_text(self):
50+
p = self.root / 'fileA'
51+
p.write_text('äbcdefg', encoding='latin-1')
52+
self.assertEqual(self.ground.readbytes(p), b'\xe4bcdefg')
53+
# Check that trying to write bytes does not truncate the file.
54+
self.assertRaises(TypeError, p.write_text, b'somebytes')
55+
self.assertEqual(self.ground.readbytes(p), b'\xe4bcdefg')
56+
57+
def test_write_text_with_newlines(self):
58+
# Check that `\n` character change nothing
59+
p = self.root / 'fileA'
60+
p.write_text('abcde\r\nfghlk\n\rmnopq', newline='\n')
61+
self.assertEqual(self.ground.readbytes(p), b'abcde\r\nfghlk\n\rmnopq')
62+
63+
# Check that `\r` character replaces `\n`
64+
p = self.root / 'fileB'
65+
p.write_text('abcde\r\nfghlk\n\rmnopq', newline='\r')
66+
self.assertEqual(self.ground.readbytes(p), b'abcde\r\rfghlk\r\rmnopq')
67+
68+
# Check that `\r\n` character replaces `\n`
69+
p = self.root / 'fileC'
70+
p.write_text('abcde\r\nfghlk\n\rmnopq', newline='\r\n')
71+
self.assertEqual(self.ground.readbytes(p), b'abcde\r\r\nfghlk\r\n\rmnopq')
72+
73+
# Check that no argument passed will change `\n` to `os.linesep`
74+
os_linesep_byte = bytes(os.linesep, encoding='ascii')
75+
p = self.root / 'fileD'
76+
p.write_text('abcde\nfghlk\n\rmnopq')
77+
self.assertEqual(self.ground.readbytes(p),
78+
b'abcde' + os_linesep_byte +
79+
b'fghlk' + os_linesep_byte + b'\rmnopq')
80+
81+
def test_mkdir(self):
82+
p = self.root / 'newdirA'
83+
self.assertFalse(self.ground.isdir(p))
84+
p.mkdir()
85+
self.assertTrue(self.ground.isdir(p))
86+
87+
def test_symlink_to(self):
88+
if not self.ground.can_symlink:
89+
self.skipTest('needs symlinks')
90+
link = self.root.joinpath('linkA')
91+
link.symlink_to('fileA')
92+
self.assertTrue(self.ground.islink(link))
93+
self.assertEqual(self.ground.readlink(link), 'fileA')
94+
95+
96+
class ZipPathWriteTest(WriteTestBase, unittest.TestCase):
97+
ground = ZipPathGround(WritableZipPath)
98+
99+
100+
class LocalPathWriteTest(WriteTestBase, unittest.TestCase):
101+
ground = LocalPathGround(WritableLocalPath)
102+
103+
104+
class PathWriteTest(WriteTestBase, unittest.TestCase):
105+
ground = LocalPathGround(Path)
106+
107+
108+
if __name__ == "__main__":
109+
unittest.main()

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.