Add tests for XPathToken helpers and datatypes
This commit is contained in:
parent
d89b2ad987
commit
910b63603a
|
@ -7,7 +7,8 @@
|
|||
.project
|
||||
.ipynb_checkpoints/
|
||||
.tox/
|
||||
.coverage
|
||||
.coverage*
|
||||
!.coveragerc
|
||||
doc/_*
|
||||
__pycache__/
|
||||
dist/
|
||||
|
|
|
@ -2,9 +2,10 @@
|
|||
CHANGELOG
|
||||
*********
|
||||
|
||||
`v1.2.1`_ (TBD)
|
||||
===============
|
||||
`v1.2.1`_ (2019-08-30)
|
||||
======================
|
||||
* Hashable XSD datatypes classes
|
||||
* Fix Duration types comparison
|
||||
|
||||
`v1.2.0`_ (2019-08-14)
|
||||
======================
|
||||
|
|
|
@ -40,8 +40,8 @@ class XPath1Parser(Parser):
|
|||
|
||||
:param namespaces: A dictionary with mapping from namespace prefixes into URIs.
|
||||
:param variables: A dictionary with the static context's in-scope variables.
|
||||
:param strict: If strict mode is `False` the parser enables parsing of QNames, \
|
||||
like the ElementPath library. Default is `True`.
|
||||
:param strict: If strict mode is `False` the parser enables parsing of QNames \
|
||||
in extended format, like the Python's ElementPath library. Default is `True`.
|
||||
"""
|
||||
token_base_class = XPathToken
|
||||
|
||||
|
@ -160,11 +160,14 @@ class XPath1Parser(Parser):
|
|||
while k < min_args:
|
||||
if self.parser.next_token.symbol == ')':
|
||||
msg = 'Too few arguments: expected at least %s arguments' % min_args
|
||||
self.wrong_nargs(msg[:-1] if min_args == 1 else msg)
|
||||
self.wrong_nargs(msg if min_args > 1 else msg[:-1])
|
||||
|
||||
self[k:] = self.parser.expression(5),
|
||||
k += 1
|
||||
if k < min_args:
|
||||
if self.parser.next_token.symbol == ')':
|
||||
msg = 'Too few arguments: expected at least %s arguments' % min_args
|
||||
self.wrong_nargs(msg if min_args > 1 else msg[:-1])
|
||||
self.parser.advance(',')
|
||||
|
||||
while k < max_args:
|
||||
|
@ -179,7 +182,7 @@ class XPath1Parser(Parser):
|
|||
|
||||
if self.parser.next_token.symbol == ',':
|
||||
msg = 'Too many arguments: expected at most %s arguments' % max_args
|
||||
self.wrong_nargs(msg[:-1] if max_args == 1 else msg)
|
||||
self.wrong_nargs(msg if max_args > 1 else msg[:-1])
|
||||
|
||||
self.parser.advance(')')
|
||||
return self
|
||||
|
|
|
@ -637,7 +637,7 @@ def evaluate(self, context=None):
|
|||
elif self.symbol != 'cast':
|
||||
return False
|
||||
else:
|
||||
self.wrong_value("atomic value is required")
|
||||
self.wrong_context_type("an atomic value is required")
|
||||
|
||||
try:
|
||||
if namespace != XSD_NAMESPACE:
|
||||
|
|
|
@ -38,7 +38,9 @@ class XPathContext(object):
|
|||
def __init__(self, root, item=None, position=0, size=1, axis=None, variables=None,
|
||||
current_dt=None, timezone=None):
|
||||
if not is_element_node(root) and not is_document_node(root):
|
||||
raise ElementPathTypeError("argument 'root' must be an Element: %r" % root)
|
||||
raise ElementPathTypeError(
|
||||
"invalid argument root={!r}, an Element is required.".format(root)
|
||||
)
|
||||
self._root = root
|
||||
if item is not None:
|
||||
self.item = item
|
||||
|
|
|
@ -35,6 +35,9 @@ from .tdop_parser import Token
|
|||
|
||||
|
||||
def ordinal(n):
|
||||
if n in {11, 12, 13}:
|
||||
return '%dth' % n
|
||||
|
||||
least_significant_digit = n % 10
|
||||
if least_significant_digit == 1:
|
||||
return '%dst' % n
|
||||
|
@ -81,7 +84,7 @@ class XPathToken(Token):
|
|||
if symbol == '$':
|
||||
return '$%s variable reference' % (self[0].value if self else '')
|
||||
elif symbol == ',':
|
||||
return 'comma operator'
|
||||
return 'comma operator' if self.parser.version > '1.0' else 'comma symbol'
|
||||
elif label == 'function':
|
||||
return '%r function' % symbol
|
||||
elif label == 'axis':
|
||||
|
@ -104,7 +107,7 @@ class XPathToken(Token):
|
|||
elif symbol == '$':
|
||||
return u'$%s' % self[0].source
|
||||
elif symbol == '{':
|
||||
return u'{%s}%s' % (self.value, self[0].source)
|
||||
return u'{%s}%s' % (self[0].value, self[1].value)
|
||||
elif symbol == 'instance':
|
||||
return u'%s instance of %s' % (self[0].source, ''.join(t.source for t in self[1:]))
|
||||
elif symbol == 'treat':
|
||||
|
|
|
@ -6,8 +6,8 @@ publiccodeYmlVersion: '0.2'
|
|||
name: elementpath
|
||||
url: 'https://github.com/sissaschool/elementpath'
|
||||
landingURL: 'https://github.com/sissaschool/elementpath'
|
||||
releaseDate: '2019-08-14'
|
||||
softwareVersion: v1.2.0
|
||||
releaseDate: '2019-08-30'
|
||||
softwareVersion: v1.2.1
|
||||
developmentStatus: stable
|
||||
platforms:
|
||||
- linux
|
||||
|
@ -24,7 +24,7 @@ maintenance:
|
|||
contacts:
|
||||
- name: Davide Brunato
|
||||
email: davide.brunato@sissa.it
|
||||
affiliation: ' Scuola Internazionale Superiore di Studi Avanzati'
|
||||
affiliation: 'Scuola Internazionale Superiore di Studi Avanzati'
|
||||
legal:
|
||||
license: MIT
|
||||
mainCopyrightOwner: Scuola Internazionale Superiore di Studi Avanzati
|
||||
|
|
|
@ -105,6 +105,10 @@ class UntypedAtomicTest(unittest.TestCase):
|
|||
self.assertEqual(UntypedAtomic(1) % 2, 1)
|
||||
self.assertEqual(UntypedAtomic('1') % 2, 1.0)
|
||||
|
||||
def test_hashing(self):
|
||||
self.assertEqual(hash(UntypedAtomic(12345)), 12345)
|
||||
self.assertIsInstance(hash(UntypedAtomic('alpha')), int)
|
||||
|
||||
|
||||
class DateTimeTypesTest(unittest.TestCase):
|
||||
|
||||
|
@ -427,6 +431,10 @@ class DateTimeTypesTest(unittest.TestCase):
|
|||
|
||||
self.assertEqual(date10("-2001-04-02-02:00") - date10("-2001-04-01"), DayTimeDuration.fromstring('P1DT2H'))
|
||||
|
||||
def test_hashing(self):
|
||||
dt = DateTime.fromstring("2002-04-02T12:00:00-01:00")
|
||||
self.assertIsInstance(hash(dt), int)
|
||||
|
||||
|
||||
class DurationTypesTest(unittest.TestCase):
|
||||
|
||||
|
@ -582,6 +590,12 @@ class DurationTypesTest(unittest.TestCase):
|
|||
def test_year_month_duration(self):
|
||||
self.assertEqual(YearMonthDuration(10).months, 10)
|
||||
|
||||
def test_hashing(self):
|
||||
if sys.version_info < (3, 8):
|
||||
self.assertEqual(hash(Duration(16)), 3713063228956366931)
|
||||
else:
|
||||
self.assertEqual(hash(Duration(16)), 6141449309508620102)
|
||||
|
||||
|
||||
class TimezoneTypeTest(unittest.TestCase):
|
||||
|
||||
|
|
|
@ -22,12 +22,16 @@ from elementpath.xpath_nodes import AttributeNode, NamespaceNode, is_etree_eleme
|
|||
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
|
||||
from elementpath.xpath_token import ordinal
|
||||
from elementpath.xpath_helpers import boolean_value
|
||||
from elementpath.xpath1_parser import XPath1Parser
|
||||
|
||||
|
||||
class ExceptionHelpersTest(unittest.TestCase):
|
||||
parser = XPath1Parser(namespaces={'xs': XSD_NAMESPACE, 'tst': "http://xpath.test/ns"})
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.parser = XPath1Parser(namespaces={'xs': XSD_NAMESPACE, 'tst': "http://xpath.test/ns"})
|
||||
|
||||
def test_exception_repr(self):
|
||||
err = ElementPathError("unknown error")
|
||||
|
@ -228,7 +232,20 @@ class NodeHelpersTest(unittest.TestCase):
|
|||
self.assertEqual(node_name(namespace), 'xs')
|
||||
|
||||
|
||||
class CompatibilityHelpersTest(unittest.TestCase):
|
||||
class XPathTokenHelpersTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.parser = XPath1Parser(namespaces={'xs': XSD_NAMESPACE, 'tst': "http://xpath.test/ns"})
|
||||
|
||||
def test_ordinal_function(self):
|
||||
self.assertEqual(ordinal(1), '1st')
|
||||
self.assertEqual(ordinal(2), '2nd')
|
||||
self.assertEqual(ordinal(3), '3rd')
|
||||
self.assertEqual(ordinal(4), '4th')
|
||||
self.assertEqual(ordinal(11), '11th')
|
||||
self.assertEqual(ordinal(23), '23rd')
|
||||
self.assertEqual(ordinal(34), '34th')
|
||||
|
||||
def test_boolean_value_function(self):
|
||||
elem = ElementTree.Element('A')
|
||||
|
@ -244,6 +261,13 @@ class CompatibilityHelpersTest(unittest.TestCase):
|
|||
self.assertFalse(boolean_value(0))
|
||||
self.assertTrue(boolean_value(1))
|
||||
|
||||
def test_get_argument_method(self):
|
||||
token = self.parser.symbol_table['true'](self.parser)
|
||||
|
||||
self.assertIsNone(token.get_argument(2))
|
||||
with self.assertRaises(TypeError):
|
||||
token.get_argument(1, required=True)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
|
@ -300,7 +300,7 @@ class XPath2ParserXMLSchemaTest(test_xpath2_parser.XPath2ParserTest):
|
|||
self.check_value("'5' cast as xs:integer", 5)
|
||||
self.check_value("'hello' cast as xs:integer", ValueError)
|
||||
self.check_value("('5', '6') cast as xs:integer", TypeError)
|
||||
self.check_value("() cast as xs:integer", ValueError)
|
||||
self.check_value("() cast as xs:integer", TypeError)
|
||||
self.check_value("() cast as xs:integer?", [])
|
||||
self.check_value('"1" cast as xs:boolean', True)
|
||||
self.check_value('"0" cast as xs:boolean', False)
|
||||
|
|
|
@ -323,6 +323,10 @@ class XPath1ParserTest(unittest.TestCase):
|
|||
self.check_token('(name)', 'literal', "'schema' name",
|
||||
"_name_literal_token(value='schema')", 'schema')
|
||||
|
||||
# Variables
|
||||
self.check_token('$', 'operator', "$ variable reference",
|
||||
"_DollarSign_operator_token()")
|
||||
|
||||
# Axes
|
||||
self.check_token('self', 'axis', "'self' axis", "_self_axis_token()")
|
||||
self.check_token('child', 'axis', "'child' axis", "_child_axis_token()")
|
||||
|
@ -344,6 +348,10 @@ class XPath1ParserTest(unittest.TestCase):
|
|||
|
||||
# Operators
|
||||
self.check_token('and', 'operator', "'and' operator", "_and_operator_token()")
|
||||
if self.parser.version == '1.0':
|
||||
self.check_token(',', 'symbol', "comma symbol", "_Comma_symbol_token()")
|
||||
else:
|
||||
self.check_token(',', 'operator', "comma operator", "_Comma_operator_token()")
|
||||
|
||||
def test_token_tree(self):
|
||||
self.check_tree('child::B1', '(child (B1))')
|
||||
|
@ -367,6 +375,12 @@ class XPath1ParserTest(unittest.TestCase):
|
|||
self.check_source('attribute::name="Galileo"', "attribute::name = 'Galileo'")
|
||||
self.check_source(".//eg:a | .//eg:b", '. // eg:a | . // eg:b')
|
||||
|
||||
try:
|
||||
self.parser.strict = False
|
||||
self.check_source("{tns1}name", '{tns1}name')
|
||||
finally:
|
||||
self.parser.strict = True
|
||||
|
||||
def test_wrong_syntax(self):
|
||||
self.wrong_syntax('')
|
||||
self.wrong_syntax(" \n \n )")
|
||||
|
|
|
@ -500,6 +500,7 @@ class XPath2ParserTest(test_xpath1_parser.XPath1ParserTest):
|
|||
self.check_value('fn:replace("abracadabra", "a.*a", "*")', "*")
|
||||
self.check_value('fn:replace("abracadabra", "a.*?a", "*")', "*c*bra")
|
||||
self.check_value('fn:replace("abracadabra", "a", "")', "brcdbr")
|
||||
self.wrong_type('fn:replace("abracadabra")')
|
||||
|
||||
self.check_value('fn:replace("abracadabra", "a(.)", "a$1$1")', "abbraccaddabbra")
|
||||
self.wrong_value('fn:replace("abracadabra", ".*?", "$1")')
|
||||
|
|
Loading…
Reference in New Issue