1#!/usr/bin/env python 2# 3# Copyright 2016 Google Inc. 4# 5# Use of this source code is governed by a BSD-style license that can be 6# found in the LICENSE file. 7 8 9"""Utilities for zipping and unzipping files.""" 10 11 12from __future__ import print_function 13import fnmatch 14import ntpath 15import os 16import posixpath 17import zipfile 18 19 20def filtered(names, to_skip): 21 """Filter the list of file or directory names.""" 22 rv = names[:] 23 for pattern in to_skip: 24 rv = [n for n in rv if not fnmatch.fnmatch(n, pattern)] 25 return rv 26 27 28def zip(target_dir, zip_file, to_skip=None): # pylint: disable=W0622 29 """Zip the given directory, write to the given zip file.""" 30 if not os.path.isdir(target_dir): 31 raise IOError('%s does not exist!' % target_dir) 32 to_skip = to_skip or [] 33 with zipfile.ZipFile(zip_file, 'w', zipfile.ZIP_DEFLATED, True) as z: 34 for r, d, f in os.walk(target_dir, topdown=True): 35 d[:] = filtered(d, to_skip) 36 for filename in filtered(f, to_skip): 37 filepath = os.path.join(r, filename) 38 zi = zipfile.ZipInfo(filepath) 39 zi.filename = os.path.relpath(filepath, target_dir) 40 if os.name == 'nt': 41 # Dumb path separator replacement for Windows. 42 zi.filename = zi.filename.replace(ntpath.sep, posixpath.sep) 43 try: 44 perms = os.stat(filepath).st_mode 45 except OSError: 46 if os.path.islink(filepath): 47 print('Skipping symlink %s' % filepath) 48 continue 49 else: 50 raise 51 zi.external_attr = perms << 16 52 zi.compress_type = zipfile.ZIP_DEFLATED 53 with open(filepath, 'rb') as f: 54 content = f.read() 55 z.writestr(zi, content) 56 for dirname in d: 57 dirpath = os.path.join(r, dirname) 58 z.write(dirpath, os.path.relpath(dirpath, target_dir)) 59 60 61def unzip(zip_file, target_dir): 62 """Unzip the given zip file into the target dir.""" 63 if not os.path.isdir(target_dir): 64 os.makedirs(target_dir) 65 with zipfile.ZipFile(zip_file, 'r', zipfile.ZIP_DEFLATED, True) as z: 66 for zi in z.infolist(): 67 dst_subpath = zi.filename 68 if os.name == 'nt': 69 # Dumb path separator replacement for Windows. 70 dst_subpath = dst_subpath.replace(posixpath.sep, ntpath.sep) 71 dst_path = os.path.join(target_dir, dst_subpath) 72 if dst_path.endswith(os.path.sep): 73 os.mkdir(dst_path) 74 else: 75 with open(dst_path, 'wb') as f: 76 f.write(z.read(zi)) 77 perms = zi.external_attr >> 16 78 os.chmod(dst_path, perms) 79