Add coverage to testing

- Added .coveragerc
  - Added tests/test_helpers.py
  - Added coverage to develop requirements
  - Code cleaning for xpath_helpers.py
This commit is contained in:
Davide Brunato 2019-05-16 12:28:15 +02:00
parent 12f94db836
commit 5d715f98fc
14 changed files with 417 additions and 80 deletions

3
.coveragerc Normal file
View File

@ -0,0 +1,3 @@
[run]
branch = True
source = elementpath/

1
.gitignore vendored
View File

@ -7,6 +7,7 @@
.project
.ipynb_checkpoints/
.tox/
.coverage
doc/_*
__pycache__/
dist/

View File

@ -33,22 +33,25 @@ XPATH_2_DEFAULT_NAMESPACES = {
}
# XML namespace attributes
XML_BASE_QNAME = '{%s}base' % XML_NAMESPACE
XML_LANG_QNAME = '{%s}lang' % XML_NAMESPACE
XML_SPACE_QNAME = '{%s}space' % XML_NAMESPACE
XML_ID_QNAME = '{%s}id' % XML_NAMESPACE
XML_BASE = '{%s}base' % XML_NAMESPACE
XML_LANG = '{%s}lang' % XML_NAMESPACE
XML_SPACE = '{%s}space' % XML_NAMESPACE
XML_ID = '{%s}id' % XML_NAMESPACE
# XML Schema Instance namespace attributes
XSI_TYPE_QNAME = '{%s}type' % XSI_NAMESPACE
XSI_NIL_QNAME = '{%s}nil' % XSI_NAMESPACE
XSI_SCHEMA_LOCATION_QNAME = '{%s}schemaLocation' % XSI_NAMESPACE
XSI_NONS_SCHEMA_LOCATION_QNAME = '{%s}schemaLocation' % XSI_NAMESPACE
XSI_TYPE = '{%s}type' % XSI_NAMESPACE
XSI_NIL = '{%s}nil' % XSI_NAMESPACE
XSI_SCHEMA_LOCATION = '{%s}schemaLocation' % XSI_NAMESPACE
XSI_NONS_SCHEMA_LOCATION = '{%s}schemaLocation' % XSI_NAMESPACE
# XSD tags and attributes
# XML Schema types
XSD_NOTATION = '{%s}NOTATION' % XSD_NAMESPACE
XSD_ANY_ATOMIC_TYPE = '{%s}anyAtomicType' % XSD_NAMESPACE
XSD_UNTYPED = '{%s}untyped' % XSD_NAMESPACE
XSD_UNTYPED_ATOMIC = '{%s}untypedAtomic' % XSD_NAMESPACE
XSD_ID = '{%s}ID' % XSD_NAMESPACE
XSD_IDREF = '{%s}IDREF' % XSD_NAMESPACE
XSD_IDREFS = '{%s}IDREFS' % XSD_NAMESPACE
def get_namespace(name):

View File

@ -18,7 +18,7 @@ from .exceptions import ElementPathSyntaxError, ElementPathTypeError, ElementPat
from .datatypes import UntypedAtomic, DayTimeDuration, YearMonthDuration, XSD_BUILTIN_TYPES
from .xpath_context import XPathSchemaContext
from .tdop_parser import Parser, MultiLabel
from .namespaces import XML_ID_QNAME, XML_LANG_QNAME, XPATH_1_DEFAULT_NAMESPACES, \
from .namespaces import XML_ID, XML_LANG, XPATH_1_DEFAULT_NAMESPACES, \
XPATH_FUNCTIONS_NAMESPACE, XSD_NAMESPACE, qname_to_prefixed
from .xpath_token import XPathToken
from .xpath_helpers import AttributeNode, NamespaceNode, is_etree_element, is_xpath_node, is_element_node, \
@ -972,7 +972,7 @@ def select(self, context=None):
item = context.item
if is_element_node(item):
for elem in item.iter():
if elem.get(XML_ID_QNAME) == value:
if elem.get(XML_ID) == value:
yield elem
@ -1143,11 +1143,11 @@ def evaluate(self, context=None):
return False
else:
try:
lang = context.item.attrib[XML_LANG_QNAME].strip()
lang = context.item.attrib[XML_LANG].strip()
except KeyError:
for elem in context.iter_ancestor():
if XML_LANG_QNAME in elem.attrib:
lang = elem.attrib[XML_LANG_QNAME]
if XML_LANG in elem.attrib:
lang = elem.attrib[XML_LANG]
break
else:
return False

