Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit76722fa

Browse files
committed
gh-51067: Add ZipInfo.remove() (#51067)
1 parent0708437 commit76722fa

File tree

5 files changed

+406
-0
lines changed

5 files changed

+406
-0
lines changed

‎Doc/library/zipfile.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,23 @@ ZipFile Objects
492492
..versionadded::3.11
493493

494494

495+
..method::ZipFile.remove(zinfo_or_arcname)
496+
497+
Removes a member from the archive. *zinfo_or_arcname* is either the full
498+
path of the member, or a:class:`ZipInfo` instance.
499+
500+
The archive must be opened with mode ``'w'``, ``'x'`` or ``'a'``.
501+
502+
Calling:meth:`remove` on a closed ZipFile will raise a:exc:`ValueError`.
503+
504+
..note::
505+
506+
Removing a member in an archive may involve a move of many internal data
507+
records, which can be I/O intensive for a large ZIP file.
508+
509+
..versionadded::3.12
510+
511+
495512
The following data attributes are also available:
496513

497514
..attribute::ZipFile.filename

‎Lib/test/test_zipfile/test_core.py

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,6 +1147,240 @@ class LzmaWriterTests(AbstractWriterTests, unittest.TestCase):
11471147
compression=zipfile.ZIP_LZMA
11481148

11491149

1150+
classAbstractRemoveTests:
1151+
1152+
def_test_removing_indexes(self,test_files,indexes):
1153+
"""Test underlying _remove_members() for removing members at given
1154+
indexes."""
1155+
# calculate the expected results
1156+
expected_files= []
1157+
withzipfile.ZipFile(TESTFN,'w')aszh:
1158+
fori, (file,data)inenumerate(test_files):
1159+
ifinotinindexes:
1160+
zh.writestr(file,data)
1161+
expected_files.append(file)
1162+
expected_size=os.path.getsize(TESTFN)
1163+
1164+
# prepare the test zip
1165+
withzipfile.ZipFile(TESTFN,'w')aszh:
1166+
forfile,dataintest_files:
1167+
zh.writestr(file,data)
1168+
1169+
# do the removal and check the result
1170+
withzipfile.ZipFile(TESTFN,'a')aszh:
1171+
members= {zh.infolist()[i]foriinindexes}
1172+
zh._remove_members(members)
1173+
1174+
# make sure internal caches have reflected the change
1175+
# and are consistent
1176+
self.assertEqual(zh.namelist(),expected_files)
1177+
forfile,_intest_files:
1178+
iffileinzh.namelist():
1179+
self.assertEqual(zh.getinfo(file).filename,file)
1180+
else:
1181+
withself.assertRaises(KeyError):
1182+
zh.getinfo(file)
1183+
1184+
self.assertIsNone(zh.testzip())
1185+
self.assertEqual(os.path.getsize(TESTFN),expected_size)
1186+
1187+
def_test_removing_combinations(self,test_files,n=None):
1188+
"""Test underlying _remove_members() for removing random combinations
1189+
of members."""
1190+
ln=len(test_files)
1191+
ifnisNone:
1192+
# iterate n from 1 to all
1193+
forninrange(1,ln+1):
1194+
forindexesinitertools.combinations(range(ln),n):
1195+
withself.subTest(remove=indexes):
1196+
self._test_removing_indexes(test_files,indexes)
1197+
else:
1198+
forindexesinitertools.combinations(range(ln),n):
1199+
withself.subTest(remove=indexes):
1200+
self._test_removing_indexes(test_files,indexes)
1201+
1202+
deftest_basic(self):
1203+
# Test underlying _remove_members() for removing random combinations of members.
1204+
test_files= [
1205+
('file0.txt',b'Lorem ipsum dolor sit amet, consectetur adipiscing elit'),
1206+
('file1.txt',b'Duis aute irure dolor in reprehenderit in voluptate velit esse'),
1207+
('file2.txt',b'Sed ut perspiciatis unde omnis iste natus error sit voluptatem'),
1208+
]
1209+
1210+
self._test_removing_combinations(test_files)
1211+
1212+
deftest_duplicated_arcname(self):
1213+
# Test underlying _remove_members() for removing any one of random duplicated members.
1214+
dupl_file='file.txt'
1215+
test_files= [
1216+
('file0.txt',b'Lorem ipsum dolor sit amet, consectetur adipiscing elit'),
1217+
('file1.txt',b'Duis aute irure dolor in reprehenderit in voluptate velit esse'),
1218+
('file2.txt',b'Sed ut perspiciatis unde omnis iste natus error sit voluptatem'),
1219+
]
1220+
1221+
ln=len(test_files)
1222+
forninrange(2,ln+1):
1223+
fordupsinitertools.combinations(range(ln),n):
1224+
files= []
1225+
fori, (file,data)inenumerate(test_files):
1226+
file_=dupl_fileifiindupselsefile
1227+
files.append((file_,data))
1228+
1229+
forindexindups:
1230+
indexes= [index]
1231+
withself.subTest(dups=dups,indexes=indexes):
1232+
self._test_removing_indexes(files,indexes)
1233+
1234+
deftest_non_physical(self):
1235+
# Test underlying _remove_members() for non-physical removing.
1236+
test_files= [
1237+
('file0.txt',b'Lorem ipsum dolor sit amet, consectetur adipiscing elit'),
1238+
('file1.txt',b'Duis aute irure dolor in reprehenderit in voluptate velit esse'),
1239+
('file2.txt',b'Sed ut perspiciatis unde omnis iste natus error sit voluptatem'),
1240+
]
1241+
1242+
ln=len(test_files)
1243+
forninrange(1,ln+1):
1244+
forindexesinitertools.combinations(range(ln),n):
1245+
withself.subTest(remove=indexes):
1246+
# prepare the test zip
1247+
expected= {}
1248+
withzipfile.ZipFile(TESTFN,'w')aszh:
1249+
fori, (file,data)inenumerate(test_files):
1250+
zh.writestr(file,data)
1251+
ifinotinindexes:
1252+
expected[file]=zh.getinfo(file).header_offset
1253+
1254+
# do the removal and check the result
1255+
withzipfile.ZipFile(TESTFN,'a')aszh:
1256+
members= {zh.infolist()[i]foriinindexes}
1257+
zh._remove_members(members,remove_physical=False)
1258+
self.assertEqual(zh.namelist(),list(expected))
1259+
forfile,offsetinexpected.items():
1260+
self.assertEqual(zh.getinfo(file).header_offset,offset)
1261+
self.assertIsNone(zh.testzip())
1262+
1263+
deftest_verify(self):
1264+
# Test if params are passed to underlying _remove_members() correctly,
1265+
# or never passed if conditions not met.
1266+
file0='file0.txt'
1267+
data0=b'Lorem ipsum dolor sit amet, consectetur adipiscing elit'
1268+
file='datafile.txt'
1269+
data=b'Sed ut perspiciatis unde omnis iste natus error sit voluptatem'
1270+
1271+
# closed: error and do nothing
1272+
withzipfile.ZipFile(TESTFN,'w')aszh:
1273+
zh.writestr(file,data)
1274+
withzipfile.ZipFile(TESTFN,'a')aszh:
1275+
zh.close()
1276+
withmock.patch('zipfile.ZipFile._remove_members')asmock_fn:
1277+
withself.assertRaises(ValueError):
1278+
zh.remove(file)
1279+
mock_fn.assert_not_called()
1280+
1281+
# writing: error and do nothing
1282+
withzipfile.ZipFile(TESTFN,'w')aszh:
1283+
zh.writestr(file,data)
1284+
withzipfile.ZipFile(TESTFN,'a')aszh:
1285+
withmock.patch('zipfile.ZipFile._remove_members')asmock_fn:
1286+
withzh.open(file0,'w')asfh:
1287+
withself.assertRaises(ValueError):
1288+
zh.remove(file)
1289+
mock_fn.assert_not_called()
1290+
1291+
# mode 'r': error and do nothing
1292+
withzipfile.ZipFile(TESTFN,'r')aszh:
1293+
withmock.patch('zipfile.ZipFile._remove_members')asmock_fn:
1294+
withself.assertRaises(ValueError):
1295+
zh.remove(file)
1296+
mock_fn.assert_not_called()
1297+
1298+
# mode 'a': the most general use case
1299+
withzipfile.ZipFile(TESTFN,'w')aszh:
1300+
zh.writestr(file,data)
1301+
# -- remove with arcname
1302+
withzipfile.ZipFile(TESTFN,'a')aszh:
1303+
withmock.patch('zipfile.ZipFile._remove_members')asmock_fn:
1304+
zh.remove(file)
1305+
mock_fn.assert_called_once_with({zh.getinfo(file)})
1306+
# -- remove with zinfo
1307+
withzipfile.ZipFile(TESTFN,'a')aszh:
1308+
withmock.patch('zipfile.ZipFile._remove_members')asmock_fn:
1309+
zinfo=zh.getinfo(file)
1310+
zh.remove(zinfo)
1311+
mock_fn.assert_called_once_with({zinfo})
1312+
# -- remove with nonexist arcname
1313+
withzipfile.ZipFile(TESTFN,'a')aszh:
1314+
withmock.patch('zipfile.ZipFile._remove_members')asmock_fn:
1315+
withself.assertRaises(KeyError):
1316+
zh.remove('nonexist.file')
1317+
mock_fn.assert_not_called()
1318+
# -- remove with nonexist zinfo (even if same name)
1319+
withzipfile.ZipFile(TESTFN,'a')aszh:
1320+
withmock.patch('zipfile.ZipFile._remove_members')asmock_fn:
1321+
zinfo=zipfile.ZipInfo(file)
1322+
withself.assertRaises(KeyError):
1323+
zh.remove(zinfo)
1324+
mock_fn.assert_not_called()
1325+
1326+
# mode 'w': like 'a'; allows removing a just written member
1327+
withzipfile.ZipFile(TESTFN,'w')aszh:
1328+
zh.writestr(file,data)
1329+
withmock.patch('zipfile.ZipFile._remove_members')asmock_fn:
1330+
zh.remove(file)
1331+
mock_fn.assert_called_once_with({zh.getinfo(file)})
1332+
1333+
# mode 'x': like 'w'
1334+
os.remove(TESTFN)
1335+
withzipfile.ZipFile(TESTFN,'x')aszh:
1336+
zh.writestr(file,data)
1337+
withmock.patch('zipfile.ZipFile._remove_members')asmock_fn:
1338+
zh.remove(file)
1339+
mock_fn.assert_called_once_with({zh.getinfo(file)})
1340+
1341+
deftest_zip64(self):
1342+
# Test if members use zip64.
1343+
file='datafile.txt'
1344+
file1='pre.txt'
1345+
file2='post.txt'
1346+
data=b'Sed ut perspiciatis unde omnis iste natus error sit voluptatem'
1347+
data1=b'Lorem ipsum dolor sit amet, consectetur adipiscing elit'
1348+
data2=b'Duis aute irure dolor in reprehenderit in voluptate velit esse'
1349+
withzipfile.ZipFile(TESTFN,'w')aszh:
1350+
withzh.open(file1,'w',force_zip64=True)asfh:
1351+
fh.write(data1)
1352+
withzh.open(file2,'w',force_zip64=True)asfh:
1353+
fh.write(data2)
1354+
expected_size=os.path.getsize(TESTFN)
1355+
1356+
withzipfile.ZipFile(TESTFN,'w')aszh:
1357+
withzh.open(file1,'w',force_zip64=True)asfh:
1358+
fh.write(data1)
1359+
withzh.open(file,'w',force_zip64=True)asfh:
1360+
fh.write(data)
1361+
withzh.open(file2,'w',force_zip64=True)asfh:
1362+
fh.write(data2)
1363+
withzipfile.ZipFile(TESTFN,'a')aszh:
1364+
zh.remove(file)
1365+
self.assertIsNone(zh.testzip())
1366+
self.assertEqual(os.path.getsize(TESTFN),expected_size)
1367+
1368+
classStoredRemoveTests(AbstractRemoveTests,unittest.TestCase):
1369+
compression=zipfile.ZIP_STORED
1370+
1371+
@requires_zlib()
1372+
classDeflateRemoveTests(AbstractRemoveTests,unittest.TestCase):
1373+
compression=zipfile.ZIP_DEFLATED
1374+
1375+
@requires_bz2()
1376+
classBzip2RemoveTests(AbstractRemoveTests,unittest.TestCase):
1377+
compression=zipfile.ZIP_BZIP2
1378+
1379+
@requires_lzma()
1380+
classLzmaRemoveTests(AbstractRemoveTests,unittest.TestCase):
1381+
compression=zipfile.ZIP_LZMA
1382+
1383+
11501384
classPyZipFileTests(unittest.TestCase):
11511385
defassertCompiledIn(self,name,namelist):
11521386
ifname+'o'notinnamelist:

‎Lib/test/test_zipfile64.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,69 @@ def tearDown(self):
8787
os_helper.unlink(TESTFN2)
8888

8989

90+
classTestRemove(unittest.TestCase):
91+
defsetUp(self):
92+
# Create test data.
93+
line_gen= ("Test of zipfile line %d."%iforiinrange(1000000))
94+
self.data='\n'.join(line_gen).encode('ascii')
95+
96+
def_write_large_file(self,fh):
97+
# It will contain enough copies of self.data to reach about 8 GiB.
98+
filecount=8*1024**3//len(self.data)
99+
100+
next_time=time.monotonic()+_PRINT_WORKING_MSG_INTERVAL
101+
fornuminrange(filecount):
102+
fh.write(self.data)
103+
# Print still working message since this test can be really slow
104+
ifnext_time<=time.monotonic():
105+
next_time=time.monotonic()+_PRINT_WORKING_MSG_INTERVAL
106+
print((
107+
' writing %d of %d, be patient...'%
108+
(num,filecount)),file=sys.__stdout__)
109+
sys.__stdout__.flush()
110+
111+
deftest_remove_large_file(self):
112+
# Try the temp file. If we do TESTFN2, then it hogs
113+
# gigabytes of disk space for the duration of the test.
114+
withTemporaryFile()asf:
115+
self._test_remove_large_file(f)
116+
self.assertFalse(f.closed)
117+
118+
def_test_remove_large_file(self,f):
119+
file='datafile.txt'
120+
file1='dummy.txt'
121+
data=b'Sed ut perspiciatis unde omnis iste natus error sit voluptatem'
122+
withzipfile.ZipFile(f,'w')aszh:
123+
withzh.open(file1,'w',force_zip64=True)asfh:
124+
self._write_large_file(fh)
125+
zh.writestr(file,data)
126+
127+
withzipfile.ZipFile(f,'a')aszh:
128+
zh.remove(file1)
129+
self.assertIsNone(zh.testzip())
130+
131+
deftest_remove_before_large_file(self):
132+
# Try the temp file. If we do TESTFN2, then it hogs
133+
# gigabytes of disk space for the duration of the test.
134+
withTemporaryFile()asf:
135+
self._test_remove_before_large_file(f)
136+
self.assertFalse(f.closed)
137+
138+
def_test_remove_before_large_file(self,f):
139+
file='datafile.txt'
140+
file1='dummy.txt'
141+
data=b'Sed ut perspiciatis unde omnis iste natus error sit voluptatem'
142+
withzipfile.ZipFile(f,'w')aszh:
143+
zh.writestr(file,data)
144+
withzh.open(file1,'w',force_zip64=True)asfh:
145+
self._write_large_file(fh)
146+
expected_size=zh.getinfo(file1).file_size
147+
148+
withzipfile.ZipFile(f,'a')aszh:
149+
zh.remove(file)
150+
self.assertIsNone(zh.testzip())
151+
152+
90153
classOtherTests(unittest.TestCase):
91154
deftestMoreThan64kFiles(self):
92155
# This test checks that more than 64k files can be added to an archive,

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp