@@ -170,8 +170,8 @@ def __init__(self, fileobj=None, mode='w', compression=ZIP_STORED, allowZip64=Fa
170170 self .paths_to_write = []
171171
172172 def __iter__ (self ):
173- for args , kwargs in self .paths_to_write :
174- for data in self .__write (* args , * *kwargs ):
173+ for kwargs in self .paths_to_write :
174+ for data in self .__write (** kwargs ):
175175 yield data
176176 for data in self .__close ():
177177 yield data
@@ -204,21 +204,30 @@ def write(self, filename, arcname=None, compress_type=None):
204204 # TODO: Reflect python's Zipfile.write
205205 # - if filename is file, write as file
206206 # - if filename is directory, write an empty directory
207- self .paths_to_write .append (
208- ((filename , ), {'arcname' : arcname , 'compress_type' : compress_type }),
209- )
207+ kwargs = {'filename' : filename , 'arcname' : arcname , 'compress_type' : compress_type }
208+ self .paths_to_write .append (kwargs )
210209
211- def __write (self , filename , arcname = None , compress_type = None ):
210+ def write_iter (self , arcname , iterable , compress_type = None ):
211+ """Write the bytes iterable `iterable` to the archive under the name `arcname`."""
212+ kwargs = {'arcname' : arcname , 'iterable' : iterable , 'compress_type' : compress_type }
213+ self .paths_to_write .append (kwargs )
214+
215+ def __write (self , filename = None , iterable = None , arcname = None , compress_type = None ):
212216 """Put the bytes from filename into the archive under the name
213- arcname."""
217+ ` arcname` ."""
214218 if not self .fp :
215219 raise RuntimeError (
216220 "Attempt to write to ZIP archive that was already closed" )
217-
218- st = os .stat (filename )
219- isdir = stat .S_ISDIR (st .st_mode )
220- mtime = time .localtime (st .st_mtime )
221- date_time = mtime [0 :6 ]
221+ if (filename is None and iterable is None ) or (filename is not None and iterable is not None ):
222+ raise ValueError ("either (exclusively) filename or iterable shall be not None" )
223+
224+ if filename :
225+ st = os .stat (filename )
226+ isdir = stat .S_ISDIR (st .st_mode )
227+ mtime = time .localtime (st .st_mtime )
228+ date_time = mtime [0 :6 ]
229+ else :
230+ st , isdir , date_time = None , False , time .localtime ()[0 :6 ]
222231 # Create ZipInfo instance to store file information
223232 if arcname is None :
224233 arcname = filename
@@ -228,13 +237,16 @@ def __write(self, filename, arcname=None, compress_type=None):
228237 if isdir :
229238 arcname += '/'
230239 zinfo = ZipInfo (arcname , date_time )
231- zinfo .external_attr = (st [0 ] & 0xFFFF ) << 16 # Unix attributes
240+ if st :
241+ zinfo .external_attr = (st [0 ] & 0xFFFF ) << 16 # Unix attributes
242+ else :
243+ zinfo .external_attr = 0o600 << 16 # ?rw-------
232244 if compress_type is None :
233245 zinfo .compress_type = self .compression
234246 else :
235247 zinfo .compress_type = compress_type
236248
237- zinfo .file_size = st . st_size
249+ zinfo .file_size = 0
238250 zinfo .flag_bits = 0x00
239251 zinfo .flag_bits |= 0x08 # ZIP flag bits, bit 3 indicates presence of data descriptor
240252 zinfo .header_offset = self .fp .tell () # Start of header bytes
@@ -255,19 +267,29 @@ def __write(self, filename, arcname=None, compress_type=None):
255267 return
256268
257269 cmpr = _get_compressor (zinfo .compress_type )
258- with open (filename , 'rb' ) as fp :
259- # Must overwrite CRC and sizes with correct data later
260- zinfo .CRC = CRC = 0
261- zinfo .compress_size = compress_size = 0
262- # Compressed size can be larger than uncompressed size
263- zip64 = self ._allowZip64 and \
264- zinfo .file_size * 1.05 > ZIP64_LIMIT
265- yield self .fp .write (zinfo .FileHeader (zip64 ))
266- file_size = 0
267- while 1 :
268- buf = fp .read (1024 * 8 )
269- if not buf :
270- break
270+
271+ # Must overwrite CRC and sizes with correct data later
272+ zinfo .CRC = CRC = 0
273+ zinfo .compress_size = compress_size = 0
274+ # Compressed size can be larger than uncompressed size
275+ zip64 = self ._allowZip64 and \
276+ zinfo .file_size * 1.05 > ZIP64_LIMIT
277+ yield self .fp .write (zinfo .FileHeader (zip64 ))
278+ file_size = 0
279+ if filename :
280+ with open (filename , 'rb' ) as fp :
281+ while 1 :
282+ buf = fp .read (1024 * 8 )
283+ if not buf :
284+ break
285+ file_size = file_size + len (buf )
286+ CRC = crc32 (buf , CRC ) & 0xffffffff
287+ if cmpr :
288+ buf = cmpr .compress (buf )
289+ compress_size = compress_size + len (buf )
290+ yield self .fp .write (buf )
291+ else : # we have an iterable
292+ for buf in iterable :
271293 file_size = file_size + len (buf )
272294 CRC = crc32 (buf , CRC ) & 0xffffffff
273295 if cmpr :
0 commit comments