aboutsummaryrefslogtreecommitdiffstats
path: root/w3c-validator.py
diff options
context:
space:
mode:
Diffstat (limited to 'w3c-validator.py')
-rw-r--r--w3c-validator.py123
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)