Repository: jmcgeheeiv/pyfakefs (python)

Issue:
os.scandir does not work with filedescriptor
**Describe the bug**
I'm using a code that is reading directory content in such a way:
```
os.chdir('/test-volume')
fd = os.open(''/test-volume'', flags=os.O_DIRECTORY)
content = list(os.scandir(fd))
```
This code should return content of `/test-volume` directory. I get: 
`[Errno 20] Not a directory in the fake filesystem: '/test-volume/df01d42e-1f05-41fa-9ef3-8463b03ff8f0'`

It would be nice to add support for file descriptors or at least throw NotImplementedError when path is int. 

**How To Reproduce**
Shown above.

**Your environment**
```
Linux-5.15.0-48-generic-x86_64-with-glibc2.35
Python 3.10.4 (main, Apr 28 2022, 10:58:00) [GCC 11.2.0]
pyfakefs 4.7.0
```


Pull request:
Fix handling of O_TMPFILE flag in os.open
- the check for the flag was incorrect
- change PyPy version in CI to 3.7
- fixes #723

Patch:
diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml
index eba1499..3885cd6 100644
--- a/.github/workflows/pythonpackage.yml
+++ b/.github/workflows/pythonpackage.yml
@@ -50,7 +50,7 @@ jobs:
         os: [ubuntu-latest, macOS-latest, windows-latest]
         python-version: [3.7, 3.8, 3.9, "3.10", "3.11-dev"]
         include:
-          - python-version: pypy3
+          - python-version: "pypy-3.7"
             os: ubuntu-latest
 
     steps:
diff --git a/CHANGES.md b/CHANGES.md
index cecb816..78da2e0 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -3,6 +3,10 @@ The released versions correspond to PyPi releases.
 
 ## Unreleased
 
+### Fixes 
+* fixed handling of `O_TMPFILE` in `os.open` (caused handling of 
+  `O_DIRECTORY` as `O_TMPFILE`) (see [#723](../../issues/723))
+
 ## [Version 4.7.0](https://pypi.python.org/pypi/pyfakefs/4.7.0) (2022-09-18)
 Changed handling of nested fixtures and bug fixes.
 
diff --git a/pyfakefs/fake_filesystem.py b/pyfakefs/fake_filesystem.py
index dcec12b..4b258e4 100644
--- a/pyfakefs/fake_filesystem.py
+++ b/pyfakefs/fake_filesystem.py
@@ -3998,7 +3998,7 @@ class FakeOsModule:
                 mode = 0o777 & ~self._umask()
 
         has_tmpfile_flag = (hasattr(os, 'O_TMPFILE') and
-                            flags & getattr(os, 'O_TMPFILE'))
+                            flags & os.O_TMPFILE == os.O_TMPFILE)
         open_modes = _OpenModes(
             must_exist=not flags & os.O_CREAT and not has_tmpfile_flag,
             can_read=not flags & os.O_WRONLY,
diff --git a/pyfakefs/fake_scandir.py b/pyfakefs/fake_scandir.py
index 8941973..2bb98cf 100644
--- a/pyfakefs/fake_scandir.py
+++ b/pyfakefs/fake_scandir.py
@@ -20,7 +20,7 @@ import os
 import sys
 
 from pyfakefs.extra_packages import use_scandir_package
-from pyfakefs.helpers import to_string
+from pyfakefs.helpers import to_string, make_string_path
 
 if sys.version_info >= (3, 6):
     BaseClass = os.PathLike
@@ -123,7 +123,8 @@ class ScanDirIter:
         if isinstance(path, int):
             if not use_scandir_package and (
                     sys.version_info < (3, 7) or
-                    self.filesystem.is_windows_fs):
+                    self.filesystem.is_windows_fs
+            ):
                 raise NotImplementedError(
                     'scandir does not support file descriptor '
                     'path argument')
@@ -131,6 +132,7 @@ class ScanDirIter:
                 self.filesystem.get_open_file(path).get_object().path)
             self.path = ''
         else:
