Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e670be2

Browse files
Issue #27029: Removed deprecated support of universal newlines mode from ZipFile.open().
1 parent e6dae87 commit e670be2

5 files changed

Lines changed: 24 additions & 222 deletions

File tree

Doc/library/zipfile.rst

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -204,18 +204,13 @@ ZipFile Objects
204204
Return a list of archive members by name.
205205

206206

207-
.. index::
208-
single: universal newlines; zipfile.ZipFile.open method
209-
210207
.. method:: ZipFile.open(name, mode='r', pwd=None, *, force_zip64=False)
211208

212-
Access a member of the archive as a file-like object. *name*
213-
is the name of the file in the archive, or a :class:`ZipInfo` object. The
214-
*mode* parameter, if included, must be one of the following: ``'r'`` (the
215-
default), ``'U'``, ``'rU'`` or ``'w'``. Choosing ``'U'`` or ``'rU'`` will
216-
enable :term:`universal newlines` support in the read-only object. *pwd* is
217-
the password used to decrypt encrypted ZIP files. Calling :meth:`.open` on
218-
a closed ZipFile will raise a :exc:`RuntimeError`.
209+
Access a member of the archive as a binary file-like object. *name*
210+
can be either the name of a file within the archive or a :class:`ZipInfo`
211+
object. The *mode* parameter, if included, must be ``'r'`` (the default)
212+
or ``'w'``. *pwd* is the password used to decrypt encrypted ZIP files.
213+
Calling :meth:`.open` on a closed ZipFile will raise a :exc:`RuntimeError`.
219214

220215
:meth:`~ZipFile.open` is also a context manager and therefore supports the
221216
:keyword:`with` statement::
@@ -224,7 +219,7 @@ ZipFile Objects
224219
with myzip.open('eggs.txt') as myfile:
225220
print(myfile.read())
226221

227-
With *mode* ``'r'``, ``'U'`` or ``'rU'``, the file-like object
222+
With *mode* ``'r'`` the file-like object
228223
(``ZipExtFile``) is read-only and provides the following methods:
229224
:meth:`~io.BufferedIOBase.read`, :meth:`~io.IOBase.readline`,
230225
:meth:`~io.IOBase.readlines`, :meth:`__iter__`,
@@ -248,8 +243,8 @@ ZipFile Objects
248243
or a :class:`ZipInfo` object. You will appreciate this when trying to read a
249244
ZIP file that contains members with duplicate names.
250245

251-
.. deprecated-removed:: 3.4 3.6
252-
The ``'U'`` or ``'rU'`` mode. Use :class:`io.TextIOWrapper` for reading
246+
.. versionchanged:: 3.6
247+
Removed support of ``mode='U'``. Use :class:`io.TextIOWrapper` for reading
253248
compressed text files in :term:`universal newlines` mode.
254249

255250
.. versionchanged:: 3.6

Doc/whatsnew/3.6.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,11 @@ API and Feature Removals
580580
:mod:`tkinter` widget classes were removed (corresponding Tk commands
581581
were obsolete since Tk 4.0).
582582

583+
* The :meth:`~zipfile.ZipFile.open` method of the :class:`zipfile.ZipFile`
584+
class no longer supports the ``'U'`` mode (was deprecated since Python 3.4).
585+
Use :class:`io.TextIOWrapper` for reading compressed text files in
586+
:term:`universal newlines` mode.
587+
583588

584589
Porting to Python 3.6
585590
=====================

Lib/test/test_zipfile.py

Lines changed: 4 additions & 163 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,6 @@ def get_files(test):
3838
yield f
3939
test.assertFalse(f.closed)
4040

41-
def openU(zipfp, fn):
42-
with check_warnings(('', DeprecationWarning)):
43-
return zipfp.open(fn, 'rU')
44-
4541
class AbstractTestsWithSourceFile:
4642
@classmethod
4743
def setUpClass(cls):
@@ -1035,32 +1031,6 @@ def test_open_via_zip_info(self):
10351031
data += zipfp.read(info)
10361032
self.assertIn(data, {b"foobar", b"barfoo"})
10371033

