mirror of https://github.com/python/cpython.git
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>
This commit is contained in:
parent
732d1b0241
commit
4e294f6feb
|
@ -47,6 +47,8 @@ although there is currently no date scheduled for their removal.
|
||||||
:data:`calendar.FEBRUARY`.
|
:data:`calendar.FEBRUARY`.
|
||||||
(Contributed by Prince Roshan in :gh:`103636`.)
|
(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
|
* :attr:`codeobject.co_lnotab`: use the :meth:`codeobject.co_lines` method
|
||||||
instead.
|
instead.
|
||||||
|
|
||||||
|
|
|
@ -208,6 +208,10 @@ wider range of codecs when working with binary files:
|
||||||
.. versionchanged:: 3.11
|
.. versionchanged:: 3.11
|
||||||
The ``'U'`` mode has been removed.
|
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')
|
.. function:: EncodedFile(file, data_encoding, file_encoding=None, errors='strict')
|
||||||
|
|
||||||
|
|
|
@ -1597,6 +1597,10 @@ Deprecated
|
||||||
as a single positional argument.
|
as a single positional argument.
|
||||||
(Contributed by Serhiy Storchaka in :gh:`109218`.)
|
(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`:
|
* :mod:`functools`:
|
||||||
Calling the Python implementation of :func:`functools.reduce` with *function*
|
Calling the Python implementation of :func:`functools.reduce` with *function*
|
||||||
or *sequence* as keyword arguments is now deprecated.
|
or *sequence* as keyword arguments is now deprecated.
|
||||||
|
|
|
@ -2056,8 +2056,7 @@ def __init__(self, buffer, encoding=None, errors=None, newline=None,
|
||||||
raise ValueError("invalid encoding: %r" % encoding)
|
raise ValueError("invalid encoding: %r" % encoding)
|
||||||
|
|
||||||
if not codecs.lookup(encoding)._is_text_encoding:
|
if not codecs.lookup(encoding)._is_text_encoding:
|
||||||
msg = ("%r is not a text encoding; "
|
msg = "%r is not a text encoding"
|
||||||
"use codecs.open() to handle arbitrary codecs")
|
|
||||||
raise LookupError(msg % encoding)
|
raise LookupError(msg % encoding)
|
||||||
|
|
||||||
if errors is None:
|
if errors is None:
|
||||||
|
|
|
@ -884,7 +884,6 @@ def __reduce_ex__(self, proto):
|
||||||
### Shortcuts
|
### Shortcuts
|
||||||
|
|
||||||
def open(filename, mode='r', encoding=None, errors='strict', buffering=-1):
|
def open(filename, mode='r', encoding=None, errors='strict', buffering=-1):
|
||||||
|
|
||||||
""" Open an encoded file using the given mode and return
|
""" Open an encoded file using the given mode and return
|
||||||
a wrapped version providing transparent encoding/decoding.
|
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
|
.encoding which allows querying the used encoding. This
|
||||||
attribute is only available if an encoding was specified as
|
attribute is only available if an encoding was specified as
|
||||||
parameter.
|
parameter.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
import warnings
|
||||||
|
warnings.warn("codecs.open() is deprecated. Use open() instead.",
|
||||||
|
DeprecationWarning, stacklevel=2)
|
||||||
|
|
||||||
if encoding is not None and \
|
if encoding is not None and \
|
||||||
'b' not in mode:
|
'b' not in mode:
|
||||||
# Force opening of the file in binary mode
|
# Force opening of the file in binary mode
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
import unittest
|
import unittest
|
||||||
import encodings
|
import encodings
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
import warnings
|
||||||
|
|
||||||
from test import support
|
from test import support
|
||||||
from test.support import os_helper
|
from test.support import os_helper
|
||||||
|
@ -20,13 +21,12 @@
|
||||||
except ImportError:
|
except ImportError:
|
||||||
_testinternalcapi = None
|
_testinternalcapi = None
|
||||||
|
|
||||||
try:
|
|
||||||
import ctypes
|
def codecs_open_no_warn(*args, **kwargs):
|
||||||
except ImportError:
|
"""Call codecs.open(*args, **kwargs) ignoring DeprecationWarning."""
|
||||||
ctypes = None
|
with warnings.catch_warnings():
|
||||||
SIZEOF_WCHAR_T = -1
|
warnings.simplefilter("ignore")
|
||||||
else:
|
return codecs.open(*args, **kwargs)
|
||||||
SIZEOF_WCHAR_T = ctypes.sizeof(ctypes.c_wchar)
|
|
||||||
|
|
||||||
def coding_checker(self, coder):
|
def coding_checker(self, coder):
|
||||||
def check(input, expect):
|
def check(input, expect):
|
||||||
|
@ -35,13 +35,13 @@ def check(input, expect):
|
||||||
|
|
||||||
# On small versions of Windows like Windows IoT or Windows Nano Server not all codepages are present
|
# On small versions of Windows like Windows IoT or Windows Nano Server not all codepages are present
|
||||||
def is_code_page_present(cp):
|
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
|
from ctypes.wintypes import BOOL, BYTE, WCHAR, UINT, DWORD
|
||||||
|
|
||||||
MAX_LEADBYTES = 12 # 5 ranges, 2 bytes ea., 0 term.
|
MAX_LEADBYTES = 12 # 5 ranges, 2 bytes ea., 0 term.
|
||||||
MAX_DEFAULTCHAR = 2 # single or double byte
|
MAX_DEFAULTCHAR = 2 # single or double byte
|
||||||
MAX_PATH = 260
|
MAX_PATH = 260
|
||||||
class CPINFOEXW(ctypes.Structure):
|
class CPINFOEXW(Structure):
|
||||||
_fields_ = [("MaxCharSize", UINT),
|
_fields_ = [("MaxCharSize", UINT),
|
||||||
("DefaultChar", BYTE*MAX_DEFAULTCHAR),
|
("DefaultChar", BYTE*MAX_DEFAULTCHAR),
|
||||||
("LeadByte", BYTE*MAX_LEADBYTES),
|
("LeadByte", BYTE*MAX_LEADBYTES),
|
||||||
|
@ -719,19 +719,19 @@ def test_bug691291(self):
|
||||||
self.addCleanup(os_helper.unlink, os_helper.TESTFN)
|
self.addCleanup(os_helper.unlink, os_helper.TESTFN)
|
||||||
with open(os_helper.TESTFN, 'wb') as fp:
|
with open(os_helper.TESTFN, 'wb') as fp:
|
||||||
fp.write(s)
|
fp.write(s)
|
||||||
with codecs.open(os_helper.TESTFN, 'r',
|
with codecs_open_no_warn(os_helper.TESTFN, 'r',
|
||||||
encoding=self.encoding) as reader:
|
encoding=self.encoding) as reader:
|
||||||
self.assertEqual(reader.read(), s1)
|
self.assertEqual(reader.read(), s1)
|
||||||
|
|
||||||
def test_invalid_modes(self):
|
def test_invalid_modes(self):
|
||||||
for mode in ('U', 'rU', 'r+U'):
|
for mode in ('U', 'rU', 'r+U'):
|
||||||
with self.assertRaises(ValueError) as cm:
|
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))
|
self.assertIn('invalid mode', str(cm.exception))
|
||||||
|
|
||||||
for mode in ('rt', 'wt', 'at', 'r+t'):
|
for mode in ('rt', 'wt', 'at', 'r+t'):
|
||||||
with self.assertRaises(ValueError) as cm:
|
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",
|
self.assertIn("can't have text and binary mode at once",
|
||||||
str(cm.exception))
|
str(cm.exception))
|
||||||
|
|
||||||
|
@ -1844,9 +1844,9 @@ def test_all(self):
|
||||||
def test_open(self):
|
def test_open(self):
|
||||||
self.addCleanup(os_helper.unlink, os_helper.TESTFN)
|
self.addCleanup(os_helper.unlink, os_helper.TESTFN)
|
||||||
for mode in ('w', 'r', 'r+', 'w+', 'a', 'a+'):
|
for mode in ('w', 'r', 'r+', 'w+', 'a', 'a+'):
|
||||||
with self.subTest(mode), \
|
with self.subTest(mode), self.assertWarns(DeprecationWarning):
|
||||||
codecs.open(os_helper.TESTFN, mode, 'ascii') as file:
|
with codecs.open(os_helper.TESTFN, mode, 'ascii') as file:
|
||||||
self.assertIsInstance(file, codecs.StreamReaderWriter)
|
self.assertIsInstance(file, codecs.StreamReaderWriter)
|
||||||
|
|
||||||
def test_undefined(self):
|
def test_undefined(self):
|
||||||
self.assertRaises(UnicodeError, codecs.encode, 'abc', 'undefined')
|
self.assertRaises(UnicodeError, codecs.encode, 'abc', 'undefined')
|
||||||
|
@ -1863,7 +1863,7 @@ def test_file_closes_if_lookup_error_raised(self):
|
||||||
mock_open = mock.mock_open()
|
mock_open = mock.mock_open()
|
||||||
with mock.patch('builtins.open', mock_open) as file:
|
with mock.patch('builtins.open', mock_open) as file:
|
||||||
with self.assertRaises(LookupError):
|
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()
|
file().close.assert_called()
|
||||||
|
|
||||||
|
@ -2883,7 +2883,7 @@ def test_seek0(self):
|
||||||
self.addCleanup(os_helper.unlink, os_helper.TESTFN)
|
self.addCleanup(os_helper.unlink, os_helper.TESTFN)
|
||||||
for encoding in tests:
|
for encoding in tests:
|
||||||
# Check if the BOM is written only once
|
# 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.write(data)
|
f.write(data)
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
|
@ -2892,7 +2892,7 @@ def test_seek0(self):
|
||||||
self.assertEqual(f.read(), data * 2)
|
self.assertEqual(f.read(), data * 2)
|
||||||
|
|
||||||
# Check that the BOM is written after a seek(0)
|
# 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])
|
f.write(data[0])
|
||||||
self.assertNotEqual(f.tell(), 0)
|
self.assertNotEqual(f.tell(), 0)
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
|
@ -2901,7 +2901,7 @@ def test_seek0(self):
|
||||||
self.assertEqual(f.read(), data)
|
self.assertEqual(f.read(), data)
|
||||||
|
|
||||||
# (StreamWriter) Check that the BOM is written after a seek(0)
|
# (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])
|
f.writer.write(data[0])
|
||||||
self.assertNotEqual(f.writer.tell(), 0)
|
self.assertNotEqual(f.writer.tell(), 0)
|
||||||
f.writer.seek(0)
|
f.writer.seek(0)
|
||||||
|
@ -2911,7 +2911,7 @@ def test_seek0(self):
|
||||||
|
|
||||||
# Check that the BOM is not written after a seek() at a position
|
# Check that the BOM is not written after a seek() at a position
|
||||||
# different than the start
|
# 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.write(data)
|
||||||
f.seek(f.tell())
|
f.seek(f.tell())
|
||||||
f.write(data)
|
f.write(data)
|
||||||
|
@ -2920,7 +2920,7 @@ def test_seek0(self):
|
||||||
|
|
||||||
# (StreamWriter) Check that the BOM is not written after a seek()
|
# (StreamWriter) Check that the BOM is not written after a seek()
|
||||||
# at a position different than the start
|
# 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.write(data)
|
||||||
f.writer.seek(f.writer.tell())
|
f.writer.seek(f.writer.tell())
|
||||||
f.writer.write(data)
|
f.writer.write(data)
|
||||||
|
|
|
@ -314,7 +314,8 @@ def test_bug1728403(self):
|
||||||
f.write(b'\xa1')
|
f.write(b'\xa1')
|
||||||
finally:
|
finally:
|
||||||
f.close()
|
f.close()
|
||||||
f = codecs.open(TESTFN, encoding='cp949')
|
with self.assertWarns(DeprecationWarning):
|
||||||
|
f = codecs.open(TESTFN, encoding='cp949')
|
||||||
try:
|
try:
|
||||||
self.assertRaises(UnicodeDecodeError, f.read, 2)
|
self.assertRaises(UnicodeDecodeError, f.read, 2)
|
||||||
finally:
|
finally:
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
# regression test for SAX 2.0
|
# regression test for SAX 2.0
|
||||||
# $Id$
|
|
||||||
|
|
||||||
from xml.sax import make_parser, ContentHandler, \
|
from xml.sax import make_parser, ContentHandler, \
|
||||||
SAXException, SAXReaderNotAvailable, SAXParseException
|
SAXException, SAXReaderNotAvailable, SAXParseException
|
||||||
|
@ -832,8 +831,9 @@ class StreamReaderWriterXmlgenTest(XmlgenTest, unittest.TestCase):
|
||||||
fname = os_helper.TESTFN + '-codecs'
|
fname = os_helper.TESTFN + '-codecs'
|
||||||
|
|
||||||
def ioclass(self):
|
def ioclass(self):
|
||||||
writer = codecs.open(self.fname, 'w', encoding='ascii',
|
with self.assertWarns(DeprecationWarning):
|
||||||
errors='xmlcharrefreplace', buffering=0)
|
writer = codecs.open(self.fname, 'w', encoding='ascii',
|
||||||
|
errors='xmlcharrefreplace', buffering=0)
|
||||||
def cleanup():
|
def cleanup():
|
||||||
writer.close()
|
writer.close()
|
||||||
os_helper.unlink(self.fname)
|
os_helper.unlink(self.fname)
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
:func:`codecs.open` is now deprecated. Use :func:`open` instead. Contributed
|
||||||
|
by Inada Naoki.
|
|
@ -1185,7 +1185,7 @@ _io_TextIOWrapper___init___impl(textio *self, PyObject *buffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check we have been asked for a real text encoding */
|
/* 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) {
|
if (codec_info == NULL) {
|
||||||
Py_CLEAR(self->encoding);
|
Py_CLEAR(self->encoding);
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -1324,8 +1324,7 @@ textiowrapper_change_encoding(textio *self, PyObject *encoding,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new encoder & decoder
|
// Create new encoder & decoder
|
||||||
PyObject *codec_info = _PyCodec_LookupTextEncoding(
|
PyObject *codec_info = _PyCodec_LookupTextEncoding(c_encoding, NULL);
|
||||||
c_encoding, "codecs.open()");
|
|
||||||
if (codec_info == NULL) {
|
if (codec_info == NULL) {
|
||||||
Py_DECREF(encoding);
|
Py_DECREF(encoding);
|
||||||
Py_DECREF(errors);
|
Py_DECREF(errors);
|
||||||
|
|
|
@ -540,11 +540,19 @@ PyObject * _PyCodec_LookupTextEncoding(const char *encoding,
|
||||||
Py_DECREF(attr);
|
Py_DECREF(attr);
|
||||||
if (is_text_codec <= 0) {
|
if (is_text_codec <= 0) {
|
||||||
Py_DECREF(codec);
|
Py_DECREF(codec);
|
||||||
if (!is_text_codec)
|
if (!is_text_codec) {
|
||||||
PyErr_Format(PyExc_LookupError,
|
if (alternate_command != NULL) {
|
||||||
"'%.400s' is not a text encoding; "
|
PyErr_Format(PyExc_LookupError,
|
||||||
"use %s to handle arbitrary codecs",
|
"'%.400s' is not a text encoding; "
|
||||||
encoding, alternate_command);
|
"use %s to handle arbitrary codecs",
|
||||||
|
encoding, alternate_command);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
PyErr_Format(PyExc_LookupError,
|
||||||
|
"'%.400s' is not a text encoding",
|
||||||
|
encoding);
|
||||||
|
}
|
||||||
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue