diff --git a/.github/workflows/testsuite.yml b/.github/workflows/testsuite.yml index 4e8b599b..f93700c8 100644 --- a/.github/workflows/testsuite.yml +++ b/.github/workflows/testsuite.yml @@ -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- @@ -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 @@ -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 diff --git a/CHANGES.md b/CHANGES.md index 98c34ad2..fa7c9e99 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,17 @@ # pyfakefs Release Notes The released versions correspond to PyPI releases. +## Planned changes for next major release (6.0.0) +* remove support for patching legacy modules `scandir` and `pathlib2` +* remove support for Python 3.7 + +## Unreleased + +### Changes +* The usage of the `pathlib2` and `scandir` modules in pyfakefs is now deprecated. + They will now cause deprecation warnings if still used. Support for patching + these modules will be removed in pyfakefs 6.0. + ## [Version 5.4.1](https://pypi.python.org/pypi/pyfakefs/5.4.0) (2024-04-11) Fixes a regression. diff --git a/extra_requirements.txt b/extra_requirements.txt index 5f858a3a..c8bcfb76 100644 --- a/extra_requirements.txt +++ b/extra_requirements.txt @@ -1,16 +1,4 @@ -# "pathlib2" and "scandir" are backports of new standard modules, pyfakefs will -# use them if available when running on older Python versions. -# -# They are dependencies of pytest when Python < 3.6 so we sometimes get them via -# requirements.txt, this file makes them explicit dependencies for testing & -# development. -# -# Older versions might work ok, the versions chosen here are just the latest -# available at the time of writing. -pathlib2>=2.3.2 -scandir>=1.8 - -# pandas + xlrd are used to test pandas-specific patches to allow +# these are used to test pandas-specific patches to allow # pyfakefs to work with pandas # we use the latest version to see any problems with new versions pandas==1.3.5; python_version == '3.7' # pyup: ignore diff --git a/legacy_requirements.txt b/legacy_requirements.txt new file mode 100644 index 00000000..dd00d2a3 --- /dev/null +++ b/legacy_requirements.txt @@ -0,0 +1,8 @@ +# "pathlib2" and "scandir" are backports of new standard modules, pyfakefs will +# patch them if available when running on older Python versions. +# +# The modules are no longer for all required Python version, and only used for CI tests. +# Note that the usage of these modules is deprecated, and their support +# will be removed in pyfakefs 6.0 +pathlib2>=2.3.2 +scandir>=1.8 diff --git a/pyfakefs/fake_filesystem_unittest.py b/pyfakefs/fake_filesystem_unittest.py index 231e2075..45202a14 100644 --- a/pyfakefs/fake_filesystem_unittest.py +++ b/pyfakefs/fake_filesystem_unittest.py @@ -83,13 +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, use_scandir - -if use_scandir: - 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" @@ -701,13 +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 - if use_scandir: - self._fake_module_classes["scandir"] = fake_scandir.FakeScanDirModule def _init_fake_module_functions(self) -> None: # handle patching function imported separately like diff --git a/pyfakefs/fake_legacy_modules.py b/pyfakefs/fake_legacy_modules.py new file mode 100644 index 00000000..7c25b98f --- /dev/null +++ b/pyfakefs/fake_legacy_modules.py @@ -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) diff --git a/pyfakefs/fake_os.py b/pyfakefs/fake_os.py index 58e85e96..c57de0d0 100644 --- a/pyfakefs/fake_os.py +++ b/pyfakefs/fake_os.py @@ -40,7 +40,6 @@ Set, ) -from pyfakefs.extra_packages import use_scandir from pyfakefs.fake_file import ( FakeDirectory, FakeDirWrapper, @@ -122,6 +121,7 @@ def dir() -> List[str]: "removedirs", "rename", "rmdir", + "scandir", "stat", "symlink", "umask", @@ -145,8 +145,6 @@ def dir() -> List[str]: "getgid", "getuid", ] - if use_scandir: - _dir += ["scandir"] return _dir def __init__(self, filesystem: "FakeFilesystem"): diff --git a/pyfakefs/fake_pathlib.py b/pyfakefs/fake_pathlib.py index 3e815dad..5a3f46d3 100644 --- a/pyfakefs/fake_pathlib.py +++ b/pyfakefs/fake_pathlib.py @@ -44,7 +44,6 @@ from urllib.parse import quote_from_bytes as urlquote_from_bytes from pyfakefs import fake_scandir -from pyfakefs.extra_packages import use_scandir from pyfakefs.fake_filesystem import FakeFilesystem from pyfakefs.fake_open import FakeFileOpen from pyfakefs.fake_os import FakeOsModule, use_original_os @@ -109,9 +108,7 @@ class _FakeAccessor(accessor): # type: ignore[valid-type, misc] ) listdir = _wrap_strfunc(FakeFilesystem.listdir) - - if use_scandir: - scandir = _wrap_strfunc(fake_scandir.scandir) + scandir = _wrap_strfunc(fake_scandir.scandir) if hasattr(os, "lchmod"): lchmod = _wrap_strfunc( @@ -775,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()` diff --git a/pyfakefs/fake_scandir.py b/pyfakefs/fake_scandir.py index 75f8caa4..b9f7690f 100644 --- a/pyfakefs/fake_scandir.py +++ b/pyfakefs/fake_scandir.py @@ -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.""" @@ -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" ) @@ -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) diff --git a/pyfakefs/extra_packages.py b/pyfakefs/legacy_packages.py similarity index 62% rename from pyfakefs/extra_packages.py rename to pyfakefs/legacy_packages.py index 23e08148..465b464b 100644 --- a/pyfakefs/extra_packages.py +++ b/pyfakefs/legacy_packages.py @@ -11,7 +11,8 @@ # limitations under the License. """Imports external packages that replace or emulate internal packages. -If the external module is not present, the built-in module is imported. +These packages are not needed with any current Python version, +and their support in pyfakefs will be removed in a an upcoming release. """ try: @@ -20,18 +21,6 @@ pathlib2 = None try: - import scandir - - use_scandir_package = True - use_builtin_scandir = False + import scandir as scandir except ImportError: - try: - from os import scandir # noqa: F401 - - use_builtin_scandir = True - use_scandir_package = False - except ImportError: - use_builtin_scandir = False - use_scandir_package = False - -use_scandir = use_scandir_package or use_builtin_scandir + scandir = None diff --git a/pyfakefs/tests/all_tests.py b/pyfakefs/tests/all_tests.py index 36945afa..2042884c 100644 --- a/pyfakefs/tests/all_tests.py +++ b/pyfakefs/tests/all_tests.py @@ -32,6 +32,7 @@ fake_pathlib_test, fake_tempfile_test, patched_packages_test, + fake_legacy_modules_test, mox3_stubout_test, ) @@ -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 diff --git a/pyfakefs/tests/all_tests_without_extra_packages.py b/pyfakefs/tests/all_tests_without_extra_packages.py index 8927802f..822e7ce7 100644 --- a/pyfakefs/tests/all_tests_without_extra_packages.py +++ b/pyfakefs/tests/all_tests_without_extra_packages.py @@ -16,16 +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 - try: - from os import scandir - except ImportError: - scandir = None - extra_packages.scandir = scandir - extra_packages.use_scandir = scandir +legacy_packages.scandir = None +legacy_packages.pathlib2 = None from pyfakefs.tests.all_tests import AllTests # noqa: E402 diff --git a/pyfakefs/tests/example_test.py b/pyfakefs/tests/example_test.py index 126bbe74..03de633a 100644 --- a/pyfakefs/tests/example_test.py +++ b/pyfakefs/tests/example_test.py @@ -32,7 +32,7 @@ import unittest from pyfakefs import fake_filesystem_unittest -from pyfakefs.extra_packages import use_scandir_package +from pyfakefs.legacy_packages import scandir from pyfakefs.tests import example # The module under test @@ -143,9 +143,7 @@ def test_os_scandir(self): self.assertTrue(entries[1].is_symlink()) self.assertTrue(entries[2].is_file()) - @unittest.skipIf( - not use_scandir_package, "Testing only if scandir module is installed" - ) + @unittest.skipIf(scandir is None, "Testing only if scandir module is installed") def test_scandir_scandir(self): """Test example.scandir() which uses `scandir.scandir()`. diff --git a/pyfakefs/tests/fake_legacy_modules_test.py b/pyfakefs/tests/fake_legacy_modules_test.py new file mode 100644 index 00000000..cce2a302 --- /dev/null +++ b/pyfakefs/tests/fake_legacy_modules_test.py @@ -0,0 +1,118 @@ +# 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 unittest + +from pyfakefs.fake_filesystem_unittest import TestCase +from pyfakefs.fake_legacy_modules import FakeScanDirModule, FakePathlib2Module +from pyfakefs.legacy_packages import pathlib2, scandir +from pyfakefs.tests.fake_os_test import FakeScandirTest +from pyfakefs.tests.fake_pathlib_test import ( + FakePathlibInitializationTest, + FakePathlibPathFileOperationTest, + FakePathlibFileObjectPropertyTest, + FakePathlibUsageInOsFunctionsTest, +) + + +class DeprecationWarningTest(TestCase): + def setUp(self): + self.setUpPyfakefs() + + @unittest.skipIf(scandir is None, "The scandir package is not installed") + def test_scandir_warning(self): + FakeScanDirModule.has_warned = False + with self.assertWarns(DeprecationWarning): + scandir.scandir("/") + + @unittest.skipIf(pathlib2 is None, "The pathlib2 package is not installed") + def test_pathlib2_warning(self): + FakePathlib2Module.has_warned = False + with self.assertWarns(DeprecationWarning): + pathlib2.Path("/foo") + + +@unittest.skipIf(scandir is None, "The scandir package is not installed") +class FakeScandirPackageTest(FakeScandirTest): + def used_scandir(self): + import pyfakefs.fake_legacy_modules + + def fake_scan_dir(p): + return pyfakefs.fake_legacy_modules.FakeScanDirModule( + self.filesystem + ).scandir(p) + + return fake_scan_dir + + def test_path_like(self): + unittest.skip("Path-like objects not available in scandir package") + + +class RealScandirPackageTest(FakeScandirPackageTest): + def used_scandir(self): + from scandir import scandir + + return scandir + + def use_real_fs(self): + return True + + +@unittest.skipIf(pathlib2 is None, "The pathlib2 package is not installed") +class FakePathlib2InitializationTest(FakePathlibInitializationTest): + def used_pathlib(self): + return pathlib2 + + +class RealPathlib2InitializationTest(FakePathlib2InitializationTest): + def use_real_fs(self): + return True + + +@unittest.skipIf(pathlib2 is None, "The pathlib2 package is not installed") +class FakePathlib2FileObjectPropertyTest(FakePathlibFileObjectPropertyTest): + def used_pathlib(self): + return pathlib2 + + +class RealPathlib2FileObjectPropertyTest(FakePathlib2FileObjectPropertyTest): + def use_real_fs(self): + return True + + +@unittest.skipIf(pathlib2 is None, "The pathlib2 package is not installed") +class FakePathlib2PathFileOperationTest(FakePathlibPathFileOperationTest): + def used_pathlib(self): + return pathlib2 + + def test_is_junction(self): + unittest.skip("is_junction not available in pathlib2") + + +class RealPathlibPath2FileOperationTest(FakePathlib2PathFileOperationTest): + def use_real_fs(self): + return True + + +@unittest.skipIf(pathlib2 is None, "The pathlib2 package is not installed") +class FakePathlib2UsageInOsFunctionsTest(FakePathlibUsageInOsFunctionsTest): + def used_pathlib(self): + return pathlib2 + + +class RealPathlib2UsageInOsFunctionsTest(FakePathlib2UsageInOsFunctionsTest): + def use_real_fs(self): + return True + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/pyfakefs/tests/fake_os_test.py b/pyfakefs/tests/fake_os_test.py index cf9af4d8..b17ddb86 100644 --- a/pyfakefs/tests/fake_os_test.py +++ b/pyfakefs/tests/fake_os_test.py @@ -21,8 +21,6 @@ import sys import unittest -from pyfakefs.helpers import IN_DOCKER, IS_PYPY, get_uid, get_gid, reset_ids - from pyfakefs import fake_filesystem, fake_os, fake_open, fake_file from pyfakefs.fake_filesystem import ( FakeFileOpen, @@ -30,12 +28,7 @@ set_uid, set_gid, ) -from pyfakefs.extra_packages import ( - use_scandir, - use_scandir_package, - use_builtin_scandir, -) - +from pyfakefs.helpers import IN_DOCKER, IS_PYPY, get_uid, get_gid, reset_ids from pyfakefs.tests.test_utils import TestCase, RealFsTestCase @@ -5244,28 +5237,17 @@ def test_file_size_truncation(self): fh.close() -@unittest.skipIf(not use_scandir, "only run if scandir is available") class FakeScandirTest(FakeOsModuleTestBase): FILE_SIZE = 50 LINKED_FILE_SIZE = 10 + def used_scandir(self): + return self.os.scandir + def setUp(self): super().setUp() self.supports_symlinks = not self.is_windows or not self.use_real_fs() - - if use_scandir_package: - if self.use_real_fs(): - from scandir import scandir - else: - import pyfakefs.fake_scandir - - def fake_scan_dir(p): - return pyfakefs.fake_scandir.scandir(self.filesystem, p) - - scandir = fake_scan_dir - else: - scandir = self.os.scandir - self.scandir = scandir + self.scandir = self.used_scandir() self.directory = self.make_path("xyzzy", "plugh") link_dir = self.make_path("linked", "plugh") @@ -5383,7 +5365,7 @@ def test_path_links_not_resolved(self): ) def test_inode(self): - if use_scandir and self.use_real_fs(): + if self.use_real_fs(): if self.is_windows: self.skipTest("inode seems not to work in scandir module under Windows") if IN_DOCKER: @@ -5503,10 +5485,6 @@ def test_stat_ino_dev(self): self.assertEqual(file_stat.st_ino, self.dir_entries[5].stat().st_ino) self.assertEqual(file_stat.st_dev, self.dir_entries[5].stat().st_dev) - @unittest.skipIf( - not use_builtin_scandir, - "Path-like objects not available in scandir package", - ) def test_path_like(self): self.assertTrue(isinstance(self.dir_entries[0], os.PathLike)) self.assertEqual( @@ -5545,7 +5523,6 @@ def use_real_fs(self): @unittest.skipIf(TestCase.is_windows, "dir_fd not supported for os.scandir in Windows") -@unittest.skipIf(use_scandir_package, "no dir_fd support for scandir package") class FakeScandirFdTest(FakeScandirTest): def tearDown(self): self.os.close(self.dir_fd) diff --git a/pyfakefs/tests/fake_pathlib_test.py b/pyfakefs/tests/fake_pathlib_test.py index e8544450..145b0cdd 100644 --- a/pyfakefs/tests/fake_pathlib_test.py +++ b/pyfakefs/tests/fake_pathlib_test.py @@ -44,6 +44,9 @@ def __init__(self, methodName="runTest"): fake_filesystem_unittest.TestCase.__init__(self, methodName) RealFsTestMixin.__init__(self) + def used_pathlib(self): + return pathlib + def setUp(self): RealFsTestMixin.setUp(self) self.filesystem = None @@ -52,8 +55,8 @@ def setUp(self): self.setUpPyfakefs() self.filesystem = self.fs self.create_basepath() - self.pathlib = pathlib - self.path = pathlib.Path + self.pathlib = self.used_pathlib() + self.path = self.pathlib.Path self.os = os self.open = open