From ccd41d2746ddaaaca6984ff34727fd75c57d1fa5 Mon Sep 17 00:00:00 2001 From: Bruno Alla Date: Wed, 21 Dec 2022 14:33:23 +0100 Subject: [PATCH 01/14] Add Python 3.12 to the test suite --- .github/workflows/testsuite.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/testsuite.yml b/.github/workflows/testsuite.yml index 65d70b1e..a814b1c1 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 From 3748e6ae1a48b0218084bbd94a8f61c6d80a5cea Mon Sep 17 00:00:00 2001 From: Bruno Alla Date: Wed, 21 Dec 2022 15:02:55 +0100 Subject: [PATCH 02/14] Fix failures under Python 3.12 --- pyfakefs/fake_pathlib.py | 5 +++++ pyfakefs/tests/fake_pathlib_test.py | 1 + 2 files changed, 6 insertions(+) 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/tests/fake_pathlib_test.py b/pyfakefs/tests/fake_pathlib_test.py index b3f1bbc4..019ea9ca 100644 --- a/pyfakefs/tests/fake_pathlib_test.py +++ b/pyfakefs/tests/fake_pathlib_test.py @@ -632,6 +632,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") From 39a80cf7d6d9264e7d42a41db59ef2340f421a72 Mon Sep 17 00:00:00 2001 From: Bruno Alla Date: Wed, 21 Dec 2022 15:38:54 +0100 Subject: [PATCH 03/14] Mark Python 3.12 as experimental --- .github/workflows/testsuite.yml | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/.github/workflows/testsuite.yml b/.github/workflows/testsuite.yml index a814b1c1..b6a5a17b 100644 --- a/.github/workflows/testsuite.yml +++ b/.github/workflows/testsuite.yml @@ -6,14 +6,26 @@ on: jobs: tests: runs-on: ${{ matrix.os }} + continue-on-error: ${{ matrix.experimental }} strategy: fail-fast: false matrix: os: [ubuntu-latest, macOS-latest, windows-latest] - python-version: [3.7, 3.8, 3.9, "3.10", "3.11", "3.12-dev"] + python-version: [3.7, 3.8, 3.9, "3.10", "3.11"] + experimental: [false] include: - - python-version: "pypy-3.7" - os: ubuntu-latest + - os: ubuntu-latest + python-version: "pypy-3.7" + experimental: false + - os: ubuntu-latest + python-version: "3.12-dev" + experimental: true + - os: macOS-latest + python-version: "3.12-dev" + experimental: true + - os: windows-latest + python-version: "3.12-dev" + experimental: true steps: - uses: actions/checkout@v2 From d0cfd0ea62aebc630c61220e58522505a4a2b5bc Mon Sep 17 00:00:00 2001 From: Bruno Alla Date: Wed, 21 Dec 2022 17:15:10 +0100 Subject: [PATCH 04/14] Add is_junction method to DirEntry class on Python 3.12 --- pyfakefs/fake_scandir.py | 10 ++++++++++ 1 file changed, 10 insertions(+) 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()` From 887d6b0aad476ae8fe382d50da80ffeb628c5066 Mon Sep 17 00:00:00 2001 From: Bruno Alla Date: Wed, 21 Dec 2022 17:40:41 +0100 Subject: [PATCH 05/14] Implement stub functions for os.path.isjunction() --- pyfakefs/fake_filesystem.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/pyfakefs/fake_filesystem.py b/pyfakefs/fake_filesystem.py index 5298888a..fb464f8a 100644 --- a/pyfakefs/fake_filesystem.py +++ b/pyfakefs/fake_filesystem.py @@ -576,6 +576,11 @@ def path(self) -> AnyStr: dir_path = sep.join(names) return self.filesystem.absnormpath(dir_path) + @property + def is_junction(self) -> bool: + # TODO: implement junctions + return False + def __getattr__(self, item: str) -> Any: """Forward some properties to stat_result.""" if item in self.stat_types: @@ -3440,6 +3445,22 @@ def islink(self, path: AnyPath) -> bool: """ return self._is_of_type(path, S_IFLNK, follow_symlinks=False) + def isjunction(self, path: AnyPath) -> bool: + """Determine if path identifies a junction. + + Args: + path: Path to filesystem object. + + Returns: + `False` on posix systems. + `True` if path is a junction on Windows. + + Raises: + TypeError: if path is None. + """ + # TODO: implement junction on Windows + return False + def confirmdir( self, target_directory: AnyStr, check_owner: bool = False ) -> FakeDirectory: @@ -3602,6 +3623,7 @@ def dir() -> List[str]: "isdir", "isfile", "islink", + "isjunction", "ismount", "join", "lexists", @@ -3702,6 +3724,20 @@ def islink(self, path: AnyStr) -> bool: """ return self.filesystem.islink(path) + def isjunction(self, path: AnyStr) -> bool: + """Determine whether path is a junction. + + Args: + path: Path to filesystem object. + + Returns: + `True` if path is a junction. + + Raises: + TypeError: if path is None. + """ + return self.filesystem.isjunction(path) + def getmtime(self, path: AnyStr) -> float: """Returns the modification time of the fake file. From 164879d53f395ed15b3bffc5c14bd308ea6e167a Mon Sep 17 00:00:00 2001 From: Bruno Alla Date: Thu, 22 Dec 2022 10:57:23 +0100 Subject: [PATCH 06/14] Always return false in stub junction functions --- pyfakefs/fake_filesystem.py | 29 +++-------------------------- 1 file changed, 3 insertions(+), 26 deletions(-) diff --git a/pyfakefs/fake_filesystem.py b/pyfakefs/fake_filesystem.py index fb464f8a..b1fb0a2a 100644 --- a/pyfakefs/fake_filesystem.py +++ b/pyfakefs/fake_filesystem.py @@ -578,8 +578,7 @@ def path(self) -> AnyStr: @property def is_junction(self) -> bool: - # TODO: implement junctions - return False + return self.filesystem.isjunction(self.path) def __getattr__(self, item: str) -> Any: """Forward some properties to stat_result.""" @@ -3446,19 +3445,7 @@ def islink(self, path: AnyPath) -> bool: return self._is_of_type(path, S_IFLNK, follow_symlinks=False) def isjunction(self, path: AnyPath) -> bool: - """Determine if path identifies a junction. - - Args: - path: Path to filesystem object. - - Returns: - `False` on posix systems. - `True` if path is a junction on Windows. - - Raises: - TypeError: if path is None. - """ - # TODO: implement junction on Windows + """Junction are never faked.""" return False def confirmdir( @@ -3725,17 +3712,7 @@ def islink(self, path: AnyStr) -> bool: return self.filesystem.islink(path) def isjunction(self, path: AnyStr) -> bool: - """Determine whether path is a junction. - - Args: - path: Path to filesystem object. - - Returns: - `True` if path is a junction. - - Raises: - TypeError: if path is None. - """ + """Junction are never faked.""" return self.filesystem.isjunction(path) def getmtime(self, path: AnyStr) -> float: From ebd31f27b961204d47b2096fc91dd39147f8ffef Mon Sep 17 00:00:00 2001 From: Bruno Alla Date: Thu, 22 Dec 2022 11:00:36 +0100 Subject: [PATCH 07/14] Skip extra deps tests for experimental jobs --- .github/workflows/testsuite.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/testsuite.yml b/.github/workflows/testsuite.yml index b6a5a17b..9e1a18f6 100644 --- a/.github/workflows/testsuite.yml +++ b/.github/workflows/testsuite.yml @@ -70,14 +70,17 @@ jobs: fi shell: bash - name: Install extra dependencies + if: ${{ ! matrix.experimental }} run: | pip install -r extra_requirements.txt shell: bash - name: Run unit tests with extra packages as non-root user + if: ${{ ! matrix.experimental }} run: | python -m pyfakefs.tests.all_tests shell: bash - name: Run performance tests + if: ${{ ! matrix.experimental }} run: | if [[ '${{ matrix.os }}' != 'macOS-latest' ]]; then export TEST_PERFORMANCE=1 From 2a60d6868697560fd0d74a61f3cc47ef12de3880 Mon Sep 17 00:00:00 2001 From: Bruno Alla Date: Thu, 22 Dec 2022 11:08:53 +0100 Subject: [PATCH 08/14] Update changelog to mention support for Python 3.12 --- CHANGES.md | 3 +++ 1 file changed, 3 insertions(+) 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`. From dcdb45879c7b86f8913173bc415658a419bc4e59 Mon Sep 17 00:00:00 2001 From: Bruno Alla Date: Thu, 22 Dec 2022 11:15:52 +0100 Subject: [PATCH 09/14] Make junction APIs conditional on Python 3.12 --- pyfakefs/fake_filesystem.py | 30 ++++++++++++++++++----------- pyfakefs/tests/fake_pathlib_test.py | 9 +++++++++ 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/pyfakefs/fake_filesystem.py b/pyfakefs/fake_filesystem.py index b1fb0a2a..cbce2e50 100644 --- a/pyfakefs/fake_filesystem.py +++ b/pyfakefs/fake_filesystem.py @@ -576,9 +576,11 @@ def path(self) -> AnyStr: dir_path = sep.join(names) return self.filesystem.absnormpath(dir_path) - @property - def is_junction(self) -> bool: - return self.filesystem.isjunction(self.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.""" @@ -3444,9 +3446,11 @@ def islink(self, path: AnyPath) -> bool: """ return self._is_of_type(path, S_IFLNK, follow_symlinks=False) - def isjunction(self, path: AnyPath) -> bool: - """Junction are never faked.""" - return False + if sys.version_info >= (3, 12): + + def isjunction(self, path: AnyPath) -> bool: + """Junction are never faked.""" + return False def confirmdir( self, target_directory: AnyStr, check_owner: bool = False @@ -3597,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", @@ -3610,7 +3614,6 @@ def dir() -> List[str]: "isdir", "isfile", "islink", - "isjunction", "ismount", "join", "lexists", @@ -3622,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. @@ -3711,9 +3717,11 @@ def islink(self, path: AnyStr) -> bool: """ return self.filesystem.islink(path) - def isjunction(self, path: AnyStr) -> bool: - """Junction are never faked.""" - return self.filesystem.isjunction(path) + if sys.version_info >= (3, 12): + + def isjunction(self, path: AnyStr) -> bool: + """Junction 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/tests/fake_pathlib_test.py b/pyfakefs/tests/fake_pathlib_test.py index 019ea9ca..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" From be9389772ffe9a20b9a9dbdc55d3646cc6d99e77 Mon Sep 17 00:00:00 2001 From: Bruno Alla Date: Thu, 22 Dec 2022 11:18:54 +0100 Subject: [PATCH 10/14] Simplify test suite workflow by removing experimental flag --- .github/workflows/testsuite.yml | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/.github/workflows/testsuite.yml b/.github/workflows/testsuite.yml index 9e1a18f6..6a14e10a 100644 --- a/.github/workflows/testsuite.yml +++ b/.github/workflows/testsuite.yml @@ -6,26 +6,14 @@ on: jobs: tests: runs-on: ${{ matrix.os }} - continue-on-error: ${{ matrix.experimental }} strategy: fail-fast: false matrix: os: [ubuntu-latest, macOS-latest, windows-latest] - python-version: [3.7, 3.8, 3.9, "3.10", "3.11"] - experimental: [false] + python-version: [3.7, 3.8, 3.9, "3.10", "3.11", "3.12-dev"] include: - - os: ubuntu-latest - python-version: "pypy-3.7" - experimental: false - - os: ubuntu-latest - python-version: "3.12-dev" - experimental: true - - os: macOS-latest - python-version: "3.12-dev" - experimental: true - - os: windows-latest - python-version: "3.12-dev" - experimental: true + - python-version: "pypy-3.7" + os: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -70,17 +58,17 @@ jobs: fi shell: bash - name: Install extra dependencies - if: ${{ ! matrix.experimental }} + if: ${{ 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.experimental }} + if: ${{ python-version == "3.12-dev" }} run: | python -m pyfakefs.tests.all_tests shell: bash - name: Run performance tests - if: ${{ ! matrix.experimental }} + if: ${{ python-version == "3.12-dev" }} run: | if [[ '${{ matrix.os }}' != 'macOS-latest' ]]; then export TEST_PERFORMANCE=1 From a0d1f7235bb77451de6a1cac6a600210b3da7ddf Mon Sep 17 00:00:00 2001 From: Bruno Alla Date: Thu, 22 Dec 2022 11:20:45 +0100 Subject: [PATCH 11/14] Enable performance tests on Python 3.12 --- .github/workflows/testsuite.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/testsuite.yml b/.github/workflows/testsuite.yml index 6a14e10a..c1655716 100644 --- a/.github/workflows/testsuite.yml +++ b/.github/workflows/testsuite.yml @@ -68,7 +68,6 @@ jobs: python -m pyfakefs.tests.all_tests shell: bash - name: Run performance tests - if: ${{ python-version == "3.12-dev" }} run: | if [[ '${{ matrix.os }}' != 'macOS-latest' ]]; then export TEST_PERFORMANCE=1 From cb03c5cca3b71399a9c0c097cedd86b28e3fd159 Mon Sep 17 00:00:00 2001 From: Bruno Alla Date: Thu, 22 Dec 2022 11:24:57 +0100 Subject: [PATCH 12/14] Fix condition --- .github/workflows/testsuite.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/testsuite.yml b/.github/workflows/testsuite.yml index c1655716..03c3c0f6 100644 --- a/.github/workflows/testsuite.yml +++ b/.github/workflows/testsuite.yml @@ -58,12 +58,12 @@ jobs: fi shell: bash - name: Install extra dependencies - if: ${{ python-version == "3.12-dev" }} + if: ${{ 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: ${{ python-version == "3.12-dev" }} + if: ${{ python-version != "3.12-dev" }} run: | python -m pyfakefs.tests.all_tests shell: bash From 311c982638a8a9af7bf5b023c6153b9292b2d3ac Mon Sep 17 00:00:00 2001 From: Bruno Alla Date: Thu, 22 Dec 2022 11:26:22 +0100 Subject: [PATCH 13/14] Fix grammar in docstrings --- pyfakefs/fake_filesystem.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyfakefs/fake_filesystem.py b/pyfakefs/fake_filesystem.py index cbce2e50..a294ab58 100644 --- a/pyfakefs/fake_filesystem.py +++ b/pyfakefs/fake_filesystem.py @@ -3449,7 +3449,7 @@ def islink(self, path: AnyPath) -> bool: if sys.version_info >= (3, 12): def isjunction(self, path: AnyPath) -> bool: - """Junction are never faked.""" + """Returns False. Junctions are never faked.""" return False def confirmdir( @@ -3720,7 +3720,7 @@ def islink(self, path: AnyStr) -> bool: if sys.version_info >= (3, 12): def isjunction(self, path: AnyStr) -> bool: - """Junction are never faked.""" + """Returns False. Junctions are never faked.""" return self.filesystem.isjunction(path) def getmtime(self, path: AnyStr) -> float: From f1b954b83a745bac90a7b5b016c13a57bf4ae117 Mon Sep 17 00:00:00 2001 From: Bruno Alla Date: Thu, 22 Dec 2022 11:27:30 +0100 Subject: [PATCH 14/14] Fix condition in workflow --- .github/workflows/testsuite.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/testsuite.yml b/.github/workflows/testsuite.yml index 03c3c0f6..b11c209a 100644 --- a/.github/workflows/testsuite.yml +++ b/.github/workflows/testsuite.yml @@ -58,12 +58,12 @@ jobs: fi shell: bash - name: Install extra dependencies - if: ${{ python-version != "3.12-dev" }} + 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: ${{ python-version != "3.12-dev" }} + if: ${{ matrix.python-version != '3.12-dev' }} run: | python -m pyfakefs.tests.all_tests shell: bash