Skip to content

Commit c7589a9

Browse files
committed
Initial commit
0 parents  commit c7589a9

File tree

4 files changed

+132
-0
lines changed

4 files changed

+132
-0
lines changed

.gitattributes

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Auto detect text files and perform LF normalization
2+
* text=auto

.gitignore

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# excel sheet unblocker
2+
# ©2021 0ti
3+
4+
__pycache__/
5+
*.xlsx

resources/updatezip.py

+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import os
2+
import shutil
3+
import tempfile
4+
from zipfile import ZipFile, ZIP_STORED, ZipInfo
5+
6+
7+
class UpdateableZipFile(ZipFile):
8+
"""
9+
Add delete (via remove_file) and update (via writestr and write methods)
10+
To enable update features use UpdateableZipFile with the 'with statement',
11+
Upon __exit__ (if updates were applied) a new zip file will override the exiting one with the updates
12+
"""
13+
14+
class DeleteMarker(object):
15+
pass
16+
17+
def __init__(self, file, mode="r", compression=ZIP_STORED, allowZip64=False):
18+
# Init base
19+
super(UpdateableZipFile, self).__init__(file, mode=mode,
20+
compression=compression,
21+
allowZip64=allowZip64)
22+
# track file to override in zip
23+
self._replace = {}
24+
# Whether the with statement was called
25+
self._allow_updates = False
26+
27+
def writestr(self, zinfo_or_arcname, bytes, compress_type=None):
28+
if isinstance(zinfo_or_arcname, ZipInfo):
29+
name = zinfo_or_arcname.filename
30+
else:
31+
name = zinfo_or_arcname
32+
# If the file exits, and needs to be overridden,
33+
# mark the entry, and create a temp-file for it
34+
# we allow this only if the with statement is used
35+
if self._allow_updates and name in self.namelist():
36+
temp_file = self._replace[name] = self._replace.get(name,
37+
tempfile.TemporaryFile())
38+
temp_file.write(bytes)
39+
# Otherwise just act normally
40+
else:
41+
super(UpdateableZipFile, self).writestr(zinfo_or_arcname,
42+
bytes, compress_type=compress_type, compresslevel=None)
43+
44+
def write(self, filename, arcname=None, compress_type=None):
45+
arcname = arcname or filename
46+
# If the file exits, and needs to be overridden,
47+
# mark the entry, and create a temp-file for it
48+
# we allow this only if the with statement is used
49+
if self._allow_updates and arcname in self.namelist():
50+
temp_file = self._replace[arcname] = self._replace.get(arcname,
51+
tempfile.TemporaryFile())
52+
with open(filename, "rb") as source:
53+
shutil.copyfileobj(source, temp_file)
54+
# Otherwise just act normally
55+
else:
56+
super(UpdateableZipFile, self).write(filename,
57+
arcname=arcname, compress_type=compress_type)
58+
59+
def __enter__(self):
60+
# Allow updates
61+
self._allow_updates = True
62+
return self
63+
64+
def __exit__(self, exc_type, exc_val, exc_tb):
65+
# call base to close zip file, organically
66+
try:
67+
super(UpdateableZipFile, self).__exit__(exc_type, exc_val, exc_tb)
68+
if len(self._replace) > 0:
69+
self._rebuild_zip()
70+
finally:
71+
# In case rebuild zip failed,
72+
# be sure to still release all the temp files
73+
self._close_all_temp_files()
74+
self._allow_updates = False
75+
76+
def _close_all_temp_files(self):
77+
for temp_file in self._replace.values():
78+
if hasattr(temp_file, 'close'):
79+
temp_file.close()
80+
81+
def remove_file(self, path):
82+
self._replace[path] = self.DeleteMarker()
83+
84+
def _rebuild_zip(self):
85+
tempdir = tempfile.mkdtemp()
86+
try:
87+
temp_zip_path = os.path.join(tempdir, 'new.zip')
88+
with ZipFile(self.filename, 'r') as zip_read:
89+
# Create new zip with assigned properties
90+
with ZipFile(temp_zip_path, 'w', compression=self.compression,
91+
allowZip64=self._allowZip64) as zip_write:
92+
for item in zip_read.infolist():
93+
# Check if the file should be replaced / or deleted
94+
replacement = self._replace.get(item.filename, None)
95+
# If marked for deletion, do not copy file to new zipfile
96+
if isinstance(replacement, self.DeleteMarker):
97+
del self._replace[item.filename]
98+
continue
99+
# If marked for replacement, copy temp_file, instead of old file
100+
elif replacement is not None:
101+
del self._replace[item.filename]
102+
# Write replacement to archive,
103+
# and then close it (deleting the temp file)
104+
replacement.seek(0)
105+
data = replacement.read()
106+
replacement.close()
107+
else:
108+
data = zip_read.read(item.filename)
109+
zip_write.writestr(item, data, compresslevel=None)
110+
# Override the archive with the updated one
111+
shutil.move(temp_zip_path, self.filename)
112+
finally:
113+
shutil.rmtree(tempdir)

unblock.py

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import zipfile, os, tempfile, fnmatch
2+
from resources.updatezip import UpdateableZipFile as uzip
3+
4+
fpaths = []
5+
6+
# get all sheet paths
7+
zip = zipfile.ZipFile('./test.xlsx')
8+
for filename in zip.namelist():
9+
if filename.startswith('xl/worksheets/sheet') and filename.endswith('.xml'):
10+
fpaths.append(filename)
11+
12+

0 commit comments

Comments
 (0)