diff --git a/.github/workflows/testsuite.yml b/.github/workflows/testsuite.yml index 65d70b1e..b11c209a 100644 --- a/.github/workflows/testsuite.yml +++ b/.github/workflows/testsuite.yml @@ -10,7 +10,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macOS-latest, windows-latest] - python-version: [3.7, 3.8, 3.9, "3.10", "3.11"] + python-version: [3.7, 3.8, 3.9, "3.10", "3.11", "3.12-dev"] include: - python-version: "pypy-3.7" os: ubuntu-latest @@ -58,10 +58,12 @@ jobs: fi shell: bash - name: Install extra dependencies + if: ${{ matrix.python-version != '3.12-dev' }} run: | pip install -r extra_requirements.txt shell: bash - name: Run unit tests with extra packages as non-root user + if: ${{ matrix.python-version != '3.12-dev' }} run: | python -m pyfakefs.tests.all_tests shell: bash diff --git a/CHANGES.md b/CHANGES.md index c6347dc4..4d6fab6b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,12 +6,15 @@ The released versions correspond to PyPI releases. ### Features * added class level setup method `setUpClassPyfakefs` for unittest and class-scoped fixture `fs_class` for pytest (see [#752](../../issues/752)) +* added experimental support for Python 3.12: added fake APIs for Windows junction + support. These are not implemented and always return `False`. ### Infrastructure * replaced end-of-life CentOS with RedHat UBI9 docker image * added tests for pytest 7.2.0 * added black to pre-commit checks, which caused some changes to the coding style (max line length is now 88, always use double quotes) +* added Python 3.12 to the test suite. ## [Version 5.0.0](https://pypi.python.org/pypi/pyfakefs/5.0.0) (2022-10-09) New version after the transfer to `pytest-dev`. diff --git a/pyfakefs/fake_filesystem.py b/pyfakefs/fake_filesystem.py index 5298888a..a294ab58 100644 --- a/pyfakefs/fake_filesystem.py +++ b/pyfakefs/fake_filesystem.py @@ -576,6 +576,12 @@ def path(self) -> AnyStr: dir_path = sep.join(names) return self.filesystem.absnormpath(dir_path) + if sys.version_info >= (3, 12): + + @property + def is_junction(self) -> bool: + return self.filesystem.isjunction(self.path) + def __getattr__(self, item: str) -> Any: """Forward some properties to stat_result.""" if item in self.stat_types: @@ -3440,6 +3446,12 @@ def islink(self, path: AnyPath) -> bool: """ return self._is_of_type(path, S_IFLNK, follow_symlinks=False) + if sys.version_info >= (3, 12): + + def isjunction(self, path: AnyPath) -> bool: + """Returns False. Junctions are never faked.""" + return False + def confirmdir( self, target_directory: AnyStr, check_owner: bool = False ) -> FakeDirectory: @@ -3589,7 +3601,7 @@ def dir() -> List[str]: """Return the list of patched function names. Used for patching functions imported from the module. """ - return [ + dir_list = [ "abspath", "dirname", "exists", @@ -3613,6 +3625,9 @@ def dir() -> List[str]: "splitdrive", "samefile", ] + if sys.version_info >= (3, 12): + dir_list.append("isjunction") + return dir_list def __init__(self, filesystem: FakeFilesystem, os_module: "FakeOsModule"): """Init. @@ -3702,6 +3717,12 @@ def islink(self, path: AnyStr) -> bool: """ return self.filesystem.islink(path) + if sys.version_info >= (3, 12): + + def isjunction(self, path: AnyStr) -> bool: + """Returns False. Junctions are never faked.""" + return self.filesystem.isjunction(path) + def getmtime(self, path: AnyStr) -> float: """Returns the modification time of the fake file. diff --git a/pyfakefs/fake_pathlib.py b/pyfakefs/fake_pathlib.py index 6ee0e5a7..253f6007 100644 --- a/pyfakefs/fake_pathlib.py +++ b/pyfakefs/fake_pathlib.py @@ -32,8 +32,10 @@ import fnmatch import functools import inspect +import ntpath import os import pathlib +import posixpath import re import sys from pathlib import PurePath @@ -383,6 +385,7 @@ class _FakeWindowsFlavour(_FakeFlavour): | {"COM%d" % i for i in range(1, 10)} | {"LPT%d" % i for i in range(1, 10)} ) + pathmod = ntpath def is_reserved(self, parts): """Return True if the path is considered reserved under Windows.""" @@ -459,6 +462,8 @@ class _FakePosixFlavour(_FakeFlavour): independent of FakeFilesystem properties. """ + pathmod = posixpath + def is_reserved(self, parts): return False diff --git a/pyfakefs/fake_scandir.py b/pyfakefs/fake_scandir.py index b6775f85..cfe9f2f9 100644 --- a/pyfakefs/fake_scandir.py +++ b/pyfakefs/fake_scandir.py @@ -114,6 +114,16 @@ def stat(self, follow_symlinks=True): def __fspath__(self): return self.path + if sys.version_info >= (3, 12): + + def is_junction(self) -> bool: + """Return True if this entry is a junction. + Junctions are not a part of posix semantic.""" + if not self._filesystem.is_windows_fs: + return False + file_object = self._filesystem.resolve(self._abspath) + return file_object.is_junction + class ScanDirIter: """Iterator for DirEntry objects returned from `scandir()` diff --git a/pyfakefs/tests/fake_pathlib_test.py b/pyfakefs/tests/fake_pathlib_test.py index b3f1bbc4..fad500a9 100644 --- a/pyfakefs/tests/fake_pathlib_test.py +++ b/pyfakefs/tests/fake_pathlib_test.py @@ -513,6 +513,15 @@ def test_read_text(self): file_path = self.path(self.make_path("text_file")) self.assertEqual(file_path.read_text(), "foo") + @unittest.skipIf( + sys.version_info < (3, 12), + "is_junction method new in Python 3.12", + ) + def test_is_junction(self): + self.create_file(self.make_path("text_file"), contents="foo") + file_path = self.path(self.make_path("text_file")) + self.assertFalse(file_path.is_junction()) + def test_read_text_with_encoding(self): self.create_file( self.make_path("text_file"), contents="ерунда", encoding="cyrillic" @@ -632,6 +641,7 @@ def test_symlink_to(self): self.assertTrue(path.is_symlink()) @unittest.skipIf(sys.version_info < (3, 8), "link_to new in Python 3.8") + @unittest.skipIf(sys.version_info >= (3, 12), "link_to removed in Python 3.12") def test_link_to(self): self.skip_if_symlink_not_supported() file_name = self.make_path("foo", "bar.txt")