Repository: pytest-dev/pyfakefs

Issue:
'WindowsPath' object has no attribute 'startswith' when setting cwd
I'm working on a project that uses pyfakefs to mock my filesystem to test folder creation and missing folders in a previously defined tree structure. I'm using **Python 3.13** on **Windows** and get this output from the terminal after running my test:

*Terminal output:*


```
E
======================================================================
ERROR: test_top_folders_exist (file_checker.tests.file_checker_tests.TestFolderCheck.test_top_folders_exist)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Users\juank\dev\projects\python\gamedev_eco\file_checker\tests\file_checker_tests.py", line 20, in test_top_folders_exist
    self.fs.create_dir(Path.cwd() / "gdd")
    ~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\juank\AppData\Local\Programs\Python\Python313\Lib\site-packages\pyfakefs\fake_filesystem.py", line 2191, in create_dir
    dir_path = self.absnormpath(dir_path)
  File "C:\Users\juank\AppData\Local\Programs\Python\Python313\Lib\site-packages\pyfakefs\fake_filesystem.py", line 1133, in absnormpath
    path = self.replace_windows_root(path)
  File "C:\Users\juank\AppData\Local\Programs\Python\Python313\Lib\site-packages\pyfakefs\fake_filesystem.py", line 1418, in replace_windows_root
    if path and self.is_windows_fs and self.root_dir:
                                       ^^^^^^^^^^^^^
  File "C:\Users\juank\AppData\Local\Programs\Python\Python313\Lib\site-packages\pyfakefs\fake_filesystem.py", line 357, in root_dir
    return self._mount_point_dir_for_cwd()
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
  File "C:\Users\juank\AppData\Local\Programs\Python\Python313\Lib\site-packages\pyfakefs\fake_filesystem.py", line 631, in _mount_point_dir_for_cwd
    if path.startswith(str_root_path) and len(str_root_path) > len(mount_path):
       ^^^^^^^^^^^^^^^
AttributeError: 'WindowsPath' object has no attribute 'startswith'

----------------------------------------------------------------------
Ran 1 test in 0.011s

FAILED (errors=1)
```
 
*Test:*

```py
from pyfakefs.fake_filesystem_unittest import TestCase

class TestFolderCheck(TestCase):
    """Test top folders = gdd marketing business"""

    @classmethod
    def setUp(cls):
        cls.setUpClassPyfakefs()
        cls.fake_fs().create_dir(Path.cwd() / "gamedev_eco")
        cls.fake_fs().cwd = Path.cwd() / "gamedev_eco"  # Needs to be cast with str() to work

    def test_top_folders_exist(self):
        self.fs.create_dir(Path.cwd() / "gdd")
```
 I found out after troubleshooting with another programmer that `cls.fake_fs().cwd` had to be cast as a string to be legally set like this: `str(cls.fake_fs().cwd)`. That is not so bad on tests but it is in my source code, otherwise tests won't pass.

```py
import os
from pathlib import Path


class InvalidNameFormat(Exception):
    pass


class MissingFileFolder(Exception):
    pass


def check_folders():
    paths = [
        str(Path.cwd() / "gdd"),  # Works
        Path.cwd() / "marketing",  # Does not work
        Path.cwd() / "business",
        Path.cwd() / "gdd" / "components",
        Path.cwd() / "gdd" / "milestones",
        Path.cwd() / "gdd" / "components" / "characters",
        Path.cwd() / "gdd" / "components" / "environments",
        Path.cwd() / "gdd" / "components" / "ui",
        Path.cwd() / "gdd" / "components" / "cinematics",
    ]

    for root, dirs, files in os.walk(Path.cwd()):
        try:
            for d in dirs:
                paths.index(os.path.join(root, d))
        except ValueError:
            for p in paths:
                if not os.path.exists(p):
                    os.mkdir(p)
            raise MissingFileFolder(
                "Missing folders have been created for you, please use them store you markdown files"
            )

        for d in dirs:
            if d.isupper() and not d.isalpha():
                raise InvalidNameFormat(
                    f"{d} folder name must be lowercase and has no digits"
                )
```

