Subversion Repositories wimsdev

Rev

Rev 11744 | Rev 12454 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. """
  4. w3c-validator - Validate HTML 5 and CSS files using the WC3 validators.
  5.  
  6. Copyright: Stuart Rackham (c) 2011
  7. License:   MIT
  8. Email:     srackham@gmail.com
  9.  
  10. """
  11.  
  12. import os
  13. import sys
  14. import time
  15. import json
  16. import commands
  17. import urllib
  18.  
  19. html_validator_url = 'http://validator.w3.org/nu/'
  20. css_validator_url = 'http://jigsaw.w3.org/css-validator/validator'
  21.  
  22. verbose_option = False
  23.  
  24.  
  25. def message(msg):
  26.     """send msg to the standard error output."""
  27.     msg = msg.encode('utf8', 'xmlcharrefreplace')
  28.     print >> sys.stderr, msg
  29.  
  30.  
  31. def verbose(msg):
  32.     """call the message function if verbose_option (--verbose arg) is set."""
  33.     if verbose_option:
  34.         message(msg)
  35.  
  36.  
  37. def validate(filename):
  38.     """
  39.    Validate file and return JSON result as dictionary.
  40.  
  41.    'filename' can be a file name or an HTTP URL.
  42.    Return '' if the validator does not return valid JSON.
  43.    Raise OSError if curl command returns an error status.
  44.  
  45.    """
  46.     quoted_filename = urllib.quote(filename)
  47.     if filename.startswith('http://'):
  48.         # Submit URI with GET.
  49.         if filename.endswith('.css'):
  50.             cmd = ('curl -sG -d uri=%s -d output=json -d warning=0 %s'
  51.                     % (quoted_filename, css_validator_url))
  52.         else:
  53.             cmd = ('curl -sG -d doc=%s -d out=json %s'
  54.                     % (quoted_filename, html_validator_url))
  55.     else:
  56.         # Upload file as multipart/form-data with POST.
  57.         if filename.endswith('.css'):
  58.             cmd = ('curl -sF "file=@%s;type=text/css" -F output=json -F warning=0 %s'
  59.                     % (quoted_filename, css_validator_url))
  60.         else:
  61.             cmd = ('curl -sH "Content-Type: text/html" --data-binary "@%s" %s?out=json'
  62.                     % (quoted_filename, html_validator_url))
  63.     verbose(cmd)
  64.     status, output = commands.getstatusoutput(cmd)
  65.     if status != 0:
  66.         raise OSError(status, 'failed: %s' % cmd)
  67.     verbose(output)
  68.     # try:
  69.     # output = output.decode('utf8')
  70.     result = json.loads(output)
  71.     # except ValueError:
  72.     #    result = ""
  73.     time.sleep(2)   # Be nice and don't hog the free validator service.
  74.     return result
  75.  
  76.  
  77. if __name__ == '__main__':
  78.     if len(sys.argv) >= 2 and sys.argv[1] == '--verbose':
  79.         verbose_option = True
  80.         args = sys.argv[2:]
  81.     else:
  82.         args = sys.argv[1:]
  83.     if len(args) == 0:
  84.         message('usage: %s [--verbose] FILE|URL...' % os.path.basename(sys.argv[0]))
  85.         exit(1)
  86.     errors = 0
  87.     warnings = 0
  88.     for f in args:
  89.         message('validating: %s ...' % f)
  90.         retrys = 0
  91.         while retrys < 2:
  92.             result = validate(f)
  93.             if result:
  94.                 break
  95.             retrys += 1
  96.             message('retrying: %s ...' % f)
  97.         else:
  98.             message('failed after too much tries: %s' % f)
  99.             errors += 1
  100.             continue
  101.         if f.endswith('.css'):
  102.             errorcount = result['cssvalidation']['result']['errorcount']
  103.             warningcount = result['cssvalidation']['result']['warningcount']
  104.             errors += errorcount
  105.             warnings += warningcount
  106.             if errorcount > 0:
  107.                 message('errors: %d' % errorcount)
  108.             if warningcount > 0:
  109.                 message('warnings: %d' % warningcount)
  110.         else:
  111.             for msg in result['messages']:
  112.                 if 'lastLine' in msg:
  113.                     message('%(type)s: line %(lastLine)d: %(message)s' % msg)
  114.                 else:
  115.                     message('%(type)s: %(message)s' % msg)
  116.                 if msg['type'] == 'error':
  117.                     errors += 1
  118.                 else:
  119.                     warnings += 1
  120.     if errors:
  121.         exit(1)
  122.