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
|
---|