Skip to content

Commit 19ba212

Browse files
authored
bpo-37330: open() no longer accept 'U' in file mode (GH-28118)
open(), io.open(), codecs.open() and fileinput.FileInput no longer accept "U" ("universal newline") in the file mode. This flag was deprecated since Python 3.3.
1 parent a806608 commit 19ba212

File tree

13 files changed

+61
-104
lines changed

13 files changed

+61
-104
lines changed

Doc/library/codecs.rst

+3
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,9 @@ wider range of codecs when working with binary files:
204204
*buffering* has the same meaning as for the built-in :func:`open` function.
205205
It defaults to -1 which means that the default buffer size will be used.
206206

207+
.. versionchanged:: 3.11
208+
The ``'U'`` mode has been removed.
209+
207210

208211
.. function:: EncodedFile(file, data_encoding, file_encoding=None, errors='strict')
209212

Doc/library/fileinput.rst

+4-4
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ available for subclassing as well:
153153
and :meth:`~io.TextIOBase.readline` cannot be mixed.
154154

155155
With *mode* you can specify which file mode will be passed to :func:`open`. It
156-
must be one of ``'r'``, ``'rU'``, ``'U'`` and ``'rb'``.
156+
must be one of ``'r'`` and ``'rb'``.
157157

158158
The *openhook*, when given, must be a function that takes two arguments,
159159
*filename* and *mode*, and returns an accordingly opened file-like object. You
@@ -171,9 +171,6 @@ available for subclassing as well:
171171
.. versionchanged:: 3.2
172172
Can be used as a context manager.
173173

174-
.. deprecated:: 3.4
175-
The ``'rU'`` and ``'U'`` modes.
176-
177174
.. deprecated:: 3.8
178175
Support for :meth:`__getitem__` method is deprecated.
179176

@@ -183,6 +180,9 @@ available for subclassing as well:
183180
.. versionchanged:: 3.10
184181
The keyword-only parameter *encoding* and *errors* are added.
185182

183+
.. versionchanged:: 3.11
184+
The ``'rU'`` and ``'U'`` modes have been removed.
185+
186186

187187
**Optional in-place filtering:** if the keyword argument ``inplace=True`` is
188188
passed to :func:`fileinput.input` or to the :class:`FileInput` constructor, the

Doc/library/functions.rst

+7-18
Original file line numberDiff line numberDiff line change
@@ -1156,12 +1156,6 @@ are always available. They are listed here in alphabetical order.
11561156
first decoded using a platform-dependent encoding or using the specified
11571157
*encoding* if given.
11581158

1159-
There is an additional mode character permitted, ``'U'``, which no longer
1160-
has any effect, and is considered deprecated. It previously enabled
1161-
:term:`universal newlines` in text mode, which became the default behavior
1162-
in Python 3.0. Refer to the documentation of the
1163-
:ref:`newline <open-newline-parameter>` parameter for further details.
1164-
11651159
.. note::
11661160

11671161
Python doesn't depend on the underlying operating system's notion of text
@@ -1304,39 +1298,34 @@ are always available. They are listed here in alphabetical order.
13041298
The ``mode`` and ``flags`` arguments may have been modified or inferred from
13051299
the original call.
13061300

1307-
.. versionchanged::
1308-
3.3
1301+
.. versionchanged:: 3.3
13091302

13101303
* The *opener* parameter was added.
13111304
* The ``'x'`` mode was added.
13121305
* :exc:`IOError` used to be raised, it is now an alias of :exc:`OSError`.
13131306
* :exc:`FileExistsError` is now raised if the file opened in exclusive
13141307
creation mode (``'x'``) already exists.
13151308

1316-
.. versionchanged::
1317-
3.4
1309+
.. versionchanged:: 3.4
13181310

13191311
* The file is now non-inheritable.
13201312

1321-
.. deprecated-removed:: 3.4 3.10
1322-
1323-
The ``'U'`` mode.
1324-
1325-
.. versionchanged::
1326-
3.5
1313+
.. versionchanged:: 3.5
13271314

13281315
* If the system call is interrupted and the signal handler does not raise an
13291316
exception, the function now retries the system call instead of raising an
13301317
:exc:`InterruptedError` exception (see :pep:`475` for the rationale).
13311318
* The ``'namereplace'`` error handler was added.
13321319

1333-
.. versionchanged::
1334-
3.6
1320+
.. versionchanged:: 3.6
13351321

13361322
* Support added to accept objects implementing :class:`os.PathLike`.
13371323
* On Windows, opening a console buffer may return a subclass of
13381324
:class:`io.RawIOBase` other than :class:`io.FileIO`.
13391325

1326+
.. versionchanged:: 3.11
1327+
The ``'U'`` mode has been removed.
1328+
13401329
.. function:: ord(c)
13411330

