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