1038-
def test_universal_deprecation(self):
1039-
f = io.BytesIO()
1040-
with zipfile.ZipFile(f, "w") as zipfp:
1041-
zipfp.writestr('spam.txt', b'ababagalamaga')
1042-
1043-
with zipfile.ZipFile(f, "r") as zipfp:
1044-
for mode in 'U', 'rU':
1045-
with self.assertWarns(DeprecationWarning):
1046-
zipopen = zipfp.open('spam.txt', mode)
1047-
zipopen.close()
1048-
1049-
def test_universal_readaheads(self):
1050-
f = io.BytesIO()
1051-
1052-
data = b'a\r\n' * 16 * 1024
1053-
with zipfile.ZipFile(f, 'w', zipfile.ZIP_STORED) as zipfp:
1054-
zipfp.writestr(TESTFN, data)
1055-
1056-
data2 = b''
1057-
with zipfile.ZipFile(f, 'r') as zipfp, \
1058-
openU(zipfp, TESTFN) as zipopen:
1059-
for line in zipopen:
1060-
data2 += line
1061-
1062-
self.assertEqual(data, data2.replace(b'\n', b'\r\n'))
1063-
10641034
def test_writestr_extended_local_header_issue1202(self):
10651035
with zipfile.ZipFile(TESTFN2, 'w') as orig_zip:
10661036
for data in 'abcdefghijklmnop':
@@ -1268,9 +1238,12 @@ def test_bad_open_mode(self):
12681238
zipf.writestr("foo.txt", "O, for a Muse of Fire!")
12691239

12701240
with zipfile.ZipFile(TESTFN, mode="r") as zipf:
1271-
# read the data to make sure the file is there
1241+
# read the data to make sure the file is there
12721242
zipf.read("foo.txt")
12731243
self.assertRaises(RuntimeError, zipf.open, "foo.txt", "q")
1244+
# universal newlines support is removed
1245+
self.assertRaises(RuntimeError, zipf.open, "foo.txt", "U")
1246+
self.assertRaises(RuntimeError, zipf.open, "foo.txt", "rU")
12741247

12751248
def test_read0(self):
12761249
"""Check that calling read(0) on a ZipExtFile object returns an empty
@@ -2011,138 +1984,6 @@ def tearDown(self):
20111984
unlink(TESTFN)
20121985

20131986

2014-
class AbstractUniversalNewlineTests:
2015-
@classmethod
2016-
def setUpClass(cls):
2017-
cls.line_gen = [bytes("Test of zipfile line %d." % i, "ascii")
2018-
for i in range(FIXEDTEST_SIZE)]
2019-
cls.seps = (b'\r', b'\r\n', b'\n')
2020-
cls.arcdata = {}
2021-
for n, s in enumerate(cls.seps):
2022-
cls.arcdata[s] = s.join(cls.line_gen) + s
2023-
2024-
def setUp(self):
2025-
self.arcfiles = {}
2026-
for n, s in enumerate(self.seps):
2027-
self.arcfiles[s] = '%s-%d' % (TESTFN, n)
2028-
with open(self.arcfiles[s], "wb") as f:
2029-
f.write(self.arcdata[s])
2030-
2031-
def make_test_archive(self, f, compression):
2032-
# Create the ZIP archive
2033-
with zipfile.ZipFile(f, "w", compression) as zipfp:
2034-
for fn in self.arcfiles.values():
2035-
zipfp.write(fn, fn)
2036-
2037-
def read_test(self, f, compression):
2038-
self.make_test_archive(f, compression)
2039-
2040-
# Read the ZIP archive
2041-
with zipfile.ZipFile(f, "r") as zipfp:
2042-
for sep, fn in self.arcfiles.items():
2043-
with openU(zipfp, fn) as fp:
2044-
zipdata = fp.read()
2045-
self.assertEqual(self.arcdata[sep], zipdata)
2046-
2047-
def test_read(self):
2048-
for f in get_files(self):
2049-
self.read_test(f, self.compression)
2050-
2051-
def readline_read_test(self, f, compression):
2052-
self.make_test_archive(f, compression)
2053-
2054-
# Read the ZIP archive
2055-
with zipfile.ZipFile(f, "r") as zipfp:
2056-
for sep, fn in self.arcfiles.items():
2057-
with openU(zipfp, fn) as zipopen:
2058-
data = b''
2059-
while True:
2060-
read = zipopen.readline()
2061-
if not read:
2062-
break
2063-
data += read
2064-
2065-
read = zipopen.read(5)
2066-
if not read:
2067-
break
2068-
data += read
2069-
2070-
self.assertEqual(data, self.arcdata[b'\n'])
2071-
2072-
def test_readline_read(self):
2073-
for f in get_files(self):
2074-
self.readline_read_test(f, self.compression)
2075-
2076-
def readline_test(self, f, compression):
2077-
self.make_test_archive(f, compression)
2078-
2079-
# Read the ZIP archive
2080-
with zipfile.ZipFile(f, "r") as zipfp:
2081-
for sep, fn in self.arcfiles.items():
2082-
with openU(zipfp, fn) as zipopen:
2083-
for line in self.line_gen:
2084-
linedata = zipopen.readline()
2085-
self.assertEqual(linedata, line + b'\n')
2086-
2087-
def test_readline(self):
2088-
for f in get_files(self):
2089-
self.readline_test(f, self.compression)
2090-
2091-
def readlines_test(self, f, compression):
2092-
self.make_test_archive(f, compression)
2093-
2094-
# Read the ZIP archive
2095-
with zipfile.ZipFile(f, "r") as zipfp:
2096-
for sep, fn in self.arcfiles.items():
2097-
with openU(zipfp, fn) as fp:
2098-
ziplines = fp.readlines()
2099-
for line, zipline in zip(self.line_gen, ziplines):
2100-
self.assertEqual(zipline, line + b'\n')
2101-
2102-
def test_readlines(self):
2103-
for f in get_files(self):
2104-
self.readlines_test(f, self.compression)
2105-
2106-
def iterlines_test(self, f, compression):
2107-
self.make_test_archive(f, compression)
2108-
2109-
# Read the ZIP archive
2110-
with zipfile.ZipFile(f, "r") as zipfp:
2111-
for sep, fn in self.arcfiles.items():
2112-
with openU(zipfp, fn) as fp:
2113-
for line, zipline in zip(self.line_gen, fp):
2114-
self.assertEqual(zipline, line + b'\n')
2115-
2116-
def test_iterlines(self):
2117-
for f in get_files(self):
2118-
self.iterlines_test(f, self.compression)
2119-
2120-
def tearDown(self):
2121-
for sep, fn in self.arcfiles.items():
2122-
unlink(fn)
2123-
unlink(TESTFN)
2124-
unlink(TESTFN2)
2125-
2126-
2127-
class StoredUniversalNewlineTests(AbstractUniversalNewlineTests,
2128-
unittest.TestCase):
2129-
compression = zipfile.ZIP_STORED
2130-
2131-
@requires_zlib
2132-
class DeflateUniversalNewlineTests(AbstractUniversalNewlineTests,
2133-
unittest.TestCase):
2134-
compression = zipfile.ZIP_DEFLATED
2135-
2136-
@requires_bz2
2137-
class Bzip2UniversalNewlineTests(AbstractUniversalNewlineTests,
2138-
unittest.TestCase):
2139-
compression = zipfile.ZIP_BZIP2
2140-
2141-
@requires_lzma
2142-
class LzmaUniversalNewlineTests(AbstractUniversalNewlineTests,
2143-
unittest.TestCase):
2144-
compression = zipfile.ZIP_LZMA
2145-
21461987
class ZipInfoTests(unittest.TestCase):
21471988
def test_from_file(self):
21481989
zi = zipfile.ZipInfo.from_file(__file__)

Lib/zipfile.py

Lines changed: 4 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -743,9 +743,6 @@ class ZipExtFile(io.BufferedIOBase):
743743
# Read from compressed files in 4k blocks.
744744
MIN_READ_SIZE = 4096
745745

746-
# Search for universal newlines or line chunks.
747-
PATTERN = re.compile(br'^(?P<chunk>[^\r\n]+)|(?P<newline>\n|\r\n?)')
748-
749746
def __init__(self, fileobj, mode, zipinfo, decrypter=None,
750747
close_fileobj=False):
751748
self._fileobj = fileobj
@@ -762,7 +759,6 @@ def __init__(self, fileobj, mode, zipinfo, decrypter=None,
762759
self._readbuffer = b''
763760
self._offset = 0
764761

765-
self._universal = 'U' in mode
766762
self.newlines = None
767763

768764
# Adjust read size for encrypted files since the first 12 bytes
@@ -799,49 +795,15 @@ def readline(self, limit=-1):
799795
If limit is specified, at most limit bytes will be read.
800796
"""
801797

