diff options
author | Victor Stinner <vstinner@python.org> | 2020-03-04 17:50:22 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-03-04 17:50:22 (GMT) |
commit | 942f7a2dea2e95a0fa848329565c0d0288d92e47 (patch) | |
tree | 31abda8d45ef676a46ea63a6a8a61ccf6061e9c5 | |
parent | 00c77ae55a82548a6b45af73cdf712ea34910645 (diff) | |
download | cpython-942f7a2dea2e95a0fa848329565c0d0288d92e47.zip cpython-942f7a2dea2e95a0fa848329565c0d0288d92e47.tar.gz cpython-942f7a2dea2e95a0fa848329565c0d0288d92e47.tar.bz2 |
bpo-39674: Revert "bpo-37330: open() no longer accept 'U' in file mode (GH-16959)" (GH-18767)
This reverts commit e471e72977c83664f13d041c78549140c86c92de.
The mode will be removed from Python 3.10.
-rw-r--r-- | Doc/library/codecs.rst | 3 | ||||
-rw-r--r-- | Doc/library/fileinput.rst | 9 | ||||
-rw-r--r-- | Doc/library/functions.rst | 14 | ||||
-rw-r--r-- | Doc/whatsnew/3.9.rst | 8 | ||||
-rw-r--r-- | Lib/_pyio.py | 14 | ||||
-rw-r--r-- | Lib/fileinput.py | 11 | ||||
-rw-r--r-- | Lib/imp.py | 2 | ||||
-rw-r--r-- | Lib/test/test_codecs.py | 18 | ||||
-rw-r--r-- | Lib/test/test_fileinput.py | 22 | ||||
-rw-r--r-- | Lib/test/test_io.py | 17 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2020-03-03-16-21-41.bpo-39674.HJVkD5.rst | 3 | ||||
-rw-r--r-- | Modules/_io/_iomodule.c | 25 | ||||
-rw-r--r-- | Modules/_io/clinic/_iomodule.c.h | 7 |
13 files changed, 99 insertions, 54 deletions
diff --git a/Doc/library/codecs.rst b/Doc/library/codecs.rst index ec6a053..f071057 100644 --- a/Doc/library/codecs.rst +++ b/Doc/library/codecs.rst @@ -197,9 +197,6 @@ wider range of codecs when working with binary files: *buffering* has the same meaning as for the built-in :func:`open` function. It defaults to -1 which means that the default buffer size will be used. - .. versionchanged:: 3.9 - The ``'U'`` mode has been removed. - .. function:: EncodedFile(file, data_encoding, file_encoding=None, errors='strict') diff --git a/Doc/library/fileinput.rst b/Doc/library/fileinput.rst index 236f5d5..cc4039a 100644 --- a/Doc/library/fileinput.rst +++ b/Doc/library/fileinput.rst @@ -148,8 +148,8 @@ available for subclassing as well: The sequence must be accessed in strictly sequential order; random access and :meth:`~io.TextIOBase.readline` cannot be mixed. - With *mode* you can specify which file mode will be passed to :func:`open`. - It must be ``'r'`` or ``'rb'``. + With *mode* you can specify which file mode will be passed to :func:`open`. It + must be one of ``'r'``, ``'rU'``, ``'U'`` and ``'rb'``. The *openhook*, when given, must be a function that takes two arguments, *filename* and *mode*, and returns an accordingly opened file-like object. You @@ -166,14 +166,15 @@ available for subclassing as well: .. versionchanged:: 3.2 Can be used as a context manager. + .. deprecated:: 3.4 + The ``'rU'`` and ``'U'`` modes. + .. deprecated:: 3.8 Support for :meth:`__getitem__` method is deprecated. .. versionchanged:: 3.8 The keyword parameter *mode* and *openhook* are now keyword-only. - .. versionchanged:: 3.9 - The ``'rU'`` and ``'U'`` modes have been removed. **Optional in-place filtering:** if the keyword argument ``inplace=True`` is diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index cc48597..ca09e6f 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -1090,6 +1090,12 @@ are always available. They are listed here in alphabetical order. first decoded using a platform-dependent encoding or using the specified *encoding* if given. + There is an additional mode character permitted, ``'U'``, which no longer + has any effect, and is considered deprecated. It previously enabled + :term:`universal newlines` in text mode, which became the default behaviour + in Python 3.0. Refer to the documentation of the + :ref:`newline <open-newline-parameter>` parameter for further details. + .. note:: Python doesn't depend on the underlying operating system's notion of text @@ -1246,6 +1252,10 @@ are always available. They are listed here in alphabetical order. * The file is now non-inheritable. + .. deprecated-removed:: 3.4 3.10 + + The ``'U'`` mode. + .. versionchanged:: 3.5 @@ -1261,10 +1271,6 @@ are always available. They are listed here in alphabetical order. * On Windows, opening a console buffer may return a subclass of :class:`io.RawIOBase` other than :class:`io.FileIO`. - .. versionchanged:: 3.9 - The ``'U'`` mode has been removed. - - .. function:: ord(c) Given a string representing one Unicode character, return an integer diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index d072b8d..a59de48 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -625,14 +625,6 @@ that may require changes to your code. Changes in the Python API ------------------------- -* :func:`open`, :func:`io.open`, :func:`codecs.open` and - :class:`fileinput.FileInput` no longer accept ``'U'`` ("universal newline") - in the file mode. This flag was deprecated since Python 3.3. In Python 3, the - "universal newline" is used by default when a file is open in text mode. The - :ref:`newline parameter <open-newline-parameter>` of :func:`open` controls - how universal newlines works. - (Contributed by Victor Stinner in :issue:`37330`.) - * :func:`__import__` and :func:`importlib.util.resolve_name` now raise :exc:`ImportError` where it previously raised :exc:`ValueError`. Callers catching the specific exception type and supporting both Python 3.9 and diff --git a/Lib/_pyio.py b/Lib/_pyio.py index 8eaa114..4804ed2 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -71,6 +71,7 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None, 'b' binary mode 't' text mode (default) '+' open a disk file for updating (reading and writing) + 'U' universal newline mode (deprecated) ========= =============================================================== The default mode is 'rt' (open for reading text). For binary random @@ -86,6 +87,10 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None, returned as strings, the bytes having been first decoded using a platform-dependent encoding or using the specified encoding if given. + 'U' mode is deprecated and will raise an exception in future versions + of Python. It has no effect in Python 3. Use newline to control + universal newlines mode. + buffering is an optional integer used to set the buffering policy. Pass 0 to switch buffering off (only allowed in binary mode), 1 to select line buffering (only usable in text mode), and an integer > 1 to indicate @@ -171,7 +176,7 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None, if errors is not None and not isinstance(errors, str): raise TypeError("invalid errors: %r" % errors) modes = set(mode) - if modes - set("axrwb+t") or len(mode) > len(modes): + if modes - set("axrwb+tU") or len(mode) > len(modes): raise ValueError("invalid mode: %r" % mode) creating = "x" in modes reading = "r" in modes @@ -180,6 +185,13 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None, updating = "+" in modes text = "t" in modes binary = "b" in modes + if "U" in modes: + if creating or writing or appending or updating: + raise ValueError("mode U cannot be combined with 'x', 'w', 'a', or '+'") + import warnings + warnings.warn("'U' mode is deprecated", + DeprecationWarning, 2) + reading = True if text and binary: raise ValueError("can't have text and binary mode at once") if creating + reading + writing + appending > 1: diff --git a/Lib/fileinput.py b/Lib/fileinput.py index 166c631..c1b0ec9 100644 --- a/Lib/fileinput.py +++ b/Lib/fileinput.py @@ -209,10 +209,15 @@ class FileInput: self._isstdin = False self._backupfilename = None # restrict mode argument to reading modes - if mode not in ('r', 'rb'): - raise ValueError("FileInput opening mode must be 'r' or 'rb'") + if mode not in ('r', 'rU', 'U', 'rb'): + raise ValueError("FileInput opening mode must be one of " + "'r', 'rU', 'U' and 'rb'") + if 'U' in mode: + import warnings + warnings.warn("'U' mode is deprecated", + DeprecationWarning, 2) self._mode = mode - self._write_mode = mode.replace('r', 'w') + self._write_mode = mode.replace('r', 'w') if 'U' not in mode else 'w' if openhook: if inplace: raise ValueError("FileInput cannot use an opening hook in inplace mode") @@ -225,7 +225,7 @@ def load_module(name, file, filename, details): """ suffix, mode, type_ = details - if mode and (not mode.startswith('r') or '+' in mode): + if mode and (not mode.startswith(('r', 'U')) or '+' in mode): raise ValueError('invalid file open mode {!r}'.format(mode)) elif file is None and type_ in {PY_SOURCE, PY_COMPILED}: msg = 'file object required for import (type code {})'.format(type_) diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py index dcdd574..54a3520 100644 --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -712,23 +712,11 @@ class UTF16Test(ReadTest, unittest.TestCase): self.addCleanup(support.unlink, support.TESTFN) with open(support.TESTFN, 'wb') as fp: fp.write(s) - with codecs.open(support.TESTFN, 'r', - encoding=self.encoding) as reader: + with support.check_warnings(('', DeprecationWarning)): + reader = codecs.open(support.TESTFN, 'U', encoding=self.encoding) + with 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(support.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(support.TESTFN, mode, encoding=self.encoding) - self.assertIn("can't have text and binary mode at once", - str(cm.exception)) - - class UTF16LETest(ReadTest, unittest.TestCase): encoding = "utf-16-le" ill_formed_sequence = b"\x80\xdc" diff --git a/Lib/test/test_fileinput.py b/Lib/test/test_fileinput.py index 819557d..014f19e 100644 --- a/Lib/test/test_fileinput.py +++ b/Lib/test/test_fileinput.py @@ -226,11 +226,19 @@ class FileInputTests(BaseTests, unittest.TestCase): self.assertEqual(fi.fileno(), -1) def test_opening_mode(self): - # invalid modes - for mode in ('w', 'rU', 'U'): - with self.subTest(mode=mode): - with self.assertRaises(ValueError): - FileInput(mode=mode) + try: + # invalid mode, should raise ValueError + fi = FileInput(mode="w") + self.fail("FileInput should reject invalid mode argument") + except ValueError: + pass + # try opening in universal newline mode + t1 = self.writeTmp(b"A\nB\r\nC\rD", mode="wb") + with check_warnings(('', DeprecationWarning)): + fi = FileInput(files=t1, mode="U") + with check_warnings(('', DeprecationWarning)): + lines = list(fi) + self.assertEqual(lines, ["A\n", "B\n", "C\n", "D"]) def test_stdin_binary_mode(self): with mock.patch('sys.stdin') as m_stdin: @@ -977,6 +985,10 @@ class Test_hook_encoded(unittest.TestCase): self.assertEqual(lines, expected_lines) check('r', ['A\n', 'B\n', 'C\n', 'D\u20ac']) + with self.assertWarns(DeprecationWarning): + check('rU', ['A\n', 'B\n', 'C\n', 'D\u20ac']) + with self.assertWarns(DeprecationWarning): + check('U', ['A\n', 'B\n', 'C\n', 'D\u20ac']) with self.assertRaises(ValueError): check('rb', ['A\n', 'B\r\n', 'C\r', 'D\u20ac']) diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index c27dfd9..4a7cbe5 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -3900,6 +3900,16 @@ class MiscIOTest(unittest.TestCase): self.assertEqual(f.mode, "wb") f.close() + with support.check_warnings(('', DeprecationWarning)): + f = self.open(support.TESTFN, "U") + self.assertEqual(f.name, support.TESTFN) + self.assertEqual(f.buffer.name, support.TESTFN) + self.assertEqual(f.buffer.raw.name, support.TESTFN) + self.assertEqual(f.mode, "U") + self.assertEqual(f.buffer.mode, "rb") + self.assertEqual(f.buffer.raw.mode, "rb") + f.close() + f = self.open(support.TESTFN, "w+") self.assertEqual(f.mode, "w+") self.assertEqual(f.buffer.mode, "rb+") # Does it really matter? @@ -3913,13 +3923,6 @@ class MiscIOTest(unittest.TestCase): f.close() g.close() - def test_removed_u_mode(self): - # "U" mode has been removed in Python 3.9 - for mode in ("U", "rU", "r+U"): - with self.assertRaises(ValueError) as cm: - self.open(support.TESTFN, mode) - self.assertIn('invalid mode', str(cm.exception)) - def test_open_pipe_with_append(self): # bpo-27805: Ignore ESPIPE from lseek() in open(). r, w = os.pipe() diff --git a/Misc/NEWS.d/next/Library/2020-03-03-16-21-41.bpo-39674.HJVkD5.rst b/Misc/NEWS.d/next/Library/2020-03-03-16-21-41.bpo-39674.HJVkD5.rst new file mode 100644 index 0000000..40f9c29 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-03-03-16-21-41.bpo-39674.HJVkD5.rst @@ -0,0 +1,3 @@ +Revert "bpo-37330: open() no longer accept 'U' in file mode". The "U" mode of +open() is kept in Python 3.9 to ease transition from Python 2.7, but will be +removed in Python 3.10. diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c index d609fa4..534d7de 100644 --- a/Modules/_io/_iomodule.c +++ b/Modules/_io/_iomodule.c @@ -138,6 +138,7 @@ Character Meaning 'b' binary mode 't' text mode (default) '+' open a disk file for updating (reading and writing) +'U' universal newline mode (deprecated) ========= =============================================================== The default mode is 'rt' (open for reading text). For binary random @@ -153,6 +154,10 @@ bytes objects without any decoding. In text mode (the default, or when returned as strings, the bytes having been first decoded using a platform-dependent encoding or using the specified encoding if given. +'U' mode is deprecated and will raise an exception in future versions +of Python. It has no effect in Python 3. Use newline to control +universal newlines mode. + buffering is an optional integer used to set the buffering policy. Pass 0 to switch buffering off (only allowed in binary mode), 1 to select line buffering (only usable in text mode), and an integer > 1 to indicate @@ -228,12 +233,12 @@ static PyObject * _io_open_impl(PyObject *module, PyObject *file, const char *mode, int buffering, const char *encoding, const char *errors, const char *newline, int closefd, PyObject *opener) -/*[clinic end generated code: output=aefafc4ce2b46dc0 input=1543f4511d2356a5]*/ +/*[clinic end generated code: output=aefafc4ce2b46dc0 input=7295902222e6b311]*/ { unsigned i; int creating = 0, reading = 0, writing = 0, appending = 0, updating = 0; - int text = 0, binary = 0; + int text = 0, binary = 0, universal = 0; char rawmode[6], *m; int line_buffering, is_number; @@ -291,6 +296,10 @@ _io_open_impl(PyObject *module, PyObject *file, const char *mode, case 'b': binary = 1; break; + case 'U': + universal = 1; + reading = 1; + break; default: goto invalid_mode; } @@ -313,6 +322,18 @@ _io_open_impl(PyObject *module, PyObject *file, const char *mode, *m = '\0'; /* Parameters validation */ + if (universal) { + if (creating || writing || appending || updating) { + PyErr_SetString(PyExc_ValueError, + "mode U cannot be combined with 'x', 'w', 'a', or '+'"); + goto error; + } + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "'U' mode is deprecated", 1) < 0) + goto error; + reading = 1; + } + if (text && binary) { PyErr_SetString(PyExc_ValueError, "can't have text and binary mode at once"); diff --git a/Modules/_io/clinic/_iomodule.c.h b/Modules/_io/clinic/_iomodule.c.h index c1f518f..1a9651d 100644 --- a/Modules/_io/clinic/_iomodule.c.h +++ b/Modules/_io/clinic/_iomodule.c.h @@ -36,6 +36,7 @@ PyDoc_STRVAR(_io_open__doc__, "\'b\' binary mode\n" "\'t\' text mode (default)\n" "\'+\' open a disk file for updating (reading and writing)\n" +"\'U\' universal newline mode (deprecated)\n" "========= ===============================================================\n" "\n" "The default mode is \'rt\' (open for reading text). For binary random\n" @@ -51,6 +52,10 @@ PyDoc_STRVAR(_io_open__doc__, "returned as strings, the bytes having been first decoded using a\n" "platform-dependent encoding or using the specified encoding if given.\n" "\n" +"\'U\' mode is deprecated and will raise an exception in future versions\n" +"of Python. It has no effect in Python 3. Use newline to control\n" +"universal newlines mode.\n" +"\n" "buffering is an optional integer used to set the buffering policy.\n" "Pass 0 to switch buffering off (only allowed in binary mode), 1 to select\n" "line buffering (only usable in text mode), and an integer > 1 to indicate\n" @@ -318,4 +323,4 @@ _io_open_code(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec exit: return return_value; } -/*[clinic end generated code: output=680e4b488c7da8a1 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=3df6bc6d91697545 input=a9049054013a1b77]*/ |