Home | History | Annotate | Download | only in scripts
      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 blank_re = re.compile(r'^[ \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         unicode(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, 'rU')
     55     except IOError: # Oops, the file was removed - ignore it
     56         return None
     57 
     58     line1 = infile.readline()
     59     line2 = infile.readline()
     60 
     61     if (get_declaration(line1) or
     62         blank_re.match(line1) and get_declaration(line2)):
     63         # the file does have an encoding declaration, so trust it
     64         infile.close()
     65         return False
     66 
     67     # check the whole file for non-ASCII characters
     68     rest = infile.read()
     69     infile.close()
     70 
     71     if has_correct_encoding(line1+line2+rest, "ascii"):
     72         return False
     73 
     74     return True
     75 
     76 
     77 usage = """Usage: %s [-cd] paths...
     78     -c: recognize Python source files trying to compile them
     79     -d: debug output""" % sys.argv[0]
     80 
     81 try:
     82     opts, args = getopt.getopt(sys.argv[1:], 'cd')
     83 except getopt.error, msg:
     84     print >>sys.stderr, msg
     85     print >>sys.stderr, usage
     86     sys.exit(1)
     87 
     88 is_python = pysource.looks_like_python
     89 debug = False
     90 
     91 for o, a in opts:
     92     if o == '-c':
     93         is_python = pysource.can_be_compiled
     94     elif o == '-d':
     95         debug = True
     96 
     97 if not args:
     98     print >>sys.stderr, usage
     99     sys.exit(1)
    100 
    101 for fullpath in pysource.walk_python_files(args, is_python):
    102     if debug:
    103         print "Testing for coding: %s" % fullpath
    104     result = needs_declaration(fullpath)
    105     if result:
    106         print fullpath
    107