Skip to content

Commit e471e72

Browse files
authored
bpo-37330: open() no longer accept 'U' in file mode (GH-16959)
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 3bfc8e0 commit e471e72

File tree

13 files changed

+57
-96
lines changed

13 files changed

+57
-96
lines changed

Doc/library/codecs.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,9 @@ wider range of codecs when working with binary files:
197197
*buffering* has the same meaning as for the built-in :func:`open` function.
198198
It defaults to -1 which means that the default buffer size will be used.
199199

200+
.. versionchanged:: 3.9
201+
The ``'U'`` mode has been removed.
202+
200203

201204
.. function:: EncodedFile(file, data_encoding, file_encoding=None, errors='strict')
202205

Doc/library/fileinput.rst

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,8 @@ available for subclassing as well:
148148
The sequence must be accessed in strictly sequential order; random access
149149
and :meth:`~io.TextIOBase.readline` cannot be mixed.
150150

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

154154
The *openhook*, when given, must be a function that takes two arguments,
155155
*filename* and *mode*, and returns an accordingly opened file-like object. You
@@ -166,15 +166,14 @@ available for subclassing as well:
166166
.. versionchanged:: 3.2
167167
Can be used as a context manager.
168168

169-
.. deprecated:: 3.4
170-
The ``'rU'`` and ``'U'`` modes.
171-
172169
.. deprecated:: 3.8
173170
Support for :meth:`__getitem__` method is deprecated.
174171

175172
.. versionchanged:: 3.8
176173
The keyword parameter *mode* and *openhook* are now keyword-only.
177174

175+
.. versionchanged:: 3.9
176+
The ``'rU'`` and ``'U'`` modes have been removed.
178177

179178

180179
**Optional in-place filtering:** if the keyword argument ``inplace=True`` is

Doc/library/functions.rst

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,12 +1085,6 @@ are always available. They are listed here in alphabetical order.
10851085
first decoded using a platform-dependent encoding or using the specified
10861086
*encoding* if given.
10871087

1088-
There is an additional mode character permitted, ``'U'``, which no longer
1089-
has any effect, and is considered deprecated. It previously enabled
1090-
:term:`universal newlines` in text mode, which became the default behaviour
1091-
in Python 3.0. Refer to the documentation of the
1092-
:ref:`newline <open-newline-parameter>` parameter for further details.
1093-
10941088
.. note::
10951089

10961090
Python doesn't depend on the underlying operating system's notion of text
@@ -1247,10 +1241,6 @@ are always available. They are listed here in alphabetical order.
12471241

12481242
* The file is now non-inheritable.
12491243

1250-
.. deprecated-removed:: 3.4 4.0
1251-
1252-
The ``'U'`` mode.
1253-
12541244
.. versionchanged::
12551245
3.5
12561246

@@ -1266,6 +1256,10 @@ are always available. They are listed here in alphabetical order.
12661256
* On Windows, opening a console buffer may return a subclass of
12671257
:class:`io.RawIOBase` other than :class:`io.FileIO`.
12681258

1259+
.. versionchanged:: 3.9
1260+
The ``'U'`` mode has been removed.
1261+
1262+
12691263
.. function:: ord(c)
12701264

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

Doc/whatsnew/3.9.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,14 @@ that may require changes to your code.
284284
Changes in the Python API
285285
-------------------------
286286

287+
* :func:`open`, :func:`io.open`, :func:`codecs.open` and
288+
:class:`fileinput.FileInput` no longer accept ``'U'`` ("universal newline")
289+
in the file mode. This flag was deprecated since Python 3.3. In Python 3, the
290+
"universal newline" is used by default when a file is open in text mode. The
291+
:ref:`newline parameter <open-newline-parameter>` of :func:`open` controls
292+
how universal newlines works.
293+
(Contributed by Victor Stinner in :issue:`37330`.)
294+
287295
* :func:`__import__` and :func:`importlib.util.resolve_name` now raise
288296
:exc:`ImportError` where it previously raised :exc:`ValueError`. Callers
289297
catching the specific exception type and supporting both Python 3.9 and

Lib/_pyio.py

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None,
7171
'b' binary mode
7272
't' text mode (default)
7373
'+' open a disk file for updating (reading and writing)
74-
'U' universal newline mode (deprecated)
7574
========= ===============================================================
7675
7776
The default mode is 'rt' (open for reading text). For binary random
@@ -87,10 +86,6 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None,
8786
returned as strings, the bytes having been first decoded using a
8887
platform-dependent encoding or using the specified encoding if given.
8988
90-
'U' mode is deprecated and will raise an exception in future versions
91-
of Python. It has no effect in Python 3. Use newline to control
92-
universal newlines mode.
93-
9489
buffering is an optional integer used to set the buffering policy.
9590
Pass 0 to switch buffering off (only allowed in binary mode), 1 to select
9691
line buffering (only usable in text mode), and an integer > 1 to indicate
@@ -176,7 +171,7 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None,
176171
if errors is not None and not isinstance(errors, str):
177172
raise TypeError("invalid errors: %r" % errors)
178173
modes = set(mode)
179-
if modes - set("axrwb+tU") or len(mode) > len(modes):
174+
if modes - set("axrwb+t") or len(mode) > len(modes):
180175
raise ValueError("invalid mode: %r" % mode)
181176
creating = "x" in modes
182177
reading = "r" in modes
@@ -185,13 +180,6 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None,
185180
updating = "+" in modes
186181
text = "t" in modes
187182
binary = "b" in modes
188-
if "U" in modes:
189-
if creating or writing or appending or updating:
190-
raise ValueError("mode U cannot be combined with 'x', 'w', 'a', or '+'")
191-
import warnings
192-
warnings.warn("'U' mode is deprecated",
193-
DeprecationWarning, 2)
194-
reading = True
195183
if text and binary:
196184
raise ValueError("can't have text and binary mode at once")
197185
if creating + reading + writing + appending > 1:

