1#!/usr/bin/env python 2 3"""\ 4List python source files. 5 6There are three functions to check whether a file is a Python source, listed 7here with increasing complexity: 8 9- has_python_ext() checks whether a file name ends in '.py[w]'. 10- look_like_python() checks whether the file is not binary and either has 11 the '.py[w]' extension or the first line contains the word 'python'. 12- can_be_compiled() checks whether the file can be compiled by compile(). 13 14The file also must be of appropriate size - not bigger than a megabyte. 15 16walk_python_files() recursively lists all Python files under the given directories. 17""" 18__author__ = "Oleg Broytmann, Georg Brandl" 19 20__all__ = ["has_python_ext", "looks_like_python", "can_be_compiled", "walk_python_files"] 21 22 23import os, re 24 25binary_re = re.compile('[\x00-\x08\x0E-\x1F\x7F]') 26 27debug = False 28 29def print_debug(msg): 30 if debug: print msg 31 32 33def _open(fullpath): 34 try: 35 size = os.stat(fullpath).st_size 36 except OSError, err: # Permission denied - ignore the file 37 print_debug("%s: permission denied: %s" % (fullpath, err)) 38 return None 39 40 if size > 1024*1024: # too big 41 print_debug("%s: the file is too big: %d bytes" % (fullpath, size)) 42 return None 43 44 try: 45 return open(fullpath, 'rU') 46 except IOError, err: # Access denied, or a special file - ignore it 47 print_debug("%s: access denied: %s" % (fullpath, err)) 48 return None 49 50def has_python_ext(fullpath): 51 return fullpath.endswith(".py") or fullpath.endswith(".pyw") 52 53def looks_like_python(fullpath): 54 infile = _open(fullpath) 55 if infile is None: 56 return False 57 58 line = infile.readline() 59 infile.close() 60 61 if binary_re.search(line): 62 # file appears to be binary 63 print_debug("%s: appears to be binary" % fullpath) 64 return False 65 66 if fullpath.endswith(".py") or fullpath.endswith(".pyw"): 67 return True 68 elif "python" in line: 69 # disguised Python script (e.g. CGI) 70 return True 71 72 return False 73 74def can_be_compiled(fullpath): 75 infile = _open(fullpath) 76 if infile is None: 77 return False 78 79 code = infile.read() 80 infile.close() 81 82 try: 83 compile(code, fullpath, "exec") 84 except Exception, err: 85 print_debug("%s: cannot compile: %s" % (fullpath, err)) 86 return False 87 88 return True 89 90 91def walk_python_files(paths, is_python=looks_like_python, exclude_dirs=None): 92 """\ 93 Recursively yield all Python source files below the given paths. 94 95 paths: a list of files and/or directories to be checked. 96 is_python: a function that takes a file name and checks whether it is a 97 Python source file 98 exclude_dirs: a list of directory base names that should be excluded in 99 the search 100 """ 101 if exclude_dirs is None: 102 exclude_dirs=[] 103 104 for path in paths: 105 print_debug("testing: %s" % path) 106 if os.path.isfile(path): 107 if is_python(path): 108 yield path 109 elif os.path.isdir(path): 110 print_debug(" it is a directory") 111 for dirpath, dirnames, filenames in os.walk(path): 112 for exclude in exclude_dirs: 113 if exclude in dirnames: 114 dirnames.remove(exclude) 115 for filename in filenames: 116 fullpath = os.path.join(dirpath, filename) 117 print_debug("testing: %s" % fullpath) 118 if is_python(fullpath): 119 yield fullpath 120 else: 121 print_debug(" unknown type") 122 123 124if __name__ == "__main__": 125 # Two simple examples/tests 126 for fullpath in walk_python_files(['.']): 127 print fullpath 128 print "----------" 129 for fullpath in walk_python_files(['.'], is_python=can_be_compiled): 130 print fullpath 131