1 #!/usr/bin/env python3 2 3 """List all those Python files that require a coding directive 4 5 Usage: findnocoding.py dir1 [dir2...] 6 """ 7 8 __author__ = "Oleg Broytmann, Georg Brandl" 9 10 import sys, os, re, getopt 11 12 # our pysource module finds Python source files 13 try: 14 import pysource 15 except ImportError: 16 # emulate the module with a simple os.walk 17 class pysource: 18 has_python_ext = looks_like_python = can_be_compiled = None 19 def walk_python_files(self, paths, *args, **kwargs): 20 for path in paths: 21 if os.path.isfile(path): 22 yield path.endswith(".py") 23 elif os.path.isdir(path): 24 for root, dirs, files in os.walk(path): 25 for filename in files: 26 if filename.endswith(".py"): 27 yield os.path.join(root, filename) 28 pysource = pysource() 29 30 31 print("The pysource module is not available; " 32 "no sophisticated Python source file search will be done.", file=sys.stderr) 33 34 35 decl_re = re.compile(rb'^[ \t\f]*#.*?coding[:=][ \t]*([-\w.]+)') 36 blank_re = re.compile(rb'^[ \t\f]*(?:[#\r\n]|$)') 37 38 def get_declaration(line): 39 match = decl_re.match(line) 40 if match: 41 return match.group(1) 42 return b'' 43 44 def has_correct_encoding(text, codec): 45 try: 46 str(text, codec) 47 except UnicodeDecodeError: 48 return False 49 else: 50 return True 51 52 def needs_declaration(fullpath): 53 try: 54 infile = open(fullpath, 'rb') 55 except IOError: # Oops, the file was removed - ignore it 56 return None 57 58 with infile: 59 line1 = infile.readline() 60 line2 = infile.readline() 61 62 if (get_declaration(line1) or 63 blank_re.match(line1) and get_declaration(line2)): 64 # the file does have an encoding declaration, so trust it 65 return False 66 67 # check the whole file for non utf-8 characters 68 rest = infile.read() 69 70 if has_correct_encoding(line1+line2+rest, "utf-8"): 71 return False 72 73 return True 74 75 76 usage = """Usage: %s [-cd] paths... 77 -c: recognize Python source files trying to compile them 78 -d: debug output""" % sys.argv[0] 79 80 if __name__ == '__main__': 81 82 try: 83 opts, args = getopt.getopt(sys.argv[1:], 'cd') 84 except getopt.error as msg: 85 print(msg, file=sys.stderr) 86 print(usage, file=sys.stderr) 87 sys.exit(1) 88 89 is_python = pysource.looks_like_python 90 debug = False 91 92 for o, a in opts: 93 if o == '-c': 94 is_python = pysource.can_be_compiled 95 elif o == '-d': 96 debug = True 97 98 if not args: 99 print(usage, file=sys.stderr) 100 sys.exit(1) 101 102 for fullpath in pysource.walk_python_files(args, is_python): 103 if debug: 104 print("Testing for coding: %s" % fullpath) 105 result = needs_declaration(fullpath) 106 if result: 107 print(fullpath) 108