Lib/fileinput.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -209,15 +209,10 @@ def __init__(self, files=None, inplace=False, backup="", *,
209209
self._isstdin = False
210210
self._backupfilename = None
211211
# restrict mode argument to reading modes
212-
if mode not in ('r', 'rU', 'U', 'rb'):
213-
raise ValueError("FileInput opening mode must be one of "
214-
"'r', 'rU', 'U' and 'rb'")
215-
if 'U' in mode:
216-
import warnings
217-
warnings.warn("'U' mode is deprecated",
218-
DeprecationWarning, 2)
212+
if mode not in ('r', 'rb'):
213+
raise ValueError("FileInput opening mode must be 'r' or 'rb'")
219214
self._mode = mode
220-
self._write_mode = mode.replace('r', 'w') if 'U' not in mode else 'w'
215+
self._write_mode = mode.replace('r', 'w')
221216
if openhook:
222217
if inplace:
223218
raise ValueError("FileInput cannot use an opening hook in inplace mode")

Lib/imp.py

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

Lib/test/test_codecs.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -712,11 +712,23 @@ def test_bug691291(self):
712712
self.addCleanup(support.unlink, support.TESTFN)
713713
with open(support.TESTFN, 'wb') as fp:
714714
fp.write(s)
715-
with support.check_warnings(('', DeprecationWarning)):
716-
reader = codecs.open(support.TESTFN, 'U', encoding=self.encoding)
717-
with reader:
715+
with codecs.open(support.TESTFN, 'r',
716+
encoding=self.encoding) as reader:
718717
self.assertEqual(reader.read(), s1)
719718

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

Lib/test/test_fileinput.py

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -226,19 +226,11 @@ def test_fileno(self):
226226
self.assertEqual(fi.fileno(), -1)
227227

228228
def test_opening_mode(self):
229-
try:
230-
# invalid mode, should raise ValueError
231-
fi = FileInput(mode="w")
232-
self.fail("FileInput should reject invalid mode argument")
233-
except ValueError:
234-
pass
235-
# try opening in universal newline mode
236-
t1 = self.writeTmp(b"A\nB\r\nC\rD", mode="wb")
237-
with check_warnings(('', DeprecationWarning)):
238-
fi = FileInput(files=t1, mode="U")
239-
with check_warnings(('', DeprecationWarning)):
240-
lines = list(fi)
241-
self.assertEqual(lines, ["A\n", "B\n", "C\n", "D"])
229+
# invalid modes
230+
for mode in ('w', 'rU', 'U'):
231+
with self.subTest(mode=mode):
232+
with self.assertRaises(ValueError):
233+
FileInput(mode=mode)
242234

243235
def test_stdin_binary_mode(self):
244236
with mock.patch('sys.stdin') as m_stdin:
@@ -985,10 +977,6 @@ def check(mode, expected_lines):
985977
self.assertEqual(lines, expected_lines)
986978

987979
check('r', ['A\n', 'B\n', 'C\n', 'D\u20ac'])
988-
with self.assertWarns(DeprecationWarning):
989-
check('rU', ['A\n', 'B\n', 'C\n', 'D\u20ac'])
990-
with self.assertWarns(DeprecationWarning):
991-
check('U', ['A\n', 'B\n', 'C\n', 'D\u20ac'])
992980
with self.assertRaises(ValueError):
993981
check('rb', ['A\n', 'B\r\n', 'C\r', 'D\u20ac'])
994982

Lib/test/test_io.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3886,16 +3886,6 @@ def test_attributes(self):
38863886
self.assertEqual(f.mode, "wb")
38873887
f.close()
38883888

3889-
with support.check_warnings(('', DeprecationWarning)):
3890-
f = self.open(support.TESTFN, "U")
3891-
self.assertEqual(f.name, support.TESTFN)
3892-
self.assertEqual(f.buffer.name, support.TESTFN)
3893-
self.assertEqual(f.buffer.raw.name, support.TESTFN)
3894-
self.assertEqual(f.mode, "U")
3895-
self.assertEqual(f.buffer.mode, "rb")
3896-
self.assertEqual(f.buffer.raw.mode, "rb")
3897-
f.close()
3898-
38993889
f = self.open(support.TESTFN, "w+")
39003890
self.assertEqual(f.mode, "w+")
39013891
self.assertEqual(f.buffer.mode, "rb+") # Does it really matter?
@@ -3909,6 +3899,13 @@ def test_attributes(self):
39093899
f.close()
39103900
g.close()
39113901

3902+
def test_removed_u_mode(self):
3903+
# "U" mode has been removed in Python 3.9
3904+
for mode in ("U", "rU", "r+U"):
3905+
with self.assertRaises(ValueError) as cm:
3906+
self.open(support.TESTFN, mode)
3907+
self.assertIn('invalid mode', str(cm.exception))
3908+
39123909
def test_io_after_close(self):
39133910
for kwargs in [
39143911
{"mode": "w"},

0 commit comments

Comments
 (0)