summaryrefslogtreecommitdiff
path: root/paste/wdg_validate.py
blob: b7bdeff4ab49867f12b281978030ee0ce069fda8 (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
"""
Middleware that checks HTML and appends messages about the validity of
the HTML.  Uses: http://www.htmlhelp.com/tools/validator/ -- interacts
with the command line client.  Use the configuration ``wdg_path`` to
override the path (default: looks for ``validate`` in $PATH).

To install, in your web context's __init__.py::

    def urlparser_wrap(environ, start_response, app):
        return wdg_validate.WDGValidateMiddleware(app)(
            environ, start_response)
"""

from cStringIO import StringIO
import subprocess
from paste import wsgilib
import re
import cgi

class WDGValidateMiddleware(object):

    _end_body_regex = re.compile(r'</body>', re.I)

    def __init__(self, app):
        self.app = app

    def __call__(self, environ, start_response):
        output = StringIO()
        response = []
        def writer_start_response(status, headers):
            response.extend((status, headers))
            start_response(status, headers)
            return output.write
        app_iter = self.app(environ, writer_start_response)
        try:
            for s in app_iter:
                output.write(s)
        finally:
            if hasattr(app_iter, 'close'):
                app_iter.close()
        page = output.getvalue()
        status, headers = response
        v = wsgilib.header_value(headers, 'content-type')
        if (not v.startswith('text/html')
            and not v.startswith('text/xhtml+xml')):
            # Can't validate
            # @@: Should validate CSS too... but using what?
            return [page]
        ops = []
        if v.startswith('text/xhtml+xml'):
            ops.append('--xml')
        # @@: Should capture encoding too
        conf = environ['paste.config']
        wdg_path = conf.get('wdg_path', 'validate')
        proc = subprocess.Popen([wdg_path] + ops,
                                shell=False,
                                close_fds=True,
                                stdout=subprocess.PIPE,
                                stdin=subprocess.PIPE,
                                stderr=subprocess.STDOUT)
        stdout = proc.communicate(page)[0]
        proc.wait()
        if not stdout:
            return [page]
        add_text = '<pre style="background-color: #ffd; color: #600; border: 1px solid #000;">%s</pre>' % cgi.escape(stdout)
        match = self._end_body_regex.search(page)
        if match:
            page = page[:match.start()] + add_text + page[match.end():]
        else:
            page += add_text
        return [page]