Skip to content

pathlib.Path() inconsistent behavior #655

Description

@sodul

Describe the bug
We have tests that are broken when they use Path objects instantiated before setUpPyfakefs() is called. If the objects are re-instantiated after setUpPyfakefs() things go back to the fake filesystem.

Recasting the Path objects after setUpPyfakefs() is called is a workaround on the issue but I suspect many unittests will now write to the real filesystem as a side effect (we have internal tests that do just that). In the lucky cases were we used '/fake' or other similarly broken paths, on purpose, the tests fails explicitly. In other cases we are concerned that directories might be deleted by tests when they should have been left alone.

How To Reproduce
Please provide a unit test or a minimal code snippet that reproduces the
problem.

import os
import pathlib
import pyfakefs.fake_filesystem_unittest as fake_fs 

FAKE_PATH = pathlib.Path('/fake')

class TestFake(fake_fs.TestCase):
    def setUp(self):
        self.setUpPyfakefs()
        self.fs.create_dir('/fake')

    def test_os(self):
        file_path = '/test/file.txt'
        self.assertFalse(os.path.exists(file_path))
        self.fs.create_file(file_path)
        self.assertTrue(os.path.exists(file_path))

    def test_pathlib(self):
        file_path = pathlib.Path(FAKE_PATH) / 'file1.txt'
        self.assertFalse(file_path.exists())
        file_path.write_text('')
        self.assertTrue(file_path.exists())

    def test_global_path(self):
        file_path = FAKE_PATH / 'file2.txt'
        self.assertFalse(file_path.exists())
        file_path.write_text('')
        self.assertTrue(file_path.exists())

The test_global_path fails but I would expect that to pass as modules from the code under test might have instantiated pathlib.Path instances during import time for example. This breaks the assumption that fakefs will capture all filesystem access.

Your environment
Please run the following and paste the output.

macOS-12.1-x86_64-i386-64bit
Python 3.10.1 (main, Jan  4 2022, 12:45:52) [Clang 13.0.0 (clang-1300.0.29.30)]
pyfakefs 4.5.3

Activity

  1. mrbean-bremen commented on Jan 6, 2022

    @mrbean-bremen
    Member

    This is a known limitation to pyfakefs (and other patching methods). From the documentation of the modules_to_reload argument:

    If initializing a global variable using a file system function, the initialization will be done using the real file system:

    from pathlib import Path
    
    path = Path("/example_home")

    In this case, path will hold the real file system path inside the test. The same is true, if a file system function is used in a decorator (...).
    To get these cases to work as expected under test, the respective modules containing the code shall be added to the modules_to_reload argument (a module list). The passed modules will be reloaded, thus allowing pyfakefs to patch them dynamically. All modules loaded after the initial patching described above will be patched using this second mechanism.

    As reloading the test itself does not make sense, modules_to_reload would not help for your example, and I don't know a way of how to fix this without changing the definition. A workaround would be to use an accessor function instead of the function, or lazy initialization of the global variable after setupPyfakefs. Both are not ideal, but as I wrote that is a limitation of patching in general.

  2. sodul commented on Jan 14, 2022

    @sodul
    Author

    @mrbean-bremen thanks for pointing this out, I did not notice that in the documentation and that really helps.

  3. locked and limited conversation to collaborators on Jan 14, 2022
  4. converted this issue into a discussion #657 on Jan 14, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions