Skip to content

Commit

Permalink
Deprecation of scandir and pathlib2
Browse files Browse the repository at this point in the history
- separate code and tests related to these packages
- add warning on first usage of a package function
  • Loading branch information
mrbean-bremen committed Apr 14, 2024
1 parent 130040c commit 3d62035
Show file tree
Hide file tree
Showing 13 changed files with 283 additions and 166 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/testsuite.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ jobs:
uses: actions/cache@v4
with:
path: ${{ steps.pip-cache.outputs.dir }}
key: ${{ matrix.os }}-${{ matrix.python-version }}-pip-${{ hashFiles('**/requirements.txt') }}-${{ hashFiles('**/extra_requirements.txt') }}
key: ${{ matrix.os }}-${{ matrix.python-version }}-pip-${{ hashFiles('**/requirements.txt') }}-${{ hashFiles('**/extra_requirements.txt') }}-${{ hashFiles('**/legacy_requirements.txt') }}
restore-keys: |
${{ matrix.os }}-${{ matrix.python-version }}-pip-
Expand Down Expand Up @@ -82,6 +82,7 @@ jobs:
if: ${{ matrix.python-version != 'pypy-3.10' }}
run: |
pip install -r extra_requirements.txt
pip install -r legacy_requirements.txt
pip install zstandard cffi # needed to test #910
shell: bash
- name: Run unit tests with extra packages as non-root user
Expand Down Expand Up @@ -148,6 +149,7 @@ jobs:
run: |
pip install -r requirements.txt
pip install -r extra_requirements.txt
pip install -r legacy_requirements.txt
pip install pytest-find-dependencies
- name: Check dependencies
run: python -m pytest --find-dependencies pyfakefs/tests
Expand Down
53 changes: 0 additions & 53 deletions pyfakefs/extra_packages.py

This file was deleted.

11 changes: 7 additions & 4 deletions pyfakefs/fake_filesystem_unittest.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,11 @@
from importlib import reload

from pyfakefs import fake_filesystem, fake_io, fake_os, fake_open, fake_path, fake_file
from pyfakefs import fake_legacy_modules
from pyfakefs import fake_filesystem_shutil
from pyfakefs import fake_pathlib
from pyfakefs import mox3_stubout
from pyfakefs.extra_packages import pathlib2
from pyfakefs import fake_scandir
from pyfakefs.legacy_packages import pathlib2, scandir

OS_MODULE = "nt" if sys.platform == "win32" else "posix"
PATH_MODULE = "ntpath" if sys.platform == "win32" else "posixpath"
Expand Down Expand Up @@ -699,12 +699,15 @@ def _init_fake_module_classes(self) -> None:
self._class_modules["Path"] = ["pathlib"]
self._unfaked_module_classes["pathlib"] = fake_pathlib.RealPathlibModule
if pathlib2:
self._fake_module_classes["pathlib2"] = fake_pathlib.FakePathlibModule
self._fake_module_classes["pathlib2"] = (
fake_legacy_modules.FakePathlib2Module
)
self._class_modules["Path"].append("pathlib2")
self._unfaked_module_classes["pathlib2"] = fake_pathlib.RealPathlibModule
if scandir:
self._fake_module_classes["scandir"] = fake_legacy_modules.FakeScanDirModule
self._fake_module_classes["Path"] = fake_pathlib.FakePathlibPathModule
self._unfaked_module_classes["Path"] = fake_pathlib.RealPathlibPathModule
self._fake_module_classes["scandir"] = fake_scandir.FakeScanDirModule

def _init_fake_module_functions(self) -> None:
# handle patching function imported separately like
Expand Down
110 changes: 110 additions & 0 deletions pyfakefs/fake_legacy_modules.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import warnings


from pyfakefs.fake_pathlib import FakePathlibModule
from pyfakefs.fake_scandir import scandir, walk


def legacy_warning(module_name):
msg = (
f"You are using the legacy package '{module_name}' instead of the "
f"built-in module."
"Patching this package will no longer be supported in pyfakefs >= 6"
)
warnings.warn(msg, category=DeprecationWarning)


class FakePathlib2Module(FakePathlibModule):
"""Uses FakeFilesystem to provide a fake pathlib module replacement.
for the `pathlib2` package available on PyPi.
The usage of `pathlib2` is deprecated and will no longer be supported
in future pyfakefs versions.
"""

has_warned = False

def __getattribute__(self, name):
attr = object.__getattribute__(self, name)
if hasattr(attr, "__call__") and not FakePathlib2Module.has_warned:
FakePathlib2Module.has_warned = True
legacy_warning("pathlib2")
return attr


class FakeScanDirModule:
"""Uses FakeFilesystem to provide a fake module replacement
for the `scandir` package available on PyPi.
The usage of the `scandir` package is deprecated and will no longer be supported
in future pyfakefs versions.
You need a fake_filesystem to use this:
`filesystem = fake_filesystem.FakeFilesystem()`
`fake_scandir_module = fake_filesystem.FakeScanDirModule(filesystem)`
"""

@staticmethod
def dir():
"""Return the list of patched function names. Used for patching
functions imported from the module.
"""
return "scandir", "walk"

def __init__(self, filesystem):
self.filesystem = filesystem

has_warned = False

def scandir(self, path="."):
"""Return an iterator of DirEntry objects corresponding to the entries
in the directory given by path.
Args:
path: Path to the target directory within the fake filesystem.
Returns:
an iterator to an unsorted list of os.DirEntry objects for
each entry in path.
Raises:
OSError: if the target is not a directory.
"""
if not self.has_warned:
self.__class__.has_warned = True
legacy_warning("scandir")
return scandir(self.filesystem, path)

