#!/usr/bin/env python ''' w3c-validator - Validate HTML and CSS files using the WC3 validators ''' import os import sys import time import json import subprocess html_validator_url = 'https://validator.w3.org/check' css_validator_url = 'https://jigsaw.w3.org/css-validator/validator' verbose_option = False def message(msg): print(msg, file=sys.stderr) def verbose(msg): if verbose_option: message(msg) def validate(filename): ''' Validate file and return JSON result as dictionary. 'filename' can be a file name or an HTTP URL. Return '' if the validator does not return valid JSON. Raise OSError if curl command returns an error status. ''' quoted_filename = filename if filename.startswith('https://'): # Submit URI with GET. if filename.endswith('.css'): cmd = ('curl -sG -d uri=%s -d output=json -d warning=0 %s' % (quoted_filename, css_validator_url)) else: cmd = ('curl -sG -d uri=%s -d output=json %s' % (quoted_filename, html_validator_url)) else: # Upload file as multipart/form-data with POST. if filename.endswith('.css'): cmd = ('curl -sF "file=@%s;type=text/css" -F output=json -F warning=0 %s' % (quoted_filename, css_validator_url)) else: cmd = ('curl -sF "uploaded_file=@%s;type=text/html" -F output=json %s' % (quoted_filename, html_validator_url)) verbose(cmd) status, output = subprocess.getstatusoutput(cmd) if status != 0: raise OSError(status, 'failed: %s' % cmd) verbose(output) try: result = json.loads(output) except ValueError: result = '' time.sleep(2) # Be nice and don't hog the free validator service return result if __name__ == '__main__': if len(sys.argv) >= 2 and sys.argv[1] == '--verbose': verbose_option = True args = sys.argv[2:] else: args = sys.argv[1:] if len(args) == 0: message('usage: %s [--verbose] FILE|URL...' % os.path.basename(sys.argv[0])) exit(1) errors = 0 warnings = 0 for f in args: message('validating: %s ...' % f) retrys = 0 while retrys < 2: result = validate(f) if result: break retrys += 1 message('retrying: %s ...' % f) else: message('failed: %s' % f) errors += 1 continue if f.endswith('.css'): errorcount = result['cssvalidation']['result']['errorcount'] warningcount = result['cssvalidation']['result']['warningcount'] errors += errorcount warnings += warningcount if errorcount > 0: message('errors: %d' % errorcount) if warningcount > 0: message('warnings: %d' % warningcount) # Output CSS warnings messages if 'warnings' in result['cssvalidation']: for msg in result['cssvalidation']['warnings']: if 'line' in msg: message('%(type)s: line %(line)d: %(message)s' % msg) else: message('%(type)s: %(message)s' % msg) # Output CSS error messages if 'errors' in result['cssvalidation']: for msg in result['cssvalidation']['errors']: if 'line' in msg: message('%(type)s: line %(line)d: %(message)s' % msg) else: message('%(type)s: %(message)s' % msg) else: for msg in result['messages']: if 'lastLine' in msg: message('%(type)s: line %(lastLine)d: %(message)s' % msg) else: message('%(type)s: %(message)s' % msg) if msg['type'] == 'error': errors += 1 else: warnings += 1 if errors: exit(1)