Commit b0d497c0 authored by Serhiy Storchaka's avatar Serhiy Storchaka

Issue #24693: Changed some RuntimeError's in the zipfile module to more

appropriate types. Improved some error messages and debugging output.
parent b32e869e
...@@ -147,10 +147,10 @@ ZipFile Objects ...@@ -147,10 +147,10 @@ ZipFile Objects
*compression* is the ZIP compression method to use when writing the archive, *compression* is the ZIP compression method to use when writing the archive,
and should be :const:`ZIP_STORED`, :const:`ZIP_DEFLATED`, and should be :const:`ZIP_STORED`, :const:`ZIP_DEFLATED`,
:const:`ZIP_BZIP2` or :const:`ZIP_LZMA`; unrecognized :const:`ZIP_BZIP2` or :const:`ZIP_LZMA`; unrecognized
values will cause :exc:`RuntimeError` to be raised. If :const:`ZIP_DEFLATED`, values will cause :exc:`NotImplementedError` to be raised. If :const:`ZIP_DEFLATED`,
:const:`ZIP_BZIP2` or :const:`ZIP_LZMA` is specified but the corresponding module :const:`ZIP_BZIP2` or :const:`ZIP_LZMA` is specified but the corresponding module
(:mod:`zlib`, :mod:`bz2` or :mod:`lzma`) is not available, :exc:`RuntimeError` (:mod:`zlib`, :mod:`bz2` or :mod:`lzma`) is not available, :exc:`RuntimeError`
is also raised. The default is :const:`ZIP_STORED`. If *allowZip64* is is raised. The default is :const:`ZIP_STORED`. If *allowZip64* is
``True`` (the default) zipfile will create ZIP files that use the ZIP64 ``True`` (the default) zipfile will create ZIP files that use the ZIP64
extensions when the zipfile is larger than 2 GiB. If it is false :mod:`zipfile` extensions when the zipfile is larger than 2 GiB. If it is false :mod:`zipfile`
will raise an exception when the ZIP file would require ZIP64 extensions. will raise an exception when the ZIP file would require ZIP64 extensions.
...@@ -179,6 +179,10 @@ ZipFile Objects ...@@ -179,6 +179,10 @@ ZipFile Objects
Added support for writing to unseekable streams. Added support for writing to unseekable streams.
Added support for the ``'x'`` mode. Added support for the ``'x'`` mode.
.. versionchanged:: 3.6
Previously, a plain :exc:`RuntimeError` was raised for unrecognized
compression values.
.. method:: ZipFile.close() .. method:: ZipFile.close()
...@@ -211,7 +215,6 @@ ZipFile Objects ...@@ -211,7 +215,6 @@ ZipFile Objects
can be either the name of a file within the archive or a :class:`ZipInfo` can be either the name of a file within the archive or a :class:`ZipInfo`
object. The *mode* parameter, if included, must be ``'r'`` (the default) object. The *mode* parameter, if included, must be ``'r'`` (the default)
or ``'w'``. *pwd* is the password used to decrypt encrypted ZIP files. or ``'w'``. *pwd* is 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::
...@@ -230,7 +233,7 @@ ZipFile Objects ...@@ -230,7 +233,7 @@ ZipFile Objects
With ``mode='w'``, a writable file handle is returned, which supports the With ``mode='w'``, a writable file handle is returned, which supports the
:meth:`~io.BufferedIOBase.write` method. While a writable file handle is open, :meth:`~io.BufferedIOBase.write` method. While a writable file handle is open,
attempting to read or write other files in the ZIP file will raise a attempting to read or write other files in the ZIP file will raise a
:exc:`RuntimeError`. :exc:`ValueError`.
When writing a file, if the file size is not known in advance but may exceed When writing a file, if the file size is not known in advance but may exceed
2 GiB, pass ``force_zip64=True`` to ensure that the header format is 2 GiB, pass ``force_zip64=True`` to ensure that the header format is
...@@ -252,6 +255,11 @@ ZipFile Objects ...@@ -252,6 +255,11 @@ ZipFile Objects
:meth:`open` can now be used to write files into the archive with the :meth:`open` can now be used to write files into the archive with the
``mode='w'`` option. ``mode='w'`` option.
.. versionchanged:: 3.6
Calling :meth:`.open` on a closed ZipFile will raise a :exc:`ValueError`.
Previously, a :exc:`RuntimeError` was raised.
.. method:: ZipFile.extract(member, path=None, pwd=None) .. method:: ZipFile.extract(member, path=None, pwd=None)
Extract a member from the archive to the current working directory; *member* Extract a member from the archive to the current working directory; *member*
...@@ -272,6 +280,10 @@ ZipFile Objects ...@@ -272,6 +280,10 @@ ZipFile Objects
characters (``:``, ``<``, ``>``, ``|``, ``"``, ``?``, and ``*``) characters (``:``, ``<``, ``>``, ``|``, ``"``, ``?``, and ``*``)
replaced by underscore (``_``). replaced by underscore (``_``).
.. versionchanged:: 3.6
Calling :meth:`extract` on a closed ZipFile will raise a
:exc:`ValueError`. Previously, a :exc:`RuntimeError` was raised.
.. method:: ZipFile.extractall(path=None, members=None, pwd=None) .. method:: ZipFile.extractall(path=None, members=None, pwd=None)
...@@ -288,6 +300,10 @@ ZipFile Objects ...@@ -288,6 +300,10 @@ ZipFile Objects
dots ``".."``. This module attempts to prevent that. dots ``".."``. This module attempts to prevent that.
See :meth:`extract` note. See :meth:`extract` note.
.. versionchanged:: 3.6
Calling :meth:`extractall` on a closed ZipFile will raise a
:exc:`ValueError`. Previously, a :exc:`RuntimeError` was raised.
.. method:: ZipFile.printdir() .. method:: ZipFile.printdir()
...@@ -305,18 +321,24 @@ ZipFile Objects ...@@ -305,18 +321,24 @@ ZipFile Objects
file in the archive, or a :class:`ZipInfo` object. The archive must be open for file in the archive, or a :class:`ZipInfo` object. The archive must be open for
read or append. *pwd* is the password used for encrypted files and, if specified, read or append. *pwd* is the password used for encrypted files and, if specified,
it will override the default password set with :meth:`setpassword`. Calling it will override the default password set with :meth:`setpassword`. Calling
:meth:`read` on a closed ZipFile will raise a :exc:`RuntimeError`. Calling
:meth:`read` on a ZipFile that uses a compression method other than :meth:`read` on a ZipFile that uses a compression method other than
:const:`ZIP_STORED`, :const:`ZIP_DEFLATED`, :const:`ZIP_BZIP2` or :const:`ZIP_STORED`, :const:`ZIP_DEFLATED`, :const:`ZIP_BZIP2` or
:const:`ZIP_LZMA` will raise a :exc:`NotImplementedError`. An error will also :const:`ZIP_LZMA` will raise a :exc:`NotImplementedError`. An error will also
be raised if the corresponding compression module is not available. be raised if the corresponding compression module is not available.
.. versionchanged:: 3.6
Calling :meth:`read` on a closed ZipFile will raise a :exc:`ValueError`.
Previously, a :exc:`RuntimeError` was raised.
.. method:: ZipFile.testzip() .. method:: ZipFile.testzip()
Read all the files in the archive and check their CRC's and file headers. Read all the files in the archive and check their CRC's and file headers.
Return the name of the first bad file, or else return ``None``. Calling Return the name of the first bad file, or else return ``None``.
:meth:`testzip` on a closed ZipFile will raise a :exc:`RuntimeError`.
.. versionchanged:: 3.6
Calling :meth:`testfile` on a closed ZipFile will raise a
:exc:`ValueError`. Previously, a :exc:`RuntimeError` was raised.
.. method:: ZipFile.write(filename, arcname=None, compress_type=None) .. method:: ZipFile.write(filename, arcname=None, compress_type=None)
...@@ -326,10 +348,7 @@ ZipFile Objects ...@@ -326,10 +348,7 @@ ZipFile Objects
letter and with leading path separators removed). If given, *compress_type* letter and with leading path separators removed). If given, *compress_type*
overrides the value given for the *compression* parameter to the constructor for overrides the value given for the *compression* parameter to the constructor for
the new entry. the new entry.
The archive must be open with mode ``'w'``, ``'x'`` or ``'a'`` -- calling The archive must be open with mode ``'w'``, ``'x'`` or ``'a'``.
:meth:`write` on a ZipFile created with mode ``'r'`` will raise a
:exc:`RuntimeError`. Calling :meth:`write` on a closed ZipFile will raise a
:exc:`RuntimeError`.
.. note:: .. note::
...@@ -348,16 +367,19 @@ ZipFile Objects ...@@ -348,16 +367,19 @@ ZipFile Objects
If ``arcname`` (or ``filename``, if ``arcname`` is not given) contains a null If ``arcname`` (or ``filename``, if ``arcname`` is not given) contains a null
byte, the name of the file in the archive will be truncated at the null byte. byte, the name of the file in the archive will be truncated at the null byte.
.. versionchanged:: 3.6
Calling :meth:`write` on a ZipFile created with mode ``'r'`` or
a closed ZipFile will raise a :exc:`ValueError`. Previously,
a :exc:`RuntimeError` was raised.
.. method:: ZipFile.writestr(zinfo_or_arcname, data[, compress_type]) .. method:: ZipFile.writestr(zinfo_or_arcname, data[, compress_type])
Write the string *data* to the archive; *zinfo_or_arcname* is either the file Write the string *data* to the archive; *zinfo_or_arcname* is either the file
name it will be given in the archive, or a :class:`ZipInfo` instance. If it's name it will be given in the archive, or a :class:`ZipInfo` instance. If it's
an instance, at least the filename, date, and time must be given. If it's a an instance, at least the filename, date, and time must be given. If it's a
name, the date and time is set to the current date and time. name, the date and time is set to the current date and time.
The archive must be opened with mode ``'w'``, ``'x'`` or ``'a'`` -- calling The archive must be opened with mode ``'w'``, ``'x'`` or ``'a'``.
:meth:`writestr` on a ZipFile created with mode ``'r'`` will raise a
:exc:`RuntimeError`. Calling :meth:`writestr` on a closed ZipFile will
raise a :exc:`RuntimeError`.
If given, *compress_type* overrides the value given for the *compression* If given, *compress_type* overrides the value given for the *compression*
parameter to the constructor for the new entry, or in the *zinfo_or_arcname* parameter to the constructor for the new entry, or in the *zinfo_or_arcname*
...@@ -373,6 +395,12 @@ ZipFile Objects ...@@ -373,6 +395,12 @@ ZipFile Objects
.. versionchanged:: 3.2 .. versionchanged:: 3.2
The *compress_type* argument. The *compress_type* argument.
.. versionchanged:: 3.6
Calling :meth:`writestr` on a ZipFile created with mode ``'r'`` or
a closed ZipFile will raise a :exc:`ValueError`. Previously,
a :exc:`RuntimeError` was raised.
The following data attributes are also available: The following data attributes are also available:
......
...@@ -449,15 +449,15 @@ class StoredTestsWithSourceFile(AbstractTestsWithSourceFile, ...@@ -449,15 +449,15 @@ class StoredTestsWithSourceFile(AbstractTestsWithSourceFile,
def test_write_to_readonly(self): def test_write_to_readonly(self):
"""Check that trying to call write() on a readonly ZipFile object """Check that trying to call write() on a readonly ZipFile object
raises a RuntimeError.""" raises a ValueError."""
with zipfile.ZipFile(TESTFN2, mode="w") as zipfp: with zipfile.ZipFile(TESTFN2, mode="w") as zipfp:
zipfp.writestr("somefile.txt", "bogus") zipfp.writestr("somefile.txt", "bogus")
with zipfile.ZipFile(TESTFN2, mode="r") as zipfp: with zipfile.ZipFile(TESTFN2, mode="r") as zipfp:
self.assertRaises(RuntimeError, zipfp.write, TESTFN) self.assertRaises(ValueError, zipfp.write, TESTFN)
with zipfile.ZipFile(TESTFN2, mode="r") as zipfp: with zipfile.ZipFile(TESTFN2, mode="r") as zipfp:
with self.assertRaises(RuntimeError): with self.assertRaises(ValueError):
zipfp.open(TESTFN, mode='w') zipfp.open(TESTFN, mode='w')
def test_add_file_before_1980(self): def test_add_file_before_1980(self):
...@@ -1210,27 +1210,27 @@ class OtherTests(unittest.TestCase): ...@@ -1210,27 +1210,27 @@ class OtherTests(unittest.TestCase):
fp.write("short file") fp.write("short file")
self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN) self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN)
def test_closed_zip_raises_RuntimeError(self): def test_closed_zip_raises_ValueError(self):
"""Verify that testzip() doesn't swallow inappropriate exceptions.""" """Verify that testzip() doesn't swallow inappropriate exceptions."""
data = io.BytesIO() data = io.BytesIO()
with zipfile.ZipFile(data, mode="w") as zipf: with zipfile.ZipFile(data, mode="w") as zipf:
zipf.writestr("foo.txt", "O, for a Muse of Fire!") zipf.writestr("foo.txt", "O, for a Muse of Fire!")
# This is correct; calling .read on a closed ZipFile should raise # This is correct; calling .read on a closed ZipFile should raise
# a RuntimeError, and so should calling .testzip. An earlier # a ValueError, and so should calling .testzip. An earlier
# version of .testzip would swallow this exception (and any other) # version of .testzip would swallow this exception (and any other)
# and report that the first file in the archive was corrupt. # and report that the first file in the archive was corrupt.
self.assertRaises(RuntimeError, zipf.read, "foo.txt") self.assertRaises(ValueError, zipf.read, "foo.txt")
self.assertRaises(RuntimeError, zipf.open, "foo.txt") self.assertRaises(ValueError, zipf.open, "foo.txt")
self.assertRaises(RuntimeError, zipf.testzip) self.assertRaises(ValueError, zipf.testzip)
self.assertRaises(RuntimeError, zipf.writestr, "bogus.txt", "bogus") self.assertRaises(ValueError, zipf.writestr, "bogus.txt", "bogus")
with open(TESTFN, 'w') as f: with open(TESTFN, 'w') as f:
f.write('zipfile test data') f.write('zipfile test data')
self.assertRaises(RuntimeError, zipf.write, TESTFN) self.assertRaises(ValueError, zipf.write, TESTFN)
def test_bad_constructor_mode(self): def test_bad_constructor_mode(self):
"""Check that bad modes passed to ZipFile constructor are caught.""" """Check that bad modes passed to ZipFile constructor are caught."""
self.assertRaises(RuntimeError, zipfile.ZipFile, TESTFN, "q") self.assertRaises(ValueError, zipfile.ZipFile, TESTFN, "q")
def test_bad_open_mode(self): def test_bad_open_mode(self):
"""Check that bad modes passed to ZipFile.open are caught.""" """Check that bad modes passed to ZipFile.open are caught."""
...@@ -1240,10 +1240,10 @@ class OtherTests(unittest.TestCase): ...@@ -1240,10 +1240,10 @@ class OtherTests(unittest.TestCase):
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(ValueError, zipf.open, "foo.txt", "q")
# universal newlines support is removed # universal newlines support is removed
self.assertRaises(RuntimeError, zipf.open, "foo.txt", "U") self.assertRaises(ValueError, zipf.open, "foo.txt", "U")
self.assertRaises(RuntimeError, zipf.open, "foo.txt", "rU") self.assertRaises(ValueError, 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
...@@ -1266,7 +1266,7 @@ class OtherTests(unittest.TestCase): ...@@ -1266,7 +1266,7 @@ class OtherTests(unittest.TestCase):
def test_bad_compression_mode(self): def test_bad_compression_mode(self):
"""Check that bad compression methods passed to ZipFile.open are """Check that bad compression methods passed to ZipFile.open are
caught.""" caught."""
self.assertRaises(RuntimeError, zipfile.ZipFile, TESTFN, "w", -1) self.assertRaises(NotImplementedError, zipfile.ZipFile, TESTFN, "w", -1)
def test_unsupported_compression(self): def test_unsupported_compression(self):
# data is declared as shrunk, but actually deflated # data is declared as shrunk, but actually deflated
...@@ -1423,15 +1423,15 @@ class OtherTests(unittest.TestCase): ...@@ -1423,15 +1423,15 @@ class OtherTests(unittest.TestCase):
with zipf.open('foo', mode='w') as w2: with zipf.open('foo', mode='w') as w2:
w2.write(msg1) w2.write(msg1)
with zipf.open('bar', mode='w') as w1: with zipf.open('bar', mode='w') as w1:
with self.assertRaises(RuntimeError): with self.assertRaises(ValueError):
zipf.open('handle', mode='w') zipf.open('handle', mode='w')
with self.assertRaises(RuntimeError): with self.assertRaises(ValueError):
zipf.open('foo', mode='r') zipf.open('foo', mode='r')
with self.assertRaises(RuntimeError): with self.assertRaises(ValueError):
zipf.writestr('str', 'abcde') zipf.writestr('str', 'abcde')
with self.assertRaises(RuntimeError): with self.assertRaises(ValueError):
zipf.write(__file__, 'file') zipf.write(__file__, 'file')
with self.assertRaises(RuntimeError): with self.assertRaises(ValueError):
zipf.close() zipf.close()
w1.write(msg2) w1.write(msg2)
with zipf.open('baz', mode='w') as w2: with zipf.open('baz', mode='w') as w2:
......
...@@ -449,7 +449,7 @@ class ZipInfo (object): ...@@ -449,7 +449,7 @@ class ZipInfo (object):
elif ln == 0: elif ln == 0:
counts = () counts = ()
else: else:
raise RuntimeError("Corrupt extra field %s"%(ln,)) raise BadZipFile("Corrupt extra field %04x (size=%d)" % (tp, ln))
idx = 0 idx = 0
...@@ -654,7 +654,7 @@ def _check_compression(compression): ...@@ -654,7 +654,7 @@ def _check_compression(compression):
raise RuntimeError( raise RuntimeError(
"Compression requires the (missing) lzma module") "Compression requires the (missing) lzma module")
else: else:
raise RuntimeError("That compression method is not supported") raise NotImplementedError("That compression method is not supported")
def _get_compressor(compress_type): def _get_compressor(compress_type):
...@@ -697,7 +697,7 @@ class _SharedFile: ...@@ -697,7 +697,7 @@ class _SharedFile:
def read(self, n=-1): def read(self, n=-1):
with self._lock: with self._lock:
if self._writing(): if self._writing():
raise RuntimeError("Can't read from the ZIP file while there " raise ValueError("Can't read from the ZIP file while there "
"is an open writing handle on it. " "is an open writing handle on it. "
"Close the writing handle before trying to read.") "Close the writing handle before trying to read.")
self._file.seek(self._pos) self._file.seek(self._pos)
...@@ -1055,7 +1055,7 @@ class ZipFile: ...@@ -1055,7 +1055,7 @@ class ZipFile:
"""Open the ZIP file with mode read 'r', write 'w', exclusive create 'x', """Open the ZIP file with mode read 'r', write 'w', exclusive create 'x',
or append 'a'.""" or append 'a'."""
if mode not in ('r', 'w', 'x', 'a'): if mode not in ('r', 'w', 'x', 'a'):
raise RuntimeError("ZipFile requires mode 'r', 'w', 'x', or 'a'") raise ValueError("ZipFile requires mode 'r', 'w', 'x', or 'a'")
_check_compression(compression) _check_compression(compression)
...@@ -1129,7 +1129,7 @@ class ZipFile: ...@@ -1129,7 +1129,7 @@ class ZipFile:
self._didModify = True self._didModify = True
self.start_dir = self.fp.tell() self.start_dir = self.fp.tell()
else: else:
raise RuntimeError("Mode must be 'r', 'w', 'x', or 'a'") raise ValueError("Mode must be 'r', 'w', 'x', or 'a'")
except: except:
fp = self.fp fp = self.fp
self.fp = None self.fp = None
...@@ -1277,7 +1277,7 @@ class ZipFile: ...@@ -1277,7 +1277,7 @@ class ZipFile:
def setpassword(self, pwd): def setpassword(self, pwd):
"""Set default password for encrypted files.""" """Set default password for encrypted files."""
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).__name__)
if pwd: if pwd:
self.pwd = pwd self.pwd = pwd
else: else:
...@@ -1291,7 +1291,7 @@ class ZipFile: ...@@ -1291,7 +1291,7 @@ class ZipFile:
@comment.setter @comment.setter
def comment(self, comment): def comment(self, comment):
if not isinstance(comment, bytes): if not isinstance(comment, bytes):
raise TypeError("comment: expected bytes, got %s" % type(comment)) raise TypeError("comment: expected bytes, got %s" % type(comment).__name__)
# check for valid comment length # check for valid comment length
if len(comment) > ZIP_MAX_COMMENT: if len(comment) > ZIP_MAX_COMMENT:
import warnings import warnings
...@@ -1323,13 +1323,13 @@ class ZipFile: ...@@ -1323,13 +1323,13 @@ class ZipFile:
instance for name, with zinfo.file_size set. instance for name, with zinfo.file_size set.
""" """
if mode not in {"r", "w"}: if mode not in {"r", "w"}:
raise RuntimeError('open() requires mode "r" or "w"') raise ValueError('open() requires mode "r" or "w"')
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).__name__)
if pwd and (mode == "w"): if pwd and (mode == "w"):
raise ValueError("pwd is only supported for reading files") raise ValueError("pwd is only supported for reading files")
if not self.fp: if not self.fp:
raise RuntimeError( raise ValueError(
"Attempt to use ZIP archive that was already closed") "Attempt to use ZIP archive that was already closed")
# Make sure we have an info object # Make sure we have an info object
...@@ -1347,7 +1347,7 @@ class ZipFile: ...@@ -1347,7 +1347,7 @@ class ZipFile:
return self._open_to_write(zinfo, force_zip64=force_zip64) return self._open_to_write(zinfo, force_zip64=force_zip64)
if self._writing: if self._writing:
raise RuntimeError("Can't read from the ZIP file while there " raise ValueError("Can't read from the ZIP file while there "
"is an open writing handle on it. " "is an open writing handle on it. "
"Close the writing handle before trying to read.") "Close the writing handle before trying to read.")
...@@ -1394,7 +1394,7 @@ class ZipFile: ...@@ -1394,7 +1394,7 @@ class ZipFile:
if not pwd: if not pwd:
pwd = self.pwd pwd = self.pwd
if not pwd: if not pwd:
raise RuntimeError("File %s is encrypted, password " raise RuntimeError("File %r is encrypted, password "
"required for extraction" % name) "required for extraction" % name)
zd = _ZipDecrypter(pwd) zd = _ZipDecrypter(pwd)
...@@ -1412,7 +1412,7 @@ class ZipFile: ...@@ -1412,7 +1412,7 @@ class ZipFile:
# compare against the CRC otherwise # compare against the CRC otherwise
check_byte = (zinfo.CRC >> 24) & 0xff check_byte = (zinfo.CRC >> 24) & 0xff
if h[11] != check_byte: if h[11] != check_byte:
raise RuntimeError("Bad password for file", name) raise RuntimeError("Bad password for file %r" % name)
return ZipExtFile(zef_file, mode, zinfo, zd, True) return ZipExtFile(zef_file, mode, zinfo, zd, True)
except: except:
...@@ -1426,7 +1426,7 @@ class ZipFile: ...@@ -1426,7 +1426,7 @@ class ZipFile:
"the ZIP file." "the ZIP file."
) )
if self._writing: if self._writing:
raise RuntimeError("Can't write to the ZIP file while there is " raise ValueError("Can't write to the ZIP file while there is "
"another write handle open on it. " "another write handle open on it. "
"Close the first handle before opening another.") "Close the first handle before opening another.")
...@@ -1548,9 +1548,9 @@ class ZipFile: ...@@ -1548,9 +1548,9 @@ class ZipFile:
import warnings import warnings
warnings.warn('Duplicate name: %r' % zinfo.filename, stacklevel=3) warnings.warn('Duplicate name: %r' % zinfo.filename, stacklevel=3)
if self.mode not in ('w', 'x', 'a'): if self.mode not in ('w', 'x', 'a'):
raise RuntimeError("write() requires mode 'w', 'x', or 'a'") raise ValueError("write() requires mode 'w', 'x', or 'a'")
if not self.fp: if not self.fp:
raise RuntimeError( raise ValueError(
"Attempt to write ZIP archive that was already closed") "Attempt to write ZIP archive that was already closed")
_check_compression(zinfo.compress_type) _check_compression(zinfo.compress_type)
if not self._allowZip64: if not self._allowZip64:
...@@ -1569,10 +1569,10 @@ class ZipFile: ...@@ -1569,10 +1569,10 @@ class ZipFile:
"""Put the bytes from filename into the archive under the name """Put the bytes from filename into the archive under the name
arcname.""" arcname."""
if not self.fp: if not self.fp:
raise RuntimeError( raise ValueError(
"Attempt to write to ZIP archive that was already closed") "Attempt to write to ZIP archive that was already closed")
if self._writing: if self._writing:
raise RuntimeError( raise ValueError(
"Can't write to ZIP archive while an open writing handle exists" "Can't write to ZIP archive while an open writing handle exists"
) )
...@@ -1628,10 +1628,10 @@ class ZipFile: ...@@ -1628,10 +1628,10 @@ class ZipFile:
zinfo = zinfo_or_arcname zinfo = zinfo_or_arcname
if not self.fp: if not self.fp:
raise RuntimeError( raise ValueError(
"Attempt to write to ZIP archive that was already closed") "Attempt to write to ZIP archive that was already closed")
if self._writing: if self._writing:
raise RuntimeError( raise ValueError(
"Can't write to ZIP archive while an open writing handle exists." "Can't write to ZIP archive while an open writing handle exists."
) )
...@@ -1654,7 +1654,7 @@ class ZipFile: ...@@ -1654,7 +1654,7 @@ class ZipFile:
return return
if self._writing: if self._writing:
raise RuntimeError("Can't close the ZIP file while there is " raise ValueError("Can't close the ZIP file while there is "
"an open writing handle on it. " "an open writing handle on it. "
"Close the writing handle before closing the zip.") "Close the writing handle before closing the zip.")
...@@ -1803,7 +1803,7 @@ class PyZipFile(ZipFile): ...@@ -1803,7 +1803,7 @@ class PyZipFile(ZipFile):
if filterfunc and not filterfunc(pathname): if filterfunc and not filterfunc(pathname):
if self.debug: if self.debug:
label = 'path' if os.path.isdir(pathname) else 'file' label = 'path' if os.path.isdir(pathname) else 'file'
print('%s "%s" skipped by filterfunc' % (label, pathname)) print('%s %r skipped by filterfunc' % (label, pathname))
return return
dir, name = os.path.split(pathname) dir, name = os.path.split(pathname)
if os.path.isdir(pathname): if os.path.isdir(pathname):
...@@ -1834,7 +1834,7 @@ class PyZipFile(ZipFile): ...@@ -1834,7 +1834,7 @@ class PyZipFile(ZipFile):
elif ext == ".py": elif ext == ".py":
if filterfunc and not filterfunc(path): if filterfunc and not filterfunc(path):
if self.debug: if self.debug:
print('file "%s" skipped by filterfunc' % path) print('file %r skipped by filterfunc' % path)
continue continue
fname, arcname = self._get_codename(path[0:-3], fname, arcname = self._get_codename(path[0:-3],
basename) basename)
...@@ -1851,7 +1851,7 @@ class PyZipFile(ZipFile): ...@@ -1851,7 +1851,7 @@ class PyZipFile(ZipFile):
if ext == ".py": if ext == ".py":
if filterfunc and not filterfunc(path): if filterfunc and not filterfunc(path):
if self.debug: if self.debug:
print('file "%s" skipped by filterfunc' % path) print('file %r skipped by filterfunc' % path)
continue continue
fname, arcname = self._get_codename(path[0:-3], fname, arcname = self._get_codename(path[0:-3],
basename) basename)
......
...@@ -135,6 +135,9 @@ Core and Builtins ...@@ -135,6 +135,9 @@ Core and Builtins
Library Library
------- -------
- Issue #24693: Changed some RuntimeError's in the zipfile module to more
appropriate types. Improved some error messages and debugging output.
- Issue #17909: ``json.load`` and ``json.loads`` now support binary input - Issue #17909: ``json.load`` and ``json.loads`` now support binary input
encoded as UTF-8, UTF-16 or UTF-32. Patch by Serhiy Storchaka. encoded as UTF-8, UTF-16 or UTF-32. Patch by Serhiy Storchaka.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment