aboutsummaryrefslogtreecommitdiffstats
path: root/w3c-validator.py
blob: b3953fb0edba24c744bb7a40a98dda6168f979c9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
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)