mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 10:26:02 +00:00 
			
		
		
		
	 132dce2246
			
		
	
	
		132dce2246
		
	
	
	
	
		
			
			Use != instead of <> since <> is documented as "obsolescent". Use "is" and "is not" when comparing with None or type objects.
		
			
				
	
	
		
			202 lines
		
	
	
	
		
			5.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			202 lines
		
	
	
	
		
			5.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| """A class to build directory diff tools on."""
 | |
| 
 | |
| import os
 | |
| 
 | |
| import dircache
 | |
| import cmpcache
 | |
| import statcache
 | |
| from stat import *
 | |
| 
 | |
| class dircmp:
 | |
|     """Directory comparison class."""
 | |
| 
 | |
|     def new(self, a, b):
 | |
|         """Initialize."""
 | |
|         self.a = a
 | |
|         self.b = b
 | |
|         # Properties that caller may change before calling self.run():
 | |
|         self.hide = [os.curdir, os.pardir] # Names never to be shown
 | |
|         self.ignore = ['RCS', 'tags'] # Names ignored in comparison
 | |
| 
 | |
|         return self
 | |
| 
 | |
|     def run(self):
 | |
|         """Compare everything except common subdirectories."""
 | |
|         self.a_list = filter(dircache.listdir(self.a), self.hide)
 | |
|         self.b_list = filter(dircache.listdir(self.b), self.hide)
 | |
|         self.a_list.sort()
 | |
|         self.b_list.sort()
 | |
|         self.phase1()
 | |
|         self.phase2()
 | |
|         self.phase3()
 | |
| 
 | |
|     def phase1(self):
 | |
|         """Compute common names."""
 | |
|         self.a_only = []
 | |
|         self.common = []
 | |
|         for x in self.a_list:
 | |
|             if x in self.b_list:
 | |
|                 self.common.append(x)
 | |
|             else:
 | |
|                 self.a_only.append(x)
 | |
| 
 | |
|         self.b_only = []
 | |
|         for x in self.b_list:
 | |
|             if x not in self.common:
 | |
|                 self.b_only.append(x)
 | |
| 
 | |
|     def phase2(self):
 | |
|         """Distinguish files, directories, funnies."""
 | |
|         self.common_dirs = []
 | |
|         self.common_files = []
 | |
|         self.common_funny = []
 | |
| 
 | |
|         for x in self.common:
 | |
|             a_path = os.path.join(self.a, x)
 | |
|             b_path = os.path.join(self.b, x)
 | |
| 
 | |
|             ok = 1
 | |
|             try:
 | |
|                 a_stat = statcache.stat(a_path)
 | |
|             except os.error, why:
 | |
|                 # print 'Can\'t stat', a_path, ':', why[1]
 | |
|                 ok = 0
 | |
|             try:
 | |
|                 b_stat = statcache.stat(b_path)
 | |
|             except os.error, why:
 | |
|                 # print 'Can\'t stat', b_path, ':', why[1]
 | |
|                 ok = 0
 | |
| 
 | |
|             if ok:
 | |
|                 a_type = S_IFMT(a_stat[ST_MODE])
 | |
|                 b_type = S_IFMT(b_stat[ST_MODE])
 | |
|                 if a_type != b_type:
 | |
|                     self.common_funny.append(x)
 | |
|                 elif S_ISDIR(a_type):
 | |
|                     self.common_dirs.append(x)
 | |
|                 elif S_ISREG(a_type):
 | |
|                     self.common_files.append(x)
 | |
|                 else:
 | |
|                     self.common_funny.append(x)
 | |
|             else:
 | |
|                 self.common_funny.append(x)
 | |
| 
 | |
|     def phase3(self):
 | |
|         """Find out differences between common files."""
 | |
|         xx = cmpfiles(self.a, self.b, self.common_files)
 | |
|         self.same_files, self.diff_files, self.funny_files = xx
 | |
| 
 | |
|     def phase4(self):
 | |
|         """Find out differences between common subdirectories.
 | |
|         A new dircmp object is created for each common subdirectory,
 | |
|         these are stored in a dictionary indexed by filename.
 | |
|         The hide and ignore properties are inherited from the parent."""
 | |
|         self.subdirs = {}
 | |
|         for x in self.common_dirs:
 | |
