Repository: pytest-dev/pyfakefs (python)

Issue:
AttributeError: 'FakeFcntlModule' object has no attribute 'LOCK_SH'  (Also 'LOCK_UN' and 'LOCK_EX')
**Describe the bug**
After updating from pyfakefs 5.6.0 to 5.7.0  the following error occurs on `flock` with `fcntl.LOCK_SH`,  `fcntl.LOCK_UN` and `LOCK_EX`.  

The error does not occur on version 5.6.0

```
self = <pyfakefs.fake_io.FakeFcntlModule object at 0x7f237a9ba760>, name = 'LOCK_SH'

    def __getattribute__(self, name):
        """Forwards any unfaked calls to the standard fcntl module."""
        fs: FakeFilesystem = object.__getattribute__(self, "filesystem")
        fnctl_module = object.__getattribute__(self, "_fcntl_module")
        if fs.patcher:
            if is_called_from_skipped_module(
                skip_names=fs.patcher.skip_names,
                case_sensitive=fs.is_case_sensitive,
            ):
                # remove the `self` argument for FakeOsModule methods
                return getattr(fnctl_module, name)
    
>       return object.__getattribute__(self, name)
E       AttributeError: 'FakeFcntlModule' object has no attribute 'LOCK_SH'
```

**How To Reproduce**
```
import fcntl
from os import path


def lock():
    with open('lock_file', 'a+') as lock_file:
        fcntl.flock(lock_file, fcntl.LOCK_SH)
        fcntl.flock(lock_file, fcntl.LOCK_UN)

def test_lock(fs):
    lock()

```

**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())"
python -c "import sys; print('Python', sys.version)"
python -c "from pyfakefs import __version__; print('pyfakefs', __version__)"
python -c "import pytest; print('pytest', pytest.__version__)"
```
```
python -c "import platform; print(platform.platform())"
python -c "import sys; print('Python', sys.version)"
python -c "from pyfakefs import __version__; print('pyfakefs', __version__)"
python -c "import pytest; print('pytest', pytest.__version__)"
Linux-6.10.11-200.fc40.x86_64-x86_64-with-glibc2.39
Python 3.9.16 (main, Sep  6 2024, 09:21:47) 
[GCC 14.2.1 20240801 (Red Hat 14.2.1-1)]
pyfakefs 5.7.0
pytest 8.3.3

```

Pull request:
Forward unfaked fcntl calls to real module
- had been removed accidentally in 5.7.0
- fixes #1074

<!--
Please prefix your PR title with [WIP] for PRs that are still in progress.
-->

While adding support for skipped modules, the forwarding of unfaked attributes to the real fcntl module had been removed accidentally.

#### 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 fee281e..2a94027 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -12,6 +12,11 @@ The released versions correspond to PyPI releases.
 * the default for `FakeFilesystem.shuffle_listdir_results` will change to `True` to reflect
   the real filesystem behavior
 
+## Unreleased
+
+### Fixes
+* fixes a regression that caused unfaked `fcntl` calls to fail (see [#1074](../../issues/1074))
+
 ## [Version 5.7.0](https://pypi.python.org/pypi/pyfakefs/5.7.0) (2024-08-10)
 Adds official Python 3.13 support, improves OS emulation behavior.
 
diff --git a/pyfakefs/fake_io.py b/pyfakefs/fake_io.py
index 0c756a5..6d5b67c 100644
--- a/pyfakefs/fake_io.py
+++ b/pyfakefs/fake_io.py
@@ -181,7 +181,7 @@ if sys.platform != "win32":
             pass
 
         def __getattribute__(self, name):
-            """Forwards any unfaked calls to the standard fcntl module."""
+            """Prevents patching of skipped modules."""
             fs: FakeFilesystem = object.__getattribute__(self, "filesystem")
             fnctl_module = object.__getattribute__(self, "_fcntl_module")
             if fs.patcher:
@@ -193,3 +193,7 @@ if sys.platform != "win32":
                     return getattr(fnctl_module, name)
 
             return object.__getattribute__(self, name)
+
+        def __getattr__(self, name):
+            """Forwards any unfaked calls to the standard fcntl module."""
+            return getattr(self._fcntl_module, name)


Test patch:
diff --git a/pyfakefs/pytest_tests/fake_fcntl_test.py b/pyfakefs/pytest_tests/fake_fcntl_test.py
new file mode 100644
index 0000000..b566b55
--- /dev/null
+++ b/pyfakefs/pytest_tests/fake_fcntl_test.py
@@ -0,0 +1,22 @@
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import sys
+
+if sys.platform == "linux":
+    import fcntl
+
+    def test_unpatched_attributes_are_forwarded_to_real_fs(fs):
+        # regression test for #1074
+        with open("lock_file", "a+") as lock_file:
+            fcntl.flock(lock_file, fcntl.LOCK_SH)
+            fcntl.flock(lock_file, fcntl.LOCK_UN)