Imported Upstream version 4.5.2
[platform/upstream/python-lxml.git] / src / lxml / schematron.pxi
1 # support for Schematron validation
2 from lxml.includes cimport schematron
3
4
5 cdef class SchematronError(LxmlError):
6     """Base class of all Schematron errors.
7     """
8
9 cdef class SchematronParseError(SchematronError):
10     """Error while parsing an XML document as Schematron schema.
11     """
12
13 cdef class SchematronValidateError(SchematronError):
14     """Error while validating an XML document with a Schematron schema.
15     """
16
17
18 ################################################################################
19 # Schematron
20
21 cdef class Schematron(_Validator):
22     u"""Schematron(self, etree=None, file=None)
23     A Schematron validator.
24
25     Pass a root Element or an ElementTree to turn it into a validator.
26     Alternatively, pass a filename as keyword argument 'file' to parse from
27     the file system.
28
29     Schematron is a less well known, but very powerful schema language.  The main
30     idea is to use the capabilities of XPath to put restrictions on the structure
31     and the content of XML documents.  Here is a simple example::
32
33       >>> schematron = Schematron(XML('''
34       ... <schema xmlns="http://www.ascc.net/xml/schematron" >
35       ...   <pattern name="id is the only permitted attribute name">
36       ...     <rule context="*">
37       ...       <report test="@*[not(name()='id')]">Attribute
38       ...         <name path="@*[not(name()='id')]"/> is forbidden<name/>
39       ...       </report>
40       ...     </rule>
41       ...   </pattern>
42       ... </schema>
43       ... '''))
44
45       >>> xml = XML('''
46       ... <AAA name="aaa">
47       ...   <BBB id="bbb"/>
48       ...   <CCC color="ccc"/>
49       ... </AAA>
50       ... ''')
51
52       >>> schematron.validate(xml)
53       0
54
55       >>> xml = XML('''
56       ... <AAA id="aaa">
57       ...   <BBB id="bbb"/>
58       ...   <CCC/>
59       ... </AAA>
60       ... ''')
61
62       >>> schematron.validate(xml)
63       1
64
65     Schematron was added to libxml2 in version 2.6.21.  Before version 2.6.32,
66     however, Schematron lacked support for error reporting other than to stderr.
67     This version is therefore required to retrieve validation warnings and
68     errors in lxml.
69     """
70     cdef schematron.xmlSchematron* _c_schema
71     cdef xmlDoc* _c_schema_doc
72     def __cinit__(self):
73         self._c_schema = NULL
74         self._c_schema_doc = NULL
75
76     def __init__(self, etree=None, *, file=None):
77         cdef _Document doc
78         cdef _Element root_node
79         cdef xmlNode* c_node
80         cdef char* c_href
81         cdef schematron.xmlSchematronParserCtxt* parser_ctxt = NULL
82         _Validator.__init__(self)
83         if not config.ENABLE_SCHEMATRON:
84             raise SchematronError, \
85                 u"lxml.etree was compiled without Schematron support."
86         if etree is not None:
87             doc = _documentOrRaise(etree)
88             root_node = _rootNodeOrRaise(etree)
89             self._c_schema_doc = _copyDocRoot(doc._c_doc, root_node._c_node)
90             parser_ctxt = schematron.xmlSchematronNewDocParserCtxt(self._c_schema_doc)
91         elif file is not None:
92             filename = _getFilenameForFile(file)
93             if filename is None:
94                 # XXX assume a string object
95                 filename = file
96             filename = _encodeFilename(filename)
97             with self._error_log:
98                 orig_loader = _register_document_loader()
99                 parser_ctxt = schematron.xmlSchematronNewParserCtxt(_cstr(filename))
100                 _reset_document_loader(orig_loader)
101         else:
102             raise SchematronParseError, u"No tree or file given"
103
104         if parser_ctxt is NULL:
105             if self._c_schema_doc is not NULL:
106                 tree.xmlFreeDoc(self._c_schema_doc)
107                 self._c_schema_doc = NULL
108             raise MemoryError()
109
110         try:
111             with self._error_log:
112                 orig_loader = _register_document_loader()
113                 self._c_schema = schematron.xmlSchematronParse(parser_ctxt)
114                 _reset_document_loader(orig_loader)
115         finally:
116             schematron.xmlSchematronFreeParserCtxt(parser_ctxt)
117
118         if self._c_schema is NULL:
119             raise SchematronParseError(
120                 u"Document is not a valid Schematron schema",
121                 self._error_log)
122
123     def __dealloc__(self):
124         schematron.xmlSchematronFree(self._c_schema)
125         if self._c_schema_doc is not NULL:
126             tree.xmlFreeDoc(self._c_schema_doc)
127
128     def __call__(self, etree):
129         u"""__call__(self, etree)
130
131         Validate doc using Schematron.
132
133         Returns true if document is valid, false if not."""
134         cdef _Document doc
135         cdef _Element root_node
136         cdef xmlDoc* c_doc
137         cdef schematron.xmlSchematronValidCtxt* valid_ctxt
138         cdef int ret
139
140         assert self._c_schema is not NULL, "Schematron instance not initialised"
141         doc = _documentOrRaise(etree)
142         root_node = _rootNodeOrRaise(etree)
143
144         valid_ctxt = schematron.xmlSchematronNewValidCtxt(
145             self._c_schema, schematron.XML_SCHEMATRON_OUT_ERROR)
146         if valid_ctxt is NULL:
147             raise MemoryError()
148
149         try:
150             self._error_log.clear()
151             schematron.xmlSchematronSetValidStructuredErrors(
152                 valid_ctxt, _receiveError, <void*>self._error_log)
153             c_doc = _fakeRootDoc(doc._c_doc, root_node._c_node)
154             with nogil:
155                 ret = schematron.xmlSchematronValidateDoc(valid_ctxt, c_doc)
156             _destroyFakeDoc(doc._c_doc, c_doc)
157         finally:
158             schematron.xmlSchematronFreeValidCtxt(valid_ctxt)
159
160         if ret == -1:
161             raise SchematronValidateError(
162                 u"Internal error in Schematron validation",
163                 self._error_log)
164         if ret == 0:
165             return True
166         else:
167             return False