diff options
author | Barney Gale <barney.gale@gmail.com> | 2025-03-21 22:18:20 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-03-21 22:18:20 +0000 |
commit | cf9d1a4b6b28a76a49edba4028d5533195172287 (patch) | |
tree | c20da37c6f435fe18a84b59ca83cbb3fae81c91f /Lib/test/test_pathlib | |
parent | 56d0f9af147b2280ea0af7af5e57df1a01271991 (diff) | |
download | cpython-cf9d1a4b6b28a76a49edba4028d5533195172287.tar.gz cpython-cf9d1a4b6b28a76a49edba4028d5533195172287.zip |
GH-128520: pathlib ABCs: allow tests to be run externally (#131315)
Adjust the tests for the `pathlib.types` module so that they can be run
against the `pathlib-abc` PyPI package, which is a backport of the module
for older Python versions.
Specifically, we add a `.support.is_pypi` switch that is false in the
stdlib and true in the pathlib-abc package. This controls which package
we import, and whether or not we run tests against `PurePath` and `Path`.
For compatibility with older Python versions, we stop using
`zipfile.ZipFile.mkdir()` and `zipfile.ZipInfo._for_archive()`.
Diffstat (limited to 'Lib/test/test_pathlib')
-rw-r--r-- | Lib/test/test_pathlib/support/__init__.py | 2 | ||||
-rw-r--r-- | Lib/test/test_pathlib/support/lexical_path.py | 10 | ||||
-rw-r--r-- | Lib/test/test_pathlib/support/local_path.py | 29 | ||||
-rw-r--r-- | Lib/test/test_pathlib/support/zip_path.py | 28 | ||||
-rw-r--r-- | Lib/test/test_pathlib/test_copy.py | 32 | ||||
-rw-r--r-- | Lib/test/test_pathlib/test_join.py | 20 | ||||
-rw-r--r-- | Lib/test/test_pathlib/test_join_posix.py | 16 | ||||
-rw-r--r-- | Lib/test/test_pathlib/test_join_windows.py | 20 | ||||
-rw-r--r-- | Lib/test/test_pathlib/test_read.py | 21 | ||||
-rw-r--r-- | Lib/test/test_pathlib/test_write.py | 21 |
10 files changed, 127 insertions, 72 deletions
diff --git a/Lib/test/test_pathlib/support/__init__.py b/Lib/test/test_pathlib/support/__init__.py index e69de29bb2d..dcaef654d77 100644 --- a/Lib/test/test_pathlib/support/__init__.py +++ b/Lib/test/test_pathlib/support/__init__.py @@ -0,0 +1,2 @@ +# Set to 'True' if the tests are run against the pathlib-abc PyPI package. +is_pypi = False diff --git a/Lib/test/test_pathlib/support/lexical_path.py b/Lib/test/test_pathlib/support/lexical_path.py index 6298513de6c..f29a521af9b 100644 --- a/Lib/test/test_pathlib/support/lexical_path.py +++ b/Lib/test/test_pathlib/support/lexical_path.py @@ -4,11 +4,17 @@ Simple implementation of JoinablePath, for use in pathlib tests. import ntpath import os.path -import pathlib.types import posixpath +from . import is_pypi -class LexicalPath(pathlib.types._JoinablePath): +if is_pypi: + from pathlib_abc import _JoinablePath +else: + from pathlib.types import _JoinablePath + + +class LexicalPath(_JoinablePath): __slots__ = ('_segments',) parser = os.path diff --git a/Lib/test/test_pathlib/support/local_path.py b/Lib/test/test_pathlib/support/local_path.py index 1cf64316b40..4f027754f6a 100644 --- a/Lib/test/test_pathlib/support/local_path.py +++ b/Lib/test/test_pathlib/support/local_path.py @@ -7,25 +7,36 @@ about local paths in tests. """ import os -import pathlib.types -from test.support import os_helper -from test.test_pathlib.support.lexical_path import LexicalPath +from . import is_pypi +from .lexical_path import LexicalPath + +if is_pypi: + from shutil import rmtree + from pathlib_abc import PathInfo, _ReadablePath, _WritablePath + can_symlink = True + testfn = "TESTFN" +else: + from pathlib.types import PathInfo, _ReadablePath, _WritablePath + from test.support import os_helper + can_symlink = os_helper.can_symlink() + testfn = os_helper.TESTFN + rmtree = os_helper.rmtree class LocalPathGround: - can_symlink = os_helper.can_symlink() + can_symlink = can_symlink def __init__(self, path_cls): self.path_cls = path_cls def setup(self, local_suffix=""): - root = self.path_cls(os_helper.TESTFN + local_suffix) + root = self.path_cls(testfn + local_suffix) os.mkdir(root) return root def teardown(self, root): - os_helper.rmtree(root) + rmtree(root) def create_file(self, p, data=b''): with open(p, 'wb') as f: @@ -79,7 +90,7 @@ class LocalPathGround: return f.read() -class LocalPathInfo(pathlib.types.PathInfo): +class LocalPathInfo(PathInfo): """ Simple implementation of PathInfo for a local path """ @@ -123,7 +134,7 @@ class LocalPathInfo(pathlib.types.PathInfo): return self._is_symlink -class ReadableLocalPath(pathlib.types._ReadablePath, LexicalPath): +class ReadableLocalPath(_ReadablePath, LexicalPath): """ Simple implementation of a ReadablePath class for local filesystem paths. """ @@ -146,7 +157,7 @@ class ReadableLocalPath(pathlib.types._ReadablePath, LexicalPath): return self.with_segments(os.readlink(self)) -class WritableLocalPath(pathlib.types._WritablePath, LexicalPath): +class WritableLocalPath(_WritablePath, LexicalPath): """ Simple implementation of a WritablePath class for local filesystem paths. """ diff --git a/Lib/test/test_pathlib/support/zip_path.py b/Lib/test/test_pathlib/support/zip_path.py index 4e24e35a03a..242cab15096 100644 --- a/Lib/test/test_pathlib/support/zip_path.py +++ b/Lib/test/test_pathlib/support/zip_path.py @@ -8,12 +8,18 @@ about zip file members in tests. import errno import io -import pathlib.types import posixpath import stat import zipfile from stat import S_IFMT, S_ISDIR, S_ISREG, S_ISLNK +from . import is_pypi + +if is_pypi: + from pathlib_abc import PathInfo, _ReadablePath, _WritablePath +else: + from pathlib.types import PathInfo, _ReadablePath, _WritablePath + class ZipPathGround: can_symlink = True @@ -31,7 +37,10 @@ class ZipPathGround: path.zip_file.writestr(str(path), data) def create_dir(self, path): - path.zip_file.mkdir(str(path)) + zip_info = zipfile.ZipInfo(str(path) + '/') + zip_info.external_attr |= stat.S_IFDIR << 16 + zip_info.external_attr |= stat.FILE_ATTRIBUTE_DIRECTORY + path.zip_file.writestr(zip_info, '') def create_symlink(self, path, target): zip_info = zipfile.ZipInfo(str(path)) @@ -80,7 +89,7 @@ class ZipPathGround: return stat.S_ISLNK(info.external_attr >> 16) -class MissingZipPathInfo: +class MissingZipPathInfo(PathInfo): """ PathInfo implementation that is used when a zip file member is missing. """ @@ -105,7 +114,7 @@ class MissingZipPathInfo: missing_zip_path_info = MissingZipPathInfo() -class ZipPathInfo: +class ZipPathInfo(PathInfo): """ PathInfo implementation for an existing zip file member. """ @@ -216,7 +225,7 @@ class ZipFileList: self.tree.resolve(item.filename, create=True).zip_info = item -class ReadableZipPath(pathlib.types._ReadablePath): +class ReadableZipPath(_ReadablePath): """ Simple implementation of a ReadablePath class for .zip files. """ @@ -279,7 +288,7 @@ class ReadableZipPath(pathlib.types._ReadablePath): return self.with_segments(self.zip_file.read(info.zip_info).decode()) -class WritableZipPath(pathlib.types._WritablePath): +class WritableZipPath(_WritablePath): """ Simple implementation of a WritablePath class for .zip files. """ @@ -314,10 +323,13 @@ class WritableZipPath(pathlib.types._WritablePath): return self.zip_file.open(str(self), 'w') def mkdir(self, mode=0o777): - self.zip_file.mkdir(str(self), mode) + zinfo = zipfile.ZipInfo(str(self) + '/') + zinfo.external_attr |= stat.S_IFDIR << 16 + zinfo.external_attr |= stat.FILE_ATTRIBUTE_DIRECTORY + self.zip_file.writestr(zinfo, '') def symlink_to(self, target, target_is_directory=False): - zinfo = zipfile.ZipInfo(str(self))._for_archive(self.zip_file) + zinfo = zipfile.ZipInfo(str(self)) zinfo.external_attr = stat.S_IFLNK << 16 if target_is_directory: zinfo.external_attr |= 0x10 diff --git a/Lib/test/test_pathlib/test_copy.py b/Lib/test/test_pathlib/test_copy.py index 698a0a7b750..5f4cf82a031 100644 --- a/Lib/test/test_pathlib/test_copy.py +++ b/Lib/test/test_pathlib/test_copy.py @@ -5,10 +5,9 @@ Tests for copying from pathlib.types._ReadablePath to _WritablePath. import contextlib import unittest -from pathlib import Path - -from test.test_pathlib.support.local_path import LocalPathGround, WritableLocalPath -from test.test_pathlib.support.zip_path import ZipPathGround, ReadableZipPath, WritableZipPath +from .support import is_pypi +from .support.local_path import LocalPathGround +from .support.zip_path import ZipPathGround, ReadableZipPath, WritableZipPath class CopyTestBase: @@ -53,7 +52,7 @@ class CopyTestBase: self.target_ground.readbytes(result)) def test_copy_file_to_directory(self): - if not isinstance(self.target_root, WritableLocalPath): + if isinstance(self.target_root, WritableZipPath): self.skipTest('needs local target') source = self.source_root / 'fileA' target = self.target_root / 'copyA' @@ -113,7 +112,7 @@ class CopyTestBase: self.assertEqual(self.target_ground.readlink(target / 'linkD'), 'dirD') def test_copy_dir_to_existing_directory(self): - if not isinstance(self.target_root, WritableLocalPath): + if isinstance(self.target_root, WritableZipPath): self.skipTest('needs local target') source = self.source_root / 'dirC' target = self.target_root / 'copyC' @@ -153,19 +152,22 @@ class ZipToZipPathCopyTest(CopyTestBase, unittest.TestCase): target_ground = ZipPathGround(WritableZipPath) -class ZipToLocalPathCopyTest(CopyTestBase, unittest.TestCase): - source_ground = ZipPathGround(ReadableZipPath) - target_ground = LocalPathGround(Path) +if not is_pypi: + from pathlib import Path + class ZipToLocalPathCopyTest(CopyTestBase, unittest.TestCase): + source_ground = ZipPathGround(ReadableZipPath) + target_ground = LocalPathGround(Path) -class LocalToZipPathCopyTest(CopyTestBase, unittest.TestCase): - source_ground = LocalPathGround(Path) - target_ground = ZipPathGround(WritableZipPath) + + class LocalToZipPathCopyTest(CopyTestBase, unittest.TestCase): + source_ground = LocalPathGround(Path) + target_ground = ZipPathGround(WritableZipPath) -class LocalToLocalPathCopyTest(CopyTestBase, unittest.TestCase): - source_ground = LocalPathGround(Path) - target_ground = LocalPathGround(Path) + class LocalToLocalPathCopyTest(CopyTestBase, unittest.TestCase): + source_ground = LocalPathGround(Path) + target_ground = LocalPathGround(Path) if __name__ == "__main__": diff --git a/Lib/test/test_pathlib/test_join.py b/Lib/test/test_pathlib/test_join.py index 03a3ecfd248..f1a24204b4c 100644 --- a/Lib/test/test_pathlib/test_join.py +++ b/Lib/test/test_pathlib/test_join.py @@ -4,9 +4,13 @@ Tests for pathlib.types._JoinablePath import unittest -from pathlib import PurePath, Path -from pathlib.types import _PathParser, _JoinablePath -from test.test_pathlib.support.lexical_path import LexicalPath +from .support import is_pypi +from .support.lexical_path import LexicalPath + +if is_pypi: + from pathlib_abc import _PathParser, _JoinablePath +else: + from pathlib.types import _PathParser, _JoinablePath class JoinTestBase: @@ -355,12 +359,14 @@ class LexicalPathJoinTest(JoinTestBase, unittest.TestCase): cls = LexicalPath -class PurePathJoinTest(JoinTestBase, unittest.TestCase): - cls = PurePath +if not is_pypi: + from pathlib import PurePath, Path + class PurePathJoinTest(JoinTestBase, unittest.TestCase): + cls = PurePath -class PathJoinTest(JoinTestBase, unittest.TestCase): - cls = Path + class PathJoinTest(JoinTestBase, unittest.TestCase): + cls = Path if __name__ == "__main__": diff --git a/Lib/test/test_pathlib/test_join_posix.py b/Lib/test/test_pathlib/test_join_posix.py index 7f657c2565d..d24fb1087c9 100644 --- a/Lib/test/test_pathlib/test_join_posix.py +++ b/Lib/test/test_pathlib/test_join_posix.py @@ -5,8 +5,8 @@ Tests for Posix-flavoured pathlib.types._JoinablePath import os import unittest -from pathlib import PurePosixPath, PosixPath -from test.test_pathlib.support.lexical_path import LexicalPosixPath +from .support import is_pypi +from .support.lexical_path import LexicalPosixPath class JoinTestBase: @@ -36,13 +36,15 @@ class LexicalPosixPathJoinTest(JoinTestBase, unittest.TestCase): cls = LexicalPosixPath -class PurePosixPathJoinTest(JoinTestBase, unittest.TestCase): - cls = PurePosixPath +if not is_pypi: + from pathlib import PurePosixPath, PosixPath + class PurePosixPathJoinTest(JoinTestBase, unittest.TestCase): + cls = PurePosixPath -if os.name != 'nt': - class PosixPathJoinTest(JoinTestBase, unittest.TestCase): - cls = PosixPath + if os.name != 'nt': + class PosixPathJoinTest(JoinTestBase, unittest.TestCase): + cls = PosixPath if __name__ == "__main__": diff --git a/Lib/test/test_pathlib/test_join_windows.py b/Lib/test/test_pathlib/test_join_windows.py index 783248d1697..2cc634f25ef 100644 --- a/Lib/test/test_pathlib/test_join_windows.py +++ b/Lib/test/test_pathlib/test_join_windows.py @@ -5,8 +5,8 @@ Tests for Windows-flavoured pathlib.types._JoinablePath import os import unittest -from pathlib import PureWindowsPath, WindowsPath -from test.test_pathlib.support.lexical_path import LexicalWindowsPath +from .support import is_pypi +from .support.lexical_path import LexicalWindowsPath class JoinTestBase: @@ -40,8 +40,6 @@ class JoinTestBase: pp = p.joinpath('E:d:s') self.assertEqual(pp, P('E:d:s')) # Joining onto a UNC path with no root - pp = P('//').joinpath('server') - self.assertEqual(pp, P('//server')) pp = P('//server').joinpath('share') self.assertEqual(pp, P(r'//server\share')) pp = P('//./BootPartition').joinpath('Windows') @@ -54,7 +52,7 @@ class JoinTestBase: self.assertEqual(p / 'x/y', P(r'C:/a/b\x/y')) self.assertEqual(p / 'x' / 'y', P(r'C:/a/b\x\y')) self.assertEqual(p / '/x/y', P('C:/x/y')) - self.assertEqual(p / '/x' / 'y', P('C:/x\y')) + self.assertEqual(p / '/x' / 'y', P(r'C:/x\y')) # Joining with a different drive => the first path is ignored, even # if the second path is relative. self.assertEqual(p / 'D:x/y', P('D:x/y')) @@ -277,13 +275,15 @@ class LexicalWindowsPathJoinTest(JoinTestBase, unittest.TestCase): cls = LexicalWindowsPath -class PureWindowsPathJoinTest(JoinTestBase, unittest.TestCase): - cls = PureWindowsPath +if not is_pypi: + from pathlib import PureWindowsPath, WindowsPath + class PureWindowsPathJoinTest(JoinTestBase, unittest.TestCase): + cls = PureWindowsPath -if os.name == 'nt': - class WindowsPathJoinTest(JoinTestBase, unittest.TestCase): - cls = WindowsPath + if os.name == 'nt': + class WindowsPathJoinTest(JoinTestBase, unittest.TestCase): + cls = WindowsPath if __name__ == "__main__": diff --git a/Lib/test/test_pathlib/test_read.py b/Lib/test/test_pathlib/test_read.py index 49015dac3b3..938d3a1e987 100644 --- a/Lib/test/test_pathlib/test_read.py +++ b/Lib/test/test_pathlib/test_read.py @@ -6,12 +6,16 @@ import collections.abc import io import unittest -from pathlib import Path -from pathlib.types import PathInfo, _ReadablePath -from pathlib._os import magic_open +from .support import is_pypi +from .support.local_path import ReadableLocalPath, LocalPathGround +from .support.zip_path import ReadableZipPath, ZipPathGround -from test.test_pathlib.support.local_path import ReadableLocalPath, LocalPathGround -from test.test_pathlib.support.zip_path import ReadableZipPath, ZipPathGround +if is_pypi: + from pathlib_abc import PathInfo, _ReadablePath + from pathlib_abc._os import magic_open +else: + from pathlib.types import PathInfo, _ReadablePath + from pathlib._os import magic_open class ReadTestBase: @@ -301,8 +305,11 @@ class LocalPathReadTest(ReadTestBase, unittest.TestCase): ground = LocalPathGround(ReadableLocalPath) -class PathReadTest(ReadTestBase, unittest.TestCase): - ground = LocalPathGround(Path) +if not is_pypi: + from pathlib import Path + + class PathReadTest(ReadTestBase, unittest.TestCase): + ground = LocalPathGround(Path) if __name__ == "__main__": diff --git a/Lib/test/test_pathlib/test_write.py b/Lib/test/test_pathlib/test_write.py index 3d6057fb9dd..040af7be152 100644 --- a/Lib/test/test_pathlib/test_write.py +++ b/Lib/test/test_pathlib/test_write.py @@ -6,12 +6,16 @@ import io import os import unittest -from pathlib import Path -from pathlib.types import _WritablePath -from pathlib._os import magic_open +from .support import is_pypi +from .support.local_path import WritableLocalPath, LocalPathGround +from .support.zip_path import WritableZipPath, ZipPathGround -from test.test_pathlib.support.local_path import WritableLocalPath, LocalPathGround -from test.test_pathlib.support.zip_path import WritableZipPath, ZipPathGround +if is_pypi: + from pathlib_abc import _WritablePath + from pathlib_abc._os import magic_open +else: + from pathlib.types import _WritablePath + from pathlib._os import magic_open class WriteTestBase: @@ -101,8 +105,11 @@ class LocalPathWriteTest(WriteTestBase, unittest.TestCase): ground = LocalPathGround(WritableLocalPath) -class PathWriteTest(WriteTestBase, unittest.TestCase): - ground = LocalPathGround(Path) +if not is_pypi: + from pathlib import Path + + class PathWriteTest(WriteTestBase, unittest.TestCase): + ground = LocalPathGround(Path) if __name__ == "__main__": |