#!/usr/bin/env python

"""List all those Python files that require a coding directive

Usage: nocoding.py dir1 [dir2...]
"""

__author__ = "Oleg Broytmann, Georg Brandl"

import sys, os, re, getopt

# our pysource module finds Python source files
try:
   import pysource
except:
   # emulate the module with a simple os.walk
   class pysource:
       has_python_ext = looks_like_python = can_be_compiled = None
       def walk_python_files(self, paths, *args, **kwargs):
           for path in paths:
               if os.path.isfile(path):
                   yield path.endswith(".py")
               elif os.path.isdir(path):
                   for root, dirs, files in os.walk(path):
                       for filename in files:
                           if filename.endswith(".py"):
                               yield os.path.join(root, filename)
   pysource = pysource()


   print >>sys.stderr, ("The pysource module is not available; "
                        "no sophisticated Python source file search will be done.")


decl_re = re.compile(r"coding[=:]\s*([-\w.]+)")

def get_declaration(line):
   match = decl_re.search(line)
   if match:
       return match.group(1)
   return ''

def has_correct_encoding(text, codec):
   try:
       unicode(text, codec)
   except UnicodeDecodeError:
       return False
   else:
       return True

def needs_declaration(fullpath):
   try:
       infile = open(fullpath, 'rU')
   except IOError: # Oops, the file was removed - ignore it
       return None

   line1 = infile.readline()
   line2 = infile.readline()

   if get_declaration(line1) or get_declaration(line2):
       # the file does have an encoding declaration, so trust it
       infile.close()
       return False

   # check the whole file for non-ASCII characters
   rest = infile.read()
   infile.close()

   if has_correct_encoding(line1+line2+rest, "ascii"):
       return False

   return True


usage = """Usage: %s [-cd] paths...
   -c: recognize Python source files trying to compile them
   -d: debug output""" % sys.argv[0]

try:
   opts, args = getopt.getopt(sys.argv[1:], 'cd')
except getopt.error, msg:
   print >>sys.stderr, msg
   print >>sys.stderr, usage
   sys.exit(1)

is_python = pysource.looks_like_python
debug = False

for o, a in opts:
   if o == '-c':
       is_python = pysource.can_be_compiled
   elif o == '-d':
       debug = True

if not args:
   print >>sys.stderr, usage
   sys.exit(1)

for fullpath in pysource.walk_python_files(args, is_python):
   if debug:
       print "Testing for coding: %s" % fullpath
   result = needs_declaration(fullpath)
   if result:
       print fullpath