mirror of
https://github.com/python/cpython.git
synced 2025-09-26 10:19:53 +00:00
Issue #27029: Removed deprecated support of universal newlines mode from ZipFile.open().
This commit is contained in:
parent
e6dae877dc
commit
e670be2273
5 changed files with 24 additions and 222 deletions
|
@ -204,18 +204,13 @@ ZipFile Objects
|
||||||
Return a list of archive members by name.
|
Return a list of archive members by name.
|
||||||
|
|
||||||
|
|
||||||
.. index::
|
|
||||||
single: universal newlines; zipfile.ZipFile.open method
|
|
||||||
|
|
||||||
.. method:: ZipFile.open(name, mode='r', pwd=None, *, force_zip64=False)
|
.. method:: ZipFile.open(name, mode='r', pwd=None, *, force_zip64=False)
|
||||||
|
|
||||||
Access a member of the archive as a file-like object. *name*
|
Access a member of the archive as a binary file-like object. *name*
|
||||||
is the name of the file in the archive, or a :class:`ZipInfo` object. The
|
can be either the name of a file within the archive or a :class:`ZipInfo`
|
||||||
*mode* parameter, if included, must be one of the following: ``'r'`` (the
|
object. The *mode* parameter, if included, must be ``'r'`` (the default)
|
||||||
default), ``'U'``, ``'rU'`` or ``'w'``. Choosing ``'U'`` or ``'rU'`` will
|
or ``'w'``. *pwd* is the password used to decrypt encrypted ZIP files.
|
||||||
enable :term:`universal newlines` support in the read-only object. *pwd* is
|
Calling :meth:`.open` on a closed ZipFile will raise a :exc:`RuntimeError`.
|
||||||
the password used to decrypt encrypted ZIP files. Calling :meth:`.open` on
|
|
||||||
a closed ZipFile will raise a :exc:`RuntimeError`.
|
|
||||||
|
|
||||||
:meth:`~ZipFile.open` is also a context manager and therefore supports the
|
:meth:`~ZipFile.open` is also a context manager and therefore supports the
|
||||||
:keyword:`with` statement::
|
:keyword:`with` statement::
|
||||||
|
@ -224,7 +219,7 @@ ZipFile Objects
|
||||||
with myzip.open('eggs.txt') as myfile:
|
with myzip.open('eggs.txt') as myfile:
|
||||||
print(myfile.read())
|
print(myfile.read())
|
||||||
|
|
||||||
With *mode* ``'r'``, ``'U'`` or ``'rU'``, the file-like object
|
With *mode* ``'r'`` the file-like object
|
||||||
(``ZipExtFile``) is read-only and provides the following methods:
|
(``ZipExtFile``) is read-only and provides the following methods:
|
||||||
:meth:`~io.BufferedIOBase.read`, :meth:`~io.IOBase.readline`,
|
:meth:`~io.BufferedIOBase.read`, :meth:`~io.IOBase.readline`,
|
||||||
:meth:`~io.IOBase.readlines`, :meth:`__iter__`,
|
:meth:`~io.IOBase.readlines`, :meth:`__iter__`,
|
||||||
|
@ -248,8 +243,8 @@ ZipFile Objects
|
||||||
or a :class:`ZipInfo` object. You will appreciate this when trying to read a
|
or a :class:`ZipInfo` object. You will appreciate this when trying to read a
|
||||||
ZIP file that contains members with duplicate names.
|
ZIP file that contains members with duplicate names.
|
||||||
|
|
||||||
.. deprecated-removed:: 3.4 3.6
|
.. versionchanged:: 3.6
|
||||||
The ``'U'`` or ``'rU'`` mode. Use :class:`io.TextIOWrapper` for reading
|
Removed support of ``mode='U'``. Use :class:`io.TextIOWrapper` for reading
|
||||||
compressed text files in :term:`universal newlines` mode.
|
compressed text files in :term:`universal newlines` mode.
|
||||||
|
|
||||||
.. versionchanged:: 3.6
|
.. versionchanged:: 3.6
|
||||||
|
|
|
@ -580,6 +580,11 @@ API and Feature Removals
|
||||||
:mod:`tkinter` widget classes were removed (corresponding Tk commands
|
:mod:`tkinter` widget classes were removed (corresponding Tk commands
|
||||||
were obsolete since Tk 4.0).
|
were obsolete since Tk 4.0).
|
||||||
|
|
||||||
|
* The :meth:`~zipfile.ZipFile.open` method of the :class:`zipfile.ZipFile`
|
||||||
|
class no longer supports the ``'U'`` mode (was deprecated since Python 3.4).
|
||||||
|
Use :class:`io.TextIOWrapper` for reading compressed text files in
|
||||||
|
:term:`universal newlines` mode.
|
||||||
|
|
||||||
|
|
||||||
Porting to Python 3.6
|
Porting to Python 3.6
|
||||||
=====================
|
=====================
|
||||||
|
|
|
@ -38,10 +38,6 @@ def get_files(test):
|
||||||
yield f
|
yield f
|
||||||
test.assertFalse(f.closed)
|
test.assertFalse(f.closed)
|
||||||
|
|
||||||
def openU(zipfp, fn):
|
|
||||||
with check_warnings(('', DeprecationWarning)):
|
|
||||||
return zipfp.open(fn, 'rU')
|
|
||||||
|
|
||||||
class AbstractTestsWithSourceFile:
|
class AbstractTestsWithSourceFile:
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
|
@ -1035,32 +1031,6 @@ class OtherTests(unittest.TestCase):
|
||||||
data += zipfp.read(info)
|
data += zipfp.read(info)
|
||||||
self.assertIn(data, {b"foobar", b"barfoo"})
|
self.assertIn(data, {b"foobar", b"barfoo"})
|
||||||
|
|
||||||
def test_universal_deprecation(self):
|
|
||||||
f = io.BytesIO()
|
|
||||||
with zipfile.ZipFile(f, "w") as zipfp:
|
|
||||||
zipfp.writestr('spam.txt', b'ababagalamaga')
|
|
||||||
|
|
||||||
with zipfile.ZipFile(f, "r") as zipfp:
|
|
||||||
for mode in 'U', 'rU':
|
|
||||||
with self.assertWarns(DeprecationWarning):
|
|
||||||
zipopen = zipfp.open('spam.txt', mode)
|
|
||||||
zipopen.close()
|
|
||||||
|
|
||||||
def test_universal_readaheads(self):
|
|
||||||
f = io.BytesIO()
|
|
||||||
|
|
||||||
data = b'a\r\n' * 16 * 1024
|
|
||||||
with zipfile.ZipFile(f, 'w', zipfile.ZIP_STORED) as zipfp:
|
|
||||||
zipfp.writestr(TESTFN, data)
|
|
||||||
|
|
||||||
data2 = b''
|
|
||||||
with zipfile.ZipFile(f, 'r') as zipfp, \
|
|
||||||
openU(zipfp, TESTFN) as zipopen:
|
|
||||||
for line in zipopen:
|
|
||||||
data2 += line
|
|
||||||
|
|
||||||
self.assertEqual(data, data2.replace(b'\n', b'\r\n'))
|
|
||||||
|
|
||||||
def test_writestr_extended_local_header_issue1202(self):
|
def test_writestr_extended_local_header_issue1202(self):
|
||||||
with zipfile.ZipFile(TESTFN2, 'w') as orig_zip:
|
with zipfile.ZipFile(TESTFN2, 'w') as orig_zip:
|
||||||
for data in 'abcdefghijklmnop':
|
for data in 'abcdefghijklmnop':
|
||||||
|
@ -1268,9 +1238,12 @@ class OtherTests(unittest.TestCase):
|
||||||
zipf.writestr("foo.txt", "O, for a Muse of Fire!")
|
zipf.writestr("foo.txt", "O, for a Muse of Fire!")
|
||||||
|
|
||||||
with zipfile.ZipFile(TESTFN, mode="r") as zipf:
|
with zipfile.ZipFile(TESTFN, mode="r") as zipf:
|
||||||
# read the data to make sure the file is there
|
# read the data to make sure the file is there
|
||||||
zipf.read("foo.txt")
|
zipf.read("foo.txt")
|
||||||
self.assertRaises(RuntimeError, zipf.open, "foo.txt", "q")
|
self.assertRaises(RuntimeError, zipf.open, "foo.txt", "q")
|
||||||
|
# universal newlines support is removed
|
||||||
|
self.assertRaises(RuntimeError, zipf.open, "foo.txt", "U")
|
||||||
|
self.assertRaises(RuntimeError, zipf.open, "foo.txt", "rU")
|
||||||
|
|
||||||
def test_read0(self):
|
def test_read0(self):
|
||||||
"""Check that calling read(0) on a ZipExtFile object returns an empty
|
"""Check that calling read(0) on a ZipExtFile object returns an empty
|
||||||
|
@ -2011,138 +1984,6 @@ class TestWithDirectory(unittest.TestCase):
|
||||||
unlink(TESTFN)
|
unlink(TESTFN)
|
||||||
|
|
||||||
|
|
||||||
class AbstractUniversalNewlineTests:
|
|
||||||
@classmethod
|
|
||||||
def setUpClass(cls):
|
|
||||||
cls.line_gen = [bytes("Test of zipfile line %d." % i, "ascii")
|
|
||||||
for i in range(FIXEDTEST_SIZE)]
|
|
||||||
cls.seps = (b'\r', b'\r\n', b'\n')
|
|
||||||
cls.arcdata = {}
|
|
||||||
for n, s in enumerate(cls.seps):
|
|
||||||
cls.arcdata[s] = s.join(cls.line_gen) + s
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
self.arcfiles = {}
|
|
||||||
for n, s in enumerate(self.seps):
|
|
||||||
self.arcfiles[s] = '%s-%d' % (TESTFN, n)
|
|
||||||
with open(self.arcfiles[s], "wb") as f:
|
|
||||||
f.write(self.arcdata[s])
|
|
||||||
|
|
||||||
def make_test_archive(self, f, compression):
|
|
||||||
# Create the ZIP archive
|
|
||||||
with zipfile.ZipFile(f, "w", compression) as zipfp:
|
|
||||||
for fn in self.arcfiles.values():
|
|
||||||
zipfp.write(fn, fn)
|
|
||||||
|
|
||||||
def read_test(self, f, compression):
|
|
||||||
self.make_test_archive(f, compression)
|
|
||||||
|
|
||||||
# Read the ZIP archive
|
|
||||||
with zipfile.ZipFile(f, "r") as zipfp:
|
|
||||||
for sep, fn in self.arcfiles.items():
|
|
||||||
with openU(zipfp, fn) as fp:
|
|
||||||
zipdata = fp.read()
|
|
||||||
self.assertEqual(self.arcdata[sep], zipdata)
|
|
||||||
|
|
||||||
def test_read(self):
|
|
||||||
for f in get_files(self):
|
|
||||||
self.read_test(f, self.compression)
|
|
||||||
|
|
||||||
def readline_read_test(self, f, compression):
|
|
||||||
self.make_test_archive(f, compression)
|
|
||||||
|
|
||||||
# Read the ZIP archive
|
|
||||||
with zipfile.ZipFile(f, "r") as zipfp:
|
|
||||||
for sep, fn in self.arcfiles.items():
|
|
||||||
with openU(zipfp, fn) as zipopen:
|
|
||||||
data = b''
|
|
||||||
while True:
|
|
||||||
read = zipopen.readline()
|
|
||||||
if not read:
|
|
||||||
break
|
|
||||||
data += read
|
|
||||||
|
|
||||||
read = zipopen.read(5)
|
|
||||||
if not read:
|
|
||||||
break
|
|
||||||
data += read
|
|
||||||
|
|
||||||
self.assertEqual(data, self.arcdata[b'\n'])
|
|
||||||
|
|
||||||
def test_readline_read(self):
|
|
||||||
for f in get_files(self):
|
|
||||||
self.readline_read_test(f, self.compression)
|
|
||||||
|
|
||||||
def readline_test(self, f, compression):
|
|
||||||
self.make_test_archive(f, compression)
|
|
||||||
|
|
||||||
# Read the ZIP archive
|
|
||||||
with zipfile.ZipFile(f, "r") as zipfp:
|
|
||||||
for sep, fn in self.arcfiles.items():
|
|
||||||
with openU(zipfp, fn) as zipopen:
|
|
||||||
for line in self.line_gen:
|
|
||||||
linedata = zipopen.readline()
|
|
||||||
self.assertEqual(linedata, line + b'\n')
|
|
||||||
|
|
||||||
def test_readline(self):
|
|
||||||
for f in get_files(self):
|
|
||||||
self.readline_test(f, self.compression)
|
|
||||||
|
|
||||||
def readlines_test(self, f, compression):
|
|
||||||
self.make_test_archive(f, compression)
|
|
||||||
|
|
||||||
# Read the ZIP archive
|
|
||||||
with zipfile.ZipFile(f, "r") as zipfp:
|
|
||||||
for sep, fn in self.arcfiles.items():
|
|
||||||
with openU(zipfp, fn) as fp:
|
|
||||||
ziplines = fp.readlines()
|
|
||||||
for line, zipline in zip(self.line_gen, ziplines):
|
|
||||||
self.assertEqual(zipline, line + b'\n')
|
|
||||||
|
|
||||||
def test_readlines(self):
|
|
||||||
for f in get_files(self):
|
|
||||||
self.readlines_test(f, self.compression)
|
|
||||||
|
|
||||||
def iterlines_test(self, f, compression):
|
|
||||||
self.make_test_archive(f, compression)
|
|
||||||
|
|
||||||
# Read the ZIP archive
|
|
||||||
with zipfile.ZipFile(f, "r") as zipfp:
|
|
||||||
for sep, fn in self.arcfiles.items():
|
|
||||||
with openU(zipfp, fn) as fp:
|
|
||||||
for line, zipline in zip(self.line_gen, fp):
|
|
||||||
self.assertEqual(zipline, line + b'\n')
|
|
||||||
|
|
||||||
def test_iterlines(self):
|
|
||||||
for f in get_files(self):
|
|
||||||
self.iterlines_test(f, self.compression)
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
for sep, fn in self.arcfiles.items():
|
|
||||||
unlink(fn)
|
|
||||||
unlink(TESTFN)
|
|
||||||
unlink(TESTFN2)
|
|
||||||
|
|
||||||
|
|
||||||
class StoredUniversalNewlineTests(AbstractUniversalNewlineTests,
|
|
||||||
unittest.TestCase):
|
|
||||||
compression = zipfile.ZIP_STORED
|
|
||||||
|
|
||||||
@requires_zlib
|
|
||||||
class DeflateUniversalNewlineTests(AbstractUniversalNewlineTests,
|
|
||||||
unittest.TestCase):
|
|
||||||
compression = zipfile.ZIP_DEFLATED
|
|
||||||
|
|
||||||
@requires_bz2
|
|
||||||
class Bzip2UniversalNewlineTests(AbstractUniversalNewlineTests,
|
|
||||||
unittest.TestCase):
|
|
||||||
compression = zipfile.ZIP_BZIP2
|
|
||||||
|
|
||||||
@requires_lzma
|
|
||||||
class LzmaUniversalNewlineTests(AbstractUniversalNewlineTests,
|
|
||||||
unittest.TestCase):
|
|
||||||
compression = zipfile.ZIP_LZMA
|
|
||||||
|
|
||||||
class ZipInfoTests(unittest.TestCase):
|
class ZipInfoTests(unittest.TestCase):
|
||||||
def test_from_file(self):
|
def test_from_file(self):
|
||||||
zi = zipfile.ZipInfo.from_file(__file__)
|
zi = zipfile.ZipInfo.from_file(__file__)
|
||||||
|
|
|
@ -743,9 +743,6 @@ class ZipExtFile(io.BufferedIOBase):
|
||||||
# Read from compressed files in 4k blocks.
|
# Read from compressed files in 4k blocks.
|
||||||
MIN_READ_SIZE = 4096
|
MIN_READ_SIZE = 4096
|
||||||
|
|
||||||
# Search for universal newlines or line chunks.
|
|
||||||
PATTERN = re.compile(br'^(?P<chunk>[^\r\n]+)|(?P<newline>\n|\r\n?)')
|
|
||||||
|
|
||||||
def __init__(self, fileobj, mode, zipinfo, decrypter=None,
|
def __init__(self, fileobj, mode, zipinfo, decrypter=None,
|
||||||
close_fileobj=False):
|
close_fileobj=False):
|
||||||
self._fileobj = fileobj
|
self._fileobj = fileobj
|
||||||
|
@ -762,7 +759,6 @@ class ZipExtFile(io.BufferedIOBase):
|
||||||
self._readbuffer = b''
|
self._readbuffer = b''
|
||||||
self._offset = 0
|
self._offset = 0
|
||||||
|
|
||||||
self._universal = 'U' in mode
|
|
||||||
self.newlines = None
|
self.newlines = None
|
||||||
|
|
||||||
# Adjust read size for encrypted files since the first 12 bytes
|
# Adjust read size for encrypted files since the first 12 bytes
|
||||||
|
@ -799,7 +795,7 @@ class ZipExtFile(io.BufferedIOBase):
|
||||||
If limit is specified, at most limit bytes will be read.
|
If limit is specified, at most limit bytes will be read.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not self._universal and limit < 0:
|
if limit < 0:
|
||||||
# Shortcut common case - newline found in buffer.
|
# Shortcut common case - newline found in buffer.
|
||||||
i = self._readbuffer.find(b'\n', self._offset) + 1
|
i = self._readbuffer.find(b'\n', self._offset) + 1
|
||||||
if i > 0:
|
if i > 0:
|
||||||
|
@ -807,41 +803,7 @@ class ZipExtFile(io.BufferedIOBase):
|
||||||
self._offset = i
|
self._offset = i
|
||||||
return line
|
return line
|
||||||
|
|
||||||
if not self._universal:
|
return io.BufferedIOBase.readline(self, limit)
|
||||||
return io.BufferedIOBase.readline(self, limit)
|
|
||||||
|
|
||||||
line = b''
|
|
||||||
while limit < 0 or len(line) < limit:
|
|
||||||
readahead = self.peek(2)
|
|
||||||
if readahead == b'':
|
|
||||||
return line
|
|
||||||
|
|
||||||
#
|
|
||||||
# Search for universal newlines or line chunks.
|
|
||||||
#
|
|
||||||
# The pattern returns either a line chunk or a newline, but not
|
|
||||||
# both. Combined with peek(2), we are assured that the sequence
|
|
||||||
# '\r\n' is always retrieved completely and never split into
|
|
||||||
# separate newlines - '\r', '\n' due to coincidental readaheads.
|
|
||||||
#
|
|
||||||
match = self.PATTERN.search(readahead)
|
|
||||||
newline = match.group('newline')
|
|
||||||
if newline is not None:
|
|
||||||
if self.newlines is None:
|
|
||||||
self.newlines = []
|
|
||||||
if newline not in self.newlines:
|
|
||||||
self.newlines.append(newline)
|
|
||||||
self._offset += len(newline)
|
|
||||||
return line + b'\n'
|
|
||||||
|
|
||||||
chunk = match.group('chunk')
|
|
||||||
if limit >= 0:
|
|
||||||
chunk = chunk[: limit - len(line)]
|
|
||||||
|
|
||||||
self._offset += len(chunk)
|
|
||||||
line += chunk
|
|
||||||
|
|
||||||
return line
|
|
||||||
|
|
||||||
def peek(self, n=1):
|
def peek(self, n=1):
|
||||||
"""Returns buffered bytes without advancing the position."""
|
"""Returns buffered bytes without advancing the position."""
|
||||||
|
@ -1360,12 +1322,8 @@ class ZipFile:
|
||||||
files. If the size is known in advance, it is best to pass a ZipInfo
|
files. If the size is known in advance, it is best to pass a ZipInfo
|
||||||
instance for name, with zinfo.file_size set.
|
instance for name, with zinfo.file_size set.
|
||||||
"""
|
"""
|
||||||
if mode not in {"r", "w", "U", "rU"}:
|
if mode not in {"r", "w"}:
|
||||||
raise RuntimeError('open() requires mode "r", "w", "U", or "rU"')
|
raise RuntimeError('open() requires mode "r" or "w"')
|
||||||
if 'U' in mode:
|
|
||||||
import warnings
|
|
||||||
warnings.warn("'U' mode is deprecated",
|
|
||||||
DeprecationWarning, 2)
|
|
||||||
if pwd and not isinstance(pwd, bytes):
|
if pwd and not isinstance(pwd, bytes):
|
||||||
raise TypeError("pwd: expected bytes, got %s" % type(pwd))
|
raise TypeError("pwd: expected bytes, got %s" % type(pwd))
|
||||||
if pwd and (mode == "w"):
|
if pwd and (mode == "w"):
|
||||||
|
|
|
@ -38,6 +38,9 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #27029: Removed deprecated support of universal newlines mode from
|
||||||
|
ZipFile.open().
|
||||||
|
|
||||||
- Issue #27030: Unknown escapes consisting of ``'\'`` and ASCII letter in
|
- Issue #27030: Unknown escapes consisting of ``'\'`` and ASCII letter in
|
||||||
regular expressions now are errors. The re.LOCALE flag now can be used
|
regular expressions now are errors. The re.LOCALE flag now can be used
|
||||||
only with bytes patterns.
|
only with bytes patterns.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue