Repository: pytest-dev/pyfakefs (python)

Issue:
OSError opening same fd twice
**Describe the bug**
Describe the observed behavior, and what you expect to happen instead.
In case of a crash, please provide the complete stack trace.

:wave: in the recent release there were some changes around opening fake files and checking their permissions in #967 which resulted in #983 it looks like.

I have a situation (repro below) where I open the same fd twice and create an io.BufferedRWPair -- with this latest update this fails with the below exception as the file is already opened once. 

To be honest im not sure this is a bug or if what I have is not very sane/smart -- the relevant bits for me where vendor'd from pexepct/ptyprocess and ive pretty much left it alone for forever :) 

What is/was the reason for only allowing things that aren't pypy to open a file once?

```python
        newline, open_modes = self._handle_file_mode(mode, newline, open_modes)
        opened_as_fd = isinstance(file_, int)
        if opened_as_fd and not helpers.IS_PYPY:
            fd: int = cast(int, file_)
            # cannot open the same file descriptor twice, except in PyPy
            for f in self.filesystem.get_open_files(fd):
                if isinstance(f, FakeFileWrapper) and f.opened_as_fd:
>                   raise OSError(errno.EBADF, "Bad file descriptor")
E                   OSError: [Errno 9] Bad file descriptor

venv/lib/python3.11/site-packages/pyfakefs/fake_open.py:152: OSError
```


**How To Reproduce**
Please provide a unit test or a minimal reproducible example that shows
the problem.

```python
from io import BufferedRWPair
from pyfakefs.fake_filesystem_unittest import Patcher


def test_repro():
    with Patcher(use_cache=False) as patcher:
        fs_ = patcher.fs

        fs_.create_file("dummy")
        dummy_file = open("/dummy")

        fd = dummy_file.fileno()

        readf = open(fd, "rb", buffering=0)
        writef = open(fd, "wb", buffering=0, closefd=False)
        _ = BufferedRWPair(readf, writef)
```

**Your environment**
Please run the following in the environment where the problem happened and
paste the output.

```bash
❯ python -c "import platform; print(platform.platform())"
macOS-14.4.1-arm64-arm-64bit
❯ python -c "import sys; print('Python', sys.version)"
Python 3.11.9 (main, Apr  2 2024, 08:25:04) [Clang 15.0.0 (clang-1500.3.9.4)]
❯ python -c "from pyfakefs import __version__; print('pyfakefs', __version__)"
pyfakefs 5.4.0
❯ python -c "import pytest; print('pytest', pytest.__version__)"
pytest 7.4.4
```


Thanks a bunch for this project, been using it in testing for a long time and its always been so nice and easy to work with!

carl 

Pull request:
Remove unneeded check leading to a regression
- the check had been incorrecty introduced to fix an issue ultimately related to reference counting/garbage collection
- the fix was incorrect, and that case can probably be ignored, as the code laeding to it makes no sense
- fixes #997

#### Tasks
- [x] Unit tests added that reproduce the issue or prove feature is working
- [x] Fix or feature added
- [x] Entry to release notes added
- [x] Pre-commit CI shows no errors
- [x] Unit tests passing


Patch:
diff --git a/CHANGES.md b/CHANGES.md
index 17df1d7..849080b 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,12 @@
 # pyfakefs Release Notes
 The released versions correspond to PyPI releases.
 
+## Unreleased
+
+### Fixes
+* fixed a regression from version 5.4.0 that incorrectly handled files opened twice via file descriptor
+  (see [#997](../../issues/997))
+
 ## [Version 5.4.0](https://pypi.python.org/pypi/pyfakefs/5.4.0) (2024-04-07)
 Improves permission handling.
 
diff --git a/pyfakefs/fake_open.py b/pyfakefs/fake_open.py
index a213b1b..15b4747 100644
--- a/pyfakefs/fake_open.py
+++ b/pyfakefs/fake_open.py
@@ -144,12 +144,6 @@ class FakeFileOpen:
 
         newline, open_modes = self._handle_file_mode(mode, newline, open_modes)
         opened_as_fd = isinstance(file_, int)
-        if opened_as_fd and not helpers.IS_PYPY:
-            fd: int = cast(int, file_)
-            # cannot open the same file descriptor twice, except in PyPy
-            for f in self.filesystem.get_open_files(fd):
-                if isinstance(f, FakeFileWrapper) and f.opened_as_fd:
-                    raise OSError(errno.EBADF, "Bad file descriptor")
 
         # the pathlib opener is defined in a Path instance that may not be
         # patched under some circumstances; as it just calls standard open(),


Test patch:
diff --git a/pyfakefs/tests/fake_os_test.py b/pyfakefs/tests/fake_os_test.py
index 0923068..cf9af4d 100644
--- a/pyfakefs/tests/fake_os_test.py
+++ b/pyfakefs/tests/fake_os_test.py
@@ -231,18 +231,18 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
         self.assert_raises_os_error(errno.EBADF, self.os.fdopen, fileno2, **kwargs)
 
     def test_fdopen_twice(self):
+        # regression test for #997
         file_path = self.make_path("some_file1")
         self.create_file(file_path, contents="contents here1")
         fake_file = self.open(file_path, "r", encoding="utf8")
         fd = fake_file.fileno()
-        self.open(fd, encoding="utf8")
-        if not IS_PYPY:
-            with self.assertRaises(OSError) as cm:
-                self.open(fd, encoding="utf8")
-            self.assertEqual(errno.EBADF, cm.exception.errno)
-        else:
-            self.open(fd, encoding="utf8")
-            self.os.close(fd)
+        # note: we need to assign the files to objects,
+        # otherwise the file will be closed immediately in the CPython implementation
+        # note that this case is not (and will probably not be) handled in pyfakefs
+        file1 = self.open(fd, encoding="utf8")  # noqa: F841
+        file2 = self.open(fd, encoding="utf8")  # noqa: F841
+
+        self.os.close(fd)
 
     def test_open_fd_write_mode_for_ro_file(self):
         # Create a writable file handle to a read-only file, see #967
@@ -3159,22 +3159,6 @@ class FakeOsModuleTestCaseInsensitiveFS(FakeOsModuleTestBase):
         with self.open(file_path, encoding="utf8") as f:
             assert f.read() == "hey"
 
-    def test_fdopen_twice(self):
-        file_path1 = self.make_path("some_file1")
-        file_path2 = self.make_path("SOME_file1")
-        self.create_file(file_path1, contents="contents here1")
-
-        fake_file1 = self.open(file_path2, "r", encoding="utf8")
-        fileno1 = fake_file1.fileno()
-        self.os.fdopen(fileno1, encoding="utf8")
-        if not IS_PYPY:
-            with self.assertRaises(OSError) as cm:
-                self.open(fileno1, encoding="utf8")
-            self.assertEqual(errno.EBADF, cm.exception.errno)
-        else:
-            self.open(fileno1, encoding="utf8")
-            self.os.close(fileno1)
-
     def test_stat(self):
         directory = self.make_path("xyzzy")
         directory1 = self.make_path("XYZZY")