summaryrefslogtreecommitdiffstats
path: root/Lib/zipfile.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/zipfile.py')
-rw-r--r--Lib/zipfile.py103
1 files changed, 39 insertions, 64 deletions
diff --git a/Lib/zipfile.py b/Lib/zipfile.py
index cc15ed3..bda6134 100644
--- a/Lib/zipfile.py
+++ b/Lib/zipfile.py
@@ -624,25 +624,6 @@ def _get_decompressor(compress_type):
raise NotImplementedError("compression type %d" % (compress_type,))
-class _SharedFile:
- def __init__(self, file, pos, close):
- self._file = file
- self._pos = pos
- self._close = close
-
- def read(self, n=-1):
- self._file.seek(self._pos)
- data = self._file.read(n)
- self._pos = self._file.tell()
- return data
-
- def close(self):
- if self._file is not None:
- fileobj = self._file
- self._file = None
- self._close(fileobj)
-
-
class ZipExtFile(io.BufferedIOBase):
"""File-like object for reading an archive member.
Is returned by ZipFile.open().
@@ -928,7 +909,7 @@ class ZipFile:
self.NameToInfo = {} # Find file info given name
self.filelist = [] # List of ZipInfo instances for archive
self.compression = compression # Method of compression
- self.mode = mode
+ self.mode = key = mode.replace('b', '')[0]
self.pwd = None
self._comment = b''
@@ -937,33 +918,28 @@ class ZipFile:
# No, it's a filename
self._filePassed = 0
self.filename = file
- modeDict = {'r' : 'rb', 'w': 'w+b', 'a' : 'r+b',
- 'r+b': 'w+b', 'w+b': 'wb'}
- filemode = modeDict[mode]
- while True:
- try:
- self.fp = io.open(file, filemode)
- except OSError:
- if filemode in modeDict:
- filemode = modeDict[filemode]
- continue
+ modeDict = {'r' : 'rb', 'w': 'wb', 'a' : 'r+b'}
+ try:
+ self.fp = io.open(file, modeDict[mode])
+ except OSError:
+ if mode == 'a':
+ mode = key = 'w'
+ self.fp = io.open(file, modeDict[mode])
+ else:
raise
- break
else:
self._filePassed = 1
self.fp = file
self.filename = getattr(file, 'name', None)
- self._fileRefCnt = 1
try:
- if mode == 'r':
+ if key == 'r':
self._RealGetContents()
- elif mode == 'w':
+ elif key == 'w':
# set the modified flag so central directory gets written
# even if no files are added to the archive
self._didModify = True
- self.start_dir = 0
- elif mode == 'a':
+ elif key == 'a':
try:
# See if file is a zip file
self._RealGetContents()
@@ -976,13 +952,13 @@ class ZipFile:
# set the modified flag so central directory gets written
# even if no files are added to the archive
self._didModify = True
- self.start_dir = self.fp.tell()
else:
raise RuntimeError('Mode must be "r", "w" or "a"')
except:
fp = self.fp
self.fp = None
- self._fpclose(fp)
+ if not self._filePassed:
+ fp.close()
raise
def __enter__(self):
@@ -1155,17 +1131,23 @@ class ZipFile:
raise RuntimeError(
"Attempt to read ZIP archive that was already closed")
- # Make sure we have an info object
- if isinstance(name, ZipInfo):
- # 'name' is already an info object
- zinfo = name
+ # Only open a new file for instances where we were not
+ # given a file object in the constructor
+ if self._filePassed:
+ zef_file = self.fp
else:
- # Get info object for name
- zinfo = self.getinfo(name)
+ zef_file = io.open(self.filename, 'rb')
- self._fileRefCnt += 1
- zef_file = _SharedFile(self.fp, zinfo.header_offset, self._fpclose)
try:
+ # Make sure we have an info object
+ if isinstance(name, ZipInfo):
+ # 'name' is already an info object
+ zinfo = name
+ else:
+ # Get info object for name
+ zinfo = self.getinfo(name)
+ zef_file.seek(zinfo.header_offset, 0)
+
# Skip the file header:
fheader = zef_file.read(sizeFileHeader)
if len(fheader) != sizeFileHeader:
@@ -1224,9 +1206,11 @@ class ZipFile:
if h[11] != check_byte:
raise RuntimeError("Bad password for file", name)
- return ZipExtFile(zef_file, mode, zinfo, zd, True)
+ return ZipExtFile(zef_file, mode, zinfo, zd,
+ close_fileobj=not self._filePassed)
except:
- zef_file.close()
+ if not self._filePassed:
+ zef_file.close()
raise
def extract(self, member, path=None, pwd=None):
@@ -1360,7 +1344,6 @@ class ZipFile:
zinfo.file_size = st.st_size
zinfo.flag_bits = 0x00
- self.fp.seek(self.start_dir, 0)
zinfo.header_offset = self.fp.tell() # Start of header bytes
if zinfo.compress_type == ZIP_LZMA:
# Compressed data includes an end-of-stream (EOS) marker
@@ -1377,7 +1360,6 @@ class ZipFile:
self.filelist.append(zinfo)
self.NameToInfo[zinfo.filename] = zinfo
self.fp.write(zinfo.FileHeader(False))
- self.start_dir = self.fp.tell()
return
cmpr = _get_compressor(zinfo.compress_type)
@@ -1416,10 +1398,10 @@ class ZipFile:
raise RuntimeError('Compressed size larger than uncompressed size')
# Seek backwards and write file header (which will now include
# correct CRC and file sizes)
- self.start_dir = self.fp.tell() # Preserve current position in file
+ position = self.fp.tell() # Preserve current position in file
self.fp.seek(zinfo.header_offset, 0)
self.fp.write(zinfo.FileHeader(zip64))
- self.fp.seek(self.start_dir, 0)
+ self.fp.seek(position, 0)
self.filelist.append(zinfo)
self.NameToInfo[zinfo.filename] = zinfo
@@ -1448,7 +1430,6 @@ class ZipFile:
"Attempt to write to ZIP archive that was already closed")
zinfo.file_size = len(data) # Uncompressed size
- self.fp.seek(self.start_dir, 0)
zinfo.header_offset = self.fp.tell() # Start of header data
if compress_type is not None:
zinfo.compress_type = compress_type
@@ -1477,7 +1458,6 @@ class ZipFile:
self.fp.write(struct.pack(fmt, zinfo.CRC, zinfo.compress_size,
zinfo.file_size))
self.fp.flush()
- self.start_dir = self.fp.tell()
self.filelist.append(zinfo)
self.NameToInfo[zinfo.filename] = zinfo
@@ -1493,7 +1473,7 @@ class ZipFile:
try:
if self.mode in ("w", "a") and self._didModify: # write ending records
- self.fp.seek(self.start_dir, 0)
+ pos1 = self.fp.tell()
for zinfo in self.filelist: # write central directory
dt = zinfo.date_time
dosdate = (dt[0] - 1980) << 9 | dt[1] << 5 | dt[2]
@@ -1559,8 +1539,8 @@ class ZipFile:
pos2 = self.fp.tell()
# Write end-of-zip-archive record
centDirCount = len(self.filelist)
- centDirSize = pos2 - self.start_dir
- centDirOffset = self.start_dir
+ centDirSize = pos2 - pos1
+ centDirOffset = pos1
requires_zip64 = None
if centDirCount > ZIP_FILECOUNT_LIMIT:
requires_zip64 = "Files count"
@@ -1596,13 +1576,8 @@ class ZipFile:
finally:
fp = self.fp
self.fp = None
- self._fpclose(fp)
-
- def _fpclose(self, fp):
- assert self._fileRefCnt > 0
- self._fileRefCnt -= 1
- if not self._fileRefCnt and not self._filePassed:
- fp.close()
+ if not self._filePassed:
+ fp.close()
class PyZipFile(ZipFile):