Repository: pytest-dev/pyfakefs (python)

Issue:
EncodingWarnings raised (3) when using pyfakefs
When running the distutils test suite, I see the following warnings:

```
distutils/tests/test_dist.py::TestDistributionBehavior::test_find_config_files_permission_error
  /Users/jaraco/code/pypa/distutils/.tox/py/lib/python3.12/site-packages/pyfakefs/fake_file.py:266: EncodingWarning: UTF-8 Mode affects locale.getpreferredencoding(). Consider locale.getencoding() instead.
    self.encoding or locale.getpreferredencoding(False),

distutils/tests/test_dist.py::TestDistributionBehavior::test_find_config_files_permission_error
  /Users/jaraco/code/pypa/distutils/.tox/py/lib/python3.12/site-packages/pyfakefs/fake_file.py:748: EncodingWarning: UTF-8 Mode affects locale.getpreferredencoding(). Consider locale.getencoding() instead.
    self._encoding = encoding or locale.getpreferredencoding(False)

distutils/tests/test_dist.py::TestDistributionBehavior::test_find_config_files_permission_error
  /Users/jaraco/code/pypa/distutils/.tox/py/lib/python3.12/site-packages/pyfakefs/helpers.py:409: EncodingWarning: 'encoding' argument not specified
    super().__init__(self._bytestream, encoding, errors, newline)
```


Pull request description:
Use locale.getencoding instead of getpreferredencoding
- from Python 3.11 on, this is the preferred version
- fixes #957

#### Tasks
- [ ] Unit tests added that reproduce the issue or prove feature is working - no unit tests, enabled the warning instead
- [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 7925a1d..144d20b 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -7,6 +7,7 @@ The released versions correspond to PyPI releases.
 * Fixed a specific problem on reloading a pandas-related module (see [#947](../../issues/947)),
   added possibility for unload hooks for specific modules
 * Use this also to reload django views (see [#932](../../issues/932))
+* Fixed `EncodingWarning` for Python >= 3.11 (see [#957](../../issues/957))
 
 ## [Version 5.3.5](https://pypi.python.org/pypi/pyfakefs/5.3.5) (2024-01-30)
 Fixes a regression.
diff --git a/pyfakefs/fake_file.py b/pyfakefs/fake_file.py
index 44d45be..bc37a96 100644
--- a/pyfakefs/fake_file.py
+++ b/pyfakefs/fake_file.py
@@ -16,7 +16,6 @@
 """
 import errno
 import io
-import locale
 import os
 import sys
 from stat import (
@@ -52,6 +51,7 @@ from pyfakefs.helpers import (
     real_encoding,
     AnyPath,
     AnyString,
+    get_locale_encoding,
 )
 
 if TYPE_CHECKING:
@@ -190,7 +190,7 @@ class FakeFile:
         """Return the contents as string with the original encoding."""
         if isinstance(self.byte_contents, bytes):
             return self.byte_contents.decode(
-                self.encoding or locale.getpreferredencoding(False),
+                self.encoding or get_locale_encoding(),
                 errors=self.errors,
             )
         return None
@@ -263,7 +263,7 @@ class FakeFile:
         if is_unicode_string(contents):
             contents = bytes(
                 cast(str, contents),
-                self.encoding or locale.getpreferredencoding(False),
+                self.encoding or get_locale_encoding(),
                 self.errors,
             )
         return cast(bytes, contents)
@@ -745,7 +745,7 @@ class FakeFileWrapper:
         self._use_line_buffer = not binary and buffering == 1
 
         contents = file_object.byte_contents
-        self._encoding = encoding or locale.getpreferredencoding(False)
+        self._encoding = encoding or get_locale_encoding()
         errors = errors or "strict"
         self._io: Union[BinaryBufferIO, TextBufferIO] = (
             BinaryBufferIO(contents)
diff --git a/pyfakefs/helpers.py b/pyfakefs/helpers.py
index 416be19..a5d8d5b 100644
--- a/pyfakefs/helpers.py
+++ b/pyfakefs/helpers.py
@@ -109,6 +109,12 @@ def is_unicode_string(val: Any) -> bool:
     return hasattr(val, "encode")
 
 
+def get_locale_encoding():
+    if sys.version_info >= (3, 11):
+        return locale.getencoding()
+    return locale.getpreferredencoding(False)
+
+
 @overload
 def make_string_path(dir_name: AnyStr) -> AnyStr:
     ...
@@ -127,7 +133,7 @@ def to_string(path: Union[AnyStr, Union[str, bytes]]) -> str:
     """Return the string representation of a byte string using the preferred
     encoding, or the string itself if path is a str."""
     if isinstance(path, bytes):
-        return path.decode(locale.getpreferredencoding(False))
+        return path.decode(get_locale_encoding())
     return path
 
 
@@ -135,7 +141,7 @@ def to_bytes(path: Union[AnyStr, Union[str, bytes]]) -> bytes:
     """Return the bytes representation of a string using the preferred
     encoding, or the byte string itself if path is a byte string."""
     if isinstance(path, str):
-        return bytes(path, locale.getpreferredencoding(False))
+        return bytes(path, get_locale_encoding())
     return path
 
 
@@ -181,7 +187,7 @@ def matching_string(  # type: ignore[misc]
     if string is None:
         return string
     if isinstance(matched, bytes) and isinstance(string, str):
-        return string.encode(locale.getpreferredencoding(False))
+        return string.encode(get_locale_encoding())
     return string  # pytype: disable=bad-return-type
 
 


Test patch:
diff --git a/.github/workflows/testsuite.yml b/.github/workflows/testsuite.yml
index 15d3a74..24031dd 100644
--- a/.github/workflows/testsuite.yml
+++ b/.github/workflows/testsuite.yml
@@ -21,6 +21,8 @@ jobs:
 
   tests:
     runs-on: ${{ matrix.os }}
+    env:
+      PYTHONWARNDEFAULTENCODING: true
     strategy:
       fail-fast: false
       matrix:
diff --git a/pyfakefs/tests/fake_open_test.py b/pyfakefs/tests/fake_open_test.py
index d04bb79..db51e77 100644
--- a/pyfakefs/tests/fake_open_test.py
+++ b/pyfakefs/tests/fake_open_test.py
@@ -17,7 +17,6 @@
 
 import errno
 import io
-import locale
 import os
 import stat
 import sys
@@ -25,7 +24,7 @@ import time
 import unittest
 
 from pyfakefs import fake_filesystem, helpers
-from pyfakefs.helpers import is_root, IS_PYPY
+from pyfakefs.helpers import is_root, IS_PYPY, get_locale_encoding
 from pyfakefs.fake_io import FakeIoModule
 from pyfakefs.fake_filesystem_unittest import PatchMode
 from pyfakefs.tests.test_utils import RealFsTestCase
@@ -119,14 +118,12 @@ class FakeFileOpenTest(FakeFileOpenTestBase):
         try:
             with self.open(file_path, "w") as f:
                 f.write(str_contents)
-        except UnicodeEncodeError:
+            with self.open(file_path, "rb") as f:
+                contents = f.read()
+            self.assertEqual(str_contents, contents.decode(get_locale_encoding()))
+        except UnicodeError:
             # see https://github.com/pytest-dev/pyfakefs/issues/623
             self.skipTest("This test does not work with an ASCII locale")
-        with self.open(file_path, "rb") as f:
-            contents = f.read()
-        self.assertEqual(
-            str_contents, contents.decode(locale.getpreferredencoding(False))
-        )
 
     def test_open_valid_file(self):
         contents = [