View File

@ -13,11 +13,10 @@ Helper functions for XPath nodes and functions.
"""
from collections import namedtuple
from .compat import PY3
from .namespaces import XML_BASE_QNAME, XML_ID_QNAME, XSI_TYPE_QNAME, XSI_NIL_QNAME, \
XSD_UNTYPED, XSD_UNTYPED_ATOMIC, prefixed_to_qname
from .exceptions import xpath_error
from .datatypes import UntypedAtomic
from .compat import PY3, urlparse
from .namespaces import XML_BASE, XSI_NIL, XSD_UNTYPED, XSD_UNTYPED_ATOMIC
from .exceptions import ElementPathValueError, xpath_error
from .datatypes import UntypedAtomic, ncname_validator
###
# Node types
@ -53,43 +52,61 @@ def elem_iter_strings(elem):
# for documents. Generic tuples are used for representing attributes and named-tuples for namespaces.
###
def is_element_node(obj, tag=None):
if tag is None:
return is_etree_element(obj) and not callable(obj.tag)
elif not is_etree_element(obj):
"""
Returns `True` if the first argument is an element node matching the tag, `False` otherwise.
Raises a ValueError if the argument tag has to be used but it's in a wrong format.
:param obj: the node to be tested.
:param tag: a fully qualified name, a local name or a wildcard. The accepted wildcard formats \
are '*', '*:*', '*:local-name' and '{namespace}*'.
"""
if not is_etree_element(obj) or callable(obj.tag):
return False
elif tag is None:
return True
elif not obj.tag:
return obj.tag == tag
elif tag == '*' or tag == '*:*':
return obj.tag != ''
elif tag[0] == '*':
if not obj.tag:
return False
elif obj.tag[0] == '{':
return obj.tag.split('}')[1] == tag.split(':')[1]
try:
_, name = tag.split(':')
except (ValueError, IndexError):
raise ElementPathValueError("unexpected format %r for argument 'tag'" % tag)
else:
return obj.tag == tag.split(':')[1]
return obj.tag.split('}')[1] == name if obj.tag[0] == '{' else obj.tag == name
elif tag[-1] == '*':
if not obj.tag:
return False
elif obj.tag[0] == '{':
return obj.tag.split('}')[0][1:] == tag.split('}')[0][1:]
else:
return True
if tag[0] != '{' or '}' not in tag:
raise ElementPathValueError("unexpected format %r for argument 'tag'" % tag)
return obj.tag.split('}')[0][1:] == tag.split('}')[0][1:] if obj.tag[0] == '{' else False
else:
return obj.tag == tag
def is_attribute_node(obj, name=None):
if name is None:
"""
Returns `True` if the first argument is an attribute node matching the name, `False` otherwise.
Raises a ValueError if the argument name has to be used but it's in a wrong format.
:param obj: the node to be tested.
:param name: a fully qualified name, a local name or a wildcard. The accepted wildcard formats \
are '*', '*:*', '*:local-name' and '{namespace}*'.
"""
if name is None or name == '*' or name == '*:*':
return isinstance(obj, AttributeNode)
elif not isinstance(obj, AttributeNode):
return False
elif name[0] == '*':
if obj[0][0] == '{':
return obj[0].split('}')[1] == name.split(':')[1]
try:
_, _name = name.split(':')
except (ValueError, IndexError):
raise ElementPathValueError("unexpected format %r for argument 'name'" % name)
else:
return obj[0] == name.split(':')[1]
return obj[0].split('}')[1] == _name if obj[0][0] == '{' else obj[0] == _name
elif name[-1] == '*':
if obj[0][0] == '{':
return obj[0].split('}')[0][1:] == name.split('}')[0][1:]
else:
return True
if name[0] != '{' or '}' not in name:
raise ElementPathValueError("unexpected format %r for argument 'name'" % name)
return obj[0].split('}')[0][1:] == name.split('}')[0][1:] if obj[0][0] == '{' else False
else:
return obj[0] == name
@ -123,7 +140,8 @@ def is_xpath_node(obj):
###
# Node accessors
# Node accessors: in this implementation node accessors return None instead of empty sequence.
# Ref: https://www.w3.org/TR/xpath-datamodel-31/#dm-document-uri
def node_attributes(obj):
if is_element_node(obj):
return obj.attrib
@ -132,49 +150,49 @@ def node_attributes(obj):
def node_base_uri(obj):
try:
if is_element_node(obj):
return obj.attrib[XML_BASE_QNAME]
return obj.attrib[XML_BASE]
elif is_document_node(obj):
return obj.getroot().attrib[XML_BASE_QNAME]
return obj.getroot().attrib[XML_BASE]
except KeyError:
pass
def node_document_uri(obj):
# Try the xml:base of root node because an ElementTree doesn't save reference to source.
for uri in node_base_uri(obj):
return uri
if is_document_node(obj):
try:
uri = obj.getroot().attrib[XML_BASE]
parts = urlparse(uri)
except (KeyError, ValueError):
pass
else:
if parts.scheme and parts.netloc or parts.path.startswith('/'):
return uri
def node_children(obj):
if is_element_node(obj):
for child in obj:
return child
return (child for child in obj)
elif is_document_node(obj):
return obj.getroot()
return (child for child in [obj.getroot()])
def node_is_id(obj):
if is_element_node(obj):
return XML_ID_QNAME in obj.attrib
return ncname_validator(obj.text)
elif is_attribute_node(obj):
return obj[0] == XML_ID_QNAME
return ncname_validator(obj[1])
def node_is_idrefs(obj, namespaces):
def node_is_idrefs(obj):
if is_element_node(obj):
try:
node_type = obj.attrib[XSI_TYPE_QNAME]
except KeyError:
pass
else:
return prefixed_to_qname(node_type, namespaces) in ("IDREF", "IDREFS")
elif is_attribute_node(obj) and obj[0] == XSI_TYPE_QNAME:
return prefixed_to_qname(obj[1], namespaces) in ("IDREF", "IDREFS")
return obj.text is not None and all(ncname_validator(x) for x in obj.text.split())
elif is_attribute_node(obj):
return all(ncname_validator(x) for x in obj[1].split())
def node_nilled(obj):
if is_element_node(obj):
return obj.get(XSI_NIL_QNAME) == 'true'
return obj.get(XSI_NIL) in ('true', '1')
def node_kind(obj):
@ -197,9 +215,7 @@ def node_kind(obj):
def node_name(obj):
if is_element_node(obj):
return obj.tag
elif is_attribute_node(obj):
return obj[0]
elif is_namespace_node(obj):
elif is_attribute_node(obj) or is_namespace_node(obj):
return obj[0]
@ -222,11 +238,19 @@ def node_string_value(obj):
def node_type_name(obj, schema=None):
if is_element_node(obj):
if schema is None:
return XSD_UNTYPED
if schema is not None:
xsd_element = schema.get_element(obj.tag)
if xsd_element is not None:
return xsd_element.type.name
return XSD_UNTYPED
elif is_attribute_node(obj):
if schema is None:
return XSD_UNTYPED_ATOMIC # TODO: from a PSVI ...
if schema is not None:
xsd_attribute = schema.get_attribute(obj[0])
if xsd_attribute is not None:
return xsd_attribute.type.name
return XSD_UNTYPED_ATOMIC
elif is_text_node(obj):
return XSD_UNTYPED_ATOMIC
@ -280,9 +304,7 @@ def data_value(obj):
elif not is_xpath_node(obj):
return obj
else:
value = node_string_value(obj)
if value is not None:
return UntypedAtomic(value)
return UntypedAtomic(node_string_value(obj))
def number_value(obj):

View File

@ -71,9 +71,7 @@ class Selector(object):
self.root_token = self.parser.parse(path)
def __repr__(self):
return u'%s(path=%r, namespaces=%r, parser=%s)' % (
self.__class__.__name__, self.path, self.namespaces, self.parser.__class__.__name__
)
return u'%s(path=%r, parser=%s)' % (self.__class__.__name__, self.path, self.parser.__class__.__name__)
@property
def namespaces(self):
@ -101,3 +99,5 @@ class Selector(object):
"""
context = XPathContext(root)
return self.root_token.select(context)
# 45-48, 74, 81

