1# 2# Copyright (c), 2018-2020, SISSA (International School for Advanced Studies). 3# All rights reserved. 4# This file is distributed under the terms of the MIT License. 5# See the file 'LICENSE' in the root directory of the present 6# distribution, or http://opensource.org/licenses/MIT. 7# 8# @author Davide Brunato <brunato@sissa.it> 9# 10import locale 11from typing import Optional, Any 12from .tdop import Token 13 14 15class ElementPathError(Exception): 16 """ 17 Base exception class for elementpath package. 18 19 :param message: the message related to the error. 20 :param code: an optional error code. 21 :param token: an optional token instance related with the error. 22 """ 23 def __init__(self, message: str, 24 code: Optional[str] = None, 25 token: Optional[Token[Any]] = None) -> None: 26 super(ElementPathError, self).__init__(message) 27 self.message = message 28 self.code = code 29 self.token = token 30 31 def __str__(self) -> str: 32 if self.token is None or not isinstance(self.token.value, (str, bytes)): 33 if not self.code: 34 return self.message 35 return '[{}] {}'.format(self.code, self.message) 36 elif not self.code: 37 return '{1} at line {2}, column {3}: {0}'.format( 38 self.message, self.token, *self.token.position 39 ) 40 return '{2} at line {3}, column {4}: [{1}] {0}'.format( 41 self.message, self.code, self.token, *self.token.position 42 ) 43 44 45class MissingContextError(ElementPathError): 46 """Raised when the dynamic context is required for evaluate the XPath expression.""" 47 48 49class ElementPathKeyError(ElementPathError, KeyError): 50 pass 51 52 53class ElementPathZeroDivisionError(ElementPathError, ZeroDivisionError): 54 pass 55 56 57class ElementPathNameError(ElementPathError, NameError): 58 pass 59 60 61class ElementPathOverflowError(ElementPathError, OverflowError): 62 pass 63 64 65class ElementPathRuntimeError(ElementPathError, RuntimeError): 66 pass 67 68 69class ElementPathSyntaxError(ElementPathError, SyntaxError): 70 pass 71 72 73class ElementPathTypeError(ElementPathError, TypeError): 74 pass 75 76 77class ElementPathValueError(ElementPathError, ValueError): 78 pass 79 80 81class ElementPathLocaleError(ElementPathError, locale.Error): 82 pass 83 84 85XPATH_ERROR_CODES = { 86 # XPath 2.0 parser error (https://www.w3.org/TR/xpath20/#id-errors) 87 'XPST0001': (ElementPathValueError, 'Parser not bound to a schema'), 88 'XPST0003': (ElementPathSyntaxError, 'Invalid XPath expression'), 89 'XPDY0002': (MissingContextError, 'Dynamic context required for evaluate'), 90 'XPTY0004': (ElementPathTypeError, 'Type is not appropriate for the context'), 91 'XPST0005': (ElementPathValueError, 'A not empty sequence required'), 92 'XPST0008': (ElementPathNameError, 'Name not found'), 93 'XPST0010': (ElementPathNameError, 'Axis not found'), 94 'XPST0017': (ElementPathTypeError, 'Wrong number of arguments'), 95 'XPTY0018': (ElementPathTypeError, 96 'Step result contains both nodes and atomic values'), 97 'XPTY0019': (ElementPathTypeError, 'Intermediate step contains an atomic value'), 98 'XPTY0020': (ElementPathTypeError, 'Context item is not a node'), 99 'XPDY0050': (ElementPathTypeError, 'Type does not match sequence type'), 100 'XPST0051': (ElementPathNameError, 'Unknown atomic type'), 101 'XPST0080': (ElementPathNameError, 102 'Target type cannot be xs:NOTATION or xs:anyAtomicType'), 103 'XPST0081': (ElementPathNameError, 'Unknown namespace'), 104 105 # XPath data types and function errors 106 'FOER0000': (ElementPathError, 'Unidentified error'), 107 'FOAR0001': (ElementPathZeroDivisionError, 'Division by zero'), 108 'FOAR0002': (ElementPathOverflowError, 'Numeric operation overflow/underflow'), 109 'FOCA0001': (ElementPathValueError, 'Input value too large for decimal'), 110 'FOCA0002': (ElementPathValueError, 'Invalid lexical value'), 111 'FOCA0003': (ElementPathValueError, 'Input value too large for integer'), 112 'FOCA0005': (ElementPathValueError, 'NaN supplied as float/double value'), 113 'FOCA0006': (ElementPathValueError, 114 'String to be cast to decimal has too many digits of precision'), 115 'FOCH0001': (ElementPathValueError, 'Code point not valid'), 116 'FOCH0002': (ElementPathLocaleError, 'Unsupported collation'), 117 'FOCH0003': (ElementPathValueError, 'Unsupported normalization form'), 118 'FOCH0004': (ElementPathValueError, 'Collation does not support collation units'), 119 'FODC0001': (ElementPathValueError, 'No context document'), 120 'FODC0002': (ElementPathValueError, 'Error retrieving resource'), 121 'FODC0003': (ElementPathValueError, 'Function stability not defined'), 122 'FODC0004': (ElementPathValueError, 'Invalid argument to fn:collection'), 123 'FODC0005': (ElementPathValueError, 'Invalid argument to fn:doc or fn:doc-available'), 124 'FODT0001': (ElementPathOverflowError, 'Overflow/underflow in date/time operation'), 125 'FODT0002': (ElementPathOverflowError, 'Overflow/underflow in duration operation'), 126 'FODT0003': (ElementPathValueError, 'Invalid timezone value'), 127 'FONS0004': (ElementPathKeyError, 'No namespace found for prefix'), 128 'FONS0005': (ElementPathValueError, 'Base-uri not defined in the static context'), 129 'FORG0001': (ElementPathValueError, 'Invalid value for cast/constructor'), 130 'FORG0002': (ElementPathValueError, 'Invalid argument to fn:resolve-uri()'), 131 'FORG0003': (ElementPathValueError, 132 'fn:zero-or-one called with a sequence containing more than one item'), 133 'FORG0004': (ElementPathValueError, 134 'fn:one-or-more called with a sequence containing no items'), 135 'FORG0005': (ElementPathValueError, 136 'fn:exactly-one called with a sequence containing zero or more than one item'), 137 'FORG0006': (ElementPathTypeError, 'Invalid argument type'), 138 'FORG0008': (ElementPathValueError, 139 'The two arguments to fn:dateTime have inconsistent timezones'), 140 'FORG0009': (ElementPathValueError, 141 'Error in resolving a relative URI against a base URI in fn:resolve-uri'), 142 'FORX0001': (ElementPathValueError, 'Invalid regular expression flags'), 143 'FORX0002': (ElementPathValueError, 'Invalid regular expression'), 144 'FORX0003': (ElementPathValueError, 'Regular expression matches zero-length string'), 145 'FORX0004': (ElementPathValueError, 'Invalid replacement string'), 146 'FOTY0012': (ElementPathValueError, 'Argument node does not have a typed value'), 147 148 # XPath 3.0 errors 149 'FOTY0013': (ElementPathTypeError, 'The argument to fn:data() contains a function item'), 150 'FOTY0014': (ElementPathTypeError, 'The argument to fn:string() is a function item'), 151 'FOTY0015': (ElementPathTypeError, 152 'An argument to fn:deep-equal() contains a function item'), 153 'FODC0006': (ElementPathValueError, 154 'String passed to fn:parse-xml is not a well-formed XML document'), 155 'FODC0010': (ElementPathRuntimeError, 156 'The processor does not support serialization'), 157 'FOUT1170': (ElementPathValueError, 'Invalid $href argument to fn:unparsed-text()'), 158 'FOUT1190': (ElementPathValueError, 159 'Cannot decode resource retrieved by fn:unparsed-text()'), 160 'FOUT1200': (ElementPathValueError, 161 'Cannot infer encoding of resource retrieved by fn:unparsed-text()'), 162 'FODF1280': (ElementPathValueError, 'Invalid decimal format name'), 163 'FODF1310': (ElementPathValueError, 'Invalid decimal format picture string'), 164 'FOFD1340': (ElementPathValueError, 'Invalid date/time formatting parameters'), 165 'FOFD1350': (ElementPathValueError, 'Invalid date/time formatting component'), 166 167 # XSLT and XQuery Serialization errors 168 # (the complete list: https://www.w3.org/TR/xslt-xquery-serialization/#id-errors) 169 'SENR0001': (ElementPathTypeError, 'item is an attribute node or a namespace node'), 170 'SEPM0016': (ElementPathValueError, 'parameter value is invalid for the defined domain'), 171 'SEPM0017': (ElementPathValueError, 'error during extraction of serialization parameters'), 172 'SEPM0018': (ElementPathValueError, 'use-character-maps serialization parameter in ' 173 'a sequence of length greater than one'), 174 'SEPM0019': (ElementPathValueError, 'same serialization parameter appears more than once'), 175} 176 177 178def xpath_error(code: str, message: Optional[str] = None, 179 token: Optional[Token[Any]] = None, prefix: str = 'err') -> ElementPathError: 180 """ 181 Returns an XPath error instance related with a code. An XPath/XQuery/XSLT error code 182 (ref: http://www.w3.org/2005/xqt-errors) is an alphanumeric token starting with four 183 uppercase letters and ending with four digits. 184 185 :param code: the error code. 186 :param message: an optional custom additional message. 187 :param token: an optional token instance. 188 :param prefix: the namespace prefix to apply to the error code, defaults to 'err'. 189 """ 190 if code.startswith('{'): 191 try: 192 namespace, code = code[1:].split('}') 193 except ValueError: 194 message = '{!r} is not an xs:QName'.format(code) 195 raise ElementPathValueError(message, 'err:XPTY0004', token) 196 else: 197 if namespace != 'http://www.w3.org/2005/xqt-errors': 198 message = 'invalid namespace {!r}'.format(namespace) 199 raise ElementPathValueError(message, 'err:XPTY0004', token) 200 pcode = '%s:%s' % (prefix, code) if prefix else code 201 elif ':' not in code: 202 pcode = '%s:%s' % (prefix, code) if prefix else code 203 elif not prefix or not code.startswith(prefix + ':'): 204 message = '%r is not an XPath error code' % code 205 raise ElementPathValueError(message, 'err:XPTY0004', token) 206 else: 207 pcode = code 208 code = code[len(prefix) + 1:] 209 210 try: 211 error_class, default_message = XPATH_ERROR_CODES[code] 212 except KeyError: 213 raise ElementPathValueError( 214 message or 'unknown XPath error code %r' % code, 'err:XPTY0004', token 215 ) 216 else: 217 return error_class(message or default_message, pcode, token) 218