A ZipFile that works around several issues in the stdlib. See: + https://bugs.python.org/issue15795 + https://github.com/pex-tool/pex/issues/298
| 318 | |
| 319 | |
| 320 | class ZipFileEx(ZipFile): |
| 321 | """A ZipFile that works around several issues in the stdlib. |
| 322 | |
| 323 | See: |
| 324 | + https://bugs.python.org/issue15795 |
| 325 | + https://github.com/pex-tool/pex/issues/298 |
| 326 | """ |
| 327 | |
| 328 | class ZipEntry(namedtuple("ZipEntry", ["info", "data"])): |
| 329 | pass |
| 330 | |
| 331 | @classmethod |
| 332 | def zip_info_from_file( |
| 333 | cls, |
| 334 | filename, # type: Text |
| 335 | arcname=None, # type: Optional[Text] |
| 336 | date_time=None, # type: Optional[time.struct_time] |
| 337 | file_mode=None, # type: Optional[int] |
| 338 | compress=True, # type: bool |
| 339 | ): |
| 340 | # type: (...) -> Tuple[ZipInfo, bool] |
| 341 | """Construct a ZipInfo for a file on the filesystem. |
| 342 | |
| 343 | Usually a similar `from_file` method is provided by `ZipInfo`, but it is not implemented in |
| 344 | Python 2.7 or Python 3.5; so we re-implement it here adding the possibility to control the |
| 345 | `ZipInfo` date_time separately from the underlying file mtime. |
| 346 | See https://github.com/python/cpython/blob/9d3b53c47fab9ebf1f40d6f21b7d1ad391c14cd7/Lib/zipfile/__init__.py#L607-L642 |
| 347 | """ |
| 348 | st = os.stat(filename) |
| 349 | is_dir = stat.S_ISDIR(st.st_mode) |
| 350 | if arcname is None: |
| 351 | arcname = filename |
| 352 | arcname = os.path.normpath(os.path.splitdrive(arcname)[1]) |
| 353 | while arcname[0] in (os.sep, os.altsep): |
| 354 | arcname = arcname[1:] |
| 355 | if is_dir: |
| 356 | arcname += "/" |
| 357 | if date_time is None: |
| 358 | date_time = time.localtime(st.st_mtime) |
| 359 | zip_info = zipfile.ZipInfo(filename=arcname, date_time=date_time[:6]) |
| 360 | |
| 361 | # Unix attributes |
| 362 | zip_info.external_attr = ((file_mode or st.st_mode) & 0xFFFF) << 16 |
| 363 | |
| 364 | if is_dir: |
| 365 | zip_info.file_size = 0 |
| 366 | zip_info.external_attr |= 0x10 # MS-DOS directory flag |
| 367 | zip_info.compress_type = zipfile.ZIP_STORED |
| 368 | else: |
| 369 | zip_info.file_size = st.st_size |
| 370 | zip_info.compress_type = zipfile.ZIP_DEFLATED if compress else zipfile.ZIP_STORED |
| 371 | return zip_info, is_dir |
| 372 | |
| 373 | def write_deterministic( |
| 374 | self, |
| 375 | filename, # type: Text |
| 376 | arcname=None, # type: Optional[Text] |
| 377 | digest=None, # type: Optional[Digest] |
no outgoing calls