View File

@ -1,6 +1,7 @@
# Requirements for setup a development environment
setuptools
tox
coverage
lxml
xmlschema>=1.0.9
Sphinx

View File

@ -612,6 +612,15 @@ class TimezoneTypeTest(unittest.TestCase):
self.assertEqual(str(Timezone.fromstring('+05:00')), '+05:00')
self.assertEqual(str(Timezone.fromstring('-13:15')), '-13:15')
def test_eq_operator(self):
self.assertEqual(Timezone.fromstring('+05:00'), Timezone.fromstring('+05:00'))
def test_ne_operator(self):
self.assertNotEqual(Timezone.fromstring('+05:00'), Timezone.fromstring('+06:00'))
def test_hashing(self):
self.assertEqual(hash(Timezone.fromstring('+05:00')), 1289844826723787395)
if __name__ == '__main__':
unittest.main()

View File

@ -24,6 +24,7 @@ import unittest
if __name__ == '__main__':
try:
from tests.test_helpers import HelpersTest
from tests.test_datatypes import UntypedAtomicTest, DateTimeTypesTest, DurationTypesTest, TimezoneTypeTest
from tests.test_xpath1_parser import XPath1ParserTest, LxmlXPath1ParserTest
from tests.test_xpath2_parser import XPath2ParserTest, LxmlXPath2ParserTest
@ -32,6 +33,7 @@ if __name__ == '__main__':
from tests.test_package import PackageTest
except ImportError:
# Python 2 fallback
from test_helpers import HelpersTest
from test_datatypes import UntypedAtomicTest, DateTimeTypesTest, DurationTypesTest, TimezoneTypeTest
from test_xpath1_parser import XPath1ParserTest, LxmlXPath1ParserTest
from test_xpath2_parser import XPath2ParserTest, LxmlXPath2ParserTest

260
tests/test_helpers.py Normal file
View File

@ -0,0 +1,260 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (c), 2018-2019, SISSA (International School for Advanced Studies).
# All rights reserved.
# This file is distributed under the terms of the MIT License.
# See the file 'LICENSE' in the root directory of the present
# distribution, or http://opensource.org/licenses/MIT.
#
# @author Davide Brunato <brunato@sissa.it>
#
from __future__ import unicode_literals
import unittest
import io
import math
import xml.etree.ElementTree as ElementTree
from xmlschema import XMLSchema
from elementpath.schema_proxy import XMLSchemaProxy
from elementpath.namespaces import XSD_NAMESPACE, get_namespace, qname_to_prefixed, prefixed_to_qname
from elementpath.xpath_helpers import AttributeNode, NamespaceNode, is_etree_element, is_element_node, \
is_attribute_node, is_comment_node, is_document_node, is_namespace_node, is_processing_instruction_node, \
is_text_node, node_attributes, node_base_uri, node_document_uri, node_children, node_is_id, node_is_idrefs, \
node_nilled, node_kind, node_name, node_string_value, node_type_name, boolean_value, data_value, number_value
class HelpersTest(unittest.TestCase):
elem = ElementTree.XML('<node a1="10"/>')
namespaces = {
'xs': XSD_NAMESPACE,
'tst': "http://xpath.test/ns"
}
# namespaces.py module
def test_get_namespace_function(self):
self.assertEqual(get_namespace('A'), '')
self.assertEqual(get_namespace('{ns}foo'), 'ns')
self.assertEqual(get_namespace('{}foo'), '')
self.assertEqual(get_namespace('{A}B{C}'), 'A')
def test_qname_to_prefixed_function(self):
self.assertEqual(qname_to_prefixed('{ns}foo', {'bar': 'ns'}), 'bar:foo')
self.assertEqual(qname_to_prefixed('{ns}foo', {'': 'ns'}), 'foo')
self.assertEqual(qname_to_prefixed('foo', {'': 'ns'}), 'foo')
def test_prefixed_to_qname_function(self):
self.assertEqual(prefixed_to_qname('{ns}foo', {'bar': 'ns'}), '{ns}foo')
self.assertEqual(prefixed_to_qname('bar:foo', {'bar': 'ns'}), '{ns}foo')
self.assertEqual(prefixed_to_qname('foo', {'': 'ns'}), '{ns}foo')
with self.assertRaises(ValueError):
prefixed_to_qname('bar:foo', self.namespaces)
with self.assertRaises(ValueError):
prefixed_to_qname('bar:foo:bar', {'bar': 'ns'})
with self.assertRaises(ValueError):
prefixed_to_qname(':foo', {'': 'ns'})
with self.assertRaises(ValueError):
prefixed_to_qname('foo:', {'': 'ns'})
def test_is_etree_element_function(self):
self.assertTrue(is_etree_element(self.elem))
self.assertFalse(is_etree_element('text'))
self.assertFalse(is_etree_element(None))
def test_is_element_node_function(self):
elem = ElementTree.Element('alpha')
empty_tag_elem = ElementTree.Element('')
self.assertTrue(is_element_node(elem, '*'))
self.assertFalse(is_element_node(empty_tag_elem, '*'))
with self.assertRaises(ValueError):
is_element_node(elem, '**')
with self.assertRaises(ValueError):
is_element_node(elem, '*:*:*')
with self.assertRaises(ValueError):
is_element_node(elem, 'foo:*')
self.assertFalse(is_element_node(empty_tag_elem, 'foo:*'))
self.assertFalse(is_element_node(elem, '{foo}*'))
def test_is_attribute_node_function(self):
attr = AttributeNode('a1', '10')
self.assertTrue(is_attribute_node(attr, '*'))
with self.assertRaises(ValueError):
is_attribute_node(attr, '**')
with self.assertRaises(ValueError):
is_attribute_node(attr, '*:*:*')
with self.assertRaises(ValueError):
is_attribute_node(attr, 'foo:*')
self.assertTrue(is_attribute_node(attr, '*:a1'))
self.assertFalse(is_attribute_node(attr, '{foo}*'))
self.assertTrue(is_attribute_node(AttributeNode('{foo}a1', '10'), '{foo}*'))
def test_is_comment_node_function(self):
comment = ElementTree.Comment('nothing important')
self.assertTrue(is_comment_node(comment))
self.assertFalse(is_comment_node(self.elem))
def test_is_document_node_function(self):
document = ElementTree.parse(io.StringIO('<A/>'))
self.assertTrue(is_document_node(document))
self.assertFalse(is_document_node(self.elem))
def test_is_namespace_node_function(self):
namespace = NamespaceNode('xs', 'http://www.w3.org/2001/XMLSchema')
self.assertTrue(is_namespace_node(namespace))
self.assertFalse(is_namespace_node(self.elem))
def test_is_processing_instruction_node_function(self):
pi = ElementTree.ProcessingInstruction('action', 'nothing to do')
self.assertTrue(is_processing_instruction_node(pi))
self.assertFalse(is_processing_instruction_node(self.elem))
def test_is_text_node_function(self):
self.assertTrue(is_text_node('alpha'))
self.assertFalse(is_text_node(self.elem))
def test_node_attributes_function(self):
self.assertEqual(node_attributes(self.elem), self.elem.attrib)
self.assertIsNone(node_attributes('a text node'))
def test_node_base_uri_function(self):
xml_test = '<A xmlns:xml="http://www.w3.org/XML/1998/namespace" xml:base="/" />'
self.assertEqual(node_base_uri(ElementTree.XML(xml_test)), '/')
document = ElementTree.parse(io.StringIO(xml_test))
self.assertEqual(node_base_uri(document), '/')
self.assertIsNone(node_base_uri(self.elem))
self.assertIsNone(node_base_uri('a text node'))
def test_node_document_uri_function(self):
self.assertIsNone(node_document_uri(self.elem))
xml_test = '<A xmlns:xml="http://www.w3.org/XML/1998/namespace" xml:base="/root" />'
document = ElementTree.parse(io.StringIO(xml_test))
self.assertEqual(node_document_uri(document), '/root')
xml_test = '<A xmlns:xml="http://www.w3.org/XML/1998/namespace" xml:base="http://xpath.test" />'
document = ElementTree.parse(io.StringIO(xml_test))
self.assertEqual(node_document_uri(document), 'http://xpath.test')
xml_test = '<A xmlns:xml="http://www.w3.org/XML/1998/namespace" xml:base="dir1/dir2" />'
document = ElementTree.parse(io.StringIO(xml_test))
self.assertIsNone(node_document_uri(document))
xml_test = '<A xmlns:xml="http://www.w3.org/XML/1998/namespace" xml:base="http://[xpath.test" />'
document = ElementTree.parse(io.StringIO(xml_test))
self.assertIsNone(node_document_uri(document))
def test_node_children_function(self):
self.assertListEqual(list(node_children(self.elem)), [])
elem = ElementTree.XML("<A><B1/><B2/></A>")
self.assertListEqual(list(node_children(elem)), elem[:])
document = ElementTree.parse(io.StringIO("<A><B1/><B2/></A>"))
self.assertListEqual(list(node_children(document)), [document.getroot()])
self.assertIsNone(node_children('a text node'))
def test_node_is_id_function(self):
self.assertTrue(node_is_id(ElementTree.XML('<A>xyz</A>')))
self.assertFalse(node_is_id(ElementTree.XML('<A>xyz abc</A>')))
self.assertFalse(node_is_id(ElementTree.XML('<A>12345</A>')))
self.assertTrue(node_is_id(AttributeNode('id', 'alpha')))
self.assertFalse(node_is_id(AttributeNode('id', 'alpha beta')))
self.assertFalse(node_is_id(AttributeNode('id', '12345')))
self.assertIsNone(node_is_id('a text node'))
def test_node_is_idref_function(self):
self.assertTrue(node_is_idrefs(ElementTree.XML('<A>xyz</A>')))
self.assertTrue(node_is_idrefs(ElementTree.XML('<A>xyz abc</A>')))
self.assertFalse(node_is_idrefs(ElementTree.XML('<A>12345</A>')))
self.assertTrue(node_is_idrefs(AttributeNode('id', 'alpha')))
self.assertTrue(node_is_idrefs(AttributeNode('id', 'alpha beta')))
self.assertFalse(node_is_idrefs(AttributeNode('id', '12345')))
self.assertIsNone(node_is_idrefs('a text node'))
def test_node_nilled_function(self):
xml_test = '<A xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true" />'
self.assertTrue(node_nilled(ElementTree.XML(xml_test)))
xml_test = '<A xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="false" />'
self.assertFalse(node_nilled(ElementTree.XML(xml_test)))
self.assertFalse(node_nilled(ElementTree.XML('<A />')))
def test_node_kind_function(self):
document = ElementTree.parse(io.StringIO(u'<A/>'))
element = ElementTree.Element('schema')
attribute = AttributeNode('id', '0212349350')
namespace = NamespaceNode('xs', 'http://www.w3.org/2001/XMLSchema')
comment = ElementTree.Comment('nothing important')
pi = ElementTree.ProcessingInstruction('action', 'nothing to do')
text = u'betelgeuse'
self.assertEqual(node_kind(document), 'document')
self.assertEqual(node_kind(element), 'element')
self.assertEqual(node_kind(attribute), 'attribute')
self.assertEqual(node_kind(namespace), 'namespace')
self.assertEqual(node_kind(comment), 'comment')
self.assertEqual(node_kind(pi), 'processing-instruction')
self.assertEqual(node_kind(text), 'text')
self.assertIsNone(node_kind(None))
self.assertIsNone(node_kind(10))
def test_node_name_function(self):
elem = ElementTree.Element('root')
attr = AttributeNode('a1', '20')
namespace = NamespaceNode('xs', 'http://www.w3.org/2001/XMLSchema')
self.assertEqual(node_name(elem), 'root')
self.assertEqual(node_name(attr), 'a1')
self.assertEqual(node_name(namespace), 'xs')
def test_node_string_value_function(self):
document = ElementTree.parse(io.StringIO(u'<A>123<B1>456</B1><B2>789</B2></A>'))
element = ElementTree.Element('schema')
attribute = AttributeNode('id', '0212349350')
namespace = NamespaceNode('xs', 'http://www.w3.org/2001/XMLSchema')
comment = ElementTree.Comment('nothing important')
pi = ElementTree.ProcessingInstruction('action', 'nothing to do')
text = u'betelgeuse'
self.assertEqual(node_string_value(document), '123456789')
self.assertEqual(node_string_value(element), '')
self.assertEqual(node_string_value(attribute), '0212349350')
self.assertEqual(node_string_value(namespace), 'http://www.w3.org/2001/XMLSchema')
self.assertEqual(node_string_value(comment), 'nothing important')
self.assertEqual(node_string_value(pi), 'action nothing to do')
self.assertEqual(node_string_value(text), 'betelgeuse')
self.assertIsNone(node_string_value(None))
self.assertIsNone(node_string_value(10))
def test_node_type_name_function(self):
schema = XMLSchemaProxy(
XMLSchema("""<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:attribute name="slot" type="xs:token" />
<xs:element name="frame" type="xs:decimal" />
</xs:schema>""")
)
elem = ElementTree.Element('frame')
self.assertEqual(node_type_name(elem, schema), '{http://www.w3.org/2001/XMLSchema}decimal')
self.assertEqual(node_type_name(elem), '{http://www.w3.org/2001/XMLSchema}untyped')
elem = ElementTree.Element('alpha')
self.assertEqual(node_type_name(elem, schema), '{http://www.w3.org/2001/XMLSchema}untyped')
attr = AttributeNode('slot', 'x1')
self.assertEqual(node_type_name(attr, schema), '{http://www.w3.org/2001/XMLSchema}token')
self.assertEqual(node_type_name(attr), '{http://www.w3.org/2001/XMLSchema}untypedAtomic')
attr = AttributeNode('alpha', 'x1')
self.assertEqual(node_type_name(attr, schema), '{http://www.w3.org/2001/XMLSchema}untypedAtomic')
self.assertEqual(node_type_name('slot'), '{http://www.w3.org/2001/XMLSchema}untypedAtomic')
self.assertIsNone(node_type_name(10))
def test_boolean_value_function(self):
elem = ElementTree.Element('A')
with self.assertRaises(TypeError):
boolean_value(elem)
def test_data_value_function(self):
self.assertIsNone(data_value(None))
def test_number_value_function(self):
self.assertEqual(number_value("19"), 19)
self.assertTrue(math.isnan(number_value("not a number")))
if __name__ == '__main__':
unittest.main()