13421331
Given a string representing one Unicode character, return an integer

Doc/whatsnew/3.11.rst

+8
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,14 @@ Changes in the Python API
321321
Python 3.8.
322322
(Contributed by Illia Volochii in :issue:`43234`.)
323323

324+
* :func:`open`, :func:`io.open`, :func:`codecs.open` and
325+
:class:`fileinput.FileInput` no longer accept ``'U'`` ("universal newline")
326+
in the file mode. This flag was deprecated since Python 3.3. In Python 3, the
327+
"universal newline" is used by default when a file is open in text mode. The
328+
:ref:`newline parameter <open-newline-parameter>` of :func:`open` controls
329+
how universal newlines works.
330+
(Contributed by Victor Stinner in :issue:`37330`.)
331+
324332

325333
C API Changes
326334
=============

Lib/_pyio.py

+1-13
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,6 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None,
101101
'b' binary mode
102102
't' text mode (default)
103103
'+' open a disk file for updating (reading and writing)
104-
'U' universal newline mode (deprecated)
105104
========= ===============================================================
106105
107106
The default mode is 'rt' (open for reading text). For binary random
@@ -117,10 +116,6 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None,
117116
returned as strings, the bytes having been first decoded using a
118117
platform-dependent encoding or using the specified encoding if given.
119118
120-
'U' mode is deprecated and will raise an exception in future versions
121-
of Python. It has no effect in Python 3. Use newline to control
122-
universal newlines mode.
123-
124119
buffering is an optional integer used to set the buffering policy.
125120
Pass 0 to switch buffering off (only allowed in binary mode), 1 to select
126121
line buffering (only usable in text mode), and an integer > 1 to indicate
@@ -206,7 +201,7 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None,
206201
if errors is not None and not isinstance(errors, str):
207202
raise TypeError("invalid errors: %r" % errors)
208203
modes = set(mode)
209-
if modes - set("axrwb+tU") or len(mode) > len(modes):
204+
if modes - set("axrwb+t") or len(mode) > len(modes):
210205
raise ValueError("invalid mode: %r" % mode)
211206
creating = "x" in modes
212207
reading = "r" in modes
@@ -215,13 +210,6 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None,
215210
updating = "+" in modes
216211
text = "t" in modes
217212
binary = "b" in modes
218-
if "U" in modes:
219-
if creating or writing or appending or updating:
220-
raise ValueError("mode U cannot be combined with 'x', 'w', 'a', or '+'")
221-
import warnings
222-
warnings.warn("'U' mode is deprecated",
223-
DeprecationWarning, 2)
224-
reading = True
225213
if text and binary:
226214
raise ValueError("can't have text and binary mode at once")
227215
if creating + reading + writing + appending > 1:

Lib/fileinput.py

+3-8
Original file line numberDiff line numberDiff line change
@@ -217,15 +217,10 @@ def __init__(self, files=None, inplace=False, backup="", *,
217217
EncodingWarning, 2)
218218

219219
# restrict mode argument to reading modes
220-
if mode not in ('r', 'rU', 'U', 'rb'):
221-
raise ValueError("FileInput opening mode must be one of "
222-
"'r', 'rU', 'U' and 'rb'")
223-
if 'U' in mode:
224-
import warnings
225-
warnings.warn("'U' mode is deprecated",
226-
DeprecationWarning, 2)
220+
if mode not in ('r', 'rb'):
221+
raise ValueError("FileInput opening mode must be 'r' or 'rb'")
227222
self._mode = mode
228-
self._write_mode = mode.replace('r', 'w') if 'U' not in mode else 'w'
223+
self._write_mode = mode.replace('r', 'w')
229224
if openhook:
230225
if inplace:
231226
raise ValueError("FileInput cannot use an opening hook in inplace mode")

