1*cda5da8dSAndroid Build Coastguard Worker"""Utilities for comparing files and directories. 2*cda5da8dSAndroid Build Coastguard Worker 3*cda5da8dSAndroid Build Coastguard WorkerClasses: 4*cda5da8dSAndroid Build Coastguard Worker dircmp 5*cda5da8dSAndroid Build Coastguard Worker 6*cda5da8dSAndroid Build Coastguard WorkerFunctions: 7*cda5da8dSAndroid Build Coastguard Worker cmp(f1, f2, shallow=True) -> int 8*cda5da8dSAndroid Build Coastguard Worker cmpfiles(a, b, common) -> ([], [], []) 9*cda5da8dSAndroid Build Coastguard Worker clear_cache() 10*cda5da8dSAndroid Build Coastguard Worker 11*cda5da8dSAndroid Build Coastguard Worker""" 12*cda5da8dSAndroid Build Coastguard Worker 13*cda5da8dSAndroid Build Coastguard Workerimport os 14*cda5da8dSAndroid Build Coastguard Workerimport stat 15*cda5da8dSAndroid Build Coastguard Workerfrom itertools import filterfalse 16*cda5da8dSAndroid Build Coastguard Workerfrom types import GenericAlias 17*cda5da8dSAndroid Build Coastguard Worker 18*cda5da8dSAndroid Build Coastguard Worker__all__ = ['clear_cache', 'cmp', 'dircmp', 'cmpfiles', 'DEFAULT_IGNORES'] 19*cda5da8dSAndroid Build Coastguard Worker 20*cda5da8dSAndroid Build Coastguard Worker_cache = {} 21*cda5da8dSAndroid Build Coastguard WorkerBUFSIZE = 8*1024 22*cda5da8dSAndroid Build Coastguard Worker 23*cda5da8dSAndroid Build Coastguard WorkerDEFAULT_IGNORES = [ 24*cda5da8dSAndroid Build Coastguard Worker 'RCS', 'CVS', 'tags', '.git', '.hg', '.bzr', '_darcs', '__pycache__'] 25*cda5da8dSAndroid Build Coastguard Worker 26*cda5da8dSAndroid Build Coastguard Workerdef clear_cache(): 27*cda5da8dSAndroid Build Coastguard Worker """Clear the filecmp cache.""" 28*cda5da8dSAndroid Build Coastguard Worker _cache.clear() 29*cda5da8dSAndroid Build Coastguard Worker 30*cda5da8dSAndroid Build Coastguard Workerdef cmp(f1, f2, shallow=True): 31*cda5da8dSAndroid Build Coastguard Worker """Compare two files. 32*cda5da8dSAndroid Build Coastguard Worker 33*cda5da8dSAndroid Build Coastguard Worker Arguments: 34*cda5da8dSAndroid Build Coastguard Worker 35*cda5da8dSAndroid Build Coastguard Worker f1 -- First file name 36*cda5da8dSAndroid Build Coastguard Worker 37*cda5da8dSAndroid Build Coastguard Worker f2 -- Second file name 38*cda5da8dSAndroid Build Coastguard Worker 39*cda5da8dSAndroid Build Coastguard Worker shallow -- treat files as identical if their stat signatures (type, size, 40*cda5da8dSAndroid Build Coastguard Worker mtime) are identical. Otherwise, files are considered different 41*cda5da8dSAndroid Build Coastguard Worker if their sizes or contents differ. [default: True] 42*cda5da8dSAndroid Build Coastguard Worker 43*cda5da8dSAndroid Build Coastguard Worker Return value: 44*cda5da8dSAndroid Build Coastguard Worker 45*cda5da8dSAndroid Build Coastguard Worker True if the files are the same, False otherwise. 46*cda5da8dSAndroid Build Coastguard Worker 47*cda5da8dSAndroid Build Coastguard Worker This function uses a cache for past comparisons and the results, 48*cda5da8dSAndroid Build Coastguard Worker with cache entries invalidated if their stat information 49*cda5da8dSAndroid Build Coastguard Worker changes. The cache may be cleared by calling clear_cache(). 50*cda5da8dSAndroid Build Coastguard Worker 51*cda5da8dSAndroid Build Coastguard Worker """ 52*cda5da8dSAndroid Build Coastguard Worker 53*cda5da8dSAndroid Build Coastguard Worker s1 = _sig(os.stat(f1)) 54*cda5da8dSAndroid Build Coastguard Worker s2 = _sig(os.stat(f2)) 55*cda5da8dSAndroid Build Coastguard Worker if s1[0] != stat.S_IFREG or s2[0] != stat.S_IFREG: 56*cda5da8dSAndroid Build Coastguard Worker return False 57*cda5da8dSAndroid Build Coastguard Worker if shallow and s1 == s2: 58*cda5da8dSAndroid Build Coastguard Worker return True 59*cda5da8dSAndroid Build Coastguard Worker if s1[1] != s2[1]: 60*cda5da8dSAndroid Build Coastguard Worker return False 61*cda5da8dSAndroid Build Coastguard Worker 62*cda5da8dSAndroid Build Coastguard Worker outcome = _cache.get((f1, f2, s1, s2)) 63*cda5da8dSAndroid Build Coastguard Worker if outcome is None: 64*cda5da8dSAndroid Build Coastguard Worker outcome = _do_cmp(f1, f2) 65*cda5da8dSAndroid Build Coastguard Worker if len(_cache) > 100: # limit the maximum size of the cache 66*cda5da8dSAndroid Build Coastguard Worker clear_cache() 67*cda5da8dSAndroid Build Coastguard Worker _cache[f1, f2, s1, s2] = outcome 68*cda5da8dSAndroid Build Coastguard Worker return outcome 69*cda5da8dSAndroid Build Coastguard Worker 70*cda5da8dSAndroid Build Coastguard Workerdef _sig(st): 71*cda5da8dSAndroid Build Coastguard Worker return (stat.S_IFMT(st.st_mode), 72*cda5da8dSAndroid Build Coastguard Worker st.st_size, 73*cda5da8dSAndroid Build Coastguard Worker st.st_mtime) 74*cda5da8dSAndroid Build Coastguard Worker 75*cda5da8dSAndroid Build Coastguard Workerdef _do_cmp(f1, f2): 76*cda5da8dSAndroid Build Coastguard Worker bufsize = BUFSIZE 77*cda5da8dSAndroid Build Coastguard Worker with open(f1, 'rb') as fp1, open(f2, 'rb') as fp2: 78*cda5da8dSAndroid Build Coastguard Worker while True: 79*cda5da8dSAndroid Build Coastguard Worker b1 = fp1.read(bufsize) 80*cda5da8dSAndroid Build Coastguard Worker b2 = fp2.read(bufsize) 81*cda5da8dSAndroid Build Coastguard Worker if b1 != b2: 82*cda5da8dSAndroid Build Coastguard Worker return False 83*cda5da8dSAndroid Build Coastguard Worker if not b1: 84*cda5da8dSAndroid Build Coastguard Worker return True 85*cda5da8dSAndroid Build Coastguard Worker 86*cda5da8dSAndroid Build Coastguard Worker# Directory comparison class. 87*cda5da8dSAndroid Build Coastguard Worker# 88*cda5da8dSAndroid Build Coastguard Workerclass dircmp: 89*cda5da8dSAndroid Build Coastguard Worker """A class that manages the comparison of 2 directories. 90*cda5da8dSAndroid Build Coastguard Worker 91*cda5da8dSAndroid Build Coastguard Worker dircmp(a, b, ignore=None, hide=None) 92*cda5da8dSAndroid Build Coastguard Worker A and B are directories. 93*cda5da8dSAndroid Build Coastguard Worker IGNORE is a list of names to ignore, 94*cda5da8dSAndroid Build Coastguard Worker defaults to DEFAULT_IGNORES. 95*cda5da8dSAndroid Build Coastguard Worker HIDE is a list of names to hide, 96*cda5da8dSAndroid Build Coastguard Worker defaults to [os.curdir, os.pardir]. 97*cda5da8dSAndroid Build Coastguard Worker 98*cda5da8dSAndroid Build Coastguard Worker High level usage: 99*cda5da8dSAndroid Build Coastguard Worker x = dircmp(dir1, dir2) 100*cda5da8dSAndroid Build Coastguard Worker x.report() -> prints a report on the differences between dir1 and dir2 101*cda5da8dSAndroid Build Coastguard Worker or 102*cda5da8dSAndroid Build Coastguard Worker x.report_partial_closure() -> prints report on differences between dir1 103*cda5da8dSAndroid Build Coastguard Worker and dir2, and reports on common immediate subdirectories. 104*cda5da8dSAndroid Build Coastguard Worker x.report_full_closure() -> like report_partial_closure, 105*cda5da8dSAndroid Build Coastguard Worker but fully recursive. 106*cda5da8dSAndroid Build Coastguard Worker 107*cda5da8dSAndroid Build Coastguard Worker Attributes: 108*cda5da8dSAndroid Build Coastguard Worker left_list, right_list: The files in dir1 and dir2, 109*cda5da8dSAndroid Build Coastguard Worker filtered by hide and ignore. 110*cda5da8dSAndroid Build Coastguard Worker common: a list of names in both dir1 and dir2. 111*cda5da8dSAndroid Build Coastguard Worker left_only, right_only: names only in dir1, dir2. 112*cda5da8dSAndroid Build Coastguard Worker common_dirs: subdirectories in both dir1 and dir2. 113*cda5da8dSAndroid Build Coastguard Worker common_files: files in both dir1 and dir2. 114*cda5da8dSAndroid Build Coastguard Worker common_funny: names in both dir1 and dir2 where the type differs between 115*cda5da8dSAndroid Build Coastguard Worker dir1 and dir2, or the name is not stat-able. 116*cda5da8dSAndroid Build Coastguard Worker same_files: list of identical files. 117*cda5da8dSAndroid Build Coastguard Worker diff_files: list of filenames which differ. 118*cda5da8dSAndroid Build Coastguard Worker funny_files: list of files which could not be compared. 119*cda5da8dSAndroid Build Coastguard Worker subdirs: a dictionary of dircmp instances (or MyDirCmp instances if this 120*cda5da8dSAndroid Build Coastguard Worker object is of type MyDirCmp, a subclass of dircmp), keyed by names 121*cda5da8dSAndroid Build Coastguard Worker in common_dirs. 122*cda5da8dSAndroid Build Coastguard Worker """ 123*cda5da8dSAndroid Build Coastguard Worker 124*cda5da8dSAndroid Build Coastguard Worker def __init__(self, a, b, ignore=None, hide=None): # Initialize 125*cda5da8dSAndroid Build Coastguard Worker self.left = a 126*cda5da8dSAndroid Build Coastguard Worker self.right = b 127*cda5da8dSAndroid Build Coastguard Worker if hide is None: 128*cda5da8dSAndroid Build Coastguard Worker self.hide = [os.curdir, os.pardir] # Names never to be shown 129*cda5da8dSAndroid Build Coastguard Worker else: 130*cda5da8dSAndroid Build Coastguard Worker self.hide = hide 131*cda5da8dSAndroid Build Coastguard Worker if ignore is None: 132*cda5da8dSAndroid Build Coastguard Worker self.ignore = DEFAULT_IGNORES 133*cda5da8dSAndroid Build Coastguard Worker else: 134*cda5da8dSAndroid Build Coastguard Worker self.ignore = ignore 135*cda5da8dSAndroid Build Coastguard Worker 136*cda5da8dSAndroid Build Coastguard Worker def phase0(self): # Compare everything except common subdirectories 137*cda5da8dSAndroid Build Coastguard Worker self.left_list = _filter(os.listdir(self.left), 138*cda5da8dSAndroid Build Coastguard Worker self.hide+self.ignore) 139*cda5da8dSAndroid Build Coastguard Worker self.right_list = _filter(os.listdir(self.right), 140*cda5da8dSAndroid Build Coastguard Worker self.hide+self.ignore) 141*cda5da8dSAndroid Build Coastguard Worker self.left_list.sort() 142*cda5da8dSAndroid Build Coastguard Worker self.right_list.sort() 143*cda5da8dSAndroid Build Coastguard Worker 144*cda5da8dSAndroid Build Coastguard Worker def phase1(self): # Compute common names 145*cda5da8dSAndroid Build Coastguard Worker a = dict(zip(map(os.path.normcase, self.left_list), self.left_list)) 146*cda5da8dSAndroid Build Coastguard Worker b = dict(zip(map(os.path.normcase, self.right_list), self.right_list)) 147*cda5da8dSAndroid Build Coastguard Worker self.common = list(map(a.__getitem__, filter(b.__contains__, a))) 148*cda5da8dSAndroid Build Coastguard Worker self.left_only = list(map(a.__getitem__, filterfalse(b.__contains__, a))) 149*cda5da8dSAndroid Build Coastguard Worker self.right_only = list(map(b.__getitem__, filterfalse(a.__contains__, b))) 150*cda5da8dSAndroid Build Coastguard Worker 151*cda5da8dSAndroid Build Coastguard Worker def phase2(self): # Distinguish files, directories, funnies 152*cda5da8dSAndroid Build Coastguard Worker self.common_dirs = [] 153*cda5da8dSAndroid Build Coastguard Worker self.common_files = [] 154*cda5da8dSAndroid Build Coastguard Worker self.common_funny = [] 155*cda5da8dSAndroid Build Coastguard Worker 156*cda5da8dSAndroid Build Coastguard Worker for x in self.common: 157*cda5da8dSAndroid Build Coastguard Worker a_path = os.path.join(self.left, x) 158*cda5da8dSAndroid Build Coastguard Worker b_path = os.path.join(self.right, x) 159*cda5da8dSAndroid Build Coastguard Worker 160*cda5da8dSAndroid Build Coastguard Worker ok = 1 161*cda5da8dSAndroid Build Coastguard Worker try: 162*cda5da8dSAndroid Build Coastguard Worker a_stat = os.stat(a_path) 163*cda5da8dSAndroid Build Coastguard Worker except OSError: 164*cda5da8dSAndroid Build Coastguard Worker # print('Can\'t stat', a_path, ':', why.args[1]) 165*cda5da8dSAndroid Build Coastguard Worker ok = 0 166*cda5da8dSAndroid Build Coastguard Worker try: 167*cda5da8dSAndroid Build Coastguard Worker b_stat = os.stat(b_path) 168*cda5da8dSAndroid Build Coastguard Worker except OSError: 169*cda5da8dSAndroid Build Coastguard Worker # print('Can\'t stat', b_path, ':', why.args[1]) 170*cda5da8dSAndroid Build Coastguard Worker ok = 0 171*cda5da8dSAndroid Build Coastguard Worker 172*cda5da8dSAndroid Build Coastguard Worker if ok: 173*cda5da8dSAndroid Build Coastguard Worker a_type = stat.S_IFMT(a_stat.st_mode) 174*cda5da8dSAndroid Build Coastguard Worker b_type = stat.S_IFMT(b_stat.st_mode) 175*cda5da8dSAndroid Build Coastguard Worker if a_type != b_type: 176*cda5da8dSAndroid Build Coastguard Worker self.common_funny.append(x) 177*cda5da8dSAndroid Build Coastguard Worker elif stat.S_ISDIR(a_type): 178*cda5da8dSAndroid Build Coastguard Worker self.common_dirs.append(x) 179*cda5da8dSAndroid Build Coastguard Worker elif stat.S_ISREG(a_type): 180*cda5da8dSAndroid Build Coastguard Worker self.common_files.append(x) 181*cda5da8dSAndroid Build Coastguard Worker else: 182*cda5da8dSAndroid Build Coastguard Worker self.common_funny.append(x) 183*cda5da8dSAndroid Build Coastguard Worker else: 184*cda5da8dSAndroid Build Coastguard Worker self.common_funny.append(x) 185*cda5da8dSAndroid Build Coastguard Worker 186*cda5da8dSAndroid Build Coastguard Worker def phase3(self): # Find out differences between common files 187*cda5da8dSAndroid Build Coastguard Worker xx = cmpfiles(self.left, self.right, self.common_files) 188*cda5da8dSAndroid Build Coastguard Worker self.same_files, self.diff_files, self.funny_files = xx 189*cda5da8dSAndroid Build Coastguard Worker 190*cda5da8dSAndroid Build Coastguard Worker def phase4(self): # Find out differences between common subdirectories 191*cda5da8dSAndroid Build Coastguard Worker # A new dircmp (or MyDirCmp if dircmp was subclassed) object is created 192*cda5da8dSAndroid Build Coastguard Worker # for each common subdirectory, 193*cda5da8dSAndroid Build Coastguard Worker # these are stored in a dictionary indexed by filename. 194*cda5da8dSAndroid Build Coastguard Worker # The hide and ignore properties are inherited from the parent 195*cda5da8dSAndroid Build Coastguard Worker self.subdirs = {} 196*cda5da8dSAndroid Build Coastguard Worker for x in self.common_dirs: 197*cda5da8dSAndroid Build Coastguard Worker a_x = os.path.join(self.left, x) 198*cda5da8dSAndroid Build Coastguard Worker b_x = os.path.join(self.right, x) 199*cda5da8dSAndroid Build Coastguard Worker self.subdirs[x] = self.__class__(a_x, b_x, self.ignore, self.hide) 200*cda5da8dSAndroid Build Coastguard Worker 201*cda5da8dSAndroid Build Coastguard Worker def phase4_closure(self): # Recursively call phase4() on subdirectories 202*cda5da8dSAndroid Build Coastguard Worker self.phase4() 203*cda5da8dSAndroid Build Coastguard Worker for sd in self.subdirs.values(): 204*cda5da8dSAndroid Build Coastguard Worker sd.phase4_closure() 205*cda5da8dSAndroid Build Coastguard Worker 206*cda5da8dSAndroid Build Coastguard Worker def report(self): # Print a report on the differences between a and b 207*cda5da8dSAndroid Build Coastguard Worker # Output format is purposely lousy 208*cda5da8dSAndroid Build Coastguard Worker print('diff', self.left, self.right) 209*cda5da8dSAndroid Build Coastguard Worker if self.left_only: 210*cda5da8dSAndroid Build Coastguard Worker self.left_only.sort() 211*cda5da8dSAndroid Build Coastguard Worker print('Only in', self.left, ':', self.left_only) 212*cda5da8dSAndroid Build Coastguard Worker if self.right_only: 213*cda5da8dSAndroid Build Coastguard Worker self.right_only.sort() 214*cda5da8dSAndroid Build Coastguard Worker print('Only in', self.right, ':', self.right_only) 215*cda5da8dSAndroid Build Coastguard Worker if self.same_files: 216*cda5da8dSAndroid Build Coastguard Worker self.same_files.sort() 217*cda5da8dSAndroid Build Coastguard Worker print('Identical files :', self.same_files) 218*cda5da8dSAndroid Build Coastguard Worker if self.diff_files: 219*cda5da8dSAndroid Build Coastguard Worker self.diff_files.sort() 220*cda5da8dSAndroid Build Coastguard Worker print('Differing files :', self.diff_files) 221*cda5da8dSAndroid Build Coastguard Worker if self.funny_files: 222*cda5da8dSAndroid Build Coastguard Worker self.funny_files.sort() 223*cda5da8dSAndroid Build Coastguard Worker print('Trouble with common files :', self.funny_files) 224*cda5da8dSAndroid Build Coastguard Worker if self.common_dirs: 225*cda5da8dSAndroid Build Coastguard Worker self.common_dirs.sort() 226*cda5da8dSAndroid Build Coastguard Worker print('Common subdirectories :', self.common_dirs) 227*cda5da8dSAndroid Build Coastguard Worker if self.common_funny: 228*cda5da8dSAndroid Build Coastguard Worker self.common_funny.sort() 229*cda5da8dSAndroid Build Coastguard Worker print('Common funny cases :', self.common_funny) 230*cda5da8dSAndroid Build Coastguard Worker 231*cda5da8dSAndroid Build Coastguard Worker def report_partial_closure(self): # Print reports on self and on subdirs 232*cda5da8dSAndroid Build Coastguard Worker self.report() 233*cda5da8dSAndroid Build Coastguard Worker for sd in self.subdirs.values(): 234*cda5da8dSAndroid Build Coastguard Worker print() 235*cda5da8dSAndroid Build Coastguard Worker sd.report() 236*cda5da8dSAndroid Build Coastguard Worker 237*cda5da8dSAndroid Build Coastguard Worker def report_full_closure(self): # Report on self and subdirs recursively 238*cda5da8dSAndroid Build Coastguard Worker self.report() 239*cda5da8dSAndroid Build Coastguard Worker for sd in self.subdirs.values(): 240*cda5da8dSAndroid Build Coastguard Worker print() 241*cda5da8dSAndroid Build Coastguard Worker sd.report_full_closure() 242*cda5da8dSAndroid Build Coastguard Worker 243*cda5da8dSAndroid Build Coastguard Worker methodmap = dict(subdirs=phase4, 244*cda5da8dSAndroid Build Coastguard Worker same_files=phase3, diff_files=phase3, funny_files=phase3, 245*cda5da8dSAndroid Build Coastguard Worker common_dirs = phase2, common_files=phase2, common_funny=phase2, 246*cda5da8dSAndroid Build Coastguard Worker common=phase1, left_only=phase1, right_only=phase1, 247*cda5da8dSAndroid Build Coastguard Worker left_list=phase0, right_list=phase0) 248*cda5da8dSAndroid Build Coastguard Worker 249*cda5da8dSAndroid Build Coastguard Worker def __getattr__(self, attr): 250*cda5da8dSAndroid Build Coastguard Worker if attr not in self.methodmap: 251*cda5da8dSAndroid Build Coastguard Worker raise AttributeError(attr) 252*cda5da8dSAndroid Build Coastguard Worker self.methodmap[attr](self) 253*cda5da8dSAndroid Build Coastguard Worker return getattr(self, attr) 254*cda5da8dSAndroid Build Coastguard Worker 255*cda5da8dSAndroid Build Coastguard Worker __class_getitem__ = classmethod(GenericAlias) 256*cda5da8dSAndroid Build Coastguard Worker 257*cda5da8dSAndroid Build Coastguard Worker 258*cda5da8dSAndroid Build Coastguard Workerdef cmpfiles(a, b, common, shallow=True): 259*cda5da8dSAndroid Build Coastguard Worker """Compare common files in two directories. 260*cda5da8dSAndroid Build Coastguard Worker 261*cda5da8dSAndroid Build Coastguard Worker a, b -- directory names 262*cda5da8dSAndroid Build Coastguard Worker common -- list of file names found in both directories 263*cda5da8dSAndroid Build Coastguard Worker shallow -- if true, do comparison based solely on stat() information 264*cda5da8dSAndroid Build Coastguard Worker 265*cda5da8dSAndroid Build Coastguard Worker Returns a tuple of three lists: 266*cda5da8dSAndroid Build Coastguard Worker files that compare equal 267*cda5da8dSAndroid Build Coastguard Worker files that are different 268*cda5da8dSAndroid Build Coastguard Worker filenames that aren't regular files. 269*cda5da8dSAndroid Build Coastguard Worker 270*cda5da8dSAndroid Build Coastguard Worker """ 271*cda5da8dSAndroid Build Coastguard Worker res = ([], [], []) 272*cda5da8dSAndroid Build Coastguard Worker for x in common: 273*cda5da8dSAndroid Build Coastguard Worker ax = os.path.join(a, x) 274*cda5da8dSAndroid Build Coastguard Worker bx = os.path.join(b, x) 275*cda5da8dSAndroid Build Coastguard Worker res[_cmp(ax, bx, shallow)].append(x) 276*cda5da8dSAndroid Build Coastguard Worker return res 277*cda5da8dSAndroid Build Coastguard Worker 278*cda5da8dSAndroid Build Coastguard Worker 279*cda5da8dSAndroid Build Coastguard Worker# Compare two files. 280*cda5da8dSAndroid Build Coastguard Worker# Return: 281*cda5da8dSAndroid Build Coastguard Worker# 0 for equal 282*cda5da8dSAndroid Build Coastguard Worker# 1 for different 283*cda5da8dSAndroid Build Coastguard Worker# 2 for funny cases (can't stat, etc.) 284*cda5da8dSAndroid Build Coastguard Worker# 285*cda5da8dSAndroid Build Coastguard Workerdef _cmp(a, b, sh, abs=abs, cmp=cmp): 286*cda5da8dSAndroid Build Coastguard Worker try: 287*cda5da8dSAndroid Build Coastguard Worker return not abs(cmp(a, b, sh)) 288*cda5da8dSAndroid Build Coastguard Worker except OSError: 289*cda5da8dSAndroid Build Coastguard Worker return 2 290*cda5da8dSAndroid Build Coastguard Worker 291*cda5da8dSAndroid Build Coastguard Worker 292*cda5da8dSAndroid Build Coastguard Worker# Return a copy with items that occur in skip removed. 293*cda5da8dSAndroid Build Coastguard Worker# 294*cda5da8dSAndroid Build Coastguard Workerdef _filter(flist, skip): 295*cda5da8dSAndroid Build Coastguard Worker return list(filterfalse(skip.__contains__, flist)) 296*cda5da8dSAndroid Build Coastguard Worker 297*cda5da8dSAndroid Build Coastguard Worker 298*cda5da8dSAndroid Build Coastguard Worker# Demonstration and testing. 299*cda5da8dSAndroid Build Coastguard Worker# 300*cda5da8dSAndroid Build Coastguard Workerdef demo(): 301*cda5da8dSAndroid Build Coastguard Worker import sys 302*cda5da8dSAndroid Build Coastguard Worker import getopt 303*cda5da8dSAndroid Build Coastguard Worker options, args = getopt.getopt(sys.argv[1:], 'r') 304*cda5da8dSAndroid Build Coastguard Worker if len(args) != 2: 305*cda5da8dSAndroid Build Coastguard Worker raise getopt.GetoptError('need exactly two args', None) 306*cda5da8dSAndroid Build Coastguard Worker dd = dircmp(args[0], args[1]) 307*cda5da8dSAndroid Build Coastguard Worker if ('-r', '') in options: 308*cda5da8dSAndroid Build Coastguard Worker dd.report_full_closure() 309*cda5da8dSAndroid Build Coastguard Worker else: 310*cda5da8dSAndroid Build Coastguard Worker dd.report() 311*cda5da8dSAndroid Build Coastguard Worker 312*cda5da8dSAndroid Build Coastguard Workerif __name__ == '__main__': 313*cda5da8dSAndroid Build Coastguard Worker demo() 314