xref: /aosp_15_r20/prebuilts/build-tools/common/py3-stdlib/filecmp.py (revision cda5da8d549138a6648c5ee6d7a49cf8f4a657be)
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