Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Calling os.chdir twice #530

Closed
Cheekie25 opened this issue May 12, 2020 · 7 comments
Closed

Calling os.chdir twice #530

Cheekie25 opened this issue May 12, 2020 · 7 comments
Labels

Comments

@Cheekie25
Copy link

Cheekie25 commented May 12, 2020

Describe the bug
When calling twice os.chdir, I get NotADirectoryError on the second call.

Stack trace :

================================== FAILURES ===================================
______________________________ test_twice_chdir _______________________________

self = <pyfakefs.fake_filesystem.FakeFilesystem object at 0x0000000003D39438>
file_path = '\\absolute\\path\\to\\directory\\absolute\\path\\to\\directory'
check_read_perm = True

    def get_object_from_normpath(self, file_path, check_read_perm=True):
        """Search for the specified filesystem object within the fake
            filesystem.
    
            Args:
                file_path: Specifies target FakeFile object to retrieve, with a
                    path that has already been normalized/resolved.
                check_read_perm: If True, raises OSError if a parent directory
                    does not have read permission
    
            Returns:
                The FakeFile object corresponding to file_path.
    
            Raises:
                OSError: if the object is not found.
            """
        file_path = make_string_path(file_path)
        if file_path == self.root.name:
            return self.root
        if file_path == self.dev_null.name:
            return self.dev_null
    
        file_path = self._original_path(file_path)
        path_components = self._path_components(file_path)
        target_object = self.root
        try:
            for component in path_components:
                if S_ISLNK(target_object.st_mode):
                    target_object = self.resolve(target_object.contents)
                if not S_ISDIR(target_object.st_mode):
                    if not self.is_windows_fs:
                        self.raise_os_error(errno.ENOTDIR, file_path)
                    self.raise_os_error(errno.ENOENT, file_path)
>               target_object = target_object.get_entry(component)

D:\Users\me\WinPython-64bit-3.6.3.0Qt5\python-3.6.3.amd64\lib\site-packages\pyfakefs\fake_filesystem.py:1941: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <pyfakefs.fake_filesystem.FakeDirectory object at 0x0000000004D46898>
pathname_name = 'absolute'

    def get_entry(self, pathname_name):
        """Retrieves the specified child file or directory entry.
    
            Args:
                pathname_name: The basename of the child object to retrieve.
    
            Returns:
                The fake file or directory object.
    
            Raises:
                KeyError: if no child exists by the specified name.
            """
        pathname_name = self._normalized_entryname(pathname_name)
>       return self.contents[to_string(pathname_name)]
E       KeyError: 'absolute'

D:\Users\me\WinPython-64bit-3.6.3.0Qt5\python-3.6.3.amd64\lib\site-packages\pyfakefs\fake_filesystem.py:672: KeyError

During handling of the above exception, another exception occurred:

fs = <pyfakefs.fake_filesystem.FakeFilesystem object at 0x0000000003D39438>

    def test_twice_chdir(fs):
        fs.create_dir("/absolute/path/to/directory")
        os.chdir("/absolute/path/to/directory")
>       os.chdir("/absolute/path/to/directory")

D:\Users\me\test_non_reg.py:29: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
D:\Users\me\WinPython-64bit-3.6.3.0Qt5\python-3.6.3.amd64\lib\site-packages\pyfakefs\fake_filesystem.py:3719: in chdir
    self.filesystem.confirmdir(path)
D:\Users\me\WinPython-64bit-3.6.3.0Qt5\python-3.6.3.amd64\lib\site-packages\pyfakefs\fake_filesystem.py:2914: in confirmdir
    directory = self.resolve(target_directory)
D:\Users\me\WinPython-64bit-3.6.3.0Qt5\python-3.6.3.amd64\lib\site-packages\pyfakefs\fake_filesystem.py:1995: in resolve
    file_path, check_read_perm), check_read_perm)
D:\Users\me\WinPython-64bit-3.6.3.0Qt5\python-3.6.3.amd64\lib\site-packages\pyfakefs\fake_filesystem.py:1946: in get_object_from_normpath
    self.raise_os_error(errno.ENOENT, file_path)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <pyfakefs.fake_filesystem.FakeFilesystem object at 0x0000000003D39438>
