Code

Ticket #3094: validators.py

File validators.py, 3.3 KB (added by David Danier <goliath.mailinglist@…>, 7 years ago)

validator.py including RelaxNG-class

Line 
1from django.core.validators import ValidationError
2from lxml import etree
3from StringIO import StringIO
4from django.utils.translation import gettext_lazy as _
5from django.template.defaultfilters import escape
6import re
7
8class RelaxNG(object):
9        "Validate against a Relax NG schema"
10        def __init__(self, schema_path, additional_root_element=None):
11                self.schema_path = schema_path
12                self.additional_root_element = additional_root_element
13       
14        def raiseValidationError(self, field_data, error_log):
15                display_errors = []
16                lines = field_data.split('\n')
17                for error in error_log:
18                        # Scrape the lxml error messages to reword them more nicely.
19                        m = re.search(r'Opening and ending tag mismatch: (.+?) line (\d+?) and (.+?)', error.message)
20                        if m:
21                                display_errors.append(_('Please close the unclosed %(tag)s tag from line %(line)s. (Line starts with "%(start)s".)') % \
22                                        {'tag':m.group(1).replace('/', ''), 'line':m.group(2), 'start':lines[int(m.group(2)) - 1][:30]})
23                                continue
24                        m = re.search(r'Did not expect text in element (.+?) content', error.message)
25                        if m:
26                                display_errors.append(_('Some text starting on line %(line)s is not allowed in that context. (Line starts with "%(start)s".)') % \
27                                        {'line':error.line, 'start':lines[int(error.line) - 1][:30]})
28                                continue
29                        m = re.search(r'Invalid attribute (.+?) for element (.+?)', error.message)
30                        if m:
31                                display_errors.append(_('"%(attr)s" on line %(line)s is an invalid attribute. (Line starts with "%(start)s".)') % \
32                                        {'attr':m.group(1), 'line':error.line, 'start':lines[int(error.line) - 1][:30]})
33                                continue
34                        m = re.search(r'Did not expect element (.+?) there', error.message)
35                        if m:
36                                display_errors.append(_('"<%(tag)s>" on line %(line)s is an invalid tag. (Line starts with "%(start)s".)') % \
37                                        {'tag':m.group(1), 'line':error.line, 'start':lines[int(error.line) - 1][:30]})
38                                continue
39                        m = re.search(r'Element (.+?) failed to validate attributes', error.message)
40                        if m:
41                                display_errors.append(_('A tag on line %(line)s is missing one or more required attributes. (Line starts with "%(start)s".)') % \
42                                        {'line':error.line, 'start':lines[int(error.line) - 1][:30]})
43                                continue
44                        m = re.search(r'Invalid attribute (.+?) for element (.+?)', error.message)
45                        if m:
46                                display_errors.append(_('The "%(attr)s" attribute on line %(line)s has an invalid value. (Line starts with "%(start)s".)') % \
47                                        {'attr':m.group(1), 'line':error.line, 'start':lines[int(error.line) - 1][:30]})
48                                continue
49                        # Failing all those checks, use the default error message.
50                        display_errors.append('Line %s: %s [%s]' % (error.line, error.message, error.level_name))
51                raise ValidationError, display_errors
52       
53        def __call__(self, field_data, all_data):
54                self.errors = []
55                if self.additional_root_element:
56                        field_data = '<%(are)s>%(data)s\n</%(are)s>' % {
57                                'are': self.additional_root_element,
58                                'data': field_data
59                        }
60               
61                etree.clearErrorLog()
62                try:
63                        doc = etree.parse(StringIO(field_data))
64                except etree.XMLSyntaxError, e:
65                        self.raiseValidationError(field_data, e.error_log)
66                etree.clearErrorLog()
67                try:
68                        rng_doc = etree.parse(self.schema_path)
69                except etree.XMLSyntaxError, e:
70                        self.raiseValidationError(field_data, e.error_log)
71                rng = etree.RelaxNG(rng_doc)
72                if not rng(doc):
73                        self.raiseValidationError(field_data, rng.error_log)
74