| 1 | #!/usr/bin/env python | 
|---|
| 2 |  | 
|---|
| 3 | """List all those Python files that require a coding directive | 
|---|
| 4 |  | 
|---|
| 5 | Usage: nocoding.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 >>sys.stderr, ("The pysource module is not available; " | 
|---|
| 32 | "no sophisticated Python source file search will be done.") | 
|---|
| 33 |  | 
|---|
| 34 |  | 
|---|
| 35 | decl_re = re.compile(r'^[ \t\f]*#.*coding[:=][ \t]*([-\w.]+)') | 
|---|
| 36 |  | 
|---|
| 37 | def get_declaration(line): | 
|---|
| 38 | match = decl_re.match(line) | 
|---|
| 39 | if match: | 
|---|
| 40 | return match.group(1) | 
|---|
| 41 | return b'' | 
|---|
| 42 |  | 
|---|
| 43 | def has_correct_encoding(text, codec): | 
|---|
| 44 | try: | 
|---|
| 45 | unicode(text, codec) | 
|---|
| 46 | except UnicodeDecodeError: | 
|---|
| 47 | return False | 
|---|
| 48 | else: | 
|---|
| 49 | return True | 
|---|
| 50 |  | 
|---|
| 51 | def needs_declaration(fullpath): | 
|---|
| 52 | try: | 
|---|
| 53 | infile = open(fullpath, 'rU') | 
|---|
| 54 | except IOError: # Oops, the file was removed - ignore it | 
|---|
| 55 | return None | 
|---|
| 56 |  | 
|---|
| 57 | line1 = infile.readline() | 
|---|
| 58 | line2 = infile.readline() | 
|---|
| 59 |  | 
|---|
| 60 | if get_declaration(line1) or get_declaration(line2): | 
|---|
| 61 | # the file does have an encoding declaration, so trust it | 
|---|
| 62 | infile.close() | 
|---|
| 63 | return False | 
|---|
| 64 |  | 
|---|
| 65 | # check the whole file for non-ASCII characters | 
|---|
| 66 | rest = infile.read() | 
|---|
| 67 | infile.close() | 
|---|
| 68 |  | 
|---|
| 69 | if has_correct_encoding(line1+line2+rest, "ascii"): | 
|---|
| 70 | return False | 
|---|
| 71 |  | 
|---|
| 72 | return True | 
|---|
| 73 |  | 
|---|
| 74 |  | 
|---|
| 75 | usage = """Usage: %s [-cd] paths... | 
|---|
| 76 | -c: recognize Python source files trying to compile them | 
|---|
| 77 | -d: debug output""" % sys.argv[0] | 
|---|
| 78 |  | 
|---|
| 79 | try: | 
|---|
| 80 | opts, args = getopt.getopt(sys.argv[1:], 'cd') | 
|---|
| 81 | except getopt.error, msg: | 
|---|
| 82 | print >>sys.stderr, msg | 
|---|
| 83 | print >>sys.stderr, usage | 
|---|
| 84 | sys.exit(1) | 
|---|
| 85 |  | 
|---|
| 86 | is_python = pysource.looks_like_python | 
|---|
| 87 | debug = False | 
|---|
| 88 |  | 
|---|
| 89 | for o, a in opts: | 
|---|
| 90 | if o == '-c': | 
|---|
| 91 | is_python = pysource.can_be_compiled | 
|---|
| 92 | elif o == '-d': | 
|---|
| 93 | debug = True | 
|---|
| 94 |  | 
|---|
| 95 | if not args: | 
|---|
| 96 | print >>sys.stderr, usage | 
|---|
| 97 | sys.exit(1) | 
|---|
| 98 |  | 
|---|
| 99 | for fullpath in pysource.walk_python_files(args, is_python): | 
|---|
| 100 | if debug: | 
|---|
| 101 | print "Testing for coding: %s" % fullpath | 
|---|
| 102 | result = needs_declaration(fullpath) | 
|---|
| 103 | if result: | 
|---|
| 104 | print fullpath | 
|---|