Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-113971: Make zipfile.ZipInfo._compresslevel public as .compress_level #113969

Merged
merged 7 commits into from
Jan 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Doc/library/zipfile.rst
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ The module defines the following items:
of the last modification to the file; the fields are described in section
:ref:`zipinfo-objects`.

.. versionadded:: 3.13
A public ``.compress_level`` attribute has been added to expose the
formerly protected ``._compresslevel``. The older protected name
continues to work as a property for backwards compatibility.

.. function:: is_zipfile(filename)

Returns ``True`` if *filename* is a valid ZIP file based on its magic number,
Expand Down
15 changes: 13 additions & 2 deletions Lib/test/test_zipfile/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ def test_writestr_compresslevel(self):
# Compression level follows the constructor.
a_info = zipfp.getinfo('a.txt')
self.assertEqual(a_info.compress_type, self.compression)
self.assertEqual(a_info._compresslevel, 1)
self.assertEqual(a_info.compress_level, 1)

# Compression level is overridden.
b_info = zipfp.getinfo('b.txt')
Expand Down Expand Up @@ -408,7 +408,7 @@ def test_per_file_compresslevel(self):
one_info = zipfp.getinfo('compress_1')
nine_info = zipfp.getinfo('compress_9')
self.assertEqual(one_info._compresslevel, 1)
self.assertEqual(nine_info._compresslevel, 9)
self.assertEqual(nine_info.compress_level, 9)

def test_writing_errors(self):
class BrokenFile(io.BytesIO):
Expand Down Expand Up @@ -3011,6 +3011,17 @@ def test_from_dir(self):
self.assertEqual(zi.compress_type, zipfile.ZIP_STORED)
self.assertEqual(zi.file_size, 0)

def test_compresslevel_property(self):
zinfo = zipfile.ZipInfo("xxx")
self.assertFalse(zinfo._compresslevel)
self.assertFalse(zinfo.compress_level)
zinfo._compresslevel = 99 # test the legacy @property.setter
self.assertEqual(zinfo.compress_level, 99)
self.assertEqual(zinfo._compresslevel, 99)
zinfo.compress_level = 8
self.assertEqual(zinfo.compress_level, 8)
self.assertEqual(zinfo._compresslevel, 8)


class CommandLineTest(unittest.TestCase):

Expand Down
27 changes: 18 additions & 9 deletions Lib/zipfile/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,15 +371,15 @@ def _sanitize_filename(filename):
return filename


class ZipInfo (object):
class ZipInfo:
"""Class with attributes describing each file in the ZIP archive."""

__slots__ = (
'orig_filename',
'filename',
'date_time',
'compress_type',
'_compresslevel',
'compress_level',
'comment',
'extra',
'create_system',
Expand Down Expand Up @@ -413,7 +413,7 @@ def __init__(self, filename="NoName", date_time=(1980,1,1,0,0,0)):

# Standard values:
self.compress_type = ZIP_STORED # Type of compression for the file
self._compresslevel = None # Level for the compressor
self.compress_level = None # Level for the compressor
self.comment = b"" # Comment for each file
self.extra = b"" # ZIP extra data
if sys.platform == 'win32':
Expand All @@ -435,6 +435,15 @@ def __init__(self, filename="NoName", date_time=(1980,1,1,0,0,0)):
# header_offset Byte offset to the file header
# CRC CRC-32 of the uncompressed file

# Maintain backward compatibility with the old protected attribute name.
@property
def _compresslevel(self):
return self.compress_level

@_compresslevel.setter
def _compresslevel(self, value):
self.compress_level = value

def __repr__(self):
result = ['<%s filename=%r' % (self.__class__.__name__, self.filename)]
if self.compress_type != ZIP_STORED:
Expand Down Expand Up @@ -1191,7 +1200,7 @@ def __init__(self, zf, zinfo, zip64):
self._zip64 = zip64
self._zipfile = zf
self._compressor = _get_compressor(zinfo.compress_type,
zinfo._compresslevel)
zinfo.compress_level)
self._file_size = 0
self._compress_size = 0
self._crc = 0
Expand Down Expand Up @@ -1603,7 +1612,7 @@ def open(self, name, mode="r", pwd=None, *, force_zip64=False):
elif mode == 'w':
zinfo = ZipInfo(name)
zinfo.compress_type = self.compression
zinfo._compresslevel = self.compresslevel
zinfo.compress_level = self.compresslevel
else:
# Get info object for name
zinfo = self.getinfo(name)
Expand Down Expand Up @@ -1855,9 +1864,9 @@ def write(self, filename, arcname=None,
zinfo.compress_type = self.compression

if compresslevel is not None:
zinfo._compresslevel = compresslevel
zinfo.compress_level = compresslevel
else:
zinfo._compresslevel = self.compresslevel
zinfo.compress_level = self.compresslevel

with open(filename, "rb") as src, self.open(zinfo, 'w') as dest:
shutil.copyfileobj(src, dest, 1024*8)
Expand All @@ -1875,7 +1884,7 @@ def writestr(self, zinfo_or_arcname, data,
zinfo = ZipInfo(filename=zinfo_or_arcname,
date_time=time.localtime(time.time())[:6])
zinfo.compress_type = self.compression
zinfo._compresslevel = self.compresslevel
zinfo.compress_level = self.compresslevel
if zinfo.filename.endswith('/'):
zinfo.external_attr = 0o40775 << 16 # drwxrwxr-x
zinfo.external_attr |= 0x10 # MS-DOS directory flag
Expand All @@ -1896,7 +1905,7 @@ def writestr(self, zinfo_or_arcname, data,
zinfo.compress_type = compress_type

if compresslevel is not None:
zinfo._compresslevel = compresslevel
zinfo.compress_level = compresslevel

zinfo.file_size = len(data) # Uncompressed size
with self._lock:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
The :class:`zipfile.ZipInfo` previously protected ``._compresslevel``
attribute has been made public as ``.compress_level`` with the old
``_compresslevel`` name remaining available as a property to retain
compatibility.
Loading