View File

@ -13,7 +13,7 @@ import unittest
import lxml.etree
from elementpath import *
from elementpath.namespaces import XML_LANG_QNAME, XSD_NAMESPACE
from elementpath.namespaces import XML_LANG, XSD_NAMESPACE
try:
# noinspection PyPackageRequirements
@ -52,7 +52,7 @@ class XPath2ParserXMLSchemaTest(test_xpath2_parser.XPath2ParserTest):
self.check_value("schema-element(xs:schema)", context.item, context)
self.check_tree("schema-element(xs:group)", '(schema-element (: (xs) (group)))')
context.item = AttributeNode(XML_LANG_QNAME, 'en')
context.item = AttributeNode(XML_LANG, 'en')
self.wrong_name("schema-attribute(nil)")
self.wrong_name("schema-attribute(xs:string)")
self.check_value("schema-attribute(xml:lang)", None)

View File

@ -16,6 +16,22 @@ from elementpath import *
class SelectorTest(unittest.TestCase):
root = ElementTree.XML('<author>Dickens</author>')
def test_select_function(self):
self.assertListEqual(select(self.root, 'text()'), ['Dickens'])
def test_iter_select_function(self):
self.assertListEqual(list(iter_select(self.root, 'text()')), ['Dickens'])
def test_selector_class(self):
selector = Selector('/A')
self.assertEqual(repr(selector), "Selector(path='/A', parser=XPath2Parser)")
self.assertEqual(selector.namespaces, XPath2Parser.DEFAULT_NAMESPACES)
selector = Selector('text()')
self.assertListEqual(selector.select(self.root), ['Dickens'])
self.assertListEqual(list(selector.iter_select(self.root)), ['Dickens'])
def test_issue_001(self):
selector = Selector("//FullPath[ends-with(., 'Temp')]")

