Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 

216 строки
8.3 KiB

  1. # support for XMLSchema validation
  2. from lxml.includes cimport xmlschema
  3. cdef class XMLSchemaError(LxmlError):
  4. """Base class of all XML Schema errors
  5. """
  6. cdef class XMLSchemaParseError(XMLSchemaError):
  7. """Error while parsing an XML document as XML Schema.
  8. """
  9. cdef class XMLSchemaValidateError(XMLSchemaError):
  10. """Error while validating an XML document with an XML Schema.
  11. """
  12. ################################################################################
  13. # XMLSchema
  14. cdef XPath _check_for_default_attributes = XPath(
  15. "boolean(//xs:attribute[@default or @fixed][1])",
  16. namespaces={'xs': 'http://www.w3.org/2001/XMLSchema'})
  17. cdef class XMLSchema(_Validator):
  18. """XMLSchema(self, etree=None, file=None)
  19. Turn a document into an XML Schema validator.
  20. Either pass a schema as Element or ElementTree, or pass a file or
  21. filename through the ``file`` keyword argument.
  22. Passing the ``attribute_defaults`` boolean option will make the
  23. schema insert default/fixed attributes into validated documents.
  24. """
  25. cdef xmlschema.xmlSchema* _c_schema
  26. cdef _Document _doc
  27. cdef bint _has_default_attributes
  28. cdef bint _add_attribute_defaults
  29. def __cinit__(self):
  30. self._has_default_attributes = True # play it safe
  31. self._add_attribute_defaults = False
  32. def __init__(self, etree=None, *, file=None, bint attribute_defaults=False):
  33. cdef xmlschema.xmlSchemaParserCtxt* parser_ctxt
  34. cdef xmlDoc* c_doc
  35. self._add_attribute_defaults = attribute_defaults
  36. _Validator.__init__(self)
  37. c_doc = NULL
  38. if etree is not None:
  39. doc = _documentOrRaise(etree)
  40. root_node = _rootNodeOrRaise(etree)
  41. c_doc = _copyDocRoot(doc._c_doc, root_node._c_node)
  42. self._doc = _documentFactory(c_doc, doc._parser)
  43. parser_ctxt = xmlschema.xmlSchemaNewDocParserCtxt(c_doc)
  44. elif file is not None:
  45. file = _getFSPathOrObject(file)
  46. if _isString(file):
  47. filename = _encodeFilename(file)
  48. parser_ctxt = xmlschema.xmlSchemaNewParserCtxt(_cstr(filename))
  49. else:
  50. self._doc = _parseDocument(file, None, None)
  51. parser_ctxt = xmlschema.xmlSchemaNewDocParserCtxt(self._doc._c_doc)
  52. else:
  53. raise XMLSchemaParseError, "No tree or file given"
  54. if parser_ctxt is NULL:
  55. raise MemoryError()
  56. # Need a cast here because older libxml2 releases do not use 'const' in the functype.
  57. xmlschema.xmlSchemaSetParserStructuredErrors(
  58. parser_ctxt, <xmlerror.xmlStructuredErrorFunc> _receiveError, <void*>self._error_log)
  59. if self._doc is not None:
  60. # calling xmlSchemaParse on a schema with imports or
  61. # includes will cause libxml2 to create an internal
  62. # context for parsing, so push an implied context to route
  63. # resolve requests to the document's parser
  64. __GLOBAL_PARSER_CONTEXT.pushImpliedContextFromParser(self._doc._parser)
  65. with nogil:
  66. orig_loader = _register_document_loader()
  67. self._c_schema = xmlschema.xmlSchemaParse(parser_ctxt)
  68. _reset_document_loader(orig_loader)
  69. if self._doc is not None:
  70. __GLOBAL_PARSER_CONTEXT.popImpliedContext()
  71. xmlschema.xmlSchemaFreeParserCtxt(parser_ctxt)
  72. if self._c_schema is NULL:
  73. raise XMLSchemaParseError(
  74. self._error_log._buildExceptionMessage(
  75. "Document is not valid XML Schema"),
  76. self._error_log)
  77. if self._doc is not None:
  78. self._has_default_attributes = _check_for_default_attributes(self._doc)
  79. self._add_attribute_defaults = attribute_defaults and self._has_default_attributes
  80. def __dealloc__(self):
  81. xmlschema.xmlSchemaFree(self._c_schema)
  82. def __call__(self, etree):
  83. """__call__(self, etree)
  84. Validate doc using XML Schema.
  85. Returns true if document is valid, false if not.
  86. """
  87. cdef xmlschema.xmlSchemaValidCtxt* valid_ctxt
  88. cdef _Document doc
  89. cdef _Element root_node
  90. cdef xmlDoc* c_doc
  91. cdef int ret
  92. assert self._c_schema is not NULL, "Schema instance not initialised"
  93. doc = _documentOrRaise(etree)
  94. root_node = _rootNodeOrRaise(etree)
  95. valid_ctxt = xmlschema.xmlSchemaNewValidCtxt(self._c_schema)
  96. if valid_ctxt is NULL:
  97. raise MemoryError()
  98. try:
  99. if self._add_attribute_defaults:
  100. xmlschema.xmlSchemaSetValidOptions(
  101. valid_ctxt, xmlschema.XML_SCHEMA_VAL_VC_I_CREATE)
  102. self._error_log.clear()
  103. # Need a cast here because older libxml2 releases do not use 'const' in the functype.
  104. xmlschema.xmlSchemaSetValidStructuredErrors(
  105. valid_ctxt, <xmlerror.xmlStructuredErrorFunc> _receiveError, <void*>self._error_log)
  106. c_doc = _fakeRootDoc(doc._c_doc, root_node._c_node)
  107. with nogil:
  108. ret = xmlschema.xmlSchemaValidateDoc(valid_ctxt, c_doc)
  109. _destroyFakeDoc(doc._c_doc, c_doc)
  110. finally:
  111. xmlschema.xmlSchemaFreeValidCtxt(valid_ctxt)
  112. if ret == -1:
  113. raise XMLSchemaValidateError(
  114. "Internal error in XML Schema validation.",
  115. self._error_log)
  116. if ret == 0:
  117. return True
  118. else:
  119. return False
  120. cdef _ParserSchemaValidationContext _newSaxValidator(
  121. self, bint add_default_attributes):
  122. cdef _ParserSchemaValidationContext context
  123. context = _ParserSchemaValidationContext.__new__(_ParserSchemaValidationContext)
  124. context._schema = self
  125. context._add_default_attributes = (self._has_default_attributes and (
  126. add_default_attributes or self._add_attribute_defaults))
  127. return context
  128. @cython.final
  129. @cython.internal
  130. cdef class _ParserSchemaValidationContext:
  131. cdef XMLSchema _schema
  132. cdef xmlschema.xmlSchemaValidCtxt* _valid_ctxt
  133. cdef xmlschema.xmlSchemaSAXPlugStruct* _sax_plug
  134. cdef bint _add_default_attributes
  135. def __cinit__(self):
  136. self._valid_ctxt = NULL
  137. self._sax_plug = NULL
  138. self._add_default_attributes = False
  139. def __dealloc__(self):
  140. self.disconnect()
  141. if self._valid_ctxt:
  142. xmlschema.xmlSchemaFreeValidCtxt(self._valid_ctxt)
  143. cdef _ParserSchemaValidationContext copy(self):
  144. assert self._schema is not None, "_ParserSchemaValidationContext not initialised"
  145. return self._schema._newSaxValidator(
  146. self._add_default_attributes)
  147. cdef void inject_default_attributes(self, xmlDoc* c_doc) noexcept:
  148. # we currently need to insert default attributes manually
  149. # after parsing, as libxml2 does not support this at parse
  150. # time
  151. if self._add_default_attributes:
  152. with nogil:
  153. xmlschema.xmlSchemaValidateDoc(self._valid_ctxt, c_doc)
  154. cdef int connect(self, xmlparser.xmlParserCtxt* c_ctxt, _BaseErrorLog error_log) except -1:
  155. if self._valid_ctxt is NULL:
  156. self._valid_ctxt = xmlschema.xmlSchemaNewValidCtxt(
  157. self._schema._c_schema)
  158. if self._valid_ctxt is NULL:
  159. raise MemoryError()
  160. if self._add_default_attributes:
  161. xmlschema.xmlSchemaSetValidOptions(
  162. self._valid_ctxt, xmlschema.XML_SCHEMA_VAL_VC_I_CREATE)
  163. if error_log is not None:
  164. # Need a cast here because older libxml2 releases do not use 'const' in the functype.
  165. xmlschema.xmlSchemaSetValidStructuredErrors(
  166. self._valid_ctxt, <xmlerror.xmlStructuredErrorFunc> _receiveError, <void*>error_log)
  167. self._sax_plug = xmlschema.xmlSchemaSAXPlug(
  168. self._valid_ctxt, &c_ctxt.sax, &c_ctxt.userData)
  169. cdef void disconnect(self) noexcept:
  170. if self._sax_plug is not NULL:
  171. xmlschema.xmlSchemaSAXUnplug(self._sax_plug)
  172. self._sax_plug = NULL
  173. if self._valid_ctxt is not NULL:
  174. xmlschema.xmlSchemaSetValidStructuredErrors(
  175. self._valid_ctxt, NULL, NULL)
  176. cdef bint isvalid(self) noexcept:
  177. if self._valid_ctxt is NULL:
  178. return 1 # valid
  179. return xmlschema.xmlSchemaIsValid(self._valid_ctxt)