summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorInada Naoki <songofacandy@gmail.com>2025-04-30 01:11:09 (GMT)
committerGitHub <noreply@github.com>2025-04-30 01:11:09 (GMT)
commit4e294f6feb3193854d23e0e8be487213a80b232f (patch)
tree5bd03678ff6136ef2a7ffb03a088388c275713c7
parent732d1b02417e91d6a4247879e290065287cc6b51 (diff)
downloadcpython-4e294f6feb3193854d23e0e8be487213a80b232f.zip
cpython-4e294f6feb3193854d23e0e8be487213a80b232f.tar.gz
cpython-4e294f6feb3193854d23e0e8be487213a80b232f.tar.bz2
gh-133036: Deprecate codecs.open (#133038)
Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Co-authored-by: Victor Stinner <vstinner@python.org>
-rw-r--r--Doc/deprecations/pending-removal-in-future.rst2
-rw-r--r--Doc/library/codecs.rst4
-rw-r--r--Doc/whatsnew/3.14.rst4
-rw-r--r--Lib/_pyio.py3
-rw-r--r--Lib/codecs.py6
-rw-r--r--Lib/test/test_codecs.py42
-rw-r--r--Lib/test/test_multibytecodec.py3
-rw-r--r--Lib/test/test_sax.py6
-rw-r--r--Misc/NEWS.d/next/Library/2025-04-27-15-21-05.gh-issue-133036.HCNYA7.rst2
-rw-r--r--Modules/_io/textio.c5
-rw-r--r--Python/codecs.c18
11 files changed, 58 insertions, 37 deletions
diff --git a/Doc/deprecations/pending-removal-in-future.rst b/Doc/deprecations/pending-removal-in-future.rst
index 563f994..4c4a368 100644
--- a/Doc/deprecations/pending-removal-in-future.rst
+++ b/Doc/deprecations/pending-removal-in-future.rst
@@ -47,6 +47,8 @@ although there is currently no date scheduled for their removal.
:data:`calendar.FEBRUARY`.
(Contributed by Prince Roshan in :gh:`103636`.)
+* :mod:`codecs`: use :func:`open` instead of :func:`codecs.open`. (:gh:`133038`)
+
* :attr:`codeobject.co_lnotab`: use the :meth:`codeobject.co_lines` method
instead.
diff --git a/Doc/library/codecs.rst b/Doc/library/codecs.rst
index f63148a..14f6547 100644
--- a/Doc/library/codecs.rst
+++ b/Doc/library/codecs.rst
@@ -208,6 +208,10 @@ wider range of codecs when working with binary files:
.. versionchanged:: 3.11
The ``'U'`` mode has been removed.
+ .. deprecated:: next
+
+ :func:`codecs.open` has been superseded by :func:`open`.
+
.. function:: EncodedFile(file, data_encoding, file_encoding=None, errors='strict')
diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst
index 185a467..9e6b69f 100644
--- a/Doc/whatsnew/3.14.rst
+++ b/Doc/whatsnew/3.14.rst
@@ -1597,6 +1597,10 @@ Deprecated
as a single positional argument.
(Contributed by Serhiy Storchaka in :gh:`109218`.)
+* :mod:`codecs`:
+ :func:`codecs.open` is now deprecated. Use :func:`open` instead.
+ (Contributed by Inada Naoki in :gh:`133036`.)
+
* :mod:`functools`:
Calling the Python implementation of :func:`functools.reduce` with *function*
or *sequence* as keyword arguments is now deprecated.
diff --git a/Lib/_pyio.py b/Lib/_pyio.py
index b875103..a870de5 100644
--- a/Lib/_pyio.py
+++ b/Lib/_pyio.py
@@ -2056,8 +2056,7 @@ class TextIOWrapper(TextIOBase):
raise ValueError("invalid encoding: %r" % encoding)
if not codecs.lookup(encoding)._is_text_encoding:
- msg = ("%r is not a text encoding; "
- "use codecs.open() to handle arbitrary codecs")
+ msg = "%r is not a text encoding"
raise LookupError(msg % encoding)
if errors is None:
diff --git a/Lib/codecs.py b/Lib/codecs.py
index e365e6c..fc38e92 100644
--- a/Lib/codecs.py
+++ b/Lib/codecs.py
@@ -884,7 +884,6 @@ class StreamRecoder:
### Shortcuts
def open(filename, mode='r', encoding=None, errors='strict', buffering=-1):
-
""" Open an encoded file using the given mode and return
a wrapped version providing transparent encoding/decoding.
@@ -912,8 +911,11 @@ def open(filename, mode='r', encoding=None, errors='strict', buffering=-1):
.encoding which allows querying the used encoding. This
attribute is only available if an encoding was specified as
parameter.
-
"""
+ import warnings
+ warnings.warn("codecs.open() is deprecated. Use open() instead.",
+ DeprecationWarning, stacklevel=2)
+
if encoding is not None and \
'b' not in mode:
# Force opening of the file in binary mode
diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py
index e51f7e0..94fcf98 100644
--- a/Lib/test/test_codecs.py
+++ b/Lib/test/test_codecs.py
@@ -7,6 +7,7 @@ import sys
import unittest
import encodings
from unittest import mock
+import warnings
from test import support
from test.support import os_helper
@@ -20,13 +21,12 @@ try:
except ImportError:
_testinternalcapi = None
-try:
- import ctypes
-except ImportError:
- ctypes = None
- SIZEOF_WCHAR_T = -1
-else:
- SIZEOF_WCHAR_T = ctypes.sizeof(ctypes.c_wchar)
+
+def codecs_open_no_warn(*args, **kwargs):
+ """Call codecs.open(*args, **kwargs) ignoring DeprecationWarning."""
+ with warnings.catch_warnings():
+ warnings.simplefilter("ignore")
+ return codecs.open(*args, **kwargs)
def coding_checker(self, coder):
def check(input, expect):
@@ -35,13 +35,13 @@ def coding_checker(self, coder):
# On small versions of Windows like Windows IoT or Windows Nano Server not all codepages are present
def is_code_page_present(cp):
- from ctypes import POINTER, WINFUNCTYPE, WinDLL
+ from ctypes import POINTER, WINFUNCTYPE, WinDLL, Structure
from ctypes.wintypes import BOOL, BYTE, WCHAR, UINT, DWORD
MAX_LEADBYTES = 12 # 5 ranges, 2 bytes ea., 0 term.
MAX_DEFAULTCHAR = 2 # single or double byte
MAX_PATH = 260
- class CPINFOEXW(ctypes.Structure):
+ class CPINFOEXW(Structure):
_fields_ = [("MaxCharSize", UINT),
("DefaultChar", BYTE*MAX_DEFAULTCHAR),
("LeadByte", BYTE*MAX_LEADBYTES),
@@ -719,19 +719,19 @@ class UTF16Test(ReadTest, unittest.TestCase):
self.addCleanup(os_helper.unlink, os_helper.TESTFN)
with open(os_helper.TESTFN, 'wb') as fp:
fp.write(s)
- with codecs.open(os_helper.TESTFN, 'r',
+ with codecs_open_no_warn(os_helper.TESTFN, 'r',
encoding=self.encoding) as reader:
self.assertEqual(reader.read(), s1)
def test_invalid_modes(self):
for mode in ('U', 'rU', 'r+U'):
with self.assertRaises(ValueError) as cm:
- codecs.open(os_helper.TESTFN, mode, encoding=self.encoding)
+ codecs_open_no_warn(os_helper.TESTFN, mode, encoding=self.encoding)
self.assertIn('invalid mode', str(cm.exception))
for mode in ('rt', 'wt', 'at', 'r+t'):
with self.assertRaises(ValueError) as cm:
- codecs.open(os_helper.TESTFN, mode, encoding=self.encoding)
+ codecs_open_no_warn(os_helper.TESTFN, mode, encoding=self.encoding)
self.assertIn("can't have text and binary mode at once",
str(cm.exception))
@@ -1844,9 +1844,9 @@ class CodecsModuleTest(unittest.TestCase):
def test_open(self):
self.addCleanup(os_helper.unlink, os_helper.TESTFN)
for mode in ('w', 'r', 'r+', 'w+', 'a', 'a+'):
- with self.subTest(mode), \
- codecs.open(os_helper.TESTFN, mode, 'ascii') as file:
- self.assertIsInstance(file, codecs.StreamReaderWriter)
+ with self.subTest(mode), self.assertWarns(DeprecationWarning):
+ with codecs.open(os_helper.TESTFN, mode, 'ascii') as file:
+ self.assertIsInstance(file, codecs.StreamReaderWriter)
def test_undefined(self):
self.assertRaises(UnicodeError, codecs.encode, 'abc', 'undefined')
@@ -1863,7 +1863,7 @@ class CodecsModuleTest(unittest.TestCase):
mock_open = mock.mock_open()
with mock.patch('builtins.open', mock_open) as file:
with self.assertRaises(LookupError):
- codecs.open(os_helper.TESTFN, 'wt', 'invalid-encoding')
+ codecs_open_no_warn(os_helper.TESTFN, 'wt', 'invalid-encoding')
file().close.assert_called()
@@ -2883,7 +2883,7 @@ class BomTest(unittest.TestCase):
self.addCleanup(os_helper.unlink, os_helper.TESTFN)
for encoding in tests:
# Check if the BOM is written only once
- with codecs.open(os_helper.TESTFN, 'w+', encoding=encoding) as f:
+ with codecs_open_no_warn(os_helper.TESTFN, 'w+', encoding=encoding) as f:
f.write(data)
f.write(data)
f.seek(0)
@@ -2892,7 +2892,7 @@ class BomTest(unittest.TestCase):
self.assertEqual(f.read(), data * 2)
# Check that the BOM is written after a seek(0)
- with codecs.open(os_helper.TESTFN, 'w+', encoding=encoding) as f:
+ with codecs_open_no_warn(os_helper.TESTFN, 'w+', encoding=encoding) as f:
f.write(data[0])
self.assertNotEqual(f.tell(), 0)
f.seek(0)
@@ -2901,7 +2901,7 @@ class BomTest(unittest.TestCase):
self.assertEqual(f.read(), data)
# (StreamWriter) Check that the BOM is written after a seek(0)
- with codecs.open(os_helper.TESTFN, 'w+', encoding=encoding) as f:
+ with codecs_open_no_warn(os_helper.TESTFN, 'w+', encoding=encoding) as f:
f.writer.write(data[0])
self.assertNotEqual(f.writer.tell(), 0)
f.writer.seek(0)
@@ -2911,7 +2911,7 @@ class BomTest(unittest.TestCase):
# Check that the BOM is not written after a seek() at a position
# different than the start
- with codecs.open(os_helper.TESTFN, 'w+', encoding=encoding) as f:
+ with codecs_open_no_warn(os_helper.TESTFN, 'w+', encoding=encoding) as f:
f.write(data)
f.seek(f.tell())
f.write(data)
@@ -2920,7 +2920,7 @@ class BomTest(unittest.TestCase):
# (StreamWriter) Check that the BOM is not written after a seek()
# at a position different than the start
- with codecs.open(os_helper.TESTFN, 'w+', encoding=encoding) as f:
+ with codecs_open_no_warn(os_helper.TESTFN, 'w+', encoding=encoding) as f:
f.writer.write(data)
f.writer.seek(f.writer.tell())
f.writer.write(data)
diff --git a/Lib/test/test_multibytecodec.py b/Lib/test/test_multibytecodec.py
index 1b55f1e..d7a2333 100644
--- a/Lib/test/test_multibytecodec.py
+++ b/Lib/test/test_multibytecodec.py
@@ -314,7 +314,8 @@ class Test_StreamReader(unittest.TestCase):
f.write(b'\xa1')
finally:
f.close()
- f = codecs.open(TESTFN, encoding='cp949')
+ with self.assertWarns(DeprecationWarning):
+ f = codecs.open(TESTFN, encoding='cp949')
try:
self.assertRaises(UnicodeDecodeError, f.read, 2)
finally:
diff --git a/Lib/test/test_sax.py b/Lib/test/test_sax.py
index 0d0f86c..5c10bce 100644
--- a/Lib/test/test_sax.py
+++ b/Lib/test/test_sax.py
@@ -1,5 +1,4 @@
# regression test for SAX 2.0
-# $Id$
from xml.sax import make_parser, ContentHandler, \
SAXException, SAXReaderNotAvailable, SAXParseException
@@ -832,8 +831,9 @@ class StreamReaderWriterXmlgenTest(XmlgenTest, unittest.TestCase):
fname = os_helper.TESTFN + '-codecs'
def ioclass(self):
- writer = codecs.open(self.fname, 'w', encoding='ascii',
- errors='xmlcharrefreplace', buffering=0)
+ with self.assertWarns(DeprecationWarning):
+ writer = codecs.open(self.fname, 'w', encoding='ascii',
+ errors='xmlcharrefreplace', buffering=0)
def cleanup():
writer.close()
os_helper.unlink(self.fname)
diff --git a/Misc/NEWS.d/next/Library/2025-04-27-15-21-05.gh-issue-133036.HCNYA7.rst b/Misc/NEWS.d/next/Library/2025-04-27-15-21-05.gh-issue-133036.HCNYA7.rst
new file mode 100644
index 0000000..46b1f55
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-04-27-15-21-05.gh-issue-133036.HCNYA7.rst
@@ -0,0 +1,2 @@
+:func:`codecs.open` is now deprecated. Use :func:`open` instead. Contributed
+by Inada Naoki.
diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c
index e77d844..a5b2ca7 100644
--- a/Modules/_io/textio.c
+++ b/Modules/_io/textio.c
@@ -1185,7 +1185,7 @@ _io_TextIOWrapper___init___impl(textio *self, PyObject *buffer,
}
/* Check we have been asked for a real text encoding */
- codec_info = _PyCodec_LookupTextEncoding(encoding, "codecs.open()");
+ codec_info = _PyCodec_LookupTextEncoding(encoding, NULL);
if (codec_info == NULL) {
Py_CLEAR(self->encoding);
goto error;
@@ -1324,8 +1324,7 @@ textiowrapper_change_encoding(textio *self, PyObject *encoding,
}
// Create new encoder & decoder
- PyObject *codec_info = _PyCodec_LookupTextEncoding(
- c_encoding, "codecs.open()");
+ PyObject *codec_info = _PyCodec_LookupTextEncoding(c_encoding, NULL);
if (codec_info == NULL) {
Py_DECREF(encoding);
Py_DECREF(errors);
diff --git a/Python/codecs.c b/Python/codecs.c
index 265f521..caf8d9d 100644
--- a/Python/codecs.c
+++ b/Python/codecs.c
@@ -540,11 +540,19 @@ PyObject * _PyCodec_LookupTextEncoding(const char *encoding,
Py_DECREF(attr);
if (is_text_codec <= 0) {
Py_DECREF(codec);
- if (!is_text_codec)
- PyErr_Format(PyExc_LookupError,
- "'%.400s' is not a text encoding; "
- "use %s to handle arbitrary codecs",
- encoding, alternate_command);
+ if (!is_text_codec) {
+ if (alternate_command != NULL) {
+ PyErr_Format(PyExc_LookupError,
+ "'%.400s' is not a text encoding; "
+ "use %s to handle arbitrary codecs",
+ encoding, alternate_command);
+ }
+ else {
+ PyErr_Format(PyExc_LookupError,
+ "'%.400s' is not a text encoding",
+ encoding);
+ }
+ }
return NULL;
}
}