|             a_x = os.path.join(self.a, x)
 | |
|             b_x = os.path.join(self.b, x)
 | |
|             self.subdirs[x] = newdd = dircmp().new(a_x, b_x)
 | |
|             newdd.hide = self.hide
 | |
|             newdd.ignore = self.ignore
 | |
|             newdd.run()
 | |
| 
 | |
|     def phase4_closure(self):
 | |
|         """Recursively call phase4() on subdirectories."""
 | |
|         self.phase4()
 | |
|         for x in self.subdirs.keys():
 | |
|             self.subdirs[x].phase4_closure()
 | |
| 
 | |
|     def report(self):
 | |
|         """Print a report on the differences between a and b."""
 | |
|         # Assume that phases 1 to 3 have been executed
 | |
|         # Output format is purposely lousy
 | |
|         print 'diff', self.a, self.b
 | |
|         if self.a_only:
 | |
|             print 'Only in', self.a, ':', self.a_only
 | |
|         if self.b_only:
 | |
|             print 'Only in', self.b, ':', self.b_only
 | |
|         if self.same_files:
 | |
|             print 'Identical files :', self.same_files
 | |
|         if self.diff_files:
 | |
|             print 'Differing files :', self.diff_files
 | |
|         if self.funny_files:
 | |
|             print 'Trouble with common files :', self.funny_files
 | |
|         if self.common_dirs:
 | |
|             print 'Common subdirectories :', self.common_dirs
 | |
|         if self.common_funny:
 | |
|             print 'Common funny cases :', self.common_funny
 | |
| 
 | |
|     def report_closure(self):
 | |
|         """Print reports on self and on subdirs.
 | |
|         If phase 4 hasn't been done, no subdir reports are printed."""
 | |
|         self.report()
 | |
|         try:
 | |
|             x = self.subdirs
 | |
|         except AttributeError:
 | |
|             return # No subdirectories computed
 | |
|         for x in self.subdirs.keys():
 | |
|             print
 | |
|             self.subdirs[x].report_closure()
 | |
| 
 | |
|     def report_phase4_closure(self):
 | |
|         """Report and do phase 4 recursively."""
 | |
|         self.report()
 | |
|         self.phase4()
 | |
|         for x in self.subdirs.keys():
 | |
|             print
 | |
|             self.subdirs[x].report_phase4_closure()
 | |
| 
 | |
| 
 | |
| def cmpfiles(a, b, common):
 | |
|     """Compare common files in two directories.
 | |
|     Return:
 | |
|         - files that compare equal
 | |
|         - files that compare different
 | |
|         - funny cases (can't stat etc.)"""
 | |
| 
 | |
|     res = ([], [], [])
 | |
|     for x in common:
 | |
|         res[cmp(os.path.join(a, x), os.path.join(b, x))].append(x)
 | |
|     return res
 | |
| 
 | |
| 
 | |
| def cmp(a, b):
 | |
|     """Compare two files.
 | |
|     Return:
 | |
|         0 for equal
 | |
|         1 for different
 | |
|         2 for funny cases (can't stat, etc.)"""
 | |
| 
 | |
|     try:
 | |
|         if cmpcache.cmp(a, b): return 0
 | |
|         return 1
 | |
|     except os.error:
 | |
|         return 2
 | |
| 
 | |
| 
 | |
| def filter(list, skip):
 | |
|     """Return a copy with items that occur in skip removed."""
 | |
| 
 | |
|     result = []
 | |
|     for item in list:
 | |
|         if item not in skip: result.append(item)
 | |
|     return result
 | |
| 
 | |
| 
 | |
| def demo():
 | |
|     """Demonstration and testing."""
 | |
| 
 | |
|     import sys
 | |
|     import getopt
 | |
|     options, args = getopt.getopt(sys.argv[1:], 'r')
 | |
|     if len(args) != 2:
 | |
|         raise getopt.error, 'need exactly two args'
 | |
|     dd = dircmp().new(args[0], args[1])
 | |
|     dd.run()
 | |
|     if ('-r', '') in options:
 | |
|         dd.report_phase4_closure()
 | |
|     else:
 | |
|         dd.report()
 | |
| 
 | |
| if __name__ == "__main__":
 | |
|     demo()
 |