View File

@ -1032,7 +1032,7 @@ class XPath1ParserTest(unittest.TestCase):
def test_default_namespace(self):
root = self.etree.XML('<foo>bar</foo>')
self.check_selector('/foo', root, [root])
if type(self.parser) is XPath1Parser:
if self.parser.version == '1.0':
# XPath 1.0 ignores the default namespace
self.check_selector('/foo', root, [root], namespaces={'': 'ns'}) # foo --> foo
else:
@ -1046,6 +1046,15 @@ class XPath1ParserTest(unittest.TestCase):
else:
self.check_selector('/foo', root, [root], namespaces={'': 'ns'})
root = self.etree.XML('<A xmlns="http://xpath.test/ns"><B1/></A>')
if self.parser.version > '1.0' or not hasattr(root, 'nsmap'):
self.check_selector("name(tst:B1)", root, 'tst:B1', namespaces={'tst': "http://xpath.test/ns"})
if self.parser.version > '1.0':
self.check_selector("name(B1)", root, 'B1', namespaces={'': "http://xpath.test/ns"})
else:
# XPath 1.0 ignores the default namespace declarations
self.check_selector("name(B1)", root, '', namespaces={'': "http://xpath.test/ns"})
class LxmlXPath1ParserTest(XPath1ParserTest):
etree = lxml.etree

15
tox.ini
View File

@ -5,16 +5,27 @@
[tox]
envlist = py27, py34, py35, py36, py37
skip_missing_interpreters = true
toxworkdir = {homedir}/.tox/elementpath
[testenv]
deps =
lxml
xmlschema>=1.0.9
xmlschema~=1.0.9
commands = python -m unittest
[testenv:py27]
deps =
lxml
xmlschema>=1.0.9
xmlschema~=1.0.9
commands = python tests/test_elementpath.py
[testenv:py37]
commands =
coverage run -p setup.py test -q
coverage combine
coverage report -m
deps =
lxml
xmlschema~=1.0.9
coverage