diff --git a/CHANGES.md b/CHANGES.md index c23c62f6..e2180b5c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,6 +7,7 @@ This version of pyfakefs does not support Python 2.6. Python 2.6 users must use pyfakefs 3.3 or earlier. #### New Features + * Added support for file descriptor path parameter in `os.scandir` (Python >= 3.7, Posix only) (see [#346](../../issues/346)) * Added support to fake out backported `scandir` module ([#332](../../issues/332)) * Dynamic loading of modules after setup is now on by default and no more considered experimental (see [#340](../../issues/340)) * `IOError`/`OSError` exception messages in the fake file system now always start with the message issued in the real file system in Unix systems (see [#202](../../issues/202)) diff --git a/pyfakefs/fake_scandir.py b/pyfakefs/fake_scandir.py index 292442b8..3cbfea7f 100644 --- a/pyfakefs/fake_scandir.py +++ b/pyfakefs/fake_scandir.py @@ -16,6 +16,8 @@ """ import sys +import os + class DirEntry(object): """Emulates os.DirEntry. Note that we did not enforce keyword only arguments.""" @@ -101,6 +103,12 @@ class ScanDirIter: def __init__(self, filesystem, path): self.filesystem = filesystem + if isinstance(path, int): + if sys.version_info < (3, 7) or self.filesystem.is_windows_fs: + raise NotImplementedError('scandir does not support file descriptor' + 'path argument') + path = self.filesystem.get_open_file(path).get_object().path + self.path = self.filesystem.ResolvePath(path) contents = {} try: diff --git a/tests/fake_os_test.py b/tests/fake_os_test.py index 1da4f5e2..9627c4c3 100644 --- a/tests/fake_os_test.py +++ b/tests/fake_os_test.py @@ -4011,7 +4011,6 @@ def test_file_size_truncation(self): class FakeScandirTest(FakeOsModuleTestBase): def setUp(self): super(FakeScandirTest, self).setUp() - self.supports_symlinks = (not self.is_windows or not self.use_real_fs() and not self.is_python2) @@ -4023,24 +4022,25 @@ def setUp(self): scandir = lambda p: pyfakefs.fake_scandir.scandir(self.filesystem, p) else: scandir = self.os.scandir + self.scandir = scandir - directory = self.make_path('xyzzy', 'plugh') + self.directory = self.make_path('xyzzy', 'plugh') link_dir = self.make_path('linked', 'plugh') self.linked_file_path = self.os.path.join(link_dir, 'file') self.linked_dir_path = self.os.path.join(link_dir, 'dir') self.create_dir(self.linked_dir_path) self.create_file(self.linked_file_path, contents=b'a' * 10) - self.dir_path = self.os.path.join(directory, 'dir') + self.dir_path = self.os.path.join(self.directory, 'dir') self.create_dir(self.dir_path) - self.file_path = self.os.path.join(directory, 'file') + self.file_path = self.os.path.join(self.directory, 'file') self.create_file(self.file_path, contents=b'b' * 50) - self.file_link_path = self.os.path.join(directory, 'link_file') + self.file_link_path = self.os.path.join(self.directory, 'link_file') if self.supports_symlinks: self.create_symlink(self.file_link_path, self.linked_file_path) - self.dir_link_path = self.os.path.join(directory, 'link_dir') + self.dir_link_path = self.os.path.join(self.directory, 'link_dir') self.create_symlink(self.dir_link_path, self.linked_dir_path) - self.dir_entries = [entry for entry in scandir(directory)] + self.dir_entries = [entry for entry in self.scandir(self.directory)] self.dir_entries = sorted(self.dir_entries, key=lambda entry: entry.name) @@ -4052,6 +4052,10 @@ def test_paths(self): self.assertEqual(len(sorted_names), len(self.dir_entries)) self.assertEqual(sorted_names, [entry.name for entry in self.dir_entries]) + + self.skip_real_fs() + # this one does not work in the real fs - will wait for + # final 3.7 release to check into this self.assertEqual(self.dir_path, self.dir_entries[0].path) def test_isfile(self): @@ -4131,4 +4135,24 @@ def test_stat_ino_dev(self): class RealScandirTest(FakeScandirTest): def use_real_fs(self): - return True \ No newline at end of file + return True + + +@unittest.skipIf(sys.version_info < (3, 7) or TestCase.is_windows, + 'dir_fd support for os.scandir was introduced in Python 3.7') +class FakeScandirFdTest(FakeScandirTest): + def setUp(self): + super(FakeScandirFdTest, self).setUp() + self.dir_fd = self.os.open(self.directory, os.O_RDONLY) + self.dir_entries = [entry for entry in self.scandir(self.dir_fd)] + self.dir_entries = sorted(self.dir_entries, + key=lambda entry: entry.name) + + def tearDown(self): + self.os.close(self.dir_fd) + super(FakeScandirFdTest, self).tearDown() + + +class RealScandirFdTest(FakeScandirFdTest): + def use_real_fs(self): + return True