errno = 2
filename = '\\absolute\\path\\to\\directory\\absolute\\path\\to\\directory'
winerror = None

    def raise_os_error(self, errno, filename=None, winerror=None):
        """Raises OSError.
            The error message is constructed from the given error code and shall
            start with the error string issued in the real system.
            Note: this is not true under Windows if winerror is given - in this
            case a localized message specific to winerror will be shown in the
            real file system.
    
            Args:
                errno: A numeric error code from the C variable errno.
                filename: The name of the affected file, if any.
                winerror: Windows only - the specific Windows error code.
            """
        message = self._error_message(errno)
        if (winerror is not None and sys.platform == 'win32' and
                self.is_windows_fs):
            raise OSError(errno, message, filename, winerror)
>       raise OSError(errno, message, filename)
E       FileNotFoundError: [Errno 2] No such file or directory in the fake filesystem: '\\absolute\\path\\to\\directory\\absolute\\path\\to\\directory'

D:\Users\me\WinPython-64bit-3.6.3.0Qt5\python-3.6.3.amd64\lib\site-packages\pyfakefs\fake_filesystem.py:960: FileNotFoundError
========================== 1 failed in 2.13 seconds ===========================
Process finished with exit code 0

How To Reproduce

import os

def test_twice_chdir(fs):
    fs.create_dir("/absolute/path/to/directory")
    os.chdir("/absolute/path/to/directory")
    os.chdir("/absolute/path/to/directory")

Your enviroment

Windows-7-6.1.7601-SP1
Python 3.6.3 (v3.6.3:2c5fed8, Oct  3 2017, 18:11:49) [MSC v.1900 64 bit (AMD64)]
pyfakefs 4.0.2
@mrbean-bremen
Copy link
Member

You create a file, not a directory (that would be create_dir) - so trying to chdir to a file will get you this error. Though I don't understand why you have to call it twice - it should raise on the first call.

@Cheekie25
Copy link
Author

Sorry, I copied the wrong error, I was trying things. I edited the post.
I'm working on a program that, at a moment, save the current directory, chdir to a directory, and then chdir back to the initial directory.
And sometimes the initial directory is the same as the one it chdir to. So it kind of chdir twice.

@mrbean-bremen
Copy link
Member

Hm, I cannot reproduce this - probably will have another look tomorrow.

@mrbean-bremen
Copy link
Member

That's weird - the test passes with unittest but fails with pytest!
Looks like a Windows-specific problem related to path separators. This test:

def test_twice_chdir(fs):
    fs.create_dir("\\absolute\\path\\to\\directory")
    os.chdir("\\absolute\\path\\to\\directory")
    os.chdir("\\absolute\\path\\to\\directory")

passes under Windows. So this probably comes up seldom enough...
Good catch!

@Cheekie25
Copy link
Author

Thanks !

@Cheekie25
Copy link
Author

I'm not sure where to write that so I write this here for the moment.
I started using pyfakefs and I think it's great. It makes the code of my unit tests much more readable. So congratulation for your great work to all.
I converted several tests that were written by mocking system calls, to now use pf fixture instead.

It seems to me that the time required to run the tests with pytest is quite the same if they use pyfakefs or not.
But when I run them with Coverage, it takes way more time when they use pyfakefs.

Have you heard about that and would you have a solution ? Or should I take time to open a ticket with more data to support my says ?

@mrbean-bremen
Copy link
Member

I haven't heard about this, and hadn't paid attention until now. I use test with coverage only occasionally, and take it as a given that they take longer.
I just ran the tests of pyfakefs itself (which isn't the same as running tests using pyfakefs), and the time with coverage was about double the time for a run without pyfakefs. I checked with the tests of another library of comparable size, and the tests with coverage there where about 50% longer than without coverage, though that library does more CPU-heavy stuff, so naturally the impact was less. I don't know what the normal numbers are.

You can create an issue, of course, preferably with some example test (that can be cloned and used for testing) with and without using pyfakefs, and some timings. I doubt that we can do much (I currently have no idea how to appoach this), but we can have a look - maybe something shows up that can be improved.

And thanks for the compliments :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants