1 # support for RelaxNG validation
2 from lxml.includes cimport relaxng
6 import rnc2rng as _rnc2rng
11 cdef int _require_rnc2rng() except -1:
13 raise RelaxNGParseError(
14 'compact syntax not supported (please install rnc2rng)')
18 cdef class RelaxNGError(LxmlError):
19 """Base class for RelaxNG errors.
22 cdef class RelaxNGParseError(RelaxNGError):
23 """Error while parsing an XML document as RelaxNG.
26 cdef class RelaxNGValidateError(RelaxNGError):
27 """Error while validating an XML document with a RelaxNG schema.
31 ################################################################################
34 cdef class RelaxNG(_Validator):
35 u"""RelaxNG(self, etree=None, file=None)
36 Turn a document into a Relax NG validator.
38 Either pass a schema as Element or ElementTree, or pass a file or
39 filename through the ``file`` keyword argument.
41 cdef relaxng.xmlRelaxNG* _c_schema
45 def __init__(self, etree=None, *, file=None):
47 cdef _Element root_node
48 cdef xmlDoc* fake_c_doc = NULL
49 cdef relaxng.xmlRelaxNGParserCtxt* parser_ctxt = NULL
50 _Validator.__init__(self)
52 doc = _documentOrRaise(etree)
53 root_node = _rootNodeOrRaise(etree)
54 fake_c_doc = _fakeRootDoc(doc._c_doc, root_node._c_node)
55 parser_ctxt = relaxng.xmlRelaxNGNewDocParserCtxt(fake_c_doc)
56 elif file is not None:
58 if file[-4:].lower() == '.rnc':
60 rng_data_utf8 = _utf8(_rnc2rng.dumps(_rnc2rng.load(file)))
61 doc = _parseMemoryDocument(rng_data_utf8, parser=None, url=file)
62 parser_ctxt = relaxng.xmlRelaxNGNewDocParserCtxt(doc._c_doc)
65 filename = _encodeFilename(file)
67 parser_ctxt = relaxng.xmlRelaxNGNewParserCtxt(_cstr(filename))
68 elif (_getFilenameForFile(file) or '')[-4:].lower() == '.rnc':
70 rng_data_utf8 = _utf8(_rnc2rng.dumps(_rnc2rng.load(file)))
71 doc = _parseMemoryDocument(
72 rng_data_utf8, parser=None, url=_getFilenameForFile(file))
73 parser_ctxt = relaxng.xmlRelaxNGNewDocParserCtxt(doc._c_doc)
75 doc = _parseDocument(file, parser=None, base_url=None)
76 parser_ctxt = relaxng.xmlRelaxNGNewDocParserCtxt(doc._c_doc)
78 raise RelaxNGParseError, u"No tree or file given"
80 if parser_ctxt is NULL:
81 if fake_c_doc is not NULL:
82 _destroyFakeDoc(doc._c_doc, fake_c_doc)
83 raise RelaxNGParseError(
84 self._error_log._buildExceptionMessage(
85 u"Document is not parsable as Relax NG"),
88 relaxng.xmlRelaxNGSetParserStructuredErrors(
89 parser_ctxt, _receiveError, <void*>self._error_log)
90 _connectGenericErrorLog(self._error_log, xmlerror.XML_FROM_RELAXNGP)
91 self._c_schema = relaxng.xmlRelaxNGParse(parser_ctxt)
92 _connectGenericErrorLog(None)
94 relaxng.xmlRelaxNGFreeParserCtxt(parser_ctxt)
95 if self._c_schema is NULL:
96 if fake_c_doc is not NULL:
97 _destroyFakeDoc(doc._c_doc, fake_c_doc)
98 raise RelaxNGParseError(
99 self._error_log._buildExceptionMessage(
100 u"Document is not valid Relax NG"),
102 if fake_c_doc is not NULL:
103 _destroyFakeDoc(doc._c_doc, fake_c_doc)
105 def __dealloc__(self):
106 relaxng.xmlRelaxNGFree(self._c_schema)
108 def __call__(self, etree):
109 u"""__call__(self, etree)
111 Validate doc using Relax NG.
113 Returns true if document is valid, false if not."""
115 cdef _Element root_node
117 cdef relaxng.xmlRelaxNGValidCtxt* valid_ctxt
120 assert self._c_schema is not NULL, "RelaxNG instance not initialised"
121 doc = _documentOrRaise(etree)
122 root_node = _rootNodeOrRaise(etree)
124 valid_ctxt = relaxng.xmlRelaxNGNewValidCtxt(self._c_schema)
125 if valid_ctxt is NULL:
129 self._error_log.clear()
130 relaxng.xmlRelaxNGSetValidStructuredErrors(
131 valid_ctxt, _receiveError, <void*>self._error_log)
132 _connectGenericErrorLog(self._error_log, xmlerror.XML_FROM_RELAXNGV)
133 c_doc = _fakeRootDoc(doc._c_doc, root_node._c_node)
135 ret = relaxng.xmlRelaxNGValidateDoc(valid_ctxt, c_doc)
136 _destroyFakeDoc(doc._c_doc, c_doc)
138 _connectGenericErrorLog(None)
139 relaxng.xmlRelaxNGFreeValidCtxt(valid_ctxt)
142 raise RelaxNGValidateError(
143 u"Internal error in Relax NG validation",
151 def from_rnc_string(cls, src, base_url=None):
152 """Parse a RelaxNG schema in compact syntax from a text string
154 Requires the rnc2rng package to be installed.
156 Passing the source URL or file path of the source as 'base_url'
157 will enable resolving resource references relative to the source.
160 rng_str = utf8(_rnc2rng.dumps(_rnc2rng.loads(src)))
161 return cls(_parseMemoryDocument(rng_str, parser=None, url=base_url))