[Python-checkins] bpo-43680: Deprecate io.OpenWrapper (GH-25357)

vstinner webhook-mailer at python.org
Tue Apr 13 21:24:37 EDT 2021


https://github.com/python/cpython/commit/3bc694d5f3d4eb2e5d2f0b83e498b19662845d4e
commit: 3bc694d5f3d4eb2e5d2f0b83e498b19662845d4e
branch: master
author: Victor Stinner <vstinner at python.org>
committer: vstinner <vstinner at python.org>
date: 2021-04-14T03:24:33+02:00
summary:

bpo-43680: Deprecate io.OpenWrapper (GH-25357)

Deprecate io.OpenWrapper and _pyio.OpenWrapper: use io.open and
_pyio.open instead. Until Python 3.9, _pyio.open was not a static
method and builtins.open was set to OpenWrapper to not become a bound
method when set to a class variable. _io.open is a built-in function
whereas _pyio.open is a Python function. In Python 3.10, _pyio.open()
is now a static method, and builtins.open() is now io.open().

files:
A Misc/NEWS.d/next/Library/2021-04-12-11-20-34.bpo-43680.SR0Epv.rst
M Lib/_pyio.py
M Lib/io.py
M Lib/test/test_io.py

diff --git a/Lib/_pyio.py b/Lib/_pyio.py
index cb5a619f02a48..56e9a0cb33c5f 100644
--- a/Lib/_pyio.py
+++ b/Lib/_pyio.py
@@ -311,18 +311,20 @@ def _open_code_with_warning(path):
     open_code = _open_code_with_warning
 
 
-class DocDescriptor:
-    """Helper for builtins.open.__doc__
-    """
-    def __get__(self, obj, typ=None):
-        return (
-            "open(file, mode='r', buffering=-1, encoding=None, "
-                 "errors=None, newline=None, closefd=True)\n\n" +
-            open.__doc__)
-
-
-# bpo-43680: Alias to open() kept for backward compatibility
-OpenWrapper = open
+def __getattr__(name):
+    if name == "OpenWrapper":
+        # bpo-43680: Until Python 3.9, _pyio.open was not a static method and
+        # builtins.open was set to OpenWrapper to not become a bound method
+        # when set to a class variable. _io.open is a built-in function whereas
+        # _pyio.open is a Python function. In Python 3.10, _pyio.open() is now
+        # a static method, and builtins.open() is now io.open().
+        import warnings
+        warnings.warn('OpenWrapper is deprecated, use open instead',
+                      DeprecationWarning, stacklevel=2)
+        global OpenWrapper
+        OpenWrapper = open
+        return OpenWrapper
+    raise AttributeError(name)
 
 
 # In normal operation, both `UnsupportedOperation`s should be bound to the
diff --git a/Lib/io.py b/Lib/io.py
index 01f1df80ded29..2a6140c3dd509 100644
--- a/Lib/io.py
+++ b/Lib/io.py
@@ -56,7 +56,22 @@
                  BufferedWriter, BufferedRWPair, BufferedRandom,
                  IncrementalNewlineDecoder, text_encoding, TextIOWrapper)
 
-OpenWrapper = _io.open # for compatibility with _pyio
+
+def __getattr__(name):
+    if name == "OpenWrapper":
+        # bpo-43680: Until Python 3.9, _pyio.open was not a static method and
+        # builtins.open was set to OpenWrapper to not become a bound method
+        # when set to a class variable. _io.open is a built-in function whereas
+        # _pyio.open is a Python function. In Python 3.10, _pyio.open() is now
+        # a static method, and builtins.open() is now io.open().
+        import warnings
+        warnings.warn('OpenWrapper is deprecated, use open instead',
+                      DeprecationWarning, stacklevel=2)
+        global OpenWrapper
+        OpenWrapper = open
+        return OpenWrapper
+    raise AttributeError(name)
+
 
 # Pretend this exception was created here.
 UnsupportedOperation.__module__ = "io"
diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py
index 48a3cca3fbde4..32c29ea5dc4a7 100644
--- a/Lib/test/test_io.py
+++ b/Lib/test/test_io.py
@@ -4283,6 +4283,14 @@ def test_check_encoding_warning(self):
         self.assertTrue(
             warnings[1].startswith(b"<string>:8: EncodingWarning: "))
 
+    @support.cpython_only
+    # Depending if OpenWrapper was already created or not, the warning is
+    # emitted or not. For example, the attribute is already created when this
+    # test is run multiple times.
+    @warnings_helper.ignore_warnings(category=DeprecationWarning)
+    def test_openwrapper(self):
+        self.assertIs(self.io.OpenWrapper, self.io.open)
+
 
 class CMiscIOTest(MiscIOTest):
     io = io
@@ -4598,8 +4606,6 @@ def load_tests(*args):
     globs = globals()
     c_io_ns.update((x.__name__, globs["C" + x.__name__]) for x in mocks)
     py_io_ns.update((x.__name__, globs["Py" + x.__name__]) for x in mocks)
-    # Avoid turning open into a bound method.
-    py_io_ns["open"] = pyio.OpenWrapper
     for test in tests:
         if test.__name__.startswith("C"):
             for name, obj in c_io_ns.items():
diff --git a/Misc/NEWS.d/next/Library/2021-04-12-11-20-34.bpo-43680.SR0Epv.rst b/Misc/NEWS.d/next/Library/2021-04-12-11-20-34.bpo-43680.SR0Epv.rst
new file mode 100644
index 0000000000000..e4ddb96491b57
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2021-04-12-11-20-34.bpo-43680.SR0Epv.rst
@@ -0,0 +1,6 @@
+Deprecate io.OpenWrapper and _pyio.OpenWrapper: use io.open and _pyio.open
+instead. Until Python 3.9, _pyio.open was not a static method and
+builtins.open was set to OpenWrapper to not become a bound method when set
+to a class variable. _io.open is a built-in function whereas _pyio.open is a
+Python function. In Python 3.10, _pyio.open() is now a static method, and
+builtins.open() is now io.open().



More information about the Python-checkins mailing list