+            path = make_string_path(path)
             self.abspath = self.filesystem.absnormpath(path)
             self.path = to_string(path)
         entries = self.filesystem.confirmdir(self.abspath).entries


Test patch:
diff --git a/pyfakefs/tests/fake_os_test.py b/pyfakefs/tests/fake_os_test.py
index 18269c7..6a34038 100644
--- a/pyfakefs/tests/fake_os_test.py
+++ b/pyfakefs/tests/fake_os_test.py
@@ -5045,6 +5045,22 @@ class FakeScandirTest(FakeOsModuleTestBase):
                     self.assertEqual(1, scandir_stat_nlink)
                 self.assertEqual(1, self.os.stat(self.file_path).st_nlink)
 
+    @unittest.skipIf(not hasattr(os, 'O_DIRECTORY'),
+                     "opening directory not supported")
+    @unittest.skipIf(sys.version_info < (3, 7),
+                     "fd not supported for scandir")
+    def test_scandir_with_fd(self):
+        # regression test for #723
+        temp_dir = self.make_path('tmp', 'dir')
+        self.create_dir(temp_dir)
+        self.create_file(self.os.path.join(temp_dir, 'file1'))
+        self.create_file(self.os.path.join(temp_dir, 'file2'))
+        self.create_dir(self.os.path.join(temp_dir, 'subdir'))
+        self.os.chdir(temp_dir)
+        fd = self.os.open(temp_dir, flags=os.O_RDONLY | os.O_DIRECTORY)
+        children = [dir_entry.name for dir_entry in self.os.scandir(fd)]
+        assert sorted(children) == ['file1', 'file2', 'subdir']
+
     def check_stat(self, absolute_symlink_expected_size,
                    relative_symlink_expected_size):
         self.assertEqual(self.FILE_SIZE, self.dir_entries[1].stat().st_size)
@@ -5136,9 +5152,10 @@ class RealScandirRelTest(FakeScandirRelTest):
         return True
 
 
-@unittest.skipIf(sys.version_info < (3, 7) or TestCase.is_windows or
-                 use_scandir_package,
-                 'dir_fd support for os.scandir was introduced in Python 3.7')
+@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/patched_packages_test.py b/pyfakefs/tests/patched_packages_test.py
index f8d8a1a..8c3f106 100644
--- a/pyfakefs/tests/patched_packages_test.py
+++ b/pyfakefs/tests/patched_packages_test.py
@@ -15,8 +15,10 @@ Provides patches for some commonly used modules that enable them to work
 with pyfakefs.
 """
 import os
+import unittest
 
 from pyfakefs import fake_filesystem_unittest
+from pyfakefs.helpers import IS_PYPY
 
 try:
     import pandas as pd
@@ -45,6 +47,7 @@ class TestPatchedPackages(fake_filesystem_unittest.TestCase):
             df = pd.read_csv(path)
             assert (df.columns == ['1', '2', '3', '4']).all()
 
+        @unittest.skipIf(IS_PYPY, "Has a problem with current PyPy")
         def test_read_table(self):
             path = '/foo/bar.csv'
             self.fs.create_file(path, contents='1|2|3|4')
@@ -52,6 +55,7 @@ class TestPatchedPackages(fake_filesystem_unittest.TestCase):
             assert (df.columns == ['1', '2', '3', '4']).all()
 
     if pd is not None and xlrd is not None:
+        @unittest.skipIf(IS_PYPY, "Has a problem with current PyPy")
         def test_read_excel(self):
             path = '/foo/bar.xlsx'
             src_path = os.path.dirname(os.path.abspath(__file__))
@@ -63,6 +67,7 @@ class TestPatchedPackages(fake_filesystem_unittest.TestCase):
             assert (df.columns == [1, 2, 3, 4]).all()
 
     if pd is not None and openpyxl is not None:
+        @unittest.skipIf(IS_PYPY, "Has a problem with current PyPy")
         def test_write_excel(self):
             self.fs.create_dir('/foo')
             path = '/foo/bar.xlsx'