diff options
Diffstat (limited to 'w3c-validator.py')
-rw-r--r-- | w3c-validator.py | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/w3c-validator.py b/w3c-validator.py new file mode 100644 index 0000000..b3953fb --- /dev/null +++ b/w3c-validator.py @@ -0,0 +1,123 @@ +#!/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) |