(source_filename, dest_dir)
| 523 | |
| 524 | # http://stackoverflow.com/questions/12886768/simple-way-to-unzip-file-in-python-on-all-oses |
| 525 | def unzip(source_filename, dest_dir): |
| 526 | print(f"Unpacking '{source_filename}' to '{dest_dir}'") |
| 527 | mkdir_p(dest_dir) |
| 528 | common_subdir = None |
| 529 | try: |
| 530 | with zipfile.ZipFile(source_filename) as zf: |
| 531 | # Implement '--strip 1' behavior to unzipping by testing if all the files |
| 532 | # in the zip reside in a common subdirectory, and if so, we move the |
| 533 | # output tree at the end of uncompression step. |
| 534 | for member in zf.infolist(): |
| 535 | words = member.filename.split('/') |
| 536 | if len(words) > 1: # If there is a directory component? |
| 537 | if common_subdir is None: |
| 538 | common_subdir = words[0] |
| 539 | elif common_subdir != words[0]: |
| 540 | common_subdir = None |
| 541 | break |
| 542 | else: |
| 543 | common_subdir = None |
| 544 | break |
| 545 | |
| 546 | unzip_to_dir = dest_dir |
| 547 | if common_subdir: |
| 548 | unzip_to_dir = os.path.join(os.path.dirname(dest_dir), 'unzip_temp') |
| 549 | |
| 550 | # Now do the actual decompress. |
| 551 | unzip_to_dir = fix_potentially_long_windows_pathname(unzip_to_dir) |
| 552 | for member in zf.infolist(): |
| 553 | zf.extract(member, unzip_to_dir) |
| 554 | dst_filename = os.path.join(unzip_to_dir, os.path.normpath(member.filename)) |
| 555 | |
| 556 | # See: https://stackoverflow.com/questions/42326428/zipfile-in-python-file-permission |
| 557 | unix_attributes = member.external_attr >> 16 |
| 558 | if unix_attributes: |
| 559 | os.chmod(dst_filename, unix_attributes) |
| 560 | |
| 561 | # Move the extracted file to its final location without the base |
| 562 | # directory name, if we are stripping that away. |
| 563 | if common_subdir: |
| 564 | assert member.filename.startswith(common_subdir), f'unexpected filename {member.filename}' |
| 565 | stripped_filename = '.' + member.filename[len(common_subdir):] |
| 566 | final_dst_filename = os.path.join(dest_dir, stripped_filename) |
| 567 | # Check if a directory |
| 568 | if stripped_filename.endswith('/'): |
| 569 | d = fix_potentially_long_windows_pathname(final_dst_filename) |
| 570 | if not os.path.isdir(d): |
| 571 | os.mkdir(d) |
| 572 | else: |
| 573 | parent_dir = os.path.dirname(fix_potentially_long_windows_pathname(final_dst_filename)) |
| 574 | if parent_dir and not os.path.exists(parent_dir): |
| 575 | os.makedirs(parent_dir) |
| 576 | move_with_overwrite(fix_potentially_long_windows_pathname(dst_filename), fix_potentially_long_windows_pathname(final_dst_filename)) |
| 577 | |
| 578 | if common_subdir: |
| 579 | remove_tree(unzip_to_dir) |
| 580 | except zipfile.BadZipfile as e: |
| 581 | errlog(f"Unzipping file '{source_filename}' failed due to reason: {e}! Removing the corrupted zip file.") |
| 582 | rmfile(source_filename) |
no test coverage detected