Code

Ticket #3094: validators.2.py

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

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