def walk(self, top, topdown=True, onerror=None, followlinks=False):
"""Perform a walk operation over the fake filesystem.
Args:
top: The root directory from which to begin walk.
topdown: Determines whether to return the tuples with the root as
the first entry (`True`) or as the last, after all the child
directory tuples (`False`).
onerror: If not `None`, function which will be called to handle the
`os.error` instance provided when `os.listdir()` fails.
followlinks: If `True`, symbolic links are followed.
Yields:
(path, directories, nondirectories) for top and each of its
subdirectories. See the documentation for the builtin os module
for further details.
"""
if not self.has_warned:
self.__class__.has_warned = True
legacy_warning("scandir")

return walk(self.filesystem, top, topdown, onerror, followlinks)
2 changes: 0 additions & 2 deletions pyfakefs/fake_pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -772,8 +772,6 @@ def is_reserved(self):

class FakePathlibModule:
"""Uses FakeFilesystem to provide a fake pathlib module replacement.
Can be used to replace both the standard `pathlib` module and the
`pathlib2` package available on PyPi.
You need a fake_filesystem to use this:
`filesystem = fake_filesystem.FakeFilesystem()`
Expand Down
70 changes: 2 additions & 68 deletions pyfakefs/fake_scandir.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,10 @@
import os
import sys

from pyfakefs.extra_packages import use_scandir_package
from pyfakefs.helpers import to_string, make_string_path

if sys.version_info >= (3, 6):
BaseClass = os.PathLike
else:
BaseClass = object


class DirEntry(BaseClass):
class DirEntry(os.PathLike):
"""Emulates os.DirEntry. Note that we did not enforce keyword only
arguments."""

Expand Down Expand Up @@ -133,9 +127,7 @@ class ScanDirIter:
def __init__(self, filesystem, path):
self.filesystem = filesystem
if isinstance(path, int):
if not use_scandir_package and (
sys.version_info < (3, 7) or self.filesystem.is_windows_fs
):
if self.filesystem.is_windows_fs:
raise NotImplementedError(
"scandir does not support file descriptor " "path argument"
)
Expand Down Expand Up @@ -263,61 +255,3 @@ def do_walk(top_dir, top_most=False):
yield top_contents

return do_walk(make_string_path(to_string(top)), top_most=True)


class FakeScanDirModule:
"""Uses FakeFilesystem to provide a fake `scandir` module replacement.
.. Note:: The ``scandir`` function is a part of the standard ``os`` module
since Python 3.5. This class handles the separate ``scandir`` module
that is available on pypi.
You need a fake_filesystem to use this:
`filesystem = fake_filesystem.FakeFilesystem()`
`fake_scandir_module = fake_filesystem.FakeScanDirModule(filesystem)`
"""

@staticmethod
def dir():
"""Return the list of patched function names. Used for patching
functions imported from the module.
"""
return "scandir", "walk"

def __init__(self, filesystem):
self.filesystem = filesystem

def scandir(self, path="."):
"""Return an iterator of DirEntry objects corresponding to the entries
in the directory given by path.
Args:
path: Path to the target directory within the fake filesystem.
Returns:
an iterator to an unsorted list of os.DirEntry objects for
each entry in path.
Raises:
OSError: if the target is not a directory.
"""
return scandir(self.filesystem, path)

def walk(self, top, topdown=True, onerror=None, followlinks=False):
"""Perform a walk operation over the fake filesystem.
Args:
top: The root directory from which to begin walk.
topdown: Determines whether to return the tuples with the root as
the first entry (`True`) or as the last, after all the child
directory tuples (`False`).
onerror: If not `None`, function which will be called to handle the
`os.error` instance provided when `os.listdir()` fails.
followlinks: If `True`, symbolic links are followed.
Yields:
(path, directories, nondirectories) for top and each of its
subdirectories. See the documentation for the builtin os module
for further details.
"""
return walk(self.filesystem, top, topdown, onerror, followlinks)
26 changes: 26 additions & 0 deletions pyfakefs/legacy_packages.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Imports external packages that replace or emulate internal packages.
These packages are not needed with any current Python version,
and their support in pyfakefs will be removed in a an upcoming release.
"""

try:
import pathlib2
except ImportError:
pathlib2 = None

try:
import scandir as scandir
except ImportError:
scandir = None
2 changes: 2 additions & 0 deletions pyfakefs/tests/all_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
fake_pathlib_test,
fake_tempfile_test,
patched_packages_test,
fake_legacy_modules_test,
mox3_stubout_test,
)

Expand All @@ -57,6 +58,7 @@ def suite(self): # pylint: disable-msg=C6409
loader.loadTestsFromModule(dynamic_patch_test),
loader.loadTestsFromModule(fake_pathlib_test),
loader.loadTestsFromModule(patched_packages_test),
loader.loadTestsFromModule(fake_legacy_modules_test),
]
)
return self
Expand Down
9 changes: 3 additions & 6 deletions pyfakefs/tests/all_tests_without_extra_packages.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,10 @@
import sys
import unittest

from pyfakefs import extra_packages
from pyfakefs import legacy_packages

if extra_packages.use_scandir_package:
extra_packages.use_scandir_package = False
from os import scandir

extra_packages.scandir = scandir
legacy_packages.scandir = None
legacy_packages.pathlib2 = None

from pyfakefs.tests.all_tests import AllTests # noqa: E402

Expand Down
Loading

0 comments on commit 3d62035

Please sign in to comment.