Fix xs:decimal based datatypes validation and function converion rules

This commit is contained in:
Davide Brunato 2019-08-05 17:51:33 +02:00
parent f00732ede5
commit 9994139e17
3 changed files with 107 additions and 57 deletions

View File

@ -1075,7 +1075,8 @@ def ncname_validator(x):
XSD_BUILTIN_TYPES = {
'anyType': XsdBuiltin(
lambda x: True, UntypedAtomic('1')
lambda x: True,
value=UntypedAtomic('1')
),
'anySimpleType': XsdBuiltin(
lambda x: isinstance(x, (string_base_type, int, float, bool, decimal.Decimal,
@ -1083,138 +1084,183 @@ XSD_BUILTIN_TYPES = {
value=UntypedAtomic('1')
),
'anyAtomicType': XsdBuiltin(
lambda x: False, value=None
lambda x: False,
value=None
),
'string': XsdBuiltin(
lambda x: isinstance(x, string_base_type), value=' alpha\t'
lambda x: isinstance(x, string_base_type),
value=' alpha\t'
),
'decimal': XsdBuiltin(
lambda x: isinstance(x, (int, float, decimal.Decimal)), value=decimal.Decimal('1.0')
lambda x: isinstance(x, (int, float, decimal.Decimal)) and not isinstance(x, bool),
value=decimal.Decimal('1.0')
),
'double': XsdBuiltin(
lambda x: isinstance(x, float), value=1.0
lambda x: isinstance(x, float),
value=1.0
),
'float': XsdBuiltin(
lambda x: isinstance(x, float), value=1.0
lambda x: isinstance(x, float),
value=1.0
),
'date': XsdBuiltin(
lambda x: isinstance(x, Date), value=Date.fromstring('2000-01-01')
lambda x: isinstance(x, Date),
value=Date.fromstring('2000-01-01')
),
'dateTime': XsdBuiltin(
lambda x: isinstance(x, DateTime), value=DateTime.fromstring('2000-01-01T12:00:00')
lambda x: isinstance(x, DateTime),
value=DateTime.fromstring('2000-01-01T12:00:00')
),
'gDay': XsdBuiltin(
lambda x: isinstance(x, GregorianDay), value=GregorianDay.fromstring('---31')
lambda x: isinstance(x, GregorianDay),
value=GregorianDay.fromstring('---31')
),
'gMonth': XsdBuiltin(
lambda x: isinstance(x, GregorianMonth), value=GregorianMonth.fromstring('--12')
lambda x: isinstance(x, GregorianMonth),
value=GregorianMonth.fromstring('--12')
),
'gMonthDay': XsdBuiltin(
lambda x: isinstance(x, GregorianMonthDay), value=GregorianMonthDay.fromstring('--12-01')
lambda x: isinstance(x, GregorianMonthDay),
value=GregorianMonthDay.fromstring('--12-01')
),
'gYear': XsdBuiltin(
lambda x: isinstance(x, GregorianYear), value=GregorianYear.fromstring('1999')
lambda x: isinstance(x, GregorianYear),
value=GregorianYear.fromstring('1999')
),
'gYearMonth': XsdBuiltin(
lambda x: isinstance(x, GregorianYearMonth), value=GregorianYearMonth.fromstring('1999-09')
lambda x: isinstance(x, GregorianYearMonth),
value=GregorianYearMonth.fromstring('1999-09')
),
'time': XsdBuiltin(
lambda x: isinstance(x, Time), value=Time.fromstring('09:26:54')
lambda x: isinstance(x, Time),
value=Time.fromstring('09:26:54')
),
'duration': XsdBuiltin(
lambda x: isinstance(x, Duration), value=Duration.fromstring('P1MT1S')
lambda x: isinstance(x, Duration),
value=Duration.fromstring('P1MT1S')
),
'dayTimeDuration': XsdBuiltin(
lambda x: isinstance(x, DayTimeDuration), value=DayTimeDuration.fromstring('P1DT1S')
lambda x: isinstance(x, DayTimeDuration),
value=DayTimeDuration.fromstring('P1DT1S')
),
'yearMonthDuration': XsdBuiltin(
lambda x: isinstance(x, YearMonthDuration), value=YearMonthDuration.fromstring('P1Y1M')
lambda x: isinstance(x, YearMonthDuration),
value=YearMonthDuration.fromstring('P1Y1M')
),
'QName': XsdBuiltin(
lambda x: isinstance(x, string_base_type) and QNAME_PATTERN.match(x) is not None, value='xs:element'
lambda x: isinstance(x, string_base_type) and QNAME_PATTERN.match(x) is not None,
value='xs:element'
),
'NOTATION': XsdBuiltin(
lambda x: isinstance(x, string_base_type), value='alpha'
lambda x: isinstance(x, string_base_type),
value='alpha'
),
'anyURI': XsdBuiltin(
lambda x: isinstance(x, string_base_type), value='https://example.com'
lambda x: isinstance(x, string_base_type),
value='https://example.com'
),
'normalizedString': XsdBuiltin(
lambda x: isinstance(x, string_base_type) and '\t' not in x and '\r' not in x, value=' alpha ',
lambda x: isinstance(x, string_base_type) and '\t' not in x and '\r' not in x,
value=' alpha ',
),
'token': XsdBuiltin(
lambda x: isinstance(x, string_base_type) and WHITESPACES_PATTERN.match(x) is None, value='a token'
lambda x: isinstance(x, string_base_type) and WHITESPACES_PATTERN.match(x) is None,
value='a token'
),
'language': XsdBuiltin(
lambda x: isinstance(x, string_base_type) and LANGUAGE_CODE_PATTERN.match(x) is not None, value='en-US'
lambda x: isinstance(x, string_base_type) and LANGUAGE_CODE_PATTERN.match(x) is not None,
value='en-US'
),
'Name': XsdBuiltin(
lambda x: isinstance(x, string_base_type) and NAME_PATTERN.match(x) is not None, value='_a.name::'
lambda x: isinstance(x, string_base_type) and NAME_PATTERN.match(x) is not None,
value='_a.name::'
),
'NCName': XsdBuiltin(
ncname_validator, value='nc-name'
ncname_validator,
value='nc-name'
),
'ID': XsdBuiltin(
ncname_validator, value='id1'
ncname_validator,
value='id1'
),
'IDREF': XsdBuiltin(
ncname_validator, value='id_ref1'
ncname_validator,
value='id_ref1'
),
'ENTITY': XsdBuiltin(
ncname_validator, value='entity1'
ncname_validator,
value='entity1'
),
'NMTOKEN': XsdBuiltin(
lambda x: isinstance(x, string_base_type) and NMTOKEN_PATTERN.match(x) is not None, value='a_token'
lambda x: isinstance(x, string_base_type) and NMTOKEN_PATTERN.match(x) is not None,
value='a_token'
),
'base64Binary': XsdBuiltin(
base64_binary_validator, value=b'YWxwaGE='
base64_binary_validator,
value=b'YWxwaGE='
),
'hexBinary': XsdBuiltin(
hex_binary_validator, value=b'31'
hex_binary_validator,
value=b'31'
),
'dateTimeStamp': XsdBuiltin(
lambda x: isinstance(x, string_base_type), value='2000-01-01T12:00:00+01:00'
lambda x: isinstance(x, string_base_type),
value='2000-01-01T12:00:00+01:00'
),
'integer': XsdBuiltin(
lambda x: isinstance(x, int), value=1
lambda x: isinstance(x, int) and not isinstance(x, bool),
value=1
),
'long': XsdBuiltin(
lambda x: isinstance(x, int) and (-2**63 <= x < 2**63), value=1
lambda x: isinstance(x, int) and not isinstance(x, bool) and (-2**63 <= x < 2**63),
value=1
),
'int': XsdBuiltin(
lambda x: isinstance(x, int) and (-2**31 <= x < 2**31), value=1
lambda x: isinstance(x, int) and not isinstance(x, bool) and (-2**31 <= x < 2**31),
value=1
),
'short': XsdBuiltin(
lambda x: isinstance(x, int) and (-2**15 <= x < 2**15), value=1
lambda x: isinstance(x, int) and not isinstance(x, bool) and (-2**15 <= x < 2**15),
value=1
),
'byte': XsdBuiltin(
lambda x: isinstance(x, int) and (-2**7 <= x < 2**7), value=1
lambda x: isinstance(x, int) and not isinstance(x, bool) and (-2**7 <= x < 2**7),
value=1
),
'positiveInteger': XsdBuiltin(
lambda x: isinstance(x, int) and x > 0, value=1
lambda x: isinstance(x, int) and not isinstance(x, bool) and x > 0,
value=1
),
'negativeInteger': XsdBuiltin(
lambda x: isinstance(x, int) and x < 0, value=-1
lambda x: isinstance(x, int) and not isinstance(x, bool) and x < 0,
value=-1
),
'nonPositiveInteger': XsdBuiltin(
lambda x: isinstance(x, int) and x <= 0, value=0
lambda x: isinstance(x, int) and not isinstance(x, bool) and x <= 0,
value=0
),
'nonNegativeInteger': XsdBuiltin(
lambda x: isinstance(x, int) and x >= 0, value=0
lambda x: isinstance(x, int) and not isinstance(x, bool) and x >= 0,
value=0
),
'unsignedLong': XsdBuiltin(
lambda x: isinstance(x, int) and (0 <= x < 2**64), value=1
lambda x: isinstance(x, int) and not isinstance(x, bool) and (0 <= x < 2**64),
value=1
),
'unsignedInt': XsdBuiltin(
lambda x: isinstance(x, int) and (0 <= x < 2**32), value=1
lambda x: isinstance(x, int) and not isinstance(x, bool) and (0 <= x < 2**32),
value=1
),
'unsignedShort': XsdBuiltin(
lambda x: isinstance(x, int) and (0 <= x < 2**16), value=1
lambda x: isinstance(x, int) and not isinstance(x, bool) and (0 <= x < 2**16),
value=1
),
'unsignedByte': XsdBuiltin(
lambda x: isinstance(x, int) and (0 <= x < 2**8), value=1
lambda x: isinstance(x, int) and not isinstance(x, bool) and (0 <= x < 2**8),
value=1
),
'boolean': XsdBuiltin(
lambda x: isinstance(x, bool), value=True
lambda x: isinstance(x, bool),
value=True
),
}

View File

@ -1028,9 +1028,6 @@ 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))

View File

@ -21,6 +21,7 @@ for documents. Generic tuples are used for representing attributes and named-tup
"""
import locale
import contextlib
import decimal
from .compat import string_base_type
from .exceptions import xpath_error
@ -118,7 +119,8 @@ class XPathToken(Token):
###
# Helper methods
def get_argument(self, context, index=0, required=False, default_to_context=False, default=None, cls=None):
def get_argument(self, context, index=0, required=False, default_to_context=False,
default=None, cls=None):
"""
Get the argument value of a function of constructor token. A zero length sequence is
converted to a `None` value. If the function has no argument returns the context's
@ -158,12 +160,9 @@ class XPathToken(Token):
if not required:
return default
ord_arg = ordinal(index + 1)
if cls is None:
self.missing_sequence("A not empty sequence required for %s argument" % ord_arg)
else:
self.missing_sequence("A not empty sequence of %r required for %s argument" % (cls, ord_arg))
self.missing_sequence("A not empty sequence required for %s argument" % ord_arg)
# Type checking and conversion (see "function conversion rules" in XPath 2.0 language definition)
# Type promotion checking (see "function conversion rules" in XPath 2.0 language definition)
if cls is not None and not isinstance(item, cls):
if self.parser.compatibility_mode:
if issubclass(cls, string_base_type):
@ -173,14 +172,20 @@ class XPathToken(Token):
if self.parser.version > '1.0':
value = self.data_value(item)
if isinstance(value, UntypedAtomic):
if isinstance(value, cls):
return value
elif isinstance(value, UntypedAtomic):
try:
return str(value) if issubclass(cls, string_base_type) else cls(value)
except (TypeError, ValueError):
pass
elif issubclass(cls, float) and not isinstance(value, bool) \
and isinstance(value, (int, float, decimal.Decimal)):
return self.number_value(value)
code = 'XPTY0004' if self.label == 'function' else 'FORG0006'
raise self.error(code, "the %s argument %r is not a %r instance" % (ordinal(index + 1), item, cls))
message = "the %s argument %r is not a %r instance"
raise self.error(code, message % (ordinal(index + 1), item, cls))
return item
@ -374,6 +379,8 @@ class XPathToken(Token):
# of the primitive type
primitive_type = self.parser.schema.get_primitive_type(obj.type)
return XSD_BUILTIN_TYPES[primitive_type.local_name].value
elif obj.type.local_name == 'anyType':
return XSD_BUILTIN_TYPES['anyType'].value
def boolean_value(self, obj):
"""