Skip to content

Commit b44b9d9

Browse files
authored
gh-113971: Make zipfile.ZipInfo._compresslevel public as .compress_level (#113969)
Make zipfile.ZipInfo.compress_level public. A property is used to retain the behavior of the ._compresslevel. People constructing zipfile.ZipInfo instances to pass into existing APIs to control per-file compression levels already treat this as public, there was never a reason for it not to be. I used the more modern name compress_level instead of compresslevel as the keyword argument on other ZipFile APIs is called to be consistent with compress_type and a general long term preference of not runningwordstogether without a separator in names.
1 parent ac92527 commit b44b9d9

File tree

4 files changed

+40
-11
lines changed

4 files changed

+40
-11
lines changed

Doc/library/zipfile.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ The module defines the following items:
7979
of the last modification to the file; the fields are described in section
8080
:ref:`zipinfo-objects`.
8181

82+
.. versionadded:: 3.13
83+
A public ``.compress_level`` attribute has been added to expose the
84+
formerly protected ``._compresslevel``. The older protected name
85+
continues to work as a property for backwards compatibility.
86+
8287
.. function:: is_zipfile(filename)
8388

8489
Returns ``True`` if *filename* is a valid ZIP file based on its magic number,

Lib/test/test_zipfile/test_core.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ def test_writestr_compresslevel(self):
315315
# Compression level follows the constructor.
316316
a_info = zipfp.getinfo('a.txt')
317317
self.assertEqual(a_info.compress_type, self.compression)
318-
self.assertEqual(a_info._compresslevel, 1)
318+
self.assertEqual(a_info.compress_level, 1)
319319

320320
# Compression level is overridden.
321321
b_info = zipfp.getinfo('b.txt')
@@ -408,7 +408,7 @@ def test_per_file_compresslevel(self):
408408
one_info = zipfp.getinfo('compress_1')
409409
nine_info = zipfp.getinfo('compress_9')
410410
self.assertEqual(one_info._compresslevel, 1)
411-
self.assertEqual(nine_info._compresslevel, 9)
411+
self.assertEqual(nine_info.compress_level, 9)
412412

413413
def test_writing_errors(self):
414414
class BrokenFile(io.BytesIO):
@@ -3011,6 +3011,17 @@ def test_from_dir(self):
30113011
self.assertEqual(zi.compress_type, zipfile.ZIP_STORED)
30123012
self.assertEqual(zi.file_size, 0)
30133013

3014+
def test_compresslevel_property(self):
3015+
zinfo = zipfile.ZipInfo("xxx")
3016+
self.assertFalse(zinfo._compresslevel)
3017+
self.assertFalse(zinfo.compress_level)
3018+
zinfo._compresslevel = 99 # test the legacy @property.setter
3019+
self.assertEqual(zinfo.compress_level, 99)
3020+
self.assertEqual(zinfo._compresslevel, 99)
3021+
zinfo.compress_level = 8
3022+
self.assertEqual(zinfo.compress_level, 8)
3023+
self.assertEqual(zinfo._compresslevel, 8)
3024+
30143025

30153026
class CommandLineTest(unittest.TestCase):
30163027

Lib/zipfile/__init__.py

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -371,15 +371,15 @@ def _sanitize_filename(filename):
371371
return filename
372372

373373

374-
class ZipInfo (object):
374+
class ZipInfo:
375375
"""Class with attributes describing each file in the ZIP archive."""
376376

377377
__slots__ = (
378378
'orig_filename',
379379
'filename',
380380
'date_time',
381381
'compress_type',
382-
'_compresslevel',
382+
'compress_level',
383383
'comment',
384384
'extra',
385385
'create_system',
@@ -413,7 +413,7 @@ def __init__(self, filename="NoName", date_time=(1980,1,1,0,0,0)):
413413

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

438+
# Maintain backward compatibility with the old protected attribute name.
439+
@property
440+
def _compresslevel(self):
441+
return self.compress_level
442+
443+
@_compresslevel.setter
444+
def _compresslevel(self, value):
445+
self.compress_level = value
446+
438447
def __repr__(self):
439448
result = ['<%s filename=%r' % (self.__class__.__name__, self.filename)]
440449
if self.compress_type != ZIP_STORED:
@@ -1191,7 +1200,7 @@ def __init__(self, zf, zinfo, zip64):
11911200
self._zip64 = zip64
11921201
self._zipfile = zf
11931202
self._compressor = _get_compressor(zinfo.compress_type,
1194-
zinfo._compresslevel)
1203+
zinfo.compress_level)
11951204
self._file_size = 0
11961205
self._compress_size = 0
11971206
self._crc = 0
@@ -1603,7 +1612,7 @@ def open(self, name, mode="r", pwd=None, *, force_zip64=False):
16031612
elif mode == 'w':
16041613
zinfo = ZipInfo(name)
16051614
zinfo.compress_type = self.compression
1606-
zinfo._compresslevel = self.compresslevel
1615+
zinfo.compress_level = self.compresslevel
16071616
else:
16081617
# Get info object for name
16091618
zinfo = self.getinfo(name)
@@ -1855,9 +1864,9 @@ def write(self, filename, arcname=None,
18551864
zinfo.compress_type = self.compression
18561865

18571866
if compresslevel is not None:
1858-
zinfo._compresslevel = compresslevel
1867+
zinfo.compress_level = compresslevel
18591868
else:
1860-
zinfo._compresslevel = self.compresslevel
1869+
zinfo.compress_level = self.compresslevel
18611870

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

18981907
if compresslevel is not None:
1899-
zinfo._compresslevel = compresslevel
1908+
zinfo.compress_level = compresslevel
19001909

19011910
zinfo.file_size = len(data) # Uncompressed size
19021911
with self._lock:
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
The :class:`zipfile.ZipInfo` previously protected ``._compresslevel``
2+
attribute has been made public as ``.compress_level`` with the old
3+
``_compresslevel`` name remaining available as a property to retain
4+
compatibility.

0 commit comments

Comments
 (0)