@@ -371,7 +371,7 @@ def __repr__(self):
371371 result .append (' filemode=%r' % stat .filemode (hi ))
372372 if lo :
373373 result .append (' external_attr=%#x' % lo )
374- isdir = self .filename [ - 1 :] == '/'
374+ isdir = self .is_dir ()
375375 if not isdir or self .file_size :
376376 result .append (' file_size=%r' % self .file_size )
377377 if ((not isdir or self .compress_size ) and
@@ -469,6 +469,41 @@ def _decodeExtra(self):
469469
470470 extra = extra [ln + 4 :]
471471
472+ @classmethod
473+ def from_file (cls , filename , arcname = None ):
474+ """Construct an appropriate ZipInfo for a file on the filesystem.
475+
476+ filename should be the path to a file or directory on the filesystem.
477+
478+ arcname is the name which it will have within the archive (by default,
479+ this will be the same as filename, but without a drive letter and with
480+ leading path separators removed).
481+ """
482+ st = os .stat (filename )
483+ isdir = stat .S_ISDIR (st .st_mode )
484+ mtime = time .localtime (st .st_mtime )
485+ date_time = mtime [0 :6 ]
486+ # Create ZipInfo instance to store file information
487+ if arcname is None :
488+ arcname = filename
489+ arcname = os .path .normpath (os .path .splitdrive (arcname )[1 ])
490+ while arcname [0 ] in (os .sep , os .altsep ):
491+ arcname = arcname [1 :]
492+ if isdir :
493+ arcname += '/'
494+ zinfo = cls (arcname , date_time )
495+ zinfo .external_attr = (st .st_mode & 0xFFFF ) << 16 # Unix attributes
496+ if isdir :
497+ zinfo .file_size = 0
498+ zinfo .external_attr |= 0x10 # MS-DOS directory flag
499+ else :
500+ zinfo .file_size = st .st_size
501+
502+ return zinfo
503+
504+ def is_dir (self ):
505+ return self .filename [- 1 ] == '/'
506+
472507
473508class _ZipDecrypter :
474509 """Class to handle decryption of files stored within a ZIP archive.
@@ -1389,7 +1424,7 @@ def _extract_member(self, member, targetpath, pwd):
13891424 if upperdirs and not os .path .exists (upperdirs ):
13901425 os .makedirs (upperdirs )
13911426
1392- if member .filename [ - 1 ] == '/' :
1427+ if member .is_dir () :
13931428 if not os .path .isdir (targetpath ):
13941429 os .mkdir (targetpath )
13951430 return targetpath
@@ -1430,29 +1465,17 @@ def write(self, filename, arcname=None, compress_type=None):
14301465 raise RuntimeError (
14311466 "Attempt to write to ZIP archive that was already closed" )
14321467
1433- st = os .stat (filename )
1434- isdir = stat .S_ISDIR (st .st_mode )
1435- mtime = time .localtime (st .st_mtime )
1436- date_time = mtime [0 :6 ]
1437- # Create ZipInfo instance to store file information
1438- if arcname is None :
1439- arcname = filename
1440- arcname = os .path .normpath (os .path .splitdrive (arcname )[1 ])
1441- while arcname [0 ] in (os .sep , os .altsep ):
1442- arcname = arcname [1 :]
1443- if isdir :
1444- arcname += '/'
1445- zinfo = ZipInfo (arcname , date_time )
1446- zinfo .external_attr = (st [0 ] & 0xFFFF ) << 16 # Unix attributes
1447- if isdir :
1448- zinfo .compress_type = ZIP_STORED
1449- elif compress_type is None :
1450- zinfo .compress_type = self .compression
1468+ zinfo = ZipInfo .from_file (filename , arcname )
1469+
1470+ if zinfo .is_dir ():
1471+ zinfo .compress_size = 0
1472+ zinfo .CRC = 0
14511473 else :
1452- zinfo .compress_type = compress_type
1474+ if compress_type is not None :
1475+ zinfo .compress_type = compress_type
1476+ else :
1477+ zinfo .compress_type = self .compression
14531478
1454- zinfo .file_size = st .st_size
1455- zinfo .flag_bits = 0x00
14561479 with self ._lock :
14571480 if self ._seekable :
14581481 self .fp .seek (self .start_dir )
@@ -1464,11 +1487,7 @@ def write(self, filename, arcname=None, compress_type=None):
14641487 self ._writecheck (zinfo )
14651488 self ._didModify = True
14661489
1467- if isdir :
1468- zinfo .file_size = 0
1469- zinfo .compress_size = 0
1470- zinfo .CRC = 0
1471- zinfo .external_attr |= 0x10 # MS-DOS directory flag
1490+ if zinfo .is_dir ():
14721491 self .filelist .append (zinfo )
14731492 self .NameToInfo [zinfo .filename ] = zinfo
14741493 self .fp .write (zinfo .FileHeader (False ))
0 commit comments