2020from zipstream import ZipStream
2121
2222
23+ PY313 = sys .version_info < (3 , 14 )
2324PY36 = sys .version_info < (3 , 7 )
2425PY35 = sys .version_info < (3 , 6 )
2526
3031 ("mbyte" , 1024 * 1024 ),
3132]
3233
34+ COMPRESS_TYPES = [
35+ zipfile .ZIP_STORED ,
36+ zipfile .ZIP_LZMA ,
37+ zipfile .ZIP_DEFLATED ,
38+ zipfile .ZIP_BZIP2 ,
39+ ]
40+ if not PY313 :
41+ COMPRESS_TYPES .append (zipfile .ZIP_ZSTANDARD )
3342
3443# Patch is_dir onto ZipInfo objects in 3.5 to make testing easier
3544@pytest .fixture (autouse = PY35 )
@@ -107,12 +116,7 @@ def _gen_rand():
107116# Tests start
108117################################
109118
110- @pytest .mark .parametrize ("ct" , [
111- zipfile .ZIP_STORED ,
112- zipfile .ZIP_LZMA ,
113- zipfile .ZIP_DEFLATED ,
114- zipfile .ZIP_BZIP2
115- ])
119+ @pytest .mark .parametrize ("ct" , COMPRESS_TYPES )
116120def test_zipstream_compression (caplog , files , ct ):
117121 """Test that all types of compression properly compress and extract"""
118122 caplog .set_level (logging .WARNING )
@@ -135,12 +139,7 @@ def test_zipstream_compression(caplog, files, ct):
135139 _verify_zip_contains (zf , f )
136140
137141
138- @pytest .mark .parametrize ("ct" , [
139- zipfile .ZIP_STORED ,
140- zipfile .ZIP_LZMA ,
141- zipfile .ZIP_DEFLATED ,
142- zipfile .ZIP_BZIP2
143- ])
142+ @pytest .mark .parametrize ("ct" , COMPRESS_TYPES )
144143@pytest .mark .parametrize ("cl" , [None , 2 ])
145144def test_mixed_compression_and_getinfo (ct , cl ):
146145 """Test that files are compressed using the correct method and level and
@@ -159,11 +158,14 @@ def test_mixed_compression_and_getinfo(ct, cl):
159158 zs .add (b"3c" , arcname = "3c" , compress_type = zipfile .ZIP_DEFLATED , compress_level = TEST_CL )
160159 zs .add (b"4" , arcname = "4" , compress_type = zipfile .ZIP_BZIP2 )
161160 zs .add (b"4c" , arcname = "4c" , compress_type = zipfile .ZIP_BZIP2 , compress_level = TEST_CL )
161+ if not PY313 :
162+ zs .add (b"5" , arcname = "5" , compress_type = zipfile .ZIP_ZSTANDARD )
163+ zs .add (b"5c" , arcname = "5c" , compress_type = zipfile .ZIP_ZSTANDARD , compress_level = TEST_CL )
162164
163165 zf = _get_zip (zs )
164166 zinfos = zf .infolist ()
165167 fullinfos = zs .info_list ()
166- assert len (zinfos ) == len (fullinfos ) == 9
168+ assert len (zinfos ) == len (fullinfos ) == 9 + ( 0 if PY313 else 2 )
167169
168170 def assert_zinfo (idx , name , compress_type , compress_level ):
169171 zi = zinfos [idx ]
@@ -189,6 +191,9 @@ def assert_zinfo(idx, name, compress_type, compress_level):
189191 assert_zinfo (6 , "3c" , zipfile .ZIP_DEFLATED , TEST_CL )
190192 assert_zinfo (7 , "4" , zipfile .ZIP_BZIP2 , cl )
191193 assert_zinfo (8 , "4c" , zipfile .ZIP_BZIP2 , TEST_CL )
194+ if not PY313 :
195+ assert_zinfo (9 , "5" , zipfile .ZIP_ZSTANDARD , cl )
196+ assert_zinfo (10 , "5c" , zipfile .ZIP_ZSTANDARD , TEST_CL )
192197
193198
194199@pytest .mark .parametrize ("zip64" , [False , True ])
@@ -368,6 +373,34 @@ def test_invalid_compression(ct):
368373 zs .add ("." , arcname = "." , compress_type = ct )
369374
370375
376+ @pytest .mark .skipif (PY313 , reason = "Tests zstd compress_level (Python 3.14+ only)" )
377+ def test_invalid_zstd_compression ():
378+ """Test zstd values outside of valid ones cause an error"""
379+ ZipStream (compress_type = zipfile .ZIP_ZSTANDARD )
380+
381+ from compression .zstd import CompressionParameter
382+ lower , upper = CompressionParameter .compression_level .bounds ()
383+
384+ for x in (lower , lower + 1 , 0 , upper - 1 , upper ):
385+ ZipStream (compress_type = zipfile .ZIP_ZSTANDARD , compress_level = x )
386+
387+ for x in (lower - 1 , upper + 1 ):
388+ with pytest .raises (ValueError ):
389+ ZipStream (compress_type = zipfile .ZIP_ZSTANDARD , compress_level = x )
390+ with pytest .raises (ValueError ):
391+ ZipStream ().add_path ("." , compress_type = zipfile .ZIP_ZSTANDARD , compress_level = x )
392+ with pytest .raises (ValueError ):
393+ ZipStream ().add ("." , arcname = "." , compress_type = zipfile .ZIP_ZSTANDARD , compress_level = x )
394+
395+ zs = ZipStream (compress_type = zipfile .ZIP_ZSTANDARD )
396+ with pytest .raises (ValueError ):
397+ zs .add ("." , arcname = "." , compress_level = x )
398+
399+ zs = ZipStream (compress_level = x )
400+ with pytest .raises (ValueError ):
401+ zs .add ("." , arcname = "." , compress_type = zipfile .ZIP_ZSTANDARD )
402+
403+
371404def test_multibyte_and_non_ascii_characters_in_filenames ():
372405 zs = ZipStream (sized = True )
373406 zs .add (None , "☆/" )
@@ -734,12 +767,7 @@ def custom_walk(path):
734767 [b"a" , b"list" , b"of" , b"bytes" ],
735768 _gen_rand ()
736769])
737- @pytest .mark .parametrize ("ct" , [
738- zipfile .ZIP_STORED ,
739- zipfile .ZIP_LZMA ,
740- zipfile .ZIP_DEFLATED ,
741- zipfile .ZIP_BZIP2
742- ])
770+ @pytest .mark .parametrize ("ct" , COMPRESS_TYPES )
743771def test_adding_data (caplog , data , ct ):
744772 """Test adding non-files with different compression methods"""
745773 caplog .set_level (logging .WARNING )
@@ -1173,6 +1201,36 @@ def fakelocaltime(_=None):
11731201 assert zinfos [0 ].date_time == (2107 , 12 , 31 , 23 , 59 , 58 )
11741202
11751203
1204+ @pytest .mark .skipif (PY313 , reason = "Tests zstd compress_level (Python 3.14+ only)" )
1205+ def test_zstd_uses_compression_level ():
1206+ """Test that the zstd compression level is applied"""
1207+ zs = ZipStream (compress_type = zipfile .ZIP_ZSTANDARD )
1208+ test = b"a" * 1024
1209+ zs .add (test , "-7.txt" , compress_level = - 7 )
1210+ zs .add (test , "default.txt" )
1211+ zs .add (test , "22.txt" , compress_level = 22 )
1212+
1213+ data = bytes (zs )
1214+ info = list (zs .info_list ())
1215+ assert len (info ) == zs .num_streamed () == 3
1216+
1217+ for x in info :
1218+ assert x ["size" ] == 1024
1219+ assert x ["compress_type" ] == zipfile .ZIP_ZSTANDARD
1220+ assert x ["CRC" ] == 2085984185
1221+
1222+ assert info [0 ]["name" ] == "-7.txt"
1223+ assert info [1 ]["name" ] == "default.txt"
1224+ assert info [2 ]["name" ] == "22.txt"
1225+
1226+ # check compress level set
1227+ assert info [0 ]["compress_level" ] == - 7
1228+ assert info [1 ]["compress_level" ] == None
1229+ assert info [2 ]["compress_level" ] == 22
1230+
1231+ # check different compressed sizes for each level (in decreasing order as level increases)
1232+ assert info [0 ]["compressed_size" ] > info [1 ]["compressed_size" ] > info [2 ]["compressed_size" ]
1233+
11761234def test_info_list (monkeypatch ):
11771235 faketime = (1980 , 1 , 1 , 0 , 0 , 0 )
11781236
@@ -1228,8 +1286,8 @@ def fakelocaltime(_=None):
12281286 assert len ([x for x in info2 if not x ["streamed" ]]) == zs .num_queued () == 0
12291287 assert len ([x for x in info2 if x ["streamed" ]]) == zs .num_streamed () == 3
12301288
1231- # Make sure any information that ws provided up-front hasn't changed
1232- # (except for the "streamed" key which mush got False -> True)
1289+ # Make sure any information that was provided up-front hasn't changed
1290+ # (except for the "streamed" key which must go False -> True)
12331291 for pre , post in zip (info , info2 ):
12341292 for k , v in pre .items ():
12351293 if k == "streamed" :
@@ -1525,6 +1583,9 @@ def test_sized_zipstream(monkeypatch, files, zip64):
15251583 ZipStream (sized = True , compress_type = zipfile .ZIP_LZMA )
15261584 with pytest .raises (ValueError ):
15271585 ZipStream (sized = True , compress_type = zipfile .ZIP_BZIP2 )
1586+ if not PY313 :
1587+ with pytest .raises (ValueError ):
1588+ ZipStream (sized = True , compress_type = zipfile .ZIP_ZSTANDARD )
15281589
15291590 with pytest .raises (ValueError ):
15301591 ZipStream .from_path ("." , sized = True , compress_type = zipfile .ZIP_DEFLATED )
@@ -1546,6 +1607,9 @@ def test_sized_zipstream(monkeypatch, files, zip64):
15461607 szs .add ("invalid" , "invalid" , compress_type = zipfile .ZIP_LZMA )
15471608 with pytest .raises (ValueError ):
15481609 szs .add ("invalid" , "invalid" , compress_type = zipfile .ZIP_BZIP2 )
1610+ if not PY313 :
1611+ with pytest .raises (ValueError ):
1612+ szs .add ("invalid" , "invalid" , compress_type = zipfile .ZIP_ZSTANDARD )
15491613
15501614 assert szs .sized
15511615 calculated = len (szs )
0 commit comments