Refactor xmlschema related tests

- XMLSchemaProxy will be moved to xmlschema package because
    it requires a specialized context class. Schema tests will
    be kept in elementpath to help integration and to test
    elementpath schema-related features.
This commit is contained in:
Davide Brunato 2019-08-04 16:02:27 +02:00
parent 3f50cfd962
commit b5a0a87a3e
5 changed files with 42 additions and 13 deletions

View File

@ -226,7 +226,9 @@ class AbstractSchemaProxy(object):
class XMLSchemaProxy(AbstractSchemaProxy):
"""
Schema proxy for the *xmlschema* library.
Schema proxy for the *xmlschema* library. It will be removed soon because
xmlschema v1.0.14 will includes an its own version of schema proxy that
uses a custom context implementation that recognizes circular references.
"""
def __init__(self, schema=None, base_element=None):
if schema is None:

View File

@ -82,6 +82,8 @@ class XPath1Parser(Parser):
in the instance with the ones passed with the *namespaces* argument.
"""
schema = None # To simplify the schema bind checks in compatibility with XPath2Parser
def __init__(self, namespaces=None, variables=None, strict=True, *args, **kwargs):
super(XPath1Parser, self).__init__()
self.namespaces = self.DEFAULT_NAMESPACES.copy()
@ -1026,12 +1028,18 @@ def evaluate(self, context=None):
@method(function('string-length', nargs=1))
def evaluate(self, context=None):
if self.parser.version == '1.0':
arg = self.get_argument(context, default_to_context=True, default='')
return len(self.string_value(arg))
return len(self.get_argument(context, default_to_context=True, default='', cls=string_base_type))
@method(function('normalize-space', nargs=1))
def evaluate(self, context=None):
arg = self.get_argument(context, default_to_context=True, default='', cls=string_base_type)
if self.parser.version == '1.0':
arg = self.string_value(self.get_argument(context, default_to_context=True, default=''))
else:
arg = self.get_argument(context, default_to_context=True, default='', cls=string_base_type)
return ' '.join(arg.strip().split())

View File

@ -233,4 +233,9 @@ class XPathContext(object):
class XPathSchemaContext(XPathContext):
"""Schema context class used during static analysis phase for matching tokens with schema types."""
"""
The XPath dynamic context base class for schema bounded parsers. Use this class
as dynamic context for schema instances in order to perform a schema-based type
checking during the static analysis phase. Don't use this as dynamic context on
XML instances.
"""

View File

@ -3,6 +3,6 @@ setuptools
tox
coverage
lxml
xmlschema>=1.0.13
xmlschema~=1.0.13
Sphinx
-e .

View File

@ -18,7 +18,7 @@ try:
except ImportError:
lxml_etree = None
from elementpath import *
from elementpath import AttributeNode, XPathContext, XPath2Parser, ElementPathTypeError
from elementpath.compat import PY3
from elementpath.namespaces import XML_LANG, XSD_NAMESPACE
@ -27,6 +27,11 @@ try:
import xmlschema
except (ImportError, AttributeError):
xmlschema = None
else:
try:
from xmlschema.xpath import XMLSchemaProxy # it works if xmlschema~=1.0.14
except ImportError:
from elementpath.schema_proxy import XMLSchemaProxy
try:
from tests import test_xpath2_parser
@ -47,7 +52,8 @@ class XPath2ParserXMLSchemaTest(test_xpath2_parser.XPath2ParserTest):
def setUp(self):
self.schema_proxy = XMLSchemaProxy(self.schema)
self.parser = XPath2Parser(namespaces=self.namespaces, schema=self.schema_proxy, variables=self.variables)
self.parser = XPath2Parser(namespaces=self.namespaces, schema=self.schema_proxy,
variables=self.variables)
def test_schema_proxy_init(self):
schema_src = """<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
@ -60,16 +66,20 @@ class XPath2ParserXMLSchemaTest(test_xpath2_parser.XPath2ParserTest):
with self.assertRaises(TypeError):
XMLSchemaProxy(schema=schema_tree)
with self.assertRaises(TypeError):
XMLSchemaProxy(schema=xmlschema.XMLSchema(schema_src), base_element=schema_tree)
XMLSchemaProxy(schema=xmlschema.XMLSchema(schema_src),
base_element=schema_tree)
with self.assertRaises(TypeError):
XMLSchemaProxy(schema=xmlschema.XMLSchema(schema_src), base_element=schema_tree.getroot())
XMLSchemaProxy(schema=xmlschema.XMLSchema(schema_src),
base_element=schema_tree.getroot())
schema = xmlschema.XMLSchema(schema_src)
with self.assertRaises(ValueError):
XMLSchemaProxy(base_element=schema.elements['test_element'])
def test_xmlschema_proxy(self):
context = XPathContext(root=self.etree.XML('<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"/>'))
context = XPathContext(
root=self.etree.XML('<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"/>')
)
self.wrong_name("schema-element(nil)")
self.wrong_name("schema-element(xs:string)")
@ -82,7 +92,8 @@ class XPath2ParserXMLSchemaTest(test_xpath2_parser.XPath2ParserTest):
self.wrong_name("schema-attribute(xs:string)")
self.check_value("schema-attribute(xml:lang)", None)
self.check_value("schema-attribute(xml:lang)", context.item, context)
self.check_tree("schema-attribute(xsi:schemaLocation)", '(schema-attribute (: (xsi) (schemaLocation)))')
self.check_tree("schema-attribute(xsi:schemaLocation)",
'(schema-attribute (: (xsi) (schemaLocation)))')
def test_get_type_api(self):
schema_proxy = XMLSchemaProxy()
@ -135,7 +146,8 @@ class XPath2ParserXMLSchemaTest(test_xpath2_parser.XPath2ParserTest):
<xs:attribute name="max" type="xs:int"/>
</xs:complexType>
</xs:schema>''')
parser = XPath2Parser(namespaces=self.namespaces, schema=XMLSchemaProxy(schema, schema.elements['range']))
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" />'))))
@ -149,7 +161,8 @@ class XPath2ParserXMLSchemaTest(test_xpath2_parser.XPath2ParserTest):
<xs:attribute name="max" type="xs:string"/>
</xs:complexType>
</xs:schema>''')
parser = XPath2Parser(namespaces=self.namespaces, schema=XMLSchemaProxy(schema, schema.elements['range']))
parser = XPath2Parser(namespaces=self.namespaces,
schema=XMLSchemaProxy(schema, schema.elements['range']))
if PY3:
self.assertRaises(TypeError, parser.parse, '@min le @max')
else:
@ -225,7 +238,8 @@ class XPath2ParserXMLSchemaTest(test_xpath2_parser.XPath2ParserTest):
self.assertEqual(token[0][1].xsd_type, schema.types['rangeType'])
self.assertEqual(token[1][0].xsd_type, schema.maps.types['{%s}integer' % XSD_NAMESPACE])
context = XPathContext(root=self.etree.XML('<values xmlns="http://xpath.test/ns"><b min="19"/></values>'))
context = XPathContext(
root=self.etree.XML('<values xmlns="http://xpath.test/ns"><b min="19"/></values>'))
token = parser.parse("//b/@min lt //b/@max")
self.assertEqual(token[0][0][0].xsd_type, schema.types['rangeType'])
self.assertEqual(token[0][1][0].xsd_type, schema.maps.types['{%s}integer' % XSD_NAMESPACE])