@@ -624,25 +624,6 @@ def _get_decompressor(compress_type):
624624 raise NotImplementedError ("compression type %d" % (compress_type ,))
625625
626626
627- class _SharedFile :
628- def __init__ (self , file , pos , close ):
629- self ._file = file
630- self ._pos = pos
631- self ._close = close
632-
633- def read (self , n = - 1 ):
634- self ._file .seek (self ._pos )
635- data = self ._file .read (n )
636- self ._pos = self ._file .tell ()
637- return data
638-
639- def close (self ):
640- if self ._file is not None :
641- fileobj = self ._file
642- self ._file = None
643- self ._close (fileobj )
644-
645-
646627class ZipExtFile (io .BufferedIOBase ):
647628 """File-like object for reading an archive member.
648629 Is returned by ZipFile.open().
@@ -928,7 +909,7 @@ def __init__(self, file, mode="r", compression=ZIP_STORED, allowZip64=True):
928909 self .NameToInfo = {} # Find file info given name
929910 self .filelist = [] # List of ZipInfo instances for archive
930911 self .compression = compression # Method of compression
931- self .mode = mode
912+ self .mode = key = mode . replace ( 'b' , '' )[ 0 ]
932913 self .pwd = None
933914 self ._comment = b''
934915
@@ -937,33 +918,28 @@ def __init__(self, file, mode="r", compression=ZIP_STORED, allowZip64=True):
937918 # No, it's a filename
938919 self ._filePassed = 0
939920 self .filename = file
940- modeDict = {'r' : 'rb' , 'w' : 'w+b' , 'a' : 'r+b' ,
941- 'r+b' : 'w+b' , 'w+b' : 'wb' }
942- filemode = modeDict [mode ]
943- while True :
944- try :
945- self .fp = io .open (file , filemode )
946- except OSError :
947- if filemode in modeDict :
948- filemode = modeDict [filemode ]
949- continue
921+ modeDict = {'r' : 'rb' , 'w' : 'wb' , 'a' : 'r+b' }
922+ try :
923+ self .fp = io .open (file , modeDict [mode ])
924+ except OSError :
925+ if mode == 'a' :
926+ mode = key = 'w'
927+ self .fp = io .open (file , modeDict [mode ])
928+ else :
950929 raise
951- break
952930 else :
953931 self ._filePassed = 1
954932 self .fp = file
955933 self .filename = getattr (file , 'name' , None )
956- self ._fileRefCnt = 1
957934
958935 try :
959- if mode == 'r' :
936+ if key == 'r' :
960937 self ._RealGetContents ()
961- elif mode == 'w' :
938+ elif key == 'w' :
962939 # set the modified flag so central directory gets written
963940 # even if no files are added to the archive
964941 self ._didModify = True
965- self .start_dir = 0
966- elif mode == 'a' :
942+ elif key == 'a' :
967943 try :
968944 # See if file is a zip file
969945 self ._RealGetContents ()
@@ -976,13 +952,13 @@ def __init__(self, file, mode="r", compression=ZIP_STORED, allowZip64=True):
976952 # set the modified flag so central directory gets written
977953 # even if no files are added to the archive
978954 self ._didModify = True
979- self .start_dir = self .fp .tell ()
980955 else :
981956 raise RuntimeError ('Mode must be "r", "w" or "a"' )
982957 except :
983958 fp = self .fp
984959 self .fp = None
985- self ._fpclose (fp )
960+ if not self ._filePassed :
961+ fp .close ()
986962 raise
987963
988964 def __enter__ (self ):
@@ -1155,17 +1131,23 @@ def open(self, name, mode="r", pwd=None):
11551131 raise RuntimeError (
11561132 "Attempt to read ZIP archive that was already closed" )
11571133
1158- # Make sure we have an info object
1159- if isinstance ( name , ZipInfo ):
1160- # 'name' is already an info object
1161- zinfo = name
1134+ # Only open a new file for instances where we were not
1135+ # given a file object in the constructor
1136+ if self . _filePassed :
1137+ zef_file = self . fp
11621138 else :
1163- # Get info object for name
1164- zinfo = self .getinfo (name )
1139+ zef_file = io .open (self .filename , 'rb' )
11651140
1166- self ._fileRefCnt += 1
1167- zef_file = _SharedFile (self .fp , zinfo .header_offset , self ._fpclose )
11681141 try :
1142+ # Make sure we have an info object
1143+ if isinstance (name , ZipInfo ):
1144+ # 'name' is already an info object
1145+ zinfo = name
1146+ else :
1147+ # Get info object for name
1148+ zinfo = self .getinfo (name )
1149+ zef_file .seek (zinfo .header_offset , 0 )
1150+
11691151 # Skip the file header:
11701152 fheader = zef_file .read (sizeFileHeader )
11711153 if len (fheader ) != sizeFileHeader :
@@ -1224,9 +1206,11 @@ def open(self, name, mode="r", pwd=None):
12241206 if h [11 ] != check_byte :
12251207 raise RuntimeError ("Bad password for file" , name )
12261208
1227- return ZipExtFile (zef_file , mode , zinfo , zd , True )
1209+ return ZipExtFile (zef_file , mode , zinfo , zd ,
1210+ close_fileobj = not self ._filePassed )
12281211 except :
1229- zef_file .close ()
1212+ if not self ._filePassed :
1213+ zef_file .close ()
12301214 raise
12311215
12321216 def extract (self , member , path = None , pwd = None ):
@@ -1360,7 +1344,6 @@ def write(self, filename, arcname=None, compress_type=None):
13601344
13611345 zinfo .file_size = st .st_size
13621346 zinfo .flag_bits = 0x00
1363- self .fp .seek (self .start_dir , 0 )
13641347 zinfo .header_offset = self .fp .tell () # Start of header bytes
13651348 if zinfo .compress_type == ZIP_LZMA :
13661349 # Compressed data includes an end-of-stream (EOS) marker
@@ -1377,7 +1360,6 @@ def write(self, filename, arcname=None, compress_type=None):
13771360 self .filelist .append (zinfo )
13781361 self .NameToInfo [zinfo .filename ] = zinfo
13791362 self .fp .write (zinfo .FileHeader (False ))
1380- self .start_dir = self .fp .tell ()
13811363 return
13821364
13831365 cmpr = _get_compressor (zinfo .compress_type )
@@ -1416,10 +1398,10 @@ def write(self, filename, arcname=None, compress_type=None):
14161398 raise RuntimeError ('Compressed size larger than uncompressed size' )
14171399 # Seek backwards and write file header (which will now include
14181400 # correct CRC and file sizes)
1419- self . start_dir = self .fp .tell () # Preserve current position in file
1401+ position = self .fp .tell () # Preserve current position in file
14201402 self .fp .seek (zinfo .header_offset , 0 )
14211403 self .fp .write (zinfo .FileHeader (zip64 ))
1422- self .fp .seek (self . start_dir , 0 )
1404+ self .fp .seek (position , 0 )
14231405 self .filelist .append (zinfo )
14241406 self .NameToInfo [zinfo .filename ] = zinfo
14251407
@@ -1448,7 +1430,6 @@ def writestr(self, zinfo_or_arcname, data, compress_type=None):
14481430 "Attempt to write to ZIP archive that was already closed" )
14491431
14501432 zinfo .file_size = len (data ) # Uncompressed size
1451- self .fp .seek (self .start_dir , 0 )
14521433 zinfo .header_offset = self .fp .tell () # Start of header data
14531434 if compress_type is not None :
14541435 zinfo .compress_type = compress_type
@@ -1477,7 +1458,6 @@ def writestr(self, zinfo_or_arcname, data, compress_type=None):
14771458 self .fp .write (struct .pack (fmt , zinfo .CRC , zinfo .compress_size ,
14781459 zinfo .file_size ))
14791460 self .fp .flush ()
1480- self .start_dir = self .fp .tell ()
14811461 self .filelist .append (zinfo )
14821462 self .NameToInfo [zinfo .filename ] = zinfo
14831463
@@ -1493,7 +1473,7 @@ def close(self):
14931473
14941474 try :
14951475 if self .mode in ("w" , "a" ) and self ._didModify : # write ending records
1496- self .fp .seek ( self . start_dir , 0 )
1476+ pos1 = self .fp .tell ( )
14971477 for zinfo in self .filelist : # write central directory
14981478 dt = zinfo .date_time
14991479 dosdate = (dt [0 ] - 1980 ) << 9 | dt [1 ] << 5 | dt [2 ]
@@ -1559,8 +1539,8 @@ def close(self):
15591539 pos2 = self .fp .tell ()
15601540 # Write end-of-zip-archive record
15611541 centDirCount = len (self .filelist )
1562- centDirSize = pos2 - self . start_dir
1563- centDirOffset = self . start_dir
1542+ centDirSize = pos2 - pos1
1543+ centDirOffset = pos1
15641544 requires_zip64 = None
15651545 if centDirCount > ZIP_FILECOUNT_LIMIT :
15661546 requires_zip64 = "Files count"
@@ -1596,13 +1576,8 @@ def close(self):
15961576 finally :
15971577 fp = self .fp
15981578 self .fp = None
1599- self ._fpclose (fp )
1600-
1601- def _fpclose (self , fp ):
1602- assert self ._fileRefCnt > 0
1603- self ._fileRefCnt -= 1
1604- if not self ._fileRefCnt and not self ._filePassed :
1605- fp .close ()
1579+ if not self ._filePassed :
1580+ fp .close ()
16061581
16071582
16081583class PyZipFile (ZipFile ):
0 commit comments