I couldn't find anything that could give me a specific way to set `cwd` rather than just any string but I wonder if this is a platform or python version related issue. Maybe there's something different in Python 3.13 that causes this problem since a StackOverFlow community member confirmed me in this [question](https://stackoverflow.com/questions/79336604/failed-creating-mock-folders-with-pyfakefs?noredirect=1#comment139904438_79336604) he didn't get any trouble running the code on **Linux** and **Python 3.11**. 

Please let me know if you need more details from my side I'd be glad to contribute to your project by helping out troubleshooting.

Hints from issue comments:
@mrbean-bremen: Thanks for the report! I can reproduce this with different Python versions, so this is not a Python 3.13-specific problem. It looks like a path object is passed somewhere where a string is expected.@wavesinaroom: Sure! Not a problem. @wavesinaroom: I just double-checked that this is related with the way I set `cwd` in my `setUp` method. I looked for a method in the documentation that would help me do that but apparently direct assignment seems the only choice. Do you have a specific method to set this variable e.g `cls.fake_fs().setCwd()` that my LSP (lsp-python) misses or I'm ignoring?

![image](https://github.com/user-attachments/assets/9b77a5b2-1660-4b62-a3c1-6e7fcedb1db7)
@mrbean-bremen: I made a PR that should fix this, will probably merge tomorrow in case nobody objects.
There is still another more complicated issue I wanted to handle before a patch release, but if I don't get that fixed in the near future, I'll probably make a release anyway.@wavesinaroom: Awesome @mrbean-bremen! Many thanks for fixing this so quickly.

Have a good one \m/@mrbean-bremen: FYI: the patch release is out now.@wavesinaroom: Thanks a lot! Having it working has been awesome for the project I'm working on.

Cheers!

Solution patch:
diff --git a/CHANGES.md b/CHANGES.md
index 951d2e5..b381f4a 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -17,6 +17,7 @@ The released versions correspond to PyPI releases.
 ### Fixes
 * fixed a problem with module and session scoped fixtures in Python 3.13
   (see [#1101](../../issues/1101))
+* fixed handling of `cwd` if set to a `pathlib.Path` (see [#1108](../../issues/1108))
 
 ## [Version 5.7.3](https://pypi.python.org/pypi/pyfakefs/5.7.3) (2024-12-15)
 Fixes a regression in version 5.7.3.
diff --git a/pyfakefs/fake_filesystem.py b/pyfakefs/fake_filesystem.py
index fde46a6..efaa178 100644
--- a/pyfakefs/fake_filesystem.py
+++ b/pyfakefs/fake_filesystem.py
@@ -346,7 +346,10 @@ class FakeFilesystem:
         """Set the current working directory of the fake filesystem.
         Make sure a new drive or share is auto-mounted under Windows.
         """
-        self._cwd = value
+        _cwd = make_string_path(value)
+        self._cwd = _cwd.replace(
+            matching_string(_cwd, os.sep), matching_string(_cwd, self.path_separator)
+        )
         self._auto_mount_drive_if_needed(value)
 
     @property


Test patch:
diff --git a/pyfakefs/tests/fake_filesystem_test.py b/pyfakefs/tests/fake_filesystem_test.py
index 12c05b8..96f7046 100644
--- a/pyfakefs/tests/fake_filesystem_test.py
+++ b/pyfakefs/tests/fake_filesystem_test.py
@@ -17,6 +17,7 @@
 import contextlib
 import errno
 import os
+import pathlib
 import shutil
 import stat
 import sys
@@ -231,6 +232,10 @@ class NormalizePathTest(TestCase):
         self.filesystem.cwd = "/foo"
         self.assertEqual("/foo/bar", self.filesystem.absnormpath(path))
 
+    def test_cwd_from_pathlib_path(self):
+        self.filesystem.cwd = pathlib.Path("/foo/bar")
+        self.assertEqual("/foo/bar", self.filesystem.cwd)
+
     def test_absolute_path_remains_unchanged(self):
         path = "foo/bar"
         self.assertEqual(self.root_name + path, self.filesystem.absnormpath(path))