1#!/usr/bin/env python
2
3"""List all those Python files that require a coding directive
4
5Usage: nocoding.py dir1 [dir2...]
6"""
7
8__author__ = "Oleg Broytmann, Georg Brandl"
9
10import sys, os, re, getopt
11
12# our pysource module finds Python source files
13try:
14    import pysource
15except 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
35decl_re = re.compile(r"coding[=:]\s*([-\w.]+)")
36
37def get_declaration(line):
38    match = decl_re.search(line)
39    if match:
40        return match.group(1)
41    return ''
42
43def has_correct_encoding(text, codec):
44    try:
45        unicode(text, codec)
46    except UnicodeDecodeError:
47        return False
48    else:
49        return True
50
51def 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
75usage = """Usage: %s [-cd] paths...
76    -c: recognize Python source files trying to compile them
77    -d: debug output""" % sys.argv[0]
78
79try:
80    opts, args = getopt.getopt(sys.argv[1:], 'cd')
81except getopt.error, msg:
82    print >>sys.stderr, msg
83    print >>sys.stderr, usage
84    sys.exit(1)
85
86is_python = pysource.looks_like_python
87debug = False
88
89for o, a in opts:
90    if o == '-c':
91        is_python = pysource.can_be_compiled
92    elif o == '-d':
93        debug = True
94
95if not args:
96    print >>sys.stderr, usage
97    sys.exit(1)
98
99for 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
105