802-
if not self._universal and limit < 0:
798+
if limit < 0:
803799
# Shortcut common case - newline found in buffer.
804800
i = self._readbuffer.find(b'\n', self._offset) + 1
805801
if i > 0:
806802
line = self._readbuffer[self._offset: i]
807803
self._offset = i
808804
return line
809805

810-
if not self._universal:
811-
return io.BufferedIOBase.readline(self, limit)
812-
813-
line = b''
814-
while limit < 0 or len(line) < limit:
815-
readahead = self.peek(2)
816-
if readahead == b'':
817-
return line
818-
819-
#
820-
# Search for universal newlines or line chunks.
821-
#
822-
# The pattern returns either a line chunk or a newline, but not
823-
# both. Combined with peek(2), we are assured that the sequence
824-
# '\r\n' is always retrieved completely and never split into
825-
# separate newlines - '\r', '\n' due to coincidental readaheads.
826-
#
827-
match = self.PATTERN.search(readahead)
828-
newline = match.group('newline')
829-
if newline is not None:
830-
if self.newlines is None:
831-
self.newlines = []
832-
if newline not in self.newlines:
833-
self.newlines.append(newline)
834-
self._offset += len(newline)
835-
return line + b'\n'
836-
837-
chunk = match.group('chunk')
838-
if limit >= 0:
839-
chunk = chunk[: limit - len(line)]
840-
841-
self._offset += len(chunk)
842-
line += chunk
843-
844-
return line
806+
return io.BufferedIOBase.readline(self, limit)
845807

846808
def peek(self, n=1):
847809
"""Returns buffered bytes without advancing the position."""
@@ -1360,12 +1322,8 @@ def open(self, name, mode="r", pwd=None, *, force_zip64=False):
13601322
files. If the size is known in advance, it is best to pass a ZipInfo
13611323
instance for name, with zinfo.file_size set.
13621324
"""
1363-
if mode not in {"r", "w", "U", "rU"}:
1364-
raise RuntimeError('open() requires mode "r", "w", "U", or "rU"')
1365-
if 'U' in mode:
1366-
import warnings
1367-
warnings.warn("'U' mode is deprecated",
1368-
DeprecationWarning, 2)
1325+
if mode not in {"r", "w"}:
1326+
raise RuntimeError('open() requires mode "r" or "w"')
13691327
if pwd and not isinstance(pwd, bytes):
13701328
raise TypeError("pwd: expected bytes, got %s" % type(pwd))
13711329
if pwd and (mode == "w"):

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ Core and Builtins
3838
Library
3939
-------
4040

41+
- Issue #27029: Removed deprecated support of universal newlines mode from
42+
ZipFile.open().
43+
4144
- Issue #27030: Unknown escapes consisting of ``'\'`` and ASCII letter in
4245
regular expressions now are errors. The re.LOCALE flag now can be used
4346
only with bytes patterns.

0 commit comments

Comments
 (0)