def _parse_assertion(self, elem): try: path = elem.attrib['test'] except KeyError as err: self.parse_error(str(err), elem=elem) path = 'true()' try: default_namespace = get_xpath_default_namespace( elem, self.namespaces[''], self.target_namespace) except ValueError as err: self.parse_error(str(err), elem=elem) parser = XPath2Parser(self.namespaces, strict=False, schema=XMLSchemaProxy( self.schema.meta_schema), build_constructors=True) else: parser = XPath2Parser(self.namespaces, strict=False, schema=XMLSchemaProxy( self.schema.meta_schema), default_namespace=default_namespace, build_constructors=True) try: return path, parser.parse(path) except ElementPathSyntaxError as err: self.parse_error(str(err), elem=elem) return path, parser.parse('true()')
def test_attributes_type(self): parser = XPath2Parser(namespaces=self.namespaces) token = parser.parse("@min le @max") self.assertTrue( token.evaluate(context=XPathContext( self.etree.XML('<root min="10" max="20" />')))) self.assertTrue( token.evaluate(context=XPathContext( self.etree.XML('<root min="10" max="2" />')))) schema = xmlschema.XMLSchema(''' <xs:schema xmlns="http://xpath.test/ns" xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://xpath.test/ns"> <xs:element name="range" type="intRange"/> <xs:complexType name="intRange"> <xs:attribute name="min" type="xs:int"/> <xs:attribute name="max" type="xs:int"/> </xs:complexType> </xs:schema>''') parser = XPath2Parser(namespaces=self.namespaces, schema=XMLSchemaProxy(schema, schema.elements['range'])) token = parser.parse("@min le @max") self.assertTrue( token.evaluate(context=XPathContext( self.etree.XML('<root min="10" max="20" />')))) self.assertFalse( token.evaluate(context=XPathContext( self.etree.XML('<root min="10" max="2" />')))) schema = xmlschema.XMLSchema(''' <xs:schema xmlns="http://xpath.test/ns" xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://xpath.test/ns"> <xs:element name="range" type="intRange"/> <xs:complexType name="intRange"> <xs:attribute name="min" type="xs:int"/> <xs:attribute name="max" type="xs:string"/> </xs:complexType> </xs:schema>''') parser = XPath2Parser(namespaces=self.namespaces, schema=XMLSchemaProxy(schema, schema.elements['range'])) if PY3: self.assertRaises(TypeError, parser.parse, '@min le @max') else: # In Python 2 strings and numbers are comparable and strings are 'greater than' numbers. token = parser.parse("@min le @max") self.assertTrue( token.evaluate(context=XPathContext( self.etree.XML('<root min="10" max="20" />')))) self.assertTrue( token.evaluate(context=XPathContext( self.etree.XML('<root min="10" max="2" />'))))
def test_dot_shortcut_token(self): parser = XPath2Parser(default_namespace="http://xpath.test/ns") context = XMLSchemaContext(self.schema1) elem_a = self.schema1.elements['a'] elem_b3 = self.schema1.elements['b3'] token = parser.parse('.') self.assertIsNone(token.xsd_types) result = token.evaluate(context) self.assertListEqual(result, [self.schema1]) self.assertEqual(token.xsd_types, {"{http://xpath.test/ns}a": elem_a.type, "{http://xpath.test/ns}b3": elem_b3.type}) context = XMLSchemaContext(self.schema1, item=self.schema1) token = parser.parse('.') self.assertIsNone(token.xsd_types) result = token.evaluate(context) self.assertListEqual(result, [self.schema1]) self.assertEqual(token.xsd_types, {"{http://xpath.test/ns}a": elem_a.type, "{http://xpath.test/ns}b3": elem_b3.type}) context = XMLSchemaContext(self.schema1, item=self.schema2) token = parser.parse('.') self.assertIsNone(token.xsd_types) result = token.evaluate(context) self.assertListEqual(result, [self.schema2]) self.assertIsNone(token.xsd_types)
def build(self): if not self.base_type.has_simple_content(): builtin_type = XSD_BUILTIN_TYPES['anyType'] else: try: builtin_type_name = self.base_type.content.primitive_type.local_name except AttributeError: builtin_type = XSD_BUILTIN_TYPES['anySimpleType'] else: builtin_type = XSD_BUILTIN_TYPES[builtin_type_name] # Patch for compatibility with next elementpath minor release (v1.5) # where parser variables will be filled with types. if elementpath.__version__.startswith('1.4.'): variables = {'value': builtin_type.value} else: variables = {'value': builtin_type} self.parser = XPath2Parser( namespaces=self.namespaces, variables=variables, strict=False, default_namespace=self.xpath_default_namespace, schema=XMLSchemaProxy(self.schema, self)) try: self.token = self.parser.parse(self.path) except ElementPathError as err: self.parse_error(err, elem=self.elem) self.token = self.parser.parse('true()') finally: self.parser.variables.clear()
def parse_xpath_test(self): if not self.base_type.has_simple_content(): variables = {'value': XSD_BUILTIN_TYPES['anyType'].value} else: try: builtin_type_name = self.base_type.content_type.primitive_type.local_name except AttributeError: variables = {'value': XSD_BUILTIN_TYPES['anySimpleType'].value} else: variables = { 'value': XSD_BUILTIN_TYPES[builtin_type_name].value } self.parser = XPath2Parser( namespaces=self.namespaces, variables=variables, strict=False, default_namespace=self.xpath_default_namespace, schema=XMLSchemaProxy(self.schema, self)) try: self.token = self.parser.parse(self.path) except ElementPathError as err: self.parse_error(err, elem=self.elem) self.token = self.parser.parse('true()')
def _parse(self): XsdComponent._parse(self) attrib = self.elem.attrib if 'xpathDefaultNamespace' in attrib: self.xpath_default_namespace = self._parse_xpath_default_namespace( self.elem) else: self.xpath_default_namespace = self.schema.xpath_default_namespace parser = XPath2Parser(self.namespaces, strict=False, default_namespace=self.xpath_default_namespace) try: self.path = attrib['test'] except KeyError: pass # an absent test is not an error, it should be the default type else: try: self.token = parser.parse(self.path) except ElementPathError as err: self.parse_error(err) self.token = parser.parse('false()') self.path = 'false()' try: type_qname = self.schema.resolve_qname(attrib['type']) except (KeyError, ValueError, RuntimeError) as err: if 'type' in attrib: self.parse_error(err) self.type = self.maps.lookup_type(XSD_ANY_TYPE) else: child = self._parse_child_component(self.elem, strict=False) if child is None or child.tag not in (XSD_COMPLEX_TYPE, XSD_SIMPLE_TYPE): self.parse_error("missing 'type' attribute") self.type = self.maps.lookup_type(XSD_ANY_TYPE) elif child.tag == XSD_COMPLEX_TYPE: self.type = self.schema.BUILDERS.complex_type_class( child, self.schema, self) else: self.type = self.schema.BUILDERS.simple_type_factory( child, self.schema, self) else: try: self.type = self.maps.lookup_type(type_qname) except KeyError: self.parse_error("unknown type %r" % attrib['type']) else: if self.type.name != XSD_ERROR and not self.type.is_derived( self.parent.type): msg = "type {!r} is not derived from {!r}" self.parse_error( msg.format(attrib['type'], self.parent.type)) child = self._parse_child_component(self.elem, strict=False) if child is not None and child.tag in (XSD_COMPLEX_TYPE, XSD_SIMPLE_TYPE): msg = "the attribute 'type' and the <%s> local declaration are mutually exclusive" self.parse_error(msg % child.tag.split('}')[-1])
def _parse(self): super(XsdFacet, self)._parse() try: self.path = self.elem.attrib['test'] except KeyError as err: self.parse_error(str(err), elem=self.elem) self.path = 'true()' builtin_type_name = self.base_type.primitive_type.local_name variables = { 'value': datatypes.XSD_BUILTIN_TYPES[builtin_type_name].value } if 'xpathDefaultNamespace' in self.elem.attrib: self.xpath_default_namespace = self._parse_xpath_default_namespace( self.elem) else: self.xpath_default_namespace = self.schema.xpath_default_namespace self.parser = XPath2Parser( self.namespaces, strict=False, variables=variables, default_namespace=self.xpath_default_namespace) try: self.token = self.parser.parse(self.path) except (ElementPathSyntaxError, ElementPathTypeError) as err: self.parse_error(err, elem=self.elem) self.token = self.parser.parse('true()')
def test_wildcard_token(self): parser = XPath2Parser(default_namespace="http://xpath.test/ns") context = XMLSchemaContext(self.schema1) elem_a = self.schema1.elements['a'] elem_b3 = self.schema1.elements['b3'] token = parser.parse('*') self.assertEqual(token.symbol, '*') self.assertIsNone(token.xsd_types) result = token.evaluate(context) self.assertListEqual(result, [elem_a, elem_b3]) self.assertEqual(token.xsd_types, {"{http://xpath.test/ns}a": elem_a.type, "{http://xpath.test/ns}b3": elem_b3.type}) token = parser.parse('a/*') self.assertEqual(token.symbol, '/') self.assertEqual(token[0].symbol, '(name)') self.assertEqual(token[1].symbol, '*') result = token.evaluate(context) self.assertListEqual(result, elem_a.type.content[:]) self.assertIsNone(token.xsd_types) self.assertEqual(token[0].xsd_types, {"{http://xpath.test/ns}a": elem_a.type}) self.assertEqual(token[1].xsd_types, {'b1': elem_a.type.content[0].type, 'b2': elem_a.type.content[1].type, '{http://xpath.test/ns}b3': elem_b3.type})
def test_name_token(self): parser = XPath2Parser(default_namespace="http://xpath.test/ns") schema_context = XPathSchemaContext(self.schema1) elem_a = self.schema1.elements['a'] token = parser.parse('a') self.assertIsNone(token.xsd_types) context = copy(schema_context) element_node = context.root[0] self.assertIs(element_node.elem, elem_a) self.assertIs(element_node.xsd_type, elem_a.type) result = token.evaluate(context) self.assertEqual(token.xsd_types, {"{http://xpath.test/ns}a": elem_a.type}) self.assertListEqual(result, [element_node]) elem_b1 = elem_a.type.content[0] token = parser.parse('a/b1') self.assertIsNone(token[0].xsd_types) self.assertIsNone(token[1].xsd_types) context = copy(schema_context) element_node = context.root[0][0] self.assertIs(element_node.elem, elem_b1) self.assertIs(element_node.xsd_type, elem_b1.type) result = token.evaluate(context) self.assertEqual(token[0].xsd_types, {"{http://xpath.test/ns}a": elem_a.type}) self.assertEqual(token[1].xsd_types, {"b1": elem_b1.type}) self.assertListEqual(result, [element_node])
def test_bind_parser_method(self): schema_src = """<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:simpleType name="test_type"> <xs:restriction base="xs:string"/> </xs:simpleType> </xs:schema>""" schema = xmlschema.XMLSchema(schema_src) schema_proxy = XMLSchemaProxy(schema=schema) parser = XPath2Parser(namespaces=self.namespaces) schema_proxy.bind_parser(parser) self.assertIs(schema_proxy, parser.schema) parser = XPath2Parser(namespaces=self.namespaces) super(XMLSchemaProxy, schema_proxy).bind_parser(parser) self.assertIs(schema_proxy, parser.schema) super(XMLSchemaProxy, schema_proxy).bind_parser(parser) self.assertIs(schema_proxy, parser.schema)
def evaluate_assertion(self, xml_document, context_element, namespaces, parser_variables, assertion): parser = XPath2Parser(namespaces, parser_variables) context = XPathContextXSLT(root=xml_document, item=context_element) expr = "fn:boolean(%s)" % assertion root_token = parser.parse(expr) result = root_token.evaluate(context=context) return result
def evaluate_value_of_query(self, xml_document, context_element, namespaces, parser_variables, name_query): parser = XPath2Parser(namespaces, parser_variables) context = XPathContextXSLT(root=xml_document, item=context_element) expr = "fn:string(%s)" % name_query root_token = parser.parse(expr) result = root_token.evaluate(context=context) return result
def test_bind_parser_method(self): schema_proxy1 = XMLSchemaProxy(self.xs1) schema_proxy2 = XMLSchemaProxy(self.xs2) parser = XPath2Parser(strict=False, schema=schema_proxy1) self.assertIs(parser.schema, schema_proxy1) schema_proxy1.bind_parser(parser) self.assertIs(parser.schema, schema_proxy1) schema_proxy2.bind_parser(parser) self.assertIs(parser.schema, schema_proxy2)
def assert_deep_eq(self, test_context): output = create_and_run_test(test_context) expression = "fn:deep-equal($result, (%s))" % self.value variables = {'result': output} parser = XPath2Parser(variables=variables) root_node = parser.parse(expression) context = XPathContext(root=etree.XML("<empty/>")) result = root_node.evaluate(context) return result == True
def iterfind(self, path, namespaces=None): """ Creates and iterator for all XSD subelements matching the path. :param path: an XPath expression that considers the data element as the root. :param namespaces: is an optional mapping from namespace prefix to full name. :return: an iterable yielding all matching data elements in document order. """ parser = XPath2Parser(namespaces, strict=False) context = XPathContext(self) return parser.parse(path).select_results(context)
def find(self, path, namespaces=None): """ Finds the first data element matching the path. :param path: an XPath expression that considers the data element as the root. :param namespaces: an optional mapping from namespace prefix to namespace URI. :return: the first matching data element or ``None`` if there is no match. """ parser = XPath2Parser(namespaces, strict=False) context = XPathContext(self) return next(parser.parse(path).select_results(context), None)
def parse_expression(self, xml_document, expression, namespaces, variables, context_item=None): parser = XPath2Parser(namespaces, variables) root_node = parser.parse(expression) context = XPathContextXSLT(root=xml_document, item=context_item) result = root_node.evaluate(context) return result
def assert_eq(self, test_context): output = create_and_run_test(test_context) parser = XPath2Parser() root_node = parser.parse(self.value) context = XPathContext(root=etree.XML("<empty/>")) result = root_node.evaluate(context) if type(output) == list and len(output) == 1: output = output[0] # print("result: '%s' (%s)" % (str(result), str(type(result)))) return result == output
def xassert(self, test_context): # Assert contains an xpath expression whose value must be true # The expression may use the variable $result, which is the output of # the original test output = create_and_run_test(test_context) variables = {'result': output} parser = XPath2Parser(variables=variables) root_node = parser.parse(self.value) context = XPathContext(root=etree.XML("<empty/>")) result = root_node.evaluate(context) return result == True
def findall(self, path, namespaces=None): """ Finds all data elements matching the path. :param path: an XPath expression that considers the data element as the root. :param namespaces: an optional mapping from namespace prefix to full name. :return: a list containing all matching data elements in document order, \ an empty list is returned if there is no match. """ parser = XPath2Parser(namespaces, strict=False) context = XPathContext(self) return parser.parse(path).get_results(context)
def setUp(self): self.parser = XPath2Parser() self.xml_str = """<doc> <element> <name>Foo</name> <number>1</number> <decimal>12.34</decimal> <subelement> <name>Bar</name> </subelement> </element> </doc>""" self.xml_doc = etree.XML(self.xml_str)
def test_bind_parser_method(self): schema_src = dedent(""" <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:simpleType name="stringType"> <xs:restriction base="xs:string"/> </xs:simpleType> </xs:schema>""") schema = xmlschema.XMLSchema(schema_src) schema_proxy = XMLSchemaProxy(schema=schema) parser = XPath2Parser(namespaces=self.namespaces) self.assertFalse(parser.is_schema_bound()) schema_proxy.bind_parser(parser) self.assertTrue(parser.is_schema_bound()) self.assertIs(schema_proxy, parser.schema) # To test AbstractSchemaProxy.bind_parser() parser = XPath2Parser(namespaces=self.namespaces) super(XMLSchemaProxy, schema_proxy).bind_parser(parser) self.assertIs(schema_proxy, parser.schema) super(XMLSchemaProxy, schema_proxy).bind_parser(parser) self.assertIs(schema_proxy, parser.schema)
def _xpath_parse(self, path, namespaces=None): path = path.strip() if path.startswith('/') and not path.startswith('//'): path = ''.join(['/', XSD_SCHEMA, path]) namespaces = self._get_xpath_namespaces(namespaces) if self._xpath_parser is None: self._xpath_parser = XPath2Parser(namespaces, strict=False, schema=self.xpath_proxy) else: self._xpath_parser.namespaces = namespaces return self._xpath_parser.parse(path)
def test_schema_constructors(self): schema_src = dedent(""" <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:simpleType name="stringType"> <xs:restriction base="xs:string"/> </xs:simpleType> <xs:simpleType name="intType"> <xs:restriction base="xs:int"/> </xs:simpleType> </xs:schema>""") schema = xmlschema.XMLSchema(schema_src) schema_proxy = XMLSchemaProxy(schema=schema) parser = XPath2Parser(namespaces=self.namespaces, schema=schema_proxy) with self.assertRaises(NameError) as ctx: parser.schema_constructor(XSD_ANY_ATOMIC_TYPE) self.assertIn('XPST0080', str(ctx.exception)) with self.assertRaises(NameError) as ctx: parser.schema_constructor(XSD_NOTATION) self.assertIn('XPST0080', str(ctx.exception)) token = parser.parse('stringType("apple")') self.assertEqual(token.symbol, 'stringType') self.assertEqual(token.label, 'constructor function') self.assertEqual(token.evaluate(), 'apple') token = parser.parse('stringType(())') self.assertEqual(token.symbol, 'stringType') self.assertEqual(token.label, 'constructor function') self.assertEqual(token.evaluate(), []) token = parser.parse('stringType(10)') self.assertEqual(token.symbol, 'stringType') self.assertEqual(token.label, 'constructor function') self.assertEqual(token.evaluate(), '10') token = parser.parse('stringType(.)') self.assertEqual(token.symbol, 'stringType') self.assertEqual(token.label, 'constructor function') token = parser.parse('intType(10)') self.assertEqual(token.symbol, 'intType') self.assertEqual(token.label, 'constructor function') self.assertEqual(token.evaluate(), 10) with self.assertRaises(ValueError) as ctx: parser.parse('intType(true())') self.assertIn('FORG0001', str(ctx.exception))
def _xpath_parse(self, path, namespaces=None): path = path.strip() if path.startswith('/') and not path.startswith('//'): path = ''.join(['/', XSD_SCHEMA, path]) path = _REGEX_TAG_POSITION.sub('', path) # Strips tags's positions from path namespaces = self._get_xpath_namespaces(namespaces) with self._xpath_lock: parser = self._xpath_parser if parser is None: parser = XPath2Parser(namespaces, strict=False, schema=self.xpath_proxy) self._xpath_parser = parser else: parser.namespaces = namespaces return parser.parse(path)
def iterfind(self, path, namespaces=None): """ Creates and iterator for all XSD subelements matching the path. :param path: an XPath expression that considers the XSD component as the root element. :param namespaces: is an optional mapping from namespace prefix to full name. :return: an iterable yielding all matching XSD subelements in document order. """ if path.startswith('/'): path = u'.%s' % path # Avoid document root positioning namespaces = self.xpath_namespaces if namespaces is None else namespaces parser = XPath2Parser(namespaces, strict=False) root_token = parser.parse(path) context = ElementPathContext(self) return root_token.select(context)
def find(self, path, namespaces=None): """ Finds the first XSD subelement matching the path. :param path: an XPath expression that considers the XSD component as the root element. :param namespaces: an optional mapping from namespace prefix to full name. :return: The first matching XSD subelement or ``None`` if there is not match. """ if path.startswith('/'): path = u'.%s' % path namespaces = self.xpath_namespaces if namespaces is None else namespaces parser = XPath2Parser(namespaces, strict=False) root_token = parser.parse(path) context = ElementPathContext(self) return next(root_token.select(context), None)
def find(self, path, namespaces=None): """ Finds the first XSD subelement matching the path. :param path: an XPath expression that considers the XSD component as the root element. :param namespaces: an optional mapping from namespace prefix to namespace URI. :return: the first matching XSD subelement or ``None`` if there is no match. """ path = _REGEX_TAG_POSITION.sub( '', path.strip()) # Strips tags positions from path namespaces = self._get_xpath_namespaces(namespaces) parser = XPath2Parser(namespaces, strict=False) context = XMLSchemaContext(self) return next(parser.parse(path).select_results(context), None)
def iterfind(self, path, namespaces=None): """ Creates and iterator for all XSD subelements matching the path. :param path: an XPath expression that considers the XSD component as the root element. :param namespaces: is an optional mapping from namespace prefix to full name. :return: an iterable yielding all matching XSD subelements in document order. """ path = _REGEX_TAG_POSITION.sub( '', path.strip()) # Strips tags positions from path namespaces = self._get_xpath_namespaces(namespaces) parser = XPath2Parser(namespaces, strict=False) context = XMLSchemaContext(self) return parser.parse(path).select_results(context)
def _xpath_parse(self, path, namespaces=None): path = _REGEX_TAG_POSITION.sub( '', path.strip()) # Strips tags positions from path namespaces = self._get_xpath_namespaces(namespaces) with self._xpath_lock: parser = self._xpath_parser if parser is None: parser = XPath2Parser(namespaces, strict=False, schema=self.xpath_proxy) self._xpath_parser = parser else: parser.namespaces = namespaces return parser.parse(path)