Lib/imp.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ def load_module(name, file, filename, details):
226226
227227
"""
228228
suffix, mode, type_ = details
229-
if mode and (not mode.startswith(('r', 'U')) or '+' in mode):
229+
if mode and (not mode.startswith('r') or '+' in mode):
230230
raise ValueError('invalid file open mode {!r}'.format(mode))
231231
elif file is None and type_ in {PY_SOURCE, PY_COMPILED}:
232232
msg = 'file object required for import (type code {})'.format(type_)

Lib/test/test_codecs.py

+15-3
Original file line numberDiff line numberDiff line change
@@ -714,11 +714,23 @@ def test_bug691291(self):
714714
self.addCleanup(os_helper.unlink, os_helper.TESTFN)
715715
with open(os_helper.TESTFN, 'wb') as fp:
716716
fp.write(s)
717-
with warnings_helper.check_warnings(('', DeprecationWarning)):
718-
reader = codecs.open(os_helper.TESTFN, 'U', encoding=self.encoding)
719-
with reader:
717+
with codecs.open(os_helper.TESTFN, 'r',
718+
encoding=self.encoding) as reader:
720719
self.assertEqual(reader.read(), s1)
721720

721+
def test_invalid_modes(self):
722+
for mode in ('U', 'rU', 'r+U'):
723+
with self.assertRaises(ValueError) as cm:
724+
codecs.open(os_helper.TESTFN, mode, encoding=self.encoding)
725+
self.assertIn('invalid mode', str(cm.exception))
726+
727+
for mode in ('rt', 'wt', 'at', 'r+t'):
728+
with self.assertRaises(ValueError) as cm:
729+
codecs.open(os_helper.TESTFN, mode, encoding=self.encoding)
730+
self.assertIn("can't have text and binary mode at once",
731+
str(cm.exception))
732+
733+
722734
class UTF16LETest(ReadTest, unittest.TestCase):
723735
encoding = "utf-16-le"
724736
ill_formed_sequence = b"\x80\xdc"

Lib/test/test_fileinput.py

+5-18
Original file line numberDiff line numberDiff line change
@@ -230,20 +230,11 @@ def test_fileno(self):
230230
line = list(fi)
231231
self.assertEqual(fi.fileno(), -1)
232232

233-
def test_opening_mode(self):
234-
try:
235-
# invalid mode, should raise ValueError
236-
fi = FileInput(mode="w", encoding="utf-8")
237-
self.fail("FileInput should reject invalid mode argument")
238-
except ValueError:
239-
pass
240-
# try opening in universal newline mode
241-
t1 = self.writeTmp(b"A\nB\r\nC\rD", mode="wb")
242-
with warnings_helper.check_warnings(('', DeprecationWarning)):
243-
fi = FileInput(files=t1, mode="U", encoding="utf-8")
244-
with warnings_helper.check_warnings(('', DeprecationWarning)):
245-
lines = list(fi)
246-
self.assertEqual(lines, ["A\n", "B\n", "C\n", "D"])
233+
def test_invalid_opening_mode(self):
234+
for mode in ('w', 'rU', 'U'):
235+
with self.subTest(mode=mode):
236+
with self.assertRaises(ValueError):
237+
FileInput(mode=mode)
247238

248239
def test_stdin_binary_mode(self):
249240
with mock.patch('sys.stdin') as m_stdin:
@@ -1015,10 +1006,6 @@ def check(mode, expected_lines):
10151006
self.assertEqual(lines, expected_lines)
10161007

10171008
check('r', ['A\n', 'B\n', 'C\n', 'D\u20ac'])
1018-
with self.assertWarns(DeprecationWarning):
1019-
check('rU', ['A\n', 'B\n', 'C\n', 'D\u20ac'])
1020-
with self.assertWarns(DeprecationWarning):
1021-
check('U', ['A\n', 'B\n', 'C\n', 'D\u20ac'])
10221009
with self.assertRaises(ValueError):
10231010
check('rb', ['A\n', 'B\r\n', 'C\r', 'D\u20ac'])
10241011

Lib/test/test_io.py

+7-10
Original file line numberDiff line numberDiff line change
@@ -3954,16 +3954,6 @@ def test_attributes(self):
39543954
self.assertEqual(f.mode, "wb")
39553955
f.close()
39563956

3957-
with warnings_helper.check_warnings(('', DeprecationWarning)):
3958-
f = self.open(os_helper.TESTFN, "U", encoding="utf-8")
3959-
self.assertEqual(f.name, os_helper.TESTFN)
3960-
self.assertEqual(f.buffer.name, os_helper.TESTFN)
3961-
self.assertEqual(f.buffer.raw.name, os_helper.TESTFN)
3962-
self.assertEqual(f.mode, "U")
3963-
self.assertEqual(f.buffer.mode, "rb")
3964-
self.assertEqual(f.buffer.raw.mode, "rb")
3965-
f.close()
3966-
39673957
f = self.open(os_helper.TESTFN, "w+", encoding="utf-8")
39683958
self.assertEqual(f.mode, "w+")
39693959
self.assertEqual(f.buffer.mode, "rb+") # Does it really matter?
@@ -3977,6 +3967,13 @@ def test_attributes(self):
39773967
f.close()
39783968
g.close()
39793969

3970+
def test_removed_u_mode(self):
3971+
# bpo-37330: The "U" mode has been removed in Python 3.11
3972+
for mode in ("U", "rU", "r+U"):
3973+
with self.assertRaises(ValueError) as cm:
3974+
self.open(os_helper.TESTFN, mode)
3975+
self.assertIn('invalid mode', str(cm.exception))
3976+
39803977
def test_open_pipe_with_append(self):
39813978
# bpo-27805: Ignore ESPIPE from lseek() in open().
39823979
r, w = os.pipe()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
:func:`open`, :func:`io.open`, :func:`codecs.open` and
2+
:class:`fileinput.FileInput` no longer accept ``'U'`` ("universal newline")
3+
in the file mode. This flag was deprecated since Python 3.3. Patch by Victor
4+
Stinner.

Modules/_io/_iomodule.c

+2-23
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,6 @@ Character Meaning
138138
'b' binary mode
139139
't' text mode (default)
140140
'+' open a disk file for updating (reading and writing)
141-
'U' universal newline mode (deprecated)
142141
========= ===============================================================
143142
144143
The default mode is 'rt' (open for reading text). For binary random
@@ -154,10 +153,6 @@ bytes objects without any decoding. In text mode (the default, or when
154153
returned as strings, the bytes having been first decoded using a
155154
platform-dependent encoding or using the specified encoding if given.
156155
157-
'U' mode is deprecated and will raise an exception in future versions
158-
of Python. It has no effect in Python 3. Use newline to control
159-
universal newlines mode.
160-
161156
buffering is an optional integer used to set the buffering policy.
162157
Pass 0 to switch buffering off (only allowed in binary mode), 1 to select
163158
line buffering (only usable in text mode), and an integer > 1 to indicate
@@ -233,12 +228,12 @@ static PyObject *
233228
_io_open_impl(PyObject *module, PyObject *file, const char *mode,
234229
int buffering, const char *encoding, const char *errors,
235230
const char *newline, int closefd, PyObject *opener)
236-
/*[clinic end generated code: output=aefafc4ce2b46dc0 input=7295902222e6b311]*/
231+
/*[clinic end generated code: output=aefafc4ce2b46dc0 input=1543f4511d2356a5]*/
237232
{
238233
unsigned i;
239234

240235
int creating = 0, reading = 0, writing = 0, appending = 0, updating = 0;
241-
int text = 0, binary = 0, universal = 0;
236+
int text = 0, binary = 0;
242237

243238
char rawmode[6], *m;
244239
int line_buffering, is_number;
@@ -296,10 +291,6 @@ _io_open_impl(PyObject *module, PyObject *file, const char *mode,
296291
case 'b':
297292
binary = 1;
298293
break;
299-
case 'U':
300-
universal = 1;
301-
reading = 1;
302-
break;
303294
default:
304295
goto invalid_mode;
305296
}
@@ -322,18 +313,6 @@ _io_open_impl(PyObject *module, PyObject *file, const char *mode,
322313
*m = '\0';
323314

324315
/* Parameters validation */
325-
if (universal) {
326-
if (creating || writing || appending || updating) {
327-
PyErr_SetString(PyExc_ValueError,
328-
"mode U cannot be combined with 'x', 'w', 'a', or '+'");
329-
goto error;
330-
}
331-
if (PyErr_WarnEx(PyExc_DeprecationWarning,
332-
"'U' mode is deprecated", 1) < 0)
333-
goto error;
334-
reading = 1;
335-
}
336-
337316
if (text && binary) {
338317
PyErr_SetString(PyExc_ValueError,
339318
"can't have text and binary mode at once");

Modules/_io/clinic/_iomodule.c.h

+1-6
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ PyDoc_STRVAR(_io_open__doc__,
3636
"\'b\' binary mode\n"
3737
"\'t\' text mode (default)\n"
3838
"\'+\' open a disk file for updating (reading and writing)\n"
39-
"\'U\' universal newline mode (deprecated)\n"
4039
"========= ===============================================================\n"
4140
"\n"
4241
"The default mode is \'rt\' (open for reading text). For binary random\n"
@@ -52,10 +51,6 @@ PyDoc_STRVAR(_io_open__doc__,
5251
"returned as strings, the bytes having been first decoded using a\n"
5352
"platform-dependent encoding or using the specified encoding if given.\n"
5453
"\n"
55-
"\'U\' mode is deprecated and will raise an exception in future versions\n"
56-
"of Python. It has no effect in Python 3. Use newline to control\n"
57-
"universal newlines mode.\n"
58-
"\n"
5954
"buffering is an optional integer used to set the buffering policy.\n"
6055
"Pass 0 to switch buffering off (only allowed in binary mode), 1 to select\n"
6156
"line buffering (only usable in text mode), and an integer > 1 to indicate\n"
@@ -359,4 +354,4 @@ _io_open_code(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec
359354
exit:
360355
return return_value;
361356
}
362-
/*[clinic end generated code: output=06e055d1d80b835d input=a9049054013a1b77]*/
357+
/*[clinic end generated code: output=6ea315343f6a94ba input=a9049054013a1b77]*/

0 